@numueg/theme-sdk 0.6.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/dist/chunk-3C37JX2X.cjs +207 -0
  3. package/dist/chunk-3C37JX2X.cjs.map +1 -0
  4. package/dist/chunk-JIAA7TGR.mjs +397 -0
  5. package/dist/chunk-JIAA7TGR.mjs.map +1 -0
  6. package/dist/chunk-PUR3FRGQ.mjs +183 -0
  7. package/dist/chunk-PUR3FRGQ.mjs.map +1 -0
  8. package/dist/chunk-TBSNHHFH.cjs +96 -0
  9. package/dist/chunk-TBSNHHFH.cjs.map +1 -0
  10. package/dist/chunk-X3J4GDI7.cjs +408 -0
  11. package/dist/chunk-X3J4GDI7.cjs.map +1 -0
  12. package/dist/chunk-XF2FGIVS.mjs +94 -0
  13. package/dist/chunk-XF2FGIVS.mjs.map +1 -0
  14. package/dist/{entities-6MGANln7.d.mts → entities-B8378GKp.d.mts} +12 -0
  15. package/dist/{entities-6MGANln7.d.ts → entities-B8378GKp.d.ts} +12 -0
  16. package/dist/index.cjs +640 -964
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.mts +215 -82
  19. package/dist/index.d.ts +215 -82
  20. package/dist/index.mjs +482 -907
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{mount-BGumg1JM.d.mts → mount-CM4kGg9w.d.ts} +87 -14
  23. package/dist/{mount-Bt-4ken5.d.ts → mount-NLjFNyBv.d.mts} +87 -14
  24. package/dist/normalize.cjs +7 -91
  25. package/dist/normalize.cjs.map +1 -1
  26. package/dist/normalize.d.mts +1 -1
  27. package/dist/normalize.d.ts +1 -1
  28. package/dist/normalize.mjs +1 -92
  29. package/dist/normalize.mjs.map +1 -1
  30. package/dist/{theme-D8MOopvi.d.mts → theme-CNTB4KnU.d.mts} +29 -3
  31. package/dist/{theme-D8MOopvi.d.ts → theme-CNTB4KnU.d.ts} +29 -3
  32. package/dist/types.d.mts +2 -2
  33. package/dist/types.d.ts +2 -2
  34. package/dist/v2-compat.cjs +6 -163
  35. package/dist/v2-compat.cjs.map +1 -1
  36. package/dist/v2-compat.d.mts +1 -1
  37. package/dist/v2-compat.d.ts +1 -1
  38. package/dist/v2-compat.mjs +2 -159
  39. package/dist/v2-compat.mjs.map +1 -1
  40. package/dist/validation.cjs +43 -403
  41. package/dist/validation.cjs.map +1 -1
  42. package/dist/validation.d.mts +1 -1
  43. package/dist/validation.d.ts +1 -1
  44. package/dist/validation.mjs +1 -395
  45. package/dist/validation.mjs.map +1 -1
  46. package/dist/verify.cjs +6 -21
  47. package/dist/verify.cjs.map +1 -1
  48. package/dist/verify.d.mts +3 -3
  49. package/dist/verify.d.ts +3 -3
  50. package/dist/verify.mjs +3 -20
  51. package/dist/verify.mjs.map +1 -1
  52. package/package.json +1 -1
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { i as Store, e as Product, b as Collection, C as Cart, c as Customer, P as Page, S as SizeChart, g as ProductVariant } from './entities-6MGANln7.mjs';
2
- export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, j as ProductOption, h as SizeChartMode } from './entities-6MGANln7.mjs';
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-D8MOopvi.mjs';
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-D8MOopvi.mjs';
1
+ import { i as Store, e as Product, b as Collection, c as Customer, P as Page, S as SizeChart, g as ProductVariant, C as Cart } from './entities-B8378GKp.mjs';
2
+ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, j as ProductOption, h as SizeChartMode } from './entities-B8378GKp.mjs';
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
+ 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 { L as LocalizationState, T as ThemeMountContext, a as ThemeRenderArgs, M as MenuItemData } from './mount-BGumg1JM.mjs';
7
- export { C as CartContext, b as CollectionContext, c as CustomerContext, d as LocalizationContext, N as NavigationContext, P as PageContext, e as ProductContext, S as ShopContext, f as ThemeMountPage, g as ThemeSettingsContext, h as buildThemeElement, m as mountTheme } from './mount-BGumg1JM.mjs';
6
+ import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-NLjFNyBv.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-NLjFNyBv.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';
@@ -43,17 +43,7 @@ declare function useProductOptional(): Product | null;
43
43
  declare function useCollection(): Collection;
