@numueg/theme-sdk 0.6.0 → 0.9.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-3C37JX2X.cjs +207 -0
  3. package/dist/chunk-3C37JX2X.cjs.map +1 -0
  4. package/dist/chunk-JIAA7TGR.mjs +397 -0
  5. package/dist/chunk-JIAA7TGR.mjs.map +1 -0
  6. package/dist/chunk-PUR3FRGQ.mjs +183 -0
  7. package/dist/chunk-PUR3FRGQ.mjs.map +1 -0
  8. package/dist/chunk-TBSNHHFH.cjs +96 -0
  9. package/dist/chunk-TBSNHHFH.cjs.map +1 -0
  10. package/dist/chunk-X3J4GDI7.cjs +408 -0
  11. package/dist/chunk-X3J4GDI7.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 +643 -965
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.mts +215 -82
  19. package/dist/index.d.ts +215 -82
  20. package/dist/index.mjs +485 -908
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{mount-BGumg1JM.d.mts → mount-CM4kGg9w.d.ts} +87 -14
  23. package/dist/{mount-Bt-4ken5.d.ts → mount-NLjFNyBv.d.mts} +87 -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
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { C as Cart, b as Collection, c as Customer, P as Page, e as Product, i as Store } from './entities-6MGANln7.mjs';
4
- import { T as ThemeSettingsV3, M as MountResult } from './theme-D8MOopvi.mjs';
3
+ import { C as Cart, b as Collection, c as Customer, P as Page, e as Product, i as Store } from './entities-B8378GKp.js';
4
+ import { T as ThemeSettingsV3, M as MountResult } from './theme-CNTB4KnU.js';
5
5
 
