@numueg/theme-sdk 0.12.0 → 0.13.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.
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { i as Store, e as Product, b as Collection, c as Customer, P as Page, M as Metafield, j as ArticleDetail, k as ArticleSummary, B as BlogSummary, S as SizeChart, g as ProductVariant, C as Cart } from './entities-1XRRUTlO.mjs';
2
- export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, l as ProductOption, h as SizeChartMode } from './entities-1XRRUTlO.mjs';
1
+ import { i as Store, e as Product, b as Collection, c as Customer, P as Page, M as Metafield, j as ArticleDetail, k as ArticleSummary, B as BlogSummary, C as Cart, S as SizeChart, g as ProductVariant } from './entities-DRKn04q0.mjs';
2
+ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, l as ProductOption, h as SizeChartMode } from './entities-DRKn04q0.mjs';
3
3
  import { T as ThemeSettingsV3, c as SectionInstance, M as MountResult, B as BlockInstance, b as BlockSchema, a as BlockProps$1, f as SectionSchema, e as SectionProps$1 } from './theme-CNTB4KnU.mjs';
4
4
  export { E as ExternalThemeMetadata, h as MAX_BLOCK_DEPTH, P as PageTemplate, i as PresetBlock, S as SectionGroup, d as SectionPreset, g as SettingDefinition } from './theme-CNTB4KnU.mjs';
5
5
  export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, ValidationIssue, ValidationResult, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './validation.mjs';
6
- import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-DDFMacmj.mjs';
7
- export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-DDFMacmj.mjs';
6
+ import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-BTaDtz8k.mjs';
7
+ export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-BTaDtz8k.mjs';
8
8
  import * as react from 'react';