44
44
  declare function useCollectionOptional(): Collection | null;
45
45
 
46
- declare function useCart(): {
47
- cart: Cart;
48
- addItem: (productId: string, variantId?: string, quantity?: number) => Promise<void>;
49
- removeItem: (itemId: string) => Promise<void>;
50
- updateQuantity: (itemId: string, quantity: number) => Promise<void>;
51
- applyDiscount: (code: string) => Promise<void>;
52
- removeDiscount: () => Promise<void>;
53
- updateNote: (note: string) => Promise<void>;
54
- clearCart: () => Promise<void>;
55
- loading: boolean;
56
- };
46
+ declare function useCart(): CartContextValue;
57
47
 
58
48
  declare function useCustomer(): Customer | null;
59
49
 
@@ -136,14 +126,67 @@ declare function useSection(): SectionInstance;
136
126
  declare function useSectionOptional(): SectionInstance | null;
137
127
 
138
128
  /**
139
- * useMoney convenience hook that returns a stable formatter bound to
140
- * the active store's currency.
129
+ * A section instance from a section group, carrying its own `id` (the key it
130
+ * had inside the group's `sections` map). The raw `SectionInstance` drops the
131
+ * id — but a theme needs it to key its React list, wire `<Section id={…}>`
132
+ * click-to-select, and look the type up in its own registry — so
133
+ * `useSectionGroup` re-attaches it here.
134
+ */
135
+ interface SectionGroupInstance extends SectionInstance {
136
+ /** The instance's key within the group's `sections` map. */
137
+ id: string;
138
+ }
139
+ /**
140
+ * useSectionGroup — read the ordered section instances for a named section
141
+ * group ("header", "footer", or any custom global group) out of
142
+ * `themeSettings.section_groups[group]`.
143
+ *
144
+ * This is the SDK side of "global sections shared across pages": the host
145
+ * ships one `section_groups` map on every page, and a theme renders a given
146
+ * group in its layout (header/footer) or wherever it wants a page-independent
147
+ * section list. The returned array is in the group's `order`, and each entry
148
+ * carries its `id` so the theme can key + select + dispatch on it:
149
+ *
150
+ * ```tsx
151
+ * const headerSections = useSectionGroup("header");
152
+ * return headerSections.map(({ id, type, settings }) => {
153
+ * const Comp = registry[type]?.render; // theme owns the registry
154
+ * return Comp ? (
155
+ * <Section key={id} id={id} type={type} groupId="header">
156
+ * <Comp settings={settings} />
157
+ * </Section>
158
+ * ) : null;
159
+ * });
160
+ * ```
161
+ *
162
+ * Pure data hook — it does NOT map types to components. The SDK has no section
163
+ * registry (a theme builds its own via `collectSections`), so there is no
164
+ * generic `<GlobalSections>` renderer to ship: the theme walks the returned
165
+ * instances through its own registry, exactly as it already does for template
166
+ * sections. Returns `[]` (a stable reference) when there is no provider, no
167
+ * `section_groups`, the named group is absent, or its `order` is empty.
168
+ *
169
+ * Disabled instances are INCLUDED (a `disabled` flag is preserved on each
170
+ * entry); filter with `.filter((s) => !s.disabled)` if the theme hides them.
171
+ * Ids listed in `order` but missing from `sections` are skipped defensively.
172
+ */
173
+ declare function useSectionGroup(group: string): SectionGroupInstance[];
174
+
175
+ /**
176
+ * useMoney — convenience hook that returns a formatter bound to the active
177
+ * store's currency.
141
178
  *
142
179
  * Useful when a section needs to format multiple amounts in a render:
143
180
  *
144
181
  * const money = useMoney();
145
182
  * return <td>{money(item.price * item.quantity)}</td>
146
183
  *
184
+ * Multi-currency: like <Money>, when the store has `auto_convert` on and no
185
+ * `currencyOverride` is passed, amounts are converted to and formatted in the
186
+ * visitor's SELECTED presentment currency (shared via `useCurrency()`), so a
187
+ * `<CurrencySwitcher>` change reflows these too. Pass a `currencyOverride` to
188
+ * pin a specific currency and skip conversion.
189
+ *
147
190
  * For one-off price displays prefer the <Money> component which handles
148
191
  * compare-at and inline rendering.
149
192
  */
