@numueg/theme-sdk 0.12.0 → 0.13.1

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 +53 -0
  2. package/dist/{chunk-ZECKSIDL.mjs → chunk-AFOLV2U5.mjs} +3 -3
  3. package/dist/{chunk-ZECKSIDL.mjs.map → chunk-AFOLV2U5.mjs.map} +1 -1
  4. package/dist/{chunk-7RZISLP2.cjs → chunk-JXJP6FND.cjs} +3 -3
  5. package/dist/{chunk-7RZISLP2.cjs.map → chunk-JXJP6FND.cjs.map} +1 -1
  6. package/dist/{chunk-ZYZZG4JR.mjs → chunk-VZO2WMEM.mjs} +13 -4
  7. package/dist/chunk-VZO2WMEM.mjs.map +1 -0
  8. package/dist/{chunk-V3JDQXD3.cjs → chunk-WNMFED3F.cjs} +13 -4
  9. package/dist/chunk-WNMFED3F.cjs.map +1 -0
  10. package/dist/{entities-1XRRUTlO.d.mts → entities-DRKn04q0.d.mts} +32 -0
  11. package/dist/{entities-1XRRUTlO.d.ts → entities-DRKn04q0.d.ts} +32 -0
  12. package/dist/index.cjs +228 -101
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.mts +215 -5
  15. package/dist/index.d.ts +215 -5
  16. package/dist/index.mjs +156 -35
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/{mount-DDFMacmj.d.mts → mount-BTaDtz8k.d.mts} +1 -1
  19. package/dist/{mount-qofjiBxw.d.ts → mount-CLQniVfc.d.ts} +1 -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-V3JDQXD3.cjs.map +0 -1
  34. package/dist/chunk-ZYZZG4JR.mjs.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 chunk7RZISLP2_cjs = require('./chunk-7RZISLP2.cjs');
5
- var chunkV3JDQXD3_cjs = require('./chunk-V3JDQXD3.cjs');
4
+ var chunkJXJP6FND_cjs = require('./chunk-JXJP6FND.cjs');
5
+ var chunkWNMFED3F_cjs = require('./chunk-WNMFED3F.cjs');
6
6
  var react = require('react');
7
7
  var client = require('react-dom/client');
8
8
  var jsxRuntime = require('react/jsx-runtime');
@@ -10,23 +10,23 @@ 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(chunkV3JDQXD3_cjs.ProductContext);
13
+ const ctx = react.useContext(chunkWNMFED3F_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(chunkV3JDQXD3_cjs.ProductContext);
18
+ return react.useContext(chunkWNMFED3F_cjs.ProductContext);
19
19
  }
