@rebuy/rebuy 3.43.1 → 3.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/index.cjs +72 -13
  2. package/dist/index.mjs +72 -13
  3. package/dist/pricing/postPurchaseDisplayPrice.d.ts +2 -0
  4. package/dist/schema/postPurchaseDisplayPrice.d.ts +4 -3
  5. package/dist/schema/postPurchaseProductCollectionView.d.ts +365 -3
  6. package/dist/schema/postPurchaseProductView.d.ts +367 -4
  7. package/dist/schema/postPurchaseTransition.d.ts +80 -3
  8. package/dist/server/index.cjs +119 -40
  9. package/dist/server/index.d.ts +1 -0
  10. package/dist/server/index.mjs +119 -40
  11. package/dist/server/postPurchaseCardSections.d.ts +12 -0
  12. package/dist/server/postPurchaseProductEligibility.d.ts +1 -1
  13. package/dist/server/postPurchaseSubscriptionEligibility.d.ts +1 -1
  14. package/dist/server/postPurchaseUnavailableChoice.d.ts +12 -0
  15. package/dist/server/preparePostPurchaseAdmissionView.d.ts +125 -2
  16. package/dist/server/preparePostPurchaseCollectionGroup.d.ts +130 -4
  17. package/dist/server/preparePostPurchaseCollectionView.d.ts +125 -2
  18. package/dist/server/preparePostPurchaseGroupAdmissionView.d.ts +125 -2
  19. package/dist/server/preparePostPurchaseGroupDefault.d.ts +125 -2
  20. package/dist/server/preparePostPurchaseGroupView.d.ts +125 -2
  21. package/dist/server/preparePostPurchaseMemberView.d.ts +375 -6
  22. package/dist/server/preparePostPurchaseQuantityAdmissionView.d.ts +125 -2
  23. package/dist/server/preparePostPurchaseQuantityView.d.ts +125 -2
  24. package/dist/server/preparePostPurchaseView.d.ts +4 -1
  25. package/dist/transforms/index.cjs +57 -7
  26. package/dist/transforms/index.mjs +57 -7
  27. package/dist/transforms/legacyLabelDefaults.d.ts +15 -0
  28. package/dist/transforms/localizedContent.d.ts +23 -6
  29. package/dist/transforms/postPurchase/productCardRows.d.ts +3 -1
  30. package/package.json +1 -1
@@ -48,6 +48,7 @@ __export(server_exports, {
48
48
  fetchUserConfig: () => fetchUserConfig,
49
49
  postAnalyticsEvents: () => postAnalyticsEvents,
50
50
  preparePostPurchaseAdmissionView: () => preparePostPurchaseAdmissionView,
51
+ preparePostPurchaseCardSections: () => preparePostPurchaseCardSections,
51
52
  preparePostPurchaseCollectionView: () => preparePostPurchaseCollectionView,
52
53
  preparePostPurchaseDeclineDisplay: () => preparePostPurchaseDeclineDisplay,
53
54
  preparePostPurchaseGroupAdmissionView: () => preparePostPurchaseGroupAdmissionView,
@@ -2858,10 +2859,37 @@ var SWITCH_LABELS = {
2858
2859
  }
2859
2860
  };
2860
2861
 
2862
+ // src/transforms/legacyLabelDefaults.ts
2863
+ var LEGACY_LABEL_DEFAULTS = {
2864
+ /** The banner's RTE default, compared by its plain text. */
2865
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
2866
+ /** Offers template; the Recommended template stores the bare `Add`. */
2867
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
2868
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
2869
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
2870
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
2871
+ switchToSubscriptionNoDiscount: [
2872
+ "Upgrade to Subscription",
2873
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2874
+ ],
2875
+ /** Line-item editor template, then the offers template. */
2876
+ switchToSubscriptionWithDiscount: [
2877
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
2878
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2879
+ ]
2880
+ };
2881
+
2861
2882
  // src/transforms/localizedContent.ts
2862
- var localizedContent = (defaults, override, knownTokens = []) => {
2883
+ var comparable = (label2) => label2.replace(/\s+/g, " ").trim().toLowerCase();
2884
+ var isDefaultLabel = (label2, defaults, knownTokens = [], legacyDefaults = []) => {
2885
+ const candidates = [defaults.en, ...legacyDefaults];
2886
+ const target = comparable(normalizeTokens(label2, knownTokens));
2887
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
2888
+ };
2889
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
2863
2890
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
2864
- return trimmed ? { ...defaults, en: trimmed } : defaults;
2891
+ if (!trimmed) return defaults;
2892
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
2865
2893
  };
2866
2894
 
2867
2895
  // src/transforms/lineItemEditorV1/convertLineItemEditorToV2.ts
@@ -2882,7 +2910,8 @@ var convertLineItemEditorToV2 = ({ id, settings }) => {
2882
2910
  content: localizedContent(
2883
2911
  SWITCH_LABELS[buttonField],
2884
2912
  switchToSubscription?.language?.[OVERRIDE_KEY[buttonField]],
2885
- SWITCH_LABEL_TOKENS
2913
+ SWITCH_LABEL_TOKENS,
2914
+ LEGACY_LABEL_DEFAULTS[buttonField]
2886
2915
  ),
2887
2916
  sectionType: "text"
2888
2917
  }));