@@ -579,10 +622,19 @@ declare function useAnalytics(): AnalyticsApi;
579
622
  * `{ available: false }` rather than as a network error — themes
580
623
  * branch on availability without try/catch.
581
624
  *
582
- * The hook revalidates whenever the slug changes, but does NOT refetch
583
- * on focus / interval app data is usually slow-changing (config +
584
- * manifest). Themes that need live data should layer their own
585
- * refresh on top of the returned `refresh()` callback.
625
+ * Phase 3 (client-data layer): the fetch now runs through the shared
626
+ * `useCachedResource` store keyed by `numu:app:<store_id>:<slug>`. Two upgrades
627
+ * over the old per-instance `useState` + fetch:
628
+ * - DEDUP + SYNC: N consumers of the same app slug share ONE request and one
629
+ * result, instead of each firing its own `/apps/{slug}` fetch.
630
+ * - CANCELLATION / ORDERING: the fetcher receives an AbortSignal and the
631
+ * cache seq-guards results, so a slow response for a superseded slug (or a
632
+ * superseded `refresh()`) can no longer apply stale state over a newer one
633
+ * — the audited "out-of-order responses apply stale state" bug.
634
+ *
635
+ * `loading` reflects the FIRST load only; a `refresh()` revalidates in the
636
+ * background (`isValidating`) while keeping the last-good data visible, rather
637
+ * than blanking to a skeleton on every manual refresh.
586
638
  */