20
20
  function useCollection() {
21
- const ctx = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
21
+ const ctx = react.useContext(chunkWNMFED3F_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(chunkV3JDQXD3_cjs.CollectionContext);
26
+ return react.useContext(chunkWNMFED3F_cjs.CollectionContext);
27
27
  }
28
28
  function useListingHeading(options = {}) {
29
- const collection = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
29
+ const collection = react.useContext(chunkWNMFED3F_cjs.CollectionContext);
30
30
  const clean = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : "";
31
31
  const collectionName = clean(collection?.name);
32
32
  const collectionDescription = clean(
@@ -40,12 +40,12 @@ function useListingHeading(options = {}) {
40
40
  };
41
41
  }
42
42
  function useCart() {
43
- const ctx = react.useContext(chunkV3JDQXD3_cjs.CartContext);
43
+ const ctx = react.useContext(chunkWNMFED3F_cjs.CartContext);
44
44
  if (!ctx) throw new Error("useCart must be used within NuMuProvider");
45
45
  return ctx;
46
46
  }
47
47
  function useCurrentTemplate() {
48
- return react.useContext(chunkV3JDQXD3_cjs.CurrentTemplateContext);
48
+ return react.useContext(chunkWNMFED3F_cjs.CurrentTemplateContext);
49
49
  }
50
50
  function isMetafield(value) {
51
51
  return typeof value === "object" && value !== null && typeof value.namespace === "string" && typeof value.key === "string";
@@ -59,9 +59,9 @@ function pageMetafields(data) {
59
59
  return [];
60
60
  }
61
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);
62
+ const product = react.useContext(chunkWNMFED3F_cjs.ProductContext);
63
+ const collection = react.useContext(chunkWNMFED3F_cjs.CollectionContext);
64
+ const page = react.useContext(chunkWNMFED3F_cjs.PageContext);
65
65
  switch (owner) {
66
66
  case "product":
67
67
  return product?.metafields ?? [];
@@ -78,7 +78,7 @@ function useMetafield(owner, namespace, key) {
78
78
  return fields.find((m) => m.namespace === namespace && m.key === key) ?? null;
79
79
  }
80
80
  function pageData() {
81
- return react.useContext(chunkV3JDQXD3_cjs.PageContext)?.data;
81
+ return react.useContext(chunkWNMFED3F_cjs.PageContext)?.data;
82
82
  }
83
83
  function useBlogs() {
84
84
  const data = pageData();
@@ -111,7 +111,7 @@ function useSectionOptional() {
111
111
  }
112
112
  var EMPTY = Object.freeze([]);
113
113
  function useSectionGroup(group) {
114
- const settings = react.useContext(chunkV3JDQXD3_cjs.ThemeSettingsContext);
114
+ const settings = react.useContext(chunkWNMFED3F_cjs.ThemeSettingsContext);
115
115
  return react.useMemo(() => {
116
116
  const grp = settings?.section_groups?.[group];
117
117
  if (!grp || !Array.isArray(grp.order) || grp.order.length === 0) {
@@ -128,8 +128,8 @@ function useSectionGroup(group) {
128
128
  }, [settings, group]);
129
129
  }
130
130
  function useCurrency() {
131
- const ctx = react.useContext(chunkV3JDQXD3_cjs.CurrencyContext);
132
- const shop = chunkV3JDQXD3_cjs.useShop();
131
+ const ctx = react.useContext(chunkWNMFED3F_cjs.CurrencyContext);
132
+ const shop = chunkWNMFED3F_cjs.useShop();
133
133
  const fallback = react.useMemo(() => {
134
134
  const ccy = shop?.currency || "EGP";
135
135
  return {
@@ -149,8 +149,8 @@ function useCurrency() {
149
149
 
150
150
  // src/hooks/useMoney.ts
151
151
  function useMoney(currencyOverride) {
152
- const { formatMoney: formatMoney2 } = chunkV3JDQXD3_cjs.useLocalization();
153
- const shop = chunkV3JDQXD3_cjs.useShop();
152
+ const { formatMoney: formatMoney2 } = chunkWNMFED3F_cjs.useLocalization();
153
+ const shop = chunkWNMFED3F_cjs.useShop();
154
154
  const { selected, base, autoConvert, convert } = useCurrency();
155
155
  return (amount) => {
156
156
  const shouldConvert = autoConvert && !currencyOverride && !!selected && selected !== base;
@@ -198,7 +198,7 @@ function unwrap(json) {
198
198
  return json;
199
199
  }
200
200
  function useOrders() {
201
- const customer = chunkV3JDQXD3_cjs.useCustomer();
201
+ const customer = chunkWNMFED3F_cjs.useCustomer();
202
202
  const [orders, setOrders] = react.useState([]);
203
203
  const [loading, setLoading] = react.useState(false);
204
204
  const [error, setError] = react.useState(null);
@@ -245,7 +245,7 @@ function useOrders() {
245
245
  };
246
246
  }
247
247
  function useOrder(id) {
248
- const customer = chunkV3JDQXD3_cjs.useCustomer();
248
+ const customer = chunkWNMFED3F_cjs.useCustomer();
249
249
  const [order, setOrder] = react.useState(null);
250
250
  const [loading, setLoading] = react.useState(false);
251
251
  const [error, setError] = react.useState(null);
@@ -308,7 +308,7 @@ async function readBody(res) {
308
308
  }
309
309
  }
310
310
  function useCustomerAddresses() {
311
- const customer = chunkV3JDQXD3_cjs.useCustomer();
311
+ const customer = chunkWNMFED3F_cjs.useCustomer();
312
312
  const [addresses, setAddresses] = react.useState([]);
313
313
  const [loading, setLoading] = react.useState(false);
314
314
  const [error, setError] = react.useState(null);
@@ -471,8 +471,8 @@ function toNavigationItem(raw, locale) {
471
471
  };
472
472
  }
473
473
  function useNavigation(handle, options) {
474
- const navMap = react.useContext(chunkV3JDQXD3_cjs.NavigationContext);
475
- const localization = react.useContext(chunkV3JDQXD3_cjs.LocalizationContext);
474
+ const navMap = react.useContext(chunkWNMFED3F_cjs.NavigationContext);
475
+ const localization = react.useContext(chunkWNMFED3F_cjs.LocalizationContext);
476
476
  const locale = localization?.locale ?? "en";
477
477
  const hostProvidedAny = !!navMap && Object.keys(navMap).length > 0;
478
478
  const rawItems = handle ? navMap?.[handle] : void 0;
@@ -878,7 +878,7 @@ function useCachedResource(key, fetcher, options) {
878
878
 
879
879
  // src/hooks/useApp.ts
880
880
  function useApp(slug) {
881
- const shop = chunkV3JDQXD3_cjs.useShop();
881
+ const shop = chunkWNMFED3F_cjs.useShop();
882
882
  const key = slug ? `numu:app:${shop.id}:${slug}` : "";
883
883
  const fetcher = react.useCallback(
884
884
  async (signal) => {
@@ -1023,10 +1023,137 @@ function useRelatedProducts(productId, options = {}) {
1023
1023
  });
1024
1024
  return { items: data ?? EMPTY4, loading: isLoading, error: error ?? null };
1025
1025
  }
1026
+ function useActivePromotions(page = "/", locale = "ar") {
1027
+ const key = `numu:promotions:${page}:${locale}`;
1028
+ const fetcher = react.useCallback(
1029
+ async (signal) => {
1030
+ const qs = new URLSearchParams({ page, locale });
1031
+ const res = await fetch(`/api/storefront/promotions?${qs.toString()}`, {
1032
+ credentials: "include",
1033
+ cache: "no-store",
1034
+ signal
1035
+ });
1036
+ if (!res.ok) return null;
1037
+ const json = await res.json();
1038
+ if (!json) return null;
1039
+ return (json.data ?? json) || null;
1040
+ },
1041
+ [page, locale]
1042
+ );
1043
+ const { data } = useCachedResource(
1044
+ key,
1045
+ fetcher,
1046
+ { initialData: null }
1047
+ );
1048
+ return data ?? null;
1049
+ }
1050
+
1051
+ // src/utils/money.ts
1052
+ function resolveLocale(locale) {
1053
+ return locale === "ar" ? "ar-EG" : "en-EG";
1054
+ }
1055
+ function formatMoney(cents, options = {}) {
1056
+ const { currency, locale, fractionDigits = 0 } = options;
1057
+ const safe = Number.isFinite(cents) ? cents : 0;
1058
+ return new Intl.NumberFormat(resolveLocale(locale), {
1059
+ style: "currency",
1060
+ currency: currency || "EGP",
1061
+ maximumFractionDigits: fractionDigits
1062
+ }).format(safe / 100);
1063
+ }
1064
+ function formatMoneyMajor(amount, options = {}) {
1065
+ return formatMoney(
1066
+ (Number.isFinite(amount) ? amount : 0) * 100,
1067
+ options
1068
+ );
1069
+ }
1070
+ function centsToMajor(cents) {
1071
+ return (Number.isFinite(cents) ? cents : 0) / 100;
1072
+ }
1073
+ function majorToCents(amount) {
1074
+ return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
1075
+ }
1076
+
1077
+ // src/lib/promotions.ts
1078
+ function multibuyOffers(promotions) {
1079
+ if (!promotions) return [];
1080
+ const list = Array.isArray(promotions) ? promotions : promotions.auto_discounts ?? [];
1081
+ const offers = [];
1082
+ for (const promo of list) {
1083
+ const rule = promo?.discount_rule;
1084
+ if (!rule || rule.kind !== "multibuy") continue;
1085
+ const quantity = rule.multibuy_quantity;
1086
+ const groupPriceCents = rule.multibuy_price_cents;
1087
+ if (typeof quantity !== "number" || quantity < 2) continue;
1088
+ if (typeof groupPriceCents !== "number" || groupPriceCents <= 0) continue;
1089
+ const eligibleProductIds = promo.eligible_product_ids ?? [];
1090
+ const eligibleCategoryIds = promo.eligible_category_ids ?? [];
1091
+ offers.push({
1092
+ promotionId: promo.promotion_id,
1093
+ quantity,
1094
+ groupPriceCents,
1095
+ groupPriceMajor: centsToMajor(groupPriceCents),
1096
+ headline: promo.translated_content?.headline,
1097
+ eligibleProductIds,
1098
+ eligibleCategoryIds,
1099
+ // No scoping rows at all ⇒ every product qualifies. Note an older
1100
+ // backend that doesn't send these fields also lands here, which is the
1101
+ // right default: counting everything is what themes did before.
1102
+ isStoreWide: eligibleProductIds.length === 0 && eligibleCategoryIds.length === 0,
1103
+ raw: promo
1104
+ });
1105
+ }
1106
+ return offers;
1107
+ }
1108
+ function offerIncludesProduct(offer, product) {
1109
+ if (!offer) return false;
1110
+ if (offer.isStoreWide) return true;
1111
+ const id = product?.product_id ?? product?.id;
1112
+ if (id && offer.eligibleProductIds.includes(id)) return true;
1113
+ const category = product?.category_id;
1114
+ return Boolean(category && offer.eligibleCategoryIds.includes(category));
1115
+ }
1116
+ function eligibleUnitsInCart(offer, cart) {
1117
+ if (!offer) return 0;
1118
+ return (cart?.items ?? []).reduce((sum, item) => {
1119
+ if (!item) return sum;
1120
+ if (!offerIncludesProduct(offer, item)) return sum;
1121
+ return sum + (item.quantity || 0);
1122
+ }, 0);
1123
+ }
1124
+ function offerProgress(offer, cart, eligibleUnits) {
1125
+ const empty = {
1126
+ unitsInCart: 0,
1127
+ unitsNeeded: 0,
1128
+ groupsUnlocked: 0,
1129
+ savingMajor: 0
1130
+ };
1131
+ if (!offer) return empty;
1132
+ const unitsInCart = typeof eligibleUnits === "number" ? Math.max(0, eligibleUnits) : eligibleUnitsInCart(offer, cart);
1133
+ const groupsUnlocked = Math.floor(unitsInCart / offer.quantity);
1134
+ const remainder = unitsInCart % offer.quantity;
1135
+ const unitsNeeded = remainder === 0 ? 0 : offer.quantity - remainder;
1136
+ const applied = (cart?.applied_promotions ?? []).find(
1137
+ (p) => p && p.id === offer.promotionId
1138
+ );
1139
+ return {
1140
+ unitsInCart,
1141
+ unitsNeeded,
1142
+ groupsUnlocked,
1143
+ savingMajor: applied ? applied.amount || 0 : 0
1144
+ };
1145
+ }
1146
+ function offerBeatsRegularPrice(offer, unitPriceMajor) {
1147
+ if (!offer || typeof unitPriceMajor !== "number" || unitPriceMajor <= 0) {
1148
+ return false;
1149
+ }
1150
+ const unitCents = Math.round(unitPriceMajor * 100);
1151
+ return unitCents * offer.quantity > offer.groupPriceCents;
1152
+ }
1026
1153
  function useProductSizeChart(productOverride) {
1027
1154
  const ctxProduct = useProductOptional();
1028
1155
  const product = productOverride ?? ctxProduct;
1029
- const shop = chunkV3JDQXD3_cjs.useShop();
1156
+ const shop = chunkWNMFED3F_cjs.useShop();
1030
1157
  const storeSettings = shop?.settings;
1031
1158
  return react.useMemo(
1032
1159
  () => resolveSizeChart(product?.attributes, storeSettings),
@@ -1496,11 +1623,27 @@ var EMPTY_CART = {
1496
1623
  };
1497
1624
  function normalizeCartFromServer(cart) {
1498
1625
  const toMajor = (n) => typeof n === "number" ? n / 100 : 0;
1626
+ const { automatic_discount_cents: wireAutomaticCents, ...rest } = cart;
1499
1627
  return {
1500
- ...cart,
1628
+ ...rest,
1501
1629
  subtotal: toMajor(cart.subtotal),
1502
1630
  total: toMajor(cart.total),
1503
1631
  ...cart.discount_amount != null ? { discount_amount: toMajor(cart.discount_amount) } : {},
1632
+ // Offers-v2 automatic promotions. These MUST be converted here too —
1633
+ // they arrive in cents next to already-major totals, so forwarding them
1634
+ // raw makes every theme render a saving 100x too large. This function is
1635
+ // the one and only cents→major boundary for the cart.
1636
+ //
1637
+ // The backend field is `automatic_discount_cents`; themes get
1638
+ // `automatic_discount` (major), because a `_cents` name holding pounds
1639
+ // invites exactly the double-divide this conversion exists to prevent.
1640
+ ...wireAutomaticCents != null ? { automatic_discount: toMajor(wireAutomaticCents) } : {},
1641
+ ...Array.isArray(cart.applied_promotions) ? {
1642
+ applied_promotions: cart.applied_promotions.map((p) => ({
1643
+ ...p,
1644
+ amount: toMajor(p.amount)
1645
+ }))
1646
+ } : {},
1504
1647
  items: Array.isArray(cart.items) ? cart.items.map((it) => {
1505
1648
  const raw = it;
1506
1649
  return {
@@ -2071,13 +2214,13 @@ function NuMuProvider({
2071
2214
  convertCurrency
2072
2215
  ]
2073
2216
  );
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 }) }) }) }) }) }) }) }) }) });
2217
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.ShopContext.Provider, { value: store, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.ThemeSettingsContext.Provider, { value: themeSettings, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.CurrentTemplateContext.Provider, { value: currentTemplate, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.PageContext.Provider, { value: pageValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.LocalizationContext.Provider, { value: localization, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.CurrencyContext.Provider, { value: currencyValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.CartContext.Provider, { value: cartValue, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.CustomerContext.Provider, { value: customerState, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerActionsContext.Provider, { value: customerActions, children: /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.NavigationContext.Provider, { value: navigation ?? {}, children }) }) }) }) }) }) }) }) }) });
2075
2218
  }
2076
2219
  function ProductProvider({ product, children }) {
2077
- return /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.ProductContext.Provider, { value: product, children });
2220
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.ProductContext.Provider, { value: product, children });
2078
2221
  }
2079
2222
  function CollectionProvider({ collection, children }) {
2080
- return /* @__PURE__ */ jsxRuntime.jsx(chunkV3JDQXD3_cjs.CollectionContext.Provider, { value: collection, children });
2223
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkWNMFED3F_cjs.CollectionContext.Provider, { value: collection, children });
2081
2224
  }
2082
2225
 
2083
2226
  // src/utils/logoStyle.ts
@@ -2416,8 +2559,8 @@ function Money({
2416
2559
  className,
2417
2560
  as = "span"
2418
2561
  }) {
2419
- const { formatMoney: formatMoney2 } = chunkV3JDQXD3_cjs.useLocalization();
2420
- const shop = chunkV3JDQXD3_cjs.useShop();
2562
+ const { formatMoney: formatMoney2 } = chunkWNMFED3F_cjs.useLocalization();
2563
+ const shop = chunkWNMFED3F_cjs.useShop();
2421
2564
  const { selected, base, autoConvert, convert } = useCurrency();
2422
2565
  const shouldConvert = autoConvert && !currency && !!selected && selected !== base;
2423
2566
  const ccy = shouldConvert ? selected : currency || shop?.currency;
@@ -2464,16 +2607,20 @@ function applyImageTransform(t, fit = "cover") {
2464
2607
  return style;
2465
2608
  }
2466
2609
  var clamp01 = (n) => Math.min(1, Math.max(0, n));
2610
+ function hostHonorsCropParams() {
2611
+ return globalThis.__NUMU_CF_IMAGE_RESIZING__ === true;
2612
+ }
2467
2613
  function focalSrc(url, options = {}) {
2468
2614
  if (!url) return "";
2469
2615
  if (url.startsWith("data:") || /[?&](fp-x|fp-y)=/.test(url)) return url;
2616
+ const crop = hostHonorsCropParams();
2470
2617
  const p = new URLSearchParams();
2471
2618
  p.set("url", url);
2472
2619
  if (options.width) p.set("w", String(Math.round(options.width)));
2473
- if (options.focal?.x != null) p.set("fp-x", String(clamp01(options.focal.x)));
2474
- if (options.focal?.y != null) p.set("fp-y", String(clamp01(options.focal.y)));
2475
- if (options.aspect) p.set("ar", options.aspect);
2476
- if (options.fit) p.set("fit", options.fit);
2620
+ if (crop && options.focal?.x != null) p.set("fp-x", String(clamp01(options.focal.x)));
2621
+ if (crop && options.focal?.y != null) p.set("fp-y", String(clamp01(options.focal.y)));
2622
+ if (crop && options.aspect) p.set("ar", options.aspect);
2623
+ if (crop && options.fit) p.set("fit", options.fit);
2477
2624
  if (options.quality) p.set("q", String(Math.min(100, Math.max(1, Math.round(options.quality)))));
2478
2625
  if (options.format) p.set("f", options.format.toLowerCase());
2479
2626
  return `/api/image-transform?${p.toString()}`;
@@ -2684,8 +2831,8 @@ function Logo({
2684
2831
  style,
2685
2832
  fallback
2686
2833
  }) {
2687
- const settings = chunkV3JDQXD3_cjs.useThemeSettings();
2688
- const shop = chunkV3JDQXD3_cjs.useShop();
2834
+ const settings = chunkWNMFED3F_cjs.useThemeSettings();
2835
+ const shop = chunkWNMFED3F_cjs.useShop();
2689
2836
  const g = settings?.global_settings ?? {};
2690
2837
  const url = str(src) || str(g.logo_url) || shop?.logo_url || "";
2691
2838
  const resolvedShape = shape || str(g.logo_shape) || "none";
@@ -2721,7 +2868,7 @@ function requestNavigate(href) {
2721
2868
  return !window.dispatchEvent(event);
2722
2869
  }
2723
2870
  function Link({ to, children, onClick, ...rest }) {
2724
- const shop = chunkV3JDQXD3_cjs.useShop();
2871
+ const shop = chunkWNMFED3F_cjs.useShop();
2725
2872
  const isAbsolute = ABSOLUTE_URL.test(to);
2726
2873
  let href = to;
2727
2874
  if (!isAbsolute && shop && !to.startsWith("/")) {
@@ -3300,8 +3447,8 @@ function LocaleSwitcher({
3300
3447
  onSelect,
3301
3448
  render
3302
3449
  }) {
3303
- const shop = chunkV3JDQXD3_cjs.useShop();
3304
- const { locale } = chunkV3JDQXD3_cjs.useLocalization();
3450
+ const shop = chunkWNMFED3F_cjs.useShop();
3451
+ const { locale } = chunkWNMFED3F_cjs.useLocalization();
3305
3452
  const available = shop.available_locales || [];
3306
3453
  const list = available.length > 0 ? available : shop.default_language === "en" ? ["en"] : Array.from(/* @__PURE__ */ new Set([shop.default_language, "en"]));
3307
3454
  const labelFor = react.useCallback((code) => {
@@ -3719,9 +3866,9 @@ function resolveSettingsMap(settings, ctx) {
3719
3866
  return out;
3720
3867
  }
3721
3868
  function useResolvedSettings(instance) {
3722
- const product = react.useContext(chunkV3JDQXD3_cjs.ProductContext);
3723
- const collection = react.useContext(chunkV3JDQXD3_cjs.CollectionContext);
3724
- const store = react.useContext(chunkV3JDQXD3_cjs.ShopContext);
3869
+ const product = react.useContext(chunkWNMFED3F_cjs.ProductContext);
3870
+ const collection = react.useContext(chunkWNMFED3F_cjs.CollectionContext);
3871
+ const store = react.useContext(chunkWNMFED3F_cjs.ShopContext);
3725
3872
  const ctx = react.useMemo(
3726
3873
  () => ({ product, collection, store }),
3727
3874
  [product, collection, store]
@@ -3851,32 +3998,6 @@ function assetUrl(name) {
3851
3998
  return `${cleanBase}${filename}`;
3852
3999
  }
3853
4000
 
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
4001
  // src/utils/templates.ts
3881
4002
  function resolveSections(group) {
3882
4003
  if (!group) return [];
@@ -3971,131 +4092,131 @@ Object.defineProperty(exports, "resolveThemeSettings", {
3971
4092
  });
3972
4093
  Object.defineProperty(exports, "KNOWN_SETTING_TYPES", {
3973
4094
  enumerable: true,
3974
- get: function () { return chunk7RZISLP2_cjs.KNOWN_SETTING_TYPES; }
4095
+ get: function () { return chunkJXJP6FND_cjs.KNOWN_SETTING_TYPES; }
3975
4096
  });
3976
4097
  Object.defineProperty(exports, "KNOWN_TEMPLATES", {
3977
4098
  enumerable: true,
3978
- get: function () { return chunk7RZISLP2_cjs.KNOWN_TEMPLATES; }
4099
+ get: function () { return chunkJXJP6FND_cjs.KNOWN_TEMPLATES; }
3979
4100
  });
3980
4101
  Object.defineProperty(exports, "REQUIRED_TEMPLATES", {
3981
4102
  enumerable: true,
3982
- get: function () { return chunk7RZISLP2_cjs.REQUIRED_TEMPLATES; }
4103
+ get: function () { return chunkJXJP6FND_cjs.REQUIRED_TEMPLATES; }
3983
4104
  });
3984
4105
  Object.defineProperty(exports, "SDK_VERSION", {
3985
4106
  enumerable: true,
3986
- get: function () { return chunk7RZISLP2_cjs.SDK_VERSION; }
4107
+ get: function () { return chunkJXJP6FND_cjs.SDK_VERSION; }
3987
4108
  });
3988
4109
  Object.defineProperty(exports, "THEME_CONTRACT_VERSION", {
3989
4110
  enumerable: true,
3990
- get: function () { return chunk7RZISLP2_cjs.THEME_CONTRACT_VERSION; }
4111
+ get: function () { return chunkJXJP6FND_cjs.THEME_CONTRACT_VERSION; }
3991
4112
  });
3992
4113
  Object.defineProperty(exports, "mergeResults", {
3993
4114
  enumerable: true,
3994
- get: function () { return chunk7RZISLP2_cjs.mergeResults; }
4115
+ get: function () { return chunkJXJP6FND_cjs.mergeResults; }
3995
4116
  });
3996
4117
  Object.defineProperty(exports, "validateBuiltManifest", {
3997
4118
  enumerable: true,
3998
- get: function () { return chunk7RZISLP2_cjs.validateBuiltManifest; }
4119
+ get: function () { return chunkJXJP6FND_cjs.validateBuiltManifest; }
3999
4120
  });
4000
4121
  Object.defineProperty(exports, "validateManifest", {
4001
4122
  enumerable: true,
4002
- get: function () { return chunk7RZISLP2_cjs.validateManifest; }
4123
+ get: function () { return chunkJXJP6FND_cjs.validateManifest; }
4003
4124
  });
4004
4125
  Object.defineProperty(exports, "validateSectionSchema", {
4005
4126
  enumerable: true,
4006
- get: function () { return chunk7RZISLP2_cjs.validateSectionSchema; }
4127
+ get: function () { return chunkJXJP6FND_cjs.validateSectionSchema; }
4007
4128
  });
4008
4129
  Object.defineProperty(exports, "validateSettingsAgainstSchema", {
4009
4130
  enumerable: true,
4010
- get: function () { return chunk7RZISLP2_cjs.validateSettingsAgainstSchema; }
4131
+ get: function () { return chunkJXJP6FND_cjs.validateSettingsAgainstSchema; }
4011
4132
  });
4012
4133
  Object.defineProperty(exports, "CartContext", {
4013
4134
  enumerable: true,
4014
- get: function () { return chunkV3JDQXD3_cjs.CartContext; }
4135
+ get: function () { return chunkWNMFED3F_cjs.CartContext; }
4015
4136
  });
4016
4137
  Object.defineProperty(exports, "CollectionContext", {
4017
4138
  enumerable: true,
4018
- get: function () { return chunkV3JDQXD3_cjs.CollectionContext; }
4139
+ get: function () { return chunkWNMFED3F_cjs.CollectionContext; }
4019
4140
  });
4020
4141
  Object.defineProperty(exports, "CurrencyContext", {
4021
4142
  enumerable: true,
4022
- get: function () { return chunkV3JDQXD3_cjs.CurrencyContext; }
4143
+ get: function () { return chunkWNMFED3F_cjs.CurrencyContext; }
4023
4144
  });
4024
4145
  Object.defineProperty(exports, "CustomerContext", {
4025
4146
  enumerable: true,
4026
- get: function () { return chunkV3JDQXD3_cjs.CustomerContext; }
4147
+ get: function () { return chunkWNMFED3F_cjs.CustomerContext; }
4027
4148
  });
4028
4149
  Object.defineProperty(exports, "LocalizationContext", {
4029
4150
  enumerable: true,
4030
- get: function () { return chunkV3JDQXD3_cjs.LocalizationContext; }
4151
+ get: function () { return chunkWNMFED3F_cjs.LocalizationContext; }
4031
4152
  });
4032
4153
  Object.defineProperty(exports, "NavigationContext", {
4033
4154
  enumerable: true,
4034
- get: function () { return chunkV3JDQXD3_cjs.NavigationContext; }
4155
+ get: function () { return chunkWNMFED3F_cjs.NavigationContext; }
4035
4156
  });
4036
4157
  Object.defineProperty(exports, "PageContext", {
4037
4158
  enumerable: true,
4038
- get: function () { return chunkV3JDQXD3_cjs.PageContext; }
4159
+ get: function () { return chunkWNMFED3F_cjs.PageContext; }
4039
4160
  });
4040
4161
  Object.defineProperty(exports, "ProductContext", {
4041
4162
  enumerable: true,
4042
- get: function () { return chunkV3JDQXD3_cjs.ProductContext; }
4163
+ get: function () { return chunkWNMFED3F_cjs.ProductContext; }
4043
4164
  });
4044
4165
  Object.defineProperty(exports, "ShopContext", {
4045
4166
  enumerable: true,
4046
- get: function () { return chunkV3JDQXD3_cjs.ShopContext; }
4167
+ get: function () { return chunkWNMFED3F_cjs.ShopContext; }
4047
4168
  });
4048
4169
  Object.defineProperty(exports, "ThemeSettingsContext", {
4049
4170
  enumerable: true,
4050
- get: function () { return chunkV3JDQXD3_cjs.ThemeSettingsContext; }
4171
+ get: function () { return chunkWNMFED3F_cjs.ThemeSettingsContext; }
4051
4172
  });
4052
4173
  Object.defineProperty(exports, "useCollections", {
4053
4174
  enumerable: true,
4054
- get: function () { return chunkV3JDQXD3_cjs.useCollections; }
4175
+ get: function () { return chunkWNMFED3F_cjs.useCollections; }
4055
4176
  });
4056
4177
  Object.defineProperty(exports, "useCustomer", {
4057
4178
  enumerable: true,
4058
- get: function () { return chunkV3JDQXD3_cjs.useCustomer; }
4179
+ get: function () { return chunkWNMFED3F_cjs.useCustomer; }
4059
4180
  });
4060
4181
  Object.defineProperty(exports, "useDirection", {
4061
4182
  enumerable: true,
4062
- get: function () { return chunkV3JDQXD3_cjs.useDirection; }
4183
+ get: function () { return chunkWNMFED3F_cjs.useDirection; }
4063
4184
  });
4064
4185
  Object.defineProperty(exports, "useFieldTranslation", {
4065
4186
  enumerable: true,
4066
- get: function () { return chunkV3JDQXD3_cjs.useFieldTranslation; }
4187
+ get: function () { return chunkWNMFED3F_cjs.useFieldTranslation; }
4067
4188
  });
4068
4189
  Object.defineProperty(exports, "useLocale", {
4069
4190
  enumerable: true,
4070
- get: function () { return chunkV3JDQXD3_cjs.useLocale; }
4191
+ get: function () { return chunkWNMFED3F_cjs.useLocale; }
4071
4192
  });
4072
4193
  Object.defineProperty(exports, "useLocalization", {
4073
4194
  enumerable: true,
4074
- get: function () { return chunkV3JDQXD3_cjs.useLocalization; }
4195
+ get: function () { return chunkWNMFED3F_cjs.useLocalization; }
4075
4196
  });
4076
4197
  Object.defineProperty(exports, "useNumberFormat", {
4077
4198
  enumerable: true,
4078
- get: function () { return chunkV3JDQXD3_cjs.useNumberFormat; }
4199
+ get: function () { return chunkWNMFED3F_cjs.useNumberFormat; }
4079
4200
  });
4080
4201
  Object.defineProperty(exports, "usePage", {
4081
4202
  enumerable: true,
4082
- get: function () { return chunkV3JDQXD3_cjs.usePage; }
4203
+ get: function () { return chunkWNMFED3F_cjs.usePage; }
4083
4204
  });
4084
4205
  Object.defineProperty(exports, "useProducts", {
4085
4206
  enumerable: true,
4086
- get: function () { return chunkV3JDQXD3_cjs.useProducts; }
4207
+ get: function () { return chunkWNMFED3F_cjs.useProducts; }
4087
4208
  });
4088
4209
  Object.defineProperty(exports, "useShop", {
4089
4210
  enumerable: true,
4090
- get: function () { return chunkV3JDQXD3_cjs.useShop; }
4211
+ get: function () { return chunkWNMFED3F_cjs.useShop; }
4091
4212
  });
4092
4213
  Object.defineProperty(exports, "useThemeSettings", {
4093
4214
  enumerable: true,
4094
- get: function () { return chunkV3JDQXD3_cjs.useThemeSettings; }
4215
+ get: function () { return chunkWNMFED3F_cjs.useThemeSettings; }
4095
4216
  });
4096
4217
  Object.defineProperty(exports, "useTranslation", {
4097
4218
  enumerable: true,
4098
- get: function () { return chunkV3JDQXD3_cjs.useTranslation; }
4219
+ get: function () { return chunkWNMFED3F_cjs.useTranslation; }
4099
4220
  });
4100
4221
  exports.AddToCartButton = AddToCartButton;
4101
4222
  exports.Block = Block;
@@ -4142,6 +4263,7 @@ exports.defineBlock = defineBlock;
4142
4263
  exports.defineSection = defineSection;
4143
4264
  exports.defineThemeEntry = defineThemeEntry;
4144
4265
  exports.dynamicSource = dynamicSource;
4266
+ exports.eligibleUnitsInCart = eligibleUnitsInCart;
4145
4267
  exports.findVariantByOptions = findVariantByOptions;
4146
4268
  exports.flattenMessages = flattenMessages;
4147
4269
  exports.focalSrc = focalSrc;
@@ -4157,6 +4279,10 @@ exports.logoImgStyle = logoImgStyle;
4157
4279
  exports.logoStyleTokens = logoStyleTokens;
4158
4280
  exports.majorToCents = majorToCents;
4159
4281
  exports.mountTheme = mountTheme;
4282
+ exports.multibuyOffers = multibuyOffers;
4283
+ exports.offerBeatsRegularPrice = offerBeatsRegularPrice;
4284
+ exports.offerIncludesProduct = offerIncludesProduct;
4285
+ exports.offerProgress = offerProgress;
4160
4286
  exports.pickTranslations = pickTranslations;
4161
4287
  exports.productHref = productHref;
4162
4288
  exports.publishVariantSelection = publishVariantSelection;
@@ -4173,6 +4299,7 @@ exports.resolveSourcePath = resolveSourcePath;
4173
4299
  exports.sanitizeHtml = sanitizeHtml;
4174
4300
  exports.selectChromeSections = selectChromeSections;
4175
4301
  exports.selectTemplateSections = selectTemplateSections;
4302
+ exports.useActivePromotions = useActivePromotions;
4176
4303
  exports.useAnalytics = useAnalytics;
4177
4304
  exports.useApp = useApp;
4178
4305
  exports.useArticle = useArticle;