@@ -3242,6 +3271,14 @@ var convertPlaceholderToV2 = ({ id, settings }) => CABRootSection.parse({
3242
3271
  // src/transforms/placeholderV1/types.ts
3243
3272
  var isPlaceholderV1 = (data) => !!data && typeof data === "object" && data.type === WidgetType.placeholder;
3244
3273
 
3274
+ // src/cab/listOf.ts
3275
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
3276
+
3277
+ // src/cab/tiptapPlain.ts
3278
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
3279
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
3280
+ ).join(" ").trim();
3281
+
3245
3282
  // src/transforms/productCard/earnedText.ts
3246
3283
  var contentMutator = (effect, variable, operator) => ({
3247
3284
  effect,
@@ -3312,7 +3349,12 @@ var subscriptionToggle = (productType, labels = {}) => {
3312
3349
  if (productType !== "both" && productType !== "subscription") return [];
3313
3350
  const slot = (buttonField, override) => ({
3314
3351
  buttonField,
3315
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS2),
3352
+ content: localizedContent(
3353
+ SWITCH_LABELS[buttonField],
3354
+ override,
3355
+ SWITCH_LABEL_TOKENS2,
3356
+ LEGACY_LABEL_DEFAULTS[buttonField]
3357
+ ),
3316
3358
  sectionType: "text"
3317
3359
  });
3318
3360
  return [
@@ -3514,7 +3556,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
3514
3556
  var buildAddButton = (settings) => {
3515
3557
  const slot = (buttonField, override) => ({
3516
3558
  buttonField,
3517
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
3559
+ content: localizedContent(
3560
+ OFFER_LABELS[buttonField],
3561
+ override,
3562
+ ADD_LABEL_TOKENS,
3563
+ LEGACY_LABEL_DEFAULTS[buttonField]
3564
+ ),
3518
3565
  sectionType: "text"
3519
3566
  });
3520
3567
  return {
@@ -3695,9 +3742,13 @@ var buildDiscountSection = (discount) => {
3695
3742
  }
3696
3743
  ];
3697
3744
  };
3745
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
3698
3746
  var bannerMessageContent = (message) => {
3699
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
3700
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
3747
+ const { addedToCartMessage } = OFFER_LABELS;
3748
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
3749
+ if (typeof message === "string" || !message)
3750
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
3751
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
3701
3752
  };
3702
3753
  var coerceIntegrations = (integrations) => Object.fromEntries(
3703
3754
  Object.entries(integrations).map(([key, value]) => {
@@ -6379,6 +6430,19 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6379
6430
  })
6380
6431
  });
6381
6432
 
6433
+ // src/server/postPurchaseUnavailableChoice.ts
6434
+ var VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
6435
+ var PLAN_UNAVAILABLE = "Subscription plan is unavailable";
6436
+ var CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
6437
+ var GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
6438
+ var choiceLevel = /* @__PURE__ */ new Set([
6439
+ VARIANT_UNAVAILABLE,
6440
+ PLAN_UNAVAILABLE,
6441
+ CHOICE_UNAVAILABLE,
6442
+ GROUP_DEFAULT_UNAVAILABLE
6443
+ ]);
6444
+ var isChoiceUnavailable = (reason) => reason !== void 0 && choiceLevel.has(reason);
6445
+
6382
6446
  // src/server/postPurchaseVariantAvailable.ts
6383
6447
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6384
6448
 
@@ -6393,12 +6457,13 @@ var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) =>
6393
6457
  return unavailable;
6394
6458
  const product = data[0];
6395
6459
  const variant2 = product.variants[0];
6396
- if (product.productGroupVariants.length || !postPurchaseVariantAvailable(variant2) || variant2.productId !== product.id)
6397
- return unavailable;
6460
+ if (product.productGroupVariants.length || variant2.productId !== product.id) return unavailable;
6398
6461
  const terms = [original, effective].map(
6399
6462
  (config) => PostPurchaseSubscriptionSettings.safeParse(config.subscription)
6400
6463
  );
6401
- if (!plan || terms.some((term) => !term.success)) return unavailable;
6464
+ if (terms.some((term) => !term.success)) return unavailable;
6465
+ if (!plan) return PLAN_UNAVAILABLE;
6466
+ if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6402
6467
  };
6403
6468
 
6404
6469
  // src/server/postPurchaseProductDisplay.ts
@@ -6652,7 +6717,7 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
6652
6717
 
6653
6718
  // src/server/prepareNormalizedSubscription.ts
6654
6719
  var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowCollection = false) => {
6655
- if (!plan) return { reason: "Subscription plan is unavailable", status: "unsupported" };
6720
+ if (!plan) return { reason: PLAN_UNAVAILABLE, status: "unsupported" };
6656
6721
  const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
6657
6722
  if (reason) return { reason, status: "unsupported" };
6658
6723
  const { effective } = normalized;
@@ -6703,8 +6768,9 @@ var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
6703
6768
  return "Only fixed one-time configuration is supported";
6704
6769
  const product = data[0];
6705
6770
  const variant2 = product.variants[0];
6706
- if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan) || !postPurchaseVariantAvailable(variant2))
6707
- return "An available one-time variant without product groups is required";
6771
+ if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6772
+ return "A one-time variant without product groups or a required selling plan is required";
6773
+ if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6708
6774
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
6709
6775
  };