587
639
  interface AppManifestBlock {
588
640
  type: string;
@@ -628,8 +680,17 @@ declare function useApp<T = unknown>(slug: string): AppState<T>;
628
680
  * v1 implementation: localStorage only. The /api/customer/me/wishlist
629
681
  * endpoint isn't wired yet; an authed visitor still gets the local
630
682
  * fallback so themes work end-to-end. When the endpoint lands, the
631
- * mutation methods will short-circuit to the server fetch and the
632
- * effect below will drop the localStorage path for authed users.
683
+ * fetcher below swaps its localStorage read for the server fetch and the
684
+ * mutation methods gain a `{ revalidate: true }` write-through.
685
+ *
686
+ * Phase 3 (client-data layer): the items list now lives in the shared
687
+ * `useCachedResource` store keyed by `numu_wishlist_<store_id>`, NOT in
688
+ * per-instance `useState`. Previously two `<Heart>`s for the same product
689
+ * each held their own copy, so adding via one never re-rendered the other —
690
+ * the two hearts DESYNCED. Now every `useWishlist(storeId)` consumer reads
691
+ * and writes ONE shared entry, so an add/remove anywhere reflows every heart.
692
+ * Writes are optimistic (the shared store updates instantly) and roll back if
693
+ * persistence throws.
633
694
  */
634
695
  interface WishlistItem {
635
696
  product_id: string;
@@ -661,11 +722,17 @@ declare function useWishlist(storeId: string): WishlistState;
661
722
  * together"; v1 ships the simpler same-category-minus-self heuristic.
662
723
  *
663
724
  * Backend contract: GET /api/storefront/products/{id}/related?limit=N
664
- * → { items: Product[] } | Product[]
725
+ * → { items: Product[] } | { data: Product[] } | Product[]
665
726
  *
666
727
  * Returns an empty list (no error) when the endpoint is missing or the
667
728
  * product has no siblings — themes should branch on `items.length` and
668
729
  * either render the section or skip it entirely.
730
+ *
731
+ * Phase 3 (client-data layer): routed through the shared `useCachedResource`
732
+ * store keyed by `numu:related:<productId>:<limit>`. Two "related products"
733
+ * sections on the same PDP now share ONE request + one result instead of each
734
+ * refetching, and the AbortSignal + cache seq-guard drop a superseded response
735
+ * (e.g. after a rapid product switch) so it can't overwrite the newer list.
669
736
  */
670
737
  interface RelatedProductsState {
671
738
  items: Product[];
@@ -676,6 +743,103 @@ declare function useRelatedProducts(productId: string | null | undefined, option
676
743
  limit?: number;
677
744
  }): RelatedProductsState;
678
745
 
746
+ /**
747
+ * Shared client-data layer (Phase 3) — a tiny SWR-style cache with ZERO
748
+ * runtime dependencies.
749
+ *
750
+ * Why this exists (from the audit): several hooks fetched + held per-instance
751
+ * state, so N components rendering the same resource fired N identical requests
752
+ * AND drifted out of sync (e.g. two wishlist hearts for one product; `useApp`
753
+ * applying an out-of-order response over a newer one). `useCachedResource`
754
+ * centralizes fetching behind one module-level store keyed by a string:
755
+ *
756
+ * (a) DEDUP — the first consumer of a key starts the fetch and stashes the
757
+ * in-flight promise on the entry; every other consumer that revalidates
758
+ * the same key while it's pending joins that promise instead of firing a
759
+ * second request. N consumers → ONE network call.
760
+ * (b) CACHE — results live on the entry keyed by string, so a later mount
761
+ * reads the cached value immediately (and skips refetch within
762
+ * `dedupeIntervalMs`).
763
+ * (c) SUBSCRIBE / NOTIFY — every hook instance subscribes to its key via
764
+ * `useSyncExternalStore`. Any update (fetch resolve, `mutate`) rebuilds
765
+ * the entry's immutable snapshot and notifies ALL subscribers, so every
766
+ * instance re-renders with the same value — cross-instance state sync.
767
+ * (d) REVALIDATE + CANCELLATION — each fetch reserves a monotonic sequence
768
+ * number and an `AbortController`. Starting a new (forced) fetch aborts
769
+ * the previous one; when any fetch settles, its result is applied ONLY if
770
+ * its sequence is still the latest (`mySeq === entry.seq`). So a slow,
771
+ * superseded response can never overwrite a newer result regardless of
772
+ * the order the network resolves them in.
773
+ *
774
+ * SSR-safe: the fetch is triggered from `useEffect` (never runs under
775
+ * `renderToString`), and `useSyncExternalStore`'s server snapshot returns the
776
+ * `initialData` fallback without ever touching the module cache — so nothing is
777
+ * written to the shared map on the server and there is no cross-request bleed.
778
+ *
779
+ * This is intentionally NOT a full SWR clone (no focus/reconnect revalidation,
780
+ * no suspense, no infinite pagination). It's the smallest primitive that fixes
781
+ * the audited dedup + sync + ordering bugs and that themes can reuse.
782
+ */
783
+ /** The reactive slice a consumer reads. */
784
+ interface CachedResourceState<T> {
785
+ /** Latest cached value, or `initialData` before the first fetch settles. */
786
+ data: T | undefined;
787
+ /** Error from the most recent settled fetch (cleared on success / mutate). */
788
+ error: Error | undefined;
789
+ /** True until the first fetch (or `mutate`) settles a value for this key. */
790
+ isLoading: boolean;
791
+ /** True whenever a fetch is currently in flight (initial OR background). */
792
+ isValidating: boolean;
793
+ }
794
+ /** Fetcher receives an AbortSignal so a superseding fetch can cancel it. */
795
+ type CacheFetcher<T> = (signal: AbortSignal) => Promise<T>;
796
+ /** `mutate` accepts a value or an updater `(current) => next`. */
797
+ type CacheMutator<T> = T | undefined | ((current: T | undefined) => T | undefined);
798
+ interface MutateOptions {
799
+ /** After applying the local value, kick a fresh (forced) revalidation. */
800
+ revalidate?: boolean;
801
+ }
802
+ interface CachedResource<T> extends CachedResourceState<T> {
803
+ /**
804
+ * Optimistically write the cached value for this key (all subscribers
805
+ * re-render). Pass `{ revalidate: true }` to also refetch afterwards.
806
+ */
807
+ mutate: (next: CacheMutator<T>, opts?: MutateOptions) => void;
808
+ /** Force a fresh fetch, superseding + aborting any in-flight request. */
809
+ revalidate: () => Promise<void>;
810
+ }
811
+ interface UseCachedResourceOptions<T> {
812
+ /**
813
+ * Value shown on the server and on the first client paint before the fetch
814
+ * resolves. Used as the SSR snapshot (SSR-safe: no fetch on the server).
815
+ */
816
+ initialData?: T;
817
+ /** When `false`, the hook holds no subscription and never fetches. */
818
+ enabled?: boolean;
819
+ /** Auto-fetch on mount / key change (default `true`). */
820
+ revalidateOnMount?: boolean;
821
+ /**
822
+ * Skip the mount fetch if a value settled within this window (ms). Prevents
823
+ * a staggered second mount from refetching a still-fresh key. Default 2000.
824
+ */
825
+ dedupeIntervalMs?: number;
826
+ /** Called on a non-abort fetch error, with the key. */
827
+ onError?: (error: Error, key: string) => void;
828
+ }
829
+ /**
830
+ * Subscribe to a cached resource keyed by `key`, deduping the fetch across all
831
+ * instances and keeping them in sync. See the module docblock for the full
832
+ * contract.
833
+ *
834
+ * @param key Cache key. `null`/`undefined`/`""` (or `enabled: false`)
835
+ * disables the hook — no subscription, no fetch, `initialData`
836
+ * is returned.
837
+ * @param fetcher `(signal) => Promise<T>`. Recreated-per-render is fine; the
838
+ * latest is captured in a ref so it doesn't retrigger fetches.
839
+ * @param options See {@link UseCachedResourceOptions}.
840
+ */
841
+ declare function useCachedResource<T>(key: string | null | undefined, fetcher: CacheFetcher<T>, options?: UseCachedResourceOptions<T>): CachedResource<T>;
842
+
679
843
  /**
680
844
  * useProductSizeChart — resolve the size chart to show for a product.
681
845
  *
@@ -703,53 +867,6 @@ declare function useProductSizeChart(productOverride?: Product | null): SizeChar
703
867
  */
704
868
  declare function resolveSizeChart(productAttributes: Record<string, unknown> | undefined, storeSettings: Record<string, unknown> | undefined): SizeChart | null;
705
869
 
706
- /**
707
- * Multi-currency presentment — Phase 6.
708
- *
709
- * The store's *capture* currency (what Paymob/Stripe/etc. charges)
710
- * never changes mid-session. This hook is purely about **display**:
711
- * letting visitors browse prices in a currency they recognize.
712
- *
713
- * Usage:
714
- *
715
- * const { base, selected, presentment, convert, setSelected } = useCurrency();
716
- * <p>{convert(product.price.amount_cents)} {selected}</p>
717
- * {presentment.length > 1 && (
718
- * <select value={selected} onChange={(e) => setSelected(e.target.value)}>
719
- * {presentment.map(c => <option key={c}>{c}</option>)}
720
- * </select>
721
- * )}
722
- *
723
- * Behavior:
724
- * - `selected` defaults to the persisted `numu_currency` cookie if
725
- * valid, else `default_presentment`, else `base`.
726
- * - `setSelected` writes the cookie (path=/, 30d) so navigation
727
- * preserves the choice across pages.
728
- * - `convert(cents)` returns the converted cents in `selected`,
729
- * using the rates from the API. When no rate exists, returns
730
- * the input unchanged (theme renders in base — better than a
731
- * wrong number).
732
- *
733
- * Use `<CurrencySwitcher>` from the SDK for an opinionated UI, or
734
- * read this hook directly for full control.
735
- */
736
- interface CurrencyConfig {
737
- base: string;
738
- default_presentment: string;
739
- presentment: string[];
740
- rates: Record<string, string>;
741
- auto_convert: boolean;
742
- }
743
- interface CurrencyState {
744
- base: string;
745
- selected: string;
746
- presentment: string[];
747
- rates: Record<string, number>;
748
- autoConvert: boolean;
749
- loading: boolean;
750
- setSelected: (currency: string) => void;
751
- convert: (cents: number, target?: string) => number;
752
- }
753
870
  declare function useCurrency(): CurrencyState;
754
871
 
755
872
  /**
@@ -1092,6 +1209,15 @@ interface NuMuProviderProps {
1092
1209
  * built before the prop existed keep rendering their home template.
1093
1210
  */
1094
1211
  currentTemplate?: string;
1212
+ /**
1213
+ * I3 — resolved alternate template key for the current page (e.g.
1214
+ * `"product.wholesale"`) when the storefront routed it to a template
1215
+ * suffix. Published on the synthesized `PageContext` value so themes read
1216
+ * it via `usePage()?.template`. Distinct from `currentTemplate`, which is
1217
+ * the base route type (`"product"`). Omit for pages on their default
1218
+ * template; additive, so themes/hosts predating it are unaffected.
1219
+ */
1220
+ pageTemplate?: string;
1095
1221
  /**
1096
1222
  * Pre-fetched product list for the current page. Themes that call
1097
1223
  * `useProducts()` will read these from PageContext without needing
@@ -1117,7 +1243,7 @@ interface NuMuProviderProps {
1117
1243
  navigation?: Record<string, MenuItemData[]>;
1118
1244
  children: ReactNode;
1119
1245
  }
1120
- declare function NuMuProvider({ store, themeSettings, initialCart, customer, locale: initialLocale, translations: initialTranslations, currentTemplate, initialProducts, initialCollections, navigation, children, }: NuMuProviderProps): react.JSX.Element;
1246
+ declare function NuMuProvider({ store, themeSettings, initialCart, customer, locale: initialLocale, translations: initialTranslations, currentTemplate, pageTemplate, initialProducts, initialCollections, navigation, children, }: NuMuProviderProps): react.JSX.Element;
1121
1247
 
1122
1248
  interface ProductProviderProps {
1123
1249
  product: Product;
@@ -1154,6 +1280,13 @@ interface MoneyProps {
1154
1280
  * Intl.NumberFormat. Renders inline with `dir="auto"` so the digits
1155
1281
  * flow naturally in RTL (Arabic) without flipping the currency symbol.
1156
1282
  *
1283
+ * Multi-currency: when the store has `auto_convert` on and the caller did NOT
1284
+ * pin an explicit `currency`, the amount is presented in the visitor's
1285
+ * SELECTED currency (converted via the shared rates from `useCurrency()`). A
1286
+ * `<CurrencySwitcher>` change therefore re-renders every `<Money>` on the page
1287
+ * without a reload. An explicit `currency` prop means "this amount is already
1288
+ * in that currency" — it is respected and never converted.
1289
+ *
1157
1290
  * Usage:
1158
1291
  * <Money amount={product.price} compareAt={product.compare_at_price} />
1159
1292
  */
@@ -1670,13 +1803,13 @@ interface RichTextProps {
1670
1803
  * Sanitize an HTML string against the allowlist above. Runs server-
1671
1804
  * AND client-side because rich-text fields are server-rendered for SEO.
1672
1805
  *
1673
- * Implementation note: We use DOMParser when available (browser) for
1674
- * structural correctness; on server we fall through a regex-based
1675
- * pass that handles the common formatting tags + escapes everything
1676
- * else. The server pass is intentionally conservative themes that
1677
- * need server-rendered rich content with edge-case structure should
1678
- * sanitize on the API tier and pass the sanitized HTML through
1679
- * `bypassSanitize` (escape hatch below).
1806
+ * Implementation note: in the browser we use DOMParser (structural allowlist
1807
+ * see `sanitizeHtmlClient`/`walk`). On the server (no DOM) we fall through
1808
+ * to `sanitizeHtmlServer`, a hardened regex denylist that strips dangerous
1809
+ * element blocks to a fixpoint, removes inline event handlers (incl.
1810
+ * slash-separated ones), and drops non-allowlisted `href`/`src` URLs. Themes
1811
+ * needing server-rendered rich content with edge-case structure should
1812
+ * sanitize on the API tier and pass already-safe HTML through.
1680
1813
  */
1681
1814
  declare function sanitizeHtml(input: string): string;
1682
1815
  declare function RichText({ html, className, as }: RichTextProps): react.JSX.Element | null;
@@ -2267,4 +2400,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2267
2400
  */
2268
2401
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2269
2402
 
2270
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, Cart, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, type CurrencyConfig, type 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 GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, 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, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2403
+ export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, 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 GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, type MutateOptions, 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, 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, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };