@rebuy/rebuy 3.19.0 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -54,6 +54,7 @@ __export(src_exports, {
54
54
  CABProductsSection: () => CABProductsSection,
55
55
  CABProgressBarSection: () => CABProgressBarSection,
56
56
  CABProgressTier: () => CABProgressTier,
57
+ CABProgressTierExclusion: () => CABProgressTierExclusion,
57
58
  CABQuantitySection: () => CABQuantitySection,
58
59
  CABResultCountTrigger: () => CABResultCountTrigger,
59
60
  CABResultTagTrigger: () => CABResultTagTrigger,
@@ -127,6 +128,7 @@ __export(src_exports, {
127
128
  ProductInformationBlock: () => ProductInformationBlock,
128
129
  ProductType: () => ProductType,
129
130
  ProgressBar: () => ProgressBar,
131
+ ProgressTierExclusionType: () => ProgressTierExclusionType,
130
132
  ProgressTierType: () => ProgressTierType,
131
133
  ProgressTierUnit: () => ProgressTierUnit,
132
134
  QuantityInput: () => QuantityInput,
@@ -156,6 +158,7 @@ __export(src_exports, {
156
158
  SmartCartComponent: () => SmartCartComponent,
157
159
  SmartCartConfig: () => SmartCartConfig,
158
160
  SmartCartTier: () => SmartCartTier,
161
+ SmartCartTierExclusion: () => SmartCartTierExclusion,
159
162
  SmartCartTierProduct: () => SmartCartTierProduct,
160
163
  Spacing: () => Spacing,
161
164
  TextAlignment: () => TextAlignment,
@@ -248,6 +251,7 @@ __export(src_exports, {
248
251
  popoverPositions: () => popoverPositions,
249
252
  productImageObject: () => productImageObject,
250
253
  productTypes: () => productTypes,
254
+ progressTierExclusionTypes: () => progressTierExclusionTypes,
251
255
  progressTierTypes: () => progressTierTypes,
252
256
  progressTierUnits: () => progressTierUnits,
253
257
  quantityInputs: () => quantityInputs,
@@ -1025,6 +1029,12 @@ var SmartCartTierProduct = import_zod.z.looseObject({
1025
1029
  title: import_zod.z.string().optional(),
1026
1030
  variantIds: import_zod.z.array(import_zod.z.coerce.number()).optional()
1027
1031
  });
1032
+ var SmartCartTierExclusion = import_zod.z.looseObject({
1033
+ collectionIds: import_zod.z.array(import_zod.z.coerce.number()).optional().catch(void 0),
1034
+ productIds: import_zod.z.array(import_zod.z.coerce.number()).optional().catch(void 0),
1035
+ tags: import_zod.z.array(import_zod.z.string()).optional().catch(void 0),
1036
+ type: import_zod.z.string().optional().catch(void 0)
1037
+ });
1028
1038
  var SmartCartTier = import_zod.z.looseObject({
1029
1039
  allProducts: import_zod.z.array(SmartCartTierProduct).optional(),
1030
1040
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -1041,6 +1051,19 @@ var SmartCartTier = import_zod.z.looseObject({
1041
1051
  discountAmountRemainingLabel: import_zod.z.boolean().optional(),
1042
1052
  discountAmountRemainingLabelText: import_zod.z.string().optional(),
1043
1053
  discountType: import_zod.z.string().optional(),
1054
+ /**
1055
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
1056
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
1057
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
1058
+ *
1059
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
1060
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
1061
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
1062
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
1063
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
1064
+ */
1065
+ exclusions: import_zod.z.array(SmartCartTierExclusion).optional().catch(void 0),
1066
+ exclusionsEnabled: import_zod.z.boolean().optional().catch(void 0),
1044
1067
  filterOosVariants: import_zod.z.boolean().optional(),
1045
1068
  id: import_zod.z.string().optional(),
1046
1069
  /**
@@ -1750,6 +1773,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
1750
1773
  var ProgressTierType = freezeEnum(progressTierTypes);
1751
1774
  var progressTierUnits = ["currency", "item"];
1752
1775
  var ProgressTierUnit = freezeEnum(progressTierUnits);
1776
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
1777
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
1753
1778
  var quantityInputs = ["select", "number"];
1754
1779
  var QuantityInput = freezeEnum(quantityInputs);
1755
1780
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -2721,6 +2746,12 @@ var GIFT_LABELS = {
2721
2746
  };
2722
2747
 
2723
2748
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
2749
+ var CABProgressTierExclusion = import_zod24.z.object({
2750
+ collectionIds: import_zod24.z.array(import_zod24.z.number().int().positive()).default(() => []),
2751
+ productIds: import_zod24.z.array(import_zod24.z.number().int().positive()).default(() => []),
2752
+ tags: import_zod24.z.array(import_zod24.z.string().min(1)).default(() => []),
2753
+ type: import_zod24.z.enum(progressTierExclusionTypes)
2754
+ });
2724
2755
  var CABProgressTier = import_zod24.z.object({
2725
2756
  /**
2726
2757
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -2729,6 +2760,13 @@ var CABProgressTier = import_zod24.z.object({
2729
2760
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
2730
2761
  */
2731
2762
  discount: import_zod24.z.object({ amount: import_zod24.z.number().nonnegative(), type: import_zod24.z.enum(["fixedAmount", "percentage"]) }).optional(),
2763
+ /**
2764
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
2765
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
2766
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
2767
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
2768
+ */
2769
+ exclusions: import_zod24.z.array(CABProgressTierExclusion).default(() => []),
2732
2770
  /**
2733
2771
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
2734
2772
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
package/dist/index.mjs CHANGED
@@ -739,6 +739,12 @@ var SmartCartTierProduct = z.looseObject({
739
739
  title: z.string().optional(),
740
740
  variantIds: z.array(z.coerce.number()).optional()
741
741
  });
742
+ var SmartCartTierExclusion = z.looseObject({
743
+ collectionIds: z.array(z.coerce.number()).optional().catch(void 0),
744
+ productIds: z.array(z.coerce.number()).optional().catch(void 0),
745
+ tags: z.array(z.string()).optional().catch(void 0),
746
+ type: z.string().optional().catch(void 0)
747
+ });
742
748
  var SmartCartTier = z.looseObject({
743
749
  allProducts: z.array(SmartCartTierProduct).optional(),
744
750
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -755,6 +761,19 @@ var SmartCartTier = z.looseObject({
755
761
  discountAmountRemainingLabel: z.boolean().optional(),
756
762
  discountAmountRemainingLabelText: z.string().optional(),
757
763
  discountType: z.string().optional(),
764
+ /**
765
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
766
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
767
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
768
+ *
769
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
770
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
771
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
772
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
773
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
774
+ */
775
+ exclusions: z.array(SmartCartTierExclusion).optional().catch(void 0),
776
+ exclusionsEnabled: z.boolean().optional().catch(void 0),
758
777
  filterOosVariants: z.boolean().optional(),
759
778
  id: z.string().optional(),
760
779
  /**
@@ -1464,6 +1483,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
1464
1483
  var ProgressTierType = freezeEnum(progressTierTypes);
1465
1484
  var progressTierUnits = ["currency", "item"];
1466
1485
  var ProgressTierUnit = freezeEnum(progressTierUnits);
1486
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
1487
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
1467
1488
  var quantityInputs = ["select", "number"];
1468
1489
  var QuantityInput = freezeEnum(quantityInputs);
1469
1490
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -2435,6 +2456,12 @@ var GIFT_LABELS = {
2435
2456
  };
2436
2457
 
2437
2458
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
2459
+ var CABProgressTierExclusion = z24.object({
2460
+ collectionIds: z24.array(z24.number().int().positive()).default(() => []),
2461
+ productIds: z24.array(z24.number().int().positive()).default(() => []),
2462
+ tags: z24.array(z24.string().min(1)).default(() => []),
2463
+ type: z24.enum(progressTierExclusionTypes)
2464
+ });
2438
2465
  var CABProgressTier = z24.object({
2439
2466
  /**
2440
2467
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -2443,6 +2470,13 @@ var CABProgressTier = z24.object({
2443
2470
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
2444
2471
  */
2445
2472
  discount: z24.object({ amount: z24.number().nonnegative(), type: z24.enum(["fixedAmount", "percentage"]) }).optional(),
2473
+ /**
2474
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
2475
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
2476
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
2477
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
2478
+ */
2479
+ exclusions: z24.array(CABProgressTierExclusion).default(() => []),
2446
2480
  /**
2447
2481
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
2448
2482
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -4705,6 +4739,7 @@ export {
4705
4739
  CABProductsSection,
4706
4740
  CABProgressBarSection,
4707
4741
  CABProgressTier,
4742
+ CABProgressTierExclusion,
4708
4743
  CABQuantitySection,
4709
4744
  CABResultCountTrigger,
4710
4745
  CABResultTagTrigger,
@@ -4778,6 +4813,7 @@ export {
4778
4813
  ProductInformationBlock,
4779
4814
  ProductType,
4780
4815
  ProgressBar,
4816
+ ProgressTierExclusionType,
4781
4817
  ProgressTierType,
4782
4818
  ProgressTierUnit,
4783
4819
  QuantityInput,
@@ -4807,6 +4843,7 @@ export {
4807
4843
  SmartCartComponent,
4808
4844
  SmartCartConfig,
4809
4845
  SmartCartTier,
4846
+ SmartCartTierExclusion,
4810
4847
  SmartCartTierProduct,
4811
4848
  Spacing,
4812
4849
  TextAlignment,
@@ -4899,6 +4936,7 @@ export {
4899
4936
  popoverPositions,
4900
4937
  productImageObject,
4901
4938
  productTypes,
4939
+ progressTierExclusionTypes,
4902
4940
  progressTierTypes,
4903
4941
  progressTierUnits,
4904
4942
  quantityInputs,
@@ -131,6 +131,13 @@ export declare const CabUserConfig: z.ZodObject<{
131
131
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
132
132
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
133
133
  discountType: z.ZodOptional<z.ZodString>;
134
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
135
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
136
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
137
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
138
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
139
+ }, z.core.$loose>>>>;
140
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
134
141
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
135
142
  id: z.ZodOptional<z.ZodString>;
136
143
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -39,6 +39,7 @@ __export(common_exports, {
39
39
  OffersDirection: () => OffersDirection,
40
40
  PopoverPosition: () => PopoverPosition,
41
41
  ProductType: () => ProductType,
42
+ ProgressTierExclusionType: () => ProgressTierExclusionType,
42
43
  ProgressTierType: () => ProgressTierType,
43
44
  ProgressTierUnit: () => ProgressTierUnit,
44
45
  QuantityInput: () => QuantityInput,
@@ -77,6 +78,7 @@ __export(common_exports, {
77
78
  popoverPositionMap: () => popoverPositionMap,
78
79
  popoverPositions: () => popoverPositions,
79
80
  productTypes: () => productTypes,
81
+ progressTierExclusionTypes: () => progressTierExclusionTypes,
80
82
  progressTierTypes: () => progressTierTypes,
81
83
  progressTierUnits: () => progressTierUnits,
82
84
  quantityInputs: () => quantityInputs,
@@ -218,6 +220,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
218
220
  var ProgressTierType = freezeEnum(progressTierTypes);
219
221
  var progressTierUnits = ["currency", "item"];
220
222
  var ProgressTierUnit = freezeEnum(progressTierUnits);
223
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
224
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
221
225
  var quantityInputs = ["select", "number"];
222
226
  var QuantityInput = freezeEnum(quantityInputs);
223
227
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -121,6 +121,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
121
121
  var ProgressTierType = freezeEnum(progressTierTypes);
122
122
  var progressTierUnits = ["currency", "item"];
123
123
  var ProgressTierUnit = freezeEnum(progressTierUnits);
124
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
125
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
124
126
  var quantityInputs = ["select", "number"];
125
127
  var QuantityInput = freezeEnum(quantityInputs);
126
128
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -216,6 +218,7 @@ export {
216
218
  OffersDirection,
217
219
  PopoverPosition,
218
220
  ProductType,
221
+ ProgressTierExclusionType,
219
222
  ProgressTierType,
220
223
  ProgressTierUnit,
221
224
  QuantityInput,
@@ -254,6 +257,7 @@ export {
254
257
  popoverPositionMap,
255
258
  popoverPositions,
256
259
  productTypes,
260
+ progressTierExclusionTypes,
257
261
  progressTierTypes,
258
262
  progressTierUnits,
259
263
  quantityInputs,
@@ -51,6 +51,7 @@ __export(checkout_and_beyond_exports, {
51
51
  CABProductsSection: () => CABProductsSection,
52
52
  CABProgressBarSection: () => CABProgressBarSection,
53
53
  CABProgressTier: () => CABProgressTier,
54
+ CABProgressTierExclusion: () => CABProgressTierExclusion,
54
55
  CABQuantitySection: () => CABQuantitySection,
55
56
  CABResultCountTrigger: () => CABResultCountTrigger,
56
57
  CABResultTagTrigger: () => CABResultTagTrigger,
@@ -97,6 +98,7 @@ __export(checkout_and_beyond_exports, {
97
98
  OffersDirection: () => OffersDirection,
98
99
  PopoverPosition: () => PopoverPosition,
99
100
  ProductType: () => ProductType,
101
+ ProgressTierExclusionType: () => ProgressTierExclusionType,
100
102
  ProgressTierType: () => ProgressTierType,
101
103
  ProgressTierUnit: () => ProgressTierUnit,
102
104
  QuantityInput: () => QuantityInput,
@@ -172,6 +174,7 @@ __export(checkout_and_beyond_exports, {
172
174
  popoverPositionMap: () => popoverPositionMap,
173
175
  popoverPositions: () => popoverPositions,
174
176
  productTypes: () => productTypes,
177
+ progressTierExclusionTypes: () => progressTierExclusionTypes,
175
178
  progressTierTypes: () => progressTierTypes,
176
179
  progressTierUnits: () => progressTierUnits,
177
180
  quantityInputs: () => quantityInputs,
@@ -321,6 +324,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
321
324
  var ProgressTierType = freezeEnum(progressTierTypes);
322
325
  var progressTierUnits = ["currency", "item"];
323
326
  var ProgressTierUnit = freezeEnum(progressTierUnits);
327
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
328
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
324
329
  var quantityInputs = ["select", "number"];
325
330
  var QuantityInput = freezeEnum(quantityInputs);
326
331
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1254,6 +1259,12 @@ var GIFT_LABELS = {
1254
1259
  };
1255
1260
 
1256
1261
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1262
+ var CABProgressTierExclusion = import_zod14.z.object({
1263
+ collectionIds: import_zod14.z.array(import_zod14.z.number().int().positive()).default(() => []),
1264
+ productIds: import_zod14.z.array(import_zod14.z.number().int().positive()).default(() => []),
1265
+ tags: import_zod14.z.array(import_zod14.z.string().min(1)).default(() => []),
1266
+ type: import_zod14.z.enum(progressTierExclusionTypes)
1267
+ });
1257
1268
  var CABProgressTier = import_zod14.z.object({
1258
1269
  /**
1259
1270
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1262,6 +1273,13 @@ var CABProgressTier = import_zod14.z.object({
1262
1273
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1263
1274
  */
1264
1275
  discount: import_zod14.z.object({ amount: import_zod14.z.number().nonnegative(), type: import_zod14.z.enum(["fixedAmount", "percentage"]) }).optional(),
1276
+ /**
1277
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1278
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1279
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1280
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1281
+ */
1282
+ exclusions: import_zod14.z.array(CABProgressTierExclusion).default(() => []),
1265
1283
  /**
1266
1284
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1267
1285
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -124,6 +124,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
124
124
  var ProgressTierType = freezeEnum(progressTierTypes);
125
125
  var progressTierUnits = ["currency", "item"];
126
126
  var ProgressTierUnit = freezeEnum(progressTierUnits);
127
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
128
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
127
129
  var quantityInputs = ["select", "number"];
128
130
  var QuantityInput = freezeEnum(quantityInputs);
129
131
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1057,6 +1059,12 @@ var GIFT_LABELS = {
1057
1059
  };
1058
1060
 
1059
1061
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1062
+ var CABProgressTierExclusion = z14.object({
1063
+ collectionIds: z14.array(z14.number().int().positive()).default(() => []),
1064
+ productIds: z14.array(z14.number().int().positive()).default(() => []),
1065
+ tags: z14.array(z14.string().min(1)).default(() => []),
1066
+ type: z14.enum(progressTierExclusionTypes)
1067
+ });
1060
1068
  var CABProgressTier = z14.object({
1061
1069
  /**
1062
1070
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1065,6 +1073,13 @@ var CABProgressTier = z14.object({
1065
1073
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1066
1074
  */
1067
1075
  discount: z14.object({ amount: z14.number().nonnegative(), type: z14.enum(["fixedAmount", "percentage"]) }).optional(),
1076
+ /**
1077
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1078
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1079
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1080
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1081
+ */
1082
+ exclusions: z14.array(CABProgressTierExclusion).default(() => []),
1068
1083
  /**
1069
1084
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1070
1085
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -1859,6 +1874,7 @@ export {
1859
1874
  CABProductsSection,
1860
1875
  CABProgressBarSection,
1861
1876
  CABProgressTier,
1877
+ CABProgressTierExclusion,
1862
1878
  CABQuantitySection,
1863
1879
  CABResultCountTrigger,
1864
1880
  CABResultTagTrigger,
@@ -1905,6 +1921,7 @@ export {
1905
1921
  OffersDirection,
1906
1922
  PopoverPosition,
1907
1923
  ProductType,
1924
+ ProgressTierExclusionType,
1908
1925
  ProgressTierType,
1909
1926
  ProgressTierUnit,
1910
1927
  QuantityInput,
@@ -1980,6 +1997,7 @@ export {
1980
1997
  popoverPositionMap,
1981
1998
  popoverPositions,
1982
1999
  productTypes,
2000
+ progressTierExclusionTypes,
1983
2001
  progressTierTypes,
1984
2002
  progressTierUnits,
1985
2003
  quantityInputs,
@@ -10,6 +10,23 @@ export declare const SmartCartTierProduct: z.ZodObject<{
10
10
  title: z.ZodOptional<z.ZodString>;
11
11
  variantIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
12
12
  }, z.core.$loose>;
13
+ /**
14
+ * onsite `exclusions[]` entry on a tier (REB-22305): products, collections or tags whose cart lines do NOT count
15
+ * toward that tier's progress. Loose and coerced like the rest of the config; the converter validates `type`
16
+ * and drops junk ids, so a malformed rule degrades to "matches nothing" rather than failing the tier.
17
+ *
18
+ * `.catch(undefined)` sits on EACH field, not on the rule or the array: `SmartCartTier.exclusions` catches at
19
+ * the array level, so without these a single drifted field (a bare `tags: 'sale'`, a `gid://` collection id
20
+ * that coerces to `NaN`) would fail its element, fail the array, and silently drop every rule on the tier.
21
+ * Per-field, the bad list reads as absent, the converter drops just that rule (empty matching list), and the
22
+ * tier's other rules still apply.
23
+ */
24
+ export declare const SmartCartTierExclusion: z.ZodObject<{
25
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
26
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
27
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
28
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
29
+ }, z.core.$loose>;
13
30
  /**
14
31
  * One reward tier on a smart-cart tiered progress bar. `type` is `product` (gift), `shipping`,
15
32
  * `discount` (order discount applied by the order-discount Function, keyed by `_barId`) or
@@ -42,6 +59,13 @@ export declare const SmartCartTier: z.ZodObject<{
42
59
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
43
60
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
44
61
  discountType: z.ZodOptional<z.ZodString>;
62
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
63
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
64
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
65
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
66
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
67
+ }, z.core.$loose>>>>;
68
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
45
69
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
46
70
  id: z.ZodOptional<z.ZodString>;
47
71
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -104,6 +128,13 @@ export declare const SmartCartBar: z.ZodObject<{
104
128
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
105
129
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
106
130
  discountType: z.ZodOptional<z.ZodString>;
131
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
132
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
133
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
134
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
135
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
136
+ }, z.core.$loose>>>>;
137
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
107
138
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
108
139
  id: z.ZodOptional<z.ZodString>;
109
140
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -165,6 +196,13 @@ export declare const SmartCartComponent: z.ZodObject<{
165
196
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
166
197
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
167
198
  discountType: z.ZodOptional<z.ZodString>;
199
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
200
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
201
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
202
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
203
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
204
+ }, z.core.$loose>>>>;
205
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
168
206
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
169
207
  id: z.ZodOptional<z.ZodString>;
170
208
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -237,6 +275,13 @@ export declare const SmartCartConfig: z.ZodObject<{
237
275
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
238
276
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
239
277
  discountType: z.ZodOptional<z.ZodString>;
278
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
279
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
280
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
281
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
282
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
283
+ }, z.core.$loose>>>>;
284
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
240
285
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
241
286
  id: z.ZodOptional<z.ZodString>;
242
287
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -118,6 +118,12 @@ var SmartCartTierProduct = import_zod2.z.looseObject({
118
118
  title: import_zod2.z.string().optional(),
119
119
  variantIds: import_zod2.z.array(import_zod2.z.coerce.number()).optional()
120
120
  });
121
+ var SmartCartTierExclusion = import_zod2.z.looseObject({
122
+ collectionIds: import_zod2.z.array(import_zod2.z.coerce.number()).optional().catch(void 0),
123
+ productIds: import_zod2.z.array(import_zod2.z.coerce.number()).optional().catch(void 0),
124
+ tags: import_zod2.z.array(import_zod2.z.string()).optional().catch(void 0),
125
+ type: import_zod2.z.string().optional().catch(void 0)
126
+ });
121
127
  var SmartCartTier = import_zod2.z.looseObject({
122
128
  allProducts: import_zod2.z.array(SmartCartTierProduct).optional(),
123
129
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -134,6 +140,19 @@ var SmartCartTier = import_zod2.z.looseObject({
134
140
  discountAmountRemainingLabel: import_zod2.z.boolean().optional(),
135
141
  discountAmountRemainingLabelText: import_zod2.z.string().optional(),
136
142
  discountType: import_zod2.z.string().optional(),
143
+ /**
144
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
145
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
146
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
147
+ *
148
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
149
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
150
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
151
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
152
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
153
+ */
154
+ exclusions: import_zod2.z.array(SmartCartTierExclusion).optional().catch(void 0),
155
+ exclusionsEnabled: import_zod2.z.boolean().optional().catch(void 0),
137
156
  filterOosVariants: import_zod2.z.boolean().optional(),
138
157
  id: import_zod2.z.string().optional(),
139
158
  /**
@@ -94,6 +94,12 @@ var SmartCartTierProduct = z2.looseObject({
94
94
  title: z2.string().optional(),
95
95
  variantIds: z2.array(z2.coerce.number()).optional()
96
96
  });
97
+ var SmartCartTierExclusion = z2.looseObject({
98
+ collectionIds: z2.array(z2.coerce.number()).optional().catch(void 0),
99
+ productIds: z2.array(z2.coerce.number()).optional().catch(void 0),
100
+ tags: z2.array(z2.string()).optional().catch(void 0),
101
+ type: z2.string().optional().catch(void 0)
102
+ });
97
103
  var SmartCartTier = z2.looseObject({
98
104
  allProducts: z2.array(SmartCartTierProduct).optional(),
99
105
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -110,6 +116,19 @@ var SmartCartTier = z2.looseObject({
110
116
  discountAmountRemainingLabel: z2.boolean().optional(),
111
117
  discountAmountRemainingLabelText: z2.string().optional(),
112
118
  discountType: z2.string().optional(),
119
+ /**
120
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
121
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
122
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
123
+ *
124
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
125
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
126
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
127
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
128
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
129
+ */
130
+ exclusions: z2.array(SmartCartTierExclusion).optional().catch(void 0),
131
+ exclusionsEnabled: z2.boolean().optional().catch(void 0),
113
132
  filterOosVariants: z2.boolean().optional(),
114
133
  id: z2.string().optional(),
115
134
  /**
@@ -116,6 +116,13 @@ export declare const UserConfig: z.ZodObject<{
116
116
  discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
117
117
  discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
118
118
  discountType: z.ZodOptional<z.ZodString>;
119
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
120
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
121
+ productIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
122
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
123
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
124
+ }, z.core.$loose>>>>;
125
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
119
126
  filterOosVariants: z.ZodOptional<z.ZodBoolean>;
120
127
  id: z.ZodOptional<z.ZodString>;
121
128
  minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -152,6 +152,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
152
152
  var ProgressTierType = freezeEnum(progressTierTypes);
153
153
  var progressTierUnits = ["currency", "item"];
154
154
  var ProgressTierUnit = freezeEnum(progressTierUnits);
155
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
156
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
155
157
  var quantityInputs = ["select", "number"];
156
158
  var QuantityInput = freezeEnum(quantityInputs);
157
159
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1095,6 +1097,12 @@ var GIFT_LABELS = {
1095
1097
  };
1096
1098
 
1097
1099
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1100
+ var CABProgressTierExclusion = import_zod17.z.object({
1101
+ collectionIds: import_zod17.z.array(import_zod17.z.number().int().positive()).default(() => []),
1102
+ productIds: import_zod17.z.array(import_zod17.z.number().int().positive()).default(() => []),
1103
+ tags: import_zod17.z.array(import_zod17.z.string().min(1)).default(() => []),
1104
+ type: import_zod17.z.enum(progressTierExclusionTypes)
1105
+ });
1098
1106
  var CABProgressTier = import_zod17.z.object({
1099
1107
  /**
1100
1108
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1103,6 +1111,13 @@ var CABProgressTier = import_zod17.z.object({
1103
1111
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1104
1112
  */
1105
1113
  discount: import_zod17.z.object({ amount: import_zod17.z.number().nonnegative(), type: import_zod17.z.enum(["fixedAmount", "percentage"]) }).optional(),
1114
+ /**
1115
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1116
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1117
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1118
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1119
+ */
1120
+ exclusions: import_zod17.z.array(CABProgressTierExclusion).default(() => []),
1106
1121
  /**
1107
1122
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1108
1123
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -127,6 +127,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
127
127
  var ProgressTierType = freezeEnum(progressTierTypes);
128
128
  var progressTierUnits = ["currency", "item"];
129
129
  var ProgressTierUnit = freezeEnum(progressTierUnits);
130
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
131
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
130
132
  var quantityInputs = ["select", "number"];
131
133
  var QuantityInput = freezeEnum(quantityInputs);
132
134
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1070,6 +1072,12 @@ var GIFT_LABELS = {
1070
1072
  };
1071
1073
 
1072
1074
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1075
+ var CABProgressTierExclusion = z17.object({
1076
+ collectionIds: z17.array(z17.number().int().positive()).default(() => []),
1077
+ productIds: z17.array(z17.number().int().positive()).default(() => []),
1078
+ tags: z17.array(z17.string().min(1)).default(() => []),
1079
+ type: z17.enum(progressTierExclusionTypes)
1080
+ });
1073
1081
  var CABProgressTier = z17.object({
1074
1082
  /**
1075
1083
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1078,6 +1086,13 @@ var CABProgressTier = z17.object({
1078
1086
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1079
1087
  */
1080
1088
  discount: z17.object({ amount: z17.number().nonnegative(), type: z17.enum(["fixedAmount", "percentage"]) }).optional(),
1089
+ /**
1090
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1091
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1092
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1093
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1094
+ */
1095
+ exclusions: z17.array(CABProgressTierExclusion).default(() => []),
1081
1096
  /**
1082
1097
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1083
1098
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -214,6 +214,14 @@ export declare const ProgressTierUnit: {
214
214
  readonly item: "item";
215
215
  };
216
216
  export type ProgressTierUnit = (typeof progressTierUnits)[number];
217
+ /** What a per-tier exclusion rule matches cart lines by (onsite `CartProgressBarTierExclusionType`). */
218
+ export declare const progressTierExclusionTypes: readonly ["collections", "products", "tags"];
219
+ export declare const ProgressTierExclusionType: {
220
+ readonly tags: "tags";
221
+ readonly products: "products";
222
+ readonly collections: "collections";
223
+ };
224
+ export type ProgressTierExclusionType = (typeof progressTierExclusionTypes)[number];
217
225
  export declare const quantityInputs: readonly ["select", "number"];
218
226
  export declare const QuantityInput: {
219
227
  readonly number: "number";
@@ -1,6 +1,24 @@
1
1
  import { z } from 'zod';
2
2
  import { CABLayoutSection } from './layout';
3
3
  import { type SectionDetails } from './shared';
4
+ /**
5
+ * One per-tier exclusion rule (onsite `getTierExclusionTotal`): cart lines matching the rule's `type` list
6
+ * are subtracted from THIS tier's progress only, after the bar-level exclusions (gift lines, the exclusion
7
+ * tag, `excludedVariantIds`) have already removed theirs — so a line is never double-counted. The client
8
+ * resolves collections and tags per cart product from the Storefront API; `productIds` match the line's
9
+ * product directly. Lists default empty so a rule always reads as "match nothing" rather than throwing.
10
+ */
11
+ export declare const CABProgressTierExclusion: z.ZodObject<{
12
+ collectionIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
13
+ productIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
14
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
15
+ type: z.ZodEnum<{
16
+ tags: "tags";
17
+ products: "products";
18
+ collections: "collections";
19
+ }>;
20
+ }, z.core.$strip>;
21
+ export type CABProgressTierExclusion = z.infer<typeof CABProgressTierExclusion>;
4
22
  export declare const CABProgressTier: z.ZodObject<{
5
23
  discount: z.ZodOptional<z.ZodObject<{
6
24
  amount: z.ZodNumber;
@@ -9,6 +27,16 @@ export declare const CABProgressTier: z.ZodObject<{
9
27
  fixedAmount: "fixedAmount";
10
28
  }>;
11
29
  }, z.core.$strip>>;
30
+ exclusions: z.ZodDefault<z.ZodArray<z.ZodObject<{
31
+ collectionIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
32
+ productIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
33
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
34
+ type: z.ZodEnum<{
35
+ tags: "tags";
36
+ products: "products";
37
+ collections: "collections";
38
+ }>;
39
+ }, z.core.$strip>>>;
12
40
  filterOosVariants: z.ZodDefault<z.ZodBoolean>;
13
41
  gift: z.ZodOptional<z.ZodObject<{
14
42
  productId: z.ZodNumber;
@@ -329,6 +357,16 @@ export declare const CABProgressBarSection: z.ZodLazy<z.ZodObject<{
329
357
  fixedAmount: "fixedAmount";
330
358
  }>;
331
359
  }, z.core.$strip>>;
360
+ exclusions: z.ZodDefault<z.ZodArray<z.ZodObject<{
361
+ collectionIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
362
+ productIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
363
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
364
+ type: z.ZodEnum<{
365
+ tags: "tags";
366
+ products: "products";
367
+ collections: "collections";
368
+ }>;
369
+ }, z.core.$strip>>>;
332
370
  filterOosVariants: z.ZodDefault<z.ZodBoolean>;
333
371
  gift: z.ZodOptional<z.ZodObject<{
334
372
  productId: z.ZodNumber;
@@ -459,6 +459,12 @@ var SmartCartTierProduct = import_zod5.z.looseObject({
459
459
  title: import_zod5.z.string().optional(),
460
460
  variantIds: import_zod5.z.array(import_zod5.z.coerce.number()).optional()
461
461
  });
462
+ var SmartCartTierExclusion = import_zod5.z.looseObject({
463
+ collectionIds: import_zod5.z.array(import_zod5.z.coerce.number()).optional().catch(void 0),
464
+ productIds: import_zod5.z.array(import_zod5.z.coerce.number()).optional().catch(void 0),
465
+ tags: import_zod5.z.array(import_zod5.z.string()).optional().catch(void 0),
466
+ type: import_zod5.z.string().optional().catch(void 0)
467
+ });
462
468
  var SmartCartTier = import_zod5.z.looseObject({
463
469
  allProducts: import_zod5.z.array(SmartCartTierProduct).optional(),
464
470
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -475,6 +481,19 @@ var SmartCartTier = import_zod5.z.looseObject({
475
481
  discountAmountRemainingLabel: import_zod5.z.boolean().optional(),
476
482
  discountAmountRemainingLabelText: import_zod5.z.string().optional(),
477
483
  discountType: import_zod5.z.string().optional(),
484
+ /**
485
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
486
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
487
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
488
+ *
489
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
490
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
491
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
492
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
493
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
494
+ */
495
+ exclusions: import_zod5.z.array(SmartCartTierExclusion).optional().catch(void 0),
496
+ exclusionsEnabled: import_zod5.z.boolean().optional().catch(void 0),
478
497
  filterOosVariants: import_zod5.z.boolean().optional(),
479
498
  id: import_zod5.z.string().optional(),
480
499
  /**
@@ -775,6 +794,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
775
794
  var ProgressTierType = freezeEnum(progressTierTypes);
776
795
  var progressTierUnits = ["currency", "item"];
777
796
  var ProgressTierUnit = freezeEnum(progressTierUnits);
797
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
798
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
778
799
  var quantityInputs = ["select", "number"];
779
800
  var QuantityInput = freezeEnum(quantityInputs);
780
801
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1689,6 +1710,12 @@ var GIFT_LABELS = {
1689
1710
  };
1690
1711
 
1691
1712
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1713
+ var CABProgressTierExclusion = import_zod21.z.object({
1714
+ collectionIds: import_zod21.z.array(import_zod21.z.number().int().positive()).default(() => []),
1715
+ productIds: import_zod21.z.array(import_zod21.z.number().int().positive()).default(() => []),
1716
+ tags: import_zod21.z.array(import_zod21.z.string().min(1)).default(() => []),
1717
+ type: import_zod21.z.enum(progressTierExclusionTypes)
1718
+ });
1692
1719
  var CABProgressTier = import_zod21.z.object({
1693
1720
  /**
1694
1721
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1697,6 +1724,13 @@ var CABProgressTier = import_zod21.z.object({
1697
1724
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1698
1725
  */
1699
1726
  discount: import_zod21.z.object({ amount: import_zod21.z.number().nonnegative(), type: import_zod21.z.enum(["fixedAmount", "percentage"]) }).optional(),
1727
+ /**
1728
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1729
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1730
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1731
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1732
+ */
1733
+ exclusions: import_zod21.z.array(CABProgressTierExclusion).default(() => []),
1700
1734
  /**
1701
1735
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1702
1736
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -3663,6 +3697,7 @@ var iconSizeOf = (tier) => {
3663
3697
  const px = Math.round(Number.parseFloat(String(tier.productTierElementSize ?? "")));
3664
3698
  return Number.isFinite(px) && px > 0 ? Math.min(px, MAX_ICON_SIZE_PX) : void 0;
3665
3699
  };
3700
+ var positiveIds = (ids) => (ids ?? []).filter((id) => Number.isInteger(id) && id > 0);
3666
3701
  var giftOf = (tier) => {
3667
3702
  if (tier.type !== "product") return void 0;
3668
3703
  const product = tier.products?.[0] ?? tier.allProducts?.[0];
@@ -3672,12 +3707,26 @@ var giftOf = (tier) => {
3672
3707
  return {
3673
3708
  productId,
3674
3709
  title: product.title,
3675
- // One junk (non-positive) variant id must not fail the whole tier's schema — drop only the bad ids.
3676
- variantIds: (product.variantIds ?? []).filter((id) => Number.isInteger(id) && id > 0)
3710
+ variantIds: positiveIds(product.variantIds)
3677
3711
  };
3678
3712
  };
3713
+ var RULE_LIST = { collections: "collectionIds", products: "productIds", tags: "tags" };
3714
+ var exclusionsOf = (tier) => {
3715
+ if (!tier.exclusionsEnabled) return void 0;
3716
+ const rules = (tier.exclusions ?? []).flatMap((rule) => {
3717
+ const parsed = CABProgressTierExclusion.safeParse({
3718
+ ...rule.type === "collections" && { collectionIds: positiveIds(rule.collectionIds) },
3719
+ ...rule.type === "products" && { productIds: positiveIds(rule.productIds) },
3720
+ ...rule.type === "tags" && { tags: (rule.tags ?? []).map((tag) => tag.trim()).filter(Boolean) },
3721
+ type: rule.type
3722
+ });
3723
+ return parsed.success && parsed.data[RULE_LIST[parsed.data.type]].length > 0 ? [parsed.data] : [];
3724
+ });
3725
+ return rules.length > 0 ? rules : void 0;
3726
+ };
3679
3727
  var toTierMeta = (tier) => {
3680
3728
  const discount = discountOf(tier);
3729
+ const exclusions = exclusionsOf(tier);
3681
3730
  const gift = giftOf(tier);
3682
3731
  const iconSizePx = iconSizeOf(tier);
3683
3732
  const isItemCount = tier.unlockConditionType === "item_quantity";
@@ -3685,6 +3734,8 @@ var toTierMeta = (tier) => {
3685
3734
  const type = tier.type;
3686
3735
  return {
3687
3736
  ...discount && { discount },
3737
+ /** Carry per-tier exclusion rules only when enabled and non-empty — absent ⇒ the schema's `[]`, the plain bar-level progress. */
3738
+ ...exclusions && { exclusions },
3688
3739
  /** Carry the OOS-filter flag only on gift tiers that set it — the client drops the tier when its gift is fully out of stock. */
3689
3740
  ...gift && tier.filterOosVariants && { filterOosVariants: true },
3690
3741
  ...gift && { gift },
@@ -408,6 +408,12 @@ var SmartCartTierProduct = z5.looseObject({
408
408
  title: z5.string().optional(),
409
409
  variantIds: z5.array(z5.coerce.number()).optional()
410
410
  });
411
+ var SmartCartTierExclusion = z5.looseObject({
412
+ collectionIds: z5.array(z5.coerce.number()).optional().catch(void 0),
413
+ productIds: z5.array(z5.coerce.number()).optional().catch(void 0),
414
+ tags: z5.array(z5.string()).optional().catch(void 0),
415
+ type: z5.string().optional().catch(void 0)
416
+ });
411
417
  var SmartCartTier = z5.looseObject({
412
418
  allProducts: z5.array(SmartCartTierProduct).optional(),
413
419
  /** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
@@ -424,6 +430,19 @@ var SmartCartTier = z5.looseObject({
424
430
  discountAmountRemainingLabel: z5.boolean().optional(),
425
431
  discountAmountRemainingLabelText: z5.string().optional(),
426
432
  discountType: z5.string().optional(),
433
+ /**
434
+ * onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
435
+ * `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
436
+ * one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
437
+ *
438
+ * `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
439
+ * (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
440
+ * `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
441
+ * types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
442
+ * A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
443
+ */
444
+ exclusions: z5.array(SmartCartTierExclusion).optional().catch(void 0),
445
+ exclusionsEnabled: z5.boolean().optional().catch(void 0),
427
446
  filterOosVariants: z5.boolean().optional(),
428
447
  id: z5.string().optional(),
429
448
  /**
@@ -724,6 +743,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
724
743
  var ProgressTierType = freezeEnum(progressTierTypes);
725
744
  var progressTierUnits = ["currency", "item"];
726
745
  var ProgressTierUnit = freezeEnum(progressTierUnits);
746
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
747
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
727
748
  var quantityInputs = ["select", "number"];
728
749
  var QuantityInput = freezeEnum(quantityInputs);
729
750
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1638,6 +1659,12 @@ var GIFT_LABELS = {
1638
1659
  };
1639
1660
 
1640
1661
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1662
+ var CABProgressTierExclusion = z21.object({
1663
+ collectionIds: z21.array(z21.number().int().positive()).default(() => []),
1664
+ productIds: z21.array(z21.number().int().positive()).default(() => []),
1665
+ tags: z21.array(z21.string().min(1)).default(() => []),
1666
+ type: z21.enum(progressTierExclusionTypes)
1667
+ });
1641
1668
  var CABProgressTier = z21.object({
1642
1669
  /**
1643
1670
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1646,6 +1673,13 @@ var CABProgressTier = z21.object({
1646
1673
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1647
1674
  */
1648
1675
  discount: z21.object({ amount: z21.number().nonnegative(), type: z21.enum(["fixedAmount", "percentage"]) }).optional(),
1676
+ /**
1677
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1678
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1679
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1680
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1681
+ */
1682
+ exclusions: z21.array(CABProgressTierExclusion).default(() => []),
1649
1683
  /**
1650
1684
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1651
1685
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -3612,6 +3646,7 @@ var iconSizeOf = (tier) => {
3612
3646
  const px = Math.round(Number.parseFloat(String(tier.productTierElementSize ?? "")));
3613
3647
  return Number.isFinite(px) && px > 0 ? Math.min(px, MAX_ICON_SIZE_PX) : void 0;
3614
3648
  };
3649
+ var positiveIds = (ids) => (ids ?? []).filter((id) => Number.isInteger(id) && id > 0);
3615
3650
  var giftOf = (tier) => {
3616
3651
  if (tier.type !== "product") return void 0;
3617
3652
  const product = tier.products?.[0] ?? tier.allProducts?.[0];
@@ -3621,12 +3656,26 @@ var giftOf = (tier) => {
3621
3656
  return {
3622
3657
  productId,
3623
3658
  title: product.title,
3624
- // One junk (non-positive) variant id must not fail the whole tier's schema — drop only the bad ids.
3625
- variantIds: (product.variantIds ?? []).filter((id) => Number.isInteger(id) && id > 0)
3659
+ variantIds: positiveIds(product.variantIds)
3626
3660
  };
3627
3661
  };
3662
+ var RULE_LIST = { collections: "collectionIds", products: "productIds", tags: "tags" };
3663
+ var exclusionsOf = (tier) => {
3664
+ if (!tier.exclusionsEnabled) return void 0;
3665
+ const rules = (tier.exclusions ?? []).flatMap((rule) => {
3666
+ const parsed = CABProgressTierExclusion.safeParse({
3667
+ ...rule.type === "collections" && { collectionIds: positiveIds(rule.collectionIds) },
3668
+ ...rule.type === "products" && { productIds: positiveIds(rule.productIds) },
3669
+ ...rule.type === "tags" && { tags: (rule.tags ?? []).map((tag) => tag.trim()).filter(Boolean) },
3670
+ type: rule.type
3671
+ });
3672
+ return parsed.success && parsed.data[RULE_LIST[parsed.data.type]].length > 0 ? [parsed.data] : [];
3673
+ });
3674
+ return rules.length > 0 ? rules : void 0;
3675
+ };
3628
3676
  var toTierMeta = (tier) => {
3629
3677
  const discount = discountOf(tier);
3678
+ const exclusions = exclusionsOf(tier);
3630
3679
  const gift = giftOf(tier);
3631
3680
  const iconSizePx = iconSizeOf(tier);
3632
3681
  const isItemCount = tier.unlockConditionType === "item_quantity";
@@ -3634,6 +3683,8 @@ var toTierMeta = (tier) => {
3634
3683
  const type = tier.type;
3635
3684
  return {
3636
3685
  ...discount && { discount },
3686
+ /** Carry per-tier exclusion rules only when enabled and non-empty — absent ⇒ the schema's `[]`, the plain bar-level progress. */
3687
+ ...exclusions && { exclusions },
3637
3688
  /** Carry the OOS-filter flag only on gift tiers that set it — the client drops the tier when its gift is fully out of stock. */
3638
3689
  ...gift && tier.filterOosVariants && { filterOosVariants: true },
3639
3690
  ...gift && { gift },
@@ -178,6 +178,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
178
178
  var ProgressTierType = freezeEnum(progressTierTypes);
179
179
  var progressTierUnits = ["currency", "item"];
180
180
  var ProgressTierUnit = freezeEnum(progressTierUnits);
181
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
182
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
181
183
  var quantityInputs = ["select", "number"];
182
184
  var QuantityInput = freezeEnum(quantityInputs);
183
185
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1092,6 +1094,12 @@ var GIFT_LABELS = {
1092
1094
  };
1093
1095
 
1094
1096
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1097
+ var CABProgressTierExclusion = import_zod15.z.object({
1098
+ collectionIds: import_zod15.z.array(import_zod15.z.number().int().positive()).default(() => []),
1099
+ productIds: import_zod15.z.array(import_zod15.z.number().int().positive()).default(() => []),
1100
+ tags: import_zod15.z.array(import_zod15.z.string().min(1)).default(() => []),
1101
+ type: import_zod15.z.enum(progressTierExclusionTypes)
1102
+ });
1095
1103
  var CABProgressTier = import_zod15.z.object({
1096
1104
  /**
1097
1105
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1100,6 +1108,13 @@ var CABProgressTier = import_zod15.z.object({
1100
1108
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1101
1109
  */
1102
1110
  discount: import_zod15.z.object({ amount: import_zod15.z.number().nonnegative(), type: import_zod15.z.enum(["fixedAmount", "percentage"]) }).optional(),
1111
+ /**
1112
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1113
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1114
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1115
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1116
+ */
1117
+ exclusions: import_zod15.z.array(CABProgressTierExclusion).default(() => []),
1103
1118
  /**
1104
1119
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1105
1120
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -3497,6 +3512,7 @@ var iconSizeOf = (tier) => {
3497
3512
  const px = Math.round(Number.parseFloat(String(tier.productTierElementSize ?? "")));
3498
3513
  return Number.isFinite(px) && px > 0 ? Math.min(px, MAX_ICON_SIZE_PX) : void 0;
3499
3514
  };
3515
+ var positiveIds = (ids) => (ids ?? []).filter((id) => Number.isInteger(id) && id > 0);
3500
3516
  var giftOf = (tier) => {
3501
3517
  if (tier.type !== "product") return void 0;
3502
3518
  const product = tier.products?.[0] ?? tier.allProducts?.[0];
@@ -3506,12 +3522,26 @@ var giftOf = (tier) => {
3506
3522
  return {
3507
3523
  productId,
3508
3524
  title: product.title,
3509
- // One junk (non-positive) variant id must not fail the whole tier's schema — drop only the bad ids.
3510
- variantIds: (product.variantIds ?? []).filter((id) => Number.isInteger(id) && id > 0)
3525
+ variantIds: positiveIds(product.variantIds)
3511
3526
  };
3512
3527
  };
3528
+ var RULE_LIST = { collections: "collectionIds", products: "productIds", tags: "tags" };
3529
+ var exclusionsOf = (tier) => {
3530
+ if (!tier.exclusionsEnabled) return void 0;
3531
+ const rules = (tier.exclusions ?? []).flatMap((rule) => {
3532
+ const parsed = CABProgressTierExclusion.safeParse({
3533
+ ...rule.type === "collections" && { collectionIds: positiveIds(rule.collectionIds) },
3534
+ ...rule.type === "products" && { productIds: positiveIds(rule.productIds) },
3535
+ ...rule.type === "tags" && { tags: (rule.tags ?? []).map((tag) => tag.trim()).filter(Boolean) },
3536
+ type: rule.type
3537
+ });
3538
+ return parsed.success && parsed.data[RULE_LIST[parsed.data.type]].length > 0 ? [parsed.data] : [];
3539
+ });
3540
+ return rules.length > 0 ? rules : void 0;
3541
+ };
3513
3542
  var toTierMeta = (tier) => {
3514
3543
  const discount = discountOf(tier);
3544
+ const exclusions = exclusionsOf(tier);
3515
3545
  const gift = giftOf(tier);
3516
3546
  const iconSizePx = iconSizeOf(tier);
3517
3547
  const isItemCount = tier.unlockConditionType === "item_quantity";
@@ -3519,6 +3549,8 @@ var toTierMeta = (tier) => {
3519
3549
  const type = tier.type;
3520
3550
  return {
3521
3551
  ...discount && { discount },
3552
+ /** Carry per-tier exclusion rules only when enabled and non-empty — absent ⇒ the schema's `[]`, the plain bar-level progress. */
3553
+ ...exclusions && { exclusions },
3522
3554
  /** Carry the OOS-filter flag only on gift tiers that set it — the client drops the tier when its gift is fully out of stock. */
3523
3555
  ...gift && tier.filterOosVariants && { filterOosVariants: true },
3524
3556
  ...gift && { gift },
@@ -119,6 +119,8 @@ var progressTierTypes = ["decorative", "discount", "product", "shipping"];
119
119
  var ProgressTierType = freezeEnum(progressTierTypes);
120
120
  var progressTierUnits = ["currency", "item"];
121
121
  var ProgressTierUnit = freezeEnum(progressTierUnits);
122
+ var progressTierExclusionTypes = ["collections", "products", "tags"];
123
+ var ProgressTierExclusionType = freezeEnum(progressTierExclusionTypes);
122
124
  var quantityInputs = ["select", "number"];
123
125
  var QuantityInput = freezeEnum(quantityInputs);
124
126
  var resultCountOperators = ["equals", "greater_than", "less_than"];
@@ -1033,6 +1035,12 @@ var GIFT_LABELS = {
1033
1035
  };
1034
1036
 
1035
1037
  // src/schema/widgets/checkout-and-beyond/progressBar.ts
1038
+ var CABProgressTierExclusion = z15.object({
1039
+ collectionIds: z15.array(z15.number().int().positive()).default(() => []),
1040
+ productIds: z15.array(z15.number().int().positive()).default(() => []),
1041
+ tags: z15.array(z15.string().min(1)).default(() => []),
1042
+ type: z15.enum(progressTierExclusionTypes)
1043
+ });
1036
1044
  var CABProgressTier = z15.object({
1037
1045
  /**
1038
1046
  * Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
@@ -1041,6 +1049,13 @@ var CABProgressTier = z15.object({
1041
1049
  * `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
1042
1050
  */
1043
1051
  discount: z15.object({ amount: z15.number().nonnegative(), type: z15.enum(["fixedAmount", "percentage"]) }).optional(),
1052
+ /**
1053
+ * Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
1054
+ * toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
1055
+ * the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
1056
+ * `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
1057
+ */
1058
+ exclusions: z15.array(CABProgressTierExclusion).default(() => []),
1044
1059
  /**
1045
1060
  * Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
1046
1061
  * tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
@@ -3438,6 +3453,7 @@ var iconSizeOf = (tier) => {
3438
3453
  const px = Math.round(Number.parseFloat(String(tier.productTierElementSize ?? "")));
3439
3454
  return Number.isFinite(px) && px > 0 ? Math.min(px, MAX_ICON_SIZE_PX) : void 0;
3440
3455
  };
3456
+ var positiveIds = (ids) => (ids ?? []).filter((id) => Number.isInteger(id) && id > 0);
3441
3457
  var giftOf = (tier) => {
3442
3458
  if (tier.type !== "product") return void 0;
3443
3459
  const product = tier.products?.[0] ?? tier.allProducts?.[0];
@@ -3447,12 +3463,26 @@ var giftOf = (tier) => {
3447
3463
  return {
3448
3464
  productId,
3449
3465
  title: product.title,
3450
- // One junk (non-positive) variant id must not fail the whole tier's schema — drop only the bad ids.
3451
- variantIds: (product.variantIds ?? []).filter((id) => Number.isInteger(id) && id > 0)
3466
+ variantIds: positiveIds(product.variantIds)
3452
3467
  };
3453
3468
  };
3469
+ var RULE_LIST = { collections: "collectionIds", products: "productIds", tags: "tags" };
3470
+ var exclusionsOf = (tier) => {
3471
+ if (!tier.exclusionsEnabled) return void 0;
3472
+ const rules = (tier.exclusions ?? []).flatMap((rule) => {
3473
+ const parsed = CABProgressTierExclusion.safeParse({
3474
+ ...rule.type === "collections" && { collectionIds: positiveIds(rule.collectionIds) },
3475
+ ...rule.type === "products" && { productIds: positiveIds(rule.productIds) },
3476
+ ...rule.type === "tags" && { tags: (rule.tags ?? []).map((tag) => tag.trim()).filter(Boolean) },
3477
+ type: rule.type
3478
+ });
3479
+ return parsed.success && parsed.data[RULE_LIST[parsed.data.type]].length > 0 ? [parsed.data] : [];
3480
+ });
3481
+ return rules.length > 0 ? rules : void 0;
3482
+ };
3454
3483
  var toTierMeta = (tier) => {
3455
3484
  const discount = discountOf(tier);
3485
+ const exclusions = exclusionsOf(tier);
3456
3486
  const gift = giftOf(tier);
3457
3487
  const iconSizePx = iconSizeOf(tier);
3458
3488
  const isItemCount = tier.unlockConditionType === "item_quantity";
@@ -3460,6 +3490,8 @@ var toTierMeta = (tier) => {
3460
3490
  const type = tier.type;
3461
3491
  return {
3462
3492
  ...discount && { discount },
3493
+ /** Carry per-tier exclusion rules only when enabled and non-empty — absent ⇒ the schema's `[]`, the plain bar-level progress. */
3494
+ ...exclusions && { exclusions },
3463
3495
  /** Carry the OOS-filter flag only on gift tiers that set it — the client drops the tier when its gift is fully out of stock. */
3464
3496
  ...gift && tier.filterOosVariants && { filterOosVariants: true },
3465
3497
  ...gift && { gift },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.19.0",
3
+ "version": "3.20.0",
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.",