shop-client 3.14.1 → 3.15.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.
@@ -7,7 +7,7 @@ import {
7
7
 
8
8
  // src/products.ts
9
9
  import { filter, isNonNullish } from "remeda";
10
- function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDto, productDto, getStoreInfo, findProduct) {
10
+ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDto, productDto, getStoreInfo, findProduct, ai) {
11
11
  const cacheExpiryMs = 5 * 60 * 1e3;
12
12
  const findCache = /* @__PURE__ */ new Map();
13
13
  const getCached = (key) => {
@@ -242,12 +242,6 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
242
242
  if (!productHandle || typeof productHandle !== "string") {
243
243
  throw new Error("Product handle is required and must be a string");
244
244
  }
245
- const apiKey = (options == null ? void 0 : options.apiKey) || process.env.OPENROUTER_API_KEY;
246
- if (!apiKey) {
247
- throw new Error(
248
- "Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY."
249
- );
250
- }
251
245
  const baseProduct = await operations.find(productHandle);
252
246
  if (!baseProduct) {
253
247
  return null;
@@ -255,7 +249,8 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
255
249
  const handle = baseProduct.handle;
256
250
  const { enrichProduct } = await import("./ai/enrich.mjs");
257
251
  const enriched = await enrichProduct(storeDomain, handle, {
258
- apiKey,
252
+ apiKey: options == null ? void 0 : options.apiKey,
253
+ openRouter: ai == null ? void 0 : ai.openRouter,
259
254
  useGfm: options == null ? void 0 : options.useGfm,
260
255
  inputType: options == null ? void 0 : options.inputType,
261
256
  model: options == null ? void 0 : options.model,
@@ -266,18 +261,28 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
266
261
  enriched_content: enriched.mergedMarkdown
267
262
  };
268
263
  },
269
- classify: async (productHandle, options) => {
264
+ enrichedPrompts: async (productHandle, options) => {
270
265
  if (!productHandle || typeof productHandle !== "string") {
271
266
  throw new Error("Product handle is required and must be a string");
272
267
  }
273
- const apiKey = (options == null ? void 0 : options.apiKey) || process.env.OPENROUTER_API_KEY;
274
- if (!apiKey) {
275
- throw new Error(
276
- "Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY."
277
- );
268
+ const baseProduct = await operations.find(productHandle);
269
+ if (!baseProduct) {
270
+ throw new Error("Product not found");
271
+ }
272
+ const handle = baseProduct.handle;
273
+ const { buildEnrichPromptForProduct } = await import("./ai/enrich.mjs");
274
+ return buildEnrichPromptForProduct(storeDomain, handle, {
275
+ useGfm: options == null ? void 0 : options.useGfm,
276
+ inputType: options == null ? void 0 : options.inputType,
277
+ outputFormat: options == null ? void 0 : options.outputFormat
278
+ });
279
+ },
280
+ classify: async (productHandle, options) => {
281
+ if (!productHandle || typeof productHandle !== "string") {
282
+ throw new Error("Product handle is required and must be a string");
278
283
  }
279
284
  const enrichedProduct = await operations.enriched(productHandle, {
280
- apiKey,
285
+ apiKey: options == null ? void 0 : options.apiKey,
281
286
  inputType: "html",
282
287
  model: options == null ? void 0 : options.model,
283
288
  outputFormat: "json"
@@ -304,20 +309,30 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
304
309
  }
305
310
  const { classifyProduct } = await import("./ai/enrich.mjs");
306
311
  const classification = await classifyProduct(productContent, {
307
- apiKey,
312
+ apiKey: options == null ? void 0 : options.apiKey,
313
+ openRouter: ai == null ? void 0 : ai.openRouter,
308
314
  model: options == null ? void 0 : options.model
309
315
  });
310
316
  return classification;
311
317
  },
312
- generateSEOContent: async (productHandle, options) => {
318
+ classifyPrompts: async (productHandle, options) => {
313
319
  if (!productHandle || typeof productHandle !== "string") {
314
320
  throw new Error("Product handle is required and must be a string");
315
321
  }
316
- const apiKey = (options == null ? void 0 : options.apiKey) || process.env.OPENROUTER_API_KEY;
317
- if (!apiKey) {
318
- throw new Error(
319
- "Missing OpenRouter API key. Pass options.apiKey or set OPENROUTER_API_KEY."
320
- );
322
+ const baseProduct = await operations.find(productHandle);
323
+ if (!baseProduct) {
324
+ throw new Error("Product not found");
325
+ }
326
+ const handle = baseProduct.handle;
327
+ const { buildClassifyPromptForProduct } = await import("./ai/enrich.mjs");
328
+ return buildClassifyPromptForProduct(storeDomain, handle, {
329
+ useGfm: options == null ? void 0 : options.useGfm,
330
+ inputType: options == null ? void 0 : options.inputType
331
+ });
332
+ },
333
+ generateSEOContent: async (productHandle, options) => {
334
+ if (!productHandle || typeof productHandle !== "string") {
335
+ throw new Error("Product handle is required and must be a string");
321
336
  }
322
337
  const baseProduct = await operations.find(productHandle);
323
338
  if (!baseProduct) return null;
@@ -330,7 +345,8 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
330
345
  };
331
346
  const { generateSEOContent: generateSEOContentLLM } = await import("./ai/enrich.mjs");
332
347
  const seo = await generateSEOContentLLM(payload, {
333
- apiKey,
348
+ apiKey: options == null ? void 0 : options.apiKey,
349
+ openRouter: ai == null ? void 0 : ai.openRouter,
334
350
  model: options == null ? void 0 : options.model
335
351
  });
336
352
  return seo;
@@ -355,10 +371,19 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
355
371
  */
356
372
  showcased: async () => {
357
373
  const storeInfo = await getStoreInfo();
374
+ const normalizedHandles = storeInfo.showcase.products.map((h) => {
375
+ var _a;
376
+ return (_a = h.split("?")[0]) == null ? void 0 : _a.replace(/^\/|\/$/g, "");
377
+ }).filter((base) => Boolean(base));
378
+ const seen = /* @__PURE__ */ new Set();
379
+ const uniqueHandles = [];
380
+ for (const base of normalizedHandles) {
381
+ if (seen.has(base)) continue;
382
+ seen.add(base);
383
+ uniqueHandles.push(base);
384
+ }
358
385
  const products = await Promise.all(
359
- storeInfo.showcase.products.map(
360
- (productHandle) => findProduct(productHandle)
361
- )
386
+ uniqueHandles.map((productHandle) => findProduct(productHandle))
362
387
  );
363
388
  return filter(products, isNonNullish);
364
389
  },
@@ -171,6 +171,19 @@ async function getInfoForShop(args, options) {
171
171
  (handle) => Boolean(handle)
172
172
  );
173
173
  }
174
+ const dedupeByNormalized = (arr) => {
175
+ var _a2;
176
+ const out = [];
177
+ const seen = /* @__PURE__ */ new Set();
178
+ for (const h of arr) {
179
+ const base = (_a2 = h == null ? void 0 : h.split("?")[0]) == null ? void 0 : _a2.replace(/^\/|\/$/g, "");
180
+ if (!base) continue;
181
+ if (seen.has(base)) continue;
182
+ seen.add(base);
183
+ out.push(base);
184
+ }
185
+ return out;
186
+ };
174
187
  const info = {
175
188
  name: name || slug,
176
189
  domain: sanitizeDomain(baseUrl),
@@ -182,8 +195,8 @@ async function getInfoForShop(args, options) {
182
195
  contactLinks,
183
196
  headerLinks,
184
197
  showcase: {
185
- products: unique(homePageProductLinks != null ? homePageProductLinks : []),
186
- collections: unique(homePageCollectionLinks != null ? homePageCollectionLinks : [])
198
+ products: dedupeByNormalized(homePageProductLinks != null ? homePageProductLinks : []),
199
+ collections: dedupeByNormalized(homePageCollectionLinks != null ? homePageCollectionLinks : [])
187
200
  },
188
201
  jsonLdData: ((_p = (_o = html.match(
189
202
  /<script[^>]*type="application\/ld\+json"[^>]*>([^<]+)<\/script>/g
@@ -230,8 +230,19 @@ function createCollectionOperations(baseUrl, storeDomain, fetchCollections, coll
230
230
  */
231
231
  showcased: async () => {
232
232
  const storeInfo = await getStoreInfo();
233
+ const normalizedHandles = storeInfo.showcase.collections.map((h) => {
234
+ var _a;
235
+ return (_a = h.split("?")[0]) == null ? void 0 : _a.replace(/^\/|\/$/g, "");
236
+ }).filter((base) => Boolean(base));
237
+ const seen = /* @__PURE__ */ new Set();
238
+ const uniqueHandles = [];
239
+ for (const base of normalizedHandles) {
240
+ if (seen.has(base)) continue;
241
+ seen.add(base);
242
+ uniqueHandles.push(base);
243
+ }
233
244
  const collections = await Promise.all(
234
- storeInfo.showcase.collections.map(
245
+ uniqueHandles.map(
235
246
  (collectionHandle) => findCollection(collectionHandle)
236
247
  )
237
248
  );
@@ -1,5 +1,5 @@
1
1
  import { ShopInfo } from './store.js';
2
- import { C as Collection, f as CurrencyCode, P as Product, b as ShopifyCollection } from './types-luPg5O08.js';
2
+ import { C as Collection, f as CurrencyCode, P as Product, b as ShopifyCollection } from './types-BRXamZMS.js';
3
3
 
4
4
  /**
5
5
  * Interface for collection operations
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createCollectionOperations
3
- } from "./chunk-554O5ED6.mjs";
3
+ } from "./chunk-QCB3U4AO.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-U3RQRBXZ.mjs";
6
6
  export {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ShopifyProduct, P as Product, a as ShopifySingleProduct, b as ShopifyCollection, C as Collection, J as JsonLdEntry, c as StoreTypeBreakdown } from './types-luPg5O08.js';
2
- export { d as CountryDetectionResult, e as CountryScores, f as CurrencyCode, L as LocalizedPricing, M as MetaTag, g as ProductImage, h as ProductOption, i as ProductVariant, j as ProductVariantImage } from './types-luPg5O08.js';
1
+ import { O as OpenRouterConfig, S as ShopifyProduct, P as Product, a as ShopifySingleProduct, b as ShopifyCollection, C as Collection, J as JsonLdEntry, c as StoreTypeBreakdown } from './types-BRXamZMS.js';
2
+ export { d as CountryDetectionResult, e as CountryScores, f as CurrencyCode, L as LocalizedPricing, M as MetaTag, g as ProductImage, h as ProductOption, i as ProductVariant, j as ProductVariantImage } from './types-BRXamZMS.js';
3
3
  import { CheckoutOperations } from './checkout.js';
4
4
  import { CollectionOperations } from './collections.js';
5
5
  import { ProductOperations } from './products.js';
@@ -27,11 +27,13 @@ export { configureRateLimit } from './utils/rate-limit.js';
27
27
  */
28
28
  type ShopClientOptions = {
29
29
  cacheTTL?: number;
30
+ openRouter?: OpenRouterConfig;
30
31
  };
31
32
  declare class ShopClient {
32
33
  private storeDomain;
33
34
  private baseUrl;
34
35
  private storeSlug;
36
+ private openRouter?;
35
37
  private validationCache;
36
38
  private cacheExpiry;
37
39
  private cacheTimestamps;
@@ -186,4 +188,4 @@ declare class ShopClient {
186
188
  }): Promise<StoreTypeBreakdown>;
187
189
  }
188
190
 
189
- export { CheckoutOperations, Collection, CollectionOperations, OpenGraphMeta, Product, ProductOperations, ShopClient, type ShopClientOptions, ShopInfo, ShopOperations, StoreTypeBreakdown };
191
+ export { CheckoutOperations, Collection, CollectionOperations, OpenGraphMeta, OpenRouterConfig, Product, ProductOperations, ShopClient, type ShopClientOptions, ShopInfo, ShopOperations, StoreTypeBreakdown };
package/dist/index.mjs CHANGED
@@ -3,19 +3,19 @@ import {
3
3
  } from "./chunk-W4SF6W2P.mjs";
4
4
  import {
5
5
  createCollectionOperations
6
- } from "./chunk-554O5ED6.mjs";
6
+ } from "./chunk-QCB3U4AO.mjs";
7
7
  import {
8
8
  createProductOperations
9
- } from "./chunk-ZF4M6GMB.mjs";
9
+ } from "./chunk-ESTBP7AD.mjs";
10
10
  import {
11
11
  createShopOperations,
12
12
  getInfoForShop
13
- } from "./chunk-7IMI76JZ.mjs";
13
+ } from "./chunk-OA76XD32.mjs";
14
14
  import {
15
15
  classifyProduct,
16
16
  determineStoreType,
17
17
  generateSEOContent
18
- } from "./chunk-GNIBTUEK.mjs";
18
+ } from "./chunk-2W623LCW.mjs";
19
19
  import {
20
20
  configureRateLimit,
21
21
  rateLimitedFetch
@@ -55,7 +55,7 @@ async function determineStoreTypeForStore(args) {
55
55
  collections: collectionsSample
56
56
  }
57
57
  },
58
- { apiKey: args.apiKey, model: args.model }
58
+ { apiKey: args.apiKey, model: args.model, openRouter: args.openRouter }
59
59
  );
60
60
  return breakdown;
61
61
  }
@@ -353,6 +353,7 @@ var ShopClient = class {
353
353
  if (typeof (options == null ? void 0 : options.cacheTTL) === "number" && options.cacheTTL > 0) {
354
354
  this.cacheExpiry = options.cacheTTL;
355
355
  }
356
+ this.openRouter = options == null ? void 0 : options.openRouter;
356
357
  this.shopOperations = createShopOperations({
357
358
  baseUrl: this.baseUrl,
358
359
  storeDomain: this.storeDomain,
@@ -368,7 +369,8 @@ var ShopClient = class {
368
369
  this.productsDto.bind(this),
369
370
  this.productDto.bind(this),
370
371
  () => this.getInfo(),
371
- (handle) => this.products.find(handle)
372
+ (handle) => this.products.find(handle),
373
+ { openRouter: this.openRouter }
372
374
  );
373
375
  this.collections = createCollectionOperations(
374
376
  this.baseUrl,
@@ -786,12 +788,14 @@ var ShopClient = class {
786
788
  */
787
789
  async determineStoreType(options) {
788
790
  try {
791
+ const openRouter = this.openRouter;
789
792
  const breakdown = await determineStoreTypeForStore({
790
793
  baseUrl: this.baseUrl,
791
794
  getInfo: () => this.getInfo(),
792
795
  findProduct: (handle) => this.products.find(handle),
793
796
  apiKey: options == null ? void 0 : options.apiKey,
794
797
  model: options == null ? void 0 : options.model,
798
+ openRouter,
795
799
  maxShowcaseProducts: options == null ? void 0 : options.maxShowcaseProducts,
796
800
  maxShowcaseCollections: options == null ? void 0 : options.maxShowcaseCollections
797
801
  });
@@ -1,5 +1,5 @@
1
1
  import { ShopInfo } from './store.js';
2
- import { f as CurrencyCode, P as Product, k as ProductClassification, l as SEOContent, S as ShopifyProduct, a as ShopifySingleProduct } from './types-luPg5O08.js';
2
+ import { f as CurrencyCode, P as Product, k as ProductClassification, l as SEOContent, S as ShopifyProduct, a as ShopifySingleProduct, O as OpenRouterConfig } from './types-BRXamZMS.js';
3
3
 
4
4
  /**
5
5
  * Interface for product operations
@@ -27,7 +27,7 @@ interface ProductOperations {
27
27
  }): Promise<Product | null>;
28
28
  /**
29
29
  * Finds a product by handle and enriches its content using LLM.
30
- * Requires an OpenAI API key via options.apiKey or process.env.OPENAI_API_KEY.
30
+ * Requires an OpenRouter API key via options.apiKey or ShopClient options.
31
31
  */
32
32
  enriched(productHandle: string, options?: {
33
33
  apiKey?: string;
@@ -36,10 +36,25 @@ interface ProductOperations {
36
36
  model?: string;
37
37
  outputFormat?: "markdown" | "json";
38
38
  }): Promise<Product | null>;
39
+ enrichedPrompts(productHandle: string, options?: {
40
+ useGfm?: boolean;
41
+ inputType?: "markdown" | "html";
42
+ outputFormat?: "markdown" | "json";
43
+ }): Promise<{
44
+ system: string;
45
+ user: string;
46
+ }>;
39
47
  classify(productHandle: string, options?: {
40
48
  apiKey?: string;
41
49
  model?: string;
42
50
  }): Promise<ProductClassification | null>;
51
+ classifyPrompts(productHandle: string, options?: {
52
+ useGfm?: boolean;
53
+ inputType?: "markdown" | "html";
54
+ }): Promise<{
55
+ system: string;
56
+ user: string;
57
+ }>;
43
58
  /**
44
59
  * Generate SEO and marketing content for a product.
45
60
  */
@@ -77,6 +92,8 @@ interface ProductOperations {
77
92
  /**
78
93
  * Creates product operations for a store instance
79
94
  */
80
- declare function createProductOperations(baseUrl: string, storeDomain: string, fetchProducts: (page: number, limit: number) => Promise<Product[] | null>, productsDto: (products: ShopifyProduct[]) => Product[] | null, productDto: (product: ShopifySingleProduct) => Product, getStoreInfo: () => Promise<ShopInfo>, findProduct: (handle: string) => Promise<Product | null>): ProductOperations;
95
+ declare function createProductOperations(baseUrl: string, storeDomain: string, fetchProducts: (page: number, limit: number) => Promise<Product[] | null>, productsDto: (products: ShopifyProduct[]) => Product[] | null, productDto: (product: ShopifySingleProduct) => Product, getStoreInfo: () => Promise<ShopInfo>, findProduct: (handle: string) => Promise<Product | null>, ai?: {
96
+ openRouter?: OpenRouterConfig;
97
+ }): ProductOperations;
81
98
 
82
99
  export { type ProductOperations, createProductOperations };
package/dist/products.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createProductOperations
3
- } from "./chunk-ZF4M6GMB.mjs";
3
+ } from "./chunk-ESTBP7AD.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-U3RQRBXZ.mjs";
6
6
  export {
package/dist/store.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { J as JsonLdEntry, d as CountryDetectionResult, f as CurrencyCode } from './types-luPg5O08.js';
1
+ import { J as JsonLdEntry, d as CountryDetectionResult, f as CurrencyCode } from './types-BRXamZMS.js';
2
2
 
3
3
  /**
4
4
  * Store operations interface for managing store-related functionality.
package/dist/store.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createShopOperations
3
- } from "./chunk-7IMI76JZ.mjs";
3
+ } from "./chunk-OA76XD32.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-G7OCMGA6.mjs";
6
6
  import "./chunk-U3RQRBXZ.mjs";
@@ -1,3 +1,16 @@
1
+ type OpenRouterConfig = {
2
+ apiKey?: string;
3
+ model?: string;
4
+ fallbackModels?: string[];
5
+ baseUrl?: string;
6
+ siteUrl?: string;
7
+ appTitle?: string;
8
+ offline?: boolean;
9
+ };
10
+ type SystemUserPrompt = {
11
+ system: string;
12
+ user: string;
13
+ };
1
14
  /**
2
15
  * Base timestamp fields used across Shopify entities.
3
16
  */
@@ -398,4 +411,4 @@ type SEOContent = {
398
411
  };
399
412
  type StoreTypeBreakdown = Partial<Record<"adult_male" | "adult_female" | "kid_male" | "kid_female" | "generic", Partial<Record<"clothing" | "beauty" | "accessories" | "home-decor" | "food-and-beverages", string[]>>>>;
400
413
 
401
- export type { Collection as C, JsonLdEntry as J, LocalizedPricing as L, MetaTag as M, Product as P, ShopifyProduct as S, ShopifySingleProduct as a, ShopifyCollection as b, StoreTypeBreakdown as c, CountryDetectionResult as d, CountryScores as e, CurrencyCode as f, ProductImage as g, ProductOption as h, ProductVariant as i, ProductVariantImage as j, ProductClassification as k, SEOContent as l };
414
+ export type { Collection as C, JsonLdEntry as J, LocalizedPricing as L, MetaTag as M, OpenRouterConfig as O, Product as P, ShopifyProduct as S, ShopifySingleProduct as a, ShopifyCollection as b, StoreTypeBreakdown as c, CountryDetectionResult as d, CountryScores as e, CurrencyCode as f, ProductImage as g, ProductOption as h, ProductVariant as i, ProductVariantImage as j, ProductClassification as k, SEOContent as l, SystemUserPrompt as m };
@@ -1,4 +1,4 @@
1
- import { d as CountryDetectionResult } from '../types-luPg5O08.js';
1
+ import { d as CountryDetectionResult } from '../types-BRXamZMS.js';
2
2
 
3
3
  /**
4
4
  * Detects the country of a Shopify store by analyzing various signals in the HTML content.
@@ -1,4 +1,4 @@
1
- import { f as CurrencyCode } from '../types-luPg5O08.js';
1
+ import { f as CurrencyCode } from '../types-BRXamZMS.js';
2
2
 
3
3
  declare function extractDomainWithoutSuffix(domain: string): string | null;
4
4
  declare function generateStoreSlug(domain: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shop-client",
3
- "version": "3.14.1",
3
+ "version": "3.15.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",