6710
6776
 
@@ -6757,7 +6823,7 @@ var PostPurchaseSubscriptionDisplay = import_zod67.z.object({
6757
6823
  var { currencyCode: CurrencyCode, priceMinor: Minor2, savingPercent: Percent } = PostPurchaseSubscriptionDisplay.shape;
6758
6824
  var PostPurchaseDisplayPrice = import_zod68.z.object({
6759
6825
  currencyCode: CurrencyCode,
6760
- savingFormat: import_zod68.z.enum(["amount", "percent"]),
6826
+ savingFormat: import_zod68.z.enum(["amount", "percent"]).nullable(),
6761
6827
  savingMinor: Minor2,
6762
6828
  savingPercent: Percent,
6763
6829
  subscriptionUnit: PostPurchaseSubscriptionDisplay.optional(),
@@ -6911,7 +6977,8 @@ var PostPurchaseProductView = import_zod72.z.union([
6911
6977
  planCatalog: PostPurchasePlanCatalog.nullable().optional(),
6912
6978
  preparation: PostPurchasePreparation.options[1],
6913
6979
  purchaseOptions: PostPurchaseOptions.optional(),
6914
- sections: import_zod72.z.null(),
6980
+ /** A choice-level unavailable product keeps its card (values resolve blank); configuration failures send null. */
6981
+ sections: CABRootSection.nullable(),
6915
6982
  variantCatalog: PostPurchaseVariantCatalog.optional()
6916
6983
  })
6917
6984
  ]);
@@ -6942,7 +7009,24 @@ var postPurchaseOfferCopy = (blocks, metadata) => {
6942
7009
  });
6943
7010
  };
6944
7011
 
7012
+ // src/server/postPurchaseCardSections.ts
7013
+ var postPurchaseCardSections = (widgetId, { effective, metadata }, report) => {
7014
+ if (!effective.blocks?.length) return null;
7015
+ const sections = convertMessagePreview(
7016
+ widgetId,
7017
+ {
7018
+ ...effective,
7019
+ blocks: postPurchaseOfferCopy(effective.blocks, metadata),
7020
+ type: "shopify_post_purchase_extension"
7021
+ },
7022
+ report
7023
+ );
7024
+ return sections.sections.filter(({ sectionType }) => sectionType === "offers").length === 1 ? sections : null;
7025
+ };
7026
+ var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response), report);
7027
+
6945
7028
  // src/server/selectPostPurchaseChoice.ts
7029
+ var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
6946
7030
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
6947
7031
  const { data, effective } = normalized;
6948
7032
  const modes = [];
@@ -6957,7 +7041,10 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
6957
7041
  choice: void 0,
6958
7042
  options: void 0,
6959
7043
  plan: void 0,
6960
- reason: effective.productType === "one-time" ? oneTimeReason : subscriptionReason
7044
+ reason: preferChoiceLevel(
7045
+ effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
7046
+ effective.productType === "one-time" ? subscriptionReason : oneTimeReason
7047
+ )
6961
7048
  };
6962
7049
  const defaultChoice = firstPlan && modes.includes("subscription") && (modes[0] === "subscription" || effective.subscriptionIsDefault) ? { mode: "subscription", sellingPlanId: firstPlan.id } : { mode: "one-time" };
6963
7050
  const options2 = PostPurchaseOptions.parse({ defaultChoice, modes, selector: effective.productTypeSelector });
@@ -6968,7 +7055,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
6968
7055
  choice: bound,
6969
7056
  options: options2,
6970
7057
  plan: bound ? plan : void 0,
6971
- reason: bound ? void 0 : "Requested purchase choice is unavailable"
7058
+ reason: bound ? void 0 : CHOICE_UNAVAILABLE
6972
7059
  };
6973
7060
  };
6974
7061
 
@@ -6983,27 +7070,19 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
6983
7070
  const purchaseOptions = selected.options;
6984
7071
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
6985
7072
  const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
6986
- const unsupported = (reason) => ({
6987
- labels,
6988
- planCatalog,
6989
- preparation: { reason, status: "unsupported" },
6990
- purchaseOptions,
6991
- sections: null
6992
- });
6993
- if (preparation.status === "unsupported")
6994
- return { labels, planCatalog, preparation, purchaseOptions, sections: null };
6995
- if (!normalized.effective.blocks?.length) return unsupported("One fixed product information block is required");
6996
- const sections = convertMessagePreview(
6997
- widgetId,
6998
- {
6999
- ...normalized.effective,
7000
- blocks: postPurchaseOfferCopy(normalized.effective.blocks, normalized.metadata),
7001
- type: "shopify_post_purchase_extension"
7002
- },
7003
- report
7004
- );
7005
- if (sections.sections.filter((section) => section.sectionType === "offers").length !== 1)
7006
- return unsupported("One fixed product information block is required");
7073
+ if (preparation.status === "unsupported") {
7074
+ const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report) : null;
7075
+ return PostPurchaseProductView.parse({ labels, planCatalog, preparation, purchaseOptions, sections: sections2 });
7076
+ }
7077
+ const sections = postPurchaseCardSections(widgetId, normalized, report);
7078
+ if (!sections)
7079
+ return {
7080
+ labels,
7081
+ planCatalog,
7082
+ preparation: { reason: "One fixed product information block is required", status: "unsupported" },
7083
+ purchaseOptions,
7084
+ sections: null
7085
+ };
7007
7086
  return PostPurchaseProductView.parse({
7008
7087
  planCatalog,
7009
7088
  preparation: {
@@ -7594,8 +7673,8 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
7594
7673
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
7595
7674
  groupCatalog: group.catalog,
7596
7675
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
7597
- preparation: { reason: "Post-purchase group default is unavailable", status: "unsupported" },
7598
- sections: null
7676
+ preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
7677
+ sections: postPurchaseCardSections(widgetId, normalized, report)
7599
7678
  }
7600
7679
  };
7601
7680
  };
@@ -26,6 +26,7 @@ export { preparePostPurchaseQuantityAdmissionView } from '../server/preparePostP
26
26
  export { preparePostPurchaseGroupView } from '../server/preparePostPurchaseGroupView';
27
27
  export { preparePostPurchaseGroupAdmissionView } from '../server/preparePostPurchaseGroupAdmissionView';
28
28
  export { preparePostPurchaseGroupDefault } from '../server/preparePostPurchaseGroupDefault';
