@numueg/theme-sdk 0.10.0 → 0.12.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 (34) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/dist/{chunk-KWODVI7F.cjs → chunk-7RZISLP2.cjs} +3 -3
  3. package/dist/{chunk-KWODVI7F.cjs.map → chunk-7RZISLP2.cjs.map} +1 -1
  4. package/dist/{chunk-IBNAZRUT.cjs → chunk-V3JDQXD3.cjs} +2 -2
  5. package/dist/chunk-V3JDQXD3.cjs.map +1 -0
  6. package/dist/{chunk-EB2ESBLD.mjs → chunk-ZECKSIDL.mjs} +3 -3
  7. package/dist/{chunk-EB2ESBLD.mjs.map → chunk-ZECKSIDL.mjs.map} +1 -1
  8. package/dist/{chunk-4TMO5Y5W.mjs → chunk-ZYZZG4JR.mjs} +2 -2
  9. package/dist/chunk-ZYZZG4JR.mjs.map +1 -0
  10. package/dist/{entities-B8378GKp.d.ts → entities-1XRRUTlO.d.mts} +45 -1
  11. package/dist/{entities-B8378GKp.d.mts → entities-1XRRUTlO.d.ts} +45 -1
  12. package/dist/index.cjs +277 -86
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.mts +282 -8
  15. package/dist/index.d.ts +282 -8
  16. package/dist/index.mjs +202 -27
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/{mount-eAhGgGtZ.d.mts → mount-DDFMacmj.d.mts} +6 -1
  19. package/dist/{mount-HbnOAYZ-.d.ts → mount-qofjiBxw.d.ts} +6 -1
  20. package/dist/types.d.mts +1 -1
  21. package/dist/types.d.ts +1 -1
  22. package/dist/v2-compat.cjs +6 -6
  23. package/dist/v2-compat.d.mts +1 -1
  24. package/dist/v2-compat.d.ts +1 -1
  25. package/dist/v2-compat.mjs +1 -1
  26. package/dist/validation.cjs +11 -11
  27. package/dist/validation.mjs +1 -1
  28. package/dist/verify.cjs +3 -3
  29. package/dist/verify.d.mts +2 -2
  30. package/dist/verify.d.ts +2 -2
  31. package/dist/verify.mjs +2 -2
  32. package/package.json +1 -1
  33. package/dist/chunk-4TMO5Y5W.mjs.map +0 -1
  34. package/dist/chunk-IBNAZRUT.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkTBSNHHFH_cjs = require('./chunk-TBSNHHFH.cjs');
4
- var chunkKWODVI7F_cjs = require('./chunk-KWODVI7F.cjs');
5
- var chunkIBNAZRUT_cjs = require('./chunk-IBNAZRUT.cjs');
4
+ var chunk7RZISLP2_cjs = require('./chunk-7RZISLP2.cjs');
5
+ var chunkV3JDQXD3_cjs = require('./chunk-V3JDQXD3.cjs');
6
6
  var react = require('react');
7
7
  var client = require('react-dom/client');
8
8
  var jsxRuntime = require('react/jsx-runtime');
@@ -10,28 +10,95 @@ var jsxRuntime = require('react/jsx-runtime');
10
10
  // src/types/theme.ts
11
11
  var MAX_BLOCK_DEPTH = 5;
12
12
  function useProduct() {
13
- const ctx = react.useContext(chunkIBNAZRUT_cjs.ProductContext);
13
+ const ctx = react.useContext(chunkV3JDQXD3_cjs.ProductContext);
14
14
  if (!ctx) throw new Error("useProduct must be used within ProductProvider");
15
15
  return ctx;
16
16
  }
17
17
  function useProductOptional() {
18
- return react.useContext(chunkIBNAZRUT_cjs.ProductContext);
18
+ return react.useContext(chunkV3JDQXD3_cjs.ProductContext);
19
19
  }
20
20
  function useCollection() {
21
- const ctx = react.useContext(chunkIBNAZRUT_cjs.CollectionContext);
21
+ const ctx = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
22
22
  if (!ctx) throw new Error("useCollection must be used within CollectionProvider");
23
23
  return ctx;
24
24
  }
25
25
  function useCollectionOptional() {
26
- return react.useContext(chunkIBNAZRUT_cjs.CollectionContext);
26
+ return react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
27
+ }
28
+ function useListingHeading(options = {}) {
29
+ const collection = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
30
+ const clean = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : "";
31
+ const collectionName = clean(collection?.name);
32
+ const collectionDescription = clean(
33
+ collection?.description
34
+ );
35
+ return {
36
+ collection: collection ?? null,
37
+ isCollection: collectionName !== "",
38
+ title: collectionName || clean(options.title) || clean(options.defaultTitle) || "",
39
+ description: collectionDescription || clean(options.description) || ""
40
+ };
27
41
  }
28
42
  function useCart() {
29
- const ctx = react.useContext(chunkIBNAZRUT_cjs.CartContext);
43
+ const ctx = react.useContext(chunkV3JDQXD3_cjs.CartContext);
30
44
  if (!ctx) throw new Error("useCart must be used within NuMuProvider");
31
45
  return ctx;
32
46
  }
