shop-client 3.14.0 → 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
  },
@@ -12,7 +12,7 @@ import {
12
12
 
13
13
  // src/client/get-info.ts
14
14
  import { unique } from "remeda";
15
- async function getInfoForStore(args, options) {
15
+ async function getInfoForShop(args, options) {
16
16
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
17
17
  const {
18
18
  baseUrl,
@@ -171,6 +171,19 @@ async function getInfoForStore(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 getInfoForStore(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
@@ -206,7 +219,7 @@ async function getInfoForStore(args, options) {
206
219
  }
207
220
 
208
221
  // src/store.ts
209
- function createStoreOperations(context) {
222
+ function createShopOperations(context) {
210
223
  return {
211
224
  /**
212
225
  * Fetches comprehensive store information including metadata, social links, and showcase content.
@@ -242,7 +255,7 @@ function createStoreOperations(context) {
242
255
  */
243
256
  info: async () => {
244
257
  try {
245
- const { info } = await getInfoForStore({
258
+ const { info } = await getInfoForShop({
246
259
  baseUrl: context.baseUrl,
247
260
  storeDomain: context.storeDomain,
248
261
  validateProductExists: context.validateProductExists,
@@ -253,11 +266,134 @@ function createStoreOperations(context) {
253
266
  } catch (error) {
254
267
  context.handleFetchError(error, "fetching store info", context.baseUrl);
255
268
  }
269
+ },
270
+ getMetaData: async () => {
271
+ try {
272
+ const response = await rateLimitedFetch(context.baseUrl, {
273
+ rateLimitClass: "store:metadata",
274
+ timeoutMs: 7e3
275
+ });
276
+ if (!response.ok) {
277
+ throw new Error(`HTTP error! status: ${response.status}`);
278
+ }
279
+ const html = await response.text();
280
+ const getPropertyMetaTag = (property) => {
281
+ const regex = new RegExp(
282
+ `<meta[^>]*property=["']${property}["'][^>]*content=["'](.*?)["']`
283
+ );
284
+ const match = html.match(regex);
285
+ return match ? match[1] : null;
286
+ };
287
+ const siteName = getPropertyMetaTag("og:site_name");
288
+ const title = getPropertyMetaTag("og:title");
289
+ const description = getPropertyMetaTag("og:description");
290
+ const url = getPropertyMetaTag("og:url");
291
+ const type = getPropertyMetaTag("og:type");
292
+ const image = getPropertyMetaTag("og:image");
293
+ const imageSecureUrl = getPropertyMetaTag("og:image:secure_url");
294
+ return {
295
+ siteName: siteName != null ? siteName : null,
296
+ title: title != null ? title : null,
297
+ description: description != null ? description : null,
298
+ url: url != null ? url : null,
299
+ type: type != null ? type : null,
300
+ image: image != null ? image : null,
301
+ imageSecureUrl: imageSecureUrl != null ? imageSecureUrl : null
302
+ };
303
+ } catch (error) {
304
+ context.handleFetchError(
305
+ error,
306
+ "fetching store metadata",
307
+ context.baseUrl
308
+ );
309
+ }
310
+ },
311
+ getJsonLd: async () => {
312
+ var _a, _b;
313
+ try {
314
+ const response = await rateLimitedFetch(context.baseUrl, {
315
+ rateLimitClass: "store:jsonld",
316
+ timeoutMs: 7e3
317
+ });
318
+ if (!response.ok) {
319
+ throw new Error(`HTTP error! status: ${response.status}`);
320
+ }
321
+ const html = await response.text();
322
+ const scripts = (_b = (_a = html.match(
323
+ /<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g
324
+ )) == null ? void 0 : _a.map(
325
+ (match) => match.replace(/^.*?>/, "").replace(/<\/script>$/i, "")
326
+ )) != null ? _b : [];
327
+ const parsed = scripts.map((json) => {
328
+ try {
329
+ return JSON.parse(json);
330
+ } catch {
331
+ return void 0;
332
+ }
333
+ }).filter((x) => !!x);
334
+ return parsed.length ? parsed : void 0;
335
+ } catch (error) {
336
+ context.handleFetchError(
337
+ error,
338
+ "fetching store JSON-LD",
339
+ context.baseUrl
340
+ );
341
+ }
342
+ },
343
+ getHeaderLinks: async () => {
344
+ var _a;
345
+ try {
346
+ const response = await rateLimitedFetch(context.baseUrl, {
347
+ rateLimitClass: "store:header",
348
+ timeoutMs: 7e3
349
+ });
350
+ if (!response.ok) {
351
+ throw new Error(`HTTP error! status: ${response.status}`);
352
+ }
353
+ const html = await response.text();
354
+ const sections = (_a = html.match(
355
+ /<(header|nav|div|section)\b[^>]*\b(?:id|class)=["'][^"']*(?=.*shopify-section)(?=.*\b(header|navigation|nav|menu)\b)[^"']*["'][^>]*>[\s\S]*?<\/\1>/gi
356
+ )) != null ? _a : [];
357
+ const links = sections.flatMap((section) => {
358
+ var _a2;
359
+ return (_a2 = section.match(/href=["']([^"']+)["']/g)) != null ? _a2 : [];
360
+ }).map((link) => {
361
+ var _a2;
362
+ return (_a2 = link.match(/href=["']([^"']+)["']/)) == null ? void 0 : _a2[1];
363
+ }).filter((href) => !!href).filter(
364
+ (href) => href.includes("/products/") || href.includes("/collections/") || href.includes("/pages/")
365
+ ).map((href) => {
366
+ try {
367
+ if (href.startsWith("//"))
368
+ return new URL(`https:${href}`).pathname;
369
+ if (href.startsWith("/"))
370
+ return new URL(href, context.storeDomain).pathname;
371
+ return new URL(href).pathname;
372
+ } catch {
373
+ return href;
374
+ }
375
+ }).map((p) => p.replace(/^\/|\/$/g, ""));
376
+ const seen = /* @__PURE__ */ new Set();
377
+ const deduped = [];
378
+ for (const l of links) {
379
+ if (!seen.has(l)) {
380
+ seen.add(l);
381
+ deduped.push(l);
382
+ }
383
+ }
384
+ return deduped;
385
+ } catch (error) {
386
+ context.handleFetchError(
387
+ error,
388
+ "fetching header links",
389
+ context.baseUrl
390
+ );
391
+ }
256
392
  }
257
393
  };
258
394
  }
259
395
 
260
396
  export {
261
- getInfoForStore,
262
- createStoreOperations
397
+ getInfoForShop,
398
+ createShopOperations
263
399
  };
@@ -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
- import { StoreInfo } from './store.js';
2
- import { C as Collection, f as CurrencyCode, P as Product, b as ShopifyCollection } from './types-luPg5O08.js';
1
+ import { ShopInfo } from './store.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
@@ -60,6 +60,6 @@ interface CollectionOperations {
60
60
  declare function createCollectionOperations(baseUrl: string, storeDomain: string, fetchCollections: (page: number, limit: number) => Promise<Collection[] | null>, collectionsDto: (collections: ShopifyCollection[]) => Collection[], fetchPaginatedProductsFromCollection: (collectionHandle: string, options?: {
61
61
  page?: number;
62
62
  limit?: number;
63
- }) => Promise<Product[] | null>, getStoreInfo: () => Promise<StoreInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
63
+ }) => Promise<Product[] | null>, getStoreInfo: () => Promise<ShopInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
64
64
 
65
65
  export { type CollectionOperations, createCollectionOperations };
@@ -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,9 +1,9 @@
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';
1
3
  import { CheckoutOperations } from './checkout.js';
2
4
  import { CollectionOperations } from './collections.js';
3
5
  import { ProductOperations } from './products.js';
4
- import { StoreOperations, StoreInfo } from './store.js';
5
- import { S as ShopifyProduct, P as Product, a as ShopifySingleProduct, b as ShopifyCollection, C as Collection, c as StoreTypeBreakdown } from './types-luPg5O08.js';
6
- 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';
6
+ import { ShopOperations, ShopInfo, OpenGraphMeta } from './store.js';
7
7
  export { classifyProduct, generateSEOContent } from './ai/enrich.js';
8
8
  export { detectShopCountry } from './utils/detect-country.js';
9
9
  export { calculateDiscount, extractDomainWithoutSuffix, genProductSlug, generateStoreSlug, safeParseDate, sanitizeDomain } from './utils/func.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;
@@ -40,10 +42,13 @@ declare class ShopClient {
40
42
  private infoCacheValue?;
41
43
  private infoCacheTimestamp?;
42
44
  private infoInFlight?;
45
+ private metaCacheValue?;
46
+ private metaCacheTimestamp?;
47
+ private metaInFlight?;
43
48
  products: ProductOperations;
44
49
  collections: CollectionOperations;
45
50
  checkout: CheckoutOperations;
46
- storeOperations: StoreOperations;
51
+ shopOperations: ShopOperations;
47
52
  /**
48
53
  * Creates a new ShopClient instance for interacting with a Shopify store.
49
54
  *
@@ -156,12 +161,21 @@ declare class ShopClient {
156
161
  force?: boolean;
157
162
  validateShowcase?: boolean;
158
163
  validationBatchSize?: number;
159
- }): Promise<StoreInfo>;
164
+ }): Promise<ShopInfo>;
160
165
  /**
161
166
  * Manually clear the cached store info.
162
167
  * The next call to `getInfo()` will fetch fresh data regardless of TTL.
163
168
  */
164
169
  clearInfoCache(): void;
170
+ /**
171
+ * Fetch OpenGraph metadata from the store homepage.
172
+ * Returns only `og:*` fields without additional parsing or validation.
173
+ */
174
+ getMetaData(options?: {
175
+ force?: boolean;
176
+ }): Promise<OpenGraphMeta>;
177
+ getJsonLd(): Promise<JsonLdEntry[] | undefined>;
178
+ getHeaderLinks(): Promise<string[]>;
165
179
  /**
166
180
  * Determine the store's primary vertical and target audience.
167
181
  * Uses `getInfo()` internally; no input required.
@@ -174,4 +188,4 @@ declare class ShopClient {
174
188
  }): Promise<StoreTypeBreakdown>;
175
189
  }
176
190
 
177
- export { CheckoutOperations, Collection, CollectionOperations, Product, ProductOperations, ShopClient, type ShopClientOptions, StoreInfo, StoreOperations, 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
- createStoreOperations,
12
- getInfoForStore
13
- } from "./chunk-RLVH7LEG.mjs";
11
+ createShopOperations,
12
+ getInfoForShop
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,7 +353,8 @@ 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.storeOperations = createStoreOperations({
356
+ this.openRouter = options == null ? void 0 : options.openRouter;
357
+ this.shopOperations = createShopOperations({
357
358
  baseUrl: this.baseUrl,
358
359
  storeDomain: this.storeDomain,
359
360
  validateProductExists: this.validateProductExists.bind(this),
@@ -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,
@@ -688,7 +690,7 @@ var ShopClient = class {
688
690
  return await this.infoInFlight;
689
691
  }
690
692
  this.infoInFlight = (async () => {
691
- const { info, currencyCode } = await getInfoForStore(
693
+ const { info, currencyCode } = await getInfoForShop(
692
694
  {
693
695
  baseUrl: this.baseUrl,
694
696
  storeDomain: this.storeDomain,
@@ -726,18 +728,74 @@ var ShopClient = class {
726
728
  this.infoCacheValue = void 0;
727
729
  this.infoCacheTimestamp = void 0;
728
730
  }
731
+ /**
732
+ * Fetch OpenGraph metadata from the store homepage.
733
+ * Returns only `og:*` fields without additional parsing or validation.
734
+ */
735
+ async getMetaData(options) {
736
+ try {
737
+ if ((options == null ? void 0 : options.force) === true) {
738
+ this.metaCacheValue = void 0;
739
+ this.metaCacheTimestamp = void 0;
740
+ }
741
+ if (this.metaCacheValue && this.metaCacheTimestamp !== void 0 && Date.now() - this.metaCacheTimestamp < this.cacheExpiry) {
742
+ return this.metaCacheValue;
743
+ }
744
+ if (this.metaInFlight) {
745
+ return await this.metaInFlight;
746
+ }
747
+ this.metaInFlight = (async () => {
748
+ const meta = await this.shopOperations.getMetaData();
749
+ this.metaCacheValue = meta;
750
+ this.metaCacheTimestamp = Date.now();
751
+ return meta;
752
+ })();
753
+ try {
754
+ const result = await this.metaInFlight;
755
+ return result;
756
+ } finally {
757
+ this.metaInFlight = void 0;
758
+ }
759
+ } catch (error) {
760
+ throw this.handleFetchError(
761
+ error,
762
+ "fetching store metadata",
763
+ this.baseUrl
764
+ );
765
+ }
766
+ }
767
+ async getJsonLd() {
768
+ try {
769
+ return await this.shopOperations.getJsonLd();
770
+ } catch (error) {
771
+ throw this.handleFetchError(
772
+ error,
773
+ "fetching store JSON-LD",
774
+ this.baseUrl
775
+ );
776
+ }
777
+ }
778
+ async getHeaderLinks() {
779
+ try {
780
+ return await this.shopOperations.getHeaderLinks();
781
+ } catch (error) {
782
+ throw this.handleFetchError(error, "fetching header links", this.baseUrl);
783
+ }
784
+ }
729
785
  /**
730
786
  * Determine the store's primary vertical and target audience.
731
787
  * Uses `getInfo()` internally; no input required.
732
788
  */
733
789
  async determineStoreType(options) {
734
790
  try {
791
+ const openRouter = this.openRouter;
735
792
  const breakdown = await determineStoreTypeForStore({
736
793
  baseUrl: this.baseUrl,
737
794
  getInfo: () => this.getInfo(),
738
795
  findProduct: (handle) => this.products.find(handle),
739
796
  apiKey: options == null ? void 0 : options.apiKey,
740
797
  model: options == null ? void 0 : options.model,
798
+ openRouter,
741
799
  maxShowcaseProducts: options == null ? void 0 : options.maxShowcaseProducts,
742
800
  maxShowcaseCollections: options == null ? void 0 : options.maxShowcaseCollections
743
801
  });
@@ -1,5 +1,5 @@
1
- import { StoreInfo } 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';
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, 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<StoreInfo>, 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 {