29
+ export { preparePostPurchaseCardSections } from '../server/postPurchaseCardSections';
29
30
  export { preparePostPurchaseCollectionView } from '../server/preparePostPurchaseCollectionView';
30
31
  export { classifyPostPurchaseOffer } from '../server/classifyPostPurchaseOffer';
31
32
  export { createPostPurchaseOrderAttributes } from '../server/createPostPurchaseOrderAttributes';
@@ -2787,10 +2787,37 @@ var SWITCH_LABELS = {
2787
2787
  }
2788
2788
  };
2789
2789
 
2790
+ // src/transforms/legacyLabelDefaults.ts
2791
+ var LEGACY_LABEL_DEFAULTS = {
2792
+ /** The banner's RTE default, compared by its plain text. */
2793
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
2794
+ /** Offers template; the Recommended template stores the bare `Add`. */
2795
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
2796
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
2797
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
2798
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
2799
+ switchToSubscriptionNoDiscount: [
2800
+ "Upgrade to Subscription",
2801
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2802
+ ],
2803
+ /** Line-item editor template, then the offers template. */
2804
+ switchToSubscriptionWithDiscount: [
2805
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
2806
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2807
+ ]
2808
+ };
2809
+
2790
2810
  // src/transforms/localizedContent.ts
2791
- var localizedContent = (defaults, override, knownTokens = []) => {
2811
+ var comparable = (label2) => label2.replace(/\s+/g, " ").trim().toLowerCase();
2812
+ var isDefaultLabel = (label2, defaults, knownTokens = [], legacyDefaults = []) => {
2813
+ const candidates = [defaults.en, ...legacyDefaults];
2814
+ const target = comparable(normalizeTokens(label2, knownTokens));
2815
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
2816
+ };
2817
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
2792
2818
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
2793
- return trimmed ? { ...defaults, en: trimmed } : defaults;
2819
+ if (!trimmed) return defaults;
2820
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
2794
2821
  };
2795
2822
 
2796
2823
  // src/transforms/lineItemEditorV1/convertLineItemEditorToV2.ts
@@ -2811,7 +2838,8 @@ var convertLineItemEditorToV2 = ({ id, settings }) => {
2811
2838
  content: localizedContent(
2812
2839
  SWITCH_LABELS[buttonField],
2813
2840
  switchToSubscription?.language?.[OVERRIDE_KEY[buttonField]],
2814
- SWITCH_LABEL_TOKENS
2841
+ SWITCH_LABEL_TOKENS,
2842
+ LEGACY_LABEL_DEFAULTS[buttonField]
2815
2843
  ),
2816
2844
  sectionType: "text"
2817
2845
  }));