9
9
  import { ReactElement, ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
10
10
  export { resolveThemeSettings } from './normalize.mjs';
@@ -926,6 +926,216 @@ interface UseCachedResourceOptions<T> {
926
926
  */
927
927
  declare function useCachedResource<T>(key: string | null | undefined, fetcher: CacheFetcher<T>, options?: UseCachedResourceOptions<T>): CachedResource<T>;
928
928
 
929
+ /**
930
+ * Storefront promotion types — the shape the platform serves to every theme.
931
+ *
932
+ * Source of truth is the backend's offers-v2 engine. The host proxies it at
933
+ * `GET /api/storefront/promotions`, which returns the visitor's active
934
+ * promotions grouped by surface. Themes only ever *render* these; the engine
935
+ * decides what a cart costs, and the same engine runs at checkout. A theme
936
+ * that computes its own discount is telling the shopper a number the server
937
+ * will overrule.
938
+ *
939
+ * ⚠️ Every `*_cents` field here is in INTEGER CENTS, straight off the API —
940
+ * unlike `Cart`, which the SDK normalizes to major units. Convert with the
941
+ * helpers in `lib/promotions` rather than dividing ad hoc; a missed
942
+ * conversion is a silent 100x error.
943
+ */
944
+ /** One step of a tiered "spend X, get Y%" rule. */
945
+ interface DiscountTier {
946
+ threshold_cents: number;
947
+ percent: number;
948
+ }
949
+ /**
950
+ * The math behind a promotion. `kind` is an open string union on purpose:
951
+ * the platform adds kinds over time and older themes must degrade quietly
952
+ * rather than crash on one they've never heard of.
953
+ */
954
+ interface DiscountRule {
955
+ kind: "percentage" | "fixed" | "free_shipping" | "bogo" | "tiered" | "multibuy" | (string & {});
956
+ value_cents?: number | null;
957
+ value_percent?: number | null;
958
+ min_subtotal_cents?: number | null;
959
+ max_discount_cents?: number | null;
960
+ buy_quantity?: number | null;
961
+ get_quantity?: number | null;
962
+ get_discount_percent?: number | null;
963
+ tiers?: DiscountTier[];
964
+ /** MULTIBUY: how many eligible items form one group (N). */
965
+ multibuy_quantity?: number | null;
966
+ /** MULTIBUY: the fixed total price for one complete group (P), in cents. */
967
+ multibuy_price_cents?: number | null;
968
+ }
969
+ /** A promotion resolved as active for the current visitor. */
970
+ interface ActivePromotion {
971
+ promotion_id: string;
972
+ surface?: string;
973
+ priority?: number;
974
+ content?: Record<string, unknown>;
975
+ translated_content?: {
976
+ headline?: Record<string, string>;
977
+ body?: Record<string, string>;
978
+ [key: string]: unknown;
979
+ };
980
+ discount_rule?: DiscountRule | null;
981
+ coupon_code?: string | null;
982
+ /**
983
+ * Which catalog entries can take part in the rule. BOTH EMPTY (or absent,
984
+ * on an older backend) means the whole store qualifies.
985
+ *
986
+ * Needed so a scoped offer doesn't lie: without it a "3 for EGP 650 on
987
+ * scarves" offer counts every cart unit, and a shopper holding one
988
+ * ineligible item is told "add 2 more" and then doesn't get the discount.
989
+ */
990
+ eligible_product_ids?: string[];
991
+ eligible_category_ids?: string[];
992
+ }
993
+ /** The `/api/storefront/promotions` payload, grouped by surface. */
994
+ interface ActivePromotionsPayload {
995
+ announcement_bars?: ActivePromotion[];
996
+ popups?: ActivePromotion[];
997
+ floating_widgets?: ActivePromotion[];
998
+ cookie_banner?: ActivePromotion | null;
999
+ /** Automatic (no code needed) discounts — where multibuy offers live. */
1000
+ auto_discounts?: ActivePromotion[];
1001
+ discount_codes_visible?: ActivePromotion[];
1002
+ }
1003
+ /**
1004
+ * A validated multibuy offer, normalized for display.
1005
+ *
1006
+ * `groupPriceMajor` is in MAJOR units (EGP, not piastres) so it can go
1007
+ * straight into `<Money>` / `formatMoney` — the conversion happened once,
1008
+ * here, rather than in each theme.
1009
+ */
1010
+ interface MultibuyOffer {
1011
+ promotionId: string;
1012
+ /** N — how many items make a group. */
1013
+ quantity: number;
1014
+ /** P in cents, as the engine stores it. */
1015
+ groupPriceCents: number;
1016
+ /** P in major units, ready to render. */
1017
+ groupPriceMajor: number;
1018
+ /** Merchant's bilingual headline, when they set one. */
1019
+ headline?: Record<string, string>;
1020
+ /** Products the offer is scoped to. Empty (with categories) = whole store. */
1021
+ eligibleProductIds: string[];
1022
+ /** Categories the offer is scoped to. Empty (with products) = whole store. */
1023
+ eligibleCategoryIds: string[];
1024
+ /** True when the offer applies to the entire catalogue. */
1025
+ isStoreWide: boolean;
1026
+ raw: ActivePromotion;
1027
+ }
1028
+ /** Where a shopper stands against one multibuy offer. */
1029
+ interface OfferProgress {
1030
+ /** Eligible units currently in the cart. */
1031
+ unitsInCart: number;
1032
+ /** How many more units to complete the next group. 0 = just unlocked. */
1033
+ unitsNeeded: number;
1034
+ /** Complete groups the cart has already earned. */
1035
+ groupsUnlocked: number;
1036
+ /**
1037
+ * What the engine says this offer actually saved, in MAJOR units. Read
1038
+ * from `cart.applied_promotions` — never recomputed, so the number the
1039
+ * shopper reads is the number they are charged.
1040
+ */
1041
+ savingMajor: number;
1042
+ }
1043
+
1044
+ /**
1045
+ * The store's active promotions for the current visitor.
1046
+ *
1047
+ * Reads the host proxy `GET /api/storefront/promotions` (never NUMU-api
1048
+ * directly — the host owns credentials, caching and the store resolution).
1049
+ * The backend groups by surface; the host already renders announcement bars,
1050
+ * popups, the cookie banner and floating widgets in the shell, so what a
1051
+ * theme normally wants is `auto_discounts` — the offers that price the cart
1052
+ * with no code to type.
1053
+ *
1054
+ * SSR-safe: `useCachedResource` doesn't fetch during server render, so this
1055
+ * resolves on hydrate. Sections must therefore tolerate a null first pass —
1056
+ * render nothing (or a skeleton) rather than assuming data on first paint.
1057
+ *
1058
+ * Returns `null` on any miss (offline, 404, flag off). That is deliberate:
1059
+ * a promotions outage should make an offer banner disappear, never break the
1060
+ * page it sits on.
1061
+ *
1062
+ * @param page page path used for page-targeted promotions (default "/")
1063
+ * @param locale "ar" | "en" — selects the translated content
1064
+ */
1065
+ declare function useActivePromotions(page?: string, locale?: string): ActivePromotionsPayload | null;
1066
+
1067
+ /**
1068
+ * Pure helpers for rendering platform promotions in a theme.
1069
+ *
1070
+ * These are headless by design — data and logic only, zero markup and zero
1071
+ * styling — so every theme keeps its own look while sharing one correct
1072
+ * implementation of the fiddly parts (unit counting, group math, the
1073
+ * cents→major boundary).
1074
+ *
1075
+ * The hard rule these encode: **a theme never computes a discount.** The
1076
+ * engine prices the cart and reports what it saved; helpers here only
1077
+ * describe progress toward an offer and format what the engine already
1078
+ * decided. Anything else eventually renders a number the server won't honour.
1079
+ */
1080
+
1081
+ /**
1082
+ * Extract the usable multibuy offers ("any N for a fixed total P").
1083
+ *
1084
+ * Accepts either the whole `/api/storefront/promotions` payload or just an
1085
+ * array of promotions, so callers don't have to reach into `auto_discounts`
1086
+ * themselves. Anything that isn't a well-formed multibuy — a kind this SDK
1087
+ * version doesn't know, a missing N or P — is skipped rather than thrown on:
1088
+ * a theme built before a rule kind existed must keep rendering when a
1089
+ * merchant starts using it.
1090
+ */
1091
+ declare function multibuyOffers(promotions: ActivePromotionsPayload | ActivePromotion[] | null | undefined): MultibuyOffer[];
1092
+ /**
1093
+ * Does this product take part in the offer?
1094
+ *
1095
+ * A store-wide offer includes everything. Otherwise the product must be named
1096
+ * directly or sit in a named category. A scoped offer over a product whose
1097
+ * `category_id` we don't know returns false — better to under-promise than to
1098
+ * advertise a discount the server won't apply.
1099
+ */
1100
+ declare function offerIncludesProduct(offer: MultibuyOffer | null | undefined, product: {
1101
+ id?: string;
1102
+ product_id?: string;
1103
+ category_id?: string | null;
1104
+ }): boolean;
1105
+ /**
1106
+ * How many cart UNITS actually qualify for the offer.
1107
+ *
1108
+ * Units, not lines — three of one product is a valid trio, the way the engine
1109
+ * scores it. For a scoped offer this is what the nudge must count; counting
1110
+ * the whole cart is how a shopper gets told "add 2 more" and then doesn't get
1111
+ * the discount.
1112
+ */
1113
+ declare function eligibleUnitsInCart(offer: MultibuyOffer | null | undefined, cart: Cart | null | undefined): number;
1114
+ /**
1115
+ * Where the cart stands against one multibuy offer.
1116
+ *
1117
+ * Counts UNITS, not lines — three of the same product is a valid trio, the
1118
+ * same way the engine scores it. `savingMajor` is read from the cart's
1119
+ * engine-supplied `applied_promotions`, so an unlocked offer shows the real
1120
+ * saving and a theme can never drift from the charged amount.
1121
+ *
1122
+ * Scoping is handled automatically: when the offer names products or
1123
+ * categories, only qualifying units are counted (via `eligibleUnitsInCart`).
1124
+ * Pass `eligibleUnits` explicitly to override that — e.g. when the theme has
1125
+ * a better source of truth than the cart lines.
1126
+ */
1127
+ declare function offerProgress(offer: MultibuyOffer | null | undefined, cart: Cart | null | undefined, eligibleUnits?: number): OfferProgress;
1128
+ /**
1129
+ * Should a product page advertise this offer?
1130
+ *
1131
+ * Only when N of this product actually costs more than the group price —
1132
+ * otherwise the "deal" is worse than just buying them, and §5 of the offer
1133
+ * spec forbids advertising it (the engine likewise refuses to apply it).
1134
+ *
1135
+ * @param unitPriceMajor the product's price in MAJOR units, as a theme has it
1136
+ */
1137
+ declare function offerBeatsRegularPrice(offer: MultibuyOffer | null | undefined, unitPriceMajor: number | null | undefined): boolean;
1138
+
929
1139
  /**
930
1140
  * useProductSizeChart — resolve the size chart to show for a product.
931
1141
  *
@@ -2733,4 +2943,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2733
2943
  */
2734
2944
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2735
2945
 
2736
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, ArticleDetail, ArticleSummary, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, BlogSummary, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type FormatMoneyOptions, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type ListingHeading, type ListingHeadingOptions, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, type MaybeOrderedTemplate, MenuItemData, type MetafieldOwner, Money, MountResult, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, type ResolvedSection, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, centsToMajor, clearSdkSingleton, collectBlocks, collectSections, collectionHref, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, useAnalytics, useApp, useArticle, useArticles, useBlog, useBlogs, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useListingHeading, useLocale, useLocalization, useMetafield, useMetafields, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2946
+ export { type ActivePromotion, type ActivePromotionsPayload, AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, ArticleDetail, ArticleSummary, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, BlogSummary, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DiscountRule, type DiscountTier, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type FormatMoneyOptions, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type ListingHeading, type ListingHeadingOptions, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, type MaybeOrderedTemplate, MenuItemData, type MetafieldOwner, Money, MountResult, type MultibuyOffer, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OfferProgress, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, type ResolvedSection, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, centsToMajor, clearSdkSingleton, collectBlocks, collectSections, collectionHref, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, eligibleUnitsInCart, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, multibuyOffers, offerBeatsRegularPrice, offerIncludesProduct, offerProgress, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, useActivePromotions, useAnalytics, useApp, useArticle, useArticles, useBlog, useBlogs, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useListingHeading, useLocale, useLocalization, useMetafield, useMetafields, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { i as Store, e as Product, b as Collection, c as Customer, P as Page, M as Metafield, j as ArticleDetail, k as ArticleSummary, B as BlogSummary, S as SizeChart, g as ProductVariant, C as Cart } from './entities-1XRRUTlO.js';
2
- export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, l as ProductOption, h as SizeChartMode } from './entities-1XRRUTlO.js';
1
+ import { i as Store, e as Product, b as Collection, c as Customer, P as Page, M as Metafield, j as ArticleDetail, k as ArticleSummary, B as BlogSummary, C as Cart, S as SizeChart, g as ProductVariant } from './entities-DRKn04q0.js';
2
+ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, l as ProductOption, h as SizeChartMode } from './entities-DRKn04q0.js';
3
3
  import { T as ThemeSettingsV3, c as SectionInstance, M as MountResult, B as BlockInstance, b as BlockSchema, a as BlockProps$1, f as SectionSchema, e as SectionProps$1 } from './theme-CNTB4KnU.js';
4
4
  export { E as ExternalThemeMetadata, h as MAX_BLOCK_DEPTH, P as PageTemplate, i as PresetBlock, S as SectionGroup, d as SectionPreset, g as SettingDefinition } from './theme-CNTB4KnU.js';
5
5
  export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, ValidationIssue, ValidationResult, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './validation.js';
6
- import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-qofjiBxw.js';
7
- export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-qofjiBxw.js';
6
+ import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-CLQniVfc.js';
7
+ export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-CLQniVfc.js';
8
8
  import * as react from 'react';
9
9
  import { ReactElement, ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
10
10
  export { resolveThemeSettings } from './normalize.js';
@@ -926,6 +926,216 @@ interface UseCachedResourceOptions<T> {
926
926
  */
927
927
  declare function useCachedResource<T>(key: string | null | undefined, fetcher: CacheFetcher<T>, options?: UseCachedResourceOptions<T>): CachedResource<T>;
928
928
 
929
+ /**
930
+ * Storefront promotion types — the shape the platform serves to every theme.
931
+ *
932
+ * Source of truth is the backend's offers-v2 engine. The host proxies it at
933
+ * `GET /api/storefront/promotions`, which returns the visitor's active
934
+ * promotions grouped by surface. Themes only ever *render* these; the engine
935
+ * decides what a cart costs, and the same engine runs at checkout. A theme
936
+ * that computes its own discount is telling the shopper a number the server
937
+ * will overrule.
938
+ *
939
+ * ⚠️ Every `*_cents` field here is in INTEGER CENTS, straight off the API —
940
+ * unlike `Cart`, which the SDK normalizes to major units. Convert with the
941
+ * helpers in `lib/promotions` rather than dividing ad hoc; a missed
942
+ * conversion is a silent 100x error.
943
+ */
944
+ /** One step of a tiered "spend X, get Y%" rule. */
945
+ interface DiscountTier {
946
+ threshold_cents: number;
947
+ percent: number;
948
+ }
949
+ /**
950
+ * The math behind a promotion. `kind` is an open string union on purpose:
951
+ * the platform adds kinds over time and older themes must degrade quietly
952
+ * rather than crash on one they've never heard of.
953
+ */
954
+ interface DiscountRule {
955
+ kind: "percentage" | "fixed" | "free_shipping" | "bogo" | "tiered" | "multibuy" | (string & {});
956
+ value_cents?: number | null;
957
+ value_percent?: number | null;
958
+ min_subtotal_cents?: number | null;
959
+ max_discount_cents?: number | null;
960
+ buy_quantity?: number | null;
961
+ get_quantity?: number | null;
962
+ get_discount_percent?: number | null;
963
+ tiers?: DiscountTier[];
964
+ /** MULTIBUY: how many eligible items form one group (N). */
965
+ multibuy_quantity?: number | null;
966
+ /** MULTIBUY: the fixed total price for one complete group (P), in cents. */
967
+ multibuy_price_cents?: number | null;
968
+ }
969
+ /** A promotion resolved as active for the current visitor. */
970
+ interface ActivePromotion {
971
+ promotion_id: string;
972
+ surface?: string;
973
+ priority?: number;
974
+ content?: Record<string, unknown>;
975
+ translated_content?: {
976
+ headline?: Record<string, string>;
977
+ body?: Record<string, string>;
978
+ [key: string]: unknown;
979
+ };
980
+ discount_rule?: DiscountRule | null;
981
+ coupon_code?: string | null;
982
+ /**
983
+ * Which catalog entries can take part in the rule. BOTH EMPTY (or absent,
984
+ * on an older backend) means the whole store qualifies.
985
+ *
986
+ * Needed so a scoped offer doesn't lie: without it a "3 for EGP 650 on
987
+ * scarves" offer counts every cart unit, and a shopper holding one
988
+ * ineligible item is told "add 2 more" and then doesn't get the discount.
989
+ */
990
+ eligible_product_ids?: string[];
991
+ eligible_category_ids?: string[];
992
+ }
993
+ /** The `/api/storefront/promotions` payload, grouped by surface. */
994
+ interface ActivePromotionsPayload {
995
+ announcement_bars?: ActivePromotion[];
996
+ popups?: ActivePromotion[];
997
+ floating_widgets?: ActivePromotion[];
998
+ cookie_banner?: ActivePromotion | null;
999
+ /** Automatic (no code needed) discounts — where multibuy offers live. */
1000
+ auto_discounts?: ActivePromotion[];
1001
+ discount_codes_visible?: ActivePromotion[];
1002
+ }
1003
+ /**
1004
+ * A validated multibuy offer, normalized for display.
1005
+ *
1006
+ * `groupPriceMajor` is in MAJOR units (EGP, not piastres) so it can go
1007
+ * straight into `<Money>` / `formatMoney` — the conversion happened once,
1008
+ * here, rather than in each theme.
1009
+ */
1010
+ interface MultibuyOffer {
1011
+ promotionId: string;
1012
+ /** N — how many items make a group. */
1013
+ quantity: number;
1014
+ /** P in cents, as the engine stores it. */
1015
+ groupPriceCents: number;
1016
+ /** P in major units, ready to render. */
1017
+ groupPriceMajor: number;
1018
+ /** Merchant's bilingual headline, when they set one. */
1019
+ headline?: Record<string, string>;
1020
+ /** Products the offer is scoped to. Empty (with categories) = whole store. */
1021
+ eligibleProductIds: string[];
1022
+ /** Categories the offer is scoped to. Empty (with products) = whole store. */
1023
+ eligibleCategoryIds: string[];
1024
+ /** True when the offer applies to the entire catalogue. */
1025
+ isStoreWide: boolean;
1026
+ raw: ActivePromotion;
1027
+ }
1028
+ /** Where a shopper stands against one multibuy offer. */
1029
+ interface OfferProgress {
1030
+ /** Eligible units currently in the cart. */
1031
+ unitsInCart: number;
1032
+ /** How many more units to complete the next group. 0 = just unlocked. */
1033
+ unitsNeeded: number;
1034
+ /** Complete groups the cart has already earned. */
1035
+ groupsUnlocked: number;
1036
+ /**
1037
+ * What the engine says this offer actually saved, in MAJOR units. Read
1038
+ * from `cart.applied_promotions` — never recomputed, so the number the
1039
+ * shopper reads is the number they are charged.
1040
+ */
1041
+ savingMajor: number;
1042
+ }
1043
+
1044
+ /**
1045
+ * The store's active promotions for the current visitor.
1046
+ *
1047
+ * Reads the host proxy `GET /api/storefront/promotions` (never NUMU-api
1048
+ * directly — the host owns credentials, caching and the store resolution).
1049
+ * The backend groups by surface; the host already renders announcement bars,
1050
+ * popups, the cookie banner and floating widgets in the shell, so what a
1051
+ * theme normally wants is `auto_discounts` — the offers that price the cart
1052
+ * with no code to type.
1053
+ *
1054
+ * SSR-safe: `useCachedResource` doesn't fetch during server render, so this
1055
+ * resolves on hydrate. Sections must therefore tolerate a null first pass —
1056
+ * render nothing (or a skeleton) rather than assuming data on first paint.
1057
+ *
1058
+ * Returns `null` on any miss (offline, 404, flag off). That is deliberate:
1059
+ * a promotions outage should make an offer banner disappear, never break the
1060
+ * page it sits on.
1061
+ *
1062
+ * @param page page path used for page-targeted promotions (default "/")
1063
+ * @param locale "ar" | "en" — selects the translated content
1064
+ */
1065
+ declare function useActivePromotions(page?: string, locale?: string): ActivePromotionsPayload | null;
1066
+
1067
+ /**
1068
+ * Pure helpers for rendering platform promotions in a theme.
1069
+ *
1070
+ * These are headless by design — data and logic only, zero markup and zero
1071
+ * styling — so every theme keeps its own look while sharing one correct
1072
+ * implementation of the fiddly parts (unit counting, group math, the
1073
+ * cents→major boundary).
1074
+ *
1075
+ * The hard rule these encode: **a theme never computes a discount.** The
1076
+ * engine prices the cart and reports what it saved; helpers here only
1077
+ * describe progress toward an offer and format what the engine already
1078
+ * decided. Anything else eventually renders a number the server won't honour.
1079
+ */
1080
+
1081
+ /**
1082
+ * Extract the usable multibuy offers ("any N for a fixed total P").
1083
+ *
1084
+ * Accepts either the whole `/api/storefront/promotions` payload or just an
1085
+ * array of promotions, so callers don't have to reach into `auto_discounts`
1086
+ * themselves. Anything that isn't a well-formed multibuy — a kind this SDK
1087
+ * version doesn't know, a missing N or P — is skipped rather than thrown on:
1088
+ * a theme built before a rule kind existed must keep rendering when a
1089
+ * merchant starts using it.
1090
+ */
1091
+ declare function multibuyOffers(promotions: ActivePromotionsPayload | ActivePromotion[] | null | undefined): MultibuyOffer[];
1092
+ /**
1093
+ * Does this product take part in the offer?
1094
+ *
1095
+ * A store-wide offer includes everything. Otherwise the product must be named
1096
+ * directly or sit in a named category. A scoped offer over a product whose
1097
+ * `category_id` we don't know returns false — better to under-promise than to
1098
+ * advertise a discount the server won't apply.
1099
+ */
1100
+ declare function offerIncludesProduct(offer: MultibuyOffer | null | undefined, product: {
1101
+ id?: string;
1102
+ product_id?: string;
1103
+ category_id?: string | null;
1104
+ }): boolean;
1105
+ /**
1106
+ * How many cart UNITS actually qualify for the offer.
1107
+ *
1108
+ * Units, not lines — three of one product is a valid trio, the way the engine
1109
+ * scores it. For a scoped offer this is what the nudge must count; counting
1110
+ * the whole cart is how a shopper gets told "add 2 more" and then doesn't get
1111
+ * the discount.
1112
+ */
1113
+ declare function eligibleUnitsInCart(offer: MultibuyOffer | null | undefined, cart: Cart | null | undefined): number;
1114
+ /**
1115
+ * Where the cart stands against one multibuy offer.
1116
+ *
1117
+ * Counts UNITS, not lines — three of the same product is a valid trio, the
1118
+ * same way the engine scores it. `savingMajor` is read from the cart's
1119
+ * engine-supplied `applied_promotions`, so an unlocked offer shows the real
1120
+ * saving and a theme can never drift from the charged amount.
1121
+ *
1122
+ * Scoping is handled automatically: when the offer names products or
1123
+ * categories, only qualifying units are counted (via `eligibleUnitsInCart`).
1124
+ * Pass `eligibleUnits` explicitly to override that — e.g. when the theme has
1125
+ * a better source of truth than the cart lines.
1126
+ */
1127
+ declare function offerProgress(offer: MultibuyOffer | null | undefined, cart: Cart | null | undefined, eligibleUnits?: number): OfferProgress;
1128
+ /**
1129
+ * Should a product page advertise this offer?
1130
+ *
1131
+ * Only when N of this product actually costs more than the group price —
1132
+ * otherwise the "deal" is worse than just buying them, and §5 of the offer
1133
+ * spec forbids advertising it (the engine likewise refuses to apply it).
1134
+ *
1135
+ * @param unitPriceMajor the product's price in MAJOR units, as a theme has it
1136
+ */
1137
+ declare function offerBeatsRegularPrice(offer: MultibuyOffer | null | undefined, unitPriceMajor: number | null | undefined): boolean;
1138
+
929
1139
  /**
930
1140
  * useProductSizeChart — resolve the size chart to show for a product.
931
1141
  *
@@ -2733,4 +2943,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2733
2943
  */
2734
2944
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2735
2945
 
2736
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, ArticleDetail, ArticleSummary, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, BlogSummary, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type FormatMoneyOptions, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type ListingHeading, type ListingHeadingOptions, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, type MaybeOrderedTemplate, MenuItemData, type MetafieldOwner, Money, MountResult, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, type ResolvedSection, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, centsToMajor, clearSdkSingleton, collectBlocks, collectSections, collectionHref, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, useAnalytics, useApp, useArticle, useArticles, useBlog, useBlogs, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useListingHeading, useLocale, useLocalization, useMetafield, useMetafields, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2946
+ export { type ActivePromotion, type ActivePromotionsPayload, AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, ArticleDetail, ArticleSummary, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, BlogSummary, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DiscountRule, type DiscountTier, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type FormatMoneyOptions, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type ListingHeading, type ListingHeadingOptions, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, type MaybeOrderedTemplate, MenuItemData, type MetafieldOwner, Money, MountResult, type MultibuyOffer, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OfferProgress, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, type ResolvedSection, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, centsToMajor, clearSdkSingleton, collectBlocks, collectSections, collectionHref, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, eligibleUnitsInCart, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, multibuyOffers, offerBeatsRegularPrice, offerIncludesProduct, offerProgress, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, useActivePromotions, useAnalytics, useApp, useArticle, useArticles, useBlog, useBlogs, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useListingHeading, useLocale, useLocalization, useMetafield, useMetafields, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };