@shoppexio/storefront 1.0.90 → 1.0.91

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.js CHANGED
@@ -1,28 +1,15 @@
1
1
  import {
2
- _enum,
3
- array,
4
- boolean,
5
- coerce_exports,
6
2
  customerLoyaltyRedeemSchema,
7
3
  customerLoyaltySchema,
4
+ customerPortalAffiliateCreateLinkSchema,
8
5
  customerPortalDashboardSchema,
9
6
  customerPortalInvoiceDetailSchema,
7
+ customerPortalNotificationPreferencesSchema,
10
8
  customerPortalPaginatedInvoicesSchema,
11
9
  customerWarrantyClaimSchema,
12
10
  customerWarrantyListSchema,
13
- discriminatedUnion,
14
- email,
15
- external_exports,
16
- literal,
17
- number,
18
- object,
19
- preprocess,
20
- record,
21
- string,
22
- union,
23
- unknown,
24
- url
25
- } from "./chunk-ROQI7BO6.js";
11
+ storefrontStorageKey
12
+ } from "./chunk-UW6DFRB5.js";
26
13
 
27
14
  // ../sdk/src/core/errors.ts
28
15
  var CURRENCY_UNAVAILABLE_ERROR_CODE = "errors.storefront.currency_unavailable";
@@ -280,40 +267,40 @@ function createClient(clientOptions) {
280
267
  return { error, response };
281
268
  }