@@ -3171,6 +3199,14 @@ var convertPlaceholderToV2 = ({ id, settings }) => CABRootSection.parse({
3171
3199
  // src/transforms/placeholderV1/types.ts
3172
3200
  var isPlaceholderV1 = (data) => !!data && typeof data === "object" && data.type === WidgetType.placeholder;
3173
3201
 
3202
+ // src/cab/listOf.ts
3203
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
3204
+
3205
+ // src/cab/tiptapPlain.ts
3206
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
3207
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
3208
+ ).join(" ").trim();
3209
+
3174
3210
  // src/transforms/productCard/earnedText.ts
3175
3211
  var contentMutator = (effect, variable, operator) => ({
3176
3212
  effect,
@@ -3241,7 +3277,12 @@ var subscriptionToggle = (productType, labels = {}) => {
3241
3277
  if (productType !== "both" && productType !== "subscription") return [];
3242
3278
  const slot = (buttonField, override) => ({
3243
3279
  buttonField,
3244
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS2),
3280
+ content: localizedContent(
3281
+ SWITCH_LABELS[buttonField],
3282
+ override,
3283
+ SWITCH_LABEL_TOKENS2,
3284
+ LEGACY_LABEL_DEFAULTS[buttonField]
3285
+ ),
3245
3286
  sectionType: "text"
3246
3287
  });
3247
3288
  return [
@@ -3443,7 +3484,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
3443
3484
  var buildAddButton = (settings) => {
3444
3485
  const slot = (buttonField, override) => ({
3445
3486
  buttonField,
3446
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
3487
+ content: localizedContent(
3488
+ OFFER_LABELS[buttonField],
3489
+ override,
3490
+ ADD_LABEL_TOKENS,
3491
+ LEGACY_LABEL_DEFAULTS[buttonField]
3492
+ ),
3447
3493
  sectionType: "text"
3448
3494
  });
3449
3495
  return {
@@ -3624,9 +3670,13 @@ var buildDiscountSection = (discount) => {
3624
3670
  }
3625
3671
  ];
3626
3672
  };
3673
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
3627
3674
  var bannerMessageContent = (message) => {
3628
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
3629
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
3675
+ const { addedToCartMessage } = OFFER_LABELS;
3676
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
3677
+ if (typeof message === "string" || !message)
3678
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
3679
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
3630
3680
  };
3631
3681
  var coerceIntegrations = (integrations) => Object.fromEntries(
3632
3682
  Object.entries(integrations).map(([key, value]) => {
@@ -6308,6 +6358,19 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6308
6358
  })
6309
6359
  });
6310
6360
 
6361
+ // src/server/postPurchaseUnavailableChoice.ts
6362
+ var VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
6363
+ var PLAN_UNAVAILABLE = "Subscription plan is unavailable";
6364
+ var CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
6365
+ var GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
6366
+ var choiceLevel = /* @__PURE__ */ new Set([
6367
+ VARIANT_UNAVAILABLE,
6368
+ PLAN_UNAVAILABLE,
6369
+ CHOICE_UNAVAILABLE,
6370
+ GROUP_DEFAULT_UNAVAILABLE
6371
+ ]);
6372
+ var isChoiceUnavailable = (reason) => reason !== void 0 && choiceLevel.has(reason);
6373
+
6311
6374
  // src/server/postPurchaseVariantAvailable.ts
6312
6375
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6313
6376
 
@@ -6322,12 +6385,13 @@ var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) =>
6322
6385
  return unavailable;
6323
6386
  const product = data[0];
6324
6387
  const variant2 = product.variants[0];
6325
- if (product.productGroupVariants.length || !postPurchaseVariantAvailable(variant2) || variant2.productId !== product.id)
6326
- return unavailable;
6388
+ if (product.productGroupVariants.length || variant2.productId !== product.id) return unavailable;
6327
6389
  const terms = [original, effective].map(
6328
6390
  (config) => PostPurchaseSubscriptionSettings.safeParse(config.subscription)
6329
6391
  );
6330
- if (!plan || terms.some((term) => !term.success)) return unavailable;
6392
+ if (terms.some((term) => !term.success)) return unavailable;
6393
+ if (!plan) return PLAN_UNAVAILABLE;
6394
+ if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6331
6395
  };
6332
6396
 
6333
6397
  // src/server/postPurchaseProductDisplay.ts
@@ -6581,7 +6645,7 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
6581
6645
 
6582
6646
  // src/server/prepareNormalizedSubscription.ts
6583
6647
  var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowCollection = false) => {
6584
- if (!plan) return { reason: "Subscription plan is unavailable", status: "unsupported" };
6648
+ if (!plan) return { reason: PLAN_UNAVAILABLE, status: "unsupported" };
6585
6649
  const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
6586
6650
  if (reason) return { reason, status: "unsupported" };
6587
6651
  const { effective } = normalized;
@@ -6632,8 +6696,9 @@ var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
6632
6696
  return "Only fixed one-time configuration is supported";
6633
6697
  const product = data[0];
6634
6698
  const variant2 = product.variants[0];
6635
- if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan) || !postPurchaseVariantAvailable(variant2))
6636
- return "An available one-time variant without product groups is required";
6699
+ if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6700
+ return "A one-time variant without product groups or a required selling plan is required";
6701
+ if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6637
6702
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
6638
6703
  };
6639
6704
 
@@ -6686,7 +6751,7 @@ var PostPurchaseSubscriptionDisplay = z67.object({
6686
6751
  var { currencyCode: CurrencyCode, priceMinor: Minor2, savingPercent: Percent } = PostPurchaseSubscriptionDisplay.shape;
6687
6752
  var PostPurchaseDisplayPrice = z68.object({
6688
6753
  currencyCode: CurrencyCode,
6689
- savingFormat: z68.enum(["amount", "percent"]),
6754
+ savingFormat: z68.enum(["amount", "percent"]).nullable(),
6690
6755
  savingMinor: Minor2,
6691
6756
  savingPercent: Percent,
6692
6757
  subscriptionUnit: PostPurchaseSubscriptionDisplay.optional(),
@@ -6840,7 +6905,8 @@ var PostPurchaseProductView = z72.union([
6840
6905
  planCatalog: PostPurchasePlanCatalog.nullable().optional(),
6841
6906
  preparation: PostPurchasePreparation.options[1],
6842
6907
  purchaseOptions: PostPurchaseOptions.optional(),
6843
- sections: z72.null(),
6908
+ /** A choice-level unavailable product keeps its card (values resolve blank); configuration failures send null. */
6909
+ sections: CABRootSection.nullable(),
6844
6910
  variantCatalog: PostPurchaseVariantCatalog.optional()
6845
6911
  })
6846
6912
  ]);
@@ -6871,7 +6937,24 @@ var postPurchaseOfferCopy = (blocks, metadata) => {
6871
6937
  });
6872
6938
  };
6873
6939
 
6940
+ // src/server/postPurchaseCardSections.ts
6941
+ var postPurchaseCardSections = (widgetId, { effective, metadata }, report) => {
6942
+ if (!effective.blocks?.length) return null;
6943
+ const sections = convertMessagePreview(
6944
+ widgetId,
6945
+ {
6946
+ ...effective,
6947
+ blocks: postPurchaseOfferCopy(effective.blocks, metadata),
6948
+ type: "shopify_post_purchase_extension"
6949
+ },
6950
+ report
6951
+ );
6952
+ return sections.sections.filter(({ sectionType }) => sectionType === "offers").length === 1 ? sections : null;
6953
+ };
6954
+ var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response), report);
6955
+
6874
6956
  // src/server/selectPostPurchaseChoice.ts
6957
+ var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
6875
6958
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
6876
6959
  const { data, effective } = normalized;
6877
6960
  const modes = [];
@@ -6886,7 +6969,10 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
6886
6969
  choice: void 0,
6887
6970
  options: void 0,
6888
6971
  plan: void 0,
6889
- reason: effective.productType === "one-time" ? oneTimeReason : subscriptionReason
6972
+ reason: preferChoiceLevel(
6973
+ effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
6974
+ effective.productType === "one-time" ? subscriptionReason : oneTimeReason
6975
+ )
6890
6976
  };
6891
6977
  const defaultChoice = firstPlan && modes.includes("subscription") && (modes[0] === "subscription" || effective.subscriptionIsDefault) ? { mode: "subscription", sellingPlanId: firstPlan.id } : { mode: "one-time" };
6892
6978
  const options2 = PostPurchaseOptions.parse({ defaultChoice, modes, selector: effective.productTypeSelector });
@@ -6897,7 +6983,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
6897
6983
  choice: bound,
6898
6984
  options: options2,
6899
6985
  plan: bound ? plan : void 0,
6900
- reason: bound ? void 0 : "Requested purchase choice is unavailable"
6986
+ reason: bound ? void 0 : CHOICE_UNAVAILABLE
6901
6987
  };
6902
6988
  };
6903
6989
 
@@ -6912,27 +6998,19 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
6912
6998
  const purchaseOptions = selected.options;
6913
6999
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
6914
7000
  const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
6915
- const unsupported = (reason) => ({
6916
- labels,
6917
- planCatalog,
6918
- preparation: { reason, status: "unsupported" },
6919
- purchaseOptions,
6920
- sections: null
6921
- });
6922
- if (preparation.status === "unsupported")
6923
- return { labels, planCatalog, preparation, purchaseOptions, sections: null };
6924
- if (!normalized.effective.blocks?.length) return unsupported("One fixed product information block is required");
6925
- const sections = convertMessagePreview(
6926
- widgetId,
6927
- {
6928
- ...normalized.effective,
6929
- blocks: postPurchaseOfferCopy(normalized.effective.blocks, normalized.metadata),
6930
- type: "shopify_post_purchase_extension"
6931
- },
6932
- report
6933
- );
6934
- if (sections.sections.filter((section) => section.sectionType === "offers").length !== 1)
6935
- return unsupported("One fixed product information block is required");
7001
+ if (preparation.status === "unsupported") {
7002
+ const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report) : null;
7003
+ return PostPurchaseProductView.parse({ labels, planCatalog, preparation, purchaseOptions, sections: sections2 });
7004
+ }
7005
+ const sections = postPurchaseCardSections(widgetId, normalized, report);
7006
+ if (!sections)
7007
+ return {
7008
+ labels,
7009
+ planCatalog,
7010
+ preparation: { reason: "One fixed product information block is required", status: "unsupported" },
7011
+ purchaseOptions,
7012
+ sections: null
7013
+ };
6936
7014
  return PostPurchaseProductView.parse({
6937
7015
  planCatalog,
6938
7016
  preparation: {
@@ -7523,8 +7601,8 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
7523
7601
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
7524
7602
  groupCatalog: group.catalog,
7525
7603
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
7526
- preparation: { reason: "Post-purchase group default is unavailable", status: "unsupported" },
7527
- sections: null
7604
+ preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
7605
+ sections: postPurchaseCardSections(widgetId, normalized, report)
7528
7606
  }
7529
7607
  };
7530
7608
  };
@@ -8189,6 +8267,7 @@ export {
8189
8267
  fetchUserConfig,
8190
8268
  postAnalyticsEvents,
8191
8269
  preparePostPurchaseAdmissionView,
8270
+ preparePostPurchaseCardSections,
8192
8271
  preparePostPurchaseCollectionView,
8193
8272
  preparePostPurchaseDeclineDisplay,
8194
8273
  preparePostPurchaseGroupAdmissionView,
@@ -0,0 +1,12 @@
1
+ import { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
2
+ import type { Reporter } from '../server/shared';
3
+ /**
4
+ * The product's section tree from the effective settings snapshot, or `null` unless it holds exactly one product
5
+ * card. No product values enter the tree; the client resolves its tokens.
6
+ */
7
+ export declare const postPurchaseCardSections: (widgetId: number, { effective, metadata }: Pick<ReturnType<typeof normalizePostPurchaseProduct>, "effective" | "metadata">, report?: Reporter) => import("..").CABRootSection | null;
8
+ /**
9
+ * The same tree from the raw settings and data-source response, for a caller's own choice-level fallback — rebuy-api's
10
+ * group default that resolves no available merchandise.
11
+ */
12
+ export declare const preparePostPurchaseCardSections: (widgetId: number, settings: unknown, response: unknown, report?: Reporter) => import("..").CABRootSection | null;
@@ -1,4 +1,4 @@
1
1
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
2
2
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
3
3
  /** The same eligibility rule drives advertised modes and their prepared terms. */
4
- export declare const oneTimeUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "Only one fixed product and variant are supported" | "Only fixed one-time configuration is supported" | "An available one-time variant without product groups is required" | undefined;
4
+ export declare const oneTimeUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "The chosen variant is unavailable" | "Only one fixed product and variant are supported" | "Only fixed one-time configuration is supported" | "A one-time variant without product groups or a required selling plan is required" | undefined;
@@ -1,4 +1,4 @@
1
1
  import type { PostPurchasePlanOption } from '../schema/postPurchasePlanCatalog';
2
2
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
3
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
4
- export declare const subscriptionUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, plan: PostPurchasePlanOption | undefined, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "An eligible subscription plan with supported shipping and discount is required" | undefined;
4
+ export declare const subscriptionUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, plan: PostPurchasePlanOption | undefined, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "The chosen variant is unavailable" | "Subscription plan is unavailable" | "An eligible subscription plan with supported shipping and discount is required" | undefined;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Choice-level unavailability: the widget is readable and its card converts, but the choice the buyer is looking
3
+ * at cannot be bought right now — its variant is sold out, its plan is gone, the requested mode is not offered,
4
+ * or a group has no available default. Like cab-checkout's sold-out card, such a product still carries its tree
5
+ * (values resolve blank; Pay shows `notAvailable`). Every other reason is configuration-level and keeps
6
+ * `sections: null`.
7
+ */
8
+ export declare const VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
9
+ export declare const PLAN_UNAVAILABLE = "Subscription plan is unavailable";
10
+ export declare const CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
11
+ export declare const GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
12
+ export declare const isChoiceUnavailable: (reason: string | undefined) => boolean;