@numueg/theme-sdk 0.1.2

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.
@@ -0,0 +1,96 @@
1
+ 'use strict';
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
+ }
93
+
94
+ exports.resolveThemeSettings = resolveThemeSettings;
95
+ //# sourceMappingURL=normalize.cjs.map
96
+ //# sourceMappingURL=normalize.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":";;;AAeA,IAAM,mBACH,OAAO,OAAA,KAAY,WAAA,IAClB,OAAA,CAAQ,KAAK,qBAAA,IACf,QAAA;AAYK,SAAS,qBACd,GAAA,EACiB;AACjB,EAAA,MAAM,OAAA,GAAW,OAAO,EAAC;AAGzB,EAAA,IAAI,OAAA,CAAQ,mBAAmB,CAAA,EAAG;AAChC,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GAAc,OAAA,CAAQ,KAAA,IAAqC,EAAC;AAClE,EAAA,MAAM,UACH,OAAO,UAAA,CAAW,UAAA,KAAe,QAAA,IAAY,WAAW,UAAA,IACzD,gBAAA;AAEF,EAAA,MAAM,iBAA0C,EAAC;AACjD,EAAA,KAAA,MAAW,GAAA,IAAO;AAAA,IAChB,eAAA;AAAA,IACA,iBAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF,EAAG;AACD,IAAA,IAAI,UAAA,CAAW,GAAG,CAAA,KAAM,MAAA,iBAA0B,GAAG,CAAA,GAAI,WAAW,GAAG,CAAA;AAAA,EACzE;AACA,EAAA,IAAI,OAAA,CAAQ,QAAA,EAAU,cAAA,CAAe,QAAA,GAAW,OAAA,CAAQ,QAAA;AAExD,EAAA,MAAM,WAA4C,EAAC;AACnD,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,MAAM,OAAO,OAAA,CAAQ,IAAA;AACrB,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,QAAA,CAAS,QAAQ,CAAA,GAAI;AAAA,MACnB,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU;AAAA;AAAA;AAAA,QAGR,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,WAAA,EAAa,KAAK,WAAA,IAAe,EAAA;AAAA,QACjC,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,gBAAA,EAAkB,KAAK,cAAA,IAAkB,EAAA;AAAA,QACzC,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,QAAA,EAAU,KAAK,QAAA,IAAY;AAAA;AAC7B,KACF;AACA,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,WAAW,OAAA,CAAQ,QAAA;AACzB,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,QAAA,CAAS,YAAY,CAAA,GAAI;AAAA,MACvB,IAAA,EAAM,mBAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACZ;AACA,IAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AAAA,EACzB;AAEA,EAAA,MAAM,YAA0C,EAAC;AACjD,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,SAAS,CAAA,EAAG;AACpC,IAAA,SAAA,CAAU,MAAM,CAAA,GAAI,EAAE,IAAA,EAAM,MAAA,EAAQ,UAAU,KAAA,EAAM;AAAA,EACtD;AAEA,EAAA,MAAM,aAAA,GAA8C;AAAA,IAClD,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,cAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,QAAA;AAAA,UACN,QAAA,EAAW,OAAA,CAAQ,MAAA,IAAsC;AAAC;AAC5D,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAU;AAAA,KACpB;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,cAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,QAAA;AAAA,UACN,QAAA,EAAW,OAAA,CAAQ,MAAA,IAAsC;AAAC;AAC5D,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAU;AAAA;AACpB,GACF;AAEA,EAAA,IAAI,aAAA,GAAmD,IAAA;AACvD,EAAA,MAAM,MAAM,OAAA,CAAQ,cAAA;AAGpB,EAAA,IACE,GAAA,IACA,OAAO,GAAA,KAAQ,QAAA,IACf,OAAO,GAAA,CAAI,UAAA,KAAe,QAAA,IAC1B,GAAA,CAAI,UAAA,EACJ;AACA,IAAA,aAAA,GAAgB;AAAA,MACd,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,SAAS,OAAO,GAAA,CAAI,OAAA,KAAY,QAAA,GAAW,IAAI,OAAA,GAAU,IAAA;AAAA,MACzD,MAAM,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,GAAW,IAAI,IAAA,GAAO;AAAA,KAClD;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,cAAA,EAAgB,CAAA;AAAA,IAChB,QAAA,EAAU,OAAA;AAAA,IACV,eAAA,EAAiB,cAAA;AAAA,IACjB,SAAA;AAAA,IACA,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAA,EAAgB;AAAA,GAClB;AACF","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"]}
@@ -0,0 +1,15 @@
1
+ import { T as ThemeSettingsV3 } from './theme-NWJAU9jd.mjs';
2
+
3
+ /**
4
+ * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.
5
+ * Portable — works in both Node.js (SSR) and browser.
6
+ *
7
+ * Output must match the backend's `normalize_legacy_to_v3()` byte-for-byte
8
+ * on the same input. The unit test in __tests__/normalize.test.ts asserts
9
+ * this. If you change the shape here, update the backend mapper too —
10
+ * otherwise stores rendered via SDK and stores rendered via SSR will
11
+ * disagree on what the page contains.
12
+ */
13
+ declare function resolveThemeSettings(raw: Record<string, unknown> | null | undefined): ThemeSettingsV3;
14
+
15
+ export { resolveThemeSettings };
@@ -0,0 +1,15 @@
1
+ import { T as ThemeSettingsV3 } from './theme-NWJAU9jd.js';
2
+
3
+ /**
4
+ * Dual-Read normalization: converts V1/V2 legacy payloads to V3 in memory.
5
+ * Portable — works in both Node.js (SSR) and browser.
6
+ *
7
+ * Output must match the backend's `normalize_legacy_to_v3()` byte-for-byte
8
+ * on the same input. The unit test in __tests__/normalize.test.ts asserts
9
+ * this. If you change the shape here, update the backend mapper too —
10
+ * otherwise stores rendered via SDK and stores rendered via SSR will
11
+ * disagree on what the page contains.
12
+ */
13
+ declare function resolveThemeSettings(raw: Record<string, unknown> | null | undefined): ThemeSettingsV3;
14
+
15
+ export { resolveThemeSettings };
@@ -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=normalize.mjs.map
94
+ //# sourceMappingURL=normalize.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/normalize.ts"],"names":[],"mappings":";AAeA,IAAM,mBACH,OAAO,OAAA,KAAY,WAAA,IAClB,OAAA,CAAQ,KAAK,qBAAA,IACf,QAAA;AAYK,SAAS,qBACd,GAAA,EACiB;AACjB,EAAA,MAAM,OAAA,GAAW,OAAO,EAAC;AAGzB,EAAA,IAAI,OAAA,CAAQ,mBAAmB,CAAA,EAAG;AAChC,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GAAc,OAAA,CAAQ,KAAA,IAAqC,EAAC;AAClE,EAAA,MAAM,UACH,OAAO,UAAA,CAAW,UAAA,KAAe,QAAA,IAAY,WAAW,UAAA,IACzD,gBAAA;AAEF,EAAA,MAAM,iBAA0C,EAAC;AACjD,EAAA,KAAA,MAAW,GAAA,IAAO;AAAA,IAChB,eAAA;AAAA,IACA,iBAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF,EAAG;AACD,IAAA,IAAI,UAAA,CAAW,GAAG,CAAA,KAAM,MAAA,iBAA0B,GAAG,CAAA,GAAI,WAAW,GAAG,CAAA;AAAA,EACzE;AACA,EAAA,IAAI,OAAA,CAAQ,QAAA,EAAU,cAAA,CAAe,QAAA,GAAW,OAAA,CAAQ,QAAA;AAExD,EAAA,MAAM,WAA4C,EAAC;AACnD,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,MAAM,OAAO,OAAA,CAAQ,IAAA;AACrB,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,QAAA,CAAS,QAAQ,CAAA,GAAI;AAAA,MACnB,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU;AAAA;AAAA;AAAA,QAGR,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,WAAA,EAAa,KAAK,WAAA,IAAe,EAAA;AAAA,QACjC,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,gBAAA,EAAkB,KAAK,cAAA,IAAkB,EAAA;AAAA,QACzC,QAAA,EAAU,KAAK,QAAA,IAAY,EAAA;AAAA,QAC3B,QAAA,EAAU,KAAK,QAAA,IAAY;AAAA;AAC7B,KACF;AACA,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,WAAW,OAAA,CAAQ,QAAA;AACzB,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,QAAA,CAAS,YAAY,CAAA,GAAI;AAAA,MACvB,IAAA,EAAM,mBAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACZ;AACA,IAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AAAA,EACzB;AAEA,EAAA,MAAM,YAA0C,EAAC;AACjD,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,SAAS,CAAA,EAAG;AACpC,IAAA,SAAA,CAAU,MAAM,CAAA,GAAI,EAAE,IAAA,EAAM,MAAA,EAAQ,UAAU,KAAA,EAAM;AAAA,EACtD;AAEA,EAAA,MAAM,aAAA,GAA8C;AAAA,IAClD,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,cAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,QAAA;AAAA,UACN,QAAA,EAAW,OAAA,CAAQ,MAAA,IAAsC;AAAC;AAC5D,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAU;AAAA,KACpB;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,cAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,QAAA;AAAA,UACN,QAAA,EAAW,OAAA,CAAQ,MAAA,IAAsC;AAAC;AAC5D,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAU;AAAA;AACpB,GACF;AAEA,EAAA,IAAI,aAAA,GAAmD,IAAA;AACvD,EAAA,MAAM,MAAM,OAAA,CAAQ,cAAA;AAGpB,EAAA,IACE,GAAA,IACA,OAAO,GAAA,KAAQ,QAAA,IACf,OAAO,GAAA,CAAI,UAAA,KAAe,QAAA,IAC1B,GAAA,CAAI,UAAA,EACJ;AACA,IAAA,aAAA,GAAgB;AAAA,MACd,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,SAAS,OAAO,GAAA,CAAI,OAAA,KAAY,QAAA,GAAW,IAAI,OAAA,GAAU,IAAA;AAAA,MACzD,MAAM,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,GAAW,IAAI,IAAA,GAAO;AAAA,KAClD;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,cAAA,EAAgB,CAAA;AAAA,IAChB,QAAA,EAAU,OAAA;AAAA,IACV,eAAA,EAAiB,cAAA;AAAA,IACjB,SAAA;AAAA,IACA,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAA,EAAgB;AAAA,GAClB;AACF","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"]}
@@ -0,0 +1,128 @@
1
+ /** V3 Theme Settings — the canonical data shape */
2
+ interface ThemeSettingsV3 {
3
+ schema_version: 3;
4
+ theme_id: string;
5
+ global_settings: Record<string, any>;
6
+ templates: Record<string, PageTemplate>;
7
+ section_groups: Record<string, SectionGroup>;
8
+ external_theme?: ExternalThemeMetadata | null;
9
+ }
10
+ interface PageTemplate {
11
+ name: string;
12
+ sections: Record<string, SectionInstance>;
13
+ order: string[];
14
+ }
15
+ interface SectionGroup {
16
+ name: string;
17
+ sections: Record<string, SectionInstance>;
18
+ order: string[];
19
+ }
20
+ interface SectionInstance {
21
+ type: string;
22
+ settings: Record<string, any>;
23
+ disabled?: boolean;
24
+ blocks?: Record<string, BlockInstance>;
25
+ block_order?: string[];
26
+ }
27
+ interface BlockInstance {
28
+ type: string;
29
+ settings: Record<string, any>;
30
+ disabled?: boolean;
31
+ }
32
+ interface ExternalThemeMetadata {
33
+ bundle_url: string;
34
+ css_url?: string | null;
35
+ mode?: string;
36
+ settings_schema?: Record<string, any> | null;
37
+ section_schemas?: Record<string, any> | null;
38
+ }
39
+ /** Section schema for customizer form generation */
40
+ interface SectionSchema {
41
+ type: string;
42
+ name: string;
43
+ name_ar?: string;
44
+ tag?: string;
45
+ class?: string;
46
+ limit?: number;
47
+ settings: SettingDefinition[];
48
+ blocks?: BlockSchema[];
49
+ max_blocks?: number;
50
+ presets?: SectionPreset[];
51
+ }
52
+ interface BlockSchema {
53
+ type: string;
54
+ name: string;
55
+ name_ar?: string;
56
+ limit?: number;
57
+ settings: SettingDefinition[];
58
+ }
59
+ /**
60
+ * Setting types the V3 customizer renders. Themes can declare any
61
+ * string here; unknown types fall through to a plain text input. The
62
+ * union below documents the canonical set so theme authors get
63
+ * autocomplete and TypeScript errors on typos.
64
+ */
65
+ type SettingType = "text" | "textarea" | "richtext" | "number" | "range" | "color" | "checkbox" | "select" | "radio" | "font" | "image_picker" | "url" | "product" | "collection" | "header" | "paragraph" | "html" | "date" | "time" | "video_picker" | "color_scheme" | "page_picker" | "blog_picker" | "link_list_picker" | "variant_picker" | "file_upload";
66
+ /**
67
+ * `visible_if` — conditional visibility expression evaluated against
68
+ * sibling settings in the same schema. Two flavors:
69
+ *
70
+ * - String DSL: `"settings.show_button == true && settings.layout != 'minimal'"`
71
+ * - Object form: `{ show_button: true, layout: ["full", "split"] }`
72
+ *
73
+ * Evaluator lives in the merchant hub: any setting whose expression is
74
+ * falsy is skipped from the rendered form.
75
+ */
76
+ type VisibleIf = string | Record<string, unknown>;
77
+ interface SettingDefinition {
78
+ type: SettingType | (string & {});
79
+ id: string;
80
+ label: string;
81
+ label_ar?: string;
82
+ default?: any;
83
+ info?: string;
84
+ info_ar?: string;
85
+ placeholder?: string;
86
+ options?: {
87
+ value: string;
88
+ label: string;
89
+ label_ar?: string;
90
+ }[];
91
+ min?: number;
92
+ max?: number;
93
+ step?: number;
94
+ unit?: string;
95
+ /** Hide this setting unless the expression evaluates truthy. */
96
+ visible_if?: VisibleIf;
97
+ }
98
+ interface SectionPreset {
99
+ name: string;
100
+ /** Localized names so the Add Section dialog reads in the editor's
101
+ * current locale. Falls back to `name` when the locale is missing. */
102
+ locales?: {
103
+ en?: {
104
+ name?: string;
105
+ };
106
+ ar?: {
107
+ name?: string;
108
+ };
109
+ };
110
+ settings?: Record<string, any>;
111
+ blocks?: {
112
+ type: string;
113
+ settings?: Record<string, any>;
114
+ }[];
115
+ }
116
+ /** Section component props */
117
+ interface SectionProps {
118
+ settings: Record<string, any>;
119
+ blocks?: Record<string, BlockInstance>;
120
+ blockOrder?: string[];
121
+ storeData?: any;
122
+ }
123
+ /** Block component props */
124
+ interface BlockProps {
125
+ settings: Record<string, any>;
126
+ }
127
+
128
+ export type { BlockSchema as B, ExternalThemeMetadata as E, PageTemplate as P, SectionInstance as S, ThemeSettingsV3 as T, BlockProps as a, SectionSchema as b, SectionProps as c, BlockInstance as d, SectionGroup as e, SectionPreset as f, SettingDefinition as g };
@@ -0,0 +1,128 @@
1
+ /** V3 Theme Settings — the canonical data shape */
2
+ interface ThemeSettingsV3 {
3
+ schema_version: 3;
4
+ theme_id: string;
5
+ global_settings: Record<string, any>;
6
+ templates: Record<string, PageTemplate>;
7
+ section_groups: Record<string, SectionGroup>;
8
+ external_theme?: ExternalThemeMetadata | null;
9
+ }
10
+ interface PageTemplate {
11
+ name: string;
12
+ sections: Record<string, SectionInstance>;
13
+ order: string[];
14
+ }
15
+ interface SectionGroup {
16
+ name: string;
17
+ sections: Record<string, SectionInstance>;
18
+ order: string[];
19
+ }
20
+ interface SectionInstance {
21
+ type: string;
22
+ settings: Record<string, any>;
23
+ disabled?: boolean;
24
+ blocks?: Record<string, BlockInstance>;
25
+ block_order?: string[];
26
+ }
27
+ interface BlockInstance {
28
+ type: string;
29
+ settings: Record<string, any>;
30
+ disabled?: boolean;
31
+ }
32
+ interface ExternalThemeMetadata {
33
+ bundle_url: string;
34
+ css_url?: string | null;
35
+ mode?: string;
36
+ settings_schema?: Record<string, any> | null;
37
+ section_schemas?: Record<string, any> | null;
38
+ }
39
+ /** Section schema for customizer form generation */
40
+ interface SectionSchema {
41
+ type: string;
42
+ name: string;
43
+ name_ar?: string;
44
+ tag?: string;
45
+ class?: string;
46
+ limit?: number;
47
+ settings: SettingDefinition[];
48
+ blocks?: BlockSchema[];
49
+ max_blocks?: number;
50
+ presets?: SectionPreset[];
51
+ }
52
+ interface BlockSchema {
53
+ type: string;
54
+ name: string;
55
+ name_ar?: string;
56
+ limit?: number;
57
+ settings: SettingDefinition[];
58
+ }
59
+ /**
60
+ * Setting types the V3 customizer renders. Themes can declare any
61
+ * string here; unknown types fall through to a plain text input. The
62
+ * union below documents the canonical set so theme authors get
63
+ * autocomplete and TypeScript errors on typos.
64
+ */
65
+ type SettingType = "text" | "textarea" | "richtext" | "number" | "range" | "color" | "checkbox" | "select" | "radio" | "font" | "image_picker" | "url" | "product" | "collection" | "header" | "paragraph" | "html" | "date" | "time" | "video_picker" | "color_scheme" | "page_picker" | "blog_picker" | "link_list_picker" | "variant_picker" | "file_upload";
66
+ /**
67
+ * `visible_if` — conditional visibility expression evaluated against
68
+ * sibling settings in the same schema. Two flavors:
69
+ *
70
+ * - String DSL: `"settings.show_button == true && settings.layout != 'minimal'"`
71
+ * - Object form: `{ show_button: true, layout: ["full", "split"] }`
72
+ *
73
+ * Evaluator lives in the merchant hub: any setting whose expression is
74
+ * falsy is skipped from the rendered form.
75
+ */
76
+ type VisibleIf = string | Record<string, unknown>;
77
+ interface SettingDefinition {
78
+ type: SettingType | (string & {});
79
+ id: string;
80
+ label: string;
81
+ label_ar?: string;
82
+ default?: any;
83
+ info?: string;
84
+ info_ar?: string;
85
+ placeholder?: string;
86
+ options?: {
87
+ value: string;
88
+ label: string;
89
+ label_ar?: string;
90
+ }[];
91
+ min?: number;
92
+ max?: number;
93
+ step?: number;
94
+ unit?: string;
95
+ /** Hide this setting unless the expression evaluates truthy. */
96
+ visible_if?: VisibleIf;
97
+ }
98
+ interface SectionPreset {
99
+ name: string;
100
+ /** Localized names so the Add Section dialog reads in the editor's
101
+ * current locale. Falls back to `name` when the locale is missing. */
102
+ locales?: {
103
+ en?: {
104
+ name?: string;
105
+ };
106
+ ar?: {
107
+ name?: string;
108
+ };
109
+ };
110
+ settings?: Record<string, any>;
111
+ blocks?: {
112
+ type: string;
113
+ settings?: Record<string, any>;
114
+ }[];
115
+ }
116
+ /** Section component props */
117
+ interface SectionProps {
118
+ settings: Record<string, any>;
119
+ blocks?: Record<string, BlockInstance>;
120
+ blockOrder?: string[];
121
+ storeData?: any;
122
+ }
123
+ /** Block component props */
124
+ interface BlockProps {
125
+ settings: Record<string, any>;
126
+ }
127
+
128
+ export type { BlockSchema as B, ExternalThemeMetadata as E, PageTemplate as P, SectionInstance as S, ThemeSettingsV3 as T, BlockProps as a, SectionSchema as b, SectionProps as c, BlockInstance as d, SectionGroup as e, SectionPreset as f, SettingDefinition as g };
package/dist/types.cjs ADDED
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+
3
+ //# sourceMappingURL=types.cjs.map
4
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"types.cjs"}
@@ -0,0 +1,2 @@
1
+ export { A as Address, a as Cart, e as CartItem, C as Collection, b as Customer, O as Order, f as OrderItem, c as Page, P as Product, g as ProductImage, d as ProductVariant, S as Store } from './index-zSb0FIyh.mjs';
2
+ export { d as BlockInstance, a as BlockProps, B as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, e as SectionGroup, S as SectionInstance, f as SectionPreset, c as SectionProps, b as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-NWJAU9jd.mjs';
@@ -0,0 +1,2 @@
1
+ export { A as Address, a as Cart, e as CartItem, C as Collection, b as Customer, O as Order, f as OrderItem, c as Page, P as Product, g as ProductImage, d as ProductVariant, S as Store } from './index-CGx6FNqb.js';
2
+ export { d as BlockInstance, a as BlockProps, B as BlockSchema, E as ExternalThemeMetadata, P as PageTemplate, e as SectionGroup, S as SectionInstance, f as SectionPreset, c as SectionProps, b as SectionSchema, g as SettingDefinition, T as ThemeSettingsV3 } from './theme-NWJAU9jd.js';
package/dist/types.mjs ADDED
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=types.mjs.map
3
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"types.mjs"}
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@numueg/theme-sdk",
3
+ "version": "0.1.2",
4
+ "description": "NUMU Theme SDK — typed hooks, components, and utilities for building NUMU storefront themes",
5
+ "keywords": [
6
+ "numu",
7
+ "theme",
8
+ "sdk",
9
+ "react",
10
+ "hooks",
11
+ "storefront",
12
+ "ecommerce",
13
+ "byot"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "NUMU <hello@numueg.app>",
17
+ "homepage": "https://numueg.app/docs/sdk/overview",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/NUMU-IO/numu-theme-sdk.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/NUMU-IO/numu-theme-sdk/issues"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org/"
28
+ },
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.mjs",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.mjs",
36
+ "require": "./dist/index.cjs"
37
+ },
38
+ "./types": {
39
+ "types": "./dist/types.d.ts",
40
+ "import": "./dist/types.mjs",
41
+ "require": "./dist/types.cjs"
42
+ },
43
+ "./normalize": {
44
+ "types": "./dist/normalize.d.ts",
45
+ "import": "./dist/normalize.mjs",
46
+ "require": "./dist/normalize.cjs"
47
+ },
48
+ "./package.json": "./package.json"
49
+ },
50
+ "sideEffects": false,
51
+ "files": [
52
+ "dist",
53
+ "README.md",
54
+ "LICENSE",
55
+ "CHANGELOG.md"
56
+ ],
57
+ "engines": {
58
+ "node": ">=18"
59
+ },
60
+ "scripts": {
61
+ "build": "tsup",
62
+ "dev": "tsup --watch",
63
+ "typecheck": "tsc --noEmit",
64
+ "prepublishOnly": "npm run typecheck && npm run build",
65
+ "pack:dry": "npm pack --dry-run"
66
+ },
67
+ "peerDependencies": {
68
+ "react": ">=18.0.0",
69
+ "react-dom": ">=18.0.0"
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "^22.0.0",
73
+ "@types/react": "^19.1.0",
74
+ "@types/react-dom": "^19.1.0",
75
+ "react": "^19.1.0",
76
+ "react-dom": "^19.1.0",
77
+ "tsup": "^8.4.0",
78
+ "typescript": "^5.8.0",
79
+ "vitest": "^4.1.4"
80
+ }
81
+ }