UNPKG

parallaxapis-sdk-ts

Version:
1 lines 18.9 kB
{"version":3,"sources":["../src/constants.ts","../src/api.ts","../src/tasks.ts","../src/datadome.ts","../src/perimeterx.ts"],"sourcesContent":["export const DEFAULT_DATADOME_API_HOST = \"dd.parallaxsystems.io\";\nexport const DEFAULT_PX_API_HOST = \"api.parallaxsystems.io\"; ","import { request } from \"undici\";\r\nimport type { ApiClientConfig } from \"./config\";\r\nimport type { GenericResponse, ResponseGetUsage } from \"./responses\";\r\nimport type { GenericTask } from \"./tasks\";\r\n\r\nexport type Endpoint = `/${string}`;\r\n\r\nexport class ApiClient {\r\n private apiHost = \"\";\r\n private apiKey = \"\";\r\n private requestOptions: Parameters<typeof request>[1];\r\n\r\n /**\r\n * Creates an instance of a Client.\r\n * @param cfg Configuration object containing host and api key.\r\n */\r\n constructor(cfg: ApiClientConfig) {\r\n this.apiHost = cfg.apiHost;\r\n this.apiKey = cfg.apiKey;\r\n\r\n this.requestOptions = {};\r\n\r\n if (cfg.timeout) this.requestOptions.headersTimeout = cfg.timeout;\r\n if (cfg.bodyTimeout) this.requestOptions.bodyTimeout = cfg.bodyTimeout;\r\n if (cfg.dispatcher) this.requestOptions.dispatcher = cfg.dispatcher;\r\n }\r\n\r\n protected async request<T extends GenericResponse<{}>, TBody>(\r\n endpoint: Endpoint,\r\n body: TBody,\r\n ): Promise<T> {\r\n const url = `https://${this.apiHost}${endpoint}`;\r\n\r\n const payload: GenericTask<TBody> = {\r\n auth: this.apiKey,\r\n ...body,\r\n };\r\n\r\n const options = {\r\n ...this.requestOptions,\r\n method: \"POST\",\r\n body: JSON.stringify(payload),\r\n headers: { \"content-type\": \"application/json\" },\r\n };\r\n\r\n const res = await request(url, options);\r\n\r\n if (res.statusCode != 200)\r\n throw new Error(\r\n `Unexpected status code returned from parallax api:\\n ${await res.body.text()}`,\r\n );\r\n\r\n const resBody = (await res.body.json()) as T;\r\n\r\n if (resBody.error) {\r\n if (!resBody.message) resBody.message = resBody.cookie; //dd px system mismatch, needs to be updated in future\r\n throw new Error(\r\n `Api responded with error, error message: ${resBody.message}`,\r\n );\r\n }\r\n\r\n return resBody;\r\n }\r\n\r\n /**\r\n * Checks usage statistics for a given site.\r\n *\r\n * @param site The site identifier to check usage for.\r\n * @returns A promise resolving to the usage response.\r\n */\r\n public async checkUsage(site: string): Promise<ResponseGetUsage> {\r\n const res = await request(\r\n `https://${this.apiHost}/usage?authToken=${this.apiKey}&site=${site}`\r\n );\r\n\r\n const resBody = (await res.body.json()) as ResponseGetUsage;\r\n\r\n return resBody;\r\n }\r\n}\r\n","export type ProxyAddress = `${'http' | 'https' | 'socks' | 'socks5' | 'socks5h'}://${string}`\r\n\r\n/**\r\n * Enum for DataDome product types.\r\n * - Captcha: Captcha challenge\r\n * - Interstitial: Interstitial page\r\n * - Init: Tags.js\r\n */\r\nexport enum ProductType {\r\n Captcha = \"captcha\",\r\n Interstitial = \"interstitial\",\r\n Init = \"init\"\r\n}\r\n\r\nexport type GenericTask<T> = T & {\r\n auth: string\r\n}\r\n\r\nexport type TaskGenerateUserAgent = {\r\n pd?: string\r\n site: string\r\n region: string\r\n};\r\n\r\nexport type TaskGenerateDatadomeCookieData = {\r\n cid: string,\r\n e: string,\r\n s: string,\r\n b: string,\r\n initialCid: string\r\n}\r\n\r\n/**\r\n * Task for generating a DataDome tags cookie cookie.\r\n * @property site - Site for which to generate the cookie.\r\n * @property region - Site region.\r\n * @property proxyregion - Proxy region.\r\n * @property proxy - Proxy address.\r\n * @property pd - Product type.\r\n * @property data - Data required for cookie generation, only cid value with null is needed.\r\n */\r\nexport type TaskGenerateDatadomeTags = {\r\n site: string\r\n region: string\r\n proxyregion: string,\r\n proxy: ProxyAddress,\r\n data: Pick<TaskGenerateDatadomeCookieData, \"cid\">\r\n};\r\n\r\n/**\r\n * Task for generating a DataDome cookie.\r\n * @property site - Site for which to generate the cookie.\r\n * @property region - Site region.\r\n * @property proxyregion - Proxy region.\r\n * @property proxy - Proxy address.\r\n * @property pd - Product type.\r\n * @property data - Data required for cookie generation.\r\n */\r\nexport type TaskGenerateDatadomeCookie = {\r\n site: string\r\n region: string\r\n proxyregion: string,\r\n proxy: ProxyAddress,\r\n pd: ProductType,\r\n data: TaskGenerateDatadomeCookieData\r\n};\r\n\r\n/**\r\n * Task for generating PX cookies.\r\n * @property site - Site for which to generate PX cookies.\r\n * @property proxyregion - Proxy region.\r\n * @property region - Site region.\r\n * @property proxy - Proxy address.\r\n */\r\nexport type TaskGeneratePXCookies = {\r\n site: string\r\n proxyregion: string\r\n region: string,\r\n proxy: ProxyAddress,\r\n};\r\n\r\n/**\r\n * Task for holdcaptcha challenge.\r\n * @property site - Site for which to solve holdcaptcha \r\n * @property proxyregion - The region of your proxy (either \"eu\" or \"us\")..\r\n * @property region - The region of the site (e.g., \"com\" for .com sites or other TLDs like \".fr\" or \".ch\")..\r\n * @property proxy - The proxy used for the request in HTTP format. Cookies must be generated with a proxy, they cannot be shared with other IPs.\r\n * @property POW_PRO - (Optional) Insert your Cuda POW solver key here if you want to use Cuda as the solution method for the proof-of-work challenge. If you leave this field blank, the system will try to solve the POW with the normal system, which only works with unflagged proxies.\r\n * @property data - holdcaptcha challenge data string.\r\n */\r\nexport type TaskGenerateHoldCaptcha = {\r\n site: string\r\n proxyregion: string\r\n region: string,\r\n proxy: ProxyAddress,\r\n POW_PRO?: string,\r\n data: string,\r\n};\r\n","import { ApiClient } from \"./api\";\r\nimport type { ApiClientConfig, ClientSDKConfig } from \"./config\";\r\nimport { DEFAULT_DATADOME_API_HOST } from \"./constants\";\r\nimport type {\r\n HtmlDatadomeBlockBody,\r\n JSONDatadomeBlockBody,\r\n} from \"./datadome-entities\";\r\nimport type {\r\n GenerateDatadomeCookieResponse,\r\n GenerateUserAgentResponse,\r\n} from \"./responses\";\r\nimport {\r\n ProductType,\r\n type TaskGenerateDatadomeCookie,\r\n type TaskGenerateDatadomeCookieData,\r\n type TaskGenerateDatadomeTags,\r\n type TaskGenerateUserAgent,\r\n} from \"./tasks\";\r\n\r\n// Datadome block url regexp\r\nconst datadomeBlockUrlRe =\r\n /geo\\.captcha\\-delivery\\.com\\/(?:interstitial|captcha)/;\r\n// Datadome script object regexp\r\nconst datadomeHtmlScriptRe = /dd=\\{[^}]+\\}/g;\r\n// Regexp for single quoted keys, like 'key':...\r\nconst singleQuotedKeyRe = /'((?:[^'\\\\]|\\\\.)*)'\\s*:/g;\r\n// Regexp for single quoted values, like ...:'value':\r\nconst singleQuotedValueRe = /:\\s*'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)'/g;\r\n\r\nexport enum TTags {\r\n T_BV = \"bv\",\r\n T_FE = \"fe\",\r\n T_IT = \"it\",\r\n}\r\n\r\nexport class DatadomeSDK extends ApiClient {\r\n /**\r\n * Creates an instance of DatadomeSDK.\r\n * Sets the API host to the default if not provided in the config.\r\n *\r\n * @param cfg Configuration object for the SDK, containing api key and optionally api host.\r\n */\r\n constructor(cfg: ClientSDKConfig) {\r\n if (!cfg?.apiHost) cfg.apiHost = DEFAULT_DATADOME_API_HOST;\r\n\r\n super({\r\n ...cfg,\r\n apiHost: cfg.apiHost,\r\n } satisfies ApiClientConfig);\r\n }\r\n\r\n /**\r\n * Generates a user agent data.\r\n */\r\n public async generateUserAgent(\r\n task: TaskGenerateUserAgent,\r\n ): Promise<GenerateUserAgentResponse> {\r\n return await this.request(\"/useragent\", task);\r\n }\r\n\r\n /**\r\n * Generates a DataDome cookie using the provided task parameters.\r\n */\r\n public async generateCookie(\r\n task: TaskGenerateDatadomeCookie,\r\n ): Promise<GenerateDatadomeCookieResponse> {\r\n return await this.request(\"/gen\", task);\r\n }\r\n\r\n /**\r\n * Parses a DataDome challenge URL and extracts the challenge data and product type.\r\n */\r\n public parseChallengeUrl(\r\n challengeUrl: string,\r\n prevDatadomeCookie: string,\r\n ): [TaskGenerateDatadomeCookieData, ProductType] {\r\n const url = new URL(challengeUrl);\r\n\r\n let pd: ProductType;\r\n\r\n if (url.pathname.startsWith(\"/captcha\")) {\r\n pd = ProductType.Captcha;\r\n } else if (url.pathname.startsWith(\"/interstitial\")) {\r\n pd = ProductType.Interstitial;\r\n } else if (url.pathname.startsWith(\"/init\")) {\r\n pd = ProductType.Init;\r\n } else throw new Error(\"unknown challenge type in URL\");\r\n\r\n const params = new URLSearchParams(challengeUrl.split(\"?\")[1]);\r\n\r\n const taskData = {\r\n cid: prevDatadomeCookie,\r\n b: params.get(\"b\") || \"0\",\r\n e: params.get(\"e\") || \"\",\r\n s: params.get(\"s\") || \"\",\r\n initialCid: params.get(\"initialCid\") || \"\",\r\n };\r\n\r\n return [taskData, pd];\r\n }\r\n\r\n /**\r\n * Parses a DataDome challenge URL and extracts the challenge data and product type.\r\n */\r\n public async generateDatadomeTagsCookie(\r\n task: TaskGenerateDatadomeTags,\r\n ): Promise<GenerateDatadomeCookieResponse> {\r\n return await this.request(\"/gen\", {\r\n ...task,\r\n pd: ProductType.Init,\r\n data: {\r\n cid: task.data.cid,\r\n b: \"\",\r\n e: \"\",\r\n initialCid: \"\",\r\n s: \"\",\r\n },\r\n } satisfies TaskGenerateDatadomeCookie);\r\n }\r\n\r\n /**\r\n * Parses a DataDome challenge JSON body and extracts the challenge data and product type.\r\n * Throws if the JSON is invalid or does not contain a 'url' property.\r\n */\r\n public parseChallengeJson(\r\n jsonBody: string,\r\n prevDatadomeCookie: string,\r\n ): [TaskGenerateDatadomeCookieData, ProductType] {\r\n let parsedBody: JSONDatadomeBlockBody;\r\n\r\n try {\r\n parsedBody = JSON.parse(jsonBody);\r\n } catch {\r\n throw new Error(\"unparsable DataDome JSON body\");\r\n }\r\n\r\n if (!parsedBody.url)\r\n throw new Error(\r\n \"unparsable DataDome JSON body (couldn't extract url from response)\",\r\n );\r\n\r\n return this.parseChallengeUrl(parsedBody.url, prevDatadomeCookie);\r\n }\r\n\r\n /**\r\n * Parses a DataDome challenge HTML and extracts the challenge data and product type.\r\n * Throws if the HTML does not contain a recognizable dd object.\r\n */\r\n public parseChallengeHtml(\r\n htmlBody: string,\r\n prevDatadomeCookie: string,\r\n ): [TaskGenerateDatadomeCookieData, ProductType] {\r\n const match = htmlBody.match(datadomeHtmlScriptRe);\r\n\r\n if (!match) throw new Error(\"no DataDome values in HTML body\");\r\n\r\n let objStr = match[0].slice(3); // skip 'dd='\r\n\r\n objStr = objStr.replaceAll(singleQuotedKeyRe, '\"$1\":');\r\n objStr = objStr.replaceAll(singleQuotedValueRe, ':\"$1\"');\r\n\r\n let dd: HtmlDatadomeBlockBody;\r\n\r\n try {\r\n console.log(objStr);\r\n dd = JSON.parse(objStr);\r\n } catch {\r\n throw new Error(\"no DataDome values in HTML body\");\r\n }\r\n\r\n let pd: ProductType;\r\n\r\n switch (dd.t) {\r\n case TTags.T_IT:\r\n pd = ProductType.Interstitial;\r\n break;\r\n case TTags.T_FE:\r\n pd = ProductType.Captcha;\r\n break;\r\n case TTags.T_BV:\r\n throw new Error(\"permanently blocked by DataDome (t=bv)\");\r\n default:\r\n throw new Error(\"unknown challenge type in HTML\");\r\n }\r\n\r\n return [\r\n {\r\n b: dd.b?.toString() || \"\",\r\n s: dd.s.toString(),\r\n e: dd.e,\r\n cid: prevDatadomeCookie,\r\n initialCid: dd.cid,\r\n },\r\n pd,\r\n ];\r\n }\r\n\r\n /**\r\n * Detects the challenge type in the body and parses accordingly.\r\n * Returns [blocked, data, productType].\r\n */\r\n public detectChallengeAndParse(\r\n body: string,\r\n prevDatadomeCookie: string,\r\n ): [boolean, TaskGenerateDatadomeCookieData | null, ProductType | null] {\r\n if (datadomeHtmlScriptRe.test(body)) {\r\n const [data, pd] = this.parseChallengeHtml(body, prevDatadomeCookie);\r\n return [true, data, pd];\r\n } else if (datadomeBlockUrlRe.test(body)) {\r\n const [data, pd] = this.parseChallengeJson(body, prevDatadomeCookie);\r\n return [true, data, pd];\r\n }\r\n\r\n return [false, null, null];\r\n }\r\n}\r\n","import { ApiClient } from \"./api\";\r\nimport type { ApiClientConfig, ClientSDKConfig } from \"./config\";\r\nimport { DEFAULT_PX_API_HOST } from \"./constants\";\r\nimport type {\r\n GenerateHoldCaptchaResponse,\r\n GeneratePxCookiesResponse,\r\n} from \"./responses\";\r\nimport {\r\n type TaskGenerateHoldCaptcha,\r\n type TaskGeneratePXCookies,\r\n} from \"./tasks\";\r\n\r\nexport class PerimeterxSDK extends ApiClient {\r\n /**\r\n * Creates an instance of PerimeterxSDK.\r\n * Sets the API host to the default if not provided in the config.\r\n *\r\n * @param cfg Configuration object for the SDK, containing apiKey and optionally apiHost.\r\n */\r\n constructor(cfg: ClientSDKConfig) {\r\n if (!cfg?.apiHost) cfg.apiHost = DEFAULT_PX_API_HOST;\r\n\r\n super({\r\n ...cfg,\r\n apiHost: cfg.apiHost,\r\n } satisfies ApiClientConfig);\r\n }\r\n\r\n /**\r\n * Generates PX cookies using the provided task parameters.\r\n *\r\n * @param task The task object containing parameters for PX cookie generation.\r\n * @returns A promise resolving to the generated PX cookies response.\r\n */\r\n public async generateCookies(\r\n task: TaskGeneratePXCookies,\r\n ): Promise<GeneratePxCookiesResponse> {\r\n return await this.request(\"/gen\", task);\r\n }\r\n\r\n /**\r\n * Generates a holdcaptcha response using the provided task parameters.\r\n *\r\n * @param task The task object containing parameters for holdcaptcha generation.\r\n * @returns A promise resolving to the generated holdcaptcha response.\r\n */\r\n public async generateHoldCaptcha(\r\n task: TaskGenerateHoldCaptcha,\r\n ): Promise<GenerateHoldCaptchaResponse> {\r\n return await this.request(\"/holdcaptcha\", task);\r\n }\r\n}\r\n"],"mappings":";AAAO,IAAM,4BAA4B;AAClC,IAAM,sBAAsB;;;ACDnC,SAAS,eAAe;AAOjB,IAAM,YAAN,MAAgB;AAAA,EACb,UAAU;AAAA,EACV,SAAS;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,YAAY,KAAsB;AAChC,SAAK,UAAU,IAAI;AACnB,SAAK,SAAS,IAAI;AAElB,SAAK,iBAAiB,CAAC;AAEvB,QAAI,IAAI,QAAS,MAAK,eAAe,iBAAiB,IAAI;AAC1D,QAAI,IAAI,YAAa,MAAK,eAAe,cAAc,IAAI;AAC3D,QAAI,IAAI,WAAY,MAAK,eAAe,aAAa,IAAI;AAAA,EAC3D;AAAA,EAEA,MAAgB,QACd,UACA,MACY;AACZ,UAAM,MAAM,WAAW,KAAK,OAAO,GAAG,QAAQ;AAE9C,UAAM,UAA8B;AAAA,MAClC,MAAM,KAAK;AAAA,MACX,GAAG;AAAA,IACL;AAEA,UAAM,UAAU;AAAA,MACd,GAAG,KAAK;AAAA,MACR,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAChD;AAEA,UAAM,MAAM,MAAM,QAAQ,KAAK,OAAO;AAEtC,QAAI,IAAI,cAAc;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,GAAwD,MAAM,IAAI,KAAK,KAAK,CAAC;AAAA,MAC/E;AAEF,UAAM,UAAW,MAAM,IAAI,KAAK,KAAK;AAErC,QAAI,QAAQ,OAAO;AACjB,UAAI,CAAC,QAAQ,QAAS,SAAQ,UAAU,QAAQ;AAChD,YAAM,IAAI;AAAA,QACR,4CAA4C,QAAQ,OAAO;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,WAAW,MAAyC;AAC/D,UAAM,MAAM,MAAM;AAAA,MAChB,WAAW,KAAK,OAAO,oBAAoB,KAAK,MAAM,SAAS,IAAI;AAAA,IACrE;AAEA,UAAM,UAAW,MAAM,IAAI,KAAK,KAAK;AAErC,WAAO;AAAA,EACT;AACF;;;ACvEO,IAAK,cAAL,kBAAKA,iBAAL;AACH,EAAAA,aAAA,aAAU;AACV,EAAAA,aAAA,kBAAe;AACf,EAAAA,aAAA,UAAO;AAHC,SAAAA;AAAA,GAAA;;;ACYZ,IAAM,qBACJ;AAEF,IAAM,uBAAuB;AAE7B,IAAM,oBAAoB;AAE1B,IAAM,sBAAsB;AAErB,IAAK,QAAL,kBAAKC,WAAL;AACL,EAAAA,OAAA,UAAO;AACP,EAAAA,OAAA,UAAO;AACP,EAAAA,OAAA,UAAO;AAHG,SAAAA;AAAA,GAAA;AAML,IAAM,cAAN,cAA0B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOzC,YAAY,KAAsB;AAChC,QAAI,CAAC,KAAK,QAAS,KAAI,UAAU;AAEjC,UAAM;AAAA,MACJ,GAAG;AAAA,MACH,SAAS,IAAI;AAAA,IACf,CAA2B;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,kBACX,MACoC;AACpC,WAAO,MAAM,KAAK,QAAQ,cAAc,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,eACX,MACyC;AACzC,WAAO,MAAM,KAAK,QAAQ,QAAQ,IAAI;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKO,kBACL,cACA,oBAC+C;AAC/C,UAAM,MAAM,IAAI,IAAI,YAAY;AAEhC,QAAI;AAEJ,QAAI,IAAI,SAAS,WAAW,UAAU,GAAG;AACvC;AAAA,IACF,WAAW,IAAI,SAAS,WAAW,eAAe,GAAG;AACnD;AAAA,IACF,WAAW,IAAI,SAAS,WAAW,OAAO,GAAG;AAC3C;AAAA,IACF,MAAO,OAAM,IAAI,MAAM,+BAA+B;AAEtD,UAAM,SAAS,IAAI,gBAAgB,aAAa,MAAM,GAAG,EAAE,CAAC,CAAC;AAE7D,UAAM,WAAW;AAAA,MACf,KAAK;AAAA,MACL,GAAG,OAAO,IAAI,GAAG,KAAK;AAAA,MACtB,GAAG,OAAO,IAAI,GAAG,KAAK;AAAA,MACtB,GAAG,OAAO,IAAI,GAAG,KAAK;AAAA,MACtB,YAAY,OAAO,IAAI,YAAY,KAAK;AAAA,IAC1C;AAEA,WAAO,CAAC,UAAU,EAAE;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,2BACX,MACyC;AACzC,WAAO,MAAM,KAAK,QAAQ,QAAQ;AAAA,MAChC,GAAG;AAAA,MACH;AAAA,MACA,MAAM;AAAA,QACJ,KAAK,KAAK,KAAK;AAAA,QACf,GAAG;AAAA,QACH,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,GAAG;AAAA,MACL;AAAA,IACF,CAAsC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBACL,UACA,oBAC+C;AAC/C,QAAI;AAEJ,QAAI;AACF,mBAAa,KAAK,MAAM,QAAQ;AAAA,IAClC,QAAQ;AACN,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAEF,WAAO,KAAK,kBAAkB,WAAW,KAAK,kBAAkB;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBACL,UACA,oBAC+C;AAC/C,UAAM,QAAQ,SAAS,MAAM,oBAAoB;AAEjD,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iCAAiC;AAE7D,QAAI,SAAS,MAAM,CAAC,EAAE,MAAM,CAAC;AAE7B,aAAS,OAAO,WAAW,mBAAmB,OAAO;AACrD,aAAS,OAAO,WAAW,qBAAqB,OAAO;AAEvD,QAAI;AAEJ,QAAI;AACF,cAAQ,IAAI,MAAM;AAClB,WAAK,KAAK,MAAM,MAAM;AAAA,IACxB,QAAQ;AACN,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,QAAI;AAEJ,YAAQ,GAAG,GAAG;AAAA,MACZ,KAAK;AACH;AACA;AAAA,MACF,KAAK;AACH;AACA;AAAA,MACF,KAAK;AACH,cAAM,IAAI,MAAM,wCAAwC;AAAA,MAC1D;AACE,cAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,WAAO;AAAA,MACL;AAAA,QACE,GAAG,GAAG,GAAG,SAAS,KAAK;AAAA,QACvB,GAAG,GAAG,EAAE,SAAS;AAAA,QACjB,GAAG,GAAG;AAAA,QACN,KAAK;AAAA,QACL,YAAY,GAAG;AAAA,MACjB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,wBACL,MACA,oBACsE;AACtE,QAAI,qBAAqB,KAAK,IAAI,GAAG;AACnC,YAAM,CAAC,MAAM,EAAE,IAAI,KAAK,mBAAmB,MAAM,kBAAkB;AACnE,aAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IACxB,WAAW,mBAAmB,KAAK,IAAI,GAAG;AACxC,YAAM,CAAC,MAAM,EAAE,IAAI,KAAK,mBAAmB,MAAM,kBAAkB;AACnE,aAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IACxB;AAEA,WAAO,CAAC,OAAO,MAAM,IAAI;AAAA,EAC3B;AACF;;;AC3MO,IAAM,gBAAN,cAA4B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3C,YAAY,KAAsB;AAChC,QAAI,CAAC,KAAK,QAAS,KAAI,UAAU;AAEjC,UAAM;AAAA,MACJ,GAAG;AAAA,MACH,SAAS,IAAI;AAAA,IACf,CAA2B;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,gBACX,MACoC;AACpC,WAAO,MAAM,KAAK,QAAQ,QAAQ,IAAI;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,oBACX,MACsC;AACtC,WAAO,MAAM,KAAK,QAAQ,gBAAgB,IAAI;AAAA,EAChD;AACF;","names":["ProductType","TTags"]}