shop-client 3.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +912 -0
- package/dist/checkout.d.mts +31 -0
- package/dist/checkout.d.ts +31 -0
- package/dist/checkout.js +115 -0
- package/dist/checkout.js.map +1 -0
- package/dist/checkout.mjs +7 -0
- package/dist/checkout.mjs.map +1 -0
- package/dist/chunk-2KBOKOAD.mjs +177 -0
- package/dist/chunk-2KBOKOAD.mjs.map +1 -0
- package/dist/chunk-BWKBRM2Z.mjs +136 -0
- package/dist/chunk-BWKBRM2Z.mjs.map +1 -0
- package/dist/chunk-O4BPIIQ6.mjs +503 -0
- package/dist/chunk-O4BPIIQ6.mjs.map +1 -0
- package/dist/chunk-QCTICSBE.mjs +398 -0
- package/dist/chunk-QCTICSBE.mjs.map +1 -0
- package/dist/chunk-QL5OUZGP.mjs +91 -0
- package/dist/chunk-QL5OUZGP.mjs.map +1 -0
- package/dist/chunk-WTK5HUFI.mjs +1287 -0
- package/dist/chunk-WTK5HUFI.mjs.map +1 -0
- package/dist/collections.d.mts +64 -0
- package/dist/collections.d.ts +64 -0
- package/dist/collections.js +540 -0
- package/dist/collections.js.map +1 -0
- package/dist/collections.mjs +9 -0
- package/dist/collections.mjs.map +1 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +3241 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +702 -0
- package/dist/index.mjs.map +1 -0
- package/dist/products.d.mts +63 -0
- package/dist/products.d.ts +63 -0
- package/dist/products.js +1206 -0
- package/dist/products.js.map +1 -0
- package/dist/products.mjs +9 -0
- package/dist/products.mjs.map +1 -0
- package/dist/store-CJVUz2Yb.d.mts +608 -0
- package/dist/store-CJVUz2Yb.d.ts +608 -0
- package/dist/store.d.mts +1 -0
- package/dist/store.d.ts +1 -0
- package/dist/store.js +698 -0
- package/dist/store.js.map +1 -0
- package/dist/store.mjs +9 -0
- package/dist/store.mjs.map +1 -0
- package/dist/utils/rate-limit.d.mts +25 -0
- package/dist/utils/rate-limit.d.ts +25 -0
- package/dist/utils/rate-limit.js +203 -0
- package/dist/utils/rate-limit.js.map +1 -0
- package/dist/utils/rate-limit.mjs +11 -0
- package/dist/utils/rate-limit.mjs.map +1 -0
- package/package.json +116 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/products.ts","../src/ai/enrich.ts"],"sourcesContent":["import { filter, isNonNullish } from \"remeda\";\nimport {\n classifyProduct,\n enrichProduct,\n generateSEOContent as generateSEOContentLLM,\n} from \"./ai/enrich\";\nimport type { StoreInfo } from \"./store\";\nimport type {\n CurrencyCode,\n Product,\n ProductClassification,\n SEOContent,\n ShopifyProduct,\n ShopifySingleProduct,\n} from \"./types\";\nimport { formatPrice } from \"./utils/func\";\nimport { rateLimitedFetch } from \"./utils/rate-limit\";\n\n/**\n * Interface for product operations\n */\nexport interface ProductOperations {\n /**\n * Fetches all products from the store across all pages.\n */\n all(options?: { currency?: CurrencyCode }): Promise<Product[] | null>;\n\n /**\n * Fetches products with pagination support.\n */\n paginated(options?: {\n page?: number;\n limit?: number;\n currency?: CurrencyCode;\n }): Promise<Product[] | null>;\n\n /**\n * Finds a specific product by its handle.\n */\n find(\n productHandle: string,\n options?: { currency?: CurrencyCode }\n ): Promise<Product | null>;\n\n /**\n * Finds a product by handle and enriches its content using LLM.\n * Requires an OpenAI API key via options.apiKey or process.env.OPENAI_API_KEY.\n */\n enriched(\n productHandle: string,\n options?: {\n apiKey?: string;\n useGfm?: boolean;\n inputType?: \"markdown\" | \"html\";\n model?: string;\n outputFormat?: \"markdown\" | \"json\";\n }\n ): Promise<Product | null>;\n classify(\n productHandle: string,\n options?: { apiKey?: string; model?: string }\n ): Promise<ProductClassification | null>;\n\n /**\n * Generate SEO and marketing content for a product.\n */\n generateSEOContent(\n productHandle: string,\n options?: { apiKey?: string; model?: string }\n ): Promise<SEOContent | null>;\n\n /**\n * Fetches products that are showcased/featured on the store's homepage.\n */\n showcased(): Promise<Product[]>;\n\n /**\n * Creates a filter map of variant options and their distinct values from all products.\n */\n filter(): Promise<Record<string, string[]> | null>;\n}\n\n/**\n * Creates product operations for a store instance\n */\nexport function createProductOperations(\n baseUrl: string,\n storeDomain: string,\n fetchProducts: (page: number, limit: number) => Promise<Product[] | null>,\n productsDto: (products: ShopifyProduct[]) => Product[] | null,\n productDto: (product: ShopifySingleProduct) => Product,\n getStoreInfo: () => Promise<StoreInfo>,\n findProduct: (handle: string) => Promise<Product | null>\n): ProductOperations {\n // Use shared formatter from utils\n\n function applyCurrencyOverride(\n product: Product,\n currency: CurrencyCode\n ): Product {\n const priceMin = product.priceMin ?? product.price ?? 0;\n const priceMax = product.priceMax ?? product.price ?? 0;\n const compareAtMin =\n product.compareAtPriceMin ?? product.compareAtPrice ?? 0;\n return {\n ...product,\n currency,\n localizedPricing: {\n currency,\n priceFormatted: formatPrice(priceMin, currency),\n priceMinFormatted: formatPrice(priceMin, currency),\n priceMaxFormatted: formatPrice(priceMax, currency),\n compareAtPriceFormatted: formatPrice(compareAtMin, currency),\n },\n };\n }\n\n function maybeOverrideProductsCurrency(\n products: Product[] | null,\n currency?: CurrencyCode\n ): Product[] | null {\n if (!products || !currency) return products;\n return products.map((p) => applyCurrencyOverride(p, currency));\n }\n\n const operations: ProductOperations = {\n /**\n * Fetches all products from the store across all pages.\n *\n * @returns {Promise<Product[] | null>} Array of all products or null if error occurs\n *\n * @throws {Error} When there's a network error or API failure\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const allProducts = await shop.products.all();\n *\n * console.log(`Found ${allProducts?.length} products`);\n * allProducts?.forEach(product => {\n * console.log(product.title, product.price);\n * });\n * ```\n */\n all: async (options?: {\n currency?: CurrencyCode;\n }): Promise<Product[] | null> => {\n const limit = 250;\n const allProducts: Product[] = [];\n\n async function fetchAll() {\n let currentPage = 1;\n\n while (true) {\n const products = await fetchProducts(currentPage, limit);\n\n if (!products || products.length === 0 || products.length < limit) {\n if (products && products.length > 0) {\n allProducts.push(...products);\n }\n break;\n }\n\n allProducts.push(...products);\n currentPage++;\n }\n return allProducts;\n }\n\n try {\n const products = await fetchAll();\n return maybeOverrideProductsCurrency(products, options?.currency);\n } catch (error) {\n console.error(\"Failed to fetch all products:\", storeDomain, error);\n throw error;\n }\n },\n\n /**\n * Fetches products with pagination support.\n *\n * @param options - Pagination options\n * @param options.page - Page number (default: 1)\n * @param options.limit - Number of products per page (default: 250, max: 250)\n *\n * @returns {Promise<Product[] | null>} Array of products for the specified page or null if error occurs\n *\n * @throws {Error} When there's a network error or API failure\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://example.myshopify.com');\n *\n * // Get first page with default limit (250)\n * const firstPage = await shop.products.paginated();\n *\n * // Get second page with custom limit\n * const secondPage = await shop.products.paginated({ page: 2, limit: 50 });\n * ```\n */\n paginated: async (options?: {\n page?: number;\n limit?: number;\n currency?: CurrencyCode;\n }): Promise<Product[] | null> => {\n const page = options?.page ?? 1;\n const limit = Math.min(options?.limit ?? 250, 250);\n const url = `${baseUrl}products.json?limit=${limit}&page=${page}`;\n\n try {\n const response = await rateLimitedFetch(url);\n if (!response.ok) {\n console.error(\n `HTTP error! status: ${response.status} for ${storeDomain} page ${page}`\n );\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = (await response.json()) as {\n products: ShopifyProduct[];\n };\n if (data.products.length === 0) {\n return [];\n }\n const normalized = productsDto(data.products);\n return maybeOverrideProductsCurrency(normalized, options?.currency);\n } catch (error) {\n console.error(\n `Error fetching products for ${storeDomain} page ${page} with limit ${limit}:`,\n error\n );\n return null;\n }\n },\n\n /**\n * Finds a specific product by its handle.\n *\n * @param productHandle - The product handle (URL slug) to search for\n *\n * @returns {Promise<Product | null>} The product if found, null if not found\n *\n * @throws {Error} When the handle is invalid or there's a network error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n *\n * // Find product by handle\n * const product = await shop.products.find('awesome-t-shirt');\n *\n * if (product) {\n * console.log(product.title, product.price);\n * console.log('Available variants:', product.variants.length);\n * }\n *\n * // Handle with query string\n * const productWithVariant = await shop.products.find('t-shirt?variant=123');\n * ```\n */\n find: async (\n productHandle: string,\n options?: { currency?: CurrencyCode }\n ): Promise<Product | null> => {\n // Validate product handle\n if (!productHandle || typeof productHandle !== \"string\") {\n throw new Error(\"Product handle is required and must be a string\");\n }\n\n try {\n let qs: string | null = null;\n if (productHandle.includes(\"?\")) {\n const parts = productHandle.split(\"?\");\n const handlePart = parts[0] ?? productHandle;\n const qsPart = parts[1] ?? null;\n productHandle = handlePart;\n qs = qsPart;\n }\n\n // Sanitize handle - remove potentially dangerous characters\n const sanitizedHandle = productHandle\n .trim()\n .replace(/[^a-zA-Z0-9\\-_]/g, \"\");\n if (!sanitizedHandle) {\n throw new Error(\"Invalid product handle format\");\n }\n\n // Check handle length (reasonable limits)\n if (sanitizedHandle.length > 255) {\n throw new Error(\"Product handle is too long\");\n }\n\n // Resolve canonical handle via HTML redirect if handle has changed\n let finalHandle = sanitizedHandle;\n try {\n const htmlResp = await rateLimitedFetch(\n `${baseUrl}products/${encodeURIComponent(sanitizedHandle)}`\n );\n if (htmlResp.ok) {\n const finalUrl = htmlResp.url;\n if (finalUrl) {\n const pathname = new URL(finalUrl).pathname.replace(/\\/$/, \"\");\n const parts = pathname.split(\"/\").filter(Boolean);\n const idx = parts.indexOf(\"products\");\n const maybeHandle = idx >= 0 ? parts[idx + 1] : undefined;\n if (typeof maybeHandle === \"string\" && maybeHandle.length) {\n finalHandle = maybeHandle;\n }\n }\n }\n } catch {\n // Ignore redirect resolution errors and proceed with original handle\n }\n\n const url = `${baseUrl}products/${encodeURIComponent(finalHandle)}.js${qs ? `?${qs}` : \"\"}`;\n const response = await rateLimitedFetch(url);\n\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const product = (await response.json()) as ShopifySingleProduct;\n const productData = productDto(product);\n return options?.currency\n ? applyCurrencyOverride(productData, options.currency)\n : productData;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `Error fetching product ${productHandle}:`,\n baseUrl,\n error.message\n );\n }\n throw error;\n }\n },\n\n /**\n * Enrich a product by generating merged markdown from body_html and product page.\n * Adds `enriched_content` to the returned product.\n */\n enriched: async (\n productHandle: string,\n options?: {\n apiKey?: string;\n useGfm?: boolean;\n inputType?: \"markdown\" | \"html\";\n model?: string;\n outputFormat?: \"markdown\" | \"json\";\n }\n ): Promise<Product | null> => {\n if (!productHandle || typeof productHandle !== \"string\") {\n throw new Error(\"Product handle is required and must be a string\");\n }\n\n const apiKey = options?.apiKey || process.env.OPENROUTER_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY.\"\n );\n }\n\n // Reuse find() for validation and normalized product\n const baseProduct = await operations.find(productHandle);\n if (!baseProduct) {\n return null;\n }\n\n // Use the normalized handle from the found product\n const handle = baseProduct.handle;\n const enriched = await enrichProduct(storeDomain, handle, {\n apiKey,\n useGfm: options?.useGfm,\n inputType: options?.inputType,\n model: options?.model,\n outputFormat: options?.outputFormat,\n });\n\n return {\n ...baseProduct,\n enriched_content: enriched.mergedMarkdown,\n };\n },\n classify: async (\n productHandle: string,\n options?: { apiKey?: string; model?: string }\n ): Promise<ProductClassification | null> => {\n if (!productHandle || typeof productHandle !== \"string\") {\n throw new Error(\"Product handle is required and must be a string\");\n }\n const apiKey = options?.apiKey || process.env.OPENROUTER_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY.\"\n );\n }\n const enrichedProduct = await operations.enriched(productHandle, {\n apiKey,\n inputType: \"html\",\n model: options?.model,\n outputFormat: \"json\",\n });\n if (!enrichedProduct || !enrichedProduct.enriched_content) return null;\n\n let productContent = enrichedProduct.enriched_content;\n try {\n const obj = JSON.parse(enrichedProduct.enriched_content);\n const lines: string[] = [];\n if (obj.title && typeof obj.title === \"string\")\n lines.push(`Title: ${obj.title}`);\n if (obj.description && typeof obj.description === \"string\")\n lines.push(`Description: ${obj.description}`);\n if (Array.isArray(obj.materials) && obj.materials.length)\n lines.push(`Materials: ${obj.materials.join(\", \")}`);\n if (Array.isArray(obj.care) && obj.care.length)\n lines.push(`Care: ${obj.care.join(\", \")}`);\n if (obj.fit && typeof obj.fit === \"string\")\n lines.push(`Fit: ${obj.fit}`);\n if (obj.returnPolicy && typeof obj.returnPolicy === \"string\")\n lines.push(`ReturnPolicy: ${obj.returnPolicy}`);\n productContent = lines.join(\"\\n\");\n } catch {\n // keep as-is if not JSON\n }\n\n const classification = await classifyProduct(productContent, {\n apiKey,\n model: options?.model,\n });\n return classification;\n },\n\n generateSEOContent: async (\n productHandle: string,\n options?: { apiKey?: string; model?: string }\n ): Promise<SEOContent | null> => {\n if (!productHandle || typeof productHandle !== \"string\") {\n throw new Error(\"Product handle is required and must be a string\");\n }\n const apiKey = options?.apiKey || process.env.OPENROUTER_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY.\"\n );\n }\n\n const baseProduct = await operations.find(productHandle);\n if (!baseProduct) return null;\n\n const payload = {\n title: baseProduct.title,\n description: baseProduct.bodyHtml || undefined,\n vendor: baseProduct.vendor,\n price: baseProduct.price,\n tags: baseProduct.tags,\n };\n\n const seo = await generateSEOContentLLM(payload, {\n apiKey,\n model: options?.model,\n });\n return seo;\n },\n\n /**\n * Fetches products that are showcased/featured on the store's homepage.\n *\n * @returns {Promise<Product[]>} Array of showcased products found on the homepage\n *\n * @throws {Error} When there's a network error or API failure\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const showcasedProducts = await shop.products.showcased();\n *\n * console.log(`Found ${showcasedProducts.length} showcased products`);\n * showcasedProducts.forEach(product => {\n * console.log(`Featured: ${product.title} - ${product.price}`);\n * });\n * ```\n */\n showcased: async () => {\n const storeInfo = await getStoreInfo();\n const products = await Promise.all(\n storeInfo.showcase.products.map((productHandle: string) =>\n findProduct(productHandle)\n )\n );\n return filter(products, isNonNullish);\n },\n\n /**\n * Creates a filter map of variant options and their distinct values from all products.\n *\n * @returns {Promise<Record<string, string[]> | null>} Map of option names to their distinct values or null if error occurs\n *\n * @throws {Error} When there's a network error or API failure\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const filters = await shop.products.filter();\n *\n * console.log('Available filters:', filters);\n * // Output: { \"Size\": [\"S\", \"M\", \"L\", \"XL\"], \"Color\": [\"Red\", \"Blue\", \"Green\"] }\n *\n * // Use filters for UI components\n * Object.entries(filters || {}).forEach(([optionName, values]) => {\n * console.log(`${optionName}: ${values.join(', ')}`);\n * });\n * ```\n */\n filter: async (): Promise<Record<string, string[]> | null> => {\n try {\n // Use the existing all() method to get all products across all pages\n const products = await operations.all();\n if (!products || products.length === 0) {\n return {};\n }\n\n // Create a map to store option names and their distinct values\n const filterMap: Record<string, Set<string>> = {};\n\n // Process each product and its variants\n products.forEach((product) => {\n if (product.variants && product.variants.length > 0) {\n // Process product options\n if (product.options && product.options.length > 0) {\n product.options.forEach((option) => {\n const lowercaseOptionName = option.name.toLowerCase();\n if (!filterMap[lowercaseOptionName]) {\n filterMap[lowercaseOptionName] = new Set();\n }\n // Add all values from this option (converted to lowercase)\n option.values.forEach((value) => {\n const trimmed = value?.trim();\n if (trimmed) {\n let set = filterMap[lowercaseOptionName];\n if (!set) {\n set = new Set<string>();\n filterMap[lowercaseOptionName] = set;\n }\n set.add(trimmed.toLowerCase());\n }\n });\n });\n }\n\n // Also process individual variant options as fallback\n product.variants.forEach((variant) => {\n if (variant.option1) {\n const optionName = (\n product.options?.[0]?.name || \"Option 1\"\n ).toLowerCase();\n let set1 = filterMap[optionName];\n if (!set1) {\n set1 = new Set<string>();\n filterMap[optionName] = set1;\n }\n set1.add(variant.option1.trim().toLowerCase());\n }\n\n if (variant.option2) {\n const optionName = (\n product.options?.[1]?.name || \"Option 2\"\n ).toLowerCase();\n let set2 = filterMap[optionName];\n if (!set2) {\n set2 = new Set<string>();\n filterMap[optionName] = set2;\n }\n set2.add(variant.option2.trim().toLowerCase());\n }\n\n if (variant.option3) {\n const optionName = (\n product.options?.[2]?.name || \"Option 3\"\n ).toLowerCase();\n if (!filterMap[optionName]) {\n filterMap[optionName] = new Set();\n }\n filterMap[optionName].add(variant.option3.trim().toLowerCase());\n }\n });\n }\n });\n\n // Convert Sets to sorted arrays (values are already lowercase and unique due to Set)\n const result: Record<string, string[]> = {};\n Object.entries(filterMap).forEach(([optionName, valueSet]) => {\n result[optionName] = Array.from(valueSet).sort();\n });\n\n return result;\n } catch (error) {\n console.error(\"Failed to create product filters:\", storeDomain, error);\n throw error;\n }\n },\n };\n\n return operations;\n}\n","import TurndownService from \"turndown\";\n// @ts-expect-error\nimport { gfm } from \"turndown-plugin-gfm\";\nimport type {\n ProductClassification,\n SEOContent,\n ShopifySingleProduct,\n} from \"../types\";\nimport { rateLimitedFetch } from \"../utils/rate-limit\";\n\nfunction ensureOpenRouter(apiKey?: string) {\n const key = apiKey || process.env.OPENROUTER_API_KEY;\n if (!key) {\n throw new Error(\n \"Missing OpenRouter API key. Set OPENROUTER_API_KEY or pass apiKey.\"\n );\n }\n return key;\n}\n\nfunction normalizeDomainToBase(domain: string): string {\n // Accept both bare domains (example.com) and full URLs (https://example.com)\n if (domain.startsWith(\"http://\") || domain.startsWith(\"https://\")) {\n try {\n const u = new URL(domain);\n return `${u.protocol}//${u.hostname}`;\n } catch {\n // Fallback to https\n return domain;\n }\n }\n return `https://${domain}`;\n}\n\nexport interface EnrichedProductResult {\n bodyHtml: string;\n pageHtml: string;\n extractedMainHtml: string;\n mergedMarkdown: string;\n}\n\n/**\n * Fetch Shopify Product AJAX API\n * /products/{handle}.js\n */\nexport async function fetchAjaxProduct(\n domain: string,\n handle: string\n): Promise<ShopifySingleProduct> {\n const base = normalizeDomainToBase(domain);\n const url = `${base}/products/${handle}.js`;\n const res = await rateLimitedFetch(url);\n if (!res.ok) throw new Error(`Failed to fetch AJAX product: ${url}`);\n const data: ShopifySingleProduct = await res.json();\n return data;\n}\n\n/**\n * Fetch full product page HTML\n */\nexport async function fetchProductPage(\n domain: string,\n handle: string\n): Promise<string> {\n const base = normalizeDomainToBase(domain);\n const url = `${base}/products/${handle}`;\n const res = await rateLimitedFetch(url);\n if (!res.ok) throw new Error(`Failed to fetch product page: ${url}`);\n return res.text();\n}\n\n/**\n * Extract the main Shopify product section WITHOUT cheerio\n * Uses regex + indexing (fast & reliable)\n */\nexport function extractMainSection(html: string): string | null {\n const startMatch = html.match(\n /<section[^>]*id=\"shopify-section-template--.*?__main\"[^>]*>/\n );\n\n if (!startMatch) return null;\n\n const startIndex = html.indexOf(startMatch[0]);\n if (startIndex === -1) return null;\n\n const endIndex = html.indexOf(\"</section>\", startIndex);\n if (endIndex === -1) return null;\n\n return html.substring(startIndex, endIndex + \"</section>\".length);\n}\n\n/**\n * Convert HTML → Clean Markdown using Turndown\n * Includes Shopify cleanup rules + GFM support\n */\nexport function htmlToMarkdown(\n html: string | null,\n options?: { useGfm?: boolean }\n): string {\n if (!html) return \"\";\n\n const td = new TurndownService({\n headingStyle: \"atx\",\n codeBlockStyle: \"fenced\",\n bulletListMarker: \"-\",\n emDelimiter: \"*\",\n strongDelimiter: \"**\",\n linkStyle: \"inlined\",\n });\n\n // Enable GitHub flavored markdown\n const useGfm = options?.useGfm ?? true;\n if (useGfm) {\n td.use(gfm);\n }\n\n // Remove noise: scripts, styles, theme blocks\n [\"script\", \"style\", \"nav\", \"footer\"].forEach((tag) => {\n td.remove((node) => node.nodeName?.toLowerCase() === tag);\n });\n\n // Remove Shopify-specific junk\n const removeByClass = (className: string) =>\n td.remove((node: any) => {\n const cls =\n typeof node.getAttribute === \"function\"\n ? node.getAttribute(\"class\") || \"\"\n : \"\";\n return (cls as string).split(/\\s+/).includes(className);\n });\n [\n \"product-form\",\n \"shopify-payment-button\",\n \"shopify-payment-buttons\",\n \"product__actions\",\n \"product__media-wrapper\",\n \"loox-rating\",\n \"jdgm-widget\",\n \"stamped-reviews\",\n ].forEach(removeByClass);\n\n // Remove UI input elements\n [\"button\", \"input\", \"select\", \"label\"].forEach((tag) => {\n td.remove((node) => node.nodeName?.toLowerCase() === tag);\n });\n\n // Remove add-to-cart + quantity controls\n [\"quantity-selector\", \"product-atc-wrapper\"].forEach(removeByClass);\n\n return td.turndown(html);\n}\n\n/**\n * Merge the two markdown sources using OpenAI GPT\n */\nexport async function mergeWithLLM(\n bodyInput: string,\n pageInput: string,\n options?: {\n apiKey?: string;\n inputType?: \"markdown\" | \"html\";\n model?: string;\n outputFormat?: \"markdown\" | \"json\";\n }\n): Promise<string> {\n const inputType = options?.inputType ?? \"markdown\";\n const bodyLabel = inputType === \"html\" ? \"BODY HTML\" : \"BODY MARKDOWN\";\n const pageLabel = inputType === \"html\" ? \"PAGE HTML\" : \"PAGE MARKDOWN\";\n const prompt =\n options?.outputFormat === \"json\"\n ? `You are extracting structured buyer-useful information from Shopify product content.\n\nInputs:\n1) ${bodyLabel}: ${inputType === \"html\" ? \"Raw Shopify product body_html\" : \"Cleaned version of Shopify product body_html\"}\n2) ${pageLabel}: ${inputType === \"html\" ? \"Raw product page HTML (main section)\" : \"Extracted product page HTML converted to markdown\"}\n\nReturn ONLY valid JSON (no markdown, no code fences) with this shape:\n{\n \"title\": null | string,\n \"description\": null | string,\n \"materials\": string[] | [],\n \"care\": string[] | [],\n \"fit\": null | string,\n \"images\": null | string[],\n \"returnPolicy\": null | string\n}\n\nRules:\n- Do not invent facts; if a field is unavailable, use null or []\n- Prefer concise, factual statements\n - Do NOT include product gallery/hero images in \"images\"; include only documentation images like size charts or measurement guides. If none, set \"images\": null.\n\n${bodyLabel}:\n${bodyInput}\n\n${pageLabel}:\n${pageInput}\n`\n : `\nYou are enriching a Shopify product for a modern shopping-discovery app.\n\nInputs:\n1) ${bodyLabel}: ${inputType === \"html\" ? \"Raw Shopify product body_html\" : \"Cleaned version of Shopify product body_html\"}\n2) ${pageLabel}: ${inputType === \"html\" ? \"Raw product page HTML (main section)\" : \"Extracted product page HTML converted to markdown\"}\n\nYour tasks:\n- Merge them into a single clean markdown document\n- Remove duplicate content\n- Remove product images\n- Remove UI text, buttons, menus, review widgets, theme junk\n- Remove product options\n- Keep only available buyer-useful info: features, materials, care, fit, size chart, return policy, size chart, care instructions\n- Include image of size-chart if present\n- Don't include statements like information not available.\n- Maintain structured headings (## Description, ## Materials, etc.)\n- Output ONLY markdown (no commentary)\n\n${bodyLabel}:\n${bodyInput}\n\n${pageLabel}:\n${pageInput}\n`;\n const apiKey = ensureOpenRouter(options?.apiKey);\n\n // Default model via environment or safe fallback (OpenRouter slug)\n const defaultModel = process.env.OPENROUTER_MODEL || \"openai/gpt-4o-mini\";\n const model = options?.model ?? defaultModel;\n\n // OpenRouter path only\n const result = await callOpenRouter(model, prompt, apiKey);\n if (options?.outputFormat === \"json\") {\n const cleaned = result.replace(/```json|```/g, \"\").trim();\n // Validate shape early to fail fast on malformed JSON responses\n const obj = safeParseJson(cleaned);\n if (!obj.ok) {\n throw new Error(`LLM returned invalid JSON: ${obj.error}`);\n }\n const schema = validateStructuredJson(obj.value);\n if (!schema.ok) {\n throw new Error(`LLM JSON schema invalid: ${schema.error}`);\n }\n\n // Sanitize any returned image URLs to avoid product gallery/hero images\n const value = obj.value as any;\n if (Array.isArray(value.images)) {\n const filtered = value.images.filter((url: string) => {\n if (typeof url !== \"string\") return false;\n const u = url.toLowerCase();\n // Common Shopify product image patterns to exclude\n const productPatterns = [\n \"cdn.shopify.com\",\n \"/products/\",\n \"%2Fproducts%2F\",\n \"_large\",\n \"_grande\",\n \"_1024x1024\",\n \"_2048x\",\n ];\n const looksLikeProductImage = productPatterns.some((p) =>\n u.includes(p)\n );\n return !looksLikeProductImage;\n });\n value.images = filtered.length > 0 ? filtered : null;\n }\n return JSON.stringify(value);\n }\n return result;\n}\n\n// Runtime JSON parse helper with descriptive errors\nfunction safeParseJson(\n input: string\n): { ok: true; value: unknown } | { ok: false; error: string } {\n try {\n const v = JSON.parse(input);\n return { ok: true, value: v };\n } catch (err: any) {\n return { ok: false, error: err?.message || \"Failed to parse JSON\" };\n }\n}\n\n// Validate relaxed schema of structured product JSON from LLM\nfunction validateStructuredJson(\n obj: unknown\n): { ok: true } | { ok: false; error: string } {\n if (!obj || typeof obj !== \"object\" || Array.isArray(obj)) {\n return { ok: false, error: \"Top-level must be a JSON object\" };\n }\n const o = obj as any;\n\n // Optional fields must match expected types when present\n if (\"title\" in o && !(o.title === null || typeof o.title === \"string\")) {\n return { ok: false, error: \"title must be null or string\" };\n }\n if (\n \"description\" in o &&\n !(o.description === null || typeof o.description === \"string\")\n ) {\n return { ok: false, error: \"description must be null or string\" };\n }\n if (\"fit\" in o && !(o.fit === null || typeof o.fit === \"string\")) {\n return { ok: false, error: \"fit must be null or string\" };\n }\n if (\n \"returnPolicy\" in o &&\n !(o.returnPolicy === null || typeof o.returnPolicy === \"string\")\n ) {\n return { ok: false, error: \"returnPolicy must be null or string\" };\n }\n\n const validateStringArray = (\n arr: unknown,\n field: string\n ): { ok: true } | { ok: false; error: string } => {\n if (!Array.isArray(arr))\n return { ok: false, error: `${field} must be an array` };\n for (const item of arr) {\n if (typeof item !== \"string\")\n return { ok: false, error: `${field} items must be strings` };\n }\n return { ok: true };\n };\n\n if (\"materials\" in o) {\n const res = validateStringArray(o.materials, \"materials\");\n if (!res.ok) return res;\n }\n if (\"care\" in o) {\n const res = validateStringArray(o.care, \"care\");\n if (!res.ok) return res;\n }\n\n if (\"images\" in o) {\n if (!(o.images === null || Array.isArray(o.images))) {\n return { ok: false, error: \"images must be null or an array\" };\n }\n if (Array.isArray(o.images)) {\n const res = validateStringArray(o.images, \"images\");\n if (!res.ok) return res;\n }\n }\n\n return { ok: true };\n}\n\n// OpenRouter handler (OpenAI-compatible payload, single key to access many models)\nasync function callOpenRouter(\n model: string,\n prompt: string,\n apiKey: string\n): Promise<string> {\n // Offline fallback for environments without network egress.\n // Enable by setting OPENROUTER_OFFLINE=1.\n if (process.env.OPENROUTER_OFFLINE === \"1\") {\n return mockOpenRouterResponse(prompt);\n }\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n };\n const referer = process.env.OPENROUTER_SITE_URL || process.env.SITE_URL;\n const title = process.env.OPENROUTER_APP_TITLE || \"Shop Search\";\n if (referer) headers[\"HTTP-Referer\"] = referer;\n if (title) headers[\"X-Title\"] = title;\n\n const buildPayload = (m: string) => ({\n model: m,\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.2,\n });\n\n // Use Quickstart-recommended base: https://openrouter.ai/api/v1\n // Allow override via OPENROUTER_BASE_URL\n const base = (\n process.env.OPENROUTER_BASE_URL || \"https://openrouter.ai/api/v1\"\n ).replace(/\\/$/, \"\");\n const endpoints = [`${base}/chat/completions`];\n\n // Prepare fallback models\n const fallbackEnv = (process.env.OPENROUTER_FALLBACK_MODELS || \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n const defaultModel = process.env.OPENROUTER_MODEL || \"openai/gpt-4o-mini\";\n const modelsToTry = Array.from(\n new Set([model, ...fallbackEnv, defaultModel])\n ).filter(Boolean);\n\n let lastErrorText = \"\";\n for (const m of modelsToTry) {\n for (const url of endpoints) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15000);\n const response = await rateLimitedFetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(buildPayload(m)),\n signal: controller.signal,\n });\n clearTimeout(timeout);\n if (!response.ok) {\n const text = await response.text();\n // If server error, try next model; otherwise capture and continue to next endpoint/model\n lastErrorText = text || `${url}: HTTP ${response.status}`;\n // Small delay before trying next\n await new Promise((r) => setTimeout(r, 300));\n continue;\n }\n const data = await response.json();\n const content = data?.choices?.[0]?.message?.content;\n if (typeof content === \"string\") return content;\n // If content missing, still capture and try fallback\n lastErrorText = JSON.stringify(data);\n await new Promise((r) => setTimeout(r, 200));\n } catch (err: any) {\n lastErrorText = `${url}: ${err?.message || String(err)}`;\n await new Promise((r) => setTimeout(r, 200));\n }\n }\n }\n throw new Error(`OpenRouter request failed: ${lastErrorText}`);\n}\n\n// Generate a deterministic offline response tailored to the prompt.\nfunction mockOpenRouterResponse(prompt: string): string {\n const p = prompt.toLowerCase();\n // Classification prompt contains \"keys:\" section\n if (p.includes(\"return only valid json\") && p.includes('\"audience\":')) {\n return JSON.stringify({\n audience: \"generic\",\n vertical: \"clothing\",\n category: null,\n subCategory: null,\n });\n }\n\n // Structured merge prompt contains \"with this shape:\" section\n if (p.includes(\"return only valid json\") && p.includes('\"materials\":')) {\n return JSON.stringify({\n title: null,\n description: null,\n materials: [],\n care: [],\n fit: null,\n images: null,\n returnPolicy: null,\n });\n }\n\n // Markdown merge fallback\n return [\n \"## Description\",\n \"Offline merge of product body and page.\",\n \"\",\n \"## Materials\",\n \"- Not available\",\n ].join(\"\\n\");\n}\n\n/**\n * MAIN WORKFLOW\n */\nexport async function enrichProduct(\n domain: string,\n handle: string,\n options?: {\n apiKey?: string;\n useGfm?: boolean;\n inputType?: \"markdown\" | \"html\";\n model?: string;\n outputFormat?: \"markdown\" | \"json\";\n }\n): Promise<EnrichedProductResult> {\n // STEP 1: Fetch Shopify single product (AJAX) and use its description\n const ajaxProduct = await fetchAjaxProduct(domain, handle);\n const bodyHtml = ajaxProduct.description || \"\";\n\n // STEP 2: Full product page HTML\n const pageHtml = await fetchProductPage(domain, handle);\n\n // STEP 3: Extract main section\n const extractedHtml = extractMainSection(pageHtml);\n\n // STEP 4: Prepare inputs based on desired input type\n const inputType = options?.inputType ?? \"markdown\";\n const bodyInput =\n inputType === \"html\"\n ? bodyHtml\n : htmlToMarkdown(bodyHtml, { useGfm: options?.useGfm });\n const pageInput =\n inputType === \"html\"\n ? extractedHtml || pageHtml\n : htmlToMarkdown(extractedHtml, { useGfm: options?.useGfm });\n\n // STEP 5: Merge using LLM\n const mergedMarkdown = await mergeWithLLM(bodyInput, pageInput, {\n apiKey: options?.apiKey,\n inputType,\n model: options?.model,\n outputFormat: options?.outputFormat,\n });\n\n // If JSON output requested, further sanitize images using Shopify REST data\n if (options?.outputFormat === \"json\") {\n try {\n const obj = JSON.parse(mergedMarkdown);\n if (obj && Array.isArray(obj.images)) {\n const productImageCandidates: string[] = [];\n // Collect featured_image (string URL)\n if (ajaxProduct.featured_image) {\n productImageCandidates.push(String(ajaxProduct.featured_image));\n }\n // Collect images (string[])\n if (Array.isArray(ajaxProduct.images)) {\n for (const img of ajaxProduct.images) {\n if (typeof img === \"string\" && img.length > 0) {\n productImageCandidates.push(img);\n }\n }\n }\n // Collect media[].src\n if (Array.isArray(ajaxProduct.media)) {\n for (const m of ajaxProduct.media) {\n if (m?.src) productImageCandidates.push(String(m.src));\n }\n }\n // Collect variants[].featured_image?.src\n if (Array.isArray(ajaxProduct.variants)) {\n for (const v of ajaxProduct.variants) {\n const fi = v?.featured_image;\n if (fi?.src) productImageCandidates.push(String(fi.src));\n }\n }\n\n const productSet = new Set(\n productImageCandidates.map((u) => String(u).toLowerCase())\n );\n const filtered = obj.images.filter((url: string) => {\n if (typeof url !== \"string\") return false;\n const u = url.toLowerCase();\n if (productSet.has(u)) return false;\n // Also exclude common Shopify product image patterns\n const productPatterns = [\n \"cdn.shopify.com\",\n \"/products/\",\n \"%2Fproducts%2F\",\n \"_large\",\n \"_grande\",\n \"_1024x1024\",\n \"_2048x\",\n ];\n const looksLikeProductImage = productPatterns.some((p) =>\n u.includes(p)\n );\n return !looksLikeProductImage;\n });\n obj.images = filtered.length > 0 ? filtered : null;\n const sanitized = JSON.stringify(obj);\n return {\n bodyHtml,\n pageHtml,\n extractedMainHtml: extractedHtml || \"\",\n mergedMarkdown: sanitized,\n };\n }\n } catch {\n // fallthrough to default return\n }\n }\n\n return {\n bodyHtml,\n pageHtml,\n extractedMainHtml: extractedHtml || \"\",\n mergedMarkdown,\n };\n}\n\n/**\n * Classify product content into a three-tier hierarchy using LLM.\n * Returns strictly validated JSON with audience, vertical, and optional category/subCategory.\n */\nexport async function classifyProduct(\n productContent: string,\n options?: { apiKey?: string; model?: string }\n): Promise<ProductClassification> {\n const apiKey = ensureOpenRouter(options?.apiKey);\n const defaultModel = process.env.OPENROUTER_MODEL || \"openai/gpt-4o-mini\";\n const model = options?.model ?? defaultModel;\n\n const prompt = `Classify the following product using a three-tiered hierarchy:\n\nProduct Content:\n${productContent}\n\nClassification Rules:\n1. First determine the vertical (main product category)\n2. Then determine the category (specific type within that vertical)\n3. Finally determine the subCategory (sub-type within that category)\n\nVertical must be one of: clothing, beauty, accessories, home-decor, food-and-beverages\nAudience must be one of: adult_male, adult_female, kid_male, kid_female, generic\n\nHierarchy Examples:\n- Clothing → tops → t-shirts\n- Clothing → footwear → sneakers\n- Beauty → skincare → moisturizers\n- Accessories → bags → backpacks\n- Home-decor → furniture → chairs\n- Food-and-beverages → snacks → chips\n\nIMPORTANT CONSTRAINTS:\n- Category must be relevant to the chosen vertical\n- subCategory must be relevant to both vertical and category\n- subCategory must be a single word or hyphenated words (no spaces)\n- subCategory should NOT be material (e.g., \"cotton\", \"leather\") or color (e.g., \"red\", \"blue\")\n- Focus on product type/function, not attributes\n\nIf you're not confident about category or sub-category, you can leave them optional.\n\nReturn ONLY valid JSON (no markdown, no code fences) with keys:\n{\n \"audience\": \"adult_male\" | \"adult_female\" | \"kid_male\" | \"kid_female\" | \"generic\",\n \"vertical\": \"clothing\" | \"beauty\" | \"accessories\" | \"home-decor\" | \"food-and-beverages\",\n \"category\": null | string,\n \"subCategory\": null | string\n}`;\n\n const raw = await callOpenRouter(model, prompt, apiKey);\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n\n // Parse and validate\n const parsed = safeParseJson(cleaned);\n if (!parsed.ok) {\n throw new Error(`LLM returned invalid JSON: ${parsed.error}`);\n }\n const validated = validateClassification(parsed.value);\n if (!validated.ok) {\n throw new Error(`LLM JSON schema invalid: ${validated.error}`);\n }\n return validated.value as ProductClassification;\n}\n\n// Validate product classification schema\nfunction validateClassification(\n obj: unknown\n): { ok: true; value: ProductClassification } | { ok: false; error: string } {\n if (!obj || typeof obj !== \"object\" || Array.isArray(obj)) {\n return { ok: false, error: \"Top-level must be a JSON object\" };\n }\n const o = obj as any;\n\n const audienceValues = [\n \"adult_male\",\n \"adult_female\",\n \"kid_male\",\n \"kid_female\",\n \"generic\",\n ] as const;\n if (typeof o.audience !== \"string\" || !audienceValues.includes(o.audience)) {\n return {\n ok: false,\n error:\n \"audience must be one of: adult_male, adult_female, kid_male, kid_female, generic\",\n };\n }\n\n const verticalValues = [\n \"clothing\",\n \"beauty\",\n \"accessories\",\n \"home-decor\",\n \"food-and-beverages\",\n ] as const;\n if (typeof o.vertical !== \"string\" || !verticalValues.includes(o.vertical)) {\n return {\n ok: false,\n error:\n \"vertical must be one of: clothing, beauty, accessories, home-decor, food-and-beverages\",\n };\n }\n\n // Optional fields\n if (\n \"category\" in o &&\n !(o.category === null || typeof o.category === \"string\")\n ) {\n return { ok: false, error: \"category must be null or string\" };\n }\n if (\n \"subCategory\" in o &&\n !(o.subCategory === null || typeof o.subCategory === \"string\")\n ) {\n return { ok: false, error: \"subCategory must be null or string\" };\n }\n\n // Enforce subCategory format when provided: single word or hyphenated (no spaces)\n if (typeof o.subCategory === \"string\") {\n const sc = o.subCategory.trim();\n if (!/^[A-Za-z0-9-]+$/.test(sc)) {\n return {\n ok: false,\n error: \"subCategory must be single word or hyphenated, no spaces\",\n };\n }\n }\n\n return {\n ok: true,\n value: {\n audience: o.audience,\n vertical: o.vertical,\n category:\n typeof o.category === \"string\" ? o.category : (o.category ?? null),\n subCategory:\n typeof o.subCategory === \"string\"\n ? o.subCategory\n : (o.subCategory ?? null),\n },\n };\n}\n\n/**\n * Generate SEO and marketing content for a product. Returns strictly validated JSON.\n */\nexport async function generateSEOContent(\n product: {\n title: string;\n description?: string;\n vendor?: string;\n price?: number;\n tags?: string[];\n },\n options?: { apiKey?: string; model?: string }\n): Promise<SEOContent> {\n const apiKey = ensureOpenRouter(options?.apiKey);\n const defaultModel = process.env.OPENROUTER_MODEL || \"openai/gpt-4o-mini\";\n const model = options?.model ?? defaultModel;\n\n // Offline deterministic mock\n if (process.env.OPENROUTER_OFFLINE === \"1\") {\n const baseTags = Array.isArray(product.tags)\n ? product.tags.slice(0, 6)\n : [];\n const titlePart = product.title.trim().slice(0, 50);\n const vendorPart = (product.vendor || \"\").trim();\n const pricePart =\n typeof product.price === \"number\" ? `$${product.price}` : \"\";\n const metaTitle = vendorPart ? `${titlePart} | ${vendorPart}` : titlePart;\n const metaDescription =\n `Discover ${product.title}. ${pricePart ? `Priced at ${pricePart}. ` : \"\"}Crafted to delight customers with quality and style.`.slice(\n 0,\n 160\n );\n const shortDescription = `${product.title} — ${vendorPart || \"Premium\"} quality, designed to impress.`;\n const longDescription =\n product.description ||\n `Introducing ${product.title}, combining performance and style for everyday use.`;\n const marketingCopy = `Get ${product.title} today${pricePart ? ` for ${pricePart}` : \"\"}. Limited availability — don’t miss out!`;\n const res: SEOContent = {\n metaTitle,\n metaDescription,\n shortDescription,\n longDescription,\n tags: baseTags.length ? baseTags : [\"new\", \"featured\", \"popular\"],\n marketingCopy,\n };\n const validated = validateSEOContent(res);\n if (!validated.ok)\n throw new Error(`Offline SEO content invalid: ${validated.error}`);\n return validated.value;\n }\n\n const prompt = `Generate SEO-optimized content for this product:\\n\\nTitle: ${product.title}\\nDescription: ${product.description || \"N/A\"}\\nVendor: ${product.vendor || \"N/A\"}\\nPrice: ${typeof product.price === \"number\" ? `$${product.price}` : \"N/A\"}\\nTags: ${Array.isArray(product.tags) && product.tags.length ? product.tags.join(\", \") : \"N/A\"}\\n\\nCreate compelling, SEO-friendly content that will help this product rank well and convert customers.\\n\\nReturn ONLY valid JSON (no markdown, no code fences) with keys: {\\n \"metaTitle\": string,\\n \"metaDescription\": string,\\n \"shortDescription\": string,\\n \"longDescription\": string,\\n \"tags\": string[],\\n \"marketingCopy\": string\\n}`;\n\n const raw = await callOpenRouter(model, prompt, apiKey);\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n const parsed = safeParseJson(cleaned);\n if (!parsed.ok) {\n throw new Error(`LLM returned invalid JSON: ${parsed.error}`);\n }\n const validated = validateSEOContent(parsed.value);\n if (!validated.ok) {\n throw new Error(`LLM JSON schema invalid: ${validated.error}`);\n }\n return validated.value as SEOContent;\n}\n\nfunction validateSEOContent(\n obj: unknown\n): { ok: true; value: SEOContent } | { ok: false; error: string } {\n if (!obj || typeof obj !== \"object\" || Array.isArray(obj)) {\n return { ok: false, error: \"Top-level must be a JSON object\" };\n }\n const o = obj as any;\n const requiredStrings = [\n \"metaTitle\",\n \"metaDescription\",\n \"shortDescription\",\n \"longDescription\",\n \"marketingCopy\",\n ];\n for (const key of requiredStrings) {\n if (typeof o[key] !== \"string\" || !o[key].trim()) {\n return { ok: false, error: `${key} must be a non-empty string` };\n }\n }\n if (!Array.isArray(o.tags)) {\n return { ok: false, error: \"tags must be an array\" };\n }\n for (const t of o.tags) {\n if (typeof t !== \"string\")\n return { ok: false, error: \"tags items must be strings\" };\n }\n // Light heuristic: metaTitle ~50-80 chars, metaDescription ~80-180 chars (do not hard-fail)\n return {\n ok: true,\n value: {\n metaTitle: String(o.metaTitle),\n metaDescription: String(o.metaDescription),\n shortDescription: String(o.shortDescription),\n longDescription: String(o.longDescription),\n tags: o.tags as string[],\n marketingCopy: String(o.marketingCopy),\n },\n };\n}\n\n/**\n * Determine store type (primary vertical and audience) from store information.\n * Accepts flexible input for showcase products/collections (titles or handles) and returns\n * strictly validated `vertical` and `audience` values.\n */\nexport async function determineStoreType(\n storeInfo: {\n title: string;\n description?: string | null;\n showcase: {\n products:\n | Array<{ title: string; productType?: string | null }>\n | string[];\n collections: Array<{ title: string }> | string[];\n };\n },\n options?: { apiKey?: string; model?: string }\n): Promise<\n Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n >\n> {\n const apiKey = ensureOpenRouter(options?.apiKey);\n const defaultModel = process.env.OPENROUTER_MODEL || \"openai/gpt-4o-mini\";\n const model = options?.model ?? defaultModel;\n\n // Normalize showcase items to titles for readable prompt content\n const productLines = (\n Array.isArray(storeInfo.showcase.products)\n ? storeInfo.showcase.products.slice(0, 10).map((p: any) => {\n if (typeof p === \"string\") return `- ${p}`;\n const pt =\n typeof p?.productType === \"string\" && p.productType.trim()\n ? p.productType\n : \"N/A\";\n return `- ${String(p?.title || \"N/A\")}: ${pt}`;\n })\n : []\n ) as string[];\n const collectionLines = (\n Array.isArray(storeInfo.showcase.collections)\n ? storeInfo.showcase.collections.slice(0, 5).map((c: any) => {\n if (typeof c === \"string\") return `- ${c}`;\n return `- ${String(c?.title || \"N/A\")}`;\n })\n : []\n ) as string[];\n\n const storeContent = `Store Title: ${storeInfo.title}\nStore Description: ${storeInfo.description ?? \"N/A\"}\n\nSample Products:\\n${productLines.join(\"\\n\") || \"- N/A\"}\n\nSample Collections:\\n${collectionLines.join(\"\\n\") || \"- N/A\"}`;\n const textNormalized =\n `${storeInfo.title} ${storeInfo.description ?? \"\"} ${productLines.join(\" \")} ${collectionLines.join(\" \")}`.toLowerCase();\n\n // Offline deterministic mock with light heuristics\n if (process.env.OPENROUTER_OFFLINE === \"1\") {\n const text =\n `${storeInfo.title} ${storeInfo.description ?? \"\"} ${productLines.join(\" \")} ${collectionLines.join(\" \")}`.toLowerCase();\n const verticalKeywords: Record<string, RegExp> = {\n clothing:\n /(dress|shirt|pant|jean|hoodie|tee|t[- ]?shirt|sneaker|apparel|clothing)/,\n beauty: /(skincare|moisturizer|serum|beauty|cosmetic|makeup)/,\n accessories:\n /(bag|belt|watch|wallet|accessor(y|ies)|sunglasses|jewell?ery)/,\n \"home-decor\": /(sofa|chair|table|decor|home|candle|lamp|rug)/,\n \"food-and-beverages\":\n /(snack|food|beverage|coffee|tea|chocolate|gourmet)/,\n };\n // Use strict word-boundary matching to avoid false positives like \"boyfriend\" or \"girlfriend\"\n const audienceKeywords: Record<string, RegExp> = {\n kid: /(\\bkid\\b|\\bchild\\b|\\bchildren\\b|\\btoddler\\b|\\bboy\\b|\\bgirl\\b)/,\n kid_male: /\\bboys\\b|\\bboy\\b/,\n kid_female: /\\bgirls\\b|\\bgirl\\b/,\n adult_male: /\\bmen\\b|\\bmale\\b|\\bman\\b|\\bmens\\b/,\n adult_female: /\\bwomen\\b|\\bfemale\\b|\\bwoman\\b|\\bwomens\\b/,\n };\n const audiences: ProductClassification[\"audience\"][] = [];\n if (audienceKeywords.kid?.test(text)) {\n if (audienceKeywords.kid_male?.test(text)) audiences.push(\"kid_male\");\n if (audienceKeywords.kid_female?.test(text)) audiences.push(\"kid_female\");\n if (\n !audienceKeywords.kid_male?.test(text) &&\n !audienceKeywords.kid_female?.test(text)\n )\n audiences.push(\"generic\");\n } else {\n if (audienceKeywords.adult_male?.test(text)) audiences.push(\"adult_male\");\n if (audienceKeywords.adult_female?.test(text))\n audiences.push(\"adult_female\");\n if (audiences.length === 0) audiences.push(\"generic\");\n }\n\n // Determine verticals present\n const verticals = Object.entries(verticalKeywords)\n .filter(([, rx]) => rx.test(text))\n .map(([k]) => k as ProductClassification[\"vertical\"]);\n if (verticals.length === 0) verticals.push(\"accessories\");\n\n // Derive categories from showcase product titles\n const allTitles = productLines.join(\" \").toLowerCase();\n const categoryMap: Record<string, RegExp> = {\n shirts: /(shirt|t[- ]?shirt|tee)/,\n pants: /(pant|trouser|chino)/,\n shorts: /shorts?/,\n jeans: /jeans?/,\n dresses: /dress/,\n skincare: /(serum|moisturizer|skincare|cream)/,\n accessories: /(belt|watch|wallet|bag)/,\n footwear: /(sneaker|shoe|boot)/,\n decor: /(candle|lamp|rug|sofa|chair|table)/,\n beverages: /(coffee|tea|chocolate)/,\n };\n const categories = Object.entries(categoryMap)\n .filter(([, rx]) => rx.test(allTitles))\n .map(([name]) => name);\n const defaultCategories = categories.length ? categories : [\"general\"];\n\n const breakdown: Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n > = {};\n for (const aud of audiences) {\n breakdown[aud] = breakdown[aud] || {};\n for (const v of verticals) {\n breakdown[aud]![v] = Array.from(new Set(defaultCategories));\n }\n }\n // Apply pruning even in offline mode to drop un-signaled audiences/verticals\n return pruneBreakdownForSignals(breakdown, textNormalized);\n }\n\n const prompt = `Analyze this store and build a multi-audience breakdown of verticals and categories.\nStore Information:\n${storeContent}\n\nReturn ONLY valid JSON (no markdown, no code fences) using this shape:\n{\n \"adult_male\": { \"clothing\": [\"shirts\", \"pants\"], \"accessories\": [\"belts\"] },\n \"adult_female\": { \"beauty\": [\"skincare\"], \"clothing\": [\"dresses\"] },\n \"generic\": { \"clothing\": [\"t-shirts\"] }\n}\n\nRules:\n- Keys MUST be audience: \"adult_male\" | \"adult_female\" | \"kid_male\" | \"kid_female\" | \"generic\".\n- Nested keys MUST be vertical: \"clothing\" | \"beauty\" | \"accessories\" | \"home-decor\" | \"food-and-beverages\".\n- Values MUST be non-empty arrays of category strings.\n`;\n\n const raw = await callOpenRouter(model, prompt, apiKey);\n const cleaned = raw.replace(/```json|```/g, \"\").trim();\n const parsed = safeParseJson(cleaned);\n if (!parsed.ok) {\n throw new Error(`LLM returned invalid JSON: ${parsed.error}`);\n }\n const validated = validateStoreTypeBreakdown(parsed.value);\n if (!validated.ok) {\n throw new Error(`LLM JSON schema invalid: ${validated.error}`);\n }\n return pruneBreakdownForSignals(validated.value, textNormalized);\n}\n\nfunction validateStoreTypeBreakdown(obj: unknown):\n | {\n ok: true;\n value: Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n >;\n }\n | { ok: false; error: string } {\n if (!obj || typeof obj !== \"object\" || Array.isArray(obj)) {\n return {\n ok: false,\n error: \"Top-level must be an object keyed by audience\",\n };\n }\n const audienceKeys = [\n \"adult_male\",\n \"adult_female\",\n \"kid_male\",\n \"kid_female\",\n \"generic\",\n ] as const;\n const verticalKeys = [\n \"clothing\",\n \"beauty\",\n \"accessories\",\n \"home-decor\",\n \"food-and-beverages\",\n ] as const;\n const o = obj as Record<string, unknown>;\n const out: Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n > = {};\n const keys = Object.keys(o);\n if (keys.length === 0) {\n return { ok: false, error: \"At least one audience key is required\" };\n }\n for (const aKey of keys) {\n if (!audienceKeys.includes(aKey as any)) {\n return { ok: false, error: `Invalid audience key: ${aKey}` };\n }\n const vObj = o[aKey];\n if (!vObj || typeof vObj !== \"object\" || Array.isArray(vObj)) {\n return {\n ok: false,\n error: `Audience ${aKey} must map to an object of verticals`,\n };\n }\n const vOut: Partial<Record<ProductClassification[\"vertical\"], string[]>> =\n {};\n for (const vKey of Object.keys(vObj as Record<string, unknown>)) {\n if (!verticalKeys.includes(vKey as any)) {\n return {\n ok: false,\n error: `Invalid vertical key ${vKey} for audience ${aKey}`,\n };\n }\n const cats = (vObj as any)[vKey];\n if (\n !Array.isArray(cats) ||\n cats.length === 0 ||\n !cats.every((c) => typeof c === \"string\" && c.trim())\n ) {\n return {\n ok: false,\n error: `Vertical ${vKey} for audience ${aKey} must be a non-empty array of strings`,\n };\n }\n vOut[vKey as ProductClassification[\"vertical\"]] = cats.map((c: string) =>\n c.trim()\n );\n }\n out[aKey as ProductClassification[\"audience\"]] = vOut;\n }\n return { ok: true, value: out };\n}\n\nexport function pruneBreakdownForSignals(\n breakdown: Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n >,\n text: string\n): Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n> {\n const audienceKeywords: Record<string, RegExp> = {\n kid: /(\\bkid\\b|\\bchild\\b|\\bchildren\\b|\\btoddler\\b|\\bboy\\b|\\bgirl\\b)/,\n kid_male: /\\bboys\\b|\\bboy\\b/,\n kid_female: /\\bgirls\\b|\\bgirl\\b/,\n adult_male: /\\bmen\\b|\\bmale\\b|\\bman\\b|\\bmens\\b/,\n adult_female: /\\bwomen\\b|\\bfemale\\b|\\bwoman\\b|\\bwomens\\b/,\n };\n const verticalKeywords: Record<string, RegExp> = {\n clothing:\n /(dress|shirt|pant|jean|hoodie|tee|t[- ]?shirt|sneaker|apparel|clothing)/,\n beauty: /(skincare|moisturizer|serum|beauty|cosmetic|makeup)/,\n accessories:\n /(bag|belt|watch|wallet|accessor(y|ies)|sunglasses|jewell?ery)/,\n // Tighten home-decor detection to avoid matching generic \"Home\" nav labels\n // and other unrelated uses. Require specific furniture/decor terms or phrases.\n \"home-decor\":\n /(sofa|chair|table|candle|lamp|rug|furniture|home[- ]?decor|homeware|housewares|living\\s?room|dining\\s?table|bed(?:room)?|wall\\s?(art|mirror|clock))/,\n \"food-and-beverages\": /(snack|food|beverage|coffee|tea|chocolate|gourmet)/,\n };\n\n const signaledAudiences = new Set<ProductClassification[\"audience\"]>();\n if (audienceKeywords.kid?.test(text)) {\n if (audienceKeywords.kid_male?.test(text))\n signaledAudiences.add(\"kid_male\");\n if (audienceKeywords.kid_female?.test(text))\n signaledAudiences.add(\"kid_female\");\n if (\n !audienceKeywords.kid_male?.test(text) &&\n !audienceKeywords.kid_female?.test(text)\n )\n signaledAudiences.add(\"generic\");\n } else {\n if (audienceKeywords.adult_male?.test(text))\n signaledAudiences.add(\"adult_male\");\n if (audienceKeywords.adult_female?.test(text))\n signaledAudiences.add(\"adult_female\");\n if (signaledAudiences.size === 0) signaledAudiences.add(\"generic\");\n }\n\n const signaledVerticals = new Set<ProductClassification[\"vertical\"]>(\n Object.entries(verticalKeywords)\n .filter(([, rx]) => rx.test(text))\n .map(([k]) => k as ProductClassification[\"vertical\"]) || []\n );\n if (signaledVerticals.size === 0) signaledVerticals.add(\"accessories\");\n\n const pruned: Partial<\n Record<\n ProductClassification[\"audience\"],\n Partial<Record<ProductClassification[\"vertical\"], string[]>>\n >\n > = {};\n for (const [audience, verticals] of Object.entries(breakdown)) {\n const a = audience as ProductClassification[\"audience\"];\n if (!signaledAudiences.has(a)) continue;\n const vOut: Partial<Record<ProductClassification[\"vertical\"], string[]>> =\n {};\n for (const [vertical, categories] of Object.entries(verticals || {})) {\n const v = vertical as ProductClassification[\"vertical\"];\n if (!signaledVerticals.has(v)) continue;\n vOut[v] = categories as string[];\n }\n if (Object.keys(vOut).length > 0) {\n pruned[a] = vOut;\n }\n }\n\n // If pruning removes all audiences, fall back to generic with any verticals present in original or signaled\n if (Object.keys(pruned).length === 0) {\n const vOut: Partial<Record<ProductClassification[\"vertical\"], string[]>> =\n {};\n for (const v of Array.from(signaledVerticals)) {\n vOut[v] = [\"general\"];\n }\n pruned.generic = vOut;\n }\n\n // Remove generic when adult audiences exist and have verticals\n const adultHasData =\n (pruned.adult_male && Object.keys(pruned.adult_male).length > 0) ||\n (pruned.adult_female && Object.keys(pruned.adult_female).length > 0);\n if (adultHasData) {\n delete pruned.generic;\n }\n\n return pruned;\n}\n"],"mappings":"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","names":["_a","validated"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { f as Collection, y as CurrencyCode, c as Product, e as ShopifyCollection, g as StoreInfo } from './store-CJVUz2Yb.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for collection operations
|
|
5
|
+
*/
|
|
6
|
+
interface CollectionOperations {
|
|
7
|
+
/**
|
|
8
|
+
* Fetches all collections from the store across all pages.
|
|
9
|
+
*/
|
|
10
|
+
all(): Promise<Collection[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Fetches collections with pagination support.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Pagination options
|
|
15
|
+
* @param options.page - Page number (default: 1)
|
|
16
|
+
* @param options.limit - Number of collections per page (default: 10, max: 250)
|
|
17
|
+
*
|
|
18
|
+
* @returns {Promise<Collection[] | null>} Array of collections for the page or null if error occurs
|
|
19
|
+
*/
|
|
20
|
+
paginated(options?: {
|
|
21
|
+
page?: number;
|
|
22
|
+
limit?: number;
|
|
23
|
+
}): Promise<Collection[] | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Finds a specific collection by its handle.
|
|
26
|
+
*/
|
|
27
|
+
find(collectionHandle: string): Promise<Collection | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Fetches collections that are showcased/featured on the store's homepage.
|
|
30
|
+
*/
|
|
31
|
+
showcased(): Promise<Collection[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Product-related methods for fetching products from specific collections.
|
|
34
|
+
*/
|
|
35
|
+
products: {
|
|
36
|
+
/**
|
|
37
|
+
* Fetches products from a specific collection with pagination support.
|
|
38
|
+
*/
|
|
39
|
+
paginated(collectionHandle: string, options?: {
|
|
40
|
+
page?: number;
|
|
41
|
+
limit?: number;
|
|
42
|
+
currency?: CurrencyCode;
|
|
43
|
+
}): Promise<Product[] | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Fetches all products from a specific collection.
|
|
46
|
+
*/
|
|
47
|
+
all(collectionHandle: string, options?: {
|
|
48
|
+
currency?: CurrencyCode;
|
|
49
|
+
}): Promise<Product[] | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Fetches all product slugs from a specific collection.
|
|
52
|
+
*/
|
|
53
|
+
slugs(collectionHandle: string): Promise<string[] | null>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates collection operations for a store instance
|
|
58
|
+
*/
|
|
59
|
+
declare function createCollectionOperations(baseUrl: string, storeDomain: string, fetchCollections: (page: number, limit: number) => Promise<Collection[] | null>, collectionsDto: (collections: ShopifyCollection[]) => Collection[], fetchPaginatedProductsFromCollection: (collectionHandle: string, options?: {
|
|
60
|
+
page?: number;
|
|
61
|
+
limit?: number;
|
|
62
|
+
}) => Promise<Product[] | null>, getStoreInfo: () => Promise<StoreInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
|
|
63
|
+
|
|
64
|
+
export { type CollectionOperations, createCollectionOperations };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { f as Collection, y as CurrencyCode, c as Product, e as ShopifyCollection, g as StoreInfo } from './store-CJVUz2Yb.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for collection operations
|
|
5
|
+
*/
|
|
6
|
+
interface CollectionOperations {
|
|
7
|
+
/**
|
|
8
|
+
* Fetches all collections from the store across all pages.
|
|
9
|
+
*/
|
|
10
|
+
all(): Promise<Collection[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Fetches collections with pagination support.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Pagination options
|
|
15
|
+
* @param options.page - Page number (default: 1)
|
|
16
|
+
* @param options.limit - Number of collections per page (default: 10, max: 250)
|
|
17
|
+
*
|
|
18
|
+
* @returns {Promise<Collection[] | null>} Array of collections for the page or null if error occurs
|
|
19
|
+
*/
|
|
20
|
+
paginated(options?: {
|
|
21
|
+
page?: number;
|
|
22
|
+
limit?: number;
|
|
23
|
+
}): Promise<Collection[] | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Finds a specific collection by its handle.
|
|
26
|
+
*/
|
|
27
|
+
find(collectionHandle: string): Promise<Collection | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Fetches collections that are showcased/featured on the store's homepage.
|
|
30
|
+
*/
|
|
31
|
+
showcased(): Promise<Collection[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Product-related methods for fetching products from specific collections.
|
|
34
|
+
*/
|
|
35
|
+
products: {
|
|
36
|
+
/**
|
|
37
|
+
* Fetches products from a specific collection with pagination support.
|
|
38
|
+
*/
|
|
39
|
+
paginated(collectionHandle: string, options?: {
|
|
40
|
+
page?: number;
|
|
41
|
+
limit?: number;
|
|
42
|
+
currency?: CurrencyCode;
|
|
43
|
+
}): Promise<Product[] | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Fetches all products from a specific collection.
|
|
46
|
+
*/
|
|
47
|
+
all(collectionHandle: string, options?: {
|
|
48
|
+
currency?: CurrencyCode;
|
|
49
|
+
}): Promise<Product[] | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Fetches all product slugs from a specific collection.
|
|
52
|
+
*/
|
|
53
|
+
slugs(collectionHandle: string): Promise<string[] | null>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates collection operations for a store instance
|
|
58
|
+
*/
|
|
59
|
+
declare function createCollectionOperations(baseUrl: string, storeDomain: string, fetchCollections: (page: number, limit: number) => Promise<Collection[] | null>, collectionsDto: (collections: ShopifyCollection[]) => Collection[], fetchPaginatedProductsFromCollection: (collectionHandle: string, options?: {
|
|
60
|
+
page?: number;
|
|
61
|
+
limit?: number;
|
|
62
|
+
}) => Promise<Product[] | null>, getStoreInfo: () => Promise<StoreInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
|
|
63
|
+
|
|
64
|
+
export { type CollectionOperations, createCollectionOperations };
|