6
6
  interface LocalizationState {
7
7
  locale: string;
@@ -33,20 +33,40 @@ interface LocalizationState {
33
33
  */
34
34
  availableLocales: string[];
35
35
  }
36
- declare const ShopContext: react.Context<Store | null>;
37
- declare const ProductContext: react.Context<Product | null>;
38
- declare const CollectionContext: react.Context<Collection | null>;
39
- declare const CartContext: react.Context<{
36
+ /**
37
+ * Result of a cart mutation (add / remove / update / discount / note).
38
+ *
39
+ * `ok` mirrors the HTTP result: `false` for any non-2xx response — an
40
+ * out-of-stock or validation rejection, a 403 CSRF failure, etc. — so a
41
+ * theme can surface the failure (toast, inline error) instead of assuming
42
+ * the write landed. `status` is the HTTP status (0 for a network throw that
43
+ * the caller catches); `message` carries the backend's error text when the
44
+ * error body parses.
45
+ *
46
+ * Back-compat: the cart methods previously resolved `Promise<void>`; callers
47
+ * that `await` them and ignore the return keep working unchanged — the
48
+ * resolved value is purely additive.
49
+ */
50
+ interface CartMutationResult {
51
+ ok: boolean;
52
+ status: number;
53
+ message?: string;
54
+ }
55
+ interface CartContextValue {
40
56
  cart: Cart;
41
- addItem: (productId: string, variantId?: string, quantity?: number) => Promise<void>;
42
- removeItem: (itemId: string) => Promise<void>;
43
- updateQuantity: (itemId: string, quantity: number) => Promise<void>;
44
- applyDiscount: (code: string) => Promise<void>;
45
- removeDiscount: () => Promise<void>;
46
- updateNote: (note: string) => Promise<void>;
57
+ addItem: (productId: string, variantId?: string, quantity?: number) => Promise<CartMutationResult>;
58
+ removeItem: (itemId: string) => Promise<CartMutationResult>;
59
+ updateQuantity: (itemId: string, quantity: number) => Promise<CartMutationResult>;
60
+ applyDiscount: (code: string) => Promise<CartMutationResult>;
61
+ removeDiscount: () => Promise<CartMutationResult>;
62
+ updateNote: (note: string) => Promise<CartMutationResult>;
47
63
  clearCart: () => Promise<void>;
48
64
  loading: boolean;
49
- } | null>;
65
+ }
66
+ declare const ShopContext: react.Context<Store | null>;
67
+ declare const ProductContext: react.Context<Product | null>;
68
+ declare const CollectionContext: react.Context<Collection | null>;
69
+ declare const CartContext: react.Context<CartContextValue | null>;
50
70
  declare const CustomerContext: react.Context<Customer | null>;
51
71
  declare const ThemeSettingsContext: react.Context<ThemeSettingsV3 | null>;
52
72
  declare const LocalizationContext: react.Context<LocalizationState | null>;
@@ -86,6 +106,52 @@ interface MenuItemData {
86
106
  * doesn't exist (render nothing / fallback), no fetch attempted.
87
107
  */
88
108
  declare const NavigationContext: react.Context<Record<string, MenuItemData[]>>;
109
+ /**
110
+ * Multi-currency presentment config, as returned by
111
+ * `GET /api/storefront/currencies`. Display-only — the store's *capture*
112
+ * currency never changes mid-session; this just lets visitors browse prices
113
+ * in a currency they recognize.
114
+ */
115
+ interface CurrencyConfig {
116
+ base: string;
117
+ default_presentment: string;
118
+ presentment: string[];
119
+ rates: Record<string, string>;
120
+ auto_convert: boolean;
121
+ }
122
+ /**
123
+ * The value `useCurrency()` returns and `CurrencyContext` carries.
124
+ *
125
+ * `convert(cents, target?)` returns the converted **cents** in `target`
126
+ * (defaults to `selected`), using the API rates; returns the input unchanged
127
+ * when no rate exists (theme renders base — better than a wrong number).
128
+ */
129
+ interface CurrencyState {
130
+ base: string;
131
+ selected: string;
132
+ presentment: string[];
133
+ rates: Record<string, number>;
134
+ autoConvert: boolean;
135
+ loading: boolean;
136
+ setSelected: (currency: string) => void;
137
+ convert: (cents: number, target?: string) => number;
138
+ }
139
+ /**
140
+ * Phase 2 (correctness) — multi-currency lifted into a provider context.
141
+ *
142
+ * Previously `useCurrency()` fetched `/api/storefront/currencies` and held
143
+ * the selected currency in per-instance `useState`, so each `<Money>`,
144
+ * `useMoney()` and `<CurrencySwitcher>` owned an INDEPENDENT copy: switching
145
+ * currency in the switcher never reached the price tags, and every consumer
146
+ * re-fetched. `NuMuProvider` now fetches ONCE and publishes a single
147
+ * `CurrencyState` here, so a `<CurrencySwitcher>` change re-renders every
148
+ * `<Money>` on the page without a reload.
149
+ *
150
+ * Null when no provider is present (SSR before hydrate, or a theme mounted
151
+ * outside `NuMuProvider`); `useCurrency()` then falls back to a base-only,
152
+ * no-convert state derived from the store currency so `<Money>` still renders.
153
+ */
154
+ declare const CurrencyContext: react.Context<CurrencyState | null>;
89
155
 
90
156
  /**
91
157
  * `mountTheme(el, ctx, renderApp)` — the canonical V3 bundle entry helper.
@@ -148,6 +214,13 @@ interface ThemeMountPage {
148
214
  handle?: string;
149
215
  title?: string;
150
216
  data?: Record<string, unknown>;
217
+ /**
218
+ * Resolved alternate template key for this page (e.g. `"product.wholesale"`)
219
+ * when the storefront routed it to a template suffix. `mountTheme` forwards
220
+ * it to `NuMuProvider` so a theme reads it via `usePage()?.template`. Absent
221
+ * for pages on their default template. Additive/optional.
222
+ */
223
+ template?: string;
151
224
  }
152
225
  /**
153
226
  * The mount context a host (or dev harness) passes to a bundle's `mount`.
@@ -222,4 +295,4 @@ declare function buildThemeElement(ctx: ThemeMountContext, mountEl: HTMLElement
222
295
  */
223
296
  declare function mountTheme(el: HTMLElement, ctx: ThemeMountContext, renderApp: (args: ThemeRenderArgs) => ReactNode): MountResult;
224
297
 
225
- export { CartContext as C, type LocalizationState as L, type MenuItemData as M, NavigationContext as N, PageContext as P, ShopContext as S, type ThemeMountContext as T, type ThemeRenderArgs as a, CollectionContext as b, CustomerContext as c, LocalizationContext as d, ProductContext as e, type ThemeMountPage as f, ThemeSettingsContext as g, buildThemeElement as h, mountTheme as m };
298
+ export { type CartContextValue as C, type LocalizationState as L, type MenuItemData as M, NavigationContext as N, PageContext as P, ShopContext as S, type ThemeMountContext as T, type CurrencyState as a, type ThemeRenderArgs as b, CartContext as c, type CartMutationResult as d, CollectionContext as e, type CurrencyConfig as f, CurrencyContext as g, CustomerContext as h, LocalizationContext as i, ProductContext as j, type ThemeMountPage as k, ThemeSettingsContext as l, buildThemeElement as m, mountTheme as n };
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { C as Cart, b as Collection, c as Customer, P as Page, e as Product, i as Store } from './entities-6MGANln7.js';
4
- import { T as ThemeSettingsV3, M as MountResult } from './theme-D8MOopvi.js';
3
+ import { C as Cart, b as Collection, c as Customer, P as Page, e as Product, i as Store } from './entities-B8378GKp.mjs';
4
+ import { T as ThemeSettingsV3, M as MountResult } from './theme-CNTB4KnU.mjs';
5
5
 
6
6
  interface LocalizationState {
7
7
  locale: string;
@@ -33,20 +33,40 @@ interface LocalizationState {
33
33
  */
34
34
  availableLocales: string[];
35
35
  }
36
- declare const ShopContext: react.Context<Store | null>;
37
- declare const ProductContext: react.Context<Product | null>;
38
- declare const CollectionContext: react.Context<Collection | null>;
39
- declare const CartContext: react.Context<{
36
+ /**
37
+ * Result of a cart mutation (add / remove / update / discount / note).
38
+ *
39
+ * `ok` mirrors the HTTP result: `false` for any non-2xx response — an
40
+ * out-of-stock or validation rejection, a 403 CSRF failure, etc. — so a
41
+ * theme can surface the failure (toast, inline error) instead of assuming
42
+ * the write landed. `status` is the HTTP status (0 for a network throw that
43
+ * the caller catches); `message` carries the backend's error text when the
44
+ * error body parses.
45
+ *
46
+ * Back-compat: the cart methods previously resolved `Promise<void>`; callers
47
+ * that `await` them and ignore the return keep working unchanged — the
48
+ * resolved value is purely additive.
49
+ */
50
+ interface CartMutationResult {
51
+ ok: boolean;
52
+ status: number;
53
+ message?: string;
54
+ }
55
+ interface CartContextValue {
40
56
  cart: Cart;
41
- addItem: (productId: string, variantId?: string, quantity?: number) => Promise<void>;
42
- removeItem: (itemId: string) => Promise<void>;
43
- updateQuantity: (itemId: string, quantity: number) => Promise<void>;
44
- applyDiscount: (code: string) => Promise<void>;
45
- removeDiscount: () => Promise<void>;
46
- updateNote: (note: string) => Promise<void>;
57
+ addItem: (productId: string, variantId?: string, quantity?: number) => Promise<CartMutationResult>;
58
+ removeItem: (itemId: string) => Promise<CartMutationResult>;
59
+ updateQuantity: (itemId: string, quantity: number) => Promise<CartMutationResult>;
60
+ applyDiscount: (code: string) => Promise<CartMutationResult>;
61
+ removeDiscount: () => Promise<CartMutationResult>;
62
+ updateNote: (note: string) => Promise<CartMutationResult>;
47
63
  clearCart: () => Promise<void>;
48
64
  loading: boolean;
49
- } | null>;
65
+ }
66
+ declare const ShopContext: react.Context<Store | null>;
67
+ declare const ProductContext: react.Context<Product | null>;
68
+ declare const CollectionContext: react.Context<Collection | null>;
69
+ declare const CartContext: react.Context<CartContextValue | null>;
50
70
  declare const CustomerContext: react.Context<Customer | null>;
51
71
  declare const ThemeSettingsContext: react.Context<ThemeSettingsV3 | null>;
52
72
  declare const LocalizationContext: react.Context<LocalizationState | null>;
@@ -86,6 +106,52 @@ interface MenuItemData {
86
106
  * doesn't exist (render nothing / fallback), no fetch attempted.
87
107
  */
88
108
  declare const NavigationContext: react.Context<Record<string, MenuItemData[]>>;
109
+ /**
110
+ * Multi-currency presentment config, as returned by
111
+ * `GET /api/storefront/currencies`. Display-only — the store's *capture*
112
+ * currency never changes mid-session; this just lets visitors browse prices
113
+ * in a currency they recognize.
114
+ */
115
+ interface CurrencyConfig {
116
+ base: string;
117
+ default_presentment: string;
118
+ presentment: string[];
119
+ rates: Record<string, string>;
120
+ auto_convert: boolean;
121
+ }
122
+ /**
123
+ * The value `useCurrency()` returns and `CurrencyContext` carries.
124
+ *
125
+ * `convert(cents, target?)` returns the converted **cents** in `target`
126
+ * (defaults to `selected`), using the API rates; returns the input unchanged
127
+ * when no rate exists (theme renders base — better than a wrong number).
128
+ */
129
+ interface CurrencyState {
130
+ base: string;
131
+ selected: string;
132
+ presentment: string[];
133
+ rates: Record<string, number>;
134
+ autoConvert: boolean;
135
+ loading: boolean;
136
+ setSelected: (currency: string) => void;
137
+ convert: (cents: number, target?: string) => number;
138
+ }
139
+ /**
140
+ * Phase 2 (correctness) — multi-currency lifted into a provider context.
141
+ *
142
+ * Previously `useCurrency()` fetched `/api/storefront/currencies` and held
143
+ * the selected currency in per-instance `useState`, so each `<Money>`,
144
+ * `useMoney()` and `<CurrencySwitcher>` owned an INDEPENDENT copy: switching
145
+ * currency in the switcher never reached the price tags, and every consumer
146
+ * re-fetched. `NuMuProvider` now fetches ONCE and publishes a single
147
+ * `CurrencyState` here, so a `<CurrencySwitcher>` change re-renders every
148
+ * `<Money>` on the page without a reload.
149
+ *
150
+ * Null when no provider is present (SSR before hydrate, or a theme mounted
151
+ * outside `NuMuProvider`); `useCurrency()` then falls back to a base-only,
152
+ * no-convert state derived from the store currency so `<Money>` still renders.
153
+ */
154
+ declare const CurrencyContext: react.Context<CurrencyState | null>;
89
155
 
90
156
  /**
91
157
  * `mountTheme(el, ctx, renderApp)` — the canonical V3 bundle entry helper.
@@ -148,6 +214,13 @@ interface ThemeMountPage {
148
214
  handle?: string;
149
215
  title?: string;
150
216
  data?: Record<string, unknown>;
217
+ /**
218
+ * Resolved alternate template key for this page (e.g. `"product.wholesale"`)
219
+ * when the storefront routed it to a template suffix. `mountTheme` forwards
220
+ * it to `NuMuProvider` so a theme reads it via `usePage()?.template`. Absent
221
+ * for pages on their default template. Additive/optional.
222
+ */
223
+ template?: string;
151
224
  }
152
225
  /**
153
226
  * The mount context a host (or dev harness) passes to a bundle's `mount`.
@@ -222,4 +295,4 @@ declare function buildThemeElement(ctx: ThemeMountContext, mountEl: HTMLElement
222
295
  */
223
296
  declare function mountTheme(el: HTMLElement, ctx: ThemeMountContext, renderApp: (args: ThemeRenderArgs) => ReactNode): MountResult;
224
297
 
225
- export { CartContext as C, type LocalizationState as L, type MenuItemData as M, NavigationContext as N, PageContext as P, ShopContext as S, type ThemeMountContext as T, type ThemeRenderArgs as a, CollectionContext as b, CustomerContext as c, LocalizationContext as d, ProductContext as e, type ThemeMountPage as f, ThemeSettingsContext as g, buildThemeElement as h, mountTheme as m };
298
+ export { type CartContextValue as C, type LocalizationState as L, type MenuItemData as M, NavigationContext as N, PageContext as P, ShopContext as S, type ThemeMountContext as T, type CurrencyState as a, type ThemeRenderArgs as b, CartContext as c, type CartMutationResult as d, CollectionContext as e, type CurrencyConfig as f, CurrencyContext as g, CustomerContext as h, LocalizationContext as i, ProductContext as j, type ThemeMountPage as k, ThemeSettingsContext as l, buildThemeElement as m, mountTheme as n };
@@ -1,96 +1,12 @@
1
1
  'use strict';
2
2
 
3
- // src/utils/normalize.ts
4
- var DEFAULT_THEME_ID = typeof process !== "undefined" && process.env?.NUMU_DEFAULT_THEME_ID || "modern";
5
- function resolveThemeSettings(raw) {
6
- const safeRaw = raw ?? {};
7
- if (safeRaw.schema_version === 3) {
8
- return safeRaw;
9
- }
10
- const themeBlock = safeRaw.theme || {};
11
- const themeId = typeof themeBlock.base_theme === "string" && themeBlock.base_theme || DEFAULT_THEME_ID;
12
- const globalSettings = {};
13
- for (const key of [
14
- "primary_color",
15
- "secondary_color",
16
- "font_family",
17
- "logo_url"
18
- ]) {
19
- if (themeBlock[key] !== void 0) globalSettings[key] = themeBlock[key];
20
- }
21
- if (safeRaw.identity) globalSettings.identity = safeRaw.identity;
22
- const sections = {};
23
- const order = [];
24
- const hero = safeRaw.hero;
25
- if (hero) {
26
- sections["hero_1"] = {
27
- type: "hero",
28
- settings: {
29
- // Mirrors backend `normalize_legacy_to_v3` exactly — includes
30
- // headline_ar so Arabic content survives the round-trip.
31
- headline: hero.headline ?? "",
32
- headline_ar: hero.headline_ar ?? "",
33
- subtitle: hero.subtitle ?? "",
34
- background_image: hero.hero_image_url ?? "",
35
- cta_text: hero.cta_text ?? "",
36
- cta_link: hero.cta_link ?? ""
37
- }
38
- };
39
- order.push("hero_1");
40
- }
41
- const products = safeRaw.products;
42
- if (products) {
43
- sections["featured_1"] = {
44
- type: "featured-products",
45
- settings: products
46
- };
47
- order.push("featured_1");
48
- }
49
- const templates = {};
50
- if (Object.keys(sections).length > 0) {
51
- templates["home"] = { name: "Home", sections, order };
52
- }
53
- const sectionGroups = {
54
- header: {
55
- name: "Header Group",
56
- sections: {
57
- header_1: {
58
- type: "header",
59
- settings: safeRaw.header || {}
60
- }
61
- },
62
- order: ["header_1"]
63
- },
64
- footer: {
65
- name: "Footer Group",
66
- sections: {
67
- footer_1: {
68
- type: "footer",
69
- settings: safeRaw.footer || {}
70
- }
71
- },
72
- order: ["footer_1"]
73
- }
74
- };
75
- let externalTheme = null;
76
- const ext = safeRaw.external_theme;
77
- if (ext && typeof ext === "object" && typeof ext.bundle_url === "string" && ext.bundle_url) {
78
- externalTheme = {
79
- bundle_url: ext.bundle_url,
80
- css_url: typeof ext.css_url === "string" ? ext.css_url : null,
81
- mode: typeof ext.mode === "string" ? ext.mode : "production"
82
- };
83
- }
84
- return {
85
- schema_version: 3,
86
- theme_id: themeId,
87
- global_settings: globalSettings,
88
- templates,
89
- section_groups: sectionGroups,
90
- external_theme: externalTheme
91
- };
92
- }
3
+ var chunkTBSNHHFH_cjs = require('./chunk-TBSNHHFH.cjs');
93
4
 
94
- exports.resolveThemeSettings = resolveThemeSettings;
5
+
6
+
7
+ Object.defineProperty(exports, "resolveThemeSettings", {
8
+ enumerable: true,
9
+ get: function () { return chunkTBSNHHFH_cjs.resolveThemeSettings; }
10
+ });
95
11
  //# sourceMappingURL=normalize.cjs.map
96
12
  //# sourceMappingURL=normalize.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":"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","file":"normalize.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"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"normalize.cjs"}
@@ -1,4 +1,4 @@
1
- import { T as ThemeSettingsV3 } from './theme-D8MOopvi.mjs';
1
+ import { T as ThemeSettingsV3 } from './theme-CNTB4KnU.mjs';
2
2
 
3
3
  /**
4
4
  * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.
@@ -1,4 +1,4 @@
1
- import { T as ThemeSettingsV3 } from './theme-D8MOopvi.js';
1
+ import { T as ThemeSettingsV3 } from './theme-CNTB4KnU.js';
2
2
 
3
3
  /**
4
4
  * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.
@@ -1,94 +1,3 @@
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 };
1
+ export { resolveThemeSettings } from './chunk-XF2FGIVS.mjs';
93
2
  //# sourceMappingURL=normalize.mjs.map
94
3
  //# sourceMappingURL=normalize.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":"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","file":"normalize.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"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"normalize.mjs"}
@@ -179,9 +179,35 @@ interface SectionPreset {
179
179
  settings?: Record<string, any>;
180
180
  blocks?: PresetBlock[];
181
181
  }
182
- /** Section component props */
183
- interface SectionProps {
184
- settings: Record<string, any>;
182
+ /**
183
+ * Section component props.
184
+ *
185
+ * Generic over the settings shape so a theme can type its own settings
186
+ * without hand-rolling a parallel props interface (the `EmpSectionProps`
187
+ * pattern every theme was duplicating). `id`/`type` mirror the section
188
+ * instance's key + `SectionInstance.type` so a component can key React
189
+ * lists, dispatch on type, or emit editor anchors without threading them
190
+ * separately; `groupId` names the owning `SectionGroup` for header/footer
191
+ * group sections (absent for template sections).
192
+ *
193
+ * // Loosely typed (default) — settings is Record<string, unknown>:
194
+ * function Hero(props: SectionProps) { … }
195
+ *
196
+ * // Strongly typed:
197
+ * interface HeroSettings { headline: string; show_cta: boolean }
198
+ * function Hero({ settings }: SectionProps<HeroSettings>) {
199
+ * return <h1>{settings.headline}</h1>;
200
+ * }
201
+ */
202
+ interface SectionProps<S = Record<string, unknown>> {
203
+ /** The section instance's key within its template/group. */
204
+ id: string;
205
+ /** The section's `type` (its schema type / component name). */
206
+ type: string;
207
+ /** Owning section-group id for header/footer group sections; absent for
208
+ * template-level sections. */
209
+ groupId?: string;
210
+ settings: S;
185
211
  blocks?: Record<string, BlockInstance>;
186
212
  blockOrder?: string[];
187
213
  storeData?: any;
@@ -179,9 +179,35 @@ interface SectionPreset {
179
179
  settings?: Record<string, any>;
180
180
  blocks?: PresetBlock[];
181
181
  }
182
- /** Section component props */
183
- interface SectionProps {
184
- settings: Record<string, any>;
182
+ /**
183
+ * Section component props.
184
+ *
185
+ * Generic over the settings shape so a theme can type its own settings
186
+ * without hand-rolling a parallel props interface (the `EmpSectionProps`
187
+ * pattern every theme was duplicating). `id`/`type` mirror the section
188
+ * instance's key + `SectionInstance.type` so a component can key React
189
+ * lists, dispatch on type, or emit editor anchors without threading them
190
+ * separately; `groupId` names the owning `SectionGroup` for header/footer
191
+ * group sections (absent for template sections).
192
+ *
193
+ * // Loosely typed (default) — settings is Record<string, unknown>:
194
+ * function Hero(props: SectionProps) { … }
195
+ *
196
+ * // Strongly typed:
197
+ * interface HeroSettings { headline: string; show_cta: boolean }
198
+ * function Hero({ settings }: SectionProps<HeroSettings>) {
199
+ * return <h1>{settings.headline}</h1>;
200
+ * }
201
+ */
202
+ interface SectionProps<S = Record<string, unknown>> {
203
+ /** The section instance's key within its template/group. */
204
+ id: string;
205
+ /** The section's `type` (its schema type / component name). */
206
+ type: string;
207
+ /** Owning section-group id for header/footer group sections; absent for
208
+ * template-level sections. */
209
+ groupId?: string;
210
+ settings: S;
185
211
  blocks?: Record<string, BlockInstance>;
186
212
  blockOrder?: string[];
187
213
  storeData?: any;
package/dist/types.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export { A as Address, C as Cart, a as CartItem, b as Collection, c as Customer, O as Order, d as OrderItem, P as Page, e as Product, f as ProductImage, g as ProductVariant, S as SizeChart, h as SizeChartMode, i as Store } from './entities-6MGANln7.mjs';
2
- export { B as BlockInstance, a as BlockProps, b as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, S as SectionGroup, c as SectionInstance, d as SectionPreset, e as SectionProps, f as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-D8MOopvi.mjs';
1
+ export { A as Address, C as Cart, a as CartItem, b as Collection, c as Customer, O as Order, d as OrderItem, P as Page, e as Product, f as ProductImage, g as ProductVariant, S as SizeChart, h as SizeChartMode, i as Store } from './entities-B8378GKp.mjs';
2
+ export { B as BlockInstance, a as BlockProps, b as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, S as SectionGroup, c as SectionInstance, d as SectionPreset, e as SectionProps, f as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-CNTB4KnU.mjs';
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { A as Address, C as Cart, a as CartItem, b as Collection, c as Customer, O as Order, d as OrderItem, P as Page, e as Product, f as ProductImage, g as ProductVariant, S as SizeChart, h as SizeChartMode, i as Store } from './entities-6MGANln7.js';
2
- export { B as BlockInstance, a as BlockProps, b as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, S as SectionGroup, c as SectionInstance, d as SectionPreset, e as SectionProps, f as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-D8MOopvi.js';
1
+ export { A as Address, C as Cart, a as CartItem, b as Collection, c as Customer, O as Order, d as OrderItem, P as Page, e as Product, f as ProductImage, g as ProductVariant, S as SizeChart, h as SizeChartMode, i as Store } from './entities-B8378GKp.js';
2
+ export { B as BlockInstance, a as BlockProps, b as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, S as SectionGroup, c as SectionInstance, d as SectionPreset, e as SectionProps, f as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-CNTB4KnU.js';