282
269
  return {
283
- request(method, url2, init2) {
284
- return coreFetch(url2, { ...init2, method: method.toUpperCase() });
270
+ request(method, url, init2) {
271
+ return coreFetch(url, { ...init2, method: method.toUpperCase() });
285
272
  },
286
273
  /** Call a GET endpoint */
287
- GET(url2, init2) {
288
- return coreFetch(url2, { ...init2, method: "GET" });
274
+ GET(url, init2) {
275
+ return coreFetch(url, { ...init2, method: "GET" });
289
276
  },
290
277
  /** Call a PUT endpoint */
291
- PUT(url2, init2) {
292
- return coreFetch(url2, { ...init2, method: "PUT" });
278
+ PUT(url, init2) {
279
+ return coreFetch(url, { ...init2, method: "PUT" });
293
280
  },
294
281
  /** Call a POST endpoint */
295
- POST(url2, init2) {
296
- return coreFetch(url2, { ...init2, method: "POST" });
282
+ POST(url, init2) {
283
+ return coreFetch(url, { ...init2, method: "POST" });
297
284
  },
298
285
  /** Call a DELETE endpoint */
299
- DELETE(url2, init2) {
300
- return coreFetch(url2, { ...init2, method: "DELETE" });
286
+ DELETE(url, init2) {
287
+ return coreFetch(url, { ...init2, method: "DELETE" });
301
288
  },
302
289
  /** Call a OPTIONS endpoint */
303
- OPTIONS(url2, init2) {
304
- return coreFetch(url2, { ...init2, method: "OPTIONS" });
290
+ OPTIONS(url, init2) {
291
+ return coreFetch(url, { ...init2, method: "OPTIONS" });
305
292
  },
306
293
  /** Call a HEAD endpoint */
307
- HEAD(url2, init2) {
308
- return coreFetch(url2, { ...init2, method: "HEAD" });
294
+ HEAD(url, init2) {
295
+ return coreFetch(url, { ...init2, method: "HEAD" });
309
296
  },
310
297
  /** Call a PATCH endpoint */
311
- PATCH(url2, init2) {
312
- return coreFetch(url2, { ...init2, method: "PATCH" });
298
+ PATCH(url, init2) {
299
+ return coreFetch(url, { ...init2, method: "PATCH" });
313
300
  },
314
301
  /** Call a TRACE endpoint */
315
- TRACE(url2, init2) {
316
- return coreFetch(url2, { ...init2, method: "TRACE" });
302
+ TRACE(url, init2) {
303
+ return coreFetch(url, { ...init2, method: "TRACE" });
317
304
  },
318
305
  /** Register middleware */
319
306
  use(...middleware) {
@@ -545,2144 +532,14 @@ function mergeHeaders(...allHeaders) {
545
532
  }
546
533
  return finalHeaders;
547
534
  }
548
- function removeTrailingSlash(url2) {
549
- if (url2.endsWith("/")) {
550
- return url2.substring(0, url2.length - 1);
535
+ function removeTrailingSlash(url) {
536
+ if (url.endsWith("/")) {
537
+ return url.substring(0, url.length - 1);
551
538
  }
552
- return url2;
539
+ return url;
553
540
  }
554
541
 
555
- // ../contracts/src/navigation.ts
556
- var NAVIGATION_MENU_SLOT_CONFIG = {
557
- header: {
558
- title: "Header",
559
- aliases: ["Header Menu"]
560
- },
561
- footer: {
562
- title: "Footer",
563
- aliases: ["Footer Links", "Legal Links"]
564
- }
565
- };
566
- function getNavigationMenuTitles(slot) {
567
- const config = NAVIGATION_MENU_SLOT_CONFIG[slot];
568
- return [config.title, ...config.aliases];
569
- }
570
- var NAVIGATION_MENU_SLOTS = Object.keys(NAVIGATION_MENU_SLOT_CONFIG);
571
-
572
- // ../contracts/src/email-marketing.ts
573
- var EmailMarketingConsentBasisSchema = _enum(["CONSENT", "SOFT_OPT_IN", "TRANSACTIONAL_ONLY", "UNKNOWN"]);
574
- var EmailMarketingSuppressionReasonSchema = _enum(["MANUAL_UNSUB", "ONE_CLICK_UNSUB", "HARD_BOUNCE", "COMPLAINT", "ADMIN_BLOCK"]);
575
- var EmailMarketingCampaignTypeSchema = _enum(["BROADCAST", "AUTOMATION"]);
576
- var EmailMarketingAutomationTriggerSchema = _enum([
577
- "ABANDONED_CART",
578
- "POST_PURCHASE",
579
- "WELCOME",
580
- "WIN_BACK",
581
- "RE_ENGAGEMENT",
582
- "TAG_ADDED"
583
- ]);
584
- var EmailMarketingContactCreateSchema = object({
585
- email: email(),
586
- name: string().trim().min(1).nullable().optional(),
587
- customer_id: string().uuid().nullable().optional(),
588
- tags: array(string().trim().min(1)).default([]),
589
- consent_basis: EmailMarketingConsentBasisSchema.default("UNKNOWN"),
590
- source: string().trim().min(1).default("dashboard")
591
- });
592
- var EmailMarketingTemplateCreateSchema = object({
593
- name: string().trim().min(1),
594
- subject: string().trim().min(1),
595
- preheader: string().nullable().optional(),
596
- mjml_source: string().trim().min(1),
597
- builder_project_json: record(string(), unknown()).nullable().optional(),
598
- thumbnail_url: url().nullable().optional()
599
- });
600
- var EmailMarketingTemplatePreviewSchema = object({
601
- mjml_source: string().trim().min(1),
602
- sample_variables: record(string(), unknown()).optional()
603
- });
604
- var EmailMarketingTemplateTestSendSchema = object({
605
- recipient_email: email(),
606
- sample_variables: record(string(), unknown()).optional()
607
- });
608
- var EmailMarketingSettingsUpdateSchema = object({
609
- marketing_enabled: boolean().optional(),
610
- default_from_name: string().trim().min(1).nullable().optional(),
611
- default_from_email: email().nullable().optional(),
612
- default_reply_to_email: email().nullable().optional(),
613
- physical_address: string().trim().min(1).nullable().optional(),
614
- physical_address_line2: string().trim().min(1).nullable().optional(),
615
- physical_city: string().trim().min(1).nullable().optional(),
616
- physical_postal_code: string().trim().min(1).nullable().optional(),
617
- physical_country: string().trim().length(2).nullable().optional(),
618
- daily_limit: number().int().positive().optional(),
619
- monthly_soft_cap: number().int().positive().optional()
620
- });
621
- var EmailMarketingCampaignCreateSchema = object({
622
- type: EmailMarketingCampaignTypeSchema.default("BROADCAST"),
623
- name: string().trim().min(1),
624
- subject: string().trim().min(1),
625
- template_id: string().uuid(),
626
- list_id: string().uuid().nullable().optional(),
627
- segment_id: string().uuid().nullable().optional(),
628
- from_name: string().trim().min(1),
629
- from_email: email(),
630
- reply_to_email: email().nullable().optional(),
631
- schedule_at: string().datetime().nullable().optional(),
632
- target_emails: array(email()).max(0, "Direct recipient entry is disabled. Use lists or segments built from Shoppex customers and paid order buyers.").default([]),
633
- idempotency_key: string().trim().min(1).nullable().optional()
634
- });
635
- var EmailMarketingAutomationCreateSchema = object({
636
- name: string().trim().min(1),
637
- subject: string().trim().min(1),
638
- template_id: string().uuid(),
639
- from_name: string().trim().min(1),
640
- from_email: email(),
641
- reply_to_email: email().nullable().optional(),
642
- trigger: EmailMarketingAutomationTriggerSchema,
643
- delay_seconds: number().int().min(0).default(0),
644
- conditions: record(string(), unknown()).default({})
645
- });
646
- var EmailMarketingAutomationTriggerEventSchema = object({
647
- trigger: EmailMarketingAutomationTriggerSchema,
648
- email: email(),
649
- customer_id: string().uuid().nullable().optional(),
650
- name: string().trim().min(1).nullable().optional(),
651
- consent_basis: EmailMarketingConsentBasisSchema.default("UNKNOWN"),
652
- variables: record(string(), unknown()).default({}),
653
- idempotency_key: string().trim().min(1).nullable().optional()
654
- });
655
- var EmailMarketingSuppressionCreateSchema = object({
656
- email: email(),
657
- reason: EmailMarketingSuppressionReasonSchema.default("ADMIN_BLOCK"),
658
- notes: string().nullable().optional()
659
- });
660
-
661
- // ../contracts/src/payment-gateways.ts
662
- var EXTERNAL_PAYMENT_ADAPTER_GATEWAY_PREFIX = "EXTERNAL:";
663
- var EXTERNAL_ADAPTER_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
664
- function parseExternalPaymentAdapterGatewayKey(gateway) {
665
- const trimmed = gateway.trim();
666
- if (!/^external:/i.test(trimmed)) {
667
- return null;
668
- }
669
- const adapterId = trimmed.slice(trimmed.indexOf(":") + 1).trim().toLowerCase();
670
- return EXTERNAL_ADAPTER_ID_PATTERN.test(adapterId) ? adapterId : null;
671
- }
672
- var COIN_GATEWAY_KEYS = [
673
- "BITCOIN",
674
- "LITECOIN",
675
- "ETHEREUM",
676
- "BITCOIN_CASH",
677
- "MONERO",
678
- "RIPPLE",
679
- "TRON",
680
- "SOLANA",
681
- "POLYGON",
682
- "BINANCE_COIN",
683
- "CRONOS",
684
- "CONCORDIUM",
685
- "NANO",
686
- "DOGECOIN",
687
- "DASH"
688
- ];
689
- var CRYPTO_PROVIDER_KEYS = [
690
- "NOWPAYMENTS",
691
- "CRYPTOMUS",
692
- "OXAPAY"
693
- ];
694
- var MANAGED_CRYPTO_PROVIDER_KEYS = [
695
- "NOWPAYMENTS_WHITELABEL"
696
- ];
697
- var GENERIC_STABLECOIN_GATEWAY_FAMILIES = {
698
- USDT: ["USDT_TRC20", "USDT_ERC20", "USDT_BEP20", "USDT_POLYGON", "USDT_SOL"],
699
- USDC: ["USDC_ERC20", "USDC_TRC20", "USDC_BEP20", "USDC_POLYGON", "USDC_SOL"],
700
- DAI: ["DAI_ERC20"]
701
- };
702
- var GENERIC_STABLECOIN_GATEWAY_KEYS = Object.keys(
703
- GENERIC_STABLECOIN_GATEWAY_FAMILIES
704
- );
705
- var TOKEN_GATEWAY_KEYS = [
706
- ...GENERIC_STABLECOIN_GATEWAY_FAMILIES.USDT,
707
- ...GENERIC_STABLECOIN_GATEWAY_FAMILIES.USDC,
708
- ...GENERIC_STABLECOIN_GATEWAY_FAMILIES.DAI
709
- ];
710
- var CRYPTO_GATEWAY_KEYS = [
711
- ...COIN_GATEWAY_KEYS,
712
- ...TOKEN_GATEWAY_KEYS
713
- ];
714
- var LEGACY_CRYPTO_GATEWAY_KEYS = [
715
- "BITCOIN_LN"
716
- ];
717
- var CRYPTO_PROVIDER_GATEWAY_KEYS = [
718
- ...CRYPTO_PROVIDER_KEYS,
719
- ...MANAGED_CRYPTO_PROVIDER_KEYS
720
- ];
721
- var CONCRETE_CRYPTO_GATEWAY_KEYS = [
722
- ...CRYPTO_GATEWAY_KEYS,
723
- ...LEGACY_CRYPTO_GATEWAY_KEYS
724
- ];
725
- var EMBED_CRYPTO_GATEWAY_KEYS = [
726
- "BITCOIN",
727
- "LITECOIN",
728
- "ETHEREUM",
729
- "BITCOIN_CASH",
730
- "MONERO",
731
- "RIPPLE",
732
- "TRON",
733
- "SOLANA",
734
- "POLYGON",
735
- "BINANCE_COIN",
736
- "CRONOS",
737
- "NANO",
738
- "DOGECOIN",
739
- "DASH",
740
- "USDT_TRC20",
741
- "USDT_ERC20",
742
- "USDT_BEP20",
743
- "USDT_POLYGON",
744
- "USDT_SOL",
745
- "USDC_ERC20",
746
- "USDC_TRC20",
747
- "USDC_BEP20",
748
- "USDC_POLYGON",
749
- "USDC_SOL",
750
- "DAI_ERC20",
751
- "BITCOIN_LN"
752
- ];
753
- var DEFAULT_NOWPAYMENTS_CRYPTO_GATEWAY_KEYS = CRYPTO_GATEWAY_KEYS;
754
- var DEFAULT_CRYPTOMUS_CRYPTO_GATEWAY_KEYS = [
755
- "BITCOIN",
756
- "LITECOIN",
757
- "ETHEREUM",
758
- "TRON",
759
- "SOLANA",
760
- "POLYGON",
761
- "BINANCE_COIN",
762
- "BITCOIN_CASH",
763
- "MONERO",
764
- "RIPPLE",
765
- "DOGECOIN",
766
- "USDT_TRC20",
767
- "USDT_ERC20",
768
- "USDT_BEP20",
769
- "USDT_POLYGON",
770
- "USDT_SOL",
771
- "USDC_ERC20",
772
- "USDC_BEP20",
773
- "USDC_POLYGON",
774
- "DAI_ERC20"
775
- ];
776
- var DEFAULT_OXAPAY_CRYPTO_GATEWAY_KEYS = [
777
- "BITCOIN",
778
- "LITECOIN",
779
- "ETHEREUM",
780
- "TRON",
781
- "SOLANA",
782
- "POLYGON",
783
- "BINANCE_COIN",
784
- "BITCOIN_CASH",
785
- "MONERO",
786
- "RIPPLE",
787
- "DOGECOIN",
788
- "USDT_TRC20",
789
- "USDT_ERC20",
790
- "USDT_BEP20",
791
- "USDT_POLYGON",
792
- "USDC_ERC20"
793
- ];
794
- var CHECKOUT_METHOD_ORDER_KEYS = [
795
- "STRIPE",
796
- "PAYPAL",
797
- "PAYPAL_FF",
798
- "MOLLIE",
799
- "AUTHORIZENET",
800
- "NMI",
801
- "SQUARE",
802
- "SUMUP",
803
- "SHOPIFY",
804
- "PANDABASE",
805
- "DEBLOMASSI",
806
- "SHOPPEXPAY",
807
- "MONEYMOTION",
808
- "OVGC",
809
- "MISTICPAY",
810
- "WHOP",
811
- "DODO",
812
- "MAVERICK",
813
- "CASHAPP",
814
- "VENMO",
815
- "REWARBLE",
816
- "CRYPTO",
817
- "CUSTOMER_BALANCE",
818
- "EXTERNAL",
819
- "MANUAL"
820
- ];
821
- var DISABLED_PAYMENT_GATEWAY_KEYS = ["PANDABASE", "STORRIK"];
822
- var DISABLED_PAYMENT_GATEWAY_KEY_SET = new Set(
823
- DISABLED_PAYMENT_GATEWAY_KEYS
824
- );
825
- function isDisabledPaymentGateway(gateway) {
826
- return DISABLED_PAYMENT_GATEWAY_KEY_SET.has(gateway.trim().toUpperCase());
827
- }
828
- var RETIRED_UI_PAYMENT_GATEWAY_KEYS = [
829
- "SHOPPEXPAY",
830
- ...DISABLED_PAYMENT_GATEWAY_KEYS
831
- ];
832
- var RETIRED_UI_PAYMENT_GATEWAY_KEY_SET = new Set(
833
- RETIRED_UI_PAYMENT_GATEWAY_KEYS
834
- );
835
- var WRITABLE_CHECKOUT_METHOD_ORDER_KEYS = CHECKOUT_METHOD_ORDER_KEYS.filter(
836
- (gateway) => !isDisabledPaymentGateway(gateway)
837
- );
838
- var COIN_GATEWAY_KEY_SET = new Set(COIN_GATEWAY_KEYS);
839
- var TOKEN_GATEWAY_KEY_SET = new Set(TOKEN_GATEWAY_KEYS);
840
- var CRYPTO_GATEWAY_KEY_SET = new Set(CRYPTO_GATEWAY_KEYS);
841
- var LEGACY_CRYPTO_GATEWAY_KEY_SET = new Set(LEGACY_CRYPTO_GATEWAY_KEYS);
842
- var CRYPTO_PROVIDER_KEY_SET = new Set(CRYPTO_PROVIDER_KEYS);
843
- var MANAGED_CRYPTO_PROVIDER_KEY_SET = new Set(MANAGED_CRYPTO_PROVIDER_KEYS);
844
- var CRYPTO_PROVIDER_GATEWAY_KEY_SET = new Set(CRYPTO_PROVIDER_GATEWAY_KEYS);
845
- var CONCRETE_CRYPTO_GATEWAY_KEY_SET = new Set(CONCRETE_CRYPTO_GATEWAY_KEYS);
846
- var EMBED_CRYPTO_GATEWAY_KEY_SET = new Set(EMBED_CRYPTO_GATEWAY_KEYS);
847
- var GENERIC_STABLECOIN_GATEWAY_KEY_SET = new Set(GENERIC_STABLECOIN_GATEWAY_KEYS);
848
- var CHECKOUT_METHOD_ORDER_KEY_SET = new Set(CHECKOUT_METHOD_ORDER_KEYS);
849
- var LEGACY_GATEWAY_ALIASES = {
850
- CASHAPP: "CASH_APP",
851
- PAYPAL_CREDIT_CARD: "PAYPAL",
852
- EUTHEREUM: "ETHEREUM",
853
- USDT_MATIC: "USDT_POLYGON",
854
- USDC_MATIC: "USDC_POLYGON"
855
- };
856
- function assertUniqueEntries(name, values) {
857
- if (new Set(values).size !== values.length) {
858
- throw new Error(`${name} contains duplicate entries`);
859
- }
860
- }
861
- assertUniqueEntries("COIN_GATEWAY_KEYS", COIN_GATEWAY_KEYS);
862
- assertUniqueEntries("TOKEN_GATEWAY_KEYS", TOKEN_GATEWAY_KEYS);
863
- assertUniqueEntries("CRYPTO_GATEWAY_KEYS", CRYPTO_GATEWAY_KEYS);
864
- assertUniqueEntries("LEGACY_CRYPTO_GATEWAY_KEYS", LEGACY_CRYPTO_GATEWAY_KEYS);
865
- assertUniqueEntries("CRYPTO_PROVIDER_KEYS", CRYPTO_PROVIDER_KEYS);
866
- assertUniqueEntries("MANAGED_CRYPTO_PROVIDER_KEYS", MANAGED_CRYPTO_PROVIDER_KEYS);
867
- assertUniqueEntries("CRYPTO_PROVIDER_GATEWAY_KEYS", CRYPTO_PROVIDER_GATEWAY_KEYS);
868
- assertUniqueEntries("DEFAULT_NOWPAYMENTS_CRYPTO_GATEWAY_KEYS", DEFAULT_NOWPAYMENTS_CRYPTO_GATEWAY_KEYS);
869
- assertUniqueEntries("DEFAULT_CRYPTOMUS_CRYPTO_GATEWAY_KEYS", DEFAULT_CRYPTOMUS_CRYPTO_GATEWAY_KEYS);
870
- assertUniqueEntries("DEFAULT_OXAPAY_CRYPTO_GATEWAY_KEYS", DEFAULT_OXAPAY_CRYPTO_GATEWAY_KEYS);
871
- for (const key of CRYPTO_GATEWAY_KEYS) {
872
- if (!COIN_GATEWAY_KEY_SET.has(key) && !TOKEN_GATEWAY_KEY_SET.has(key)) {
873
- throw new Error(`CRYPTO_GATEWAY_KEYS contains an unclassified gateway: ${key}`);
874
- }
875
- }
876
- function normalizeGatewayKey(input) {
877
- const trimmed = input.trim();
878
- if (!trimmed) {
879
- return "";
880
- }
881
- if (/^manual:/i.test(trimmed)) {
882
- const manualId = trimmed.slice(trimmed.indexOf(":") + 1).trim();
883
- return manualId ? `MANUAL:${manualId}` : "MANUAL";
884
- }
885
- if (/^external:/i.test(trimmed)) {
886
- const adapterId = parseExternalPaymentAdapterGatewayKey(trimmed);
887
- return adapterId ? `${EXTERNAL_PAYMENT_ADAPTER_GATEWAY_PREFIX}${adapterId}` : trimmed.toUpperCase();
888
- }
889
- let normalized = trimmed.toUpperCase();
890
- const stablecoinNetworkMatch = normalized.match(/^(USDT|USDC|DAI):([A-Z0-9_]+)$/);
891
- if (stablecoinNetworkMatch) {
892
- const [, asset, rawNetwork] = stablecoinNetworkMatch;
893
- const network = rawNetwork === "MATIC" ? "POLYGON" : rawNetwork;
894
- normalized = `${asset}_${network}`;
895
- }
896
- return LEGACY_GATEWAY_ALIASES[normalized] ?? normalized;
897
- }
898
- var FINANCE_GATEWAY_KEYS = [
899
- "STRIPE",
900
- "PAYPAL",
901
- "PAYPAL_FF",
902
- "SKRILL",
903
- "CASHAPP",
904
- "CASH_APP",
905
- "PERFECT_MONEY",
906
- "SQUARE",
907
- "SUMUP",
908
- "SHOPIFY",
909
- "PANDABASE",
910
- "DEBLOMASSI",
911
- "SHOPPEXPAY",
912
- "MONEYMOTION",
913
- "OVGC",
914
- "MISTICPAY",
915
- "WHOP",
916
- "DODO",
917
- "MAVERICK",
918
- "VENMO",
919
- "MOLLIE",
920
- "AUTHORIZENET",
921
- "NMI",
922
- "REWARBLE"
923
- ];
924
- var FINANCE_GATEWAY_KEY_SET = new Set(
925
- FINANCE_GATEWAY_KEYS.map((gateway) => normalizeGatewayKey(gateway))
926
- );
927
- var CRYPTO_RAIL_GATEWAY_LABELS = ["WHITE_LABEL", "NATIVE"];
928
- var CRYPTO_RAIL_GATEWAY_LABEL_SET = new Set(CRYPTO_RAIL_GATEWAY_LABELS);
929
- var CRYPTO_PAYMENT_PROCESSOR_KEYS = [...CRYPTO_PROVIDER_GATEWAY_KEYS, "NATIVE"];
930
- var CRYPTO_PAYMENT_PROCESSOR_KEY_SET = new Set(CRYPTO_PAYMENT_PROCESSOR_KEYS);
931
- var SecretRefSchema = object({
932
- set: boolean()
933
- });
934
- var PaymentGatewayHealthSchema = object({
935
- status: _enum(["READY", "NEEDS_ATTENTION"]).optional(),
936
- code: string().optional(),
937
- message: string().optional(),
938
- checked_at: string().optional()
939
- });
940
- var PaymentGatewayStateSchema = object({
941
- provider: string(),
942
- type: string(),
943
- enabled: boolean(),
944
- connected: boolean(),
945
- credentials: record(string(), SecretRefSchema),
946
- public_config: record(string(), unknown()),
947
- webhook: record(string(), unknown()).optional(),
948
- health: PaymentGatewayHealthSchema.optional()
949
- });
950
-
951
- // ../contracts/src/style-center.ts
952
- var CHECKOUT_PLATFORM_BRAND_COLOR = "#7c3aed";
953
- var CHECKOUT_PLATFORM_FONT_FAMILY = "Inter";
954
- var checkoutStyleSurfaceValues = ["checkout", "payment_link", "embed"];
955
- var checkoutStyleDensityValues = ["comfortable", "compact"];
956
- var checkoutStyleModeValues = ["light", "dark", "system"];
957
- var checkoutStylePaymentLinkHeroPositionValues = ["top", "side", "background"];
958
- var embedCloseButtonStyleValues = ["ghost", "outlined", "filled"];
959
- var embedMobileLayoutValues = ["sheet", "fullscreen", "center"];
960
- var embedContentVisibilityValues = ["visible", "hidden"];
961
- var CheckoutStyleSurfaceSchema = _enum(checkoutStyleSurfaceValues);
962
- var CheckoutStyleDensitySchema = _enum(checkoutStyleDensityValues);
963
- var CheckoutStyleModeSchema = _enum(checkoutStyleModeValues);
964
- var CheckoutStylePaymentLinkHeroPositionSchema = _enum(checkoutStylePaymentLinkHeroPositionValues);
965
- var EmbedCloseButtonStyleSchema = _enum(embedCloseButtonStyleValues);
966
- var EmbedMobileLayoutSchema = _enum(embedMobileLayoutValues);
967
- var EmbedContentVisibilitySchema = _enum(embedContentVisibilityValues);
968
- var CHECKOUT_STYLE_TOKEN_SCHEMA_VERSION = 1;
969
- var SHOPPEX_STYLE_THEME_EXPORT_FORMAT_VERSION = 2;
970
- var checkoutStyleTokenDefinitions = [
971
- { key: "color.brand", cssVar: "--spx-checkout-brand", group: "brand", type: "color", default: CHECKOUT_PLATFORM_BRAND_COLOR },
972
- { key: "color.brandContrast", cssVar: "--spx-checkout-brand-contrast", group: "brand", type: "color", default: "#ffffff", protected: true },
973
- // Empty default → CSS variable is omitted by createCheckoutStyleCssVariables,
974
- // so the layout's `var(--spx-checkout-bg, fallback)` resolves to the original
975
- // Tailwind/ambient-gradient fallback for shops that haven't customised the
976
- // background. Setting an explicit hex would clobber the ambient gradient and
977
- // the right-panel `--surface-2` accent for every default-theme checkout.
978
- { key: "color.background", cssVar: "--spx-checkout-bg", group: "color", type: "color", default: "" },
979
- // The four neutrals below are the DARK appearance of the hosted checkout's
980
- // tonal ladder (`apps/checkout/app/globals.css`); the light values sit in
981
- // CHECKOUT_PLATFORM_BASELINE_LIGHT_DEFAULTS. They are materialised into the
982
- // live checkout root, so they are what a shop with no Style Center theme
983
- // actually renders — which is why each one has to name the SAME rung the
984
- // component paints, not a palette of its own.
985
- // surface → `--surface-0`, the working panel the buyer pays in
986
- // surfaceRaised → `--card`, a panel raised above that panel
987
- // (the provider widget frame and its loading skeleton)
988
- // text → `--foreground`
989
- // textMuted → `--muted-foreground`
990
- // border → `--border`
991
- { key: "color.surface", cssVar: "--spx-checkout-surface", group: "color", type: "color", default: "#1b1b1b" },
992
- { key: "color.surfaceRaised", cssVar: "--spx-checkout-surface-raised", group: "color", type: "color", default: "#222222" },
993
- { key: "color.text", cssVar: "--spx-checkout-text", group: "color", type: "color", default: "#f5f5f5", protected: true },
994
- { key: "color.textMuted", cssVar: "--spx-checkout-text-muted", group: "color", type: "color", default: "#a1a1a1" },
995
- // Never '': the border token feeds runtime-style fallbacks for inputs,
996
- // provider widgets and buttons, and an unset value turns those slots'
997
- // `var(--spx-checkout-border, …)` chains loose. The hosted checkout draws
998
- // almost no hairlines any more — the slots that still do read this, the rest
999
- // resolve their border to `transparent` in the system CSS.
1000
- { key: "color.border", cssVar: "--spx-checkout-border", group: "color", type: "color", default: "#3a3a3a" },
1001
- { key: "color.focus", cssVar: "--spx-checkout-focus", group: "color", type: "color", default: CHECKOUT_PLATFORM_BRAND_COLOR, protected: true },
1002
- { key: "color.success", cssVar: "--spx-checkout-success", group: "color", type: "color", default: "#22c55e", protected: true },
1003
- { key: "color.warning", cssVar: "--spx-checkout-warning", group: "color", type: "color", default: "#f59e0b", protected: true },
1004
- { key: "color.error", cssVar: "--spx-checkout-error", group: "color", type: "color", default: "#ef4444", protected: true },
1005
- { key: "typography.fontFamily", cssVar: "--spx-checkout-font", group: "typography", type: "font", default: CHECKOUT_PLATFORM_FONT_FAMILY },
1006
- // Optional Google Fonts family. Empty default → CSS variable is omitted
1007
- // so the curated fontFamily wins. When set, the storefront also injects
1008
- // a <link rel="stylesheet"> to fonts.googleapis.com so the family is
1009
- // actually loaded.
1010
- { key: "typography.googleFontFamily", cssVar: "--spx-checkout-google-font", group: "typography", type: "font", default: "" },
1011
- { key: "typography.baseSize", cssVar: "--spx-checkout-font-size", group: "typography", type: "number", default: 14, min: 12, max: 18, step: 1, unit: "px" },
1012
- // ALL THREE ARE 8, and they have to be — this is one edge, stated in three
1013
- // places, and the whole reason it is worth a comment is that the three places
1014
- // are read by three different renderers:
1015
- // 1. these defaults, materialised into the live checkout root (the `shape`
1016
- // group is a foundation group, so an untouched shop still gets them);
1017
- // 2. `--radius-sm` / `--radius-lg` in apps/checkout/app/globals.css, which
1018
- // every component class paints from, both 0.5rem;
1019
- // 3. the Stripe Appearance object (`buildStripeAppearanceFromCheckoutStyle`),
1020
- // which cannot resolve `var()` and is handed these same values as plain
1021
- // strings for `borderRadius` and the `.Block` / `.AccordionItem` rules.
1022
- // A disagreement between any two of them is visible as a card inside the
1023
- // provider iframe rounded differently from the card around it. Pinned by
1024
- // `__tests__/checkout-radius-chain.test.ts` in apps/checkout.
1025
- { key: "shape.buttonRadius", cssVar: "--spx-checkout-button-radius", group: "shape", type: "number", default: 8, min: 0, max: 24, step: 1, unit: "px" },
1026
- { key: "shape.inputRadius", cssVar: "--spx-checkout-input-radius", group: "shape", type: "number", default: 8, min: 0, max: 24, step: 1, unit: "px" },
1027
- { key: "shape.cardRadius", cssVar: "--spx-checkout-card-radius", group: "shape", type: "number", default: 8, min: 0, max: 28, step: 1, unit: "px" },
1028
- {
1029
- key: "spacing.density",
1030
- cssVar: "--spx-checkout-density",
1031
- group: "spacing",
1032
- type: "select",
1033
- default: "comfortable",
1034
- allowedValues: checkoutStyleDensityValues
1035
- },
1036
- { key: "spacing.controlHeight", cssVar: "--spx-checkout-control-height", group: "spacing", type: "number", default: 48, min: 36, max: 56, step: 2, unit: "px" },
1037
- { key: "component.primaryButton.background", cssVar: "--spx-checkout-button-bg", group: "component", type: "color", default: "", protected: true },
1038
- { key: "component.primaryButton.text", cssVar: "--spx-checkout-button-text", group: "component", type: "color", default: "#ffffff", protected: true },
1039
- { key: "component.input.background", cssVar: "--spx-checkout-input-bg", group: "component", type: "color", default: "#292929" },
1040
- { key: "component.input.border", cssVar: "--spx-checkout-input-border", group: "component", type: "color", default: "#3a3a3a" },
1041
- { key: "component.input.focusRing", cssVar: "--spx-checkout-input-focus-ring", group: "component", type: "color", default: "" },
1042
- // Default '' is filtered out by the preview-iframe normaliser, so the
1043
- // CSS fallback chain on [data-spx-slot="summary.panel"] resolves to
1044
- // --spx-checkout-bg when no merchant override is set. A merchant who
1045
- // only edits color.background therefore sees the aside follow that
1046
- // change without an explicit summary override.
1047
- { key: "component.summary.background", cssVar: "--spx-checkout-summary-bg", group: "component", type: "color", default: "" },
1048
- { key: "component.paymentMethod.background", cssVar: "--spx-checkout-payment-method-bg", group: "component", type: "color", default: "" },
1049
- { key: "component.paymentMethod.cardBackground", cssVar: "--spx-checkout-payment-method-card-bg", group: "component", type: "color", default: "" },
1050
- { key: "component.paymentMethod.border", cssVar: "--spx-checkout-payment-method-border", group: "component", type: "color", default: "" },
1051
- { key: "component.paymentMethod.selectedBackground", cssVar: "--spx-checkout-payment-method-selected-bg", group: "component", type: "color", default: "" },
1052
- { key: "component.checkoutHeader.background", cssVar: "--spx-checkout-embed-header-bg", group: "component", type: "color", default: "" },
1053
- { key: "component.checkoutHeader.text", cssVar: "--spx-checkout-embed-header-text", group: "component", type: "color", default: "" },
1054
- // THE PRODUCT CARD IS GONE FROM THE HOSTED CHECKOUT, and these three tokens
1055
- // are what is left of it.
1056
- //
1057
- // The 2026 rework replaced the boxed summary line item with a FLAT ROW
1058
- // (`data-spx-slot="product.line"`, apps/checkout/components/checkout/
1059
- // product-list.tsx). A flat row has no fill of its own, no edge and no cast —
1060
- // that is the design, not an omission — so there is nothing on the hosted or
1061
- // payment-link surface for a border or a shadow token to land on. They are
1062
- // NOT re-pointed at `product.line`: handing a merchant a border control for a
1063
- // row that must not have a border is a control that can only make the page
1064
- // worse, and the slot layer would paint it with `!important` where no call
1065
- // site could undo it.
1066
- //
1067
- // `background` is the exception and stays LIVE: the EMBED surface reads
1068
- // `--spx-checkout-product-card-bg` in `applyCheckoutStyleToEmbedStyles`
1069
- // (apps/checkout/lib/checkout-style.tsx), where it is the card fill AND the
1070
- // input to the embed's light/dark inference. It is marked `surface: 'embed'`
1071
- // so the Style Center offers it where it still does something, and it keeps
1072
- // being emitted for every stored theme.
1073
- { key: "component.productCard.background", cssVar: "--spx-checkout-product-card-bg", group: "component", type: "color", default: "rgba(255,255,255,0.03)", surface: "embed" },
1074
- { key: "component.productCard.border", cssVar: "--spx-checkout-product-card-border", group: "component", type: "color", default: "rgba(255,255,255,0.06)", deprecated: true },
1075
- { key: "component.productCard.shadow", cssVar: "--spx-checkout-product-card-shadow", group: "component", type: "shadow", default: "none", deprecated: true },
1076
- { key: "component.productImage.background", cssVar: "--spx-checkout-product-image-bg", group: "component", type: "color", default: "rgba(255,255,255,0.06)" },
1077
- { key: "component.productImage.border", cssVar: "--spx-checkout-product-image-border", group: "component", type: "color", default: "rgba(255,255,255,0.04)" },
1078
- { key: "component.productImage.icon", cssVar: "--spx-checkout-product-image-icon", group: "component", type: "color", default: "rgba(250,250,250,0.4)" },
1079
- { key: "component.brandAvatar.background", cssVar: "--spx-checkout-brand-avatar-bg", group: "component", type: "color", default: "rgba(255,255,255,0.06)" },
1080
- { key: "component.brandAvatar.text", cssVar: "--spx-checkout-brand-avatar-text", group: "component", type: "color", default: "#ffffff" },
1081
- { key: "component.brandAvatar.border", cssVar: "--spx-checkout-brand-avatar-border", group: "component", type: "color", default: "rgba(255,255,255,0.1)" },
1082
- { key: "component.pill.background", cssVar: "--spx-checkout-pill-bg", group: "component", type: "color", default: "rgba(255,255,255,0.04)" },
1083
- { key: "component.pill.border", cssVar: "--spx-checkout-pill-border", group: "component", type: "color", default: "rgba(255,255,255,0.08)" },
1084
- { key: "component.pill.text", cssVar: "--spx-checkout-pill-text", group: "component", type: "color", default: "#a1a1a1" },
1085
- { key: "component.errorBanner.background", cssVar: "--spx-checkout-error-bg", group: "component", type: "color", default: "rgba(239,68,68,0.1)" },
1086
- { key: "component.errorBanner.border", cssVar: "--spx-checkout-error-border", group: "component", type: "color", default: "rgba(239,68,68,0.2)" },
1087
- { key: "component.errorBanner.text", cssVar: "--spx-checkout-error-text", group: "component", type: "color", default: "#ef4444" },
1088
- { key: "component.warningBanner.background", cssVar: "--spx-checkout-warning-bg", group: "component", type: "color", default: "" },
1089
- { key: "component.warningBanner.border", cssVar: "--spx-checkout-warning-border", group: "component", type: "color", default: "" },
1090
- { key: "component.warningBanner.text", cssVar: "--spx-checkout-warning-text", group: "component", type: "color", default: "" },
1091
- { key: "component.radio.idleRing", cssVar: "--spx-checkout-radio-idle-ring", group: "component", type: "color", default: "rgba(161,161,161,0.5)" },
1092
- { key: "component.radio.checkedFill", cssVar: "--spx-checkout-radio-checked-fill", group: "component", type: "color", default: "" },
1093
- { key: "component.radio.checkedIcon", cssVar: "--spx-checkout-radio-checked-icon", group: "component", type: "color", default: "#ffffff" },
1094
- { key: "component.divider.color", cssVar: "--spx-checkout-divider", group: "component", type: "color", default: "rgba(255,255,255,0.06)" }
1095
- ];
1096
- var checkoutStyleAssetTokenDefinitions = [
1097
- {
1098
- key: "brand.logoUrl",
1099
- group: "asset",
1100
- type: "asset_url",
1101
- default: null,
1102
- accept: ["image/png", "image/jpeg", "image/svg+xml", "image/webp"],
1103
- maxBytes: 512e3
1104
- },
1105
- {
1106
- key: "brand.logoDarkUrl",
1107
- group: "asset",
1108
- type: "asset_url",
1109
- default: null,
1110
- accept: ["image/png", "image/jpeg", "image/svg+xml", "image/webp"],
1111
- maxBytes: 512e3
1112
- },
1113
- {
1114
- key: "brand.logoMaxHeight",
1115
- group: "asset",
1116
- type: "number",
1117
- default: 32,
1118
- min: 16,
1119
- max: 80,
1120
- step: 2,
1121
- unit: "px"
1122
- },
1123
- {
1124
- key: "brand.faviconUrl",
1125
- group: "asset",
1126
- type: "asset_url",
1127
- default: null,
1128
- accept: ["image/png", "image/x-icon", "image/svg+xml"],
1129
- maxBytes: 64e3
1130
- },
1131
- {
1132
- key: "paymentLink.heroImageUrl",
1133
- group: "asset",
1134
- type: "asset_url",
1135
- default: null,
1136
- accept: ["image/png", "image/jpeg", "image/webp"],
1137
- maxBytes: 1048576,
1138
- surface: "payment_link"
1139
- },
1140
- {
1141
- key: "paymentLink.heroPosition",
1142
- group: "asset",
1143
- type: "select",
1144
- default: "top",
1145
- allowedValues: checkoutStylePaymentLinkHeroPositionValues,
1146
- surface: "payment_link"
1147
- }
1148
- ];
1149
- var checkoutStyleControlTokenDefinitions = [
1150
- ...checkoutStyleTokenDefinitions,
1151
- ...checkoutStyleAssetTokenDefinitions
1152
- ];
1153
- var checkoutStyleSlotValues = [
1154
- "checkout.shell",
1155
- "checkout.chrome",
1156
- "checkout.panel",
1157
- "checkout.header",
1158
- "checkout.footer",
1159
- "checkout.trust",
1160
- "checkout.section.active",
1161
- "checkout.section.collapsed",
1162
- "checkout.mobile_pay_bar",
1163
- // The spacer under the bar, carrying the working column's ground so the last
1164
- // scroll does not step down onto the shell's.
1165
- "checkout.mobile_pay_bar_floor",
1166
- "brand.logo",
1167
- "brand.hero",
1168
- // RETIRED, AND DELIBERATELY STILL HERE (see the note above). No element
1169
- // carries `product.card` any more (see `component.productCard.*` above), so a
1170
- // rule aimed at it is inert, which is the harmless half of the trade.
1171
- "product.card",
1172
- // What replaced it: the flat summary row. A merchant can reach it for type
1173
- // and spacing; it has no card material to override, by design.
1174
- "product.line",
1175
- "product.image",
1176
- "product.title",
1177
- "product.description",
1178
- "product.price",
1179
- "product.quantity",
1180
- "product.quantity_menu",
1181
- "product.remove",
1182
- "product.addon",
1183
- // The "Included" marker a SELECTED add-on carries in place of its "+$9.00".
1184
- // Its own slot rather than `product.price`, because it is not a figure: the
1185
- // amount is already inside the line total above it, and a merchant styling
1186
- // their price column must not accidentally style a word.
1187
- "product.addon_included",
1188
- // The line of glyph-led metadata under the product's title in the embed's
1189
- // order bar — the description trigger plus whichever of the facts below the
1190
- // product has. Its own slot so a merchant can retune the whole row's rhythm
1191
- // (or hide it) without reaching into each fact one at a time.
1192
- "product.facts",
1193
- // How many are left. It appears in TWO places and deliberately shares one
1194
- // slot: on the facts row when the product has no variants, and on each option
1195
- // tile when it does — because with options there is no single stock figure,
1196
- // only one per option. A merchant styling scarcity means the same thing in
1197
- // both, and a shop is unlikely to have configured only the one it can see.
1198
- "product.stock",
1199
- "product.delivery",
1200
- // The billing period a recurring line states — "/month" beside the figure,
1201
- // or the full phrase on the metadata line when the period is not one-per-unit.
1202
- "product.recurrence",
1203
- "product.warranty",
1204
- "paymentLink.hero",
1205
- "summary.panel",
1206
- "summary.line",
1207
- "summary.total",
1208
- // The row that CLOSES the ledger, distinct from `summary.total` (the column's
1209
- // headline figure) and from `summary.line` (one part of the sum): it is the
1210
- // sum itself, and it is the only row in the table that draws a rule.
1211
- "summary.total_line",
1212
- // What the total does next month, stated directly under it.
1213
- "summary.recurrence",
1214
- "summary.total_note",
1215
- "summary.order_number",
1216
- "summary.tracking",
1217
- "summary.tip",
1218
- "summary.after_payment",
1219
- // The mobile order summary: a persistent bar that expands into the panel.
1220
- "summary.mobile_bar",
1221
- "summary.disclosure",
1222
- "form.field",
1223
- "form.label",
1224
- "form.help",
1225
- "form.value",
1226
- "form.error",
1227
- "form.panel",
1228
- "form.save",
1229
- "form.saved",
1230
- "coupon.input",
1231
- "coupon.applied",
1232
- "input.base",
1233
- "input.error",
1234
- // The reserved line under an input that holds its message. It is present
1235
- // whether or not there is something to say, which is the point: no reflow.
1236
- "input.message_slot",
1237
- "button.primary",
1238
- "button.secondary",
1239
- "button.blocked",
1240
- "payment.methods",
1241
- "payment.method",
1242
- "payment.method.icon",
1243
- "payment.method.label",
1244
- "payment.method.meta",
1245
- "payment.method.fee",
1246
- "payment.method.indicator",
1247
- "payment.method.trailing",
1248
- "payment.method.change",
1249
- "payment.selected_method",
1250
- "payment.provider_widget",
1251
- "payment.express_checkout",
1252
- "payment.paypal_shell",
1253
- "payment.acknowledgement",
1254
- "payment.free_completion",
1255
- "payment.notice",
1256
- "payment.trial_notice",
1257
- "payment.blocker_hint",
1258
- "payment.loading",
1259
- "payment.error",
1260
- "payment.warning",
1261
- "legal.terms",
1262
- "status.success",
1263
- "status.processing",
1264
- "status.details",
1265
- "status.wait_hint",
1266
- // The seller's own wording on a paid order waiting for restock. Styleable but
1267
- // NOT protected: the sentence that states the mechanics — delivery runs by
1268
- // itself, nothing to resubmit — is `status.processing` above and stays
1269
- // whatever a theme does with this one.
1270
- "status.seller_stock_note",
1271
- // The open order kept its gateway: the buyer is told which one, and why the
1272
- // picker is gone.
1273
- "status.method_locked",
1274
- "status.payment_rescue",
1275
- "status.recovery",
1276
- "status.delivery_destination",
1277
- "embed.overlay_root",
1278
- // The rest of the embed group belongs to the standalone widget bundle, which
1279
- // no longer carries these names on an element. Kept for the same reason
1280
- // `product.card` is kept: a stored theme that targets one must still save.
1281
- "embed.launcher",
1282
- "embed.launcher.icon",
1283
- "embed.productCard",
1284
- "embed.productCard.image",
1285
- "embed.productCard.title",
1286
- "embed.productCard.price",
1287
- "embed.cart",
1288
- "embed.cart.row",
1289
- "embed.cart.summary",
1290
- "embed.modal",
1291
- "embed.modal.header",
1292
- "embed.modal.close",
1293
- "embed.skeleton",
1294
- "embed.loader",
1295
- "embed.branding"
1296
- ];
1297
- var checkoutStyleProtectedSlotValues = [
1298
- "product.title",
1299
- "product.price",
1300
- "summary.total",
1301
- "payment.method.label",
1302
- "payment.provider_widget",
1303
- // The pay control on mobile. It is the only one in the viewport once the
1304
- // page scrolls, so hiding it hides checkout.
1305
- "checkout.mobile_pay_bar",
1306
- // Whole payment routes: express wallets, the PayPal button host, and the
1307
- // completion control a zero-total order has instead of a gateway.
1308
- "payment.express_checkout",
1309
- "payment.paypal_shell",
1310
- "payment.free_completion",
1311
- // Disclosures. The acknowledgement is the buyer's consent to an offline
1312
- // payment; the trial notice is what they will be charged and when.
1313
- "payment.acknowledgement",
1314
- "payment.trial_notice",
1315
- "payment.loading",
1316
- "payment.error",
1317
- "payment.warning",
1318
- "legal.terms",
1319
- "status.success",
1320
- "status.processing",
1321
- "embed.modal.close",
1322
- "embed.branding"
1323
- ];
1324
- var CheckoutStyleSlotSchema = _enum(checkoutStyleSlotValues);
1325
- var CheckoutStyleProtectedSlotSchema = _enum(checkoutStyleProtectedSlotValues);
1326
- var HexColorSchema = string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, "Use a valid hex color");
1327
- var ExtendedColorSchema = string().trim().min(1).max(200).refine(
1328
- (value) => !/[;{}]|@import|expression\s*\(/i.test(value),
1329
- "Use a valid CSS color or gradient"
1330
- );
1331
- var ShadowSchema = string().trim().min(1).max(400).refine(
1332
- (value) => !/[;{}]|@import|expression\s*\(/i.test(value),
1333
- "Use a valid CSS box-shadow value"
1334
- );
1335
- var curatedFontValues = [
1336
- "Inter",
1337
- "Geist",
1338
- "Manrope",
1339
- "Plus Jakarta Sans",
1340
- "DM Sans",
1341
- "Space Grotesk",
1342
- "Sora",
1343
- "IBM Plex Sans",
1344
- "JetBrains Mono",
1345
- "Arial",
1346
- "System UI"
1347
- ];
1348
- var CuratedFontSchema = _enum(curatedFontValues);
1349
- var GoogleFontFamilySchema = string().trim().min(1).max(60).regex(/^[A-Za-z0-9 +\-_]+$/, "Use a Google Fonts family name");
1350
- var AssetUrlSchema = string().url();
1351
- var CheckoutStyleTokensSchema = object({
1352
- brand: object({
1353
- logoUrl: AssetUrlSchema.optional().nullable(),
1354
- logoDarkUrl: AssetUrlSchema.optional().nullable(),
1355
- logoMaxHeight: number().int().min(16).max(80).optional(),
1356
- faviconUrl: AssetUrlSchema.optional().nullable()
1357
- }).strict().optional(),
1358
- color: object({
1359
- brand: HexColorSchema.optional(),
1360
- brandContrast: HexColorSchema.optional(),
1361
- background: HexColorSchema.optional(),
1362
- surface: HexColorSchema.optional(),
1363
- surfaceRaised: HexColorSchema.optional(),
1364
- text: HexColorSchema.optional(),
1365
- textMuted: HexColorSchema.optional(),
1366
- border: HexColorSchema.optional(),
1367
- focus: HexColorSchema.optional(),
1368
- success: HexColorSchema.optional(),
1369
- warning: HexColorSchema.optional(),
1370
- error: HexColorSchema.optional()
1371
- }).strict().optional(),
1372
- typography: object({
1373
- fontFamily: CuratedFontSchema.optional(),
1374
- // Optional Google Fonts override. When set, the storefront injects the
1375
- // fonts.googleapis.com stylesheet and uses this family in addition to
1376
- // (or instead of) the curated fontFamily as the leftmost name in the
1377
- // CSS font stack.
1378
- googleFontFamily: GoogleFontFamilySchema.optional(),
1379
- baseSize: number().int().min(12).max(18).optional()
1380
- }).strict().optional(),
1381
- shape: object({
1382
- buttonRadius: number().int().min(0).max(24).optional(),
1383
- inputRadius: number().int().min(0).max(24).optional(),
1384
- cardRadius: number().int().min(0).max(28).optional()
1385
- }).strict().optional(),
1386
- spacing: object({
1387
- density: CheckoutStyleDensitySchema.optional(),
1388
- controlHeight: number().int().min(36).max(56).optional()
1389
- }).strict().optional(),
1390
- component: object({
1391
- primaryButton: object({
1392
- background: HexColorSchema.optional(),
1393
- text: HexColorSchema.optional()
1394
- }).strict().optional(),
1395
- input: object({
1396
- background: HexColorSchema.optional(),
1397
- border: HexColorSchema.optional(),
1398
- focusRing: ExtendedColorSchema.optional()
1399
- }).strict().optional(),
1400
- summary: object({
1401
- background: HexColorSchema.optional()
1402
- }).strict().optional(),
1403
- paymentMethod: object({
1404
- background: ExtendedColorSchema.optional(),
1405
- cardBackground: ExtendedColorSchema.optional(),
1406
- border: ExtendedColorSchema.optional(),
1407
- selectedBackground: ExtendedColorSchema.optional()
1408
- }).strict().optional(),
1409
- productCard: object({
1410
- background: ExtendedColorSchema.optional(),
1411
- border: ExtendedColorSchema.optional(),
1412
- shadow: ShadowSchema.optional()
1413
- }).strict().optional(),
1414
- productImage: object({
1415
- background: ExtendedColorSchema.optional(),
1416
- border: ExtendedColorSchema.optional(),
1417
- icon: ExtendedColorSchema.optional()
1418
- }).strict().optional(),
1419
- brandAvatar: object({
1420
- background: ExtendedColorSchema.optional(),
1421
- text: HexColorSchema.optional(),
1422
- border: ExtendedColorSchema.optional()
1423
- }).strict().optional(),
1424
- pill: object({
1425
- background: ExtendedColorSchema.optional(),
1426
- border: ExtendedColorSchema.optional(),
1427
- text: HexColorSchema.optional()
1428
- }).strict().optional(),
1429
- errorBanner: object({
1430
- background: ExtendedColorSchema.optional(),
1431
- border: ExtendedColorSchema.optional(),
1432
- text: HexColorSchema.optional()
1433
- }).strict().optional(),
1434
- warningBanner: object({
1435
- background: ExtendedColorSchema.optional(),
1436
- border: ExtendedColorSchema.optional(),
1437
- text: HexColorSchema.optional()
1438
- }).strict().optional(),
1439
- radio: object({
1440
- idleRing: ExtendedColorSchema.optional(),
1441
- checkedFill: HexColorSchema.optional(),
1442
- checkedIcon: HexColorSchema.optional()
1443
- }).strict().optional(),
1444
- divider: object({
1445
- color: ExtendedColorSchema.optional()
1446
- }).strict().optional()
1447
- }).strict().optional(),
1448
- paymentLink: object({
1449
- heroImageUrl: AssetUrlSchema.optional().nullable(),
1450
- heroPosition: CheckoutStylePaymentLinkHeroPositionSchema.optional()
1451
- }).strict().optional(),
1452
- embed: object({
1453
- launcher: object({
1454
- background: ExtendedColorSchema.optional(),
1455
- text: HexColorSchema.optional(),
1456
- radius: number().int().min(0).max(28).optional(),
1457
- paddingX: number().int().min(8).max(32).optional(),
1458
- paddingY: number().int().min(6).max(24).optional(),
1459
- shadow: ShadowSchema.optional()
1460
- }).strict().optional(),
1461
- productCard: object({
1462
- background: ExtendedColorSchema.optional(),
1463
- border: ExtendedColorSchema.optional(),
1464
- imageRadius: number().int().min(0).max(24).optional(),
1465
- padding: number().int().min(8).max(32).optional()
1466
- }).strict().optional(),
1467
- cart: object({
1468
- background: ExtendedColorSchema.optional(),
1469
- rowBorder: ExtendedColorSchema.optional(),
1470
- itemSpacing: number().int().min(4).max(28).optional()
1471
- }).strict().optional(),
1472
- modal: object({
1473
- background: ExtendedColorSchema.optional(),
1474
- radius: number().int().min(0).max(32).optional(),
1475
- maxWidth: number().int().min(360).max(960).optional(),
1476
- shadow: ShadowSchema.optional()
1477
- }).strict().optional(),
1478
- backdrop: object({
1479
- color: ExtendedColorSchema.optional(),
1480
- blur: number().int().min(0).max(24).optional(),
1481
- opacity: number().min(0).max(1).optional()
1482
- }).strict().optional(),
1483
- skeleton: object({
1484
- background: ExtendedColorSchema.optional(),
1485
- shimmer: ExtendedColorSchema.optional()
1486
- }).strict().optional(),
1487
- spinner: object({
1488
- color: ExtendedColorSchema.optional()
1489
- }).strict().optional(),
1490
- closeButton: object({
1491
- style: EmbedCloseButtonStyleSchema.optional()
1492
- }).strict().optional(),
1493
- mobile: object({
1494
- layout: EmbedMobileLayoutSchema.optional()
1495
- }).strict().optional(),
1496
- content: object({
1497
- productDescription: EmbedContentVisibilitySchema.optional(),
1498
- termsShortcut: EmbedContentVisibilitySchema.optional()
1499
- }).strict().optional()
1500
- }).strict().optional()
1501
- }).strict();
1502
- var CheckoutStyleSettingsSchema = object({
1503
- mode: CheckoutStyleModeSchema.default("system"),
1504
- fontSource: _enum(["curated"]).default("curated")
1505
- }).strict();
1506
- var StyleThemeShareModeSchema = _enum(["standalone", "bundle", "pointer"]);
1507
- var StyleThemeParentHintSchema = object({
1508
- name: string().trim().min(1).max(80),
1509
- fingerprint: string().trim().min(1).max(128)
1510
- }).strict();
1511
- var RETIRED_EXPORT_FIELDS = ["embed_design"];
1512
- function stripRetiredExportFields(value) {
1513
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1514
- return value;
1515
- }
1516
- const record2 = value;
1517
- if (!RETIRED_EXPORT_FIELDS.some((field) => field in record2)) {
1518
- return value;
1519
- }
1520
- const cleaned = { ...record2 };
1521
- for (const field of RETIRED_EXPORT_FIELDS) {
1522
- delete cleaned[field];
1523
- }
1524
- return cleaned;
1525
- }
1526
- var CheckoutStyleBundledParentThemeExportSchema = preprocess(
1527
- stripRetiredExportFields,
1528
- object({
1529
- type: literal("shoppex.style_theme"),
1530
- surface: literal("checkout"),
1531
- format_version: union([
1532
- literal(1),
1533
- literal(SHOPPEX_STYLE_THEME_EXPORT_FORMAT_VERSION)
1534
- ]),
1535
- name: string().trim().min(1).max(80),
1536
- description: string().trim().max(240).optional(),
1537
- token_schema_version: literal(CHECKOUT_STYLE_TOKEN_SCHEMA_VERSION),
1538
- tokens: CheckoutStyleTokensSchema.default({}),
1539
- custom_css: string().max(2e4).default(""),
1540
- settings: CheckoutStyleSettingsSchema.default({ mode: "system", fontSource: "curated" }),
1541
- _meta: record(string(), unknown()).optional()
1542
- }).strict()
1543
- );
1544
- var CheckoutStyleThemeExportSchema = preprocess(
1545
- stripRetiredExportFields,
1546
- object({
1547
- type: literal("shoppex.style_theme"),
1548
- surface: CheckoutStyleSurfaceSchema,
1549
- format_version: union([
1550
- literal(1),
1551
- literal(SHOPPEX_STYLE_THEME_EXPORT_FORMAT_VERSION)
1552
- ]),
1553
- name: string().trim().min(1).max(80),
1554
- description: string().trim().max(240).optional(),
1555
- token_schema_version: literal(CHECKOUT_STYLE_TOKEN_SCHEMA_VERSION),
1556
- tokens: CheckoutStyleTokensSchema.default({}),
1557
- custom_css: string().max(2e4).default(""),
1558
- settings: CheckoutStyleSettingsSchema.default({ mode: "system", fontSource: "curated" }),
1559
- share_mode: StyleThemeShareModeSchema.default("standalone"),
1560
- parent_hint: StyleThemeParentHintSchema.optional(),
1561
- bundled_parent: CheckoutStyleBundledParentThemeExportSchema.optional(),
1562
- _meta: record(string(), unknown()).optional()
1563
- }).strict()
1564
- );
1565
- var ResolvedCheckoutStyleSchema = object({
1566
- theme_id: string().uuid().nullable(),
1567
- surface: CheckoutStyleSurfaceSchema,
1568
- token_schema_version: literal(CHECKOUT_STYLE_TOKEN_SCHEMA_VERSION),
1569
- revision: number().int().min(1),
1570
- tokens: CheckoutStyleTokensSchema,
1571
- custom_css: string(),
1572
- css_variables: record(string(), string())
1573
- }).strict();
1574
- var ResolvedEmbedStyleSchema = object({
1575
- theme_id: string().uuid().nullable(),
1576
- parent_theme_id: string().uuid().nullable(),
1577
- surface: literal("embed"),
1578
- token_schema_version: literal(CHECKOUT_STYLE_TOKEN_SCHEMA_VERSION),
1579
- revision: number().int().min(1),
1580
- parent_revision: number().int().min(1).nullable(),
1581
- tokens: CheckoutStyleTokensSchema,
1582
- custom_css: string(),
1583
- css_variables: record(string(), string())
1584
- }).strict();
1585
- var EmbedRuntimeSessionContextSchema = object({
1586
- shop_id: string().uuid(),
1587
- product_id: string().uuid().nullable(),
1588
- product_group_id: string().uuid().nullable(),
1589
- payment_link_id: string().uuid().nullable(),
1590
- invoice_id: string().nullable()
1591
- }).strict();
1592
- var EmbedRuntimeThemeResponseSchema = object({
1593
- embed_runtime: string().min(16).max(128),
1594
- revision: number().int().min(1),
1595
- parent_revision: number().int().min(1).nullable(),
1596
- theme_id: string().uuid().nullable(),
1597
- css_variables: record(string(), string()),
1598
- custom_css: string()
1599
- }).strict();
1600
- var EmbedRuntimeTransportSchema = object({
1601
- embed_style_revision: coerce_exports.number().int().min(1).optional(),
1602
- embed_runtime: string().min(16).max(128).optional(),
1603
- preview: boolean().optional()
1604
- }).strict();
1605
-
1606
- // ../contracts/src/storefront-addons.ts
1607
- var storefrontAddonTypeValues = [
1608
- "announcement_bar",
1609
- "countdown_bar",
1610
- "promo_info_card",
1611
- "recent_purchase_popup",
1612
- "coupon_popup_modal",
1613
- "live_chat"
1614
- ];
1615
- var storefrontAddonSlotValues = [
1616
- "layout.header.before",
1617
- "layout.header.after",
1618
- "layout.overlay",
1619
- "home.hero.after",
1620
- "home.grid.before",
1621
- "product.buybox.after",
1622
- "layout.footer.before",
1623
- "floating.bottom_right"
1624
- ];
1625
- var storefrontAddonComponentValues = [
1626
- "announcement_bar",
1627
- "countdown_bar",
1628
- "promo_info_card",
1629
- "recent_purchase_popup",
1630
- "coupon_popup_modal",
1631
- "live_chat"
1632
- ];
1633
- var StorefrontAddonTypeSchema = _enum(storefrontAddonTypeValues);
1634
- var StorefrontAddonSlotSchema = _enum(storefrontAddonSlotValues);
1635
- var StorefrontAddonComponentSchema = _enum(storefrontAddonComponentValues);
1636
- var HexColorSchema2 = string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, "Use a valid hex color");
1637
- var announcementBarDisplayModeValues = [
1638
- "static",
1639
- "marquee"
1640
- ];
1641
- var announcementBarThemePresetValues = [
1642
- "brand_blue",
1643
- "emerald",
1644
- "sunset",
1645
- "rose",
1646
- "charcoal",
1647
- "custom"
1648
- ];
1649
- var DEFAULT_ANNOUNCEMENT_BAR_ANIMATION_SPEED_SECONDS = 30;
1650
- var AnnouncementBarDisplayModeSchema = _enum(announcementBarDisplayModeValues);
1651
- var AnnouncementBarThemePresetSchema = _enum(announcementBarThemePresetValues);
1652
- var countdownBarExpiryBehaviorValues = [
1653
- "hide",
1654
- "message"
1655
- ];
1656
- var countdownBarDensityValues = [
1657
- "compact",
1658
- "comfortable"
1659
- ];
1660
- var countdownBarCtaStyleValues = [
1661
- "subtle",
1662
- "outline",
1663
- "solid"
1664
- ];
1665
- var countdownBarTimerStyleValues = [
1666
- "minimal",
1667
- "boxed"
1668
- ];
1669
- var promoInfoCardThemePresetValues = [
1670
- "indigo",
1671
- "emerald",
1672
- "amber",
1673
- "rose",
1674
- "slate",
1675
- "custom"
1676
- ];
1677
- var promoInfoCardLayoutStyleValues = [
1678
- "compact",
1679
- "feature",
1680
- "alert"
1681
- ];
1682
- var promoInfoCardDensityValues = [
1683
- "compact",
1684
- "comfortable"
1685
- ];
1686
- var promoInfoCardCtaStyleValues = [
1687
- "subtle",
1688
- "outline",
1689
- "solid"
1690
- ];
1691
- var promoInfoCardIconVisibilityValues = [
1692
- "show",
1693
- "hide"
1694
- ];
1695
- var promoInfoCardIconValues = [
1696
- "sparkles",
1697
- "megaphone",
1698
- "gift",
1699
- "truck",
1700
- "shield",
1701
- "support",
1702
- "none"
1703
- ];
1704
- var CountdownBarExpiryBehaviorSchema = _enum(countdownBarExpiryBehaviorValues);
1705
- var CountdownBarDensitySchema = _enum(countdownBarDensityValues);
1706
- var CountdownBarCtaStyleSchema = _enum(countdownBarCtaStyleValues);
1707
- var CountdownBarTimerStyleSchema = _enum(countdownBarTimerStyleValues);
1708
- var PromoInfoCardThemePresetSchema = _enum(promoInfoCardThemePresetValues);
1709
- var PromoInfoCardLayoutStyleSchema = _enum(promoInfoCardLayoutStyleValues);
1710
- var PromoInfoCardDensitySchema = _enum(promoInfoCardDensityValues);
1711
- var PromoInfoCardCtaStyleSchema = _enum(promoInfoCardCtaStyleValues);
1712
- var PromoInfoCardIconSchema = _enum(promoInfoCardIconValues);
1713
- var PromoInfoCardIconVisibilitySchema = _enum(promoInfoCardIconVisibilityValues);
1714
- var couponPopupModalThemePresetValues = [
1715
- "midnight",
1716
- "ocean",
1717
- "ember",
1718
- "forest",
1719
- "custom"
1720
- ];
1721
- var couponPopupModalTriggerValues = [
1722
- "delay",
1723
- "exit_intent"
1724
- ];
1725
- var CouponPopupModalThemePresetSchema = _enum(couponPopupModalThemePresetValues);
1726
- var CouponPopupModalTriggerSchema = _enum(couponPopupModalTriggerValues);
1727
- var RelativeOrAbsoluteUrlSchema = string().trim().refine((value) => {
1728
- if (value.startsWith("/")) {
1729
- return true;
1730
- }
1731
- try {
1732
- const parsed = new URL(value);
1733
- return parsed.protocol === "http:" || parsed.protocol === "https:";
1734
- } catch {
1735
- return false;
1736
- }
1737
- }, 'Use a valid absolute URL or a path starting with "/"');
1738
- var DatetimeStringSchema = string().trim().refine((value) => {
1739
- const parsed = new Date(value);
1740
- return !Number.isNaN(parsed.getTime());
1741
- }, "Use a valid date and time");
1742
- var AnnouncementBarConfigSchema = object({
1743
- text: string().trim().min(1, "Text is required").max(160, "Text must be 160 characters or fewer"),
1744
- linkLabel: string().trim().max(32, "Link label must be 32 characters or fewer").optional().nullable(),
1745
- linkUrl: RelativeOrAbsoluteUrlSchema.optional().nullable(),
1746
- dismissible: boolean().default(false),
1747
- displayMode: AnnouncementBarDisplayModeSchema.default("marquee"),
1748
- themePreset: AnnouncementBarThemePresetSchema.default("custom"),
1749
- animationSpeedSeconds: number().int().min(8).max(40).default(DEFAULT_ANNOUNCEMENT_BAR_ANIMATION_SPEED_SECONDS),
1750
- backgroundColor: HexColorSchema2.default("#111827"),
1751
- textColor: HexColorSchema2.default("#f9fafb")
1752
- });
1753
- var CountdownBarConfigSchema = object({
1754
- text: string().trim().min(1, "Text is required").max(120, "Text must be 120 characters or fewer"),
1755
- endAt: DatetimeStringSchema,
1756
- linkLabel: string().trim().max(32, "Link label must be 32 characters or fewer").optional().nullable(),
1757
- linkUrl: RelativeOrAbsoluteUrlSchema.optional().nullable(),
1758
- dismissible: boolean().default(false),
1759
- themePreset: AnnouncementBarThemePresetSchema.default("brand_blue"),
1760
- expiredBehavior: CountdownBarExpiryBehaviorSchema.default("hide"),
1761
- expiredMessage: string().trim().max(120, "Expired message must be 120 characters or fewer").optional().nullable(),
1762
- density: CountdownBarDensitySchema.default("compact"),
1763
- ctaStyle: CountdownBarCtaStyleSchema.default("subtle"),
1764
- timerStyle: CountdownBarTimerStyleSchema.default("boxed"),
1765
- backgroundColor: HexColorSchema2.default("#111827"),
1766
- textColor: HexColorSchema2.default("#f9fafb")
1767
- });
1768
- var PromoInfoCardConfigSchema = object({
1769
- eyebrow: string().trim().max(32, "Eyebrow must be 32 characters or fewer").optional().nullable(),
1770
- title: string().trim().min(1, "Title is required").max(80, "Title must be 80 characters or fewer"),
1771
- body: string().trim().min(1, "Body is required").max(240, "Body must be 240 characters or fewer"),
1772
- linkLabel: string().trim().max(32, "Link label must be 32 characters or fewer").optional().nullable(),
1773
- linkUrl: RelativeOrAbsoluteUrlSchema.optional().nullable(),
1774
- themePreset: PromoInfoCardThemePresetSchema.default("indigo"),
1775
- layoutStyle: PromoInfoCardLayoutStyleSchema.default("feature"),
1776
- density: PromoInfoCardDensitySchema.default("comfortable"),
1777
- ctaStyle: PromoInfoCardCtaStyleSchema.default("outline"),
1778
- icon: PromoInfoCardIconSchema.default("sparkles"),
1779
- iconVisibility: PromoInfoCardIconVisibilitySchema.default("show"),
1780
- backgroundColor: HexColorSchema2.default("#111827"),
1781
- textColor: HexColorSchema2.default("#f9fafb"),
1782
- accentColor: HexColorSchema2.default("#818cf8")
1783
- });
1784
- var RecentPurchasePopupConfigSchema = object({
1785
- title: string().trim().min(1, "Title is required").max(80, "Title must be 80 characters or fewer").default("Recent purchases"),
1786
- lookbackHours: number().int().min(1).max(168).default(24),
1787
- cooldownSeconds: number().int().min(5).max(300).default(20),
1788
- maxItems: number().int().min(1).max(20).default(8),
1789
- anonymizeMode: _enum([
1790
- "first_name_city",
1791
- "first_name_country",
1792
- "initial_country",
1793
- "anonymous"
1794
- ]).default("first_name_city"),
1795
- includedProductIds: array(string().min(1)).max(50).default([])
1796
- });
1797
- var CouponPopupModalConfigSchema = object({
1798
- eyebrow: string().trim().max(32, "Eyebrow must be 32 characters or fewer").optional().nullable(),
1799
- title: string().trim().min(1, "Title is required").max(80, "Title must be 80 characters or fewer"),
1800
- body: string().trim().min(1, "Body is required").max(240, "Body must be 240 characters or fewer"),
1801
- couponCode: string().trim().min(2, "Coupon code is required").max(40, "Coupon code must be 40 characters or fewer"),
1802
- primaryButtonLabel: string().trim().min(1, "Primary button label is required").max(24, "Primary button label must be 24 characters or fewer").default("Copy code"),
1803
- secondaryButtonLabel: string().trim().min(1, "Secondary button label is required").max(24, "Secondary button label must be 24 characters or fewer").default("Maybe later"),
1804
- disclaimer: string().trim().max(100, "Disclaimer must be 100 characters or fewer").optional().nullable(),
1805
- themePreset: CouponPopupModalThemePresetSchema.default("midnight"),
1806
- trigger: CouponPopupModalTriggerSchema.default("delay"),
1807
- delaySeconds: number().int().min(0).max(60).default(6),
1808
- showOncePerSession: boolean().default(true),
1809
- reminderHours: number().int().min(1).max(720).default(24),
1810
- backgroundColor: HexColorSchema2.default("#111827"),
1811
- textColor: HexColorSchema2.default("#f8fafc"),
1812
- accentColor: HexColorSchema2.default("#7c9cff"),
1813
- heroImageUrl: RelativeOrAbsoluteUrlSchema.optional().nullable(),
1814
- discountDisplay: string().trim().max(24, "Discount display must be 24 characters or fewer").optional().nullable(),
1815
- expiresAt: DatetimeStringSchema.optional().nullable()
1816
- });
1817
- var liveChatContactFieldsValues = [
1818
- "hidden",
1819
- "optional"
1820
- ];
1821
- var LiveChatContactFieldsSchema = _enum(liveChatContactFieldsValues);
1822
- var LiveChatConfigSchema = object({
1823
- headline: string().trim().min(1, "Headline is required").max(60, "Headline must be 60 characters or fewer").default("Chat with us"),
1824
- greeting: string().trim().min(1, "Greeting is required").max(240, "Greeting must be 240 characters or fewer").default("Hi! Send us a message and we will reply as soon as possible."),
1825
- inputPlaceholder: string().trim().min(1, "Placeholder is required").max(80, "Placeholder must be 80 characters or fewer").default("Type your message\u2026"),
1826
- contactFields: LiveChatContactFieldsSchema.default("optional"),
1827
- accentColor: HexColorSchema2.default("#111827"),
1828
- launcherIconColor: HexColorSchema2.default("#f9fafb")
1829
- });
1830
- var AnnouncementBarDraftSchema = object({
1831
- type: literal("announcement_bar"),
1832
- slot: StorefrontAddonSlotSchema,
1833
- enabled: boolean().default(true),
1834
- sortOrder: number().int().min(0).max(999).default(0),
1835
- config: AnnouncementBarConfigSchema
1836
- });
1837
- var RecentPurchasePopupDraftSchema = object({
1838
- type: literal("recent_purchase_popup"),
1839
- slot: StorefrontAddonSlotSchema,
1840
- enabled: boolean().default(true),
1841
- sortOrder: number().int().min(0).max(999).default(0),
1842
- config: RecentPurchasePopupConfigSchema
1843
- });
1844
- var CountdownBarDraftSchema = object({
1845
- type: literal("countdown_bar"),
1846
- slot: StorefrontAddonSlotSchema,
1847
- enabled: boolean().default(true),
1848
- sortOrder: number().int().min(0).max(999).default(0),
1849
- config: CountdownBarConfigSchema
1850
- });
1851
- var PromoInfoCardDraftSchema = object({
1852
- type: literal("promo_info_card"),
1853
- slot: StorefrontAddonSlotSchema,
1854
- enabled: boolean().default(true),
1855
- sortOrder: number().int().min(0).max(999).default(0),
1856
- config: PromoInfoCardConfigSchema
1857
- });
1858
- var CouponPopupModalDraftSchema = object({
1859
- type: literal("coupon_popup_modal"),
1860
- slot: StorefrontAddonSlotSchema,
1861
- enabled: boolean().default(true),
1862
- sortOrder: number().int().min(0).max(999).default(0),
1863
- config: CouponPopupModalConfigSchema
1864
- });
1865
- var LiveChatDraftSchema = object({
1866
- type: literal("live_chat"),
1867
- slot: StorefrontAddonSlotSchema,
1868
- enabled: boolean().default(true),
1869
- sortOrder: number().int().min(0).max(999).default(0),
1870
- config: LiveChatConfigSchema
1871
- });
1872
- var StorefrontAddonCreateSchema = discriminatedUnion("type", [
1873
- AnnouncementBarDraftSchema,
1874
- CountdownBarDraftSchema,
1875
- PromoInfoCardDraftSchema,
1876
- RecentPurchasePopupDraftSchema,
1877
- CouponPopupModalDraftSchema,
1878
- LiveChatDraftSchema
1879
- ]);
1880
- var StorefrontAddonUpdateSchema = discriminatedUnion("type", [
1881
- AnnouncementBarDraftSchema.extend({
1882
- id: string().min(1)
1883
- }),
1884
- CountdownBarDraftSchema.extend({
1885
- id: string().min(1)
1886
- }),
1887
- PromoInfoCardDraftSchema.extend({
1888
- id: string().min(1)
1889
- }),
1890
- RecentPurchasePopupDraftSchema.extend({
1891
- id: string().min(1)
1892
- }),
1893
- CouponPopupModalDraftSchema.extend({
1894
- id: string().min(1)
1895
- }),
1896
- LiveChatDraftSchema.extend({
1897
- id: string().min(1)
1898
- })
1899
- ]);
1900
-
1901
- // ../contracts/src/manual-gateway-template.ts
1902
- var MANUAL_GATEWAY_TEMPLATE_VARIABLES = [
1903
- "amount",
1904
- "currency",
1905
- "invoice_id",
1906
- "customer_email",
1907
- "id",
1908
- "email",
1909
- "price",
1910
- "price_usd",
1911
- "product_name",
1912
- "quantity"
1913
- ];
1914
- var RESERVED_MANUAL_GATEWAY_FIELD_NAMES = new Set(
1915
- MANUAL_GATEWAY_TEMPLATE_VARIABLES.map((name) => name.toLowerCase())
1916
- );
1917
-
1918
- // ../contracts/src/external-payment-adapter.ts
1919
- var EXTERNAL_PAYMENT_ADAPTER_CONTRACT_VERSION = "2026-08-14";
1920
- var currencySchema = external_exports.string().regex(/^[A-Z]{3}$/, "Currency must be a three-letter uppercase code.");
1921
- var absoluteHttpsUrlSchema = external_exports.string().url().refine(
1922
- (value) => new URL(value).protocol === "https:",
1923
- "URL must use HTTPS."
1924
- );
1925
- var externalPaymentAdapterBaseSchema = external_exports.object({
1926
- version: external_exports.literal(EXTERNAL_PAYMENT_ADAPTER_CONTRACT_VERSION)
1927
- });
1928
- var externalPaymentAdapterSessionRequestSchema = externalPaymentAdapterBaseSchema.extend({
1929
- type: external_exports.literal("payment.session.create"),
1930
- data: external_exports.object({
1931
- attempt_id: external_exports.string().uuid(),
1932
- invoice_id: external_exports.string().uuid(),
1933
- amount_minor: external_exports.number().int().safe().positive(),
1934
- currency: currencySchema,
1935
- customer_email: external_exports.string().email().nullable(),
1936
- description: external_exports.string().min(1).max(500),
1937
- success_url: absoluteHttpsUrlSchema,
1938
- cancel_url: absoluteHttpsUrlSchema,
1939
- event_url: absoluteHttpsUrlSchema
1940
- })
1941
- });
1942
- var externalPaymentAdapterSessionResponseSchema = externalPaymentAdapterBaseSchema.extend({
1943
- provider_reference: external_exports.string().trim().min(1).max(255),
1944
- checkout_url: absoluteHttpsUrlSchema,
1945
- expires_at: external_exports.iso.datetime({ offset: true }).nullable().optional()
1946
- });
1947
- var externalPaymentAdapterEventSchema = externalPaymentAdapterBaseSchema.extend({
1948
- type: external_exports.enum([
1949
- "payment.processing",
1950
- "payment.succeeded",
1951
- "payment.failed"
1952
- ]),
1953
- data: external_exports.object({
1954
- attempt_id: external_exports.string().uuid(),
1955
- provider_reference: external_exports.string().trim().min(1).max(255),
1956
- amount_minor: external_exports.number().int().safe().positive(),
1957
- currency: currencySchema,
1958
- occurred_at: external_exports.iso.datetime({ offset: true }).nullable().optional()
1959
- })
1960
- });
1961
- var externalPaymentAdapterConformanceRequestSchema = externalPaymentAdapterBaseSchema.extend({
1962
- type: external_exports.literal("adapter.conformance.run"),
1963
- data: external_exports.object({
1964
- challenge_id: external_exports.string().uuid(),
1965
- mode: external_exports.enum(["standard", "timeout"]),
1966
- expected_event: external_exports.object({
1967
- attempt_id: external_exports.string().uuid(),
1968
- provider_reference: external_exports.string().trim().min(1).max(255),
1969
- amount_minor: external_exports.number().int().safe().positive(),
1970
- currency: currencySchema
1971
- })
1972
- })
1973
- });
1974
- var externalPaymentAdapterConformanceSampleSchema = external_exports.object({
1975
- scenario: external_exports.enum([
1976
- "valid_event",
1977
- "amount_mismatch",
1978
- "duplicate_event_first",
1979
- "duplicate_event_retry"
1980
- ]),
1981
- webhook_id: external_exports.string().trim().min(1).max(255),
1982
- webhook_timestamp: external_exports.string().trim().min(1).max(32),
1983
- webhook_signature: external_exports.string().trim().min(1).max(1024),
1984
- raw_body: external_exports.string().min(1).max(4096)
1985
- });
1986
- var externalPaymentAdapterConformanceResponseSchema = externalPaymentAdapterBaseSchema.extend({
1987
- type: external_exports.literal("adapter.conformance.result"),
1988
- data: external_exports.object({
1989
- challenge_id: external_exports.string().uuid(),
1990
- samples: external_exports.array(externalPaymentAdapterConformanceSampleSchema).length(4)
1991
- })
1992
- });
1993
-
1994
- // ../contracts/src/redirect-link-template.ts
1995
- var REMAINING_PLACEHOLDER_PATTERNS = [
1996
- /\{\{[\s\S]*?\}\}/g,
1997
- /(?<!\{)\{[^{}]+\}(?!\})/g
1998
- ];
1999
- function findDanglingBraceFragments(template) {
2000
- let stripped = template;
2001
- for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
2002
- stripped = stripped.replace(pattern, "");
2003
- }
2004
- return /[{}]/.test(stripped) ? [...new Set(stripped.match(/[{}]+/g) ?? [])] : [];
2005
- }
2006
- function findRemainingProductRedirectPlaceholders(template) {
2007
- if (!template.trim()) {
2008
- return [];
2009
- }
2010
- const remaining = /* @__PURE__ */ new Set();
2011
- for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
2012
- for (const match of template.matchAll(pattern)) {
2013
- remaining.add(match[0]);
2014
- }
2015
- }
2016
- for (const fragment of findDanglingBraceFragments(template)) {
2017
- remaining.add(fragment);
2018
- }
2019
- return [...remaining];
2020
- }
2021
- function isSafeHttpsRedirectTemplateUrl(url2) {
2022
- const trimmed = url2.trim();
2023
- if (!trimmed) {
2024
- return false;
2025
- }
2026
- try {
2027
- const parsed = new URL(trimmed);
2028
- return parsed.protocol === "https:";
2029
- } catch {
2030
- return false;
2031
- }
2032
- }
2033
-
2034
- // ../contracts/src/invoice-wire.ts
2035
- var unknownRecordSchema = external_exports.record(external_exports.string(), external_exports.unknown());
2036
- var stringOrNumberSchema = external_exports.union([external_exports.string(), external_exports.number()]);
2037
- var embedPaymentSessionWireSchema = external_exports.object({
2038
- kind: external_exports.enum(["address", "redirect", "embed_pending"]),
2039
- gateway: external_exports.string(),
2040
- checkout_url: external_exports.string().nullable().optional(),
2041
- address: external_exports.string().optional(),
2042
- amount: external_exports.string().optional(),
2043
- qr_code: external_exports.string().optional(),
2044
- expires_at: external_exports.string().optional(),
2045
- payment_id: external_exports.string().optional(),
2046
- confirmations_needed: external_exports.number().optional()
2047
- }).passthrough();
2048
- var publicInvoiceWireCustomFieldDefinitionSchema = external_exports.object({
2049
- collect_after_payment: external_exports.boolean().optional(),
2050
- default_value: external_exports.string().optional(),
2051
- min_length: external_exports.number().optional(),
2052
- name: external_exports.string(),
2053
- placeholder: external_exports.string().optional(),
2054
- regex: external_exports.string().optional(),
2055
- required: external_exports.boolean(),
2056
- type: external_exports.string()
2057
- });
2058
- var pendingPostPaymentCustomFieldsSchema = external_exports.object({
2059
- version: external_exports.literal(1),
2060
- line_items: external_exports.array(external_exports.object({
2061
- line_item_id: external_exports.string().uuid(),
2062
- product_id: external_exports.string().uuid(),
2063
- product_title: external_exports.string(),
2064
- fields: external_exports.array(publicInvoiceWireCustomFieldDefinitionSchema.extend({
2065
- collect_after_payment: external_exports.literal(true)
2066
- }))
2067
- }))
2068
- });
2069
- var publicInvoiceWireProductAddonSchema = external_exports.object({
2070
- id: external_exports.string(),
2071
- price: external_exports.number(),
2072
- quantity: external_exports.number(),
2073
- title: external_exports.string()
2074
- });
2075
- var publicInvoiceWireAvailableAddonSchema = external_exports.object({
2076
- currency: external_exports.string(),
2077
- description: external_exports.string(),
2078
- id: external_exports.string(),
2079
- price: external_exports.number(),
2080
- title: external_exports.string(),
2081
- uniqid: external_exports.string()
2082
- });
2083
- function requirePriceDisplayUnlessPayWhatYouWant(product, ctx) {
2084
- if (product.price_display === null && product.pay_what_you_want !== true) {
2085
- ctx.addIssue({
2086
- code: "custom",
2087
- path: ["price_display"],
2088
- message: "price_display may only be null for pay_what_you_want products."
2089
- });
2090
- }
2091
- }
2092
- var publicInvoiceWireProductSchema = external_exports.object({
2093
- addons: external_exports.array(publicInvoiceWireProductAddonSchema).optional(),
2094
- /** Per-line cart-edit capability from the enricher; the checkout renders no
2095
- * control without an explicit true. */
2096
- quantity_editable: external_exports.boolean().optional(),
2097
- addons_editable: external_exports.boolean().optional(),
2098
- removable: external_exports.boolean().optional(),
2099
- available_addons: external_exports.array(external_exports.unknown()).optional(),
2100
- cloudflare_image_id: external_exports.string().nullable(),
2101
- created_at: external_exports.string().optional(),
2102
- currency: external_exports.string(),
2103
- custom_fields: unknownRecordSchema.nullable().optional(),
2104
- custom_fields_config: external_exports.array(publicInvoiceWireCustomFieldDefinitionSchema).nullable().optional(),
2105
- post_payment_custom_fields_config: external_exports.array(publicInvoiceWireCustomFieldDefinitionSchema).nullable().optional(),
2106
- delivery_instruction: external_exports.unknown().optional(),
2107
- delivery_instruction_config: external_exports.unknown().optional(),
2108
- delivery_instruction_label: external_exports.unknown().optional(),
2109
- delivery_text: external_exports.string().nullable().optional(),
2110
- // The seller's own wording for this product's delivery, published by
2111
- // `InvoiceDataLoader` on every catalog-backed line. This schema strips what
2112
- // it does not declare, so an undeclared field never reaches the buyer's page
2113
- // however faithfully the API emits it — the four below are what the
2114
- // completion page and the awaiting-stock card read.
2115
- label_singular: external_exports.string().nullable().optional(),
2116
- label_plural: external_exports.string().nullable().optional(),
2117
- thank_you_message: external_exports.string().nullable().optional(),
2118
- out_of_stock_message: external_exports.string().nullable().optional(),
2119
- // Delivery wait as a bare COUNT. `0` and `null` both mean instant (the
2120
- // digital-goods default); a positive value is a "within N <unit>" promise.
2121
- delivery_time: external_exports.number().nullable().optional(),
2122
- // The unit `delivery_time` is counted in. The column is NOT NULL with a
2123
- // `days` default — which is what every row meant before the unit existed — so
2124
- // a live producer always sends one. It stays optional here because this
2125
- // schema also describes payment-link and other synthesized products that
2126
- // carry no catalog row, and those quote no wait at all.
2127
- delivery_time_unit: external_exports.enum(["minutes", "hours", "days"]).nullable().optional(),
2128
- description: external_exports.string().nullable(),
2129
- discount_percent: external_exports.number().optional(),
2130
- discount_display: external_exports.number().optional(),
2131
- discounted_total_display: external_exports.number().optional(),
2132
- gateways: external_exports.array(external_exports.string()).nullable().optional(),
2133
- id: external_exports.string().optional(),
2134
- image_name: external_exports.string().nullable(),
2135
- image_storage: external_exports.string().nullable().optional(),
2136
- line_item_id: external_exports.string().nullable().optional(),
2137
- pay_what_you_want: external_exports.boolean().optional(),
2138
- price: external_exports.string().optional(),
2139
- // Nullable ONLY for PWYW products (no fixed catalog price until the buyer
2140
- // chooses one) — enforced by requirePriceDisplayUnlessPayWhatYouWant below,
2141
- // so a fixed-price producer dropping the field still fails fast.
2142
- price_display: stringOrNumberSchema.nullable(),
2143
- quantity: external_exports.number(),
2144
- quantity_max: external_exports.number().optional(),
2145
- quantity_min: external_exports.number().optional(),
2146
- recurring_interval: external_exports.string().nullable().optional(),
2147
- recurring_interval_count: external_exports.number().nullable().optional(),
2148
- redirect_link: external_exports.string().nullable().optional(),
2149
- redirect_time: external_exports.number().nullable().optional(),
2150
- service_text: external_exports.string().nullable().optional(),
2151
- setup_cost: external_exports.string().optional(),
2152
- slug: external_exports.string().nullable().optional(),
2153
- storefront_url: external_exports.string().nullable().optional(),
2154
- status: external_exports.string().nullable().optional(),
2155
- subtype: external_exports.string().nullable().optional(),
2156
- terms_of_service: external_exports.string().nullable().optional(),
2157
- title: external_exports.string(),
2158
- total: external_exports.number(),
2159
- trial_period: external_exports.number().nullable().optional(),
2160
- type: external_exports.string().nullable().optional(),
2161
- uniqid: external_exports.string().nullable(),
2162
- unit_price: external_exports.number().optional(),
2163
- unit_price_display: stringOrNumberSchema.optional(),
2164
- unit_quantity: external_exports.number(),
2165
- updated_at: external_exports.string().optional(),
2166
- variant_title: external_exports.string().nullable().optional(),
2167
- video_link: external_exports.string().nullable().optional(),
2168
- volume_discounts: external_exports.unknown().optional(),
2169
- /** The SHORT replacement/refund eligibility window, in SECONDS. */
2170
- warranty_days: external_exports.number().nullable().optional(),
2171
- warranty_text: external_exports.string().nullable().optional(),
2172
- /**
2173
- * Long-term per-purchase coverage in DAYS, or null/absent for none.
2174
- *
2175
- * Deliberately a second field beside `warranty_days` rather than a rename:
2176
- * they are different features that a similar name keeps conflating.
2177
- * `warranty_days` is the seconds-long refund window; this is
2178
- * Before payment this is the current `products.warranty_period_days` value.
2179
- * After completion it is the invoice line's activated
2180
- * `order_item_warranties.period_days` snapshot, so later product edits cannot
2181
- * rewrite the buyer's receipt. Buyer surfaces read THIS one — reading
2182
- * `warranty_days` would print a seconds count as a day count.
2183
- */
2184
- warranty_period_days: external_exports.number().nullable().optional()
2185
- });
2186
- var gatewayFeePreviewSchema = external_exports.object({
2187
- amount: external_exports.number(),
2188
- gateway: external_exports.string()
2189
- });
2190
- var checkoutTippingSchema = external_exports.object({
2191
- custom_tip_enabled: external_exports.boolean().optional(),
2192
- enabled: external_exports.boolean(),
2193
- preset_percentages: external_exports.array(external_exports.number()).optional()
2194
- });
2195
- var customerBalanceSchema = external_exports.object({
2196
- allow_partial_payment: external_exports.boolean().optional(),
2197
- auto_apply: external_exports.boolean().optional(),
2198
- available: external_exports.string(),
2199
- currency: external_exports.string()
2200
- });
2201
- var voucherStoreCreditSchema = external_exports.object({
2202
- gateway: external_exports.literal("REWARBLE"),
2203
- amount: external_exports.string(),
2204
- currency: external_exports.string()
2205
- });
2206
- var pricingBreakdownSchema = external_exports.object({
2207
- affiliate_code: external_exports.string().nullable().optional(),
2208
- already_paid: stringOrNumberSchema.nullable().optional(),
2209
- amount_due: stringOrNumberSchema.nullable().optional(),
2210
- currency: external_exports.string().nullable().optional(),
2211
- discount: stringOrNumberSchema.nullable().optional(),
2212
- volume_discount: stringOrNumberSchema.nullable().optional(),
2213
- discount_label: external_exports.string().nullable().optional(),
2214
- gateway_fee: stringOrNumberSchema.nullable().optional(),
2215
- platform_fee: stringOrNumberSchema.nullable().optional(),
2216
- processing_fee: stringOrNumberSchema.nullable().optional(),
2217
- subtotal: stringOrNumberSchema.nullable().optional(),
2218
- tip: stringOrNumberSchema.nullable().optional(),
2219
- total: stringOrNumberSchema.nullable().optional()
2220
- });
2221
- var pollingSchema = external_exports.object({
2222
- active: external_exports.boolean(),
2223
- expires_at: external_exports.string()
2224
- });
2225
- var supportSchema = external_exports.object({
2226
- faq_url: external_exports.string().nullable(),
2227
- support_email: external_exports.string().nullable(),
2228
- support_url: external_exports.string().nullable(),
2229
- telegram_invite: external_exports.string().nullable(),
2230
- /** shop_social_links.discord — the checkout renders an optional join row from it. */
2231
- discord_invite_url: external_exports.string().nullable()
2232
- });
2233
- var gatewayEligibilityStateSchema = external_exports.object({
2234
- currency: external_exports.string().nullable().optional(),
2235
- enabled: external_exports.boolean(),
2236
- max_amount: external_exports.number().nullable().optional(),
2237
- min_amount: external_exports.number().nullable().optional(),
2238
- reason: external_exports.enum(["BELOW_MIN_AMOUNT", "ABOVE_MAX_AMOUNT"]).nullable().optional()
2239
- });
2240
- var deliverySchema = external_exports.object({
2241
- delivered_items: external_exports.array(external_exports.unknown()),
2242
- downloads: external_exports.array(external_exports.unknown()),
2243
- external_urls: external_exports.array(external_exports.unknown()),
2244
- license_keys: external_exports.array(external_exports.unknown()),
2245
- tracking_codes: external_exports.array(external_exports.unknown())
2246
- });
2247
- var underpaymentSchema = external_exports.object({
2248
- active: external_exports.boolean().optional(),
2249
- attempt_id: external_exports.string().optional(),
2250
- buyer_actionable: external_exports.boolean().nullable().optional(),
2251
- crypto_currency: external_exports.string().nullable().optional(),
2252
- currency: external_exports.string().optional(),
2253
- expected_amount: external_exports.string().nullable().optional(),
2254
- expected_crypto_amount: external_exports.string().nullable().optional(),
2255
- last_checked_at: external_exports.string().optional(),
2256
- missing_amount: external_exports.string().nullable().optional(),
2257
- missing_crypto_amount: external_exports.string().nullable().optional(),
2258
- // InvoiceEnricher.applyUnderpayment serializes providerReference ?? null —
2259
- // an active underpayment without a provider reference is a valid payload.
2260
- provider_reference: external_exports.string().nullable().optional(),
2261
- provider_status: external_exports.string().nullable().optional(),
2262
- received_amount: external_exports.string().nullable().optional(),
2263
- /** Display-only fiat estimate for the crypto amount observed on-chain. */
2264
- received_fiat_estimate: external_exports.string().nullable().optional(),
2265
- received_crypto_amount: external_exports.string().nullable().optional(),
2266
- shortfall_percent: external_exports.string().nullable().optional(),
2267
- buyer_action_reason: external_exports.enum([
2268
- "BELOW_MINIMUM_COLLECTIBLE",
2269
- "PROVIDER_SESSION_CLOSED",
2270
- "PROVIDER_TOP_UP_UNSUPPORTED",
2271
- "MERCHANT_REVIEW_REQUIRED"
2272
- ]).nullable().optional()
2273
- });
2274
- var statusHistoryEntrySchema = external_exports.object({
2275
- created_at: external_exports.string().nullable(),
2276
- details: external_exports.string().nullable(),
2277
- id: external_exports.string(),
2278
- invoice_id: external_exports.string(),
2279
- status: external_exports.string().nullable()
2280
- });
2281
- var paymentMethodOverrideSchema = external_exports.object({
2282
- billing_address_mode: external_exports.enum(["INHERIT", "REQUIRED", "DISABLED"]).optional(),
2283
- button_label: external_exports.string().nullable().optional(),
2284
- display_name: external_exports.string().nullable().optional(),
2285
- gateway: external_exports.string(),
2286
- hide_provider_attribution: external_exports.boolean().optional(),
2287
- icon_url: external_exports.string().nullable().optional(),
2288
- terms_mode: external_exports.enum(["INHERIT", "REQUIRED", "DISABLED"]).optional()
2289
- });
2290
- var customGatewaySchema = external_exports.object({
2291
- custom_fields: external_exports.array(external_exports.unknown()).optional(),
2292
- description: external_exports.string().nullable(),
2293
- display_order: external_exports.number(),
2294
- icon_preset: external_exports.string().nullable(),
2295
- icon_url: external_exports.string().nullable(),
2296
- id: external_exports.string(),
2297
- instructions: external_exports.string().nullable(),
2298
- is_active: external_exports.boolean().optional(),
2299
- name: external_exports.string(),
2300
- payment_type: external_exports.enum(["INSTRUCTIONS", "REDIRECT"]),
2301
- redirect_url: external_exports.string().nullable(),
2302
- require_proof: external_exports.boolean(),
2303
- // Which fields the buyer has to fill in when proof is required. Optional so
2304
- // an older cached payload stays parseable; the checkout falls back to BOTH.
2305
- proof_type: external_exports.enum(["TEXT", "IMAGE", "BOTH"]).optional(),
2306
- void_after_hours: external_exports.number()
2307
- });
2308
- var externalPaymentAdapterSchema = external_exports.object({
2309
- description: external_exports.string().nullable(),
2310
- display_order: external_exports.number(),
2311
- icon_url: external_exports.string().nullable(),
2312
- id: external_exports.string(),
2313
- name: external_exports.string()
2314
- });
2315
- var buyerIdentitySchema = external_exports.object({
2316
- email: external_exports.object({
2317
- masked: external_exports.string().nullable(),
2318
- persisted: external_exports.boolean(),
2319
- required_for_provider_session: external_exports.boolean().optional()
2320
- })
2321
- });
2322
- var cryptoTransactionSchema = external_exports.object({
2323
- amount: external_exports.string().nullable(),
2324
- confirmations: external_exports.number().nullable(),
2325
- created_at: external_exports.number().optional(),
2326
- hash: external_exports.string().nullable(),
2327
- status: external_exports.string().optional()
2328
- });
2329
- var voidTimeSchema = external_exports.object({
2330
- conf: external_exports.object({
2331
- partial: external_exports.number().optional(),
2332
- void: external_exports.number(),
2333
- waiting_for_confirmations: external_exports.number().optional()
2334
- }),
2335
- gateways: external_exports.array(external_exports.string())
2336
- });
2337
- var invoicePaymentSessionStateSchema = external_exports.object({
2338
- invoice_payable: external_exports.boolean(),
2339
- resumable: external_exports.boolean(),
2340
- resumable_gateway: external_exports.string().nullable(),
2341
- gateway: external_exports.string().nullable().optional(),
2342
- crypto_gateway: external_exports.string().nullable().optional(),
2343
- processor: external_exports.string().nullable().optional(),
2344
- flow_type: external_exports.string().nullable().optional(),
2345
- status: external_exports.string().nullable().optional(),
2346
- provider_reference: external_exports.string().nullable().optional(),
2347
- provider_reference_type: external_exports.string().nullable().optional(),
2348
- capture_id: external_exports.string().nullable().optional(),
2349
- manual_confirmation_pending: external_exports.boolean().optional(),
2350
- payment_method_switch_locked: external_exports.boolean().optional(),
2351
- awaiting_payment_method: external_exports.boolean().optional(),
2352
- started_at: external_exports.number().nullable().optional(),
2353
- completed_at: external_exports.number().nullable().optional()
2354
- });
2355
- var embedInvoicePaymentSessionStateSchema = invoicePaymentSessionStateSchema.extend({
2356
- gateway: external_exports.string().nullable(),
2357
- flow_type: external_exports.string().nullable(),
2358
- status: external_exports.string().nullable(),
2359
- manual_confirmation_pending: external_exports.boolean(),
2360
- payment_method_switch_locked: external_exports.boolean()
2361
- });
2362
- var checkoutStyleStateSchema = external_exports.object({
2363
- theme_id: external_exports.string().nullable(),
2364
- surface: external_exports.enum(["checkout", "payment_link", "embed"]),
2365
- token_schema_version: external_exports.literal(1),
2366
- revision: external_exports.number(),
2367
- tokens: unknownRecordSchema,
2368
- custom_css: external_exports.string(),
2369
- css_variables: external_exports.record(external_exports.string(), external_exports.string())
2370
- });
2371
- var paymentRescueSchema = external_exports.object({
2372
- link_id: external_exports.string(),
2373
- source_gateway: external_exports.string().nullable(),
2374
- invoice_uniqid: external_exports.string(),
2375
- deprioritize_source_gateway: external_exports.boolean().optional()
2376
- });
2377
- var discordIntegrationStateSchema = external_exports.object({
2378
- enabled: external_exports.boolean(),
2379
- required: external_exports.boolean(),
2380
- connected: external_exports.boolean()
2381
- });
2382
- var shopPaymentGatewayFeeSchema = external_exports.object({
2383
- gateway: external_exports.string().nullable().optional(),
2384
- active_type: external_exports.string().nullable().optional(),
2385
- percent_amount: stringOrNumberSchema.nullable().optional(),
2386
- fixed_amount: stringOrNumberSchema.nullable().optional(),
2387
- fixed_currency: external_exports.string().nullable().optional()
2388
- });
2389
- var invoiceRewardItemSchema = external_exports.object({
2390
- id: external_exports.string(),
2391
- type: external_exports.enum(["WALLET_CREDIT", "COUPON"]),
2392
- status: external_exports.enum(["PENDING", "FULFILLED", "FAILED", "REVOKED"]),
2393
- amount: external_exports.string().nullable(),
2394
- currency: external_exports.string().nullable(),
2395
- coupon_code: external_exports.string().nullable(),
2396
- reason: external_exports.enum([
2397
- "ORDER_COMPLETED",
2398
- "ORDER_COUNT_REACHED",
2399
- "SPEND_AMOUNT_REACHED",
2400
- "POSITIVE_REVIEW_LEFT",
2401
- "FIRST_PURCHASE_COMPLETED"
2402
- ]),
2403
- trigger_reference_type: external_exports.string(),
2404
- trigger_reference_id: external_exports.string(),
2405
- created_at: external_exports.number(),
2406
- fulfilled_at: external_exports.number().nullable(),
2407
- expires_at: external_exports.number().nullable()
2408
- });
2409
- var invoiceRewardsSchema = external_exports.object({
2410
- summary: external_exports.object({
2411
- available: external_exports.string(),
2412
- pending: external_exports.string(),
2413
- lifetime_earned: external_exports.string(),
2414
- redeemed: external_exports.string().nullable(),
2415
- currency: external_exports.string()
2416
- }),
2417
- activity: external_exports.array(invoiceRewardItemSchema),
2418
- earned_after_invoice: external_exports.array(invoiceRewardItemSchema),
2419
- pending_after_invoice: external_exports.array(invoiceRewardItemSchema)
2420
- });
2421
- var publicInvoiceWireSchema = external_exports.object({
2422
- already_paid_amount: stringOrNumberSchema.nullable().optional(),
2423
- apm_method: external_exports.string().nullable().optional(),
2424
- blockchain: external_exports.string().nullable(),
2425
- buyer_identity: buyerIdentitySchema.nullable().optional(),
2426
- checkout_tipping: checkoutTippingSchema.nullable().optional(),
2427
- country_regulations: external_exports.string().nullable().optional(),
2428
- /**
2429
- * THAT a coupon is on the order — never WHICH one.
2430
- *
2431
- * The redeemed code has no key on this contract, and none is coming back to
2432
- * it: this is the wire of the anonymous invoice read, whose only credential
2433
- * is knowledge of the invoice URL. Coupon codes are merchant-authored and
2434
- * routinely private (campaign, influencer, single-buyer codes), so naming one
2435
- * to an unproven reader discloses the merchant's code. The producer withholds
2436
- * it too — `coupon_code`, `coupon_id` and the raw `discount_breakdown` are all
2437
- * outside `PUBLIC_INVOICE_FIELDS` — so the summary prints "Coupon −$9.80".
2438
- *
2439
- * A buyer-bound lane may name the coupon, but only from a source that proves
2440
- * the reader: this schema is not it.
2441
- */
2442
- coupon_applied: external_exports.boolean().optional(),
2443
- /**
2444
- * How much of the order the applied coupon reaches, in lines — null when it
2445
- * covers every line or no coupon is on the order. Counts only, so nothing
2446
- * about which products the merchant scoped the code to leaves the server.
2447
- * The discount line already shows the reduced amount; this is what lets the
2448
- * checkout say "applied to 1 of 3 items" instead of leaving a smaller-than-
2449
- * advertised discount to read as a broken code.
2450
- */
2451
- coupon_scope: external_exports.object({
2452
- eligible_line_count: external_exports.number(),
2453
- line_count: external_exports.number()
2454
- }).nullable().optional(),
2455
- crypto_mode: external_exports.string().nullable().optional(),
2456
- crypto_transactions: external_exports.array(cryptoTransactionSchema).optional(),
2457
- currency: external_exports.string().nullable(),
2458
- custom_fields: unknownRecordSchema.nullable().optional(),
2459
- custom_gateways: external_exports.array(customGatewaySchema).optional(),
2460
- external_payment_adapters: external_exports.array(externalPaymentAdapterSchema).optional(),
2461
- customer_balance: customerBalanceSchema.nullable().optional(),
2462
- voucher_store_credit: voucherStoreCreditSchema.nullable().optional(),
2463
- customer_email_masked: external_exports.string().nullable().optional(),
2464
- dark_mode: external_exports.union([external_exports.literal(0), external_exports.literal(1)]).optional(),
2465
- delivery: deliverySchema.optional(),
2466
- delivery_info: deliverySchema.optional(),
2467
- discount: external_exports.number().optional(),
2468
- discount_display: stringOrNumberSchema.optional(),
2469
- eligible_gateways: external_exports.array(external_exports.string()).optional(),
2470
- environment: external_exports.string().optional(),
2471
- fee_breakdown: unknownRecordSchema.nullable().optional(),
2472
- gateway: external_exports.string().nullable(),
2473
- gateway_data: unknownRecordSchema.nullable().optional(),
2474
- gateway_eligibility: external_exports.record(external_exports.string(), gatewayEligibilityStateSchema).optional(),
2475
- gateway_fee_previews: external_exports.array(gatewayFeePreviewSchema).optional(),
2476
- gateways_available: external_exports.array(external_exports.string()),
2477
- license: external_exports.union([unknownRecordSchema, external_exports.literal(false)]).optional(),
2478
- name: external_exports.string().nullable(),
2479
- paddle_token: external_exports.string().nullable().optional(),
2480
- paddle_transaction_id: external_exports.unknown().optional(),
2481
- payment_link_id: external_exports.string().nullable().optional(),
2482
- payment_method_overrides: external_exports.array(paymentMethodOverrideSchema).optional(),
2483
- pending_custom_fields: pendingPostPaymentCustomFieldsSchema.nullable().optional(),
2484
- payment_session_state: invoicePaymentSessionStateSchema.nullable().optional(),
2485
- polling: pollingSchema,
2486
- pricing_breakdown: pricingBreakdownSchema.nullable().optional(),
2487
- product: external_exports.array(
2488
- publicInvoiceWireProductSchema.superRefine(requirePriceDisplayUnlessPayWhatYouWant)
2489
- ).optional(),
2490
- quantity: external_exports.number().nullable(),
2491
- rates_snapshot: external_exports.record(external_exports.string(), external_exports.number()).optional(),
2492
- remaining_amount: stringOrNumberSchema.nullable().optional(),
2493
- selected_gateway: external_exports.string().nullable().optional(),
2494
- shop_checkout_ambient_color: external_exports.string().nullable().optional(),
2495
- shop_cloudflare_image_id: external_exports.string().nullable(),
2496
- shop_domain: external_exports.string().nullable().optional(),
2497
- shop_force_paypal_email_delivery: external_exports.union([external_exports.literal(0), external_exports.literal(1)]).optional(),
2498
- shop_image_name: external_exports.string().nullable(),
2499
- shop_image_storage: external_exports.string().nullable().optional(),
2500
- shop_payment_gateways_fees: external_exports.array(unknownRecordSchema).optional(),
2501
- shop_paypal_credit_card: external_exports.union([external_exports.literal(0), external_exports.literal(1)]).optional(),
2502
- shop_return_url: external_exports.string().nullable().optional(),
2503
- shop_slug: external_exports.string().nullable().optional(),
2504
- shop_terms_enabled: external_exports.boolean().optional(),
2505
- shop_terms_of_service: external_exports.string().nullable().optional(),
2506
- shop_terms_url: external_exports.string().nullable().optional(),
2507
- shop_privacy_policy_url: external_exports.string().nullable().optional(),
2508
- shop_refund_policy_url: external_exports.string().nullable().optional(),
2509
- shop_walletconnect_id: external_exports.string().nullable().optional(),
2510
- status: external_exports.string().nullable(),
2511
- status_history: external_exports.array(statusHistoryEntrySchema).optional(),
2512
- status_history_legacy: external_exports.array(statusHistoryEntrySchema).optional(),
2513
- status_history_raw: external_exports.array(statusHistoryEntrySchema).optional(),
2514
- stripe_publishable_key: external_exports.string().nullable().optional(),
2515
- stripe_user_id: external_exports.string().nullable().optional(),
2516
- subtotal: external_exports.number().optional(),
2517
- subtype: external_exports.string().nullable().optional(),
2518
- support: supportSchema.optional(),
2519
- theme: external_exports.string().optional(),
2520
- tip_amount: stringOrNumberSchema.nullable().optional(),
2521
- tip_amount_display: stringOrNumberSchema.nullable().optional(),
2522
- tip_metadata: external_exports.unknown().optional(),
2523
- total: stringOrNumberSchema,
2524
- total_conversions: unknownRecordSchema.optional(),
2525
- total_display: stringOrNumberSchema,
2526
- type: external_exports.string().nullable(),
2527
- underpayment: underpaymentSchema.nullable().optional(),
2528
- underpayment_status: external_exports.string().nullable().optional(),
2529
- uniqid: external_exports.string(),
2530
- void_times: external_exports.array(voidTimeSchema).optional(),
2531
- // PUBLIC_INVOICE_FIELDS entries not exercised by the golden fixtures.
2532
- addons: external_exports.unknown().optional(),
2533
- affiliate_data: external_exports.unknown().optional(),
2534
- affiliate_revenue_customer_id: external_exports.string().nullable().optional(),
2535
- bill_info: external_exports.unknown().optional(),
2536
- binance_checkout_url: external_exports.string().nullable().optional(),
2537
- binance_invoice_id: external_exports.string().nullable().optional(),
2538
- binance_qrcode: external_exports.string().nullable().optional(),
2539
- bundle_config: external_exports.unknown().optional(),
2540
- bundles: external_exports.unknown().optional(),
2541
- cashapp_cashtag: external_exports.string().nullable().optional(),
2542
- cashapp_note: external_exports.string().nullable().optional(),
2543
- cashapp_qrcode: external_exports.string().nullable().optional(),
2544
- created_at: external_exports.string().optional(),
2545
- crypto_address: external_exports.string().nullable().optional(),
2546
- // null when the invoice has no crypto leg (card, PayPal, unselected gateway).
2547
- crypto_amount: stringOrNumberSchema.nullable().optional(),
2548
- crypto_confirmations_needed: external_exports.number().nullable().optional(),
2549
- crypto_exchange_rate: stringOrNumberSchema.nullable().optional(),
2550
- crypto_received: stringOrNumberSchema.nullable().optional(),
2551
- crypto_uri: external_exports.string().nullable().optional(),
2552
- developer_invoice: external_exports.unknown().optional(),
2553
- developer_return_url: external_exports.string().nullable().optional(),
2554
- developer_title: external_exports.string().nullable().optional(),
2555
- exchange_rate: stringOrNumberSchema.nullable().optional(),
2556
- external_order_id: external_exports.string().nullable().optional(),
2557
- lex_order_id: external_exports.string().nullable().optional(),
2558
- lex_payment_method: external_exports.string().nullable().optional(),
2559
- paydash_payment_id: external_exports.string().nullable().optional(),
2560
- paypal_apm: external_exports.string().nullable().optional(),
2561
- paypal_email_delivery: external_exports.boolean().optional(),
2562
- paypal_fee: stringOrNumberSchema.nullable().optional(),
2563
- paypal_order_id: external_exports.string().nullable().optional(),
2564
- paypal_payer_email: external_exports.string().nullable().optional(),
2565
- paypal_subscription_id: external_exports.string().nullable().optional(),
2566
- paypal_subscription_link: external_exports.string().nullable().optional(),
2567
- perfectmoney_id: external_exports.string().nullable().optional(),
2568
- product_addons: external_exports.unknown().optional(),
2569
- product_id: external_exports.string().nullable().optional(),
2570
- product_title: external_exports.string().nullable().optional(),
2571
- product_type: external_exports.string().nullable().optional(),
2572
- product_variants: external_exports.unknown().optional(),
2573
- recurring_billing_id: external_exports.string().nullable().optional(),
2574
- skrill_link: external_exports.string().nullable().optional(),
2575
- skrill_sid: external_exports.string().nullable().optional(),
2576
- status_details: external_exports.unknown().optional(),
2577
- stripe_apm: external_exports.string().nullable().optional(),
2578
- stripe_client_secret: external_exports.string().nullable().optional(),
2579
- stripe_id: external_exports.string().nullable().optional(),
2580
- stripe_price_id: external_exports.string().nullable().optional(),
2581
- subscription: external_exports.unknown().optional(),
2582
- subscription_id: external_exports.string().nullable().optional(),
2583
- sumup_id: external_exports.string().nullable().optional(),
2584
- telegram_stars_payment_link: external_exports.string().nullable().optional(),
2585
- telegram_stars_payment_note: external_exports.string().nullable().optional(),
2586
- updated_at: external_exports.string().optional(),
2587
- virtual_payments_id: external_exports.string().nullable().optional(),
2588
- void_details: external_exports.unknown().optional(),
2589
- // EU right-of-withdrawal consent (§ 356 Abs. 5 BGB / CRD Art. 16(m)).
2590
- // `withdrawal_consent_required` is the server's verdict for THIS invoice
2591
- // (shop opt-in + buyer country + digital deliverables, renewals exempt) and is
2592
- // REQUIRED on the wire: a missing verdict is a producer bug, not a licence for
2593
- // the checkout to assume `false` and show a pay button the backend refuses.
2594
- // The other two are the stored proof — always present, null until the buyer
2595
- // confirms. The checkout gates on these three and never infers the
2596
- // requirement itself.
2597
- withdrawal_consent_at: external_exports.string().nullable(),
2598
- withdrawal_consent_required: external_exports.boolean(),
2599
- withdrawal_consent_text_version: external_exports.string().nullable()
2600
- }).catchall(external_exports.unknown());
2601
- var embedCheckoutProductWireSchema = publicInvoiceWireProductSchema.extend({
2602
- available_addons: external_exports.array(publicInvoiceWireAvailableAddonSchema).optional(),
2603
- delivery_instructions: external_exports.string().nullable().optional(),
2604
- delivery_instructions_config: external_exports.object({
2605
- enabled: external_exports.literal(true),
2606
- required: external_exports.boolean(),
2607
- label: external_exports.string(),
2608
- placeholder: external_exports.string().nullable().optional(),
2609
- help_text: external_exports.string().nullable().optional(),
2610
- max_length: external_exports.number()
2611
- }).nullable().optional(),
2612
- delivery_instructions_label: external_exports.string().nullable().optional(),
2613
- image_url: external_exports.string().nullable().optional(),
2614
- redirectLink: external_exports.string().nullable().optional(),
2615
- redirectTime: external_exports.number().nullable().optional(),
2616
- serviceText: external_exports.string().nullable().optional()
2617
- }).superRefine(requirePriceDisplayUnlessPayWhatYouWant);
2618
- var embedCheckoutInvoiceWireSchema = publicInvoiceWireSchema.extend({
2619
- uniqid: external_exports.string().uuid(),
2620
- shop_id: external_exports.union([external_exports.string(), external_exports.number()]).nullable(),
2621
- customer_email: external_exports.string().nullable(),
2622
- checkout_style: checkoutStyleStateSchema.nullable().optional(),
2623
- coupon_id: external_exports.string().nullable().optional(),
2624
- // Merchant toggle (shop_commerce_settings.coupon_field_always_visible):
2625
- // render the coupon input expanded instead of behind the "Add coupon code"
2626
- // trigger. Presentation only — coupon validation is unchanged.
2627
- //
2628
- // Required, not optional: the public producer (`InvoiceEnricher`) always
2629
- // resolves this to a boolean, so an absent field means the producer broke,
2630
- // not that the merchant left it unset. Failing here beats the checkout
2631
- // quietly rendering the collapsed default for a shop that turned it on.
2632
- coupon_field_always_visible: external_exports.boolean(),
2633
- discount_breakdown: unknownRecordSchema.nullable().optional(),
2634
- affiliate_code: external_exports.string().nullable().optional(),
2635
- country: external_exports.string().nullable().optional(),
2636
- paypal_email: external_exports.string().nullable().optional(),
2637
- pandabase_available_payment_methods: external_exports.array(external_exports.string()).optional(),
2638
- payment_rescue: paymentRescueSchema.nullable().optional(),
2639
- discord_integration: discordIntegrationStateSchema.nullable().optional(),
2640
- payment_session_state: embedInvoicePaymentSessionStateSchema.nullable().optional(),
2641
- shop_payment_gateways_fees: external_exports.array(shopPaymentGatewayFeeSchema).optional(),
2642
- product: external_exports.array(embedCheckoutProductWireSchema).optional(),
2643
- products: external_exports.array(embedCheckoutProductWireSchema).optional(),
2644
- rewards: invoiceRewardsSchema.nullable().optional()
2645
- }).superRefine((invoice, context) => {
2646
- if (invoice.product === void 0 && invoice.products === void 0) {
2647
- context.addIssue({
2648
- code: "custom",
2649
- message: "Embed checkout invoice must include product or products.",
2650
- path: ["product"]
2651
- });
2652
- }
2653
- });
2654
- var embedCheckoutStartWireSchema = external_exports.object({
2655
- data: external_exports.object({
2656
- invoice: embedCheckoutInvoiceWireSchema,
2657
- invoice_url: external_exports.string().nullable(),
2658
- checkout_url: external_exports.string().nullable(),
2659
- selected_gateway: external_exports.string().nullable(),
2660
- session_started: external_exports.boolean(),
2661
- payment_session: embedPaymentSessionWireSchema.nullable(),
2662
- completion_access_grant: external_exports.string().nullable()
2663
- }).passthrough()
2664
- }).passthrough();
2665
-
2666
- // ../contracts/src/catalog-unit-price.ts
2667
- var CATALOG_UNIT_PRICE_DECIMAL_PLACES = 3;
2668
- var PAYABLE_AMOUNT_DECIMAL_PLACES = 2;
2669
- var CATALOG_UNIT_PRICE_FORMAT_OPTIONS = {
2670
- maximumFractionDigits: CATALOG_UNIT_PRICE_DECIMAL_PLACES
2671
- };
2672
- function roundPayableAmount(value) {
2673
- if (!Number.isFinite(value)) return value;
2674
- const sign = value < 0 ? -1 : 1;
2675
- const shifted = shiftDecimal(Math.abs(value), PAYABLE_AMOUNT_DECIMAL_PLACES);
2676
- const rounded = Math.round(shifted);
2677
- if (rounded === 0) return 0;
2678
- return sign * shiftDecimal(rounded, -PAYABLE_AMOUNT_DECIMAL_PLACES);
2679
- }
2680
- function shiftDecimal(value, places) {
2681
- const [coefficient, currentExponent = "0"] = String(value).split("e");
2682
- return Number(`${coefficient}e${Number(currentExponent) + places}`);
2683
- }
2684
-
2685
- // ../contracts/src/index.ts
542
+ // ../contracts/src/api-client.ts
2686
543
  function createApiClient(baseUrl, token) {
2687
544
  return createClient({
2688
545
  baseUrl,
@@ -2754,7 +611,8 @@ function initConfig(storeSlug, options) {
2754
611
  locale: options?.locale,
2755
612
  currency,
2756
613
  apiBaseUrl: options?.apiBaseUrl ?? DEFAULT_API_BASE_URL,
2757
- checkoutBaseUrl: options?.checkoutBaseUrl ?? DEFAULT_CHECKOUT_BASE_URL
614
+ checkoutBaseUrl: options?.checkoutBaseUrl ?? DEFAULT_CHECKOUT_BASE_URL,
615
+ preview: options?.preview === true
2758
616
  };
2759
617
  resetTypedClient();
2760
618
  return currentConfig;
@@ -3183,6 +1041,25 @@ function resolveStorefrontSocialLinks(store) {
3183
1041
  };
3184
1042
  }
3185
1043
 
1044
+ // ../contracts/src/catalog-unit-price.ts
1045
+ var CATALOG_UNIT_PRICE_DECIMAL_PLACES = 3;
1046
+ var PAYABLE_AMOUNT_DECIMAL_PLACES = 2;
1047
+ var CATALOG_UNIT_PRICE_FORMAT_OPTIONS = {
1048
+ maximumFractionDigits: CATALOG_UNIT_PRICE_DECIMAL_PLACES
1049
+ };
1050
+ function roundPayableAmount(value) {
1051
+ if (!Number.isFinite(value)) return value;
1052
+ const sign = value < 0 ? -1 : 1;
1053
+ const shifted = shiftDecimal(Math.abs(value), PAYABLE_AMOUNT_DECIMAL_PLACES);
1054
+ const rounded = Math.round(shifted);
1055
+ if (rounded === 0) return 0;
1056
+ return sign * shiftDecimal(rounded, -PAYABLE_AMOUNT_DECIMAL_PLACES);
1057
+ }
1058
+ function shiftDecimal(value, places) {
1059
+ const [coefficient, currentExponent = "0"] = String(value).split("e");
1060
+ return Number(`${coefficient}e${Number(currentExponent) + places}`);
1061
+ }
1062
+
3186
1063
  // ../sdk/src/core/cache.ts
3187
1064
  var cache = /* @__PURE__ */ new Map();
3188
1065
  var pending = /* @__PURE__ */ new Map();
@@ -3292,25 +1169,25 @@ var STORAGE_PREFIX = "shoppex_";
3292
1169
  function getKey(key) {
3293
1170
  return `${STORAGE_PREFIX}${key}`;
3294
1171
  }
3295
- function getItem(key) {
1172
+ function getItem(key, area = "localStorage") {
3296
1173
  try {
3297
- const item = localStorage.getItem(getKey(key));
1174
+ const item = globalThis[area].getItem(storefrontStorageKey(getKey(key)));
3298
1175
  if (!item) return null;
3299
1176
  return JSON.parse(item);
3300
1177
  } catch {
3301
1178
  return null;
3302
1179
  }
3303
1180
  }
3304
- function setItem(key, value) {
1181
+ function setItem(key, value, area = "localStorage") {
3305
1182
  try {
3306
- localStorage.setItem(getKey(key), JSON.stringify(value));
1183
+ globalThis[area].setItem(storefrontStorageKey(getKey(key)), JSON.stringify(value));
3307
1184
  } catch {
3308
- console.warn("[shoppex] Failed to save to localStorage");
1185
+ console.warn(`[shoppex] Failed to save to ${area}`);
3309
1186
  }
3310
1187
  }
3311
- function removeItem(key) {
1188
+ function removeItem(key, area = "localStorage") {
3312
1189
  try {
3313
- localStorage.removeItem(getKey(key));
1190
+ globalThis[area].removeItem(storefrontStorageKey(getKey(key)));
3314
1191
  } catch {
3315
1192
  }
3316
1193
  }
@@ -3406,9 +1283,9 @@ var MAX_RETRIES = 2;
3406
1283
  async function sleep(ms) {
3407
1284
  return new Promise((resolve) => setTimeout(resolve, ms));
3408
1285
  }
3409
- function appendQueryParam(url2, key, value) {
3410
- const separator = url2.includes("?") ? "&" : "?";
3411
- return `${url2}${separator}${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
1286
+ function appendQueryParam(url, key, value) {
1287
+ const separator = url.includes("?") ? "&" : "?";
1288
+ return `${url}${separator}${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
3412
1289
  }
3413
1290
  function forwardFailure(response, overrides = {}) {
3414
1291
  const { success: _success, data: _data, ...failure } = response;
@@ -3435,7 +1312,7 @@ async function request(endpoint, options = {}) {
3435
1312
  const retryCount = retries ?? (method === "GET" ? MAX_RETRIES : 0);
3436
1313
  const apiBaseUrl = baseUrl ?? config?.apiBaseUrl ?? "";
3437
1314
  const buyerCurrency = pricesInBuyerCurrency ? resolveBuyerCurrency(audience?.currency) : null;
3438
- const url2 = buyerCurrency ? appendQueryParam(`${apiBaseUrl}${endpoint}`, "currency", buyerCurrency) : `${apiBaseUrl}${endpoint}`;
1315
+ const url = buyerCurrency ? appendQueryParam(`${apiBaseUrl}${endpoint}`, "currency", buyerCurrency) : `${apiBaseUrl}${endpoint}`;
3439
1316
  const headers = {
3440
1317
  "Content-Type": "application/json",
3441
1318
  Accept: "application/json",
@@ -3453,7 +1330,7 @@ async function request(endpoint, options = {}) {
3453
1330
  try {
3454
1331
  const controller = new AbortController();
3455
1332
  const timeoutId = setTimeout(() => controller.abort(), timeout);
3456
- const response = await fetch(url2, {
1333
+ const response = await fetch(url, {
3457
1334
  method,
3458
1335
  headers,
3459
1336
  body: body ? JSON.stringify(body) : void 0,
@@ -3526,7 +1403,7 @@ async function request(endpoint, options = {}) {
3526
1403
  };
3527
1404
  };
3528
1405
  const result = method === "GET" && cache2 && cache2.ttl > 0 ? await getOrFetch(
3529
- `${locale ?? ""}|${buyerCurrency ?? ""}|${cache2.key ?? `GET:${url2}`}`,
1406
+ `${locale ?? ""}|${buyerCurrency ?? ""}|${cache2.key ?? `GET:${url}`}`,
3530
1407
  executeRequest,
3531
1408
  { ttl: cache2.ttl, staleWhileRevalidate: cache2.staleWhileRevalidate },
3532
1409
  (value) => value.success
@@ -3539,29 +1416,15 @@ async function request(endpoint, options = {}) {
3539
1416
  message: result.message ?? failureForTelemetry.message,
3540
1417
  statusCode: failureForTelemetry.statusCode,
3541
1418
  attemptCount: retryCount + 1,
3542
- requestUrl: url2,
1419
+ requestUrl: url,
3543
1420
  responseReceived: failureForTelemetry.responseReceived
3544
1421
  });
3545
1422
  }
3546
1423
  return result;
3547
1424
  }
3548
1425
  async function parseResponsePayload(response) {
3549
- const responseWithOptionalMethods = response;
3550
- if (typeof responseWithOptionalMethods.text !== "function") {
3551
- if (typeof responseWithOptionalMethods.json === "function") {
3552
- try {
3553
- return {
3554
- data: await responseWithOptionalMethods.json(),
3555
- rawText: null
3556
- };
3557
- } catch {
3558
- return { data: null, rawText: null };
3559
- }
3560
- }
3561
- return { data: null, rawText: null };
3562
- }
3563
1426
  try {
3564
- const rawText = await responseWithOptionalMethods.text();
1427
+ const rawText = await response.text();
3565
1428
  if (!rawText) {
3566
1429
  return { data: null, rawText: null };
3567
1430
  }
@@ -3585,8 +1448,8 @@ function isDefinitiveHttpRefusal(payload) {
3585
1448
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
3586
1449
  return false;
3587
1450
  }
3588
- const record2 = payload;
3589
- return typeof record2.status === "number" && record2.status >= 400 && record2.status < 500;
1451
+ const record = payload;
1452
+ return typeof record.status === "number" && record.status >= 400 && record.status < 500;
3590
1453
  }
3591
1454
  function mapApiResponse(apiResponse) {
3592
1455
  if (apiResponse.status >= 200 && apiResponse.status < 300) {
@@ -3609,12 +1472,12 @@ function readErrorCodeFields(payload) {
3609
1472
  if (!payload || typeof payload !== "object") {
3610
1473
  return {};
3611
1474
  }
3612
- const record2 = payload;
3613
- const code = typeof record2.error_code === "string" && record2.error_code.length > 0 ? record2.error_code : null;
1475
+ const record = payload;
1476
+ const code = typeof record.error_code === "string" && record.error_code.length > 0 ? record.error_code : null;
3614
1477
  if (!code) {
3615
1478
  return {};
3616
1479
  }
3617
- const params = record2.error_params;
1480
+ const params = record.error_params;
3618
1481
  return {
3619
1482
  code,
3620
1483
  ...params && typeof params === "object" && !Array.isArray(params) ? { errorParams: params } : {}
@@ -3933,7 +1796,7 @@ function normalizeAffiliateCode(code) {
3933
1796
  function safeRead() {
3934
1797
  if (typeof window === "undefined") return null;
3935
1798
  try {
3936
- const raw = window.localStorage.getItem(STORAGE_KEY);
1799
+ const raw = window.localStorage.getItem(storefrontStorageKey(STORAGE_KEY));
3937
1800
  if (!raw) return null;
3938
1801
  const parsed = JSON.parse(raw);
3939
1802
  if (!parsed || typeof parsed.code !== "string" || typeof parsed.expiresAt !== "number") return null;
@@ -3945,7 +1808,7 @@ function safeRead() {
3945
1808
  function safeWrite(value) {
3946
1809
  if (typeof window === "undefined") return;
3947
1810
  try {
3948
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
1811
+ window.localStorage.setItem(storefrontStorageKey(STORAGE_KEY), JSON.stringify(value));
3949
1812
  } catch {
3950
1813
  }
3951
1814
  }
@@ -3981,7 +1844,7 @@ function getStoredAffiliate() {
3981
1844
  function clearAffiliateCode() {
3982
1845
  if (typeof window === "undefined") return;
3983
1846
  try {
3984
- window.localStorage.removeItem(STORAGE_KEY);
1847
+ window.localStorage.removeItem(storefrontStorageKey(STORAGE_KEY));
3985
1848
  } catch {
3986
1849
  }
3987
1850
  }
@@ -4010,7 +1873,7 @@ function createAffiliateSessionKey() {
4010
1873
  var inMemorySessionKey = null;
4011
1874
  function getAffiliateSessionKey() {
4012
1875
  try {
4013
- const stored = window.sessionStorage.getItem(SESSION_STORAGE_KEY);
1876
+ const stored = window.sessionStorage.getItem(storefrontStorageKey(SESSION_STORAGE_KEY));
4014
1877
  if (stored && stored.length >= 8 && stored.length <= 64) {
4015
1878
  return stored;
4016
1879
  }
@@ -4019,8 +1882,8 @@ function getAffiliateSessionKey() {
4019
1882
  }
4020
1883
  const key = createAffiliateSessionKey();
4021
1884
  try {
4022
- window.sessionStorage.setItem(SESSION_STORAGE_KEY, key);
4023
- if (window.sessionStorage.getItem(SESSION_STORAGE_KEY) === key) {
1885
+ window.sessionStorage.setItem(storefrontStorageKey(SESSION_STORAGE_KEY), key);
1886
+ if (window.sessionStorage.getItem(storefrontStorageKey(SESSION_STORAGE_KEY)) === key) {
4024
1887
  inMemorySessionKey = key;
4025
1888
  return key;
4026
1889
  }
@@ -4031,7 +1894,7 @@ function getAffiliateSessionKey() {
4031
1894
  }
4032
1895
  async function trackAffiliateEvent(eventType, options) {
4033
1896
  try {
4034
- if (typeof window === "undefined" || !isInitialized()) return;
1897
+ if (typeof window === "undefined" || !isInitialized() || getConfig().preview) return;
4035
1898
  const code = options === void 0 ? getAffiliateCode() : normalizeAffiliateCode(options.code);
4036
1899
  if (!code) return;
4037
1900
  const config = getConfig();
@@ -4138,8 +2001,8 @@ async function captureAffiliateFromUrl(param = "ref") {
4138
2001
  if (typeof window === "undefined") return null;
4139
2002
  let code = null;
4140
2003
  try {
4141
- const url2 = new URL(window.location.href);
4142
- const raw = url2.searchParams.get(param);
2004
+ const url = new URL(window.location.href);
2005
+ const raw = url.searchParams.get(param);
4143
2006
  code = raw ? raw.trim() : null;
4144
2007
  } catch {
4145
2008
  code = null;
@@ -4245,6 +2108,18 @@ var STORAGE_KEYS = {
4245
2108
  function getStorageKey(type) {
4246
2109
  return `${STORAGE_KEYS[type]}_${getConfig().storeSlug}`;
4247
2110
  }
2111
+ function cartStorageArea() {
2112
+ return getConfig().preview ? "sessionStorage" : "localStorage";
2113
+ }
2114
+ function readCartStorage(type) {
2115
+ return getItem(getStorageKey(type), cartStorageArea());
2116
+ }
2117
+ function writeCartStorage(type, value) {
2118
+ setItem(getStorageKey(type), value, cartStorageArea());
2119
+ }
2120
+ function removeCartStorage(type) {
2121
+ removeItem(getStorageKey(type), cartStorageArea());
2122
+ }
4248
2123
  function hashString2(value) {
4249
2124
  let hash = 2166136261;
4250
2125
  for (let i = 0; i < value.length; i += 1) {
@@ -4267,13 +2142,13 @@ function normalizeCouponCode(value) {
4267
2142
  return normalized ? normalized : null;
4268
2143
  }
4269
2144
  function getStoredCartCode() {
4270
- const raw = getItem(getStorageKey("coupon"));
2145
+ const raw = readCartStorage("coupon");
4271
2146
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
4272
2147
  return null;
4273
2148
  }
4274
- const record2 = raw;
4275
- const code = typeof record2.code === "string" ? normalizeCouponCode(record2.code) : null;
4276
- const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
2149
+ const record = raw;
2150
+ const code = typeof record.code === "string" ? normalizeCouponCode(record.code) : null;
2151
+ const source = record.source === "COUPON" || record.source === "AFFILIATE" ? record.source : null;
4277
2152
  return code && source ? { code, source } : null;
4278
2153
  }
4279
2154
  function normalizeCartItems(value) {
@@ -4281,10 +2156,10 @@ function normalizeCartItems(value) {
4281
2156
  const normalized = [];
4282
2157
  for (const entry of value) {
4283
2158
  if (!entry || typeof entry !== "object") continue;
4284
- const record2 = entry;
4285
- const productId = typeof record2.product_id === "string" ? record2.product_id.trim() : "";
4286
- const variantId = typeof record2.variant_id === "string" ? record2.variant_id.trim() : "";
4287
- const quantity = Number(record2.quantity);
2159
+ const record = entry;
2160
+ const productId = typeof record.product_id === "string" ? record.product_id.trim() : "";
2161
+ const variantId = typeof record.variant_id === "string" ? record.variant_id.trim() : "";
2162
+ const quantity = Number(record.quantity);
4288
2163
  if (!productId || !variantId || !Number.isFinite(quantity) || quantity < 1) {
4289
2164
  continue;
4290
2165
  }
@@ -4294,51 +2169,51 @@ function normalizeCartItems(value) {
4294
2169
  variant_id: variantId,
4295
2170
  quantity: Math.floor(quantity)
4296
2171
  };
4297
- if (typeof record2.price_variant_id === "string") {
4298
- item.price_variant_id = record2.price_variant_id;
2172
+ if (typeof record.price_variant_id === "string") {
2173
+ item.price_variant_id = record.price_variant_id;
4299
2174
  }
4300
- if (record2.price_data && typeof record2.price_data === "object") {
4301
- const priceData = record2.price_data;
2175
+ if (record.price_data && typeof record.price_data === "object") {
2176
+ const priceData = record.price_data;
4302
2177
  if (typeof priceData.unit_price === "number" && Number.isFinite(priceData.unit_price)) {
4303
2178
  item.price_data = { unit_price: priceData.unit_price };
4304
2179
  }
4305
2180
  }
4306
- if (typeof record2.pay_what_you_want_price === "number" && Number.isFinite(record2.pay_what_you_want_price)) {
4307
- item.pay_what_you_want_price = record2.pay_what_you_want_price;
2181
+ if (typeof record.pay_what_you_want_price === "number" && Number.isFinite(record.pay_what_you_want_price)) {
2182
+ item.pay_what_you_want_price = record.pay_what_you_want_price;
4308
2183
  }
4309
- if (Array.isArray(record2.addons)) {
4310
- item.addons = record2.addons;
2184
+ if (Array.isArray(record.addons)) {
2185
+ item.addons = record.addons;
4311
2186
  }
4312
- if (record2.custom_fields && typeof record2.custom_fields === "object" && !Array.isArray(record2.custom_fields)) {
4313
- item.custom_fields = record2.custom_fields;
2187
+ if (record.custom_fields && typeof record.custom_fields === "object" && !Array.isArray(record.custom_fields)) {
2188
+ item.custom_fields = record.custom_fields;
4314
2189
  }
4315
- if (typeof record2.title === "string") {
4316
- item.title = record2.title;
2190
+ if (typeof record.title === "string") {
2191
+ item.title = record.title;
4317
2192
  }
4318
- if (typeof record2.variant_title === "string") {
4319
- item.variant_title = record2.variant_title;
2193
+ if (typeof record.variant_title === "string") {
2194
+ item.variant_title = record.variant_title;
4320
2195
  }
4321
- if (typeof record2.image_url === "string") {
4322
- item.image_url = record2.image_url;
2196
+ if (typeof record.image_url === "string") {
2197
+ item.image_url = record.image_url;
4323
2198
  }
4324
- if (Array.isArray(record2.addon_labels)) {
4325
- item.addon_labels = record2.addon_labels.filter((label) => typeof label === "string");
2199
+ if (Array.isArray(record.addon_labels)) {
2200
+ item.addon_labels = record.addon_labels.filter((label) => typeof label === "string");
4326
2201
  }
4327
- if (typeof record2.max_quantity === "number" && Number.isFinite(record2.max_quantity)) {
4328
- item.max_quantity = record2.max_quantity;
2202
+ if (typeof record.max_quantity === "number" && Number.isFinite(record.max_quantity)) {
2203
+ item.max_quantity = record.max_quantity;
4329
2204
  }
4330
- if (typeof record2.min_quantity === "number" && Number.isFinite(record2.min_quantity)) {
4331
- item.min_quantity = record2.min_quantity;
2205
+ if (typeof record.min_quantity === "number" && Number.isFinite(record.min_quantity)) {
2206
+ item.min_quantity = record.min_quantity;
4332
2207
  }
4333
2208
  clampQuantityToBounds(item);
4334
- const storedLineId = typeof record2.line_id === "string" ? record2.line_id.trim() : "";
2209
+ const storedLineId = typeof record.line_id === "string" ? record.line_id.trim() : "";
4335
2210
  item.line_id = storedLineId || computeCartLineId(item);
4336
2211
  normalized.push(ensureCartLineId(item));
4337
2212
  }
4338
2213
  return normalized;
4339
2214
  }
4340
2215
  function getCartMetadata() {
4341
- return getItem(getStorageKey("meta"));
2216
+ return readCartStorage("meta");
4342
2217
  }
4343
2218
  function getCartCoupon() {
4344
2219
  return getStoredCartCode()?.code ?? null;
@@ -4349,18 +2224,18 @@ function getCartCouponSource() {
4349
2224
  function setCartCoupon(coupon, source = "COUPON") {
4350
2225
  const normalizedCoupon = normalizeCouponCode(coupon);
4351
2226
  if (!normalizedCoupon) {
4352
- removeItem(getStorageKey("coupon"));
2227
+ removeCartStorage("coupon");
4353
2228
  return null;
4354
2229
  }
4355
- setItem(getStorageKey("coupon"), { code: normalizedCoupon, source });
2230
+ writeCartStorage("coupon", { code: normalizedCoupon, source });
4356
2231
  return normalizedCoupon;
4357
2232
  }
4358
2233
  function clearCartCoupon() {
4359
- removeItem(getStorageKey("coupon"));
2234
+ removeCartStorage("coupon");
4360
2235
  }
4361
2236
  function writeCart(cart) {
4362
2237
  const normalizedCart = normalizeCartItems(cart);
4363
- setItem(getStorageKey("cart"), normalizedCart);
2238
+ writeCartStorage("cart", normalizedCart);
4364
2239
  const now = Date.now();
4365
2240
  const previous = getCartMetadata();
4366
2241
  const nextMeta = {
@@ -4369,14 +2244,14 @@ function writeCart(cart) {
4369
2244
  version: (previous?.version ?? 0) + 1,
4370
2245
  checksum: computeChecksum(normalizedCart)
4371
2246
  };
4372
- setItem(getStorageKey("meta"), nextMeta);
2247
+ writeCartStorage("meta", nextMeta);
4373
2248
  if (normalizedCart.length === 0) {
4374
2249
  clearCartCoupon();
4375
2250
  }
4376
2251
  }
4377
2252
  function setCartWithMetadata(cart, metadata) {
4378
2253
  const normalizedCart = normalizeCartItems(cart);
4379
- setItem(getStorageKey("cart"), normalizedCart);
2254
+ writeCartStorage("cart", normalizedCart);
4380
2255
  const now = Date.now();
4381
2256
  const base = metadata ?? getCartMetadata();
4382
2257
  const nextMeta = {
@@ -4385,10 +2260,10 @@ function setCartWithMetadata(cart, metadata) {
4385
2260
  version: base?.version ?? 1,
4386
2261
  checksum: computeChecksum(normalizedCart)
4387
2262
  };
4388
- setItem(getStorageKey("meta"), nextMeta);
2263
+ writeCartStorage("meta", nextMeta);
4389
2264
  }
4390
2265
  function getCart() {
4391
- const raw = getItem(getStorageKey("cart"));
2266
+ const raw = readCartStorage("cart");
4392
2267
  return normalizeCartItems(raw);
4393
2268
  }
4394
2269
  function resolveCartLineId(productId, variantId, cart = getCart()) {
@@ -4624,26 +2499,26 @@ function removeFromCart(lineId) {
4624
2499
  writeCart(filtered);
4625
2500
  }
4626
2501
  function clearCart() {
4627
- removeItem(getStorageKey("cart"));
4628
- removeItem(getStorageKey("meta"));
2502
+ removeCartStorage("cart");
2503
+ removeCartStorage("meta");
4629
2504
  clearCartCoupon();
4630
2505
  clearLatestQuoteToken();
4631
2506
  }
4632
2507
  function createCartBackup() {
4633
2508
  const cart = getCart();
4634
- setItem(getStorageKey("cartBackup"), cart);
2509
+ writeCartStorage("cartBackup", cart);
4635
2510
  const cartCode = getStoredCartCode();
4636
2511
  if (cartCode) {
4637
- setItem(getStorageKey("couponBackup"), cartCode);
2512
+ writeCartStorage("couponBackup", cartCode);
4638
2513
  } else {
4639
- removeItem(getStorageKey("couponBackup"));
2514
+ removeCartStorage("couponBackup");
4640
2515
  }
4641
2516
  const metadata = getCartMetadata();
4642
2517
  if (metadata) {
4643
- setItem(getStorageKey("metaBackup"), metadata);
2518
+ writeCartStorage("metaBackup", metadata);
4644
2519
  } else {
4645
2520
  const now = Date.now();
4646
- setItem(getStorageKey("metaBackup"), {
2521
+ writeCartStorage("metaBackup", {
4647
2522
  created_at: now,
4648
2523
  last_modified: now,
4649
2524
  version: 1,
@@ -4652,17 +2527,17 @@ function createCartBackup() {
4652
2527
  }
4653
2528
  }
4654
2529
  function restoreCartFromBackup() {
4655
- const backupRaw = getItem(getStorageKey("cartBackup"));
2530
+ const backupRaw = readCartStorage("cartBackup");
4656
2531
  const backup = normalizeCartItems(backupRaw);
4657
- const backupMeta = getItem(getStorageKey("metaBackup"));
4658
- const backupCoupon = getItem(getStorageKey("couponBackup"));
2532
+ const backupMeta = readCartStorage("metaBackup");
2533
+ const backupCoupon = readCartStorage("couponBackup");
4659
2534
  if (backup && backup.length > 0) {
4660
2535
  setCartWithMetadata(backup, backupMeta);
4661
2536
  if (backupCoupon && typeof backupCoupon === "object" && !Array.isArray(backupCoupon)) {
4662
- const record2 = backupCoupon;
4663
- const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
4664
- if (typeof record2.code === "string" && source) {
4665
- setCartCoupon(record2.code, source);
2537
+ const record = backupCoupon;
2538
+ const source = record.source === "COUPON" || record.source === "AFFILIATE" ? record.source : null;
2539
+ if (typeof record.code === "string" && source) {
2540
+ setCartCoupon(record.code, source);
4666
2541
  } else {
4667
2542
  clearCartCoupon();
4668
2543
  }
@@ -4753,7 +2628,7 @@ function getCartStats() {
4753
2628
  const cart = getCart();
4754
2629
  const integrityValid = validateCartIntegrity();
4755
2630
  const metadata = getCartMetadata();
4756
- const backup = getItem(getStorageKey("cartBackup")) ?? [];
2631
+ const backup = readCartStorage("cartBackup") ?? [];
4757
2632
  const hasCompletePriceSnapshots = cart.length > 0 && cart.every((item) => typeof item.price_data?.unit_price === "number");
4758
2633
  const totalPrice = cart.reduce((sum, item) => {
4759
2634
  const unitPrice = item.price_data?.unit_price ?? 0;
@@ -4814,6 +2689,46 @@ async function quoteCart(coupon, currency) {
4814
2689
  return response;
4815
2690
  }
4816
2691
 
2692
+ // ../contracts/src/redirect-link-template.ts
2693
+ var REMAINING_PLACEHOLDER_PATTERNS = [
2694
+ /\{\{[\s\S]*?\}\}/g,
2695
+ /(?<!\{)\{[^{}]+\}(?!\})/g
2696
+ ];
2697
+ function findDanglingBraceFragments(template) {
2698
+ let stripped = template;
2699
+ for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
2700
+ stripped = stripped.replace(pattern, "");
2701
+ }
2702
+ return /[{}]/.test(stripped) ? [...new Set(stripped.match(/[{}]+/g) ?? [])] : [];
2703
+ }
2704
+ function findRemainingProductRedirectPlaceholders(template) {
2705
+ if (!template.trim()) {
2706
+ return [];
2707
+ }
2708
+ const remaining = /* @__PURE__ */ new Set();
2709
+ for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
2710
+ for (const match of template.matchAll(pattern)) {
2711
+ remaining.add(match[0]);
2712
+ }
2713
+ }
2714
+ for (const fragment of findDanglingBraceFragments(template)) {
2715
+ remaining.add(fragment);
2716
+ }
2717
+ return [...remaining];
2718
+ }
2719
+ function isSafeHttpsRedirectTemplateUrl(url) {
2720
+ const trimmed = url.trim();
2721
+ if (!trimmed) {
2722
+ return false;
2723
+ }
2724
+ try {
2725
+ const parsed = new URL(trimmed);
2726
+ return parsed.protocol === "https:";
2727
+ } catch {
2728
+ return false;
2729
+ }
2730
+ }
2731
+
4817
2732
  // ../sdk/src/modules/checkout.ts
4818
2733
  var CheckoutCreateError = class _CheckoutCreateError extends Error {
4819
2734
  constructor(message, options = {}) {
@@ -4833,8 +2748,8 @@ function normalizeCoupon(coupon) {
4833
2748
  const normalized = coupon?.trim();
4834
2749
  return normalized ? normalized : null;
4835
2750
  }
4836
- function normalizeEmail(email2) {
4837
- const normalized = email2?.trim();
2751
+ function normalizeEmail(email) {
2752
+ const normalized = email?.trim();
4838
2753
  return normalized ? normalized : null;
4839
2754
  }
4840
2755
  function resolvePostPurchaseRedirectUrl(redirectUrl) {
@@ -5064,10 +2979,10 @@ async function checkout(couponOrOptions, options) {
5064
2979
  return performCheckout(resolvedOptions);
5065
2980
  }
5066
2981
  async function performCheckout(resolvedOptions) {
5067
- const { autoRedirect = true, email: email2 } = resolvedOptions;
2982
+ const { autoRedirect = true, email } = resolvedOptions;
5068
2983
  const checkoutCodes = resolveCheckoutCodes(resolvedOptions);
5069
2984
  const normalizedCoupon = checkoutCodes.coupon;
5070
- const normalizedEmail = normalizeEmail(email2);
2985
+ const normalizedEmail = normalizeEmail(email);
5071
2986
  const normalizedAffiliateCode = checkoutCodes.affiliateCode;
5072
2987
  const requestedCurrency = resolveRequestedCheckoutCurrency(resolvedOptions);
5073
2988
  const postPurchaseRedirect = resolvePostPurchaseRedirectUrl(resolvedOptions.redirectUrl);
@@ -5179,10 +3094,10 @@ async function buildCheckoutUrl(couponOrOptions, options) {
5179
3094
  return performBuildCheckoutUrl(resolvedOptions);
5180
3095
  }
5181
3096
  async function performBuildCheckoutUrl(resolvedOptions) {
5182
- const { email: email2 } = resolvedOptions;
3097
+ const { email } = resolvedOptions;
5183
3098
  const checkoutCodes = resolveCheckoutCodes(resolvedOptions);
5184
3099
  const normalizedCoupon = checkoutCodes.coupon;
5185
- const normalizedEmail = normalizeEmail(email2);
3100
+ const normalizedEmail = normalizeEmail(email);
5186
3101
  const normalizedAffiliateCode = checkoutCodes.affiliateCode;
5187
3102
  const requestedCurrency = resolveRequestedCheckoutCurrency(resolvedOptions);
5188
3103
  const postPurchaseRedirect = resolvePostPurchaseRedirectUrl(resolvedOptions.redirectUrl);
@@ -5356,15 +3271,15 @@ function firstString(...values) {
5356
3271
  return void 0;
5357
3272
  }
5358
3273
  function normalizeFeedback(raw, index) {
5359
- const record2 = raw;
5360
- const rating = toFiniteNumber(record2.rating) ?? toFiniteNumber(record2.score) ?? 0;
5361
- const comment = firstString(record2.comment, record2.message);
5362
- const author = firstString(record2.author, record2.customer_name);
5363
- const createdAt = record2.created_at ?? record2.createdAt;
3274
+ const record = raw;
3275
+ const rating = toFiniteNumber(record.rating) ?? toFiniteNumber(record.score) ?? 0;
3276
+ const comment = firstString(record.comment, record.message);
3277
+ const author = firstString(record.author, record.customer_name);
3278
+ const createdAt = record.created_at ?? record.createdAt;
5364
3279
  const created_at = typeof createdAt === "number" || typeof createdAt === "string" ? String(createdAt) : "";
5365
- const id = firstString(record2.id, record2.uniqid) ?? `review:${created_at || index}`;
3280
+ const id = firstString(record.id, record.uniqid) ?? `review:${created_at || index}`;
5366
3281
  return {
5367
- ...record2,
3282
+ ...record,
5368
3283
  id,
5369
3284
  rating,
5370
3285
  ...comment ? { comment } : {},
@@ -5511,7 +3426,7 @@ async function requestCustomer(path, options = {}) {
5511
3426
  if (!envelope) {
5512
3427
  return { success: false, status: response.status, message: "Invalid customer API response" };
5513
3428
  }
5514
- if (!response.ok || envelope.status < 200 || envelope.status >= 300) {
3429
+ if (!response.ok || envelope.status < 200 || envelope.status >= 300 || envelope.error_code === "RESPONSE_CONTRACT_VIOLATION") {
5515
3430
  return {
5516
3431
  success: false,
5517
3432
  // The transport status, not the envelope's: a caller deciding whether
@@ -5538,16 +3453,16 @@ async function requestCustomer(path, options = {}) {
5538
3453
  };
5539
3454
  }
5540
3455
  }
5541
- function requestOtp(email2) {
3456
+ function requestOtp(email) {
5542
3457
  return requestCustomer("/auth/otp/request", {
5543
3458
  method: "POST",
5544
- body: { email: email2 }
3459
+ body: { email }
5545
3460
  });
5546
3461
  }
5547
- function verifyOtp(email2, otp) {
3462
+ function verifyOtp(email, otp) {
5548
3463
  return requestCustomer("/auth/otp/verify", {
5549
3464
  method: "POST",
5550
- body: { email: email2, otp }
3465
+ body: { email, otp }
5551
3466
  });
5552
3467
  }
5553
3468
  function logout() {
@@ -5573,6 +3488,44 @@ function order(id) {
5573
3488
  schema: customerPortalInvoiceDetailSchema
5574
3489
  });
5575
3490
  }
3491
+ async function invoicePdf(id) {
3492
+ try {
3493
+ const response = await fetch(`${CUSTOMER_API_PREFIX}/invoices/${encodeURIComponent(id)}/pdf`, {
3494
+ method: "GET",
3495
+ headers: { Accept: "application/pdf" },
3496
+ credentials: "same-origin",
3497
+ cache: "no-store"
3498
+ });
3499
+ if (!response.ok) {
3500
+ let envelope = null;
3501
+ try {
3502
+ envelope = narrowEnvelope(await response.json());
3503
+ } catch {
3504
+ envelope = null;
3505
+ }
3506
+ return {
3507
+ success: false,
3508
+ status: response.status,
3509
+ message: readMessage(envelope ?? { status: response.status, data: null }, response)
3510
+ };
3511
+ }
3512
+ if (!(response.headers.get("content-type") ?? "").toLowerCase().startsWith("application/pdf")) {
3513
+ return { success: false, message: "Customer API response did not match the expected contract" };
3514
+ }
3515
+ return {
3516
+ success: true,
3517
+ data: {
3518
+ blob: await response.blob(),
3519
+ filename: response.headers.get("content-disposition")?.match(/filename="([^"]+)"/i)?.[1] ?? null
3520
+ }
3521
+ };
3522
+ } catch (error) {
3523
+ return {
3524
+ success: false,
3525
+ message: error instanceof Error ? error.message : String(error)
3526
+ };
3527
+ }
3528
+ }
5576
3529
  function loyalty() {
5577
3530
  return requestCustomer("/loyalty", { schema: customerLoyaltySchema });
5578
3531
  }
@@ -5643,6 +3596,18 @@ function affiliateStats(days) {
5643
3596
  const suffix = days === void 0 ? "" : `?days=${encodeURIComponent(String(days))}`;
5644
3597
  return requestCustomer(`/affiliate/stats${suffix}`);
5645
3598
  }
3599
+ function createAffiliateLink(input) {
3600
+ return requestCustomer("/affiliate/links", {
3601
+ method: "POST",
3602
+ body: {
3603
+ code: input.code,
3604
+ label: input.label,
3605
+ ...input.requested_affiliate_revenue_percent !== void 0 ? { requested_affiliate_revenue_percent: input.requested_affiliate_revenue_percent } : {},
3606
+ ...input.requested_affiliate_customer_discount_percent !== void 0 ? { requested_affiliate_customer_discount_percent: input.requested_affiliate_customer_discount_percent } : {}
3607
+ },
3608
+ schema: customerPortalAffiliateCreateLinkSchema
3609
+ });
3610
+ }
5646
3611
  function createTicket(payload) {
5647
3612
  return requestCustomer("/tickets", {
5648
3613
  method: "POST",
@@ -5688,6 +3653,22 @@ function updateEmailPreferences(patch) {
5688
3653
  }
5689
3654
  });
5690
3655
  }
3656
+ function notificationPreferences() {
3657
+ return requestCustomer("/notification-preferences", {
3658
+ schema: customerPortalNotificationPreferencesSchema
3659
+ });
3660
+ }
3661
+ function updateNotificationPreferences(patch) {
3662
+ return requestCustomer("/notification-preferences", {
3663
+ method: "PATCH",
3664
+ body: {
3665
+ ...patch.order_created_emails !== void 0 ? { order_created_emails: patch.order_created_emails } : {},
3666
+ ...patch.order_completed_emails !== void 0 ? { order_completed_emails: patch.order_completed_emails } : {},
3667
+ ...patch.payment_issue_emails !== void 0 ? { payment_issue_emails: patch.payment_issue_emails } : {}
3668
+ },
3669
+ schema: customerPortalNotificationPreferencesSchema
3670
+ });
3671
+ }
5691
3672
  function sessions() {
5692
3673
  return requestCustomer("/sessions");
5693
3674
  }
@@ -5911,6 +3892,23 @@ async function getPage(slug) {
5911
3892
  return forwardFailure(response);
5912
3893
  }
5913
3894
 
3895
+ // ../contracts/src/navigation.ts
3896
+ var NAVIGATION_MENU_SLOT_CONFIG = {
3897
+ header: {
3898
+ title: "Header",
3899
+ aliases: ["Header Menu"]
3900
+ },
3901
+ footer: {
3902
+ title: "Footer",
3903
+ aliases: ["Footer Links", "Legal Links"]
3904
+ }
3905
+ };
3906
+ function getNavigationMenuTitles(slot) {
3907
+ const config = NAVIGATION_MENU_SLOT_CONFIG[slot];
3908
+ return [config.title, ...config.aliases];
3909
+ }
3910
+ var NAVIGATION_MENU_SLOTS = Object.keys(NAVIGATION_MENU_SLOT_CONFIG);
3911
+
5914
3912
  // ../sdk/src/modules/navigation.ts
5915
3913
  var NAVIGATION_CACHE_TTL = 5 * 60 * 1e3;
5916
3914
  async function getMenus() {
@@ -6023,12 +4021,12 @@ function storageKey(storeSlug) {
6023
4021
  }
6024
4022
  function readStored(storeSlug) {
6025
4023
  try {
6026
- const raw = window.localStorage.getItem(storageKey(storeSlug));
4024
+ const raw = window.localStorage.getItem(storefrontStorageKey(storageKey(storeSlug)));
6027
4025
  if (!raw) return {};
6028
4026
  const parsed = JSON.parse(raw);
6029
4027
  if (!parsed || typeof parsed.at !== "number" || typeof parsed.utm !== "object") return {};
6030
4028
  if (Date.now() - parsed.at > TTL_MS) {
6031
- window.localStorage.removeItem(storageKey(storeSlug));
4029
+ window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
6032
4030
  return {};
6033
4031
  }
6034
4032
  const utm = {};
@@ -6046,7 +4044,7 @@ function readStored(storeSlug) {
6046
4044
  function writeStored(storeSlug, utm) {
6047
4045
  try {
6048
4046
  const payload = { utm, at: Date.now() };
6049
- window.localStorage.setItem(storageKey(storeSlug), JSON.stringify(payload));
4047
+ window.localStorage.setItem(storefrontStorageKey(storageKey(storeSlug)), JSON.stringify(payload));
6050
4048
  } catch {
6051
4049
  }
6052
4050
  }
@@ -6302,6 +4300,9 @@ async function touchStorefrontPresence() {
6302
4300
  if (isPlatformPageTrackerActive()) {
6303
4301
  return { success: true, message: "Presence is reported by the storefront page tracker" };
6304
4302
  }
4303
+ if (getConfig().preview) {
4304
+ return { success: true, message: "A preview page reports no presence" };
4305
+ }
6305
4306
  const config = getConfig();
6306
4307
  const shopId = getShopId();
6307
4308
  const endpoint = shopId ? buildEndpoint("/v1/storefront/shops/id/:id/ping", { id: shopId }) : buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
@@ -6478,6 +4479,7 @@ var shoppex = {
6478
4479
  dashboard,
6479
4480
  orders,
6480
4481
  order,
4482
+ invoicePdf,
6481
4483
  loyalty,
6482
4484
  redeemLoyaltyPoints,
6483
4485
  warranties,
@@ -6492,6 +4494,7 @@ var shoppex = {
6492
4494
  removeFavorite,
6493
4495
  affiliate,
6494
4496
  affiliateStats,
4497
+ createAffiliateLink,
6495
4498
  createTicket,
6496
4499
  ticket,
6497
4500
  replyToTicket,
@@ -6500,6 +4503,8 @@ var shoppex = {
6500
4503
  removeAvatar,
6501
4504
  emailPreferences,
6502
4505
  updateEmailPreferences,
4506
+ notificationPreferences,
4507
+ updateNotificationPreferences,
6503
4508
  sessions,
6504
4509
  revokeSession,
6505
4510
  revokeAllSessions,
@@ -6579,6 +4584,7 @@ export {
6579
4584
  claimWarranty,
6580
4585
  collectProductSearchHaystack,
6581
4586
  computeCartLineId,
4587
+ createAffiliateLink,
6582
4588
  createTicket,
6583
4589
  dashboard,
6584
4590
  src_default as default,
@@ -6600,6 +4606,7 @@ export {
6600
4606
  getStorefrontOnlineUsers,
6601
4607
  getStorefrontRecentSales,
6602
4608
  groupMatchesSearchQuery,
4609
+ invoicePdf,
6603
4610
  isProductInStock,
6604
4611
  isProductOutOfStock,
6605
4612
  isStorefrontCheckboxCustomFieldValueChecked,
@@ -6610,6 +4617,7 @@ export {
6610
4617
  mergeSettings,
6611
4618
  normalizeSearchQuery,
6612
4619
  normalizeStorefrontCustomFields,
4620
+ notificationPreferences,
6613
4621
  order,
6614
4622
  orders,
6615
4623
  pauseSubscription,
@@ -6649,6 +4657,7 @@ export {
6649
4657
  trackPageView,
6650
4658
  updateAvatar,
6651
4659
  updateEmailPreferences,
4660
+ updateNotificationPreferences,
6652
4661
  updateProfile,
6653
4662
  validateStorefrontCustomFieldValue,
6654
4663
  verifyOtp,