shop-client 3.16.0 → 3.18.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 +74 -7
- package/dist/ai/enrich.d.ts +1 -1
- package/dist/ai/enrich.mjs +2 -2
- package/dist/{chunk-QCB3U4AO.mjs → chunk-242GBM2V.mjs} +136 -14
- package/dist/{chunk-ZX4IG4TY.mjs → chunk-5TGMDRUF.mjs} +88 -44
- package/dist/chunk-6XDBGOFZ.mjs +804 -0
- package/dist/{chunk-D5MTUWFO.mjs → chunk-O77Z6OBJ.mjs} +21 -6
- package/dist/{chunk-G7OCMGA6.mjs → chunk-SBHTEKLB.mjs} +3 -1
- package/dist/{chunk-OA76XD32.mjs → chunk-YWAW6C74.mjs} +5 -5
- package/dist/collections.d.ts +16 -2
- package/dist/collections.mjs +2 -2
- package/dist/index.d.ts +8 -4
- package/dist/index.mjs +180 -79
- package/dist/products.d.ts +65 -2
- package/dist/products.mjs +2 -2
- package/dist/store.d.ts +1 -1
- package/dist/store.mjs +3 -3
- package/dist/{types-BRXamZMS.d.ts → types-B4WDm14E.d.ts} +58 -1
- package/dist/utils/detect-country.d.ts +1 -1
- package/dist/utils/detect-country.mjs +1 -1
- package/dist/utils/func.d.ts +1 -1
- package/dist/utils/rate-limit.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-THCO3JT4.mjs +0 -593
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
rateLimitedFetch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-O77Z6OBJ.mjs";
|
|
4
4
|
|
|
5
5
|
// src/ai/enrich.ts
|
|
6
6
|
import TurndownService from "turndown";
|
|
@@ -30,6 +30,42 @@ var TURNDOWN_REMOVE_CLASSNAMES = [
|
|
|
30
30
|
"product-atc-wrapper"
|
|
31
31
|
];
|
|
32
32
|
var TURNDOWN_REMOVE_NODE_NAMES = ["button", "input", "select", "label"];
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
function isOneOf(values, value) {
|
|
37
|
+
return typeof value === "string" && values.includes(value);
|
|
38
|
+
}
|
|
39
|
+
function toOptionalStringOrNull(value) {
|
|
40
|
+
return typeof value === "string" ? value : null;
|
|
41
|
+
}
|
|
42
|
+
function getErrorMessage(err) {
|
|
43
|
+
if (err instanceof Error) return err.message;
|
|
44
|
+
return String(err);
|
|
45
|
+
}
|
|
46
|
+
function resolveGfmPlugin(mod) {
|
|
47
|
+
if (!isRecord(mod)) return mod;
|
|
48
|
+
const gfm = mod.gfm;
|
|
49
|
+
if (typeof gfm === "function" || isRecord(gfm)) return gfm;
|
|
50
|
+
const def = mod.default;
|
|
51
|
+
if (isRecord(def)) {
|
|
52
|
+
const defGfm = def.gfm;
|
|
53
|
+
if (typeof defGfm === "function" || isRecord(defGfm)) return defGfm;
|
|
54
|
+
return def;
|
|
55
|
+
}
|
|
56
|
+
return mod;
|
|
57
|
+
}
|
|
58
|
+
function extractOpenRouterContent(data) {
|
|
59
|
+
if (!isRecord(data)) return null;
|
|
60
|
+
const choices = data.choices;
|
|
61
|
+
if (!Array.isArray(choices) || choices.length === 0) return null;
|
|
62
|
+
const first = choices[0];
|
|
63
|
+
if (!isRecord(first)) return null;
|
|
64
|
+
const message = first.message;
|
|
65
|
+
if (!isRecord(message)) return null;
|
|
66
|
+
const content = message.content;
|
|
67
|
+
return typeof content === "string" ? content : null;
|
|
68
|
+
}
|
|
33
69
|
var cachedGfmPlugin;
|
|
34
70
|
var gfmPluginPromise;
|
|
35
71
|
var cachedTurndownPlain;
|
|
@@ -38,8 +74,7 @@ async function loadGfmPlugin() {
|
|
|
38
74
|
if (cachedGfmPlugin) return cachedGfmPlugin;
|
|
39
75
|
if (gfmPluginPromise) return gfmPluginPromise;
|
|
40
76
|
gfmPluginPromise = import("turndown-plugin-gfm").then((mod) => {
|
|
41
|
-
|
|
42
|
-
const resolved = (_d = (_c = (_b = mod == null ? void 0 : mod.gfm) != null ? _b : (_a = mod == null ? void 0 : mod.default) == null ? void 0 : _a.gfm) != null ? _c : mod == null ? void 0 : mod.default) != null ? _d : mod;
|
|
77
|
+
const resolved = resolveGfmPlugin(mod);
|
|
43
78
|
cachedGfmPlugin = resolved;
|
|
44
79
|
return resolved;
|
|
45
80
|
}).finally(() => {
|
|
@@ -55,7 +90,7 @@ function configureTurndown(td) {
|
|
|
55
90
|
});
|
|
56
91
|
}
|
|
57
92
|
const removeByClass = (className) => td.remove((node) => {
|
|
58
|
-
const cls = typeof node.getAttribute === "function" ? node.getAttribute("class") || "" : "";
|
|
93
|
+
const cls = isRecord(node) && typeof node.getAttribute === "function" ? String(node.getAttribute("class") || "") : "";
|
|
59
94
|
return cls.split(/\s+/).includes(className);
|
|
60
95
|
});
|
|
61
96
|
for (const className of TURNDOWN_REMOVE_CLASSNAMES) {
|
|
@@ -332,16 +367,18 @@ async function mergeWithLLM(bodyInput, pageInput, options) {
|
|
|
332
367
|
throw new Error(`LLM JSON schema invalid: ${schema.error}`);
|
|
333
368
|
}
|
|
334
369
|
const value = obj.value;
|
|
335
|
-
if (
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
370
|
+
if (isRecord(value)) {
|
|
371
|
+
const images = value.images;
|
|
372
|
+
if (Array.isArray(images)) {
|
|
373
|
+
const filtered = images.filter((url) => typeof url === "string").filter((url) => {
|
|
374
|
+
const u = url.toLowerCase();
|
|
375
|
+
const looksLikeProductImage = SHOPIFY_PRODUCT_IMAGE_PATTERNS.some(
|
|
376
|
+
(p) => u.includes(p)
|
|
377
|
+
);
|
|
378
|
+
return !looksLikeProductImage;
|
|
379
|
+
});
|
|
380
|
+
value.images = filtered.length > 0 ? filtered : null;
|
|
381
|
+
}
|
|
345
382
|
}
|
|
346
383
|
return JSON.stringify(value);
|
|
347
384
|
}
|
|
@@ -352,7 +389,7 @@ function safeParseJson(input) {
|
|
|
352
389
|
const v = JSON.parse(input);
|
|
353
390
|
return { ok: true, value: v };
|
|
354
391
|
} catch (err) {
|
|
355
|
-
return { ok: false, error: (err
|
|
392
|
+
return { ok: false, error: getErrorMessage(err) || "Failed to parse JSON" };
|
|
356
393
|
}
|
|
357
394
|
}
|
|
358
395
|
function validateStructuredJson(obj) {
|
|
@@ -360,16 +397,16 @@ function validateStructuredJson(obj) {
|
|
|
360
397
|
return { ok: false, error: "Top-level must be a JSON object" };
|
|
361
398
|
}
|
|
362
399
|
const o = obj;
|
|
363
|
-
if ("title" in o && !(o.title === null || typeof o.title === "string")) {
|
|
400
|
+
if ("title" in o && !(o.title === null || typeof o.title === "string" || o.title === void 0)) {
|
|
364
401
|
return { ok: false, error: "title must be null or string" };
|
|
365
402
|
}
|
|
366
|
-
if ("description" in o && !(o.description === null || typeof o.description === "string")) {
|
|
403
|
+
if ("description" in o && !(o.description === null || typeof o.description === "string" || o.description === void 0)) {
|
|
367
404
|
return { ok: false, error: "description must be null or string" };
|
|
368
405
|
}
|
|
369
|
-
if ("fit" in o && !(o.fit === null || typeof o.fit === "string")) {
|
|
406
|
+
if ("fit" in o && !(o.fit === null || typeof o.fit === "string" || o.fit === void 0)) {
|
|
370
407
|
return { ok: false, error: "fit must be null or string" };
|
|
371
408
|
}
|
|
372
|
-
if ("returnPolicy" in o && !(o.returnPolicy === null || typeof o.returnPolicy === "string")) {
|
|
409
|
+
if ("returnPolicy" in o && !(o.returnPolicy === null || typeof o.returnPolicy === "string" || o.returnPolicy === void 0)) {
|
|
373
410
|
return { ok: false, error: "returnPolicy must be null or string" };
|
|
374
411
|
}
|
|
375
412
|
const validateStringArray = (arr, field) => {
|
|
@@ -401,7 +438,7 @@ function validateStructuredJson(obj) {
|
|
|
401
438
|
return { ok: true };
|
|
402
439
|
}
|
|
403
440
|
async function callOpenRouter(args) {
|
|
404
|
-
var _a, _b, _c, _d, _e
|
|
441
|
+
var _a, _b, _c, _d, _e;
|
|
405
442
|
const openRouter = args.openRouter;
|
|
406
443
|
if (openRouter == null ? void 0 : openRouter.offline) {
|
|
407
444
|
return mockOpenRouterResponse(
|
|
@@ -460,12 +497,12 @@ async function callOpenRouter(args) {
|
|
|
460
497
|
continue;
|
|
461
498
|
}
|
|
462
499
|
const data = await response.json();
|
|
463
|
-
const content = (
|
|
500
|
+
const content = extractOpenRouterContent(data);
|
|
464
501
|
if (typeof content === "string") return content;
|
|
465
502
|
lastErrorText = JSON.stringify(data);
|
|
466
503
|
await new Promise((r) => setTimeout(r, 200));
|
|
467
504
|
} catch (err) {
|
|
468
|
-
lastErrorText = `${url}: ${(err
|
|
505
|
+
lastErrorText = `${url}: ${getErrorMessage(err)}`;
|
|
469
506
|
await new Promise((r) => setTimeout(r, 200));
|
|
470
507
|
}
|
|
471
508
|
}
|
|
@@ -613,7 +650,6 @@ async function classifyProduct(productContent, options) {
|
|
|
613
650
|
return validated.value;
|
|
614
651
|
}
|
|
615
652
|
function validateClassification(obj) {
|
|
616
|
-
var _a, _b;
|
|
617
653
|
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
618
654
|
return { ok: false, error: "Top-level must be a JSON object" };
|
|
619
655
|
}
|
|
@@ -625,7 +661,7 @@ function validateClassification(obj) {
|
|
|
625
661
|
"kid_female",
|
|
626
662
|
"generic"
|
|
627
663
|
];
|
|
628
|
-
if (
|
|
664
|
+
if (!isOneOf(audienceValues, o.audience)) {
|
|
629
665
|
return {
|
|
630
666
|
ok: false,
|
|
631
667
|
error: "audience must be one of: adult_male, adult_female, kid_male, kid_female, generic"
|
|
@@ -638,16 +674,16 @@ function validateClassification(obj) {
|
|
|
638
674
|
"home-decor",
|
|
639
675
|
"food-and-beverages"
|
|
640
676
|
];
|
|
641
|
-
if (
|
|
677
|
+
if (!isOneOf(verticalValues, o.vertical)) {
|
|
642
678
|
return {
|
|
643
679
|
ok: false,
|
|
644
680
|
error: "vertical must be one of: clothing, beauty, accessories, home-decor, food-and-beverages"
|
|
645
681
|
};
|
|
646
682
|
}
|
|
647
|
-
if ("category" in o && !(o.category === null || typeof o.category === "string")) {
|
|
683
|
+
if ("category" in o && !(o.category === null || typeof o.category === "string" || o.category === void 0)) {
|
|
648
684
|
return { ok: false, error: "category must be null or string" };
|
|
649
685
|
}
|
|
650
|
-
if ("subCategory" in o && !(o.subCategory === null || typeof o.subCategory === "string")) {
|
|
686
|
+
if ("subCategory" in o && !(o.subCategory === null || typeof o.subCategory === "string" || o.subCategory === void 0)) {
|
|
651
687
|
return { ok: false, error: "subCategory must be null or string" };
|
|
652
688
|
}
|
|
653
689
|
if (typeof o.subCategory === "string") {
|
|
@@ -659,13 +695,15 @@ function validateClassification(obj) {
|
|
|
659
695
|
};
|
|
660
696
|
}
|
|
661
697
|
}
|
|
698
|
+
const category = toOptionalStringOrNull(o.category);
|
|
699
|
+
const subCategory = toOptionalStringOrNull(o.subCategory);
|
|
662
700
|
return {
|
|
663
701
|
ok: true,
|
|
664
702
|
value: {
|
|
665
703
|
audience: o.audience,
|
|
666
704
|
vertical: o.vertical,
|
|
667
|
-
category
|
|
668
|
-
subCategory
|
|
705
|
+
category,
|
|
706
|
+
subCategory
|
|
669
707
|
}
|
|
670
708
|
};
|
|
671
709
|
}
|
|
@@ -760,7 +798,8 @@ function validateSEOContent(obj) {
|
|
|
760
798
|
"marketingCopy"
|
|
761
799
|
];
|
|
762
800
|
for (const key of requiredStrings) {
|
|
763
|
-
|
|
801
|
+
const v = o[key];
|
|
802
|
+
if (typeof v !== "string" || !v.trim()) {
|
|
764
803
|
return { ok: false, error: `${key} must be a non-empty string` };
|
|
765
804
|
}
|
|
766
805
|
}
|
|
@@ -789,15 +828,15 @@ async function determineStoreType(storeInfo, options) {
|
|
|
789
828
|
const offline = (_a = openRouter == null ? void 0 : openRouter.offline) != null ? _a : false;
|
|
790
829
|
const apiKey = (_b = options == null ? void 0 : options.apiKey) != null ? _b : openRouter == null ? void 0 : openRouter.apiKey;
|
|
791
830
|
const model = (_d = (_c = options == null ? void 0 : options.model) != null ? _c : openRouter == null ? void 0 : openRouter.model) != null ? _d : DEFAULT_OPENROUTER_MODEL;
|
|
792
|
-
const productLines =
|
|
831
|
+
const productLines = storeInfo.showcase.products.slice(0, 10).map((p) => {
|
|
793
832
|
if (typeof p === "string") return `- ${p}`;
|
|
794
|
-
const pt = typeof
|
|
795
|
-
return `- ${String(
|
|
796
|
-
})
|
|
797
|
-
const collectionLines =
|
|
833
|
+
const pt = typeof p.productType === "string" && p.productType.trim() ? p.productType : "N/A";
|
|
834
|
+
return `- ${String(p.title || "N/A")}: ${pt}`;
|
|
835
|
+
});
|
|
836
|
+
const collectionLines = storeInfo.showcase.collections.slice(0, 5).map((c) => {
|
|
798
837
|
if (typeof c === "string") return `- ${c}`;
|
|
799
|
-
return `- ${String(
|
|
800
|
-
})
|
|
838
|
+
return `- ${String(c.title || "N/A")}`;
|
|
839
|
+
});
|
|
801
840
|
const storeContent = `Store Title: ${storeInfo.title}
|
|
802
841
|
Store Description: ${(_e = storeInfo.description) != null ? _e : "N/A"}
|
|
803
842
|
|
|
@@ -926,6 +965,8 @@ function validateStoreTypeBreakdown(obj) {
|
|
|
926
965
|
"home-decor",
|
|
927
966
|
"food-and-beverages"
|
|
928
967
|
];
|
|
968
|
+
const audienceKeySet = new Set(audienceKeys);
|
|
969
|
+
const verticalKeySet = new Set(verticalKeys);
|
|
929
970
|
const o = obj;
|
|
930
971
|
const out = {};
|
|
931
972
|
const keys = Object.keys(o);
|
|
@@ -933,7 +974,7 @@ function validateStoreTypeBreakdown(obj) {
|
|
|
933
974
|
return { ok: false, error: "At least one audience key is required" };
|
|
934
975
|
}
|
|
935
976
|
for (const aKey of keys) {
|
|
936
|
-
if (!
|
|
977
|
+
if (!audienceKeySet.has(aKey)) {
|
|
937
978
|
return { ok: false, error: `Invalid audience key: ${aKey}` };
|
|
938
979
|
}
|
|
939
980
|
const vObj = o[aKey];
|
|
@@ -944,14 +985,15 @@ function validateStoreTypeBreakdown(obj) {
|
|
|
944
985
|
};
|
|
945
986
|
}
|
|
946
987
|
const vOut = {};
|
|
947
|
-
|
|
948
|
-
|
|
988
|
+
const vRec = vObj;
|
|
989
|
+
for (const vKey of Object.keys(vRec)) {
|
|
990
|
+
if (!verticalKeySet.has(vKey)) {
|
|
949
991
|
return {
|
|
950
992
|
ok: false,
|
|
951
993
|
error: `Invalid vertical key ${vKey} for audience ${aKey}`
|
|
952
994
|
};
|
|
953
995
|
}
|
|
954
|
-
const cats =
|
|
996
|
+
const cats = vRec[vKey];
|
|
955
997
|
if (!Array.isArray(cats) || cats.length === 0 || !cats.every((c) => typeof c === "string" && c.trim())) {
|
|
956
998
|
return {
|
|
957
999
|
ok: false,
|
|
@@ -1004,14 +1046,16 @@ function pruneBreakdownForSignals(breakdown, text) {
|
|
|
1004
1046
|
);
|
|
1005
1047
|
if (signaledVerticals.size === 0) signaledVerticals.add("accessories");
|
|
1006
1048
|
const pruned = {};
|
|
1007
|
-
for (const
|
|
1049
|
+
for (const audience of Object.keys(breakdown)) {
|
|
1008
1050
|
const a = audience;
|
|
1009
1051
|
if (!signaledAudiences.has(a)) continue;
|
|
1052
|
+
const verticals = breakdown[a];
|
|
1010
1053
|
const vOut = {};
|
|
1011
|
-
for (const
|
|
1054
|
+
for (const vertical of Object.keys(verticals || {})) {
|
|
1012
1055
|
const v = vertical;
|
|
1013
1056
|
if (!signaledVerticals.has(v)) continue;
|
|
1014
|
-
|
|
1057
|
+
const categories = verticals == null ? void 0 : verticals[v];
|
|
1058
|
+
if (categories) vOut[v] = categories;
|
|
1015
1059
|
}
|
|
1016
1060
|
if (Object.keys(vOut).length > 0) {
|
|
1017
1061
|
pruned[a] = vOut;
|