@numueg/theme-sdk 0.6.1 → 0.10.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/dist/chunk-4TMO5Y5W.mjs +183 -0
  3. package/dist/chunk-4TMO5Y5W.mjs.map +1 -0
  4. package/dist/chunk-EB2ESBLD.mjs +397 -0
  5. package/dist/chunk-EB2ESBLD.mjs.map +1 -0
  6. package/dist/chunk-IBNAZRUT.cjs +207 -0
  7. package/dist/chunk-IBNAZRUT.cjs.map +1 -0
  8. package/dist/chunk-KWODVI7F.cjs +408 -0
  9. package/dist/chunk-KWODVI7F.cjs.map +1 -0
  10. package/dist/chunk-TBSNHHFH.cjs +96 -0
  11. package/dist/chunk-TBSNHHFH.cjs.map +1 -0
  12. package/dist/chunk-XF2FGIVS.mjs +94 -0
  13. package/dist/chunk-XF2FGIVS.mjs.map +1 -0
  14. package/dist/{entities-6MGANln7.d.mts → entities-B8378GKp.d.mts} +12 -0
  15. package/dist/{entities-6MGANln7.d.ts → entities-B8378GKp.d.ts} +12 -0
  16. package/dist/index.cjs +693 -968
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.mts +286 -94
  19. package/dist/index.d.ts +286 -94
  20. package/dist/index.mjs +531 -911
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{mount-BGumg1JM.d.mts → mount-HbnOAYZ-.d.ts} +90 -14
  23. package/dist/{mount-Bt-4ken5.d.ts → mount-eAhGgGtZ.d.mts} +90 -14
  24. package/dist/normalize.cjs +7 -91
  25. package/dist/normalize.cjs.map +1 -1
  26. package/dist/normalize.d.mts +1 -1
  27. package/dist/normalize.d.ts +1 -1
  28. package/dist/normalize.mjs +1 -92
  29. package/dist/normalize.mjs.map +1 -1
  30. package/dist/{theme-D8MOopvi.d.mts → theme-CNTB4KnU.d.mts} +29 -3
  31. package/dist/{theme-D8MOopvi.d.ts → theme-CNTB4KnU.d.ts} +29 -3
  32. package/dist/types.d.mts +2 -2
  33. package/dist/types.d.ts +2 -2
  34. package/dist/v2-compat.cjs +6 -163
  35. package/dist/v2-compat.cjs.map +1 -1
  36. package/dist/v2-compat.d.mts +1 -1
  37. package/dist/v2-compat.d.ts +1 -1
  38. package/dist/v2-compat.mjs +2 -159
  39. package/dist/v2-compat.mjs.map +1 -1
  40. package/dist/validation.cjs +43 -403
  41. package/dist/validation.cjs.map +1 -1
  42. package/dist/validation.d.mts +1 -1
  43. package/dist/validation.d.ts +1 -1
  44. package/dist/validation.mjs +1 -395
  45. package/dist/validation.mjs.map +1 -1
  46. package/dist/verify.cjs +6 -21
  47. package/dist/verify.cjs.map +1 -1
  48. package/dist/verify.d.mts +3 -3
  49. package/dist/verify.d.ts +3 -3
  50. package/dist/verify.mjs +3 -20
  51. package/dist/verify.mjs.map +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":"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","file":"chunk-TBSNHHFH.cjs","sourcesContent":["import type {\n ThemeSettingsV3,\n SectionGroup,\n SectionInstance,\n PageTemplate,\n} from \"../types/theme\";\n\n/**\n * Default theme id when neither the V3 payload nor the legacy `theme.base_theme`\n * is set. Sourced from the env at build time so it stays in sync with the\n * backend's `NUMU_DEFAULT_THEME_ID`.\n *\n * Read once: SDK code that runs in browsers can't read env at runtime, but\n * tsup inlines `process.env.*` at build time when configured to do so.\n */\nconst DEFAULT_THEME_ID =\n (typeof process !== \"undefined\" &&\n process.env?.NUMU_DEFAULT_THEME_ID) ||\n \"modern\";\n\n/**\n * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.\n * Portable — works in both Node.js (SSR) and browser.\n *\n * Output must match the backend's `normalize_legacy_to_v3()` byte-for-byte\n * on the same input. The unit test in __tests__/normalize.test.ts asserts\n * this. If you change the shape here, update the backend mapper too —\n * otherwise stores rendered via SDK and stores rendered via SSR will\n * disagree on what the page contains.\n */\nexport function resolveThemeSettings(\n raw: Record<string, unknown> | null | undefined,\n): ThemeSettingsV3 {\n const safeRaw = (raw ?? {}) as Record<string, unknown>;\n\n // Already V3 — pass through unchanged.\n if (safeRaw.schema_version === 3) {\n return safeRaw as unknown as ThemeSettingsV3;\n }\n\n const themeBlock = (safeRaw.theme as Record<string, unknown>) || {};\n const themeId =\n (typeof themeBlock.base_theme === \"string\" && themeBlock.base_theme) ||\n DEFAULT_THEME_ID;\n\n const globalSettings: Record<string, unknown> = {};\n for (const key of [\n \"primary_color\",\n \"secondary_color\",\n \"font_family\",\n \"logo_url\",\n ]) {\n if (themeBlock[key] !== undefined) globalSettings[key] = themeBlock[key];\n }\n if (safeRaw.identity) globalSettings.identity = safeRaw.identity;\n\n const sections: Record<string, SectionInstance> = {};\n const order: string[] = [];\n\n const hero = safeRaw.hero as Record<string, unknown> | undefined;\n if (hero) {\n sections[\"hero_1\"] = {\n type: \"hero\",\n settings: {\n // Mirrors backend `normalize_legacy_to_v3` exactly — includes\n // headline_ar so Arabic content survives the round-trip.\n headline: hero.headline ?? \"\",\n headline_ar: hero.headline_ar ?? \"\",\n subtitle: hero.subtitle ?? \"\",\n background_image: hero.hero_image_url ?? \"\",\n cta_text: hero.cta_text ?? \"\",\n cta_link: hero.cta_link ?? \"\",\n },\n };\n order.push(\"hero_1\");\n }\n\n const products = safeRaw.products as Record<string, unknown> | undefined;\n if (products) {\n sections[\"featured_1\"] = {\n type: \"featured-products\",\n settings: products,\n };\n order.push(\"featured_1\");\n }\n\n const templates: Record<string, PageTemplate> = {};\n if (Object.keys(sections).length > 0) {\n templates[\"home\"] = { name: \"Home\", sections, order };\n }\n\n const sectionGroups: Record<string, SectionGroup> = {\n header: {\n name: \"Header Group\",\n sections: {\n header_1: {\n type: \"header\",\n settings: (safeRaw.header as Record<string, unknown>) || {},\n },\n },\n order: [\"header_1\"],\n },\n footer: {\n name: \"Footer Group\",\n sections: {\n footer_1: {\n type: \"footer\",\n settings: (safeRaw.footer as Record<string, unknown>) || {},\n },\n },\n order: [\"footer_1\"],\n },\n };\n\n let externalTheme: ThemeSettingsV3[\"external_theme\"] = null;\n const ext = safeRaw.external_theme as\n | { bundle_url?: unknown; css_url?: unknown; mode?: unknown }\n | undefined;\n if (\n ext &&\n typeof ext === \"object\" &&\n typeof ext.bundle_url === \"string\" &&\n ext.bundle_url\n ) {\n externalTheme = {\n bundle_url: ext.bundle_url,\n css_url: typeof ext.css_url === \"string\" ? ext.css_url : null,\n mode: typeof ext.mode === \"string\" ? ext.mode : \"production\",\n };\n }\n\n return {\n schema_version: 3,\n theme_id: themeId,\n global_settings: globalSettings,\n templates,\n section_groups: sectionGroups,\n external_theme: externalTheme,\n };\n}\n"]}
