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.
- package/README.md +23 -22
- package/dist/ai/enrich.d.ts +24 -3
- package/dist/ai/enrich.mjs +9 -1
- package/dist/{chunk-GNIBTUEK.mjs → chunk-2W623LCW.mjs} +384 -206
- package/dist/{chunk-ZF4M6GMB.mjs → chunk-ESTBP7AD.mjs} +51 -26
- package/dist/{chunk-RLVH7LEG.mjs → chunk-OA76XD32.mjs} +143 -7
- package/dist/{chunk-554O5ED6.mjs → chunk-QCB3U4AO.mjs} +12 -1
- package/dist/collections.d.ts +3 -3
- package/dist/collections.mjs +1 -1
- package/dist/index.d.ts +20 -6
- package/dist/index.mjs +68 -10
- package/dist/products.d.ts +21 -4
- package/dist/products.mjs +1 -1
- package/dist/store.d.ts +19 -7
- package/dist/store.mjs +3 -3
- package/dist/{types-luPg5O08.d.ts → types-BRXamZMS.d.ts} +14 -1
- package/dist/utils/detect-country.d.ts +1 -1
- package/dist/utils/func.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
274
|
-
if (!
|
|
275
|
-
throw new Error(
|
|
276
|
-
|
|
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
|
-
|
|
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
|
|
317
|
-
if (!
|
|
318
|
-
throw new Error(
|
|
319
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
186
|
-
collections:
|
|
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
|
|
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
|
|
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
|
-
|
|
262
|
-
|
|
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
|
-
|
|
245
|
+
uniqueHandles.map(
|
|
235
246
|
(collectionHandle) => findCollection(collectionHandle)
|
|
236
247
|
)
|
|
237
248
|
);
|
package/dist/collections.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { C as Collection, f as CurrencyCode, P as Product, b as ShopifyCollection } from './types-
|
|
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<
|
|
63
|
+
}) => Promise<Product[] | null>, getStoreInfo: () => Promise<ShopInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
|
|
64
64
|
|
|
65
65
|
export { type CollectionOperations, createCollectionOperations };
|
package/dist/collections.mjs
CHANGED
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 {
|
|
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
|
-
|
|
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<
|
|
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,
|
|
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-
|
|
6
|
+
} from "./chunk-QCB3U4AO.mjs";
|
|
7
7
|
import {
|
|
8
8
|
createProductOperations
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ESTBP7AD.mjs";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
11
|
+
createShopOperations,
|
|
12
|
+
getInfoForShop
|
|
13
|
+
} from "./chunk-OA76XD32.mjs";
|
|
14
14
|
import {
|
|
15
15
|
classifyProduct,
|
|
16
16
|
determineStoreType,
|
|
17
17
|
generateSEOContent
|
|
18
|
-
} from "./chunk-
|
|
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.
|
|
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
|
|
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
|
});
|
package/dist/products.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { f as CurrencyCode, P as Product, k as ProductClassification, l as SEOContent, S as ShopifyProduct, a as ShopifySingleProduct } from './types-
|
|
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
|
|
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<
|
|
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 };
|