shop-client 3.8.2 → 3.9.0
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 +1 -1
- package/README.md +93 -1
- package/dist/checkout.mjs +1 -7
- package/dist/chunk-6GPWNCDO.mjs +130 -0
- package/dist/chunk-EJO5U4BT.mjs +2 -0
- package/dist/chunk-FFKWCNLU.mjs +1 -0
- package/dist/chunk-KYLPIEU3.mjs +2 -0
- package/dist/chunk-MB2INNNP.mjs +1 -0
- package/dist/chunk-MI7754VX.mjs +2 -0
- package/dist/chunk-SZQPMLZG.mjs +1 -0
- package/dist/collections.d.ts +1 -1
- package/dist/collections.mjs +1 -9
- package/dist/enrich-OZHBXKK6.mjs +1 -0
- package/dist/index.d.ts +24 -6
- package/dist/index.mjs +2 -702
- package/dist/products.d.ts +1 -1
- package/dist/products.mjs +1 -9
- package/dist/{store-CJVUz2Yb.d.mts → store-iQARl6J3.d.ts} +3 -3
- package/dist/store.d.ts +1 -1
- package/dist/store.mjs +1 -9
- package/dist/utils/rate-limit.d.ts +5 -0
- package/dist/utils/rate-limit.mjs +1 -11
- package/package.json +8 -10
- package/dist/checkout.d.mts +0 -31
- package/dist/checkout.js +0 -115
- package/dist/checkout.js.map +0 -1
- package/dist/checkout.mjs.map +0 -1
- package/dist/chunk-2KBOKOAD.mjs +0 -177
- package/dist/chunk-2KBOKOAD.mjs.map +0 -1
- package/dist/chunk-BWKBRM2Z.mjs +0 -136
- package/dist/chunk-BWKBRM2Z.mjs.map +0 -1
- package/dist/chunk-O4BPIIQ6.mjs +0 -503
- package/dist/chunk-O4BPIIQ6.mjs.map +0 -1
- package/dist/chunk-QCTICSBE.mjs +0 -398
- package/dist/chunk-QCTICSBE.mjs.map +0 -1
- package/dist/chunk-QL5OUZGP.mjs +0 -91
- package/dist/chunk-QL5OUZGP.mjs.map +0 -1
- package/dist/chunk-WTK5HUFI.mjs +0 -1287
- package/dist/chunk-WTK5HUFI.mjs.map +0 -1
- package/dist/collections.d.mts +0 -64
- package/dist/collections.js +0 -540
- package/dist/collections.js.map +0 -1
- package/dist/collections.mjs.map +0 -1
- package/dist/index.d.mts +0 -233
- package/dist/index.js +0 -3241
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/products.d.mts +0 -63
- package/dist/products.js +0 -1206
- package/dist/products.js.map +0 -1
- package/dist/products.mjs.map +0 -1
- package/dist/store-CJVUz2Yb.d.ts +0 -608
- package/dist/store.d.mts +0 -1
- package/dist/store.js +0 -698
- package/dist/store.js.map +0 -1
- package/dist/store.mjs.map +0 -1
- package/dist/utils/rate-limit.d.mts +0 -25
- package/dist/utils/rate-limit.js +0 -203
- package/dist/utils/rate-limit.js.map +0 -1
- package/dist/utils/rate-limit.mjs.map +0 -1
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/ai/enrich.ts","../src/utils/rate-limit.ts","../src/ai/determine-store-type.ts","../src/checkout.ts","../src/client/get-info.ts","../src/utils/detect-country.ts","../src/utils/func.ts","../src/collections.ts","../src/dto/collections.dto.ts","../src/dto/products.mapped.ts","../src/products.ts","../src/store.ts"],"sourcesContent":["import { determineStoreTypeForStore } from \"./ai/determine-store-type\";\nimport type { CheckoutOperations } from \"./checkout\";\nimport { createCheckoutOperations } from \"./checkout\";\nimport { getInfoForStore } from \"./client/get-info\";\nimport type { CollectionOperations } from \"./collections\";\nimport { createCollectionOperations } from \"./collections\";\nimport { collectionsDto as dtoCollections } from \"./dto/collections.dto\";\nimport { mapProductDto, mapProductsDto } from \"./dto/products.mapped\";\nimport type { ProductOperations } from \"./products\";\nimport { createProductOperations } from \"./products\";\nimport type { StoreInfo, StoreOperations } from \"./store\";\nimport { createStoreOperations } from \"./store\";\nimport type {\n Collection,\n Product,\n ShopifyCollection,\n ShopifyProduct,\n ShopifySingleProduct,\n StoreTypeBreakdown,\n} from \"./types\";\nimport { generateStoreSlug } from \"./utils/func\";\nimport { rateLimitedFetch } from \"./utils/rate-limit\";\n\n/**\n * A comprehensive Shopify store client for fetching products, collections, and store information.\n *\n * @example\n * ```typescript\n * import { ShopClient } from 'shop-search';\n *\n * const shop = new ShopClient('https://exampleshop.com');\n *\n * // Fetch all products\n * const products = await shop.products.all();\n *\n * // Get store information\n * const storeInfo = await shop.getInfo();\n * ```\n */\nexport class ShopClient {\n private storeDomain: string;\n private baseUrl: string;\n private storeSlug: string;\n private validationCache: Map<string, boolean> = new Map(); // Simple cache for validation results\n private cacheExpiry: number = 5 * 60 * 1000; // 5 minutes cache expiry\n private cacheTimestamps: Map<string, number> = new Map();\n private normalizeImageUrlCache: Map<string, string> = new Map();\n private storeCurrency?: string;\n\n // Public operations interfaces\n public products: ProductOperations;\n public collections: CollectionOperations;\n public checkout: CheckoutOperations;\n public storeOperations: StoreOperations;\n\n /**\n * Creates a new ShopClient instance for interacting with a Shopify store.\n *\n * @param urlPath - The Shopify store URL (e.g., 'https://exampleshop.com' or 'exampleshop.com')\n *\n * @throws {Error} When the URL is invalid or contains malicious patterns\n *\n * @example\n * ```typescript\n * // With full URL\n * const shop = new ShopClient('https://exampleshop.com');\n *\n * // Without protocol (automatically adds https://)\n * const shop = new ShopClient('exampleshop.com');\n *\n * // Works with any Shopify store domain\n * const shop1 = new ShopClient('https://example.myshopify.com');\n * const shop2 = new ShopClient('https://boutique.fashion');\n * ```\n */\n constructor(urlPath: string) {\n // Validate input URL\n if (!urlPath || typeof urlPath !== \"string\") {\n throw new Error(\"Store URL is required and must be a string\");\n }\n\n // Sanitize and validate URL\n let normalizedUrl = urlPath.trim();\n if (\n !normalizedUrl.startsWith(\"http://\") &&\n !normalizedUrl.startsWith(\"https://\")\n ) {\n normalizedUrl = `https://${normalizedUrl}`;\n }\n\n let storeUrl: URL;\n try {\n storeUrl = new URL(normalizedUrl);\n } catch (_error) {\n throw new Error(\"Invalid store URL format\");\n }\n\n // Validate domain format (basic check for Shopify domains)\n const hostname = storeUrl.hostname;\n if (!hostname || hostname.length < 3) {\n throw new Error(\"Invalid domain name\");\n }\n\n // Check for potentially malicious patterns\n if (\n hostname.includes(\"..\") ||\n hostname.includes(\"//\") ||\n hostname.includes(\"@\")\n ) {\n throw new Error(\"Invalid characters in domain name\");\n }\n\n // Validate domain structure - must contain at least one dot for TLD\n if (\n !hostname.includes(\".\") ||\n hostname.startsWith(\".\") ||\n hostname.endsWith(\".\")\n ) {\n throw new Error(\n \"Invalid domain format - must be a valid domain with TLD\"\n );\n }\n\n // Check for valid domain pattern (basic regex)\n const domainPattern =\n /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n if (!domainPattern.test(hostname)) {\n throw new Error(\"Invalid domain format\");\n }\n\n this.storeDomain = `https://${hostname}`;\n let fetchUrl = `https://${hostname}${storeUrl.pathname}`;\n if (!fetchUrl.endsWith(\"/\")) {\n fetchUrl = `${fetchUrl}/`;\n }\n this.baseUrl = fetchUrl;\n\n // Pre-calculate store slug once\n this.storeSlug = generateStoreSlug(this.storeDomain);\n\n // Initialize operations\n this.storeOperations = createStoreOperations({\n baseUrl: this.baseUrl,\n storeDomain: this.storeDomain,\n validateProductExists: this.validateProductExists.bind(this),\n validateCollectionExists: this.validateCollectionExists.bind(this),\n validateLinksInBatches: this.validateLinksInBatches.bind(this),\n handleFetchError: this.handleFetchError.bind(this),\n });\n\n this.products = createProductOperations(\n this.baseUrl,\n this.storeDomain,\n this.fetchProducts.bind(this),\n this.productsDto.bind(this),\n this.productDto.bind(this),\n () => this.getInfo(),\n (handle: string) => this.products.find(handle)\n );\n\n this.collections = createCollectionOperations(\n this.baseUrl,\n this.storeDomain,\n this.fetchCollections.bind(this),\n this.collectionsDto.bind(this),\n this.fetchPaginatedProductsFromCollection.bind(this),\n () => this.getInfo(),\n (handle: string) => this.collections.find(handle)\n );\n\n this.checkout = createCheckoutOperations(this.baseUrl);\n }\n\n /**\n * Optimized image URL normalization with caching\n */\n private normalizeImageUrl(url?: string | null): string {\n if (!url) {\n return \"\";\n }\n\n if (this.normalizeImageUrlCache.has(url)) {\n return this.normalizeImageUrlCache.get(url)!;\n }\n\n const normalized = url.startsWith(\"//\") ? `https:${url}` : url;\n this.normalizeImageUrlCache.set(url, normalized);\n return normalized;\n }\n\n /**\n * Format a price amount (in cents) using the store currency.\n */\n private formatPrice(amountInCents: number): string {\n const currency = this.storeCurrency ?? \"USD\";\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n }).format((amountInCents || 0) / 100);\n } catch {\n const val = (amountInCents || 0) / 100;\n return `${val} ${currency}`;\n }\n }\n\n /**\n * Transform Shopify products to our Product format\n */\n productsDto(products: ShopifyProduct[]): Product[] | null {\n return mapProductsDto(products, {\n storeDomain: this.storeDomain,\n storeSlug: this.storeSlug,\n currency: this.storeCurrency ?? \"USD\",\n normalizeImageUrl: (url) => this.normalizeImageUrl(url),\n formatPrice: (amount) => this.formatPrice(amount),\n });\n }\n\n productDto(product: ShopifySingleProduct): Product {\n return mapProductDto(product, {\n storeDomain: this.storeDomain,\n storeSlug: this.storeSlug,\n currency: this.storeCurrency ?? \"USD\",\n normalizeImageUrl: (url) => this.normalizeImageUrl(url),\n formatPrice: (amount) => this.formatPrice(amount),\n });\n }\n\n collectionsDto(collections: ShopifyCollection[]): Collection[] {\n return dtoCollections(collections) ?? [];\n }\n\n /**\n * Enhanced error handling with context\n */\n private handleFetchError(\n error: unknown,\n context: string,\n url: string\n ): never {\n let errorMessage = `Error ${context}`;\n let statusCode: number | undefined;\n\n if (error instanceof Error) {\n errorMessage += `: ${error.message}`;\n\n // Check if it's a fetch error with response\n if (\"status\" in error) {\n statusCode = error.status as number;\n }\n } else if (typeof error === \"string\") {\n errorMessage += `: ${error}`;\n } else {\n errorMessage += \": Unknown error occurred\";\n }\n\n // Add URL context for debugging\n errorMessage += ` (URL: ${url})`;\n\n // Add status code if available\n if (statusCode) {\n errorMessage += ` (Status: ${statusCode})`;\n }\n\n // Create enhanced error with additional properties\n const enhancedError = new Error(errorMessage);\n (enhancedError as any).context = context;\n (enhancedError as any).url = url;\n (enhancedError as any).statusCode = statusCode;\n (enhancedError as any).originalError = error;\n\n throw enhancedError;\n }\n\n /**\n * Fetch products with pagination\n */\n private async fetchProducts(\n page: number,\n limit: number\n ): Promise<Product[] | null> {\n try {\n const url = `${this.baseUrl}products.json?page=${page}&limit=${limit}`;\n const response = await rateLimitedFetch(url);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data: { products: ShopifyProduct[] } = await response.json();\n return this.productsDto(data.products);\n } catch (error) {\n this.handleFetchError(\n error,\n \"fetching products\",\n `${this.baseUrl}products.json`\n );\n }\n }\n\n /**\n * Fetch collections with pagination\n */\n private async fetchCollections(page: number, limit: number) {\n try {\n const url = `${this.baseUrl}collections.json?page=${page}&limit=${limit}`;\n const response = await rateLimitedFetch(url);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data = await response.json();\n return this.collectionsDto(data.collections);\n } catch (error) {\n this.handleFetchError(\n error,\n \"fetching collections\",\n `${this.baseUrl}collections.json`\n );\n }\n }\n\n /**\n * Fetch paginated products from a specific collection\n */\n private async fetchPaginatedProductsFromCollection(\n collectionHandle: string,\n options: { page?: number; limit?: number } = {}\n ) {\n try {\n const { page = 1, limit = 250 } = options;\n // Resolve canonical collection handle via HTML redirect if handle has changed\n let finalHandle = collectionHandle;\n try {\n const htmlResp = await rateLimitedFetch(\n `${this.baseUrl}collections/${encodeURIComponent(collectionHandle)}`\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(\"collections\");\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 = `${this.baseUrl}collections/${finalHandle}/products.json?page=${page}&limit=${limit}`;\n const response = await rateLimitedFetch(url);\n\n if (!response.ok) {\n if (response.status === 404) {\n return null; // Collection not found\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data: { products: ShopifyProduct[] } = await response.json();\n return this.productsDto(data.products);\n } catch (error) {\n this.handleFetchError(\n error,\n \"fetching products from collection\",\n `${this.baseUrl}collections/${collectionHandle}/products.json`\n );\n }\n }\n\n /**\n * Validate if a product exists (with caching)\n */\n private async validateProductExists(handle: string): Promise<boolean> {\n const cacheKey = `product:${handle}`;\n\n if (this.isCacheValid(cacheKey)) {\n return this.validationCache.get(cacheKey) || false;\n }\n\n try {\n const url = `${this.baseUrl}products/${handle}.js`;\n const response = await rateLimitedFetch(url, { method: \"HEAD\" });\n const exists = response.ok;\n\n this.setCacheValue(cacheKey, exists);\n return exists;\n } catch (_error) {\n this.setCacheValue(cacheKey, false);\n return false;\n }\n }\n\n /**\n * Validate if a collection exists (with caching)\n */\n private async validateCollectionExists(handle: string): Promise<boolean> {\n const cacheKey = `collection:${handle}`;\n\n if (this.isCacheValid(cacheKey)) {\n return this.validationCache.get(cacheKey) || false;\n }\n\n try {\n const url = `${this.baseUrl}collections/${handle}.json`;\n const response = await rateLimitedFetch(url, { method: \"HEAD\" });\n const exists = response.ok;\n\n this.setCacheValue(cacheKey, exists);\n return exists;\n } catch (_error) {\n this.setCacheValue(cacheKey, false);\n return false;\n }\n }\n\n /**\n * Check if cache entry is still valid\n */\n private isCacheValid(key: string): boolean {\n const timestamp = this.cacheTimestamps.get(key);\n if (!timestamp) return false;\n\n return Date.now() - timestamp < this.cacheExpiry;\n }\n\n /**\n * Set cache value with timestamp\n */\n private setCacheValue(key: string, value: boolean): void {\n this.validationCache.set(key, value);\n this.cacheTimestamps.set(key, Date.now());\n }\n\n /**\n * Validate links in batches to avoid overwhelming the server\n */\n private async validateLinksInBatches<T>(\n items: T[],\n validator: (item: T) => Promise<boolean>,\n batchSize = 10\n ): Promise<T[]> {\n const validItems: T[] = [];\n\n for (let i = 0; i < items.length; i += batchSize) {\n const batch = items.slice(i, i + batchSize);\n const validationPromises = batch.map(async (item) => {\n const isValid = await validator(item);\n return isValid ? item : null;\n });\n\n const results = await Promise.all(validationPromises);\n const validBatchItems = results.filter(\n (item): item is NonNullable<typeof item> => item !== null\n );\n validItems.push(...validBatchItems);\n\n // Add small delay between batches to be respectful\n if (i + batchSize < items.length) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n return validItems;\n }\n\n /**\n * Fetches comprehensive store information including metadata, social links, and showcase content.\n *\n * @returns {Promise<StoreInfo>} Store information object containing:\n * - `name` - Store name from meta tags or domain\n * - `domain` - Store domain URL\n * - `slug` - Generated store slug\n * - `title` - Store title from meta tags\n * - `description` - Store description from meta tags\n * - `logoUrl` - Store logo URL from Open Graph or CDN\n * - `socialLinks` - Object with social media links (facebook, twitter, instagram, etc.)\n * - `contactLinks` - Object with contact information (tel, email, contactPage)\n * - `headerLinks` - Array of navigation links from header\n * - `showcase` - Object with featured products and collections from homepage\n * - `jsonLdData` - Structured data from JSON-LD scripts\n * - `techProvider` - Shopify-specific information (walletId, subDomain)\n * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., \"US\", \"GB\")\n *\n * @throws {Error} When the store URL is unreachable or returns an error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const storeInfo = await shop.getInfo();\n *\n * console.log(storeInfo.name); // \"Example Store\"\n * console.log(storeInfo.socialLinks.instagram); // \"https://instagram.com/example\"\n * console.log(storeInfo.showcase.products); // [\"product-handle-1\", \"product-handle-2\"]\n * console.log(storeInfo.country); // \"US\"\n * ```\n */\n async getInfo(): Promise<StoreInfo> {\n try {\n const { info, currencyCode } = await getInfoForStore({\n baseUrl: this.baseUrl,\n storeDomain: this.storeDomain,\n validateProductExists: (handle) => this.validateProductExists(handle),\n validateCollectionExists: (handle) =>\n this.validateCollectionExists(handle),\n validateLinksInBatches: (items, validator, batchSize) =>\n this.validateLinksInBatches(items, validator, batchSize),\n });\n if (typeof currencyCode === \"string\") {\n this.storeCurrency = currencyCode;\n }\n return info;\n } catch (error) {\n this.handleFetchError(error, \"fetching store info\", this.baseUrl);\n }\n }\n\n /**\n * Determine the store's primary vertical and target audience.\n * Uses `getInfo()` internally; no input required.\n */\n async determineStoreType(options?: {\n apiKey?: string;\n model?: string;\n maxShowcaseProducts?: number;\n maxShowcaseCollections?: number;\n }): Promise<StoreTypeBreakdown> {\n try {\n const breakdown = await determineStoreTypeForStore({\n baseUrl: this.baseUrl,\n getInfo: () => this.getInfo(),\n findProduct: (handle: string) => this.products.find(handle),\n apiKey: options?.apiKey,\n model: options?.model,\n maxShowcaseProducts: options?.maxShowcaseProducts,\n maxShowcaseCollections: options?.maxShowcaseCollections,\n });\n return breakdown;\n } catch (error: unknown) {\n throw this.handleFetchError(error, \"determineStoreType\", this.baseUrl);\n }\n }\n}\n\nexport { classifyProduct, generateSEOContent } from \"./ai/enrich\";\nexport type { CheckoutOperations } from \"./checkout\";\nexport type { CollectionOperations } from \"./collections\";\n// Export operation interfaces\nexport type { ProductOperations } from \"./products\";\nexport type { StoreInfo, StoreOperations } from \"./store\";\n// Export all types for external use\n// Classification utility\nexport type * from \"./types\";\nexport { detectShopifyCountry } from \"./utils/detect-country\";\n// Export utility functions\nexport {\n calculateDiscount,\n extractDomainWithoutSuffix,\n generateStoreSlug,\n genProductSlug,\n safeParseDate,\n sanitizeDomain,\n} from \"./utils/func\";\nexport { configureRateLimit } from \"./utils/rate-limit\";\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","export interface RateLimitOptions {\n maxRequestsPerInterval: number; // tokens refilled every interval\n intervalMs: number; // refill period\n maxConcurrency: number; // simultaneous in-flight requests\n}\n\ntype Task<T> = {\n fn: () => Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: any) => void;\n};\n\nclass RateLimiter {\n private options: RateLimitOptions;\n private queue: Task<any>[] = [];\n private tokens: number;\n private inFlight = 0;\n private refillTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: RateLimitOptions) {\n this.options = options;\n this.tokens = options.maxRequestsPerInterval;\n }\n\n private startRefill() {\n if (this.refillTimer) return;\n this.refillTimer = setInterval(() => {\n this.tokens = this.options.maxRequestsPerInterval;\n this.tryRun();\n }, this.options.intervalMs);\n // In some runtimes, timers keep process alive; make it best-effort\n if (\n this.refillTimer &&\n typeof (this.refillTimer as any).unref === \"function\"\n ) {\n (this.refillTimer as any).unref();\n }\n }\n\n private stopRefill() {\n if (this.refillTimer) {\n clearInterval(this.refillTimer);\n this.refillTimer = null;\n }\n }\n\n private ensureRefillStarted() {\n if (!this.refillTimer) {\n this.startRefill();\n }\n }\n\n configure(next: Partial<RateLimitOptions>) {\n this.options = { ...this.options, ...next };\n // Clamp to sensible minimums\n this.options.maxRequestsPerInterval = Math.max(\n 1,\n this.options.maxRequestsPerInterval\n );\n this.options.intervalMs = Math.max(10, this.options.intervalMs);\n this.options.maxConcurrency = Math.max(1, this.options.maxConcurrency);\n }\n\n schedule<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n // Start the timer lazily on first schedule/use\n this.ensureRefillStarted();\n this.queue.push({ fn, resolve, reject });\n this.tryRun();\n });\n }\n\n private tryRun() {\n while (\n this.queue.length > 0 &&\n this.inFlight < this.options.maxConcurrency &&\n this.tokens > 0\n ) {\n const task = this.queue.shift()!;\n this.tokens -= 1;\n this.inFlight += 1;\n\n // Execute task and release slot when done\n Promise.resolve()\n .then(task.fn)\n .then((result) => task.resolve(result))\n .catch((err) => task.reject(err))\n .finally(() => {\n this.inFlight -= 1;\n // Yield to event loop, then continue draining\n setTimeout(() => this.tryRun(), 0);\n });\n }\n }\n}\n\nlet enabled = false;\nconst defaultOptions: RateLimitOptions = {\n maxRequestsPerInterval: 5, // 5 requests\n intervalMs: 1000, // per second\n maxConcurrency: 5, // up to 5 in parallel\n};\n\nconst limiter = new RateLimiter(defaultOptions);\nconst hostLimiters = new Map<string, RateLimiter>();\nconst classLimiters = new Map<string, RateLimiter>();\n\nexport type RateLimitedRequestInit = RequestInit & { rateLimitClass?: string };\n\nfunction getHost(input: RequestInfo | URL): string | undefined {\n try {\n if (typeof input === \"string\") {\n return new URL(input).host;\n }\n if (input instanceof URL) {\n return input.host;\n }\n // Request object or similar\n const url = (input as any).url as string | undefined;\n if (url) {\n return new URL(url).host;\n }\n } catch {\n // ignore parsing errors\n }\n return undefined;\n}\n\nfunction getHostLimiter(host?: string): RateLimiter | undefined {\n if (!host) return undefined;\n // Exact match first\n const exact = hostLimiters.get(host);\n if (exact) return exact;\n // Wildcard suffix match: keys of the form '*.example.com'\n for (const [key, lim] of hostLimiters.entries()) {\n if (key.startsWith(\"*.\") && host.endsWith(key.slice(2))) {\n return lim;\n }\n }\n return undefined;\n}\n\nexport function configureRateLimit(\n options: Partial<RateLimitOptions & { enabled: boolean }> & {\n perHost?: Record<string, Partial<RateLimitOptions>>; // key: host (supports '*.example.com')\n perClass?: Record<string, Partial<RateLimitOptions>>; // key: arbitrary class name\n }\n) {\n if (typeof options.enabled === \"boolean\") {\n enabled = options.enabled;\n }\n const { perHost, perClass } = options;\n const globalOpts: Partial<RateLimitOptions> = {};\n if (typeof options.maxRequestsPerInterval === \"number\") {\n globalOpts.maxRequestsPerInterval = options.maxRequestsPerInterval;\n }\n if (typeof options.intervalMs === \"number\") {\n globalOpts.intervalMs = options.intervalMs;\n }\n if (typeof options.maxConcurrency === \"number\") {\n globalOpts.maxConcurrency = options.maxConcurrency;\n }\n if (Object.keys(globalOpts).length) {\n limiter.configure(globalOpts);\n }\n if (perHost) {\n for (const host of Object.keys(perHost)) {\n const opts = perHost[host]!;\n const existing = hostLimiters.get(host);\n if (existing) {\n existing.configure(opts);\n } else {\n hostLimiters.set(\n host,\n new RateLimiter({\n maxRequestsPerInterval:\n opts.maxRequestsPerInterval ??\n defaultOptions.maxRequestsPerInterval,\n intervalMs: opts.intervalMs ?? defaultOptions.intervalMs,\n maxConcurrency:\n opts.maxConcurrency ?? defaultOptions.maxConcurrency,\n })\n );\n }\n }\n }\n if (perClass) {\n for (const klass of Object.keys(perClass)) {\n const opts = perClass[klass]!;\n const existing = classLimiters.get(klass);\n if (existing) {\n existing.configure(opts);\n } else {\n classLimiters.set(\n klass,\n new RateLimiter({\n maxRequestsPerInterval:\n opts.maxRequestsPerInterval ??\n defaultOptions.maxRequestsPerInterval,\n intervalMs: opts.intervalMs ?? defaultOptions.intervalMs,\n maxConcurrency:\n opts.maxConcurrency ?? defaultOptions.maxConcurrency,\n })\n );\n }\n }\n }\n}\n\nexport async function rateLimitedFetch(\n input: RequestInfo | URL,\n init?: RateLimitedRequestInit\n): Promise<Response> {\n if (!enabled) {\n return fetch(input as any, init);\n }\n const klass = init?.rateLimitClass;\n const byClass = klass ? classLimiters.get(klass) : undefined;\n const byHost = getHostLimiter(getHost(input));\n const eff = byClass ?? byHost ?? limiter;\n return eff.schedule(() => fetch(input as any, init));\n}\n\nexport function getRateLimitStatus() {\n return {\n enabled,\n options: { ...defaultOptions },\n };\n}\n","import type { StoreInfo } from \"../store\";\nimport type { StoreTypeBreakdown } from \"../types\";\nimport { determineStoreType as determineStoreTypeViaLLM } from \"./enrich\";\n\ntype Args = {\n getInfo: () => Promise<StoreInfo>;\n baseUrl: string;\n findProduct: (handle: string) => Promise<unknown>;\n apiKey?: string;\n model?: string;\n maxShowcaseProducts?: number;\n maxShowcaseCollections?: number;\n};\n\n/**\n * Lightweight wrapper for determining store type by delegating to utils/enrich.\n * It first fetches StoreInfo via getInfo, then passes showcase samples\n * directly to the enrichment function.\n */\nexport async function determineStoreTypeForStore(\n args: Args\n): Promise<StoreTypeBreakdown> {\n const info = await args.getInfo();\n\n const maxProducts = Math.max(0, Math.min(50, args.maxShowcaseProducts ?? 10));\n const maxCollections = Math.max(\n 0,\n Math.min(50, args.maxShowcaseCollections ?? 10)\n );\n\n const take = <T>(arr: T[], n: number): T[] => arr.slice(0, Math.max(0, n));\n\n const productsSample = Array.isArray(info.showcase.products)\n ? take(info.showcase.products, maxProducts)\n : [];\n const collectionsSample = Array.isArray(info.showcase.collections)\n ? take(info.showcase.collections, maxCollections)\n : [];\n\n const breakdown = await determineStoreTypeViaLLM(\n {\n title: info.title || info.name,\n description: info.description ?? null,\n showcase: {\n products: productsSample,\n collections: collectionsSample,\n },\n },\n { apiKey: args.apiKey, model: args.model }\n );\n\n return breakdown as StoreTypeBreakdown;\n}\n","/**\n * Interface for checkout operations\n */\nexport interface CheckoutOperations {\n /**\n * Creates a Shopify checkout URL with pre-filled customer information and cart items.\n */\n createUrl(params: {\n email: string;\n items: Array<{ productVariantId: string; quantity: string }>;\n address: {\n firstName: string;\n lastName: string;\n address1: string;\n city: string;\n zip: string;\n country: string;\n province: string;\n phone: string;\n };\n }): string;\n}\n\n/**\n * Creates checkout operations for a store instance\n */\nexport function createCheckoutOperations(baseUrl: string): CheckoutOperations {\n return {\n /**\n * Creates a Shopify checkout URL with pre-filled customer information and cart items.\n *\n * @param params - Checkout parameters\n * @param params.email - Customer's email address (must be valid email format)\n * @param params.items - Array of products to add to cart\n * @param params.items[].productVariantId - Shopify product variant ID\n * @param params.items[].quantity - Quantity as string (must be positive number)\n * @param params.address - Customer's shipping address\n * @param params.address.firstName - Customer's first name\n * @param params.address.lastName - Customer's last name\n * @param params.address.address1 - Street address\n * @param params.address.city - City name\n * @param params.address.zip - Postal/ZIP code\n * @param params.address.country - Country name\n * @param params.address.province - State/Province name\n * @param params.address.phone - Phone number\n *\n * @returns {string} Complete Shopify checkout URL with pre-filled information\n *\n * @throws {Error} When email is invalid, items array is empty, or required address fields are missing\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const checkoutUrl = await shop.checkout.create([\n * { variantId: '123', quantity: 2 },\n * { variantId: '456', quantity: 1 }\n * ]);\n * console.log(checkoutUrl);\n * ```\n */\n createUrl: ({\n email,\n items,\n address,\n }: {\n email: string;\n items: Array<{ productVariantId: string; quantity: string }>;\n address: {\n firstName: string;\n lastName: string;\n address1: string;\n city: string;\n zip: string;\n country: string;\n province: string;\n phone: string;\n };\n }) => {\n // Validate and sanitize inputs\n if (!email || !email.includes(\"@\")) {\n throw new Error(\"Invalid email address\");\n }\n\n if (!items || items.length === 0) {\n throw new Error(\"Items array cannot be empty\");\n }\n\n // Validate items\n for (const item of items) {\n if (!item.productVariantId || !item.quantity) {\n throw new Error(\"Each item must have productVariantId and quantity\");\n }\n // Ensure quantity is a positive number\n const qty = Number.parseInt(item.quantity, 10);\n\n if (Number.isNaN(qty) || qty <= 0) {\n throw new Error(\"Quantity must be a positive number\");\n }\n }\n\n // Validate required address fields\n const requiredFields = [\n \"firstName\",\n \"lastName\",\n \"address1\",\n \"city\",\n \"zip\",\n \"country\",\n ];\n\n for (const field of requiredFields) {\n if (!address[field as keyof typeof address]) {\n throw new Error(`Address field '${field}' is required`);\n }\n }\n\n // Properly encode all URL parameters to prevent injection attacks\n const cartPath = items\n .map(\n (item) =>\n `${encodeURIComponent(item.productVariantId)}:${encodeURIComponent(item.quantity)}`\n )\n .join(\",\");\n\n const params = new URLSearchParams({\n \"checkout[email]\": email,\n \"checkout[shipping_address][first_name]\": address.firstName,\n \"checkout[shipping_address][last_name]\": address.lastName,\n \"checkout[shipping_address][address1]\": address.address1,\n \"checkout[shipping_address][city]\": address.city,\n \"checkout[shipping_address][zip]\": address.zip,\n \"checkout[shipping_address][country]\": address.country,\n \"checkout[shipping_address][province]\": address.province,\n \"checkout[shipping_address][phone]\": address.phone,\n });\n\n return `${baseUrl}cart/${cartPath}?${params.toString()}`;\n },\n };\n}\n","import { unique } from \"remeda\";\nimport type { StoreInfo } from \"../store\";\nimport { detectShopifyCountry } from \"../utils/detect-country\";\nimport {\n extractDomainWithoutSuffix,\n generateStoreSlug,\n sanitizeDomain,\n} from \"../utils/func\";\nimport { rateLimitedFetch } from \"../utils/rate-limit\";\n\ntype Args = {\n baseUrl: string;\n storeDomain: string;\n validateProductExists: (handle: string) => Promise<boolean>;\n validateCollectionExists: (handle: string) => Promise<boolean>;\n validateLinksInBatches: <T>(\n items: T[],\n validator: (item: T) => Promise<boolean>,\n batchSize?: number\n ) => Promise<T[]>;\n};\n\n/**\n * Fetches comprehensive store information including metadata, social links, and showcase content.\n * Returns the structured StoreInfo and detected currency code (if available).\n */\nexport async function getInfoForStore(\n args: Args\n): Promise<{ info: StoreInfo; currencyCode?: string }> {\n const {\n baseUrl,\n storeDomain,\n validateProductExists,\n validateCollectionExists,\n validateLinksInBatches,\n } = args;\n\n const response = await rateLimitedFetch(baseUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n\n const getMetaTag = (name: string) => {\n const regex = new RegExp(\n `<meta[^>]*name=[\"']${name}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const getPropertyMetaTag = (property: string) => {\n const regex = new RegExp(\n `<meta[^>]*property=[\"']${property}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const name =\n getMetaTag(\"og:site_name\") ?? extractDomainWithoutSuffix(baseUrl);\n const title = getMetaTag(\"og:title\") ?? getMetaTag(\"twitter:title\");\n const description =\n getMetaTag(\"description\") || getPropertyMetaTag(\"og:description\");\n\n const shopifyWalletId = getMetaTag(\"shopify-digital-wallet\")?.split(\"/\")[1];\n\n const myShopifySubdomainMatch = html.match(/['\"](.*?\\.myshopify\\.com)['\"]/);\n const myShopifySubdomain = myShopifySubdomainMatch\n ? myShopifySubdomainMatch[1]\n : null;\n\n let logoUrl =\n getPropertyMetaTag(\"og:image\") || getPropertyMetaTag(\"og:image:secure_url\");\n if (!logoUrl) {\n const logoMatch = html.match(\n /<img[^>]+src=[\"']([^\"']+\\/cdn\\/shop\\/[^\"']+)[\"']/\n );\n const matchedUrl = logoMatch?.[1];\n logoUrl = matchedUrl ? matchedUrl.replace(\"http://\", \"https://\") : null;\n } else {\n logoUrl = logoUrl.replace(\"http://\", \"https://\");\n }\n\n const socialLinks: Record<string, string> = {};\n const socialRegex =\n /<a[^>]+href=[\"']([^\"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\\.com[^\"']*)[\"']/g;\n for (const match of html.matchAll(socialRegex)) {\n const str = match[1];\n if (!str) continue;\n let href: string = str;\n try {\n if (href.startsWith(\"//\")) {\n href = `https:${href}`;\n } else if (href.startsWith(\"/\")) {\n href = new URL(href, baseUrl).toString();\n }\n const parsed = new URL(href);\n const domain = parsed.hostname.replace(\"www.\", \"\").split(\".\")[0];\n if (domain) {\n socialLinks[domain] = parsed.toString();\n }\n } catch {\n // Skip invalid URLs without failing\n }\n }\n\n const contactLinks = {\n tel: null as string | null,\n email: null as string | null,\n contactPage: null as string | null,\n };\n\n for (const match of html.matchAll(/href=[\"']tel:([^\"']+)[\"']/g)) {\n contactLinks.tel = match?.[1]?.trim() || null;\n }\n for (const match of html.matchAll(/href=[\"']mailto:([^\"']+)[\"']/g)) {\n contactLinks.email = match?.[1]?.trim() || null;\n }\n for (const match of html.matchAll(\n /href=[\"']([^\"']*(?:\\/contact|\\/pages\\/contact)[^\"']*)[\"']/g\n )) {\n contactLinks.contactPage = match?.[1] || null;\n }\n\n const extractedProductLinks =\n html\n .match(/href=[\"']([^\"']*\\/products\\/[^\"']+)[\"']/g)\n ?.map((match) =>\n match?.split(\"href=\")[1]?.replace(/[\"']/g, \"\")?.split(\"/\").at(-1)\n )\n ?.filter(Boolean) || [];\n\n const extractedCollectionLinks =\n html\n .match(/href=[\"']([^\"']*\\/collections\\/[^\"']+)[\"']/g)\n ?.map((match) =>\n match?.split(\"href=\")[1]?.replace(/[\"']/g, \"\")?.split(\"/\").at(-1)\n )\n ?.filter(Boolean) || [];\n\n const headerLinks =\n html\n .match(\n /<(header|nav|div|section)\\b[^>]*\\b(?:id|class)=[\"'][^\"']*(?=.*shopify-section)(?=.*\\b(header|navigation|nav|menu)\\b)[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/\\1>/gi\n )\n ?.flatMap((header) => {\n const links = header\n .match(/href=[\"']([^\"']+)[\"']/g)\n ?.filter(\n (link) =>\n link.includes(\"/products/\") ||\n link.includes(\"/collections/\") ||\n link.includes(\"/pages/\")\n );\n return (\n links\n ?.map((link) => {\n const href = link.match(/href=[\"']([^\"']+)[\"']/)?.[1];\n if (\n href &&\n !href.startsWith(\"#\") &&\n !href.startsWith(\"javascript:\")\n ) {\n try {\n const url = new URL(href, storeDomain);\n return url.pathname.replace(/^\\/|\\/$/g, \"\");\n } catch {\n return href.replace(/^\\/|\\/$/g, \"\");\n }\n }\n return null;\n })\n .filter((item): item is string => Boolean(item)) ?? []\n );\n }) ?? [];\n\n const slug = generateStoreSlug(baseUrl);\n\n const countryDetection = await detectShopifyCountry(html);\n\n const [homePageProductLinks, homePageCollectionLinks] = await Promise.all([\n validateLinksInBatches(\n extractedProductLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => validateProductExists(handle)\n ),\n validateLinksInBatches(\n extractedCollectionLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => validateCollectionExists(handle)\n ),\n ]);\n\n const info: StoreInfo = {\n name: name || slug,\n domain: sanitizeDomain(baseUrl),\n slug,\n title: title || null,\n description: description || null,\n logoUrl: logoUrl,\n socialLinks,\n contactLinks,\n headerLinks,\n showcase: {\n products: unique(homePageProductLinks ?? []),\n collections: unique(homePageCollectionLinks ?? []),\n },\n jsonLdData:\n html\n .match(\n /<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([^<]+)<\\/script>/g\n )\n ?.map(\n (match) => match?.split(\">\")[1]?.replace(/<\\/script/g, \"\") || null\n )\n ?.map((json) => (json ? JSON.parse(json) : null)) || [],\n techProvider: {\n name: \"shopify\",\n walletId: shopifyWalletId,\n subDomain: myShopifySubdomain ?? null,\n },\n country: countryDetection.country,\n };\n\n const currencyCode = (countryDetection as any)?.currencyCode;\n return { info, currencyCode };\n}\n","import type {\n CountryDetectionResult,\n CountryScores,\n ShopifyFeaturesData,\n} from \"../types\";\n\nconst COUNTRY_CODES: Record<string, string> = {\n \"+1\": \"US\", // United States (primary) / Canada also uses +1\n \"+44\": \"GB\", // United Kingdom\n \"+61\": \"AU\", // Australia\n \"+65\": \"SG\", // Singapore\n \"+91\": \"IN\", // India\n \"+81\": \"JP\", // Japan\n \"+49\": \"DE\", // Germany\n \"+33\": \"FR\", // France\n \"+971\": \"AE\", // United Arab Emirates\n \"+39\": \"IT\", // Italy\n \"+34\": \"ES\", // Spain\n \"+82\": \"KR\", // South Korea\n \"+55\": \"BR\", // Brazil\n \"+62\": \"ID\", // Indonesia\n \"+92\": \"PK\", // Pakistan\n \"+7\": \"RU\", // Russia\n};\n\nconst CURRENCY_SYMBOLS: Record<string, string> = {\n Rs: \"IN\", // India\n \"₹\": \"IN\", // India\n $: \"US\", // United States (primary, though many countries use $)\n CA$: \"CA\", // Canada\n A$: \"AU\", // Australia\n \"£\": \"GB\", // United Kingdom\n \"€\": \"EU\", // European Union (not a country code, but commonly used)\n AED: \"AE\", // United Arab Emirates\n \"₩\": \"KR\", // South Korea\n \"¥\": \"JP\", // Japan (primary, though China also uses ¥)\n};\n\n// Map currency symbols commonly found in Shopify money formats to ISO currency codes\nconst CURRENCY_SYMBOL_TO_CODE: Record<string, string> = {\n Rs: \"INR\",\n \"₹\": \"INR\",\n $: \"USD\",\n CA$: \"CAD\",\n A$: \"AUD\",\n \"£\": \"GBP\",\n \"€\": \"EUR\",\n AED: \"AED\",\n \"₩\": \"KRW\",\n \"¥\": \"JPY\",\n};\n\n// Map Shopify currency codes to likely ISO country codes\n// Note: Some codes (e.g., USD, EUR) are used by multiple countries; we treat them as signals.\nconst CURRENCY_CODE_TO_COUNTRY: Record<string, string> = {\n INR: \"IN\",\n USD: \"US\",\n CAD: \"CA\",\n AUD: \"AU\",\n GBP: \"GB\",\n EUR: \"EU\",\n AED: \"AE\",\n KRW: \"KR\",\n JPY: \"JP\",\n};\n\nfunction scoreCountry(\n countryScores: CountryScores,\n country: string,\n weight: number,\n reason: string\n): void {\n if (!country) return;\n if (!countryScores[country])\n countryScores[country] = { score: 0, reasons: [] };\n countryScores[country].score += weight;\n countryScores[country].reasons.push(reason);\n}\n\n/**\n * Detects the country of a Shopify store by analyzing various signals in the HTML content.\n *\n * This function examines multiple data sources within the HTML to determine the store's country:\n * - Shopify features JSON data (country, locale, money format)\n * - Phone number prefixes in contact information\n * - JSON-LD structured data with address information\n * - Footer mentions of country names\n * - Currency symbols in money formatting\n *\n * @param html - The HTML content of the Shopify store's homepage\n * @returns Promise resolving to country detection results containing:\n * - `country` - The detected country ISO 3166-1 alpha-2 code (e.g., \"US\", \"GB\") or \"Unknown\" if no reliable detection\n * - `confidence` - Confidence score between 0 and 1 (higher = more confident)\n * - `signals` - Array of detection signals that contributed to the result\n *\n * @example\n * ```typescript\n * const response = await fetch('https://exampleshop.com');\n * const html = await response.text();\n * const result = await detectShopifyCountry(html);\n *\n * console.log(result.country); // \"US\" (ISO code for United States)\n * console.log(result.confidence); // 0.85\n * console.log(result.signals); // [\"shopify-features.country\", \"phone prefix +1\"]\n * ```\n */\nexport async function detectShopifyCountry(\n html: string\n): Promise<CountryDetectionResult> {\n const countryScores: CountryScores = {};\n let detectedCurrencyCode: string | undefined;\n\n // 1️⃣ Extract Shopify features JSON\n const shopifyFeaturesMatch = html.match(\n /<script[^>]+id=[\"']shopify-features[\"'][^>]*>([\\s\\S]*?)<\\/script>/\n );\n if (shopifyFeaturesMatch) {\n try {\n const json = shopifyFeaturesMatch[1];\n if (!json) {\n // no content in capture group; skip\n } else {\n const data: ShopifyFeaturesData = JSON.parse(json);\n if (data.country)\n scoreCountry(\n countryScores,\n data.country,\n 1,\n \"shopify-features.country\"\n );\n if (data.locale?.includes(\"-\")) {\n const [, localeCountry] = data.locale.split(\"-\");\n if (localeCountry) {\n scoreCountry(\n countryScores,\n localeCountry.toUpperCase(),\n 0.7,\n \"shopify-features.locale\"\n );\n }\n }\n if (data.moneyFormat) {\n for (const symbol in CURRENCY_SYMBOLS) {\n if (data.moneyFormat.includes(symbol)) {\n const iso =\n CURRENCY_SYMBOLS[symbol as keyof typeof CURRENCY_SYMBOLS];\n if (typeof iso === \"string\") {\n scoreCountry(countryScores, iso, 0.6, \"moneyFormat symbol\");\n }\n // Also capture currency code if symbol is recognized\n const code =\n CURRENCY_SYMBOL_TO_CODE[\n symbol as keyof typeof CURRENCY_SYMBOL_TO_CODE\n ];\n if (!detectedCurrencyCode && typeof code === \"string\") {\n detectedCurrencyCode = code;\n }\n }\n }\n }\n }\n } catch (_error) {\n // Silently handle JSON parsing errors\n }\n }\n\n // 1️⃣ b) Detect Shopify.currency active code (common across many Shopify themes)\n // Example: Shopify.currency = {\"active\":\"INR\",\"rate\":\"1.0\"};\n // Fallback pattern: Shopify.currency.active = 'INR';\n const currencyJsonMatch = html.match(/Shopify\\.currency\\s*=\\s*(\\{[^}]*\\})/);\n if (currencyJsonMatch) {\n try {\n const raw = currencyJsonMatch[1];\n const obj = JSON.parse(raw || \"{}\") as any;\n const activeCode =\n typeof obj?.active === \"string\" ? obj.active.toUpperCase() : undefined;\n const iso = activeCode ? CURRENCY_CODE_TO_COUNTRY[activeCode] : undefined;\n if (activeCode) {\n detectedCurrencyCode = activeCode;\n }\n if (typeof iso === \"string\") {\n // Treat as a strong signal\n scoreCountry(countryScores, iso, 0.8, \"Shopify.currency.active\");\n }\n } catch (_error) {\n // ignore malformed objects\n }\n } else {\n const currencyActiveAssignMatch = html.match(\n /Shopify\\.currency\\.active\\s*=\\s*['\"]([A-Za-z]{3})['\"]/i\n );\n if (currencyActiveAssignMatch) {\n const captured = currencyActiveAssignMatch[1];\n const code =\n typeof captured === \"string\" ? captured.toUpperCase() : undefined;\n const iso = code ? CURRENCY_CODE_TO_COUNTRY[code] : undefined;\n if (code) {\n detectedCurrencyCode = code;\n }\n if (typeof iso === \"string\") {\n scoreCountry(countryScores, iso, 0.8, \"Shopify.currency.active\");\n }\n }\n }\n\n // 1️⃣ c) Detect explicit Shopify.country assignment\n // Example: Shopify.country = \"IN\";\n const shopifyCountryMatch = html.match(\n /Shopify\\.country\\s*=\\s*['\"]([A-Za-z]{2})['\"]/i\n );\n if (shopifyCountryMatch) {\n const captured = shopifyCountryMatch[1];\n const iso =\n typeof captured === \"string\" ? captured.toUpperCase() : undefined;\n if (typeof iso === \"string\") {\n // Treat as strongest signal\n scoreCountry(countryScores, iso, 1, \"Shopify.country\");\n }\n }\n\n // 2️⃣ Extract phone numbers\n const phones = html.match(/\\+\\d{1,3}[\\s\\-()0-9]{5,}/g);\n if (phones) {\n for (const phone of phones) {\n const prefix = phone.match(/^\\+\\d{1,3}/)?.[0];\n if (prefix && COUNTRY_CODES[prefix])\n scoreCountry(\n countryScores,\n COUNTRY_CODES[prefix],\n 0.8,\n `phone prefix ${prefix}`\n );\n }\n }\n\n // 3️⃣ Extract JSON-LD addressCountry fields\n const jsonLdRegex = /<script[^>]+application\\/ld\\+json[^>]*>(.*?)<\\/script>/g;\n let jsonLdMatch: RegExpExecArray | null = jsonLdRegex.exec(html);\n while (jsonLdMatch !== null) {\n try {\n const json = jsonLdMatch[1];\n if (!json) {\n // skip empty capture\n } else {\n const raw = JSON.parse(json) as unknown;\n\n const collectAddressCountries = (\n node: unknown,\n results: string[] = []\n ): string[] => {\n if (Array.isArray(node)) {\n for (const item of node) collectAddressCountries(item, results);\n return results;\n }\n if (node && typeof node === \"object\") {\n const obj = node as Record<string, unknown>;\n const address = obj.address;\n if (address && typeof address === \"object\") {\n const country = (address as Record<string, unknown>)\n .addressCountry;\n if (typeof country === \"string\") results.push(country);\n }\n // Support nested graphs\n const graph = obj[\"@graph\"];\n if (graph) collectAddressCountries(graph, results);\n }\n return results;\n };\n\n const countries = collectAddressCountries(raw);\n for (const country of countries) {\n scoreCountry(countryScores, country, 1, \"JSON-LD addressCountry\");\n }\n }\n } catch (_error) {\n // Silently handle JSON parsing errors\n }\n // advance to next match\n jsonLdMatch = jsonLdRegex.exec(html);\n }\n\n // 4️⃣ Footer country mentions - now using ISO codes\n const footerMatch = html.match(/<footer[^>]*>(.*?)<\\/footer>/i);\n if (footerMatch) {\n const footerTextGroup = footerMatch[1];\n const footerText = footerTextGroup ? footerTextGroup.toLowerCase() : \"\";\n // Create a mapping of country names to ISO codes for footer detection\n const countryNameToISO: Record<string, string> = {\n india: \"IN\",\n \"united states\": \"US\",\n canada: \"CA\",\n australia: \"AU\",\n \"united kingdom\": \"GB\",\n britain: \"GB\",\n uk: \"GB\",\n japan: \"JP\",\n \"south korea\": \"KR\",\n korea: \"KR\",\n germany: \"DE\",\n france: \"FR\",\n italy: \"IT\",\n spain: \"ES\",\n brazil: \"BR\",\n russia: \"RU\",\n singapore: \"SG\",\n indonesia: \"ID\",\n pakistan: \"PK\",\n };\n\n for (const [countryName, isoCode] of Object.entries(countryNameToISO)) {\n if (footerText.includes(countryName))\n scoreCountry(countryScores, isoCode, 0.4, \"footer mention\");\n }\n }\n\n // Pick best guess\n const sorted = Object.entries(countryScores).sort(\n (a, b) => b[1].score - a[1].score\n );\n const best = sorted[0];\n\n return best\n ? {\n country: best[0],\n confidence: Math.min(1, best[1].score / 2),\n signals: best[1].reasons,\n currencyCode: detectedCurrencyCode,\n }\n : {\n country: \"Unknown\",\n confidence: 0,\n signals: [],\n currencyCode: detectedCurrencyCode,\n };\n}\n","import { parse } from \"tldts\";\nimport type { CurrencyCode } from \"../types\";\n\nexport function extractDomainWithoutSuffix(domain: string) {\n const parsedDomain = parse(domain);\n return parsedDomain.domainWithoutSuffix;\n}\n\nexport function generateStoreSlug(domain: string): string {\n const input = new URL(domain);\n const parsedDomain = parse(input.href);\n const domainName =\n parsedDomain.domainWithoutSuffix ?? input.hostname.split(\".\")[0];\n\n return (domainName || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport const genProductSlug = ({\n handle,\n storeDomain,\n}: {\n handle: string;\n storeDomain: string;\n}) => {\n const storeSlug = generateStoreSlug(storeDomain);\n return `${handle}-by-${storeSlug}`;\n};\n\nexport const calculateDiscount = (\n price: number,\n compareAtPrice?: number\n): number =>\n !compareAtPrice || compareAtPrice === 0\n ? 0\n : Math.max(\n 0,\n Math.round(100 - (price / compareAtPrice) * 100) // Removed the decimal precision\n );\n\n/**\n * Normalize and sanitize a domain string.\n *\n * Accepts inputs like full URLs, protocol-relative URLs, bare hostnames,\n * or strings with paths/query/fragment, and returns a normalized domain.\n *\n * Examples:\n * - \"https://WWW.Example.com/path\" -> \"example.com\"\n * - \"//sub.example.co.uk\" -> \"example.co.uk\"\n * - \"www.example.com:8080\" -> \"example.com\"\n * - \"example\" -> \"example\"\n */\nexport function sanitizeDomain(\n input: string,\n opts?: { stripWWW?: boolean }\n): string {\n if (typeof input !== \"string\") {\n throw new Error(\"sanitizeDomain: input must be a string\");\n }\n let raw = input.trim();\n if (!raw) {\n throw new Error(\"sanitizeDomain: input cannot be empty\");\n }\n // Only add protocol if it's missing and not protocol-relative\n const hasProtocol = /^[a-z]+:\\/\\//i.test(raw);\n if (!hasProtocol && !raw.startsWith(\"//\")) {\n raw = `https://${raw}`;\n }\n\n const stripWWW = opts?.stripWWW ?? true;\n\n try {\n let url: URL;\n if (raw.startsWith(\"//\")) {\n url = new URL(`https:${raw}`);\n } else if (raw.includes(\"://\")) {\n url = new URL(raw);\n } else {\n url = new URL(`https://${raw}`);\n }\n let hostname = url.hostname.toLowerCase();\n const hadWWW = /^www\\./i.test(url.hostname);\n if (stripWWW) hostname = hostname.replace(/^www\\./, \"\");\n if (!hostname.includes(\".\")) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n const parsed = parse(hostname);\n if (!parsed.publicSuffix || parsed.isIcann === false) {\n // Require a valid public suffix (e.g., TLD); reject bare hostnames\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n if (!stripWWW && hadWWW) {\n return `www.${parsed.domain || hostname}`;\n }\n return parsed.domain || hostname;\n } catch {\n // Fallback: attempt to sanitize without URL parsing\n let hostname = raw.toLowerCase();\n hostname = hostname.replace(/^[a-z]+:\\/\\//, \"\"); // remove protocol if present\n hostname = hostname.replace(/^\\/\\//, \"\"); // remove protocol-relative\n hostname = hostname.replace(/[/:#?].*$/, \"\"); // remove path/query/fragment/port\n const hadWWW = /^www\\./i.test(hostname);\n if (stripWWW) hostname = hostname.replace(/^www\\./, \"\");\n if (!hostname.includes(\".\")) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n const parsed = parse(hostname);\n if (!parsed.publicSuffix || parsed.isIcann === false) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n if (!stripWWW && hadWWW) {\n return `www.${parsed.domain || hostname}`;\n }\n return parsed.domain || hostname;\n }\n}\n\n/**\n * Safely parse a date string into a Date object.\n *\n * Returns `undefined` when input is falsy or cannot be parsed into a valid date.\n * Use `|| null` at call sites that expect `null` instead of `undefined`.\n */\nexport function safeParseDate(input?: string | null): Date | undefined {\n if (!input || typeof input !== \"string\") return undefined;\n const d = new Date(input);\n return Number.isNaN(d.getTime()) ? undefined : d;\n}\n\n/**\n * Normalize an option name or value to a lowercase, underscore-separated key.\n */\nexport function normalizeKey(input: string): string {\n return input.toLowerCase().replace(/\\s+/g, \"_\");\n}\n\n/**\n * Build a map from normalized option combination → variant id strings.\n * Example key: `size#xl##color#blue`.\n */\nexport function buildVariantOptionsMap(\n optionNames: string[],\n variants: Array<{\n id: number;\n option1: string | null;\n option2: string | null;\n option3: string | null;\n }>\n): Record<string, string> {\n const keys = optionNames.map(normalizeKey);\n const map: Record<string, string> = {};\n\n for (const v of variants) {\n const parts: string[] = [];\n if (keys[0] && v.option1)\n parts.push(`${keys[0]}#${normalizeKey(v.option1)}`);\n if (keys[1] && v.option2)\n parts.push(`${keys[1]}#${normalizeKey(v.option2)}`);\n if (keys[2] && v.option3)\n parts.push(`${keys[2]}#${normalizeKey(v.option3)}`);\n\n if (parts.length > 0) {\n // Ensure deterministic alphabetical ordering of parts\n if (parts.length > 1) parts.sort();\n const key = parts.join(\"##\");\n const id = v.id.toString();\n // First-write wins: do not override if key already exists\n if (map[key] === undefined) {\n map[key] = id;\n }\n }\n }\n\n return map;\n}\n\n/**\n * Build a normalized variant key string from an object of option name → value.\n * - Normalizes both names and values using `normalizeKey`\n * - Sorts parts alphabetically for deterministic output\n * - Joins parts using `##` and uses `name#value` for each part\n *\n * Example output: `color#blue##size#xl`\n */\nexport function buildVariantKey(\n obj: Record<string, string | null | undefined>\n): string {\n const parts: string[] = [];\n for (const [name, value] of Object.entries(obj)) {\n if (value) {\n parts.push(`${normalizeKey(name)}#${normalizeKey(value)}`);\n }\n }\n if (parts.length === 0) return \"\";\n parts.sort((a, b) => a.localeCompare(b));\n return parts.join(\"##\");\n}\n\n/**\n * Format a price amount (in cents) using a given ISO 4217 currency code.\n * Falls back to a simple string when Intl formatting fails.\n */\nexport function formatPrice(\n amountInCents: number,\n currency: CurrencyCode\n): string {\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n }).format((amountInCents || 0) / 100);\n } catch {\n const val = (amountInCents || 0) / 100;\n return `${val} ${currency}`;\n }\n}\n","import { filter, isNonNullish } from \"remeda\";\nimport type { StoreInfo } from \"./store\";\nimport type {\n Collection,\n CurrencyCode,\n Product,\n ShopifyCollection,\n} from \"./types\";\nimport { formatPrice } from \"./utils/func\";\nimport { rateLimitedFetch } from \"./utils/rate-limit\";\n\n/**\n * Interface for collection operations\n */\nexport interface CollectionOperations {\n /**\n * Fetches all collections from the store across all pages.\n */\n all(): Promise<Collection[]>;\n\n /**\n * Fetches collections with pagination support.\n *\n * @param options - Pagination options\n * @param options.page - Page number (default: 1)\n * @param options.limit - Number of collections per page (default: 10, max: 250)\n *\n * @returns {Promise<Collection[] | null>} Array of collections for the page or null if error occurs\n */\n paginated(options?: {\n page?: number;\n limit?: number;\n }): Promise<Collection[] | null>;\n\n /**\n * Finds a specific collection by its handle.\n */\n find(collectionHandle: string): Promise<Collection | null>;\n\n /**\n * Fetches collections that are showcased/featured on the store's homepage.\n */\n showcased(): Promise<Collection[]>;\n\n /**\n * Product-related methods for fetching products from specific collections.\n */\n products: {\n /**\n * Fetches products from a specific collection with pagination support.\n */\n paginated(\n collectionHandle: string,\n options?: { page?: number; limit?: number; currency?: CurrencyCode }\n ): Promise<Product[] | null>;\n\n /**\n * Fetches all products from a specific collection.\n */\n all(\n collectionHandle: string,\n options?: { currency?: CurrencyCode }\n ): Promise<Product[] | null>;\n /**\n * Fetches all product slugs from a specific collection.\n */\n slugs(collectionHandle: string): Promise<string[] | null>;\n };\n}\n\n/**\n * Creates collection operations for a store instance\n */\nexport function createCollectionOperations(\n baseUrl: string,\n storeDomain: string,\n fetchCollections: (\n page: number,\n limit: number\n ) => Promise<Collection[] | null>,\n collectionsDto: (collections: ShopifyCollection[]) => Collection[],\n fetchPaginatedProductsFromCollection: (\n collectionHandle: string,\n options?: { page?: number; limit?: number }\n ) => Promise<Product[] | null>,\n getStoreInfo: () => Promise<StoreInfo>,\n findCollection: (handle: string) => Promise<Collection | null>\n): CollectionOperations {\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 return {\n /**\n * Fetches collections with pagination support.\n *\n * @param options - Pagination options\n * @param options.page - Page number (default: 1)\n * @param options.limit - Number of collections per page (default: 10, max: 250)\n *\n * @returns {Promise<Collection[] | null>} Collections for the requested page, or null on error\n */\n paginated: async (options?: {\n page?: number;\n limit?: number;\n }): Promise<Collection[] | null> => {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 10;\n\n if (page < 1 || limit < 1 || limit > 250) {\n throw new Error(\n \"Invalid pagination parameters: page must be >= 1, limit must be between 1 and 250\"\n );\n }\n\n try {\n const collections = await fetchCollections(page, limit);\n return collections ?? null;\n } catch (error) {\n console.error(\n \"Failed to fetch paginated collections:\",\n storeDomain,\n error\n );\n return null;\n }\n },\n /**\n * Fetches all collections from the store across all pages.\n *\n * @returns {Promise<Collection[]>} Array of all collections\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 allCollections = await shop.collections.all();\n *\n * console.log(`Found ${allCollections.length} collections`);\n * allCollections.forEach(collection => {\n * console.log(collection.title, collection.handle);\n * });\n * ```\n */\n all: async (): Promise<Collection[]> => {\n const limit = 250;\n const allCollections: Collection[] = [];\n\n async function fetchAll() {\n let currentPage = 1;\n\n while (true) {\n const collections = await fetchCollections(currentPage, limit);\n\n if (\n !collections ||\n collections.length === 0 ||\n collections.length < limit\n ) {\n if (!collections) {\n console.warn(\n \"fetchCollections returned null, treating as empty array.\"\n );\n break;\n }\n if (collections && collections.length > 0) {\n allCollections.push(...collections);\n }\n break;\n }\n\n allCollections.push(...collections);\n currentPage++;\n }\n return allCollections;\n }\n\n try {\n const collections = await fetchAll();\n return collections || [];\n } catch (error) {\n console.error(\"Failed to fetch all collections:\", storeDomain, error);\n throw error;\n }\n },\n\n /**\n * Finds a specific collection by its handle.\n *\n * @param collectionHandle - The collection handle (URL slug) to search for\n *\n * @returns {Promise<Collection | null>} The collection 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://example.myshopify.com');\n * const collection = await shop.collections.find('summer-collection');\n * if (collection) {\n * console.log(collection.title); // \"Summer Collection\"\n * }\n * ```\n */\n find: async (collectionHandle: string): Promise<Collection | null> => {\n // Validate collection handle\n if (!collectionHandle || typeof collectionHandle !== \"string\") {\n throw new Error(\"Collection handle is required and must be a string\");\n }\n\n // Sanitize handle - remove potentially dangerous characters\n const sanitizedHandle = collectionHandle\n .trim()\n .replace(/[^a-zA-Z0-9\\-_]/g, \"\");\n if (!sanitizedHandle) {\n throw new Error(\"Invalid collection handle format\");\n }\n\n // Check handle length (reasonable limits)\n if (sanitizedHandle.length > 255) {\n throw new Error(\"Collection handle is too long\");\n }\n\n try {\n const url = `${baseUrl}collections/${encodeURIComponent(sanitizedHandle)}.json`;\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 result = (await response.json()) as {\n collection: ShopifyCollection;\n };\n\n let collectionImage = result.collection.image;\n if (!collectionImage) {\n const collectionProduct = (\n await fetchPaginatedProductsFromCollection(\n result.collection.handle,\n {\n limit: 1,\n page: 1,\n }\n )\n )?.at(0);\n const collectionProductImage = collectionProduct?.images?.[0];\n if (collectionProduct && collectionProductImage) {\n collectionImage = {\n id: collectionProductImage.id,\n src: collectionProductImage.src,\n alt: collectionProductImage.alt || collectionProduct.title,\n created_at:\n collectionProductImage.createdAt || new Date().toISOString(),\n };\n }\n }\n\n const collectionData = collectionsDto([\n {\n ...result.collection,\n image: collectionImage,\n },\n ]);\n return collectionData[0] || null;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `Error fetching collection ${sanitizedHandle}:`,\n baseUrl,\n error.message\n );\n }\n throw error;\n }\n },\n\n /**\n * Fetches collections that are showcased/featured on the store's homepage.\n *\n * @returns {Promise<Collection[]>} Array of showcased collections 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 showcasedCollections = await shop.collections.showcased();\n *\n * console.log(`Found ${showcasedCollections.length} showcased collections`);\n * showcasedCollections.forEach(collection => {\n * console.log(`Featured: ${collection.title} - ${collection.productsCount} products`);\n * });\n * ```\n */\n showcased: async () => {\n const storeInfo = await getStoreInfo();\n const collections = await Promise.all(\n storeInfo.showcase.collections.map((collectionHandle: string) =>\n findCollection(collectionHandle)\n )\n );\n return filter(collections, isNonNullish);\n },\n\n products: {\n /**\n * Fetches products from a specific collection with pagination support.\n *\n * @param collectionHandle - The collection handle to fetch products from\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 from the collection or null if error occurs\n *\n * @throws {Error} When the collection handle is invalid or there's a network error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://example.myshopify.com');\n *\n * // Get first page of products from a collection\n * const products = await shop.collections.products.paginated('summer-collection');\n *\n * // Get second page with custom limit\n * const moreProducts = await shop.collections.products.paginated(\n * 'summer-collection',\n * { page: 2, limit: 50 }\n * );\n * ```\n */\n paginated: async (\n collectionHandle: string,\n options?: { page?: number; limit?: number; currency?: CurrencyCode }\n ) => {\n // Validate collection handle\n if (!collectionHandle || typeof collectionHandle !== \"string\") {\n throw new Error(\"Collection handle is required and must be a string\");\n }\n // Sanitize handle - remove potentially dangerous characters\n const sanitizedHandle = collectionHandle\n .trim()\n .replace(/[^a-zA-Z0-9\\-_]/g, \"\");\n\n if (!sanitizedHandle) {\n throw new Error(\"Invalid collection handle format\");\n }\n\n if (sanitizedHandle.length > 255) {\n // Check handle length (reasonable limits)\n throw new Error(\"Collection handle is too long\");\n }\n\n // Validate pagination options\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 250;\n\n if (page < 1 || limit < 1 || limit > 250) {\n throw new Error(\n \"Invalid pagination parameters: page must be >= 1, limit must be between 1 and 250\"\n );\n }\n\n const products = await fetchPaginatedProductsFromCollection(\n sanitizedHandle,\n { page, limit }\n );\n return maybeOverrideProductsCurrency(products, options?.currency);\n },\n\n /**\n * Fetches all products from a specific collection.\n *\n * @param collectionHandle - The collection handle to fetch products from\n *\n * @returns {Promise<Product[] | null>} Array of all products from the collection or null if error occurs\n *\n * @throws {Error} When the collection handle is invalid or there's a network error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const allProducts = await shop.collections.products.all('summer-collection');\n *\n * if (allProducts) {\n * console.log(`Found ${allProducts.length} products in the collection`);\n * allProducts.forEach(product => {\n * console.log(`${product.title} - $${product.price}`);\n * });\n * }\n * ```\n */\n all: async (\n collectionHandle: string,\n options?: { currency?: CurrencyCode }\n ): Promise<Product[] | null> => {\n // Validate collection handle\n if (!collectionHandle || typeof collectionHandle !== \"string\") {\n throw new Error(\"Collection handle is required and must be a string\");\n }\n\n // Sanitize handle - remove potentially dangerous characters\n const sanitizedHandle = collectionHandle\n .trim()\n .replace(/[^a-zA-Z0-9\\-_]/g, \"\");\n if (!sanitizedHandle) {\n throw new Error(\"Invalid collection handle format\");\n }\n\n // Check handle length (reasonable limits)\n if (sanitizedHandle.length > 255) {\n throw new Error(\"Collection handle is too long\");\n }\n\n try {\n const limit = 250;\n const allProducts: Product[] = [];\n\n let currentPage = 1;\n\n while (true) {\n const products = await fetchPaginatedProductsFromCollection(\n sanitizedHandle,\n {\n page: currentPage,\n limit,\n }\n );\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\n return maybeOverrideProductsCurrency(allProducts, options?.currency);\n } catch (error) {\n console.error(\n `Error fetching all products for collection ${sanitizedHandle}:`,\n baseUrl,\n error\n );\n return null;\n }\n },\n\n /**\n * Fetches all product slugs from a specific collection.\n *\n * @param collectionHandle - The collection handle to fetch product slugs from\n *\n * @returns {Promise<string[] | null>} Array of product slugs from the collection or null if error occurs\n *\n * @throws {Error} When the collection handle is invalid or there's a network error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const productSlugs = await shop.collections.products.slugs('summer-collection');\n * console.log(productSlugs);\n * ```\n */\n slugs: async (collectionHandle: string): Promise<string[] | null> => {\n // Validate collection handle\n if (!collectionHandle || typeof collectionHandle !== \"string\") {\n throw new Error(\"Collection handle is required and must be a string\");\n }\n\n // Sanitize handle - remove potentially dangerous characters\n const sanitizedHandle = collectionHandle\n .trim()\n .replace(/[^a-zA-Z0-9\\-_]/g, \"\");\n if (!sanitizedHandle) {\n throw new Error(\"Invalid collection handle format\");\n }\n\n // Check handle length (reasonable limits)\n if (sanitizedHandle.length > 255) {\n throw new Error(\"Collection handle is too long\");\n }\n\n try {\n const limit = 250;\n const slugs: string[] = [];\n\n let currentPage = 1;\n\n while (true) {\n const products = await fetchPaginatedProductsFromCollection(\n sanitizedHandle,\n {\n page: currentPage,\n limit,\n }\n );\n\n if (!products || products.length === 0 || products.length < limit) {\n if (products && products.length > 0) {\n slugs.push(...products.map((p) => p.slug));\n }\n break;\n }\n\n slugs.push(...products.map((p) => p.slug));\n currentPage++;\n }\n\n return slugs;\n } catch (error) {\n console.error(\n `Error fetching product slugs for collection ${sanitizedHandle}:`,\n baseUrl,\n error\n );\n return null;\n }\n },\n },\n };\n}\n","import type { Collection, ShopifyCollection } from \"../types\";\n\nexport function collectionsDto(\n collections: ShopifyCollection[] | null\n): Collection[] | null {\n if (!collections || collections.length === 0) return null;\n\n return collections.map((collection) => ({\n id: collection.id.toString(),\n title: collection.title,\n handle: collection.handle,\n description: collection.description,\n image: collection.image\n ? {\n id: collection.image.id,\n createdAt: collection.image.created_at,\n src: collection.image.src,\n alt: collection.image.alt,\n }\n : undefined,\n productsCount: collection.products_count,\n publishedAt: collection.published_at,\n updatedAt: collection.updated_at,\n }));\n}\n","import type { Product, ShopifyProduct, ShopifySingleProduct } from \"../types\";\nimport {\n buildVariantOptionsMap,\n genProductSlug,\n normalizeKey,\n safeParseDate,\n} from \"../utils/func\";\n\ntype Ctx = {\n storeDomain: string;\n storeSlug: string;\n currency: string;\n normalizeImageUrl: (url: string | null | undefined) => string;\n formatPrice: (amountInCents: number) => string;\n};\n\nfunction mapVariants(\n product: ShopifyProduct | ShopifySingleProduct\n): NonNullable<Product[\"variants\"]> {\n const variants = (product as ShopifyProduct).variants ?? [];\n return variants.map((variant: any) => ({\n id: variant.id.toString(),\n platformId: variant.id.toString(),\n name: variant.name,\n title: variant.title,\n option1: variant.option1 || null,\n option2: variant.option2 || null,\n option3: variant.option3 || null,\n options: [variant.option1, variant.option2, variant.option3].filter(\n Boolean\n ) as string[],\n sku: variant.sku || null,\n requiresShipping: variant.requires_shipping,\n taxable: variant.taxable,\n featuredImage: variant.featured_image\n ? {\n id: variant.featured_image.id,\n src: variant.featured_image.src,\n width: variant.featured_image.width,\n height: variant.featured_image.height,\n position: variant.featured_image.position,\n productId: variant.featured_image.product_id,\n aspectRatio: variant.featured_image.aspect_ratio || 0,\n variantIds: variant.featured_image.variant_ids || [],\n createdAt: variant.featured_image.created_at,\n updatedAt: variant.featured_image.updated_at,\n alt: variant.featured_image.alt,\n }\n : null,\n available: Boolean(variant.available),\n price:\n typeof variant.price === \"string\"\n ? Number.parseFloat(variant.price) * 100\n : variant.price,\n weightInGrams: variant.weightInGrams ?? variant.grams,\n compareAtPrice: variant.compare_at_price\n ? typeof variant.compare_at_price === \"string\"\n ? Number.parseFloat(variant.compare_at_price) * 100\n : variant.compare_at_price\n : 0,\n position: variant.position,\n productId: variant.product_id,\n createdAt: variant.created_at,\n updatedAt: variant.updated_at,\n }));\n}\n\nexport function mapProductsDto(\n products: ShopifyProduct[] | null,\n ctx: Ctx\n): Product[] | null {\n if (!products || products.length === 0) return null;\n\n return products.map((product) => {\n const optionNames = product.options.map((o) => o.name);\n const variantOptionsMap = buildVariantOptionsMap(\n optionNames,\n product.variants\n );\n const mappedVariants = mapVariants(product);\n\n const priceValues = mappedVariants\n .map((v) => v.price)\n .filter((p) => typeof p === \"number\" && !Number.isNaN(p));\n const compareAtValues = mappedVariants\n .map((v) => v.compareAtPrice || 0)\n .filter((p) => typeof p === \"number\" && !Number.isNaN(p));\n\n const priceMin = priceValues.length ? Math.min(...priceValues) : 0;\n const priceMax = priceValues.length ? Math.max(...priceValues) : 0;\n const priceVaries = mappedVariants.length > 1 && priceMin !== priceMax;\n\n const compareAtMin = compareAtValues.length\n ? Math.min(...compareAtValues)\n : 0;\n const compareAtMax = compareAtValues.length\n ? Math.max(...compareAtValues)\n : 0;\n const compareAtVaries =\n mappedVariants.length > 1 && compareAtMin !== compareAtMax;\n\n return {\n slug: genProductSlug({\n handle: product.handle,\n storeDomain: ctx.storeDomain,\n }),\n handle: product.handle,\n platformId: product.id.toString(),\n title: product.title,\n available: mappedVariants.some((v) => v.available),\n price: priceMin,\n priceMin: priceMin,\n priceMax: priceMax,\n priceVaries,\n compareAtPrice: compareAtMin,\n compareAtPriceMin: compareAtMin,\n compareAtPriceMax: compareAtMax,\n compareAtPriceVaries: compareAtVaries,\n discount: 0,\n currency: ctx.currency,\n localizedPricing: {\n currency: ctx.currency,\n priceFormatted: ctx.formatPrice(priceMin),\n priceMinFormatted: ctx.formatPrice(priceMin),\n priceMaxFormatted: ctx.formatPrice(priceMax),\n compareAtPriceFormatted: ctx.formatPrice(compareAtMin),\n },\n options: product.options.map((option) => ({\n key: normalizeKey(option.name),\n data: option.values,\n name: option.name,\n position: option.position,\n values: option.values,\n })),\n variantOptionsMap,\n bodyHtml: product.body_html || null,\n active: true,\n productType: product.product_type || null,\n tags: Array.isArray(product.tags) ? product.tags : [],\n vendor: product.vendor,\n featuredImage: product.images?.[0]?.src\n ? ctx.normalizeImageUrl(product.images[0].src)\n : null,\n isProxyFeaturedImage: false,\n createdAt: safeParseDate(product.created_at),\n updatedAt: safeParseDate(product.updated_at),\n variants: mappedVariants,\n images: product.images.map((image) => ({\n id: image.id,\n productId: image.product_id,\n alt: null,\n position: image.position,\n src: ctx.normalizeImageUrl(image.src),\n width: image.width,\n height: image.height,\n mediaType: \"image\" as const,\n variantIds: image.variant_ids || [],\n createdAt: image.created_at,\n updatedAt: image.updated_at,\n })),\n publishedAt: safeParseDate(product.published_at) ?? null,\n seo: null,\n metaTags: null,\n displayScore: undefined,\n deletedAt: null,\n storeSlug: ctx.storeSlug,\n storeDomain: ctx.storeDomain,\n url: `${ctx.storeDomain}/products/${product.handle}`,\n } as Product;\n });\n}\n\nexport function mapProductDto(\n product: ShopifySingleProduct,\n ctx: Ctx\n): Product {\n const optionNames = product.options.map((o) => o.name);\n const variantOptionsMap = buildVariantOptionsMap(\n optionNames,\n product.variants\n );\n\n const mapped: Product = {\n slug: genProductSlug({\n handle: product.handle,\n storeDomain: ctx.storeDomain,\n }),\n handle: product.handle,\n platformId: product.id.toString(),\n title: product.title,\n available: product.available,\n price: product.price,\n priceMin: product.price_min,\n priceMax: product.price_max,\n priceVaries: product.price_varies,\n compareAtPrice: product.compare_at_price || 0,\n compareAtPriceMin: product.compare_at_price_min,\n compareAtPriceMax: product.compare_at_price_max,\n compareAtPriceVaries: product.compare_at_price_varies,\n discount: 0,\n currency: ctx.currency,\n localizedPricing: {\n currency: ctx.currency,\n priceFormatted: ctx.formatPrice(product.price),\n priceMinFormatted: ctx.formatPrice(product.price_min),\n priceMaxFormatted: ctx.formatPrice(product.price_max),\n compareAtPriceFormatted: ctx.formatPrice(product.compare_at_price || 0),\n },\n options: product.options.map((option) => ({\n key: normalizeKey(option.name),\n data: option.values,\n name: option.name,\n position: option.position,\n values: option.values,\n })),\n variantOptionsMap,\n bodyHtml: product.description || null,\n active: true,\n productType: product.type || null,\n tags: Array.isArray(product.tags)\n ? product.tags\n : typeof product.tags === \"string\"\n ? [product.tags]\n : [],\n vendor: product.vendor,\n featuredImage: ctx.normalizeImageUrl(product.featured_image),\n isProxyFeaturedImage: false,\n createdAt: safeParseDate(product.created_at),\n updatedAt: safeParseDate(product.updated_at),\n variants: mapVariants(product),\n images: Array.isArray(product.images)\n ? product.images.map((imageSrc, index) => ({\n id: index + 1,\n productId: product.id,\n alt: null,\n position: index + 1,\n src: ctx.normalizeImageUrl(imageSrc),\n width: 0,\n height: 0,\n mediaType: \"image\" as const,\n variantIds: [],\n createdAt: product.created_at,\n updatedAt: product.updated_at,\n }))\n : [],\n publishedAt: safeParseDate(product.published_at) ?? null,\n seo: null,\n metaTags: null,\n displayScore: undefined,\n deletedAt: null,\n storeSlug: ctx.storeSlug,\n storeDomain: ctx.storeDomain,\n url: product.url || `${ctx.storeDomain}/products/${product.handle}`,\n };\n\n return mapped;\n}\n","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 { getInfoForStore } from \"./client/get-info\";\nimport type { CountryDetectionResult, JsonLdEntry } from \"./types\";\n\n/**\n * Store operations interface for managing store-related functionality.\n * Provides methods to fetch comprehensive store information and metadata.\n */\nexport interface StoreOperations {\n info(): Promise<StoreInfo>;\n}\n\n/**\n * Comprehensive store information structure returned by the info method.\n * Contains all metadata, branding, social links, and showcase content for a Shopify store.\n */\nexport interface StoreInfo {\n name: string;\n domain: string;\n slug: string;\n title: string | null;\n description: string | null;\n logoUrl: string | null;\n socialLinks: Record<string, string>;\n contactLinks: {\n tel: string | null;\n email: string | null;\n contactPage: string | null;\n };\n headerLinks: string[];\n showcase: {\n products: string[];\n collections: string[];\n };\n jsonLdData: JsonLdEntry[] | undefined;\n techProvider: {\n name: string;\n walletId: string | undefined;\n subDomain: string | null;\n };\n country: CountryDetectionResult[\"country\"];\n}\n\n/**\n * Creates store operations for a ShopClient instance.\n * @param context - ShopClient context containing necessary methods and properties for store operations\n */\nexport function createStoreOperations(context: {\n baseUrl: string;\n storeDomain: string;\n validateProductExists: (handle: string) => Promise<boolean>;\n validateCollectionExists: (handle: string) => Promise<boolean>;\n validateLinksInBatches: <T>(\n items: T[],\n validator: (item: T) => Promise<boolean>,\n batchSize?: number\n ) => Promise<T[]>;\n handleFetchError: (error: unknown, context: string, url: string) => never;\n}): StoreOperations {\n return {\n /**\n * Fetches comprehensive store information including metadata, social links, and showcase content.\n *\n * @returns {Promise<StoreInfo>} Store information object containing:\n * - `name` - Store name from meta tags or domain\n * - `domain` - Store domain URL\n * - `slug` - Generated store slug\n * - `title` - Store title from meta tags\n * - `description` - Store description from meta tags\n * - `logoUrl` - Store logo URL from Open Graph or CDN\n * - `socialLinks` - Object with social media links (facebook, twitter, instagram, etc.)\n * - `contactLinks` - Object with contact information (tel, email, contactPage)\n * - `headerLinks` - Array of navigation links from header\n * - `showcase` - Object with featured products and collections from homepage\n * - `jsonLdData` - Structured data from JSON-LD scripts\n * - `techProvider` - Shopify-specific information (walletId, subDomain)\n * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., \"US\", \"GB\")\n *\n * @throws {Error} When the store URL is unreachable or returns an error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const storeInfo = await shop.getInfo();\n *\n * console.log(storeInfo.name); // \"Example Store\"\n * console.log(storeInfo.socialLinks.instagram); // \"https://instagram.com/example\"\n * console.log(storeInfo.showcase.products); // [\"product-handle-1\", \"product-handle-2\"]\n * console.log(storeInfo.country); // \"US\"\n * ```\n */\n info: async (): Promise<StoreInfo> => {\n try {\n // Delegate to shared client parser to avoid redundancy\n const { info } = await getInfoForStore({\n baseUrl: context.baseUrl,\n storeDomain: context.storeDomain,\n validateProductExists: context.validateProductExists,\n validateCollectionExists: context.validateCollectionExists,\n validateLinksInBatches: context.validateLinksInBatches,\n });\n return info;\n /* const response = await rateLimitedFetch(context.baseUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n\n const getMetaTag = (name: string) => {\n const regex = new RegExp(\n `<meta[^>]*name=[\"']${name}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const getPropertyMetaTag = (property: string) => {\n const regex = new RegExp(\n `<meta[^>]*property=[\"']${property}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const name =\n getMetaTag(\"og:site_name\") ??\n extractDomainWithoutSuffix(context.baseUrl);\n const title = getMetaTag(\"og:title\") ?? getMetaTag(\"twitter:title\");\n\n const description =\n getMetaTag(\"description\") || getPropertyMetaTag(\"og:description\");\n\n const shopifyWalletId = getMetaTag(\"shopify-digital-wallet\")?.split(\n \"/\"\n )[1];\n\n const myShopifySubdomainMatch = html.match(\n /['\"](.*?\\.myshopify\\.com)['\"]/\n );\n const myShopifySubdomain = myShopifySubdomainMatch\n ? myShopifySubdomainMatch[1]\n : null;\n\n let logoUrl =\n getPropertyMetaTag(\"og:image\") ||\n getPropertyMetaTag(\"og:image:secure_url\");\n if (!logoUrl) {\n const logoMatch = html.match(\n /<img[^>]+src=[\"']([^\"']+\\/cdn\\/shop\\/[^\"']+)[\"']/\n );\n const group = logoMatch?.[1];\n logoUrl = group ? group.replace(\"http://\", \"https://\") : null;\n } else {\n logoUrl = logoUrl.replace(\"http://\", \"https://\");\n }\n\n const socialLinks: Record<string, string> = {};\n const socialRegex =\n /<a[^>]+href=[\"']([^\"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\\.com[^\"']*)[\"']/g;\n for (const match of html.matchAll(socialRegex)) {\n const hrefGroup = match[1];\n if (!hrefGroup) continue;\n let href: string = hrefGroup;\n try {\n if (href.startsWith(\"//\")) {\n href = `https:${href}`;\n } else if (href.startsWith(\"/\")) {\n href = new URL(href, context.baseUrl).toString();\n }\n const parsed = new URL(href);\n const domain = parsed.hostname.replace(\"www.\", \"\").split(\".\")[0];\n if (domain) {\n socialLinks[domain] = parsed.toString();\n }\n } catch {\n // Skip invalid URL entries silently\n }\n }\n\n const contactLinks = {\n tel: null as string | null,\n email: null as string | null,\n contactPage: null as string | null,\n };\n\n // Extract contact details using focused regexes to avoid parser pitfalls\n for (const match of html.matchAll(/href=[\"']tel:([^\"']+)[\"']/g)) {\n const group = match[1];\n if (group) contactLinks.tel = group.trim();\n }\n for (const match of html.matchAll(/href=[\"']mailto:([^\"']+)[\"']/g)) {\n const group = match[1];\n if (group) contactLinks.email = group.trim();\n }\n for (const match of html.matchAll(\n /href=[\"']([^\"']*(?:\\/contact|\\/pages\\/contact)[^\"']*)[\"']/g\n )) {\n const group = match[1];\n if (group) contactLinks.contactPage = group;\n }\n\n const extractedProductLinks =\n html\n .match(/href=[\"']([^\"']*\\/products\\/[^\"']+)[\"']/g)\n ?.map((match) => {\n const afterHref = match.split(\"href=\")[1];\n if (!afterHref) return null;\n const last = afterHref.replace(/[\\'\"]/g, \"\").split(\"/\").at(-1);\n return last ?? null;\n })\n ?.filter((x): x is string => Boolean(x)) || [];\n\n const extractedCollectionLinks =\n html\n .match(/href=[\"']([^\"']*\\/collections\\/[^\"']+)[\"']/g)\n ?.map((match) => {\n const afterHref = match.split(\"href=\")[1];\n if (!afterHref) return null;\n const last = afterHref.replace(/[\\'\"]/g, \"\").split(\"/\").at(-1);\n return last ?? null;\n })\n ?.filter((x): x is string => Boolean(x)) || [];\n\n // Validate links in batches for better performance\n const [homePageProductLinks, homePageCollectionLinks] =\n await Promise.all([\n context.validateLinksInBatches(\n extractedProductLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => context.validateProductExists(handle)\n ),\n context.validateLinksInBatches(\n extractedCollectionLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => context.validateCollectionExists(handle)\n ),\n ]);\n\n const jsonLd = html\n .match(\n /<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([^<]+)<\\/script>/g\n )\n ?.map((match) => {\n const afterGt = match.split(\">\")[1];\n return afterGt ? afterGt.replace(/<\\/script/g, \"\") : \"\";\n });\n const jsonLdData: JsonLdEntry[] | undefined = jsonLd?.map(\n (json) => JSON.parse(json) as JsonLdEntry\n );\n\n const headerLinks =\n html\n .match(\n /<(header|nav|div|section)\\b[^>]*\\b(?:id|class)=[\"'][^\"']*(?=.*shopify-section)(?=.*\\b(header|navigation|nav|menu)\\b)[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/\\1>/gi\n )\n ?.flatMap((header) => {\n const links = header\n .match(/href=[\"']([^\"']+)[\"']/g)\n ?.filter(\n (link) =>\n link.includes(\"/products/\") ||\n link.includes(\"/collections/\") ||\n link.includes(\"/pages/\")\n );\n return (\n links\n ?.map((link) => {\n const href = link.match(/href=[\"']([^\"']+)[\"']/)?.[1];\n if (\n href &&\n !href.startsWith(\"#\") &&\n !href.startsWith(\"javascript:\")\n ) {\n try {\n const url = new URL(href, context.storeDomain);\n return url.pathname.replace(/^\\/|\\/$/g, \"\");\n } catch {\n return href.replace(/^\\/|\\/$/g, \"\");\n }\n }\n return null;\n })\n .filter((item): item is string => Boolean(item)) ?? []\n );\n }) ?? [];\n\n const slug = generateStoreSlug(context.baseUrl);\n\n // Detect country information\n const countryDetection = await detectShopifyCountry(html);\n\n return {\n name: name || slug,\n domain: context.baseUrl,\n slug,\n title: title ?? null,\n description: description ?? null,\n logoUrl,\n socialLinks,\n contactLinks,\n headerLinks,\n showcase: {\n products: unique(homePageProductLinks ?? []),\n collections: unique(homePageCollectionLinks ?? []),\n },\n jsonLdData,\n techProvider: {\n name: \"shopify\",\n walletId: shopifyWalletId,\n subDomain: myShopifySubdomain ?? null,\n },\n country: countryDetection?.country || \"\",\n };\n */\n } catch (error) {\n context.handleFetchError(error, \"fetching store info\", context.baseUrl);\n }\n },\n };\n}\n"],"mappings":"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","names":["TurndownService","_a","validated","name","_a","_b","import_remeda","collectionsDto","_a","import_remeda"]}
|