@@ -0,0 +1,94 @@
1
+ // src/utils/normalize.ts
2
+ var DEFAULT_THEME_ID = typeof process !== "undefined" && process.env?.NUMU_DEFAULT_THEME_ID || "modern";
3
+ function resolveThemeSettings(raw) {
4
+ const safeRaw = raw ?? {};
5
+ if (safeRaw.schema_version === 3) {
6
+ return safeRaw;
7
+ }
8
+ const themeBlock = safeRaw.theme || {};
9
+ const themeId = typeof themeBlock.base_theme === "string" && themeBlock.base_theme || DEFAULT_THEME_ID;
10
+ const globalSettings = {};
11
+ for (const key of [
12
+ "primary_color",
13
+ "secondary_color",
14
+ "font_family",
15
+ "logo_url"
16
+ ]) {
17
+ if (themeBlock[key] !== void 0) globalSettings[key] = themeBlock[key];
18
+ }
19
+ if (safeRaw.identity) globalSettings.identity = safeRaw.identity;
20
+ const sections = {};
21
+ const order = [];
22
+ const hero = safeRaw.hero;
23
+ if (hero) {
24
+ sections["hero_1"] = {
25
+ type: "hero",
26
+ settings: {
27
+ // Mirrors backend `normalize_legacy_to_v3` exactly — includes
28
+ // headline_ar so Arabic content survives the round-trip.
29
+ headline: hero.headline ?? "",
30
+ headline_ar: hero.headline_ar ?? "",
31
+ subtitle: hero.subtitle ?? "",
32
+ background_image: hero.hero_image_url ?? "",
33
+ cta_text: hero.cta_text ?? "",
34
+ cta_link: hero.cta_link ?? ""
35
+ }
36
+ };
37
+ order.push("hero_1");
38
+ }
39
+ const products = safeRaw.products;
40
+ if (products) {
41
+ sections["featured_1"] = {
42
+ type: "featured-products",
43
+ settings: products
44
+ };
45
+ order.push("featured_1");
46
+ }
47
+ const templates = {};
48
+ if (Object.keys(sections).length > 0) {
49
+ templates["home"] = { name: "Home", sections, order };
50
+ }
51
+ const sectionGroups = {
52
+ header: {
53
+ name: "Header Group",
54
+ sections: {
55
+ header_1: {
56
+ type: "header",
57
+ settings: safeRaw.header || {}
58
+ }
59
+ },
60
+ order: ["header_1"]
61
+ },
62
+ footer: {
63
+ name: "Footer Group",
64
+ sections: {
65
+ footer_1: {
66
+ type: "footer",
67
+ settings: safeRaw.footer || {}
68
+ }
69
+ },
70
+ order: ["footer_1"]
71
+ }
72
+ };
73
+ let externalTheme = null;
74
+ const ext = safeRaw.external_theme;
75
+ if (ext && typeof ext === "object" && typeof ext.bundle_url === "string" && ext.bundle_url) {
76
+ externalTheme = {
77
+ bundle_url: ext.bundle_url,
78
+ css_url: typeof ext.css_url === "string" ? ext.css_url : null,
79
+ mode: typeof ext.mode === "string" ? ext.mode : "production"
80
+ };
81
+ }
82
+ return {
83
+ schema_version: 3,
84
+ theme_id: themeId,
85
+ global_settings: globalSettings,
86
+ templates,
87
+ section_groups: sectionGroups,
88
+ external_theme: externalTheme
89
+ };
90
+ }
91
+
92
+ export { resolveThemeSettings };
93
+ //# sourceMappingURL=chunk-XF2FGIVS.mjs.map
94
+ //# sourceMappingURL=chunk-XF2FGIVS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":"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","file":"chunk-XF2FGIVS.mjs","sourcesContent":["import type {\n ThemeSettingsV3,\n SectionGroup,\n SectionInstance,\n PageTemplate,\n} from \"../types/theme\";\n\n/**\n * Default theme id when neither the V3 payload nor the legacy `theme.base_theme`\n * is set. Sourced from the env at build time so it stays in sync with the\n * backend's `NUMU_DEFAULT_THEME_ID`.\n *\n * Read once: SDK code that runs in browsers can't read env at runtime, but\n * tsup inlines `process.env.*` at build time when configured to do so.\n */\nconst DEFAULT_THEME_ID =\n (typeof process !== \"undefined\" &&\n process.env?.NUMU_DEFAULT_THEME_ID) ||\n \"modern\";\n\n/**\n * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.\n * Portable — works in both Node.js (SSR) and browser.\n *\n * Output must match the backend's `normalize_legacy_to_v3()` byte-for-byte\n * on the same input. The unit test in __tests__/normalize.test.ts asserts\n * this. If you change the shape here, update the backend mapper too —\n * otherwise stores rendered via SDK and stores rendered via SSR will\n * disagree on what the page contains.\n */\nexport function resolveThemeSettings(\n raw: Record<string, unknown> | null | undefined,\n): ThemeSettingsV3 {\n const safeRaw = (raw ?? {}) as Record<string, unknown>;\n\n // Already V3 — pass through unchanged.\n if (safeRaw.schema_version === 3) {\n return safeRaw as unknown as ThemeSettingsV3;\n }\n\n const themeBlock = (safeRaw.theme as Record<string, unknown>) || {};\n const themeId =\n (typeof themeBlock.base_theme === \"string\" && themeBlock.base_theme) ||\n DEFAULT_THEME_ID;\n\n const globalSettings: Record<string, unknown> = {};\n for (const key of [\n \"primary_color\",\n \"secondary_color\",\n \"font_family\",\n \"logo_url\",\n ]) {\n if (themeBlock[key] !== undefined) globalSettings[key] = themeBlock[key];\n }\n if (safeRaw.identity) globalSettings.identity = safeRaw.identity;\n\n const sections: Record<string, SectionInstance> = {};\n const order: string[] = [];\n\n const hero = safeRaw.hero as Record<string, unknown> | undefined;\n if (hero) {\n sections[\"hero_1\"] = {\n type: \"hero\",\n settings: {\n // Mirrors backend `normalize_legacy_to_v3` exactly — includes\n // headline_ar so Arabic content survives the round-trip.\n headline: hero.headline ?? \"\",\n headline_ar: hero.headline_ar ?? \"\",\n subtitle: hero.subtitle ?? \"\",\n background_image: hero.hero_image_url ?? \"\",\n cta_text: hero.cta_text ?? \"\",\n cta_link: hero.cta_link ?? \"\",\n },\n };\n order.push(\"hero_1\");\n }\n\n const products = safeRaw.products as Record<string, unknown> | undefined;\n if (products) {\n sections[\"featured_1\"] = {\n type: \"featured-products\",\n settings: products,\n };\n order.push(\"featured_1\");\n }\n\n const templates: Record<string, PageTemplate> = {};\n if (Object.keys(sections).length > 0) {\n templates[\"home\"] = { name: \"Home\", sections, order };\n }\n\n const sectionGroups: Record<string, SectionGroup> = {\n header: {\n name: \"Header Group\",\n sections: {\n header_1: {\n type: \"header\",\n settings: (safeRaw.header as Record<string, unknown>) || {},\n },\n },\n order: [\"header_1\"],\n },\n footer: {\n name: \"Footer Group\",\n sections: {\n footer_1: {\n type: \"footer\",\n settings: (safeRaw.footer as Record<string, unknown>) || {},\n },\n },\n order: [\"footer_1\"],\n },\n };\n\n let externalTheme: ThemeSettingsV3[\"external_theme\"] = null;\n const ext = safeRaw.external_theme as\n | { bundle_url?: unknown; css_url?: unknown; mode?: unknown }\n | undefined;\n if (\n ext &&\n typeof ext === \"object\" &&\n typeof ext.bundle_url === \"string\" &&\n ext.bundle_url\n ) {\n externalTheme = {\n bundle_url: ext.bundle_url,\n css_url: typeof ext.css_url === \"string\" ? ext.css_url : null,\n mode: typeof ext.mode === \"string\" ? ext.mode : \"production\",\n };\n }\n\n return {\n schema_version: 3,\n theme_id: themeId,\n global_settings: globalSettings,\n templates,\n section_groups: sectionGroups,\n external_theme: externalTheme,\n };\n}\n"]}
@@ -223,6 +223,18 @@ interface Page {
223
223
  title: string;
224
224
  handle?: string;
225
225
  data?: Record<string, any>;
226
+ /**
227
+ * Resolved alternate template key for this page, when the storefront
228
+ * routed it to one — e.g. `"product.wholesale"` for a product using the
229
+ * `wholesale` template suffix. The base route type stays in `type`
230
+ * (`"product"`); `template` carries the FULL resolved key a theme uses to
231
+ * look up `themeSettings.templates[template]`.
232
+ *
233
+ * Absent when the page uses its default template (read `type` then).
234
+ * Additive/optional — themes and hosts predating this field are unaffected.
235
+ * Surfaced to themes via `usePage()?.template`.
236
+ */
237
+ template?: string;
226
238
  }
