@rebuy/rebuy 3.44.0 → 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.
package/dist/index.cjs CHANGED
@@ -5649,10 +5649,37 @@ var LOCATION_TO_EDITOR_MODE = {
5649
5649
  "thank-you": "thankYouPage"
5650
5650
  };
5651
5651
 
5652
+ // src/transforms/legacyLabelDefaults.ts
5653
+ var LEGACY_LABEL_DEFAULTS = {
5654
+ /** The banner's RTE default, compared by its plain text. */
5655
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
5656
+ /** Offers template; the Recommended template stores the bare `Add`. */
5657
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
5658
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
5659
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
5660
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
5661
+ switchToSubscriptionNoDiscount: [
5662
+ "Upgrade to Subscription",
5663
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
5664
+ ],
5665
+ /** Line-item editor template, then the offers template. */
5666
+ switchToSubscriptionWithDiscount: [
5667
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
5668
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
5669
+ ]
5670
+ };
5671
+
5652
5672
  // src/transforms/localizedContent.ts
5653
- var localizedContent = (defaults, override, knownTokens = []) => {
5673
+ var comparable = (label) => label.replace(/\s+/g, " ").trim().toLowerCase();
5674
+ var isDefaultLabel = (label, defaults, knownTokens = [], legacyDefaults = []) => {
5675
+ const candidates = [defaults.en, ...legacyDefaults];
5676
+ const target = comparable(normalizeTokens(label, knownTokens));
5677
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
5678
+ };
5679
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
5654
5680
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
5655
- return trimmed ? { ...defaults, en: trimmed } : defaults;
5681
+ if (!trimmed) return defaults;
5682
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
5656
5683
  };
5657
5684
 
5658
5685
  // src/transforms/offerV1/offerTemplateSeed.ts
@@ -6093,6 +6120,14 @@ var buildOfferTemplate = ({ location: location2, name } = {}) => CABRootSection.
6093
6120
  type: SHOPIFY_CHECKOUT_EXTENSION
6094
6121
  });
6095
6122
 
6123
+ // src/cab/listOf.ts
6124
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
6125
+
6126
+ // src/cab/tiptapPlain.ts
6127
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
6128
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
6129
+ ).join(" ").trim();
6130
+
6096
6131
  // src/transforms/productCard/earnedText.ts