33
47
  function useCurrentTemplate() {
34
- return react.useContext(chunkIBNAZRUT_cjs.CurrentTemplateContext);
48
+ return react.useContext(chunkV3JDQXD3_cjs.CurrentTemplateContext);
49
+ }
50
+ function isMetafield(value) {
51
+ return typeof value === "object" && value !== null && typeof value.namespace === "string" && typeof value.key === "string";
52
+ }
53
+ function pageMetafields(data) {
54
+ if (!data) return [];
55
+ const record = data.page;
56
+ for (const candidate of [record?.metafields, data.metafields]) {
57
+ if (Array.isArray(candidate)) return candidate.filter(isMetafield);
58
+ }
59
+ return [];
60
+ }
61
+ function useMetafields(owner) {
62
+ const product = react.useContext(chunkV3JDQXD3_cjs.ProductContext);
63
+ const collection = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
64
+ const page = react.useContext(chunkV3JDQXD3_cjs.PageContext);
65
+ switch (owner) {
66
+ case "product":
67
+ return product?.metafields ?? [];
68
+ case "collection":
69
+ return collection?.metafields ?? [];
70
+ case "page":
71
+ return pageMetafields(page?.data);
72
+ default:
73
+ return [];
74
+ }
75
+ }
76
+ function useMetafield(owner, namespace, key) {
77
+ const fields = useMetafields(owner);
78
+ return fields.find((m) => m.namespace === namespace && m.key === key) ?? null;
79
+ }
80
+ function pageData() {
81
+ return react.useContext(chunkV3JDQXD3_cjs.PageContext)?.data;
82
+ }
83
+ function useBlogs() {
84
+ const data = pageData();
85
+ const blogs = data?.blogs;
86
+ return Array.isArray(blogs) ? blogs : [];
87
+ }
88
+ function useBlog() {
89
+ const data = pageData();
90
+ const blog = data?.blog ?? data?.article?.blog;
91
+ return blog && typeof blog === "object" ? blog : null;
92
+ }
93
+ function useArticles() {
94
+ const data = pageData();
95
+ const articles = data?.articles;
96
+ return Array.isArray(articles) ? articles : [];
97
+ }
98
+ function useArticle() {
99
+ const data = pageData();
100
+ const article = data?.article;
101
+ return article && typeof article === "object" ? article : null;
35
102
  }
36
103
  var SectionContext = react.createContext(null);