227
239
 
228
240
  export type { Address as A, Cart as C, Order as O, Page as P, SizeChart as S, CartItem as a, Collection as b, Customer as c, OrderItem as d, Product as e, ProductImage as f, ProductVariant as g, SizeChartMode as h, Store as i, ProductOption as j };
@@ -223,6 +223,18 @@ interface Page {
223
223
  title: string;
224
224
  handle?: string;
225
225
  data?: Record<string, any>;
226
+ /**
227
+ * Resolved alternate template key for this page, when the storefront
228
+ * routed it to one — e.g. `"product.wholesale"` for a product using the
229
+ * `wholesale` template suffix. The base route type stays in `type`
230
+ * (`"product"`); `template` carries the FULL resolved key a theme uses to
231
+ * look up `themeSettings.templates[template]`.
232
+ *
233
+ * Absent when the page uses its default template (read `type` then).
234
+ * Additive/optional — themes and hosts predating this field are unaffected.
235
+ * Surfaced to themes via `usePage()?.template`.
236
+ */
237
+ template?: string;
226
238
  }
227
239
 
228
240
  export type { Address as A, Cart as C, Order as O, Page as P, SizeChart as S, CartItem as a, Collection as b, Customer as c, OrderItem as d, Product as e, ProductImage as f, ProductVariant as g, SizeChartMode as h, Store as i, ProductOption as j };