6097
6132
  var contentMutator = (effect, variable, operator) => ({
6098
6133
  effect,
@@ -6163,7 +6198,12 @@ var subscriptionToggle = (productType, labels = {}) => {
6163
6198
  if (productType !== "both" && productType !== "subscription") return [];
6164
6199
  const slot = (buttonField, override) => ({
6165
6200
  buttonField,
6166
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS),
6201
+ content: localizedContent(
6202
+ SWITCH_LABELS[buttonField],
6203
+ override,
6204
+ SWITCH_LABEL_TOKENS,
6205
+ LEGACY_LABEL_DEFAULTS[buttonField]
6206
+ ),
6167
6207
  sectionType: "text"
6168
6208
  });
6169
6209
  return [
@@ -6365,7 +6405,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
6365
6405
  var buildAddButton = (settings) => {
6366
6406
  const slot = (buttonField, override) => ({
6367
6407
  buttonField,
6368
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
6408
+ content: localizedContent(
6409
+ OFFER_LABELS[buttonField],
6410
+ override,
6411
+ ADD_LABEL_TOKENS,
6412
+ LEGACY_LABEL_DEFAULTS[buttonField]
6413
+ ),
6369
6414
  sectionType: "text"
6370
6415
  });
6371
6416
  return {
@@ -6546,9 +6591,13 @@ var buildDiscountSection = (discount) => {
6546
6591
  }
6547
6592
  ];
6548
6593
  };
6594
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
6549
6595
  var bannerMessageContent = (message) => {
6550
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
6551
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
6596
+ const { addedToCartMessage } = OFFER_LABELS;
6597
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
6598
+ if (typeof message === "string" || !message)
6599
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
6600
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
6552
6601
  };
6553
6602
  var coerceIntegrations = (integrations) => Object.fromEntries(
6554
6603
  Object.entries(integrations).map(([key, value]) => {
package/dist/index.mjs CHANGED
@@ -5179,10 +5179,37 @@ var LOCATION_TO_EDITOR_MODE = {
5179
5179
  "thank-you": "thankYouPage"
5180
5180
  };
5181
5181
 
5182
+ // src/transforms/legacyLabelDefaults.ts
5183
+ var LEGACY_LABEL_DEFAULTS = {
5184
+ /** The banner's RTE default, compared by its plain text. */
5185
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
5186
+ /** Offers template; the Recommended template stores the bare `Add`. */
5187
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
5188
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
5189
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
5190
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
5191
+ switchToSubscriptionNoDiscount: [
5192
+ "Upgrade to Subscription",
5193
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
5194
+ ],
5195
+ /** Line-item editor template, then the offers template. */
5196
+ switchToSubscriptionWithDiscount: [
5197
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
5198
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
5199
+ ]
5200
+ };
5201
+
5182
5202
  // src/transforms/localizedContent.ts
5183
- var localizedContent = (defaults, override, knownTokens = []) => {
5203
+ var comparable = (label) => label.replace(/\s+/g, " ").trim().toLowerCase();
5204
+ var isDefaultLabel = (label, defaults, knownTokens = [], legacyDefaults = []) => {
5205
+ const candidates = [defaults.en, ...legacyDefaults];
5206
+ const target = comparable(normalizeTokens(label, knownTokens));
5207
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
5208
+ };
5209
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
5184
5210
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
5185
- return trimmed ? { ...defaults, en: trimmed } : defaults;
5211
+ if (!trimmed) return defaults;
5212
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
5186
5213
  };
5187
5214
 
5188
5215
  // src/transforms/offerV1/offerTemplateSeed.ts
@@ -5623,6 +5650,14 @@ var buildOfferTemplate = ({ location: location2, name } = {}) => CABRootSection.
5623
5650
  type: SHOPIFY_CHECKOUT_EXTENSION
5624
5651
  });
5625
5652
 
5653
+ // src/cab/listOf.ts
5654
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
5655
+
5656
+ // src/cab/tiptapPlain.ts
5657
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
5658
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
5659
+ ).join(" ").trim();
5660
+
5626
5661
  // src/transforms/productCard/earnedText.ts
5627
5662
  var contentMutator = (effect, variable, operator) => ({
5628
5663
  effect,
@@ -5693,7 +5728,12 @@ var subscriptionToggle = (productType, labels = {}) => {
5693
5728
  if (productType !== "both" && productType !== "subscription") return [];
5694
5729
  const slot = (buttonField, override) => ({
5695
5730
  buttonField,
5696
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS),
5731
+ content: localizedContent(
5732
+ SWITCH_LABELS[buttonField],
5733
+ override,
5734
+ SWITCH_LABEL_TOKENS,
5735
+ LEGACY_LABEL_DEFAULTS[buttonField]
5736
+ ),
5697
5737
  sectionType: "text"
5698
5738
  });
5699
5739
  return [
@@ -5895,7 +5935,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
5895
5935
  var buildAddButton = (settings) => {
5896
5936
  const slot = (buttonField, override) => ({
5897
5937
  buttonField,
5898
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
5938
+ content: localizedContent(
5939
+ OFFER_LABELS[buttonField],
5940
+ override,
5941
+ ADD_LABEL_TOKENS,
5942
+ LEGACY_LABEL_DEFAULTS[buttonField]
5943
+ ),
5899
5944
  sectionType: "text"
5900
5945
  });
5901
5946
  return {
@@ -6076,9 +6121,13 @@ var buildDiscountSection = (discount) => {
6076
6121
  }
6077
6122
  ];
6078
6123
  };
6124
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
6079
6125
  var bannerMessageContent = (message) => {
6080
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
6081
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
6126
+ const { addedToCartMessage } = OFFER_LABELS;
6127
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
6128
+ if (typeof message === "string" || !message)
6129
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
6130
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
6082
6131
  };
6083
6132
  var coerceIntegrations = (integrations) => Object.fromEntries(
6084
6133
  Object.entries(integrations).map(([key, value]) => {
@@ -2859,10 +2859,37 @@ var SWITCH_LABELS = {
2859
2859
  }
2860
2860
  };
2861
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
+
2862
2882
  // src/transforms/localizedContent.ts
2863
- 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 = []) => {
2864
2890
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
2865
- return trimmed ? { ...defaults, en: trimmed } : defaults;
2891
+ if (!trimmed) return defaults;
2892
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
2866
2893
  };
2867
2894
 
2868
2895
  // src/transforms/lineItemEditorV1/convertLineItemEditorToV2.ts
@@ -2883,7 +2910,8 @@ var convertLineItemEditorToV2 = ({ id, settings }) => {
2883
2910
  content: localizedContent(
2884
2911
  SWITCH_LABELS[buttonField],
2885
2912
  switchToSubscription?.language?.[OVERRIDE_KEY[buttonField]],
2886
- SWITCH_LABEL_TOKENS
2913
+ SWITCH_LABEL_TOKENS,
2914
+ LEGACY_LABEL_DEFAULTS[buttonField]
2887
2915
  ),
2888
2916
  sectionType: "text"
2889
2917
  }));
@@ -3243,6 +3271,14 @@ var convertPlaceholderToV2 = ({ id, settings }) => CABRootSection.parse({
3243
3271
  // src/transforms/placeholderV1/types.ts
3244
3272
  var isPlaceholderV1 = (data) => !!data && typeof data === "object" && data.type === WidgetType.placeholder;
3245
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
+
3246
3282
  // src/transforms/productCard/earnedText.ts
3247
3283
  var contentMutator = (effect, variable, operator) => ({
3248
3284
  effect,
@@ -3313,7 +3349,12 @@ var subscriptionToggle = (productType, labels = {}) => {
3313
3349
  if (productType !== "both" && productType !== "subscription") return [];
3314
3350
  const slot = (buttonField, override) => ({
3315
3351
  buttonField,
3316
- 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
+ ),
3317
3358
  sectionType: "text"
3318
3359
  });
3319
3360
  return [
@@ -3515,7 +3556,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
3515
3556
  var buildAddButton = (settings) => {
3516
3557
  const slot = (buttonField, override) => ({
3517
3558
  buttonField,
3518
- 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
+ ),
3519
3565
  sectionType: "text"
3520
3566
  });
3521
3567
  return {
@@ -3696,9 +3742,13 @@ var buildDiscountSection = (discount) => {
3696
3742
  }
3697
3743
  ];
3698
3744
  };
3745
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
3699
3746
  var bannerMessageContent = (message) => {
3700
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
3701
- 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 };
3702
3752
  };
3703
3753
  var coerceIntegrations = (integrations) => Object.fromEntries(
3704
3754
  Object.entries(integrations).map(([key, value]) => {
@@ -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]) => {
@@ -2224,10 +2224,37 @@ var SWITCH_LABELS = {
2224
2224
  }
2225
2225
  };
2226
2226
 
2227
+ // src/transforms/legacyLabelDefaults.ts
2228
+ var LEGACY_LABEL_DEFAULTS = {
2229
+ /** The banner's RTE default, compared by its plain text. */
2230
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
2231
+ /** Offers template; the Recommended template stores the bare `Add`. */
2232
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
2233
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
2234
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
2235
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
2236
+ switchToSubscriptionNoDiscount: [
2237
+ "Upgrade to Subscription",
2238
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2239
+ ],
2240
+ /** Line-item editor template, then the offers template. */
2241
+ switchToSubscriptionWithDiscount: [
2242
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
2243
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2244
+ ]
2245
+ };
2246
+
2227
2247
  // src/transforms/localizedContent.ts
2228
- var localizedContent = (defaults, override, knownTokens = []) => {
2248
+ var comparable = (label) => label.replace(/\s+/g, " ").trim().toLowerCase();
2249
+ var isDefaultLabel = (label, defaults, knownTokens = [], legacyDefaults = []) => {
2250
+ const candidates = [defaults.en, ...legacyDefaults];
2251
+ const target = comparable(normalizeTokens(label, knownTokens));
2252
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
2253
+ };
2254
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
2229
2255
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
2230
- return trimmed ? { ...defaults, en: trimmed } : defaults;
2256
+ if (!trimmed) return defaults;
2257
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
2231
2258
  };
2232
2259
 
2233
2260
  // src/transforms/lineItemEditorV1/convertLineItemEditorToV2.ts
@@ -2248,7 +2275,8 @@ var convertLineItemEditorToV2 = ({ id, settings }) => {
2248
2275
  content: localizedContent(
2249
2276
  SWITCH_LABELS[buttonField],
2250
2277
  switchToSubscription?.language?.[OVERRIDE_KEY[buttonField]],
2251
- SWITCH_LABEL_TOKENS
2278
+ SWITCH_LABEL_TOKENS,
2279
+ LEGACY_LABEL_DEFAULTS[buttonField]
2252
2280
  ),
2253
2281
  sectionType: "text"
2254
2282
  }));
@@ -3039,6 +3067,14 @@ var convertPlaceholderToV2 = ({ id, settings }) => CABRootSection.parse({
3039
3067
  // src/transforms/placeholderV1/types.ts
3040
3068
  var isPlaceholderV1 = (data) => !!data && typeof data === "object" && data.type === WidgetType.placeholder;
3041
3069
 
3070
+ // src/cab/listOf.ts
3071
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
3072
+
3073
+ // src/cab/tiptapPlain.ts
3074
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
3075
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
3076
+ ).join(" ").trim();
3077
+
3042
3078
  // src/transforms/productCard/earnedText.ts
3043
3079
  var contentMutator = (effect, variable, operator) => ({
3044
3080
  effect,
@@ -3109,7 +3145,12 @@ var subscriptionToggle = (productType, labels = {}) => {
3109
3145
  if (productType !== "both" && productType !== "subscription") return [];
3110
3146
  const slot = (buttonField, override) => ({
3111
3147
  buttonField,
3112
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS2),
3148
+ content: localizedContent(
3149
+ SWITCH_LABELS[buttonField],
3150
+ override,
3151
+ SWITCH_LABEL_TOKENS2,
3152
+ LEGACY_LABEL_DEFAULTS[buttonField]
3153
+ ),
3113
3154
  sectionType: "text"
3114
3155
  });
3115
3156
  return [
@@ -3311,7 +3352,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
3311
3352
  var buildAddButton = (settings) => {
3312
3353
  const slot = (buttonField, override) => ({
3313
3354
  buttonField,
3314
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
3355
+ content: localizedContent(
3356
+ OFFER_LABELS[buttonField],
3357
+ override,
3358
+ ADD_LABEL_TOKENS,
3359
+ LEGACY_LABEL_DEFAULTS[buttonField]
3360
+ ),
3315
3361
  sectionType: "text"
3316
3362
  });
3317
3363
  return {
@@ -3492,9 +3538,13 @@ var buildDiscountSection = (discount) => {
3492
3538
  }
3493
3539
  ];
3494
3540
  };
3541
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
3495
3542
  var bannerMessageContent = (message) => {
3496
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
3497
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
3543
+ const { addedToCartMessage } = OFFER_LABELS;
3544
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
3545
+ if (typeof message === "string" || !message)
3546
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
3547
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
3498
3548
  };
3499
3549
  var coerceIntegrations = (integrations) => Object.fromEntries(
3500
3550
  Object.entries(integrations).map(([key, value]) => {
@@ -2163,10 +2163,37 @@ var SWITCH_LABELS = {
2163
2163
  }
2164
2164
  };
2165
2165
 
2166
+ // src/transforms/legacyLabelDefaults.ts
2167
+ var LEGACY_LABEL_DEFAULTS = {
2168
+ /** The banner's RTE default, compared by its plain text. */
2169
+ addedToCartMessage: ["{{quantity}} {{product_title}} successfully added to cart!"],
2170
+ /** Offers template; the Recommended template stores the bare `Add`. */
2171
+ addToCart: ["Add {{quantity}} \u2022 {{total}}", "Add"],
2172
+ /** Line-item editor template, then the base template's `cartSwitchToOnetime` (the offer card's one-time label). */
2173
+ switchToOneTimePurchase: ["Downgrade to One-time", "One-time purchase"],
2174
+ /** Line-item editor template; the offers template's one upsell label fills both upsell slots. */
2175
+ switchToSubscriptionNoDiscount: [
2176
+ "Upgrade to Subscription",
2177
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2178
+ ],
2179
+ /** Line-item editor template, then the offers template. */
2180
+ switchToSubscriptionWithDiscount: [
2181
+ "Upgrade to Subscription & Save {{subscription_discount}}%",
2182
+ "Upgrade to Subscription and Save {{subscription_discount}}%"
2183
+ ]
2184
+ };
2185
+
2166
2186
  // src/transforms/localizedContent.ts
2167
- var localizedContent = (defaults, override, knownTokens = []) => {
2187
+ var comparable = (label) => label.replace(/\s+/g, " ").trim().toLowerCase();
2188
+ var isDefaultLabel = (label, defaults, knownTokens = [], legacyDefaults = []) => {
2189
+ const candidates = [defaults.en, ...legacyDefaults];
2190
+ const target = comparable(normalizeTokens(label, knownTokens));
2191
+ return candidates.some((candidate) => comparable(normalizeTokens(candidate, knownTokens)) === target);
2192
+ };
2193
+ var localizedContent = (defaults, override, knownTokens = [], legacyDefaults = []) => {
2168
2194
  const trimmed = normalizeTokens(stripContentBlockHtml(override ?? "").trim(), knownTokens);
2169
- return trimmed ? { ...defaults, en: trimmed } : defaults;
2195
+ if (!trimmed) return defaults;
2196
+ return isDefaultLabel(trimmed, defaults, knownTokens, legacyDefaults) ? { ...defaults, en: trimmed } : { en: trimmed };
2170
2197
  };
2171
2198
 
2172
2199
  // src/transforms/lineItemEditorV1/convertLineItemEditorToV2.ts
@@ -2187,7 +2214,8 @@ var convertLineItemEditorToV2 = ({ id, settings }) => {
2187
2214
  content: localizedContent(
2188
2215
  SWITCH_LABELS[buttonField],
2189
2216
  switchToSubscription?.language?.[OVERRIDE_KEY[buttonField]],
2190
- SWITCH_LABEL_TOKENS
2217
+ SWITCH_LABEL_TOKENS,
2218
+ LEGACY_LABEL_DEFAULTS[buttonField]
2191
2219
  ),
2192
2220
  sectionType: "text"
2193
2221
  }));
@@ -2978,6 +3006,14 @@ var convertPlaceholderToV2 = ({ id, settings }) => CABRootSection.parse({
2978
3006
  // src/transforms/placeholderV1/types.ts
2979
3007
  var isPlaceholderV1 = (data) => !!data && typeof data === "object" && data.type === WidgetType.placeholder;
2980
3008
 
3009
+ // src/cab/listOf.ts
3010
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
3011
+
3012
+ // src/cab/tiptapPlain.ts
3013
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
3014
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
3015
+ ).join(" ").trim();
3016
+
2981
3017
  // src/transforms/productCard/earnedText.ts
2982
3018
  var contentMutator = (effect, variable, operator) => ({
2983
3019
  effect,
@@ -3048,7 +3084,12 @@ var subscriptionToggle = (productType, labels = {}) => {
3048
3084
  if (productType !== "both" && productType !== "subscription") return [];
3049
3085
  const slot = (buttonField, override) => ({
3050
3086
  buttonField,
3051
- content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS2),
3087
+ content: localizedContent(
3088
+ SWITCH_LABELS[buttonField],
3089
+ override,
3090
+ SWITCH_LABEL_TOKENS2,
3091
+ LEGACY_LABEL_DEFAULTS[buttonField]
3092
+ ),
3052
3093
  sectionType: "text"
3053
3094
  });
3054
3095
  return [
@@ -3250,7 +3291,12 @@ var buildRevealButton = (settings) => revealsVariants(settings) ? [
3250
3291
  var buildAddButton = (settings) => {
3251
3292
  const slot = (buttonField, override) => ({
3252
3293
  buttonField,
3253
- content: localizedContent(OFFER_LABELS[buttonField], override, ADD_LABEL_TOKENS),
3294
+ content: localizedContent(
3295
+ OFFER_LABELS[buttonField],
3296
+ override,
3297
+ ADD_LABEL_TOKENS,
3298
+ LEGACY_LABEL_DEFAULTS[buttonField]
3299
+ ),
3254
3300
  sectionType: "text"
3255
3301
  });
3256
3302
  return {
@@ -3431,9 +3477,13 @@ var buildDiscountSection = (discount) => {
3431
3477
  }
3432
3478
  ];
3433
3479
  };
3480
+ var BANNER_MESSAGE_TOKENS = ["quantity", "productTitle"];
3434
3481
  var bannerMessageContent = (message) => {
3435
- const sanitized = typeof message === "string" ? stripContentBlockHtml(message) : message;
3436
- return sanitized ? { ...OFFER_LABELS.addedToCartMessage, en: sanitized } : OFFER_LABELS.addedToCartMessage;
3482
+ const { addedToCartMessage } = OFFER_LABELS;
3483
+ const legacyDefaults = LEGACY_LABEL_DEFAULTS.addedToCartMessage;
3484
+ if (typeof message === "string" || !message)
3485
+ return localizedContent(addedToCartMessage, message, BANNER_MESSAGE_TOKENS, legacyDefaults);
3486
+ return isDefaultLabel(tiptapPlainText(message), addedToCartMessage, BANNER_MESSAGE_TOKENS, legacyDefaults) ? { ...addedToCartMessage, en: message } : { en: message };
3437
3487
  };
3438
3488
  var coerceIntegrations = (integrations) => Object.fromEntries(
3439
3489
  Object.entries(integrations).map(([key, value]) => {
@@ -0,0 +1,15 @@
1
+ import { type OFFER_LABELS } from '../schema/widgets/checkout-and-beyond/offerLabels';
2
+ import { type SWITCH_LABELS } from '../schema/widgets/checkout-and-beyond/switchLabels';
3
+ /** The CAB label slots a v1 label can override; keying by them makes a typo'd slot a type error. */
4
+ type LabelSlot = keyof typeof OFFER_LABELS | keyof typeof SWITCH_LABELS;
5
+ /**
6
+ * The defaults the v1 checkout admin stores into a new widget's `language`, per CAB label slot — the
7
+ * admin-nextjs `shopify-checkout-extension/templates/widgetSettings*.ts` values, plus the base widget
8
+ * template (`templates/widgets/widgetSettings.ts`) that older checkout widgets were created from. A stored v1 label equal
9
+ * to one of these (or to the slot's CAB `en` default) is untouched template copy, not merchant copy, so
10
+ * `localizedContent` keeps the localized defaults for it instead of showing it to every language
11
+ * (REB-24836). Most differ from the CAB `en` default in wording, so the case-insensitive `en` comparison
12
+ * alone would miss them. Token spelling is canonicalized before comparing, so legacy snake_case is fine.
13
+ */
14
+ export declare const LEGACY_LABEL_DEFAULTS: Readonly<Partial<Record<LabelSlot, readonly string[]>>>;
15
+ export {};
@@ -1,10 +1,23 @@
1
+ /**
2
+ * Whether a stored v1 label is untouched default copy rather than merchant copy: it equals the slot's
3
+ * CAB `en` default or one of the v1 admin's template defaults (`LEGACY_LABEL_DEFAULTS`), compared
4
+ * case- and whitespace-insensitively after both sides' token spelling is canonicalized.
5
+ */
6
+ export declare const isDefaultLabel: (label: string, defaults: {
7
+ en: string;
8
+ }, knownTokens?: readonly string[], legacyDefaults?: readonly string[]) => boolean;
1
9
  /**
2
10
  * The v1 label-override policy shared by the offer-card and line-item-editor converters: a defined v1
3
- * label ALWAYS passes through verbatim as the `en` entry over the localized defaults — existing widget
4
- * language is never rewritten (product owner, 2026-07-02); blank/whitespace falls back to the full
5
- * default locale set (legacy `getLabelWithFallback` parity). HTML the merchant typed into a label is
6
- * stripped first — `CABTextSection.content` rejects tag-shaped strings at parse, so a raw `<b>`-style
7
- * label would otherwise 500 the whole widget.
11
+ * label ALWAYS passes through verbatim — existing widget language is never rewritten (product owner,
12
+ * 2026-07-02); blank/whitespace falls back to the full default locale set (legacy `getLabelWithFallback`
13
+ * parity). HTML the merchant typed into a label is stripped first — `CABTextSection.content` rejects
14
+ * tag-shaped strings at parse, so a raw `<b>`-style label would otherwise 500 the whole widget.
15
+ *
16
+ * A label the merchant changed is emitted `en`-only, so `selectLanguage` shows it to EVERY buyer language
17
+ * — legacy rendered the merchant's label whatever the checkout language, and overlaying just `en` let the
18
+ * localized defaults beat it for non-English buyers (REB-24836: a Swedish "Köp" rendered as "Lägg i
19
+ * varukorgen"). A label that is still default copy (`isDefaultLabel`, given the slot's
20
+ * `LEGACY_LABEL_DEFAULTS`) keeps the localized defaults, with its own wording as `en`.
8
21
  *
9
22
  * `knownTokens` canonicalizes legacy token GRAMMAR in the stored label (single-brace `{price}`,
10
23
  * case `{{Total}}`, snake `{subscription_discount}`, internal spaces → `{{camelCase}}`) so the strict
@@ -13,4 +26,8 @@
13
26
  * This preserves the merchant's WORDING (only the token spelling is normalized), distinct from the
14
27
  * verbatim-label policy above.
15
28
  */
16
- export declare const localizedContent: (defaults: Record<string, string>, override: string | undefined, knownTokens?: readonly string[]) => Record<string, string>;
29
+ export declare const localizedContent: (defaults: Record<string, string> & {
30
+ en: string;
31
+ }, override: string | undefined, knownTokens?: readonly string[], legacyDefaults?: readonly string[]) => Record<string, string> & {
32
+ en: string;
33
+ };
@@ -18,7 +18,9 @@ export declare const styledCopy: (text: string, look: Look) => {
18
18
  type: string;
19
19
  };
20
20
  /** Merchant copy over its English default: blank falls back, markup is stripped (the `localizedContent` policy). */
21
- export declare const merchantLabel: (value: string | undefined, fallback: string) => Record<string, string>;
21
+ export declare const merchantLabel: (value: string | undefined, fallback: string) => Record<string, string> & {
22
+ en: string;
23
+ };
22
24
  export type EarnedRow = {
23
25
  /** Copy after the value in its own cell (legacy "on every Delivery", "(included)"). */
24
26
  after?: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.44.0",
3
+ "version": "3.44.1",
4
4
  "description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
5
5
  "license": "MIT",
6
6
  "author": "Rebuy, Inc.",