37
104
  function useSection() {
@@ -44,7 +111,7 @@ function useSectionOptional() {
44
111
  }
45
112
  var EMPTY = Object.freeze([]);
46
113
  function useSectionGroup(group) {
47
- const settings = react.useContext(chunkIBNAZRUT_cjs.ThemeSettingsContext);
114
+ const settings = react.useContext(chunkV3JDQXD3_cjs.ThemeSettingsContext);
48
115
  return react.useMemo(() => {
49
116
  const grp = settings?.section_groups?.[group];
50
117
  if (!grp || !Array.isArray(grp.order) || grp.order.length === 0) {
@@ -61,8 +128,8 @@ function useSectionGroup(group) {
61
128
  }, [settings, group]);
62
129
  }
63
130
  function useCurrency() {
64
- const ctx = react.useContext(chunkIBNAZRUT_cjs.CurrencyContext);
65
- const shop = chunkIBNAZRUT_cjs.useShop();
131
+ const ctx = react.useContext(chunkV3JDQXD3_cjs.CurrencyContext);
132
+ const shop = chunkV3JDQXD3_cjs.useShop();
66
133
  const fallback = react.useMemo(() => {
67
134
  const ccy = shop?.currency || "EGP";
68
135
  return {
@@ -82,16 +149,16 @@ function useCurrency() {
82
149
 
83
150
  // src/hooks/useMoney.ts
84
151
  function useMoney(currencyOverride) {
85
- const { formatMoney } = chunkIBNAZRUT_cjs.useLocalization();
86
- const shop = chunkIBNAZRUT_cjs.useShop();
152
+ const { formatMoney: formatMoney2 } = chunkV3JDQXD3_cjs.useLocalization();
153
+ const shop = chunkV3JDQXD3_cjs.useShop();
87
154
  const { selected, base, autoConvert, convert } = useCurrency();
88
155
  return (amount) => {
89
156
  const shouldConvert = autoConvert && !currencyOverride && !!selected && selected !== base;
90
157
  if (shouldConvert) {
91
158
  const converted = convert(Math.round(amount * 100), selected) / 100;
92
- return formatMoney(converted, selected);
159
+ return formatMoney2(converted, selected);
93
160
  }
94
- return formatMoney(amount, currencyOverride || shop?.currency);
161
+ return formatMoney2(amount, currencyOverride || shop?.currency);
95
162
  };
96
163
  }
97
164
  var DEFAULT_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600, 1920];
@@ -131,7 +198,7 @@ function unwrap(json) {
131
198
  return json;
132
199
  }
133
200
  function useOrders() {
134
- const customer = chunkIBNAZRUT_cjs.useCustomer();
201
+ const customer = chunkV3JDQXD3_cjs.useCustomer();
135
202
  const [orders, setOrders] = react.useState([]);
136
203
  const [loading, setLoading] = react.useState(false);
137
204
  const [error, setError] = react.useState(null);
@@ -178,7 +245,7 @@ function useOrders() {
178
245
  };
179
246
  }
180
247
  function useOrder(id) {
181
- const customer = chunkIBNAZRUT_cjs.useCustomer();
248
+ const customer = chunkV3JDQXD3_cjs.useCustomer();
182
249
  const [order, setOrder] = react.useState(null);
183
250
  const [loading, setLoading] = react.useState(false);
184
251
  const [error, setError] = react.useState(null);
@@ -241,7 +308,7 @@ async function readBody(res) {
241
308
  }
242
309
  }
243
310
  function useCustomerAddresses() {
244
- const customer = chunkIBNAZRUT_cjs.useCustomer();
311
+ const customer = chunkV3JDQXD3_cjs.useCustomer();
245
312
  const [addresses, setAddresses] = react.useState([]);
246
313
  const [loading, setLoading] = react.useState(false);
247
314
  const [error, setError] = react.useState(null);
@@ -404,8 +471,8 @@ function toNavigationItem(raw, locale) {
404
471
  };
405
472
  }
406
473
  function useNavigation(handle, options) {
407
- const navMap = react.useContext(chunkIBNAZRUT_cjs.NavigationContext);
408
- const localization = react.useContext(chunkIBNAZRUT_cjs.LocalizationContext);
474
+ const navMap = react.useContext(chunkV3JDQXD3_cjs.NavigationContext);
475
+ const localization = react.useContext(chunkV3JDQXD3_cjs.LocalizationContext);
409
476
  const locale = localization?.locale ?? "en";
410
477
  const hostProvidedAny = !!navMap && Object.keys(navMap).length > 0;
411
478
  const rawItems = handle ? navMap?.[handle] : void 0;
@@ -589,10 +656,11 @@ function getFingerprint() {
589
656
  }
590
657
  function dispatchAnalyticsEvent(eventName, payload = {}) {
591
658
  if (typeof window === "undefined") return;
659
+ const eventId = crypto.randomUUID();
592
660
  try {
593
661
  window.dispatchEvent(
594
662
  new CustomEvent("numu:analytics:event", {
595
- detail: { event: eventName, payload, ts: Date.now() }
663
+ detail: { event: eventName, payload, event_id: eventId, ts: Date.now() }
596
664
  })
597
665
  );
598
666
  } catch {
@@ -600,7 +668,7 @@ function dispatchAnalyticsEvent(eventName, payload = {}) {
600
668
  const step = EVENT_TO_FUNNEL_STEP[eventName] ?? null;
601
669
  const customerId = readCustomerId();
602
670
  const body = step ? {
603
- event_id: crypto.randomUUID(),
671
+ event_id: eventId,
604
672
  path: window.location.pathname,
605
673
  fingerprint: getFingerprint(),
606
674
  step,
@@ -611,6 +679,7 @@ function dispatchAnalyticsEvent(eventName, payload = {}) {
611
679
  } : {
612
680
  event: eventName,
613
681
  payload,
682
+ event_id: eventId,
614
683
  ts: Date.now(),
615
684
  attribution: readAttribution() ?? void 0,
616
685
  customer_id: customerId ?? void 0
@@ -809,7 +878,7 @@ function useCachedResource(key, fetcher, options) {
809
878
 
810
879
  // src/hooks/useApp.ts
811
880
  function useApp(slug) {
812
- const shop = chunkIBNAZRUT_cjs.useShop();
881
+ const shop = chunkV3JDQXD3_cjs.useShop();
813
882
  const key = slug ? `numu:app:${shop.id}:${slug}` : "";
814
883
  const fetcher = react.useCallback(
815
884
  async (signal) => {
@@ -957,7 +1026,7 @@ function useRelatedProducts(productId, options = {}) {
957
1026
  function useProductSizeChart(productOverride) {
958
1027
  const ctxProduct = useProductOptional();
959
1028
  const product = productOverride ?? ctxProduct;
960
- const shop = chunkIBNAZRUT_cjs.useShop();
1029
+ const shop = chunkV3JDQXD3_cjs.useShop();
961
1030
  const storeSettings = shop?.settings;
962
1031
  return react.useMemo(
963
1032
  () => resolveSizeChart(product?.attributes, storeSettings),
@@ -1493,8 +1562,9 @@ async function postCartMutation(endpoint, body, applyCart, reserveToken) {
1493
1562
  return { ok: false, status: res.status, message };
1494
1563
  }
1495
1564
  const json = await res.json();
1496
- applyCart(unwrapCart(json));
1497
- return { ok: true, status: res.status };
1565
+ const applied = unwrapCart(json);
1566
+ applyCart(applied);
1567
+ return { ok: true, status: res.status, cart: normalizeCartFromServer(applied) };
1498
1568
  }
1499
1569
  function NuMuProvider({
1500
1570
  store,
@@ -1742,6 +1812,10 @@ function NuMuProvider({
1742
1812
  if (!result.ok) return result;
1743
1813
  try {
1744
1814
  if (typeof window !== "undefined") {
1815
+ const line = result.cart?.items.find(
1816
+ (it) => variantId ? it.variant_id === variantId : it.product_id === productId
1817
+ );
1818
+ const value = line && typeof line.price === "number" && line.price > 0 ? Math.round(line.price * qty * 100) / 100 : void 0;
1745
1819
  window.dispatchEvent(
1746
1820
  new CustomEvent("numu:analytics:event", {
1747
1821
  detail: {
@@ -1749,7 +1823,8 @@ function NuMuProvider({
1749
1823
  payload: {
1750
1824
  content_ids: [productId],
1751
1825
  content_type: "product",
1752
- num_items: qty
1826
+ num_items: qty,
1827
+ ...value !== void 0 ? { value, currency: result.cart?.currency } : {}
1753
1828
  },
1754
1829
  event_id: eventId
1755
1830
  }
@@ -1996,13 +2071,13 @@ function NuMuProvider({
1996
2071
  convertCurrency
1997
2072
  ]
1998
2073
  );
1999
- return /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.ShopContext.Provider, { value: store, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.ThemeSettingsContext.Provider, { value: themeSettings, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.CurrentTemplateContext.Provider, { value: currentTemplate, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.PageContext.Provider, { value: pageValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.LocalizationContext.Provider, { value: localization, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.CurrencyContext.Provider, { value: currencyValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.CartContext.Provider, { value: cartValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.CustomerContext.Provider, { value: customerState, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerActionsContext.Provider, { value: customerActions, children: /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.NavigationContext.Provider, { value: navigation ?? {}, children }) }) }) }) }) }) }) }) }) });
2074
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.ShopContext.Provider, { value: store, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.ThemeSettingsContext.Provider, { value: themeSettings, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CurrentTemplateContext.Provider, { value: currentTemplate, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.PageContext.Provider, { value: pageValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.LocalizationContext.Provider, { value: localization, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CurrencyContext.Provider, { value: currencyValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CartContext.Provider, { value: cartValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CustomerContext.Provider, { value: customerState, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerActionsContext.Provider, { value: customerActions, children: /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.NavigationContext.Provider, { value: navigation ?? {}, children }) }) }) }) }) }) }) }) }) });
2000
2075
  }
2001
2076
  function ProductProvider({ product, children }) {
2002
- return /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.ProductContext.Provider, { value: product, children });
2077
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.ProductContext.Provider, { value: product, children });
2003
2078
  }
2004
2079
  function CollectionProvider({ collection, children }) {
2005
- return /* @__PURE__ */ jsxRuntime.jsx(chunkIBNAZRUT_cjs.CollectionContext.Provider, { value: collection, children });
2080
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CollectionContext.Provider, { value: collection, children });
2006
2081
  }
2007
2082
 
2008
2083
  // src/utils/logoStyle.ts
@@ -2247,13 +2322,13 @@ function pickDemo(ctx, themeSettings) {
2247
2322
  const t = themeSettings.templates;
2248
2323
  return !t || Object.keys(t).length === 0;
2249
2324
  }
2250
- function wrapEntityProviders(app, pageData) {
2325
+ function wrapEntityProviders(app, pageData2) {
2251
2326
  let inner = app;
2252
- if (pageData.collection) {
2253
- inner = /* @__PURE__ */ jsxRuntime.jsx(CollectionProvider, { collection: pageData.collection, children: inner });
2327
+ if (pageData2.collection) {
2328
+ inner = /* @__PURE__ */ jsxRuntime.jsx(CollectionProvider, { collection: pageData2.collection, children: inner });
2254
2329
  }
2255
- if (pageData.product) {
2256
- inner = /* @__PURE__ */ jsxRuntime.jsx(ProductProvider, { product: pageData.product, children: inner });
2330
+ if (pageData2.product) {
2331
+ inner = /* @__PURE__ */ jsxRuntime.jsx(ProductProvider, { product: pageData2.product, children: inner });
2257
2332
  }
2258
2333
  return inner;
2259
2334
  }
@@ -2276,7 +2351,7 @@ var ThemeMountBridge = react.forwardRef(function ThemeMountBridge2({ ctx, mountE
2276
2351
  const store = pickStore(ctx);
2277
2352
  const template = pickTemplate(ctx);
2278
2353
  const demo = pickDemo(ctx, themeSettings);
2279
- const pageData = ctx.page?.data ?? {};
2354
+ const pageData2 = ctx.page?.data ?? {};
2280
2355
  const app = renderApp({
2281
2356
  currentTemplate: template,
2282
2357
  demo,
@@ -2294,11 +2369,11 @@ var ThemeMountBridge = react.forwardRef(function ThemeMountBridge2({ ctx, mountE
2294
2369
  locale: ctx.locale,
2295
2370
  translations: ctx.translations,
2296
2371
  navigation: ctx.navigation,
2297
- initialProducts: pageData.products,
2298
- initialCollections: pageData.collections,
2372
+ initialProducts: pageData2.products,
2373
+ initialCollections: pageData2.collections,
2299
2374
  currentTemplate: template,
2300
2375
  pageTemplate: ctx.page?.template,
2301
- children: wrapEntityProviders(app, pageData)
2376
+ children: wrapEntityProviders(app, pageData2)
2302
2377
  }
2303
2378
  );
2304
2379
  });
@@ -2341,20 +2416,20 @@ function Money({
2341
2416
  className,
2342
2417
  as = "span"
2343
2418
  }) {
2344
- const { formatMoney } = chunkIBNAZRUT_cjs.useLocalization();
2345
- const shop = chunkIBNAZRUT_cjs.useShop();
2419
+ const { formatMoney: formatMoney2 } = chunkV3JDQXD3_cjs.useLocalization();
2420
+ const shop = chunkV3JDQXD3_cjs.useShop();
2346
2421
  const { selected, base, autoConvert, convert } = useCurrency();
2347
2422
  const shouldConvert = autoConvert && !currency && !!selected && selected !== base;
2348
2423
  const ccy = shouldConvert ? selected : currency || shop?.currency;
2349
2424
  const toDisplay = (major) => shouldConvert ? convert(Math.round(major * 100), selected) / 100 : major;
2350
2425
  const showCompare = compareAt != null && compareAt > amount;
2351
2426
  const children = [
2352
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatMoney(toDisplay(amount), ccy) }, "amt")
2427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatMoney2(toDisplay(amount), ccy) }, "amt")
2353
2428
  ];
2354
2429
  if (showCompare) {
2355
2430
  children.push(" ");
2356
2431
  children.push(
2357
- /* @__PURE__ */ jsxRuntime.jsx("s", { style: { opacity: 0.6 }, children: formatMoney(toDisplay(compareAt), ccy) }, "cmp")
2432
+ /* @__PURE__ */ jsxRuntime.jsx("s", { style: { opacity: 0.6 }, children: formatMoney2(toDisplay(compareAt), ccy) }, "cmp")
2358
2433
  );
2359
2434
  }
2360
2435
  return react.createElement(
@@ -2374,7 +2449,7 @@ function asImageTransform(v) {
2374
2449
  return void 0;
2375
2450
  }
2376
2451
  function applyImageTransform(t, fit = "cover") {
2377
- if (!t) return { objectFit: fit };
2452
+ if (!t) return {};
2378
2453
  const fx = Math.round(_clampT(t.focal?.x ?? 0.5, 0, 1) * 1e4) / 100;
2379
2454
  const fy = Math.round(_clampT(t.focal?.y ?? 0.5, 0, 1) * 1e4) / 100;
2380
2455
  const zoom = _clampT(t.zoom ?? 1, 1, 4);
@@ -2577,7 +2652,10 @@ function HeroMedia({
2577
2652
  const activeBase = useMobile ? MOBILE_BASE_WIDTH : DESKTOP_BASE_WIDTH;
2578
2653
  const activeSrc = rawFallback ? activeUrl : focalSrc(activeUrl, { width: activeBase, ...activeCrop });
2579
2654
  const activeSrcSet = rawFallback ? void 0 : heroSrcSet(activeUrl, activeCrop);
2580
- const fitStyle = applyImageTransform(useMobile ? mobileTransform ?? transform : transform, fit);
2655
+ const fitStyle = {
2656
+ objectFit: fit,
2657
+ ...applyImageTransform(useMobile ? mobileTransform ?? transform : transform, fit)
2658
+ };
2581
2659
  return /* @__PURE__ */ jsxRuntime.jsx(
2582
2660
  "img",
2583
2661
  {
@@ -2606,8 +2684,8 @@ function Logo({
2606
2684
  style,
2607
2685
  fallback
2608
2686
  }) {
2609
- const settings = chunkIBNAZRUT_cjs.useThemeSettings();
2610
- const shop = chunkIBNAZRUT_cjs.useShop();
2687
+ const settings = chunkV3JDQXD3_cjs.useThemeSettings();
2688
+ const shop = chunkV3JDQXD3_cjs.useShop();
2611
2689
  const g = settings?.global_settings ?? {};
2612
2690
  const url = str(src) || str(g.logo_url) || shop?.logo_url || "";
2613
2691
  const resolvedShape = shape || str(g.logo_shape) || "none";
@@ -2643,7 +2721,7 @@ function requestNavigate(href) {
2643
2721
  return !window.dispatchEvent(event);
2644
2722
  }
2645
2723
  function Link({ to, children, onClick, ...rest }) {
2646
- const shop = chunkIBNAZRUT_cjs.useShop();
2724
+ const shop = chunkV3JDQXD3_cjs.useShop();
2647
2725
  const isAbsolute = ABSOLUTE_URL.test(to);
2648
2726
  let href = to;
2649
2727
  if (!isAbsolute && shop && !to.startsWith("/")) {
@@ -2918,6 +2996,20 @@ function Form({
2918
2996
  }
2919
2997
  );
2920
2998
  }
2999
+
3000
+ // src/utils/routes.ts
3001
+ function productHref(slugOrId) {
3002
+ if (!slugOrId) return "/products";
3003
+ return `/products/${slugOrId}`;
3004
+ }
3005
+ function collectionHref(category) {
3006
+ if (!category) return "/collections";
3007
+ if (typeof category === "string") {
3008
+ return category ? `/collections/${category}` : "/collections";
3009
+ }
3010
+ const key = category.slug || category.id;
3011
+ return key ? `/collections/${key}` : "/collections";
3012
+ }
2921
3013
  function joinClass(...names) {
2922
3014
  return names.filter(Boolean).join(" ");
2923
3015
  }
@@ -2928,7 +3020,7 @@ function ProductCard({
2928
3020
  slots,
2929
3021
  imageSizes
2930
3022
  }) {
2931
- const target = href ?? `/products/${product.slug}`;
3023
+ const target = href ?? productHref(product.slug);
2932
3024
  const firstImage = product.images?.[0];
2933
3025
  const inStock = product.in_stock;
2934
3026
  const badge = slots?.badge !== void 0 ? slots.badge : inStock ? null : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "numu-product-card__badge", children: "Sold out" });
@@ -3208,8 +3300,8 @@ function LocaleSwitcher({
3208
3300
  onSelect,
3209
3301
  render
3210
3302
  }) {
3211
- const shop = chunkIBNAZRUT_cjs.useShop();
3212
- const { locale } = chunkIBNAZRUT_cjs.useLocalization();
3303
+ const shop = chunkV3JDQXD3_cjs.useShop();
3304
+ const { locale } = chunkV3JDQXD3_cjs.useLocalization();
3213
3305
  const available = shop.available_locales || [];
3214
3306
  const list = available.length > 0 ? available : shop.default_language === "en" ? ["en"] : Array.from(/* @__PURE__ */ new Set([shop.default_language, "en"]));
3215
3307
  const labelFor = react.useCallback((code) => {
@@ -3541,7 +3633,19 @@ function resolveSourcePath(path, ctx) {
3541
3633
  return null;
3542
3634
  }
3543
3635
  }
3636
+ function resolveMetafield(metafields, field) {
3637
+ const address = field.slice("metafield:".length);
3638
+ const dot = address.indexOf(".");
3639
+ if (dot === -1) return null;
3640
+ const namespace = address.slice(0, dot);
3641
+ const key = address.slice(dot + 1);
3642
+ const hit = (metafields ?? []).find(
3643
+ (m) => m.namespace === namespace && m.key === key
3644
+ );
3645
+ return hit ? hit.value ?? null : null;
3646
+ }
3544
3647
  function resolveProductField(p, field) {
3648
+ if (field.startsWith("metafield:")) return resolveMetafield(p.metafields, field);
3545
3649
  switch (field) {
3546
3650
  case "title":
3547
3651
  case "name":
@@ -3564,6 +3668,7 @@ function resolveProductField(p, field) {
3564
3668
  }
3565
3669
  }
3566
3670
  function resolveCollectionField(c, field) {
3671
+ if (field.startsWith("metafield:")) return resolveMetafield(c.metafields, field);
3567
3672
  switch (field) {
3568
3673
  case "title":
3569
3674
  case "name":
@@ -3614,9 +3719,9 @@ function resolveSettingsMap(settings, ctx) {
3614
3719
  return out;
3615
3720
  }
3616
3721
  function useResolvedSettings(instance) {
3617
- const product = react.useContext(chunkIBNAZRUT_cjs.ProductContext);
3618
- const collection = react.useContext(chunkIBNAZRUT_cjs.CollectionContext);
3619
- const store = react.useContext(chunkIBNAZRUT_cjs.ShopContext);
3722
+ const product = react.useContext(chunkV3JDQXD3_cjs.ProductContext);
3723
+ const collection = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
3724
+ const store = react.useContext(chunkV3JDQXD3_cjs.ShopContext);
3620
3725
  const ctx = react.useMemo(
3621
3726
  () => ({ product, collection, store }),
3622
3727
  [product, collection, store]
@@ -3746,6 +3851,76 @@ function assetUrl(name) {
3746
3851
  return `${cleanBase}${filename}`;
3747
3852
  }
3748
3853
 
3854
+ // src/utils/money.ts
3855
+ function resolveLocale(locale) {
3856
+ return locale === "ar" ? "ar-EG" : "en-EG";
3857
+ }
3858
+ function formatMoney(cents, options = {}) {
3859
+ const { currency, locale, fractionDigits = 0 } = options;
3860
+ const safe = Number.isFinite(cents) ? cents : 0;
3861
+ return new Intl.NumberFormat(resolveLocale(locale), {
3862
+ style: "currency",
3863
+ currency: currency || "EGP",
3864
+ maximumFractionDigits: fractionDigits
3865
+ }).format(safe / 100);
3866
+ }
3867
+ function formatMoneyMajor(amount, options = {}) {
3868
+ return formatMoney(
3869
+ (Number.isFinite(amount) ? amount : 0) * 100,
3870
+ options
3871
+ );
3872
+ }
3873
+ function centsToMajor(cents) {
3874
+ return (Number.isFinite(cents) ? cents : 0) / 100;
3875
+ }
3876
+ function majorToCents(amount) {
3877
+ return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
3878
+ }
3879
+
3880
+ // src/utils/templates.ts
3881
+ function resolveSections(group) {
3882
+ if (!group) return [];
3883
+ if (Array.isArray(group.sections)) {
3884
+ return group.sections.map((instance, idx) => ({
3885
+ id: `${instance.type}-${idx}`,
3886
+ instance
3887
+ }));
3888
+ }
3889
+ const map = group.sections ?? {};
3890
+ const order = group.order ?? Object.keys(map);
3891
+ const out = [];
3892
+ for (const id of order) {
3893
+ const instance = map[id];
3894
+ if (instance) out.push({ id, instance });
3895
+ }
3896
+ return out;
3897
+ }
3898
+ function selectTemplateSections(hostTemplate, builtinTemplate, isKnown) {
3899
+ const hostSections = resolveSections(hostTemplate);
3900
+ if (hostSections.length === 0) {
3901
+ return resolveSections(builtinTemplate);
3902
+ }
3903
+ const anyKnown = hostSections.some(({ instance }) => isKnown(instance.type));
3904
+ if (!anyKnown) return resolveSections(builtinTemplate);
3905
+ return hostSections.filter(({ instance }) => isKnown(instance.type));
3906
+ }
3907
+ function selectChromeSections(options) {
3908
+ const { hostGroup, presetGroup, inline, templates, isChrome, isKnown } = options;
3909
+ const known = (list) => list.filter(({ instance }) => isKnown(instance.type));
3910
+ const fromHost = known(resolveSections(hostGroup));
3911
+ if (fromHost.length > 0) return fromHost;
3912
+ if (inline && inline.length > 0) return known(inline);
3913
+ const fromPreset = known(resolveSections(presetGroup));
3914
+ if (fromPreset.length > 0) return fromPreset;
3915
+ for (const template of templates ?? []) {
3916
+ const borrowed = known(resolveSections(template)).filter(
3917
+ ({ instance }) => isChrome(instance.type)
3918
+ );
3919
+ if (borrowed.length > 0) return borrowed;
3920
+ }
3921
+ return [];
3922
+ }
3923
+
3749
3924
  // src/utils/locales.ts
3750
3925
  function flattenMessages(source, prefix = "") {
3751
3926
  const out = {};
@@ -3796,131 +3971,131 @@ Object.defineProperty(exports, "resolveThemeSettings", {
3796
3971
  });
3797
3972
  Object.defineProperty(exports, "KNOWN_SETTING_TYPES", {
3798
3973
  enumerable: true,
3799
- get: function () { return chunkKWODVI7F_cjs.KNOWN_SETTING_TYPES; }
3974
+ get: function () { return chunk7RZISLP2_cjs.KNOWN_SETTING_TYPES; }
3800
3975
  });
3801
3976
  Object.defineProperty(exports, "KNOWN_TEMPLATES", {
3802
3977
  enumerable: true,
3803
- get: function () { return chunkKWODVI7F_cjs.KNOWN_TEMPLATES; }
3978
+ get: function () { return chunk7RZISLP2_cjs.KNOWN_TEMPLATES; }
3804
3979
  });
3805
3980
  Object.defineProperty(exports, "REQUIRED_TEMPLATES", {
3806
3981
  enumerable: true,
3807
- get: function () { return chunkKWODVI7F_cjs.REQUIRED_TEMPLATES; }
3982
+ get: function () { return chunk7RZISLP2_cjs.REQUIRED_TEMPLATES; }
3808
3983
  });
3809
3984
  Object.defineProperty(exports, "SDK_VERSION", {
3810
3985
  enumerable: true,
3811
- get: function () { return chunkKWODVI7F_cjs.SDK_VERSION; }
3986
+ get: function () { return chunk7RZISLP2_cjs.SDK_VERSION; }
3812
3987
  });
3813
3988
  Object.defineProperty(exports, "THEME_CONTRACT_VERSION", {
3814
3989
  enumerable: true,
3815
- get: function () { return chunkKWODVI7F_cjs.THEME_CONTRACT_VERSION; }
3990
+ get: function () { return chunk7RZISLP2_cjs.THEME_CONTRACT_VERSION; }
3816
3991
  });
3817
3992
  Object.defineProperty(exports, "mergeResults", {
3818
3993
  enumerable: true,
3819
- get: function () { return chunkKWODVI7F_cjs.mergeResults; }
3994
+ get: function () { return chunk7RZISLP2_cjs.mergeResults; }
3820
3995
  });
3821
3996
  Object.defineProperty(exports, "validateBuiltManifest", {
3822
3997
  enumerable: true,
3823
- get: function () { return chunkKWODVI7F_cjs.validateBuiltManifest; }
3998
+ get: function () { return chunk7RZISLP2_cjs.validateBuiltManifest; }
3824
3999
  });
3825
4000
  Object.defineProperty(exports, "validateManifest", {
3826
4001
  enumerable: true,
3827
- get: function () { return chunkKWODVI7F_cjs.validateManifest; }
4002
+ get: function () { return chunk7RZISLP2_cjs.validateManifest; }
3828
4003
  });
3829
4004
  Object.defineProperty(exports, "validateSectionSchema", {
3830
4005
  enumerable: true,
3831
- get: function () { return chunkKWODVI7F_cjs.validateSectionSchema; }
4006
+ get: function () { return chunk7RZISLP2_cjs.validateSectionSchema; }
3832
4007
  });
3833
4008
  Object.defineProperty(exports, "validateSettingsAgainstSchema", {
3834
4009
  enumerable: true,
3835
- get: function () { return chunkKWODVI7F_cjs.validateSettingsAgainstSchema; }
4010
+ get: function () { return chunk7RZISLP2_cjs.validateSettingsAgainstSchema; }
3836
4011
  });
3837
4012
  Object.defineProperty(exports, "CartContext", {
3838
4013
  enumerable: true,
3839
- get: function () { return chunkIBNAZRUT_cjs.CartContext; }
4014
+ get: function () { return chunkV3JDQXD3_cjs.CartContext; }
3840
4015
  });
3841
4016
  Object.defineProperty(exports, "CollectionContext", {
3842
4017
  enumerable: true,
3843
- get: function () { return chunkIBNAZRUT_cjs.CollectionContext; }
4018
+ get: function () { return chunkV3JDQXD3_cjs.CollectionContext; }
3844
4019
  });
3845
4020
  Object.defineProperty(exports, "CurrencyContext", {
3846
4021
  enumerable: true,
3847
- get: function () { return chunkIBNAZRUT_cjs.CurrencyContext; }
4022
+ get: function () { return chunkV3JDQXD3_cjs.CurrencyContext; }
3848
4023
  });
3849
4024
  Object.defineProperty(exports, "CustomerContext", {
3850
4025
  enumerable: true,
3851
- get: function () { return chunkIBNAZRUT_cjs.CustomerContext; }
4026
+ get: function () { return chunkV3JDQXD3_cjs.CustomerContext; }
3852
4027
  });
3853
4028
  Object.defineProperty(exports, "LocalizationContext", {
3854
4029
  enumerable: true,
3855
- get: function () { return chunkIBNAZRUT_cjs.LocalizationContext; }
4030
+ get: function () { return chunkV3JDQXD3_cjs.LocalizationContext; }
3856
4031
  });
3857
4032
  Object.defineProperty(exports, "NavigationContext", {
3858
4033
  enumerable: true,
3859
- get: function () { return chunkIBNAZRUT_cjs.NavigationContext; }
4034
+ get: function () { return chunkV3JDQXD3_cjs.NavigationContext; }
3860
4035
  });
3861
4036
  Object.defineProperty(exports, "PageContext", {
3862
4037
  enumerable: true,
3863
- get: function () { return chunkIBNAZRUT_cjs.PageContext; }
4038
+ get: function () { return chunkV3JDQXD3_cjs.PageContext; }
3864
4039
  });
3865
4040
  Object.defineProperty(exports, "ProductContext", {
3866
4041
  enumerable: true,
3867
- get: function () { return chunkIBNAZRUT_cjs.ProductContext; }
4042
+ get: function () { return chunkV3JDQXD3_cjs.ProductContext; }
3868
4043
  });
3869
4044
  Object.defineProperty(exports, "ShopContext", {
3870
4045
  enumerable: true,
3871
- get: function () { return chunkIBNAZRUT_cjs.ShopContext; }
4046
+ get: function () { return chunkV3JDQXD3_cjs.ShopContext; }
3872
4047
  });
3873
4048
  Object.defineProperty(exports, "ThemeSettingsContext", {
3874
4049
  enumerable: true,
3875
- get: function () { return chunkIBNAZRUT_cjs.ThemeSettingsContext; }
4050
+ get: function () { return chunkV3JDQXD3_cjs.ThemeSettingsContext; }
3876
4051
  });
3877
4052
  Object.defineProperty(exports, "useCollections", {
3878
4053
  enumerable: true,
3879
- get: function () { return chunkIBNAZRUT_cjs.useCollections; }
4054
+ get: function () { return chunkV3JDQXD3_cjs.useCollections; }
3880
4055
  });
3881
4056
  Object.defineProperty(exports, "useCustomer", {
3882
4057
  enumerable: true,
3883
- get: function () { return chunkIBNAZRUT_cjs.useCustomer; }
4058
+ get: function () { return chunkV3JDQXD3_cjs.useCustomer; }
3884
4059
  });
3885
4060
  Object.defineProperty(exports, "useDirection", {
3886
4061
  enumerable: true,
3887
- get: function () { return chunkIBNAZRUT_cjs.useDirection; }
4062
+ get: function () { return chunkV3JDQXD3_cjs.useDirection; }
3888
4063
  });
3889
4064
  Object.defineProperty(exports, "useFieldTranslation", {
3890
4065
  enumerable: true,
3891
- get: function () { return chunkIBNAZRUT_cjs.useFieldTranslation; }
4066
+ get: function () { return chunkV3JDQXD3_cjs.useFieldTranslation; }
3892
4067
  });
3893
4068
  Object.defineProperty(exports, "useLocale", {
3894
4069
  enumerable: true,
3895
- get: function () { return chunkIBNAZRUT_cjs.useLocale; }
4070
+ get: function () { return chunkV3JDQXD3_cjs.useLocale; }
3896
4071
  });
3897
4072
  Object.defineProperty(exports, "useLocalization", {
3898
4073
  enumerable: true,
3899
- get: function () { return chunkIBNAZRUT_cjs.useLocalization; }
4074
+ get: function () { return chunkV3JDQXD3_cjs.useLocalization; }
3900
4075
  });
3901
4076
  Object.defineProperty(exports, "useNumberFormat", {
3902
4077
  enumerable: true,
3903
- get: function () { return chunkIBNAZRUT_cjs.useNumberFormat; }
4078
+ get: function () { return chunkV3JDQXD3_cjs.useNumberFormat; }
3904
4079
  });
3905
4080
  Object.defineProperty(exports, "usePage", {
3906
4081
  enumerable: true,
3907
- get: function () { return chunkIBNAZRUT_cjs.usePage; }
4082
+ get: function () { return chunkV3JDQXD3_cjs.usePage; }
3908
4083
  });
3909
4084
  Object.defineProperty(exports, "useProducts", {
3910
4085
  enumerable: true,
3911
- get: function () { return chunkIBNAZRUT_cjs.useProducts; }
4086
+ get: function () { return chunkV3JDQXD3_cjs.useProducts; }
3912
4087
  });
3913
4088
  Object.defineProperty(exports, "useShop", {
3914
4089
  enumerable: true,
3915
- get: function () { return chunkIBNAZRUT_cjs.useShop; }
4090
+ get: function () { return chunkV3JDQXD3_cjs.useShop; }
3916
4091
  });
3917
4092
  Object.defineProperty(exports, "useThemeSettings", {
3918
4093
  enumerable: true,
3919
- get: function () { return chunkIBNAZRUT_cjs.useThemeSettings; }
4094
+ get: function () { return chunkV3JDQXD3_cjs.useThemeSettings; }
3920
4095
  });
3921
4096
  Object.defineProperty(exports, "useTranslation", {
3922
4097
  enumerable: true,
3923
- get: function () { return chunkIBNAZRUT_cjs.useTranslation; }
4098
+ get: function () { return chunkV3JDQXD3_cjs.useTranslation; }
3924
4099
  });
3925
4100
  exports.AddToCartButton = AddToCartButton;
3926
4101
  exports.Block = Block;
@@ -3956,9 +4131,11 @@ exports.assetUrl = assetUrl;
3956
4131
  exports.availableValues = availableValues;
3957
4132
  exports.buildLocaleBundle = buildLocaleBundle;
3958
4133
  exports.buildThemeElement = buildThemeElement;
4134
+ exports.centsToMajor = centsToMajor;
3959
4135
  exports.clearSdkSingleton = clearSdkSingleton;
3960
4136
  exports.collectBlocks = collectBlocks;
3961
4137
  exports.collectSections = collectSections;
4138
+ exports.collectionHref = collectionHref;
3962
4139
  exports.computeGlobalStyleTokens = computeGlobalStyleTokens;
3963
4140
  exports.defaultVariant = defaultVariant;
3964
4141
  exports.defineBlock = defineBlock;
@@ -3968,6 +4145,8 @@ exports.dynamicSource = dynamicSource;
3968
4145
  exports.findVariantByOptions = findVariantByOptions;
3969
4146
  exports.flattenMessages = flattenMessages;
3970
4147
  exports.focalSrc = focalSrc;
4148
+ exports.formatMoney = formatMoney;
4149
+ exports.formatMoneyMajor = formatMoneyMajor;
3971
4150
  exports.getReactSingleton = getReactSingleton;
3972
4151
  exports.getSdkSingleton = getSdkSingleton;
3973
4152
  exports.isDefinedBlock = isDefinedBlock;
@@ -3976,8 +4155,10 @@ exports.isDynamicSource = isDynamicSource;
3976
4155
  exports.isSdkAvailable = isSdkAvailable;
3977
4156
  exports.logoImgStyle = logoImgStyle;
3978
4157
  exports.logoStyleTokens = logoStyleTokens;
4158
+ exports.majorToCents = majorToCents;
3979
4159
  exports.mountTheme = mountTheme;
3980
4160
  exports.pickTranslations = pickTranslations;
4161
+ exports.productHref = productHref;
3981
4162
  exports.publishVariantSelection = publishVariantSelection;
3982
4163
  exports.readVariantSelection = readVariantSelection;
3983
4164
  exports.registerReactSingleton = registerReactSingleton;
@@ -3985,12 +4166,19 @@ exports.registerSdkSingleton = registerSdkSingleton;
3985
4166
  exports.requestNavigate = requestNavigate;
3986
4167
  exports.resolveDynamicValue = resolveDynamicValue;
3987
4168
  exports.resolveFontStack = resolveFontStack;
4169
+ exports.resolveSections = resolveSections;
3988
4170
  exports.resolveSettingsMap = resolveSettingsMap;
3989
4171
  exports.resolveSizeChart = resolveSizeChart;
3990
4172
  exports.resolveSourcePath = resolveSourcePath;
3991
4173
  exports.sanitizeHtml = sanitizeHtml;
4174
+ exports.selectChromeSections = selectChromeSections;
4175
+ exports.selectTemplateSections = selectTemplateSections;
3992
4176
  exports.useAnalytics = useAnalytics;
3993
4177
  exports.useApp = useApp;
4178
+ exports.useArticle = useArticle;
4179
+ exports.useArticles = useArticles;
4180
+ exports.useBlog = useBlog;
4181
+ exports.useBlogs = useBlogs;
3994
4182
  exports.useCachedResource = useCachedResource;
3995
4183
  exports.useCart = useCart;
3996
4184
  exports.useCheckout = useCheckout;
@@ -4002,6 +4190,9 @@ exports.useCustomerActions = useCustomerActions;
4002
4190
  exports.useCustomerAddresses = useCustomerAddresses;
4003
4191
  exports.useGiftCardBalance = useGiftCardBalance;
4004
4192
  exports.useImage = useImage;
4193
+ exports.useListingHeading = useListingHeading;
4194
+ exports.useMetafield = useMetafield;
4195
+ exports.useMetafields = useMetafields;
4005
4196
  exports.useMoney = useMoney;
4006
4197
  exports.useNavigation = useNavigation;
4007
4198
  exports.useOrder = useOrder;