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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  rateLimitedFetch
3
- } from "./chunk-D5MTUWFO.mjs";
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
- var _a, _b, _c, _d;
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 (Array.isArray(value.images)) {
336
- const filtered = value.images.filter((url) => {
337
- if (typeof url !== "string") return false;
338
- const u = url.toLowerCase();
339
- const looksLikeProductImage = SHOPIFY_PRODUCT_IMAGE_PATTERNS.some(
340
- (p) => u.includes(p)
341
- );
342
- return !looksLikeProductImage;
343
- });
344
- value.images = filtered.length > 0 ? filtered : null;
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 == null ? void 0 : err.message) || "Failed to parse JSON" };
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, _f, _g, _h;
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 = (_h = (_g = (_f = data == null ? void 0 : data.choices) == null ? void 0 : _f[0]) == null ? void 0 : _g.message) == null ? void 0 : _h.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 == null ? void 0 : err.message) || String(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 (typeof o.audience !== "string" || !audienceValues.includes(o.audience)) {
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 (typeof o.vertical !== "string" || !verticalValues.includes(o.vertical)) {
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: typeof o.category === "string" ? o.category : (_a = o.category) != null ? _a : null,
668
- subCategory: typeof o.subCategory === "string" ? o.subCategory : (_b = o.subCategory) != null ? _b : null
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
- if (typeof o[key] !== "string" || !o[key].trim()) {
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 = Array.isArray(storeInfo.showcase.products) ? storeInfo.showcase.products.slice(0, 10).map((p) => {
831
+ const productLines = storeInfo.showcase.products.slice(0, 10).map((p) => {
793
832
  if (typeof p === "string") return `- ${p}`;
794
- const pt = typeof (p == null ? void 0 : p.productType) === "string" && p.productType.trim() ? p.productType : "N/A";
795
- return `- ${String((p == null ? void 0 : p.title) || "N/A")}: ${pt}`;
796
- }) : [];
797
- const collectionLines = Array.isArray(storeInfo.showcase.collections) ? storeInfo.showcase.collections.slice(0, 5).map((c) => {
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((c == null ? void 0 : c.title) || "N/A")}`;
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 (!audienceKeys.includes(aKey)) {
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
- for (const vKey of Object.keys(vObj)) {
948
- if (!verticalKeys.includes(vKey)) {
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 = vObj[vKey];
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 [audience, verticals] of Object.entries(breakdown)) {
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 [vertical, categories] of Object.entries(verticals || {})) {
1054
+ for (const vertical of Object.keys(verticals || {})) {
1012
1055
  const v = vertical;
1013
1056
  if (!signaledVerticals.has(v)) continue;
1014
- vOut[v] = categories;
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;