@rebuy/rebuy 3.61.0 → 3.62.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
@@ -121,6 +121,7 @@ __export(src_exports, {
121
121
  DiscountSource: () => DiscountSource,
122
122
  DiscountType: () => DiscountType,
123
123
  EditorMode: () => EditorMode,
124
+ EngineSmartCart: () => EngineSmartCart,
124
125
  EventCalculationSchema: () => EventCalculationSchema,
125
126
  ExitPolicySchema: () => ExitPolicySchema,
126
127
  FinishedPurchaseSchema: () => FinishedPurchaseSchema,
@@ -1378,6 +1379,7 @@ var SmartCartComponent = import_zod.z.looseObject({
1378
1379
  var SmartCartConfig = import_zod.z.looseObject({
1379
1380
  components: import_zod.z.array(SmartCartComponent).optional()
1380
1381
  });
1382
+ var EngineSmartCart = import_zod.z.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
1381
1383
 
1382
1384
  // src/schema/cabShopConfig.ts
1383
1385
  var CabActiveExperiment = import_zod2.z.object({
@@ -1475,7 +1477,7 @@ var CabShopConfig = import_zod2.z.object({
1475
1477
  });
1476
1478
  var CabUserConfig = import_zod2.z.object({
1477
1479
  shop: CabShopConfig,
1478
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
1480
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
1479
1481
  });
1480
1482
 
1481
1483
  // src/schema/metadata.ts
@@ -5263,10 +5265,14 @@ var import_zod93 = require("zod");
5263
5265
  var UserConfig = import_zod93.z.object({
5264
5266
  shop: ShopConfig,
5265
5267
  /**
5266
- * `.catch(undefined)` is the safety bar: smart-cart shape drift can NEVER fail `fetchUserConfig`,
5267
- * which resolves the apiKey for every offers/monetize/analytics/gift request.
5268
+ * Parsed like `CabUserConfig.smartCart`: the engine's `false` reads as `null` ({@link EngineSmartCart}), a
5269
+ * drifted component loses only its own settings (`SmartCartComponent.settings` catches per component), and
5270
+ * `.catch(undefined)` is the last safety bar so a wholly unreadable config can never fail the parse that
5271
+ * resolves the shop's apiKey. It lands on `undefined`, not the `DRIFTED_SMART_CART` sentinel: no drift
5272
+ * reporter reads this schema (the server parses `CabUserConfig`), and that sentinel's identity is not
5273
+ * exported from `./schema`, so a consumer could not tell it apart from an empty config anyway.
5268
5274
  */
5269
- smartCart: SmartCartConfig.nullish().catch(void 0),
5275
+ smartCart: EngineSmartCart.catch(void 0),
5270
5276
  smartFlows: import_zod93.z.array(import_zod93.z.unknown())
5271
5277
  });
5272
5278
 
package/dist/index.mjs CHANGED
@@ -896,6 +896,7 @@ var SmartCartComponent = z.looseObject({
896
896
  var SmartCartConfig = z.looseObject({
897
897
  components: z.array(SmartCartComponent).optional()
898
898
  });
899
+ var EngineSmartCart = z.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
899
900
 
900
901
  // src/schema/cabShopConfig.ts
901
902
  var CabActiveExperiment = z2.object({
@@ -993,7 +994,7 @@ var CabShopConfig = z2.object({
993
994
  });
994
995
  var CabUserConfig = z2.object({
995
996
  shop: CabShopConfig,
996
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
997
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
997
998
  });
998
999
 
999
1000
  // src/schema/metadata.ts
@@ -4781,10 +4782,14 @@ import { z as z93 } from "zod";
4781
4782
  var UserConfig = z93.object({
4782
4783
  shop: ShopConfig,
4783
4784
  /**
4784
- * `.catch(undefined)` is the safety bar: smart-cart shape drift can NEVER fail `fetchUserConfig`,
4785
- * which resolves the apiKey for every offers/monetize/analytics/gift request.
4785
+ * Parsed like `CabUserConfig.smartCart`: the engine's `false` reads as `null` ({@link EngineSmartCart}), a
4786
+ * drifted component loses only its own settings (`SmartCartComponent.settings` catches per component), and
4787
+ * `.catch(undefined)` is the last safety bar so a wholly unreadable config can never fail the parse that
4788
+ * resolves the shop's apiKey. It lands on `undefined`, not the `DRIFTED_SMART_CART` sentinel: no drift
4789
+ * reporter reads this schema (the server parses `CabUserConfig`), and that sentinel's identity is not
4790
+ * exported from `./schema`, so a consumer could not tell it apart from an empty config anyway.
4786
4791
  */
4787
- smartCart: SmartCartConfig.nullish().catch(void 0),
4792
+ smartCart: EngineSmartCart.catch(void 0),
4788
4793
  smartFlows: z93.array(z93.unknown())
4789
4794
  });
4790
4795
 
@@ -6778,6 +6783,7 @@ export {
6778
6783
  DiscountSource,
6779
6784
  DiscountType,
6780
6785
  EditorMode,
6786
+ EngineSmartCart,
6781
6787
  EventCalculationSchema,
6782
6788
  ExitPolicySchema,
6783
6789
  FinishedPurchaseSchema,
@@ -62,8 +62,9 @@ export type CabShopConfig = z.infer<typeof CabShopConfig>;
62
62
  *
63
63
  * The catch lands on the {@link DRIFTED_SMART_CART} identity sentinel (no components ⇒ no bars), not
64
64
  * `undefined`, so the server's progress-bar resolution can tell a drifted config from a shop with no smart
65
- * cart (`null`/absent stay `null`/`undefined`) and emit a `shopConfig` drift (REB-25139: this catch used to
66
- * drop every progress bar for the shop silently).
65
+ * cart (`null`/absent stay `null`/`undefined`, and the engine's `false` reads as `null` via
66
+ * {@link EngineSmartCart}) and emit a `shopConfig` drift (REB-25139: this catch used to drop every progress
67
+ * bar for the shop silently; REB-25187: `false` used to land on the sentinel and report a false drift).
67
68
  */
68
69
  export declare const CabUserConfig: z.ZodObject<{
69
70
  shop: z.ZodObject<{
@@ -98,7 +99,7 @@ export declare const CabUserConfig: z.ZodObject<{
98
99
  shopId: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
99
100
  shopName: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
100
101
  }, z.core.$strip>;
101
- smartCart: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
102
+ smartCart: z.ZodCatch<z.ZodPreprocess<z.ZodOptional<z.ZodNullable<z.ZodObject<{
102
103
  components: z.ZodOptional<z.ZodArray<z.ZodObject<{
103
104
  settings: z.ZodCatch<z.ZodPreprocess<z.ZodOptional<z.ZodObject<{
104
105
  bars: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -172,6 +173,6 @@ export declare const CabUserConfig: z.ZodObject<{
172
173
  }, z.core.$loose>>>>;
173
174
  type: z.ZodOptional<z.ZodString>;
174
175
  }, z.core.$loose>>>;
175
- }, z.core.$loose>>>>;
176
+ }, z.core.$loose>>>>>;
176
177
  }, z.core.$strip>;
177
178
  export type CabUserConfig = z.infer<typeof CabUserConfig>;
@@ -245,7 +245,7 @@ export declare const SmartCartComponent: z.ZodObject<{
245
245
  * The subset of the shop's smart-cart config the checkout extension reads: the
246
246
  * `tiered_progress_bar` component(s) and their bars/tiers. The rest of the onsite
247
247
  * smart-cart config (buy-more-save-more, custom templates/CSS, callbacks) is out of
248
- * scope — `looseObject` preserves-but-ignores it. Parsed under `UserConfig.smartCart`
248
+ * scope — `looseObject` preserves-but-ignores it. Parsed via {@link EngineSmartCart} under `UserConfig.smartCart`
249
249
  * (`.catch(undefined)`) and `CabUserConfig.smartCart` (`.catch` to a drift sentinel) so a shape
250
250
  * drift here can NEVER break apiKey resolution for offers / monetize / analytics / gift validation.
251
251
  */
@@ -324,6 +324,88 @@ export declare const SmartCartConfig: z.ZodObject<{
324
324
  type: z.ZodOptional<z.ZodString>;
325
325
  }, z.core.$loose>>>;
326
326
  }, z.core.$loose>;
327
+ /**
328
+ * The `smartCart` field of `/api/v1/user/config` as the engine actually sends it. A shop with no enabled
329
+ * Smart Cart arrives as `smart_cart: false` (the engine's "none" vocabulary, like `null`/absent), so `false`
330
+ * reads as `null` before the object schema sees it: no smart cart, no bars, and no drift. Only `false` — a
331
+ * `true`, a string or a number is a genuine retype and falls through to the reader's own `.catch`.
332
+ * `UserConfig` and `CabUserConfig` both parse through this, each adding its own config-level `.catch`.
333
+ */
334
+ export declare const EngineSmartCart: z.ZodPreprocess<z.ZodOptional<z.ZodNullable<z.ZodObject<{
335
+ components: z.ZodOptional<z.ZodArray<z.ZodObject<{
336
+ settings: z.ZodCatch<z.ZodPreprocess<z.ZodOptional<z.ZodObject<{
337
+ bars: z.ZodOptional<z.ZodArray<z.ZodObject<{
338
+ countryCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
339
+ excludeDiscountInSubtotal: z.ZodOptional<z.ZodBoolean>;
340
+ excludedProducts: z.ZodOptional<z.ZodArray<z.ZodObject<{
341
+ variantIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
342
+ }, z.core.$loose>>>;
343
+ freeShippingOnSubscription: z.ZodOptional<z.ZodBoolean>;
344
+ geolocationEnabled: z.ZodOptional<z.ZodBoolean>;
345
+ id: z.ZodOptional<z.ZodString>;
346
+ language: z.ZodPreprocess<z.ZodOptional<z.ZodObject<{
347
+ freeShippingReached: z.ZodOptional<z.ZodString>;
348
+ freeShippingRemaining: z.ZodOptional<z.ZodString>;
349
+ giftAreaTitle: z.ZodOptional<z.ZodString>;
350
+ }, z.core.$loose>>>;
351
+ name: z.ZodOptional<z.ZodString>;
352
+ tiers: z.ZodOptional<z.ZodArray<z.ZodObject<{
353
+ allProducts: z.ZodOptional<z.ZodArray<z.ZodObject<{
354
+ handle: z.ZodOptional<z.ZodString>;
355
+ id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
356
+ productId: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
357
+ title: z.ZodOptional<z.ZodString>;
358
+ variantIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
359
+ }, z.core.$loose>>>;
360
+ customTierLabel: z.ZodOptional<z.ZodBoolean>;
361
+ customTierLabelText: z.ZodOptional<z.ZodString>;
362
+ decorativeAmountReachedLabel: z.ZodOptional<z.ZodBoolean>;
363
+ decorativeAmountReachedLabelText: z.ZodOptional<z.ZodString>;
364
+ decorativeAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
365
+ decorativeAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
366
+ discountAmountFromTotal: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
367
+ discountAmountReachedLabel: z.ZodOptional<z.ZodBoolean>;
368
+ discountAmountReachedLabelText: z.ZodOptional<z.ZodString>;
369
+ discountAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
370
+ discountAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
371
+ discountType: z.ZodOptional<z.ZodString>;
372
+ exclusions: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
373
+ collectionIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
374
+ products: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
375
+ productId: z.ZodCatch<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
376
+ variantIds: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
377
+ }, z.core.$loose>>>>;
378
+ tags: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
379
+ type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
380
+ }, z.core.$loose>>>>;
381
+ exclusionsEnabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
382
+ filterOosVariants: z.ZodOptional<z.ZodBoolean>;
383
+ id: z.ZodOptional<z.ZodString>;
384
+ minimum: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
385
+ presentmentCurrencies: z.ZodPreprocess<z.ZodOptional<z.ZodObject<{
386
+ enabled: z.ZodOptional<z.ZodBoolean>;
387
+ }, z.core.$loose>>>;
388
+ productAmountReachedLabel: z.ZodOptional<z.ZodBoolean>;
389
+ productAmountReachedLabelText: z.ZodOptional<z.ZodString>;
390
+ productAmountRemainingLabel: z.ZodOptional<z.ZodBoolean>;
391
+ productAmountRemainingLabelText: z.ZodOptional<z.ZodString>;
392
+ products: z.ZodOptional<z.ZodArray<z.ZodObject<{
393
+ handle: z.ZodOptional<z.ZodString>;
394
+ id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
395
+ productId: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
396
+ title: z.ZodOptional<z.ZodString>;
397
+ variantIds: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
398
+ }, z.core.$loose>>>;
399
+ productTierElementSize: z.ZodCatch<z.ZodOptional<z.ZodString>>;
400
+ productTierImage: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
401
+ type: z.ZodOptional<z.ZodString>;
402
+ unlockConditionType: z.ZodOptional<z.ZodString>;
403
+ }, z.core.$loose>>>;
404
+ }, z.core.$loose>>>;
405
+ }, z.core.$loose>>>>;
406
+ type: z.ZodOptional<z.ZodString>;
407
+ }, z.core.$loose>>>;
408
+ }, z.core.$loose>>>>;
327
409
  export type SmartCartBar = z.infer<typeof SmartCartBar>;
328
410
  export type SmartCartConfig = z.infer<typeof SmartCartConfig>;
329
411
  export type SmartCartTier = z.infer<typeof SmartCartTier>;
@@ -277,14 +277,19 @@ var SmartCartComponent = import_zod2.z.looseObject({
277
277
  var SmartCartConfig = import_zod2.z.looseObject({
278
278
  components: import_zod2.z.array(SmartCartComponent).optional()
279
279
  });
280
+ var EngineSmartCart = import_zod2.z.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
280
281
 
281
282
  // src/schema/userConfig.ts
282
283
  var UserConfig = import_zod3.z.object({
283
284
  shop: ShopConfig,
284
285
  /**
285
- * `.catch(undefined)` is the safety bar: smart-cart shape drift can NEVER fail `fetchUserConfig`,
286
- * which resolves the apiKey for every offers/monetize/analytics/gift request.
286
+ * Parsed like `CabUserConfig.smartCart`: the engine's `false` reads as `null` ({@link EngineSmartCart}), a
287
+ * drifted component loses only its own settings (`SmartCartComponent.settings` catches per component), and
288
+ * `.catch(undefined)` is the last safety bar so a wholly unreadable config can never fail the parse that
289
+ * resolves the shop's apiKey. It lands on `undefined`, not the `DRIFTED_SMART_CART` sentinel: no drift
290
+ * reporter reads this schema (the server parses `CabUserConfig`), and that sentinel's identity is not
291
+ * exported from `./schema`, so a consumer could not tell it apart from an empty config anyway.
287
292
  */
288
- smartCart: SmartCartConfig.nullish().catch(void 0),
293
+ smartCart: EngineSmartCart.catch(void 0),
289
294
  smartFlows: import_zod3.z.array(import_zod3.z.unknown())
290
295
  });
@@ -253,15 +253,20 @@ var SmartCartComponent = z2.looseObject({
253
253
  var SmartCartConfig = z2.looseObject({
254
254
  components: z2.array(SmartCartComponent).optional()
255
255
  });
256
+ var EngineSmartCart = z2.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
256
257
 
257
258
  // src/schema/userConfig.ts
258
259
  var UserConfig = z3.object({
259
260
  shop: ShopConfig,
260
261
  /**
261
- * `.catch(undefined)` is the safety bar: smart-cart shape drift can NEVER fail `fetchUserConfig`,
262
- * which resolves the apiKey for every offers/monetize/analytics/gift request.
262
+ * Parsed like `CabUserConfig.smartCart`: the engine's `false` reads as `null` ({@link EngineSmartCart}), a
263
+ * drifted component loses only its own settings (`SmartCartComponent.settings` catches per component), and
264
+ * `.catch(undefined)` is the last safety bar so a wholly unreadable config can never fail the parse that
265
+ * resolves the shop's apiKey. It lands on `undefined`, not the `DRIFTED_SMART_CART` sentinel: no drift
266
+ * reporter reads this schema (the server parses `CabUserConfig`), and that sentinel's identity is not
267
+ * exported from `./schema`, so a consumer could not tell it apart from an empty config anyway.
263
268
  */
264
- smartCart: SmartCartConfig.nullish().catch(void 0),
269
+ smartCart: EngineSmartCart.catch(void 0),
265
270
  smartFlows: z3.array(z3.unknown())
266
271
  });
267
272
  export {
@@ -78,7 +78,7 @@ export declare const UserConfig: z.ZodObject<{
78
78
  storefrontAccessToken: z.ZodNullable<z.ZodString>;
79
79
  useRebuyIcons: z.ZodBoolean;
80
80
  }, z.core.$strip>;
81
- smartCart: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
81
+ smartCart: z.ZodCatch<z.ZodPreprocess<z.ZodOptional<z.ZodNullable<z.ZodObject<{
82
82
  components: z.ZodOptional<z.ZodArray<z.ZodObject<{
83
83
  settings: z.ZodCatch<z.ZodPreprocess<z.ZodOptional<z.ZodObject<{
84
84
  bars: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -152,7 +152,7 @@ export declare const UserConfig: z.ZodObject<{
152
152
  }, z.core.$loose>>>>;
153
153
  type: z.ZodOptional<z.ZodString>;
154
154
  }, z.core.$loose>>>;
155
- }, z.core.$loose>>>>;
155
+ }, z.core.$loose>>>>>;
156
156
  smartFlows: z.ZodArray<z.ZodUnknown>;
157
157
  }, z.core.$strip>;
158
158
  export type UserConfig = z.infer<typeof UserConfig>;
@@ -1726,6 +1726,7 @@ var SmartCartComponent = import_zod19.z.looseObject({
1726
1726
  var SmartCartConfig = import_zod19.z.looseObject({
1727
1727
  components: import_zod19.z.array(SmartCartComponent).optional()
1728
1728
  });
1729
+ var EngineSmartCart = import_zod19.z.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
1729
1730
 
1730
1731
  // src/schema/cabShopConfig.ts
1731
1732
  var CabActiveExperiment = import_zod20.z.object({
@@ -1823,7 +1824,7 @@ var CabShopConfig = import_zod20.z.object({
1823
1824
  });
1824
1825
  var CabUserConfig = import_zod20.z.object({
1825
1826
  shop: CabShopConfig,
1826
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
1827
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
1827
1828
  });
1828
1829
 
1829
1830
  // src/schema/widgets/checkout-and-beyond/image.ts
@@ -6761,6 +6762,23 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6761
6762
  }
6762
6763
  };
6763
6764
 
6765
+ // src/server/postPurchaseUnavailableChoice.ts
6766
+ var choiceUnavailable = (reason) => Object.freeze({ level: "choice", reason });
6767
+ var configurationUnavailable = (reason) => ({
6768
+ level: "configuration",
6769
+ reason
6770
+ });
6771
+ var VARIANT_UNAVAILABLE = choiceUnavailable("The chosen variant is unavailable");
6772
+ var PLAN_UNAVAILABLE = choiceUnavailable("Subscription plan is unavailable");
6773
+ var CHOICE_UNAVAILABLE = choiceUnavailable("Requested purchase choice is unavailable");
6774
+ var GROUP_DEFAULT_UNAVAILABLE = choiceUnavailable("Post-purchase group default is unavailable");
6775
+ var unsupportedPreparation = ({ level, reason }) => ({
6776
+ level,
6777
+ reason,
6778
+ status: "unsupported"
6779
+ });
6780
+ var publicPreparation = (preparation) => preparation.status === "unsupported" ? { reason: preparation.reason, status: "unsupported" } : preparation;
6781
+
6764
6782
  // src/schema/postPurchaseSubscriptionProduct.ts
6765
6783
  var import_zod64 = require("zod");
6766
6784
 
@@ -6899,26 +6917,15 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6899
6917
  })
6900
6918
  });
6901
6919
 
6902
- // src/server/postPurchaseUnavailableChoice.ts
6903
- var VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
6904
- var PLAN_UNAVAILABLE = "Subscription plan is unavailable";
6905
- var CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
6906
- var GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
6907
- var choiceLevel = /* @__PURE__ */ new Set([
6908
- VARIANT_UNAVAILABLE,
6909
- PLAN_UNAVAILABLE,
6910
- CHOICE_UNAVAILABLE,
6911
- GROUP_DEFAULT_UNAVAILABLE
6912
- ]);
6913
- var isChoiceUnavailable = (reason) => reason !== void 0 && choiceLevel.has(reason);
6914
-
6915
6920
  // src/server/postPurchaseVariantAvailable.ts
6916
6921
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6917
6922
 
6918
6923
  // src/server/postPurchaseSubscriptionEligibility.ts
6919
6924
  var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) => {
6920
6925
  const { data, effective, original } = normalized;
6921
- const unavailable = "An eligible subscription plan with supported shipping and discount is required";
6926
+ const unavailable = configurationUnavailable(
6927
+ "An eligible subscription plan with supported shipping and discount is required"
6928
+ );
6922
6929
  if (data.length !== 1 || data[0].variants.length !== 1) return unavailable;
6923
6930
  if (
6924
6931
  // Legacy STS ran whatever the product type: its trigger forced the subscription option on.
@@ -7085,9 +7092,9 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
7085
7092
 
7086
7093
  // src/server/prepareNormalizedSubscription.ts
7087
7094
  var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowCollection = false) => {
7088
- if (!plan) return { reason: PLAN_UNAVAILABLE, status: "unsupported" };
7089
- const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7090
- if (reason) return { reason, status: "unsupported" };
7095
+ if (!plan) return unsupportedPreparation(PLAN_UNAVAILABLE);
7096
+ const unavailable = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7097
+ if (unavailable) return unsupportedPreparation(unavailable);
7091
7098
  const { effective } = normalized;
7092
7099
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
7093
7100
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
@@ -7121,33 +7128,34 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
7121
7128
  // src/server/preparePostPurchaseSubscription.ts
7122
7129
  var preparePostPurchaseSubscription = (settings, response, report) => {
7123
7130
  const normalized = normalizePostPurchaseProduct(settings, response, report);
7124
- return prepareNormalizedSubscription(
7125
- normalized,
7126
- report,
7127
- projectPostPurchasePlanCatalog(normalized, report)?.plans[0]
7131
+ return publicPreparation(
7132
+ prepareNormalizedSubscription(normalized, report, projectPostPurchasePlanCatalog(normalized, report)?.plans[0])
7128
7133
  );
7129
7134
  };
7130
7135
 
7131
7136
  // src/server/postPurchaseProductEligibility.ts
7132
7137
  var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
7133
7138
  const { data, effective, original } = normalized;
7134
- if (data.length !== 1 || data[0].variants.length !== 1) return "Only one fixed product and variant are supported";
7139
+ if (data.length !== 1 || data[0].variants.length !== 1)
7140
+ return configurationUnavailable("Only one fixed product and variant are supported");
7135
7141
  if (effective.productType === "subscription" || [original, effective].some(
7136
7142
  (config) => !allowCollection && config.productLimit !== 1 || config.upgradeToSubscription.allow || !quantity3 && config.quantityInputs.enabled && (config.quantityInputs.minValue !== config.quantityInputs.maxValue || config.quantityInputs.defaultQuantity !== config.quantityInputs.minValue)
7137
7143
  ))
7138
- return "Only fixed one-time configuration is supported";
7144
+ return configurationUnavailable("Only fixed one-time configuration is supported");
7139
7145
  const product = data[0];
7140
7146
  const variant2 = product.variants[0];
7141
7147
  if (product.productGroupVariants.length || requiresOwnSellingPlan(product) || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
7142
- return "A one-time variant without product groups or a required selling plan is required";
7148
+ return configurationUnavailable(
7149
+ "A one-time variant without product groups or a required selling plan is required"
7150
+ );
7143
7151
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
7144
7152
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
7145
7153
  };
7146
7154
 
7147
7155
  // src/server/preparePostPurchaseProduct.ts
7148
7156
  var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection = false) => {
7149
- const reason = oneTimeUnavailable(normalized, allowCollection, quantity3);
7150
- if (reason) return { reason, status: "unsupported" };
7157
+ const unavailable = oneTimeUnavailable(normalized, allowCollection, quantity3);
7158
+ if (unavailable) return unsupportedPreparation(unavailable);
7151
7159
  const { effective } = normalized;
7152
7160
  return PreparedPostPurchaseProduct.parse({
7153
7161
  ...projectPostPurchaseProduct(normalized, report, quantity3),
@@ -7158,7 +7166,7 @@ var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection =
7158
7166
  }
7159
7167
  });
7160
7168
  };
7161
- var preparePostPurchaseProduct = (settings, response, report) => prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report);
7169
+ var preparePostPurchaseProduct = (settings, response, report) => publicPreparation(prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report));
7162
7170
 
7163
7171
  // src/schema/postPurchaseProductView.ts
7164
7172
  var import_zod73 = require("zod");
@@ -7414,7 +7422,7 @@ var postPurchaseCardSections = (widgetId, {
7414
7422
  var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response, report), report);
7415
7423
 
7416
7424
  // src/server/selectPostPurchaseChoice.ts
7417
- var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
7425
+ var preferChoiceLevel = (preferred, other) => preferred.level === "configuration" && other.level === "choice" ? other : preferred;
7418
7426
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
7419
7427
  const { data, effective } = normalized;
7420
7428
  const modes = [];
@@ -7429,7 +7437,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7429
7437
  choice: void 0,
7430
7438
  options: void 0,
7431
7439
  plan: void 0,
7432
- reason: preferChoiceLevel(
7440
+ unavailable: preferChoiceLevel(
7433
7441
  effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
7434
7442
  effective.productType === "one-time" ? subscriptionReason : oneTimeReason
7435
7443
  )
@@ -7443,7 +7451,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7443
7451
  choice: bound,
7444
7452
  options: options2,
7445
7453
  plan: bound ? plan : void 0,
7446
- reason: bound ? void 0 : CHOICE_UNAVAILABLE
7454
+ unavailable: bound ? void 0 : CHOICE_UNAVAILABLE
7447
7455
  };
7448
7456
  };
7449
7457
 
@@ -7459,13 +7467,13 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7459
7467
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7460
7468
  const sts = quantity3?.sts === true;
7461
7469
  const marker = sts ? { sts: true } : {};
7462
- const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7470
+ const preparation = !selected.choice ? unsupportedPreparation(selected.unavailable) : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7463
7471
  if (preparation.status === "unsupported") {
7464
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7472
+ const sections2 = preparation.level === "choice" ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7465
7473
  return PostPurchaseProductView.parse({
7466
7474
  labels,
7467
7475
  planCatalog,
7468
- preparation,
7476
+ preparation: publicPreparation(preparation),
7469
7477
  purchaseOptions,
7470
7478
  sections: sections2,
7471
7479
  ...marker
@@ -8195,7 +8203,7 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
8195
8203
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
8196
8204
  groupCatalog: group.catalog,
8197
8205
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
8198
- preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
8206
+ preparation: publicPreparation(unsupportedPreparation(GROUP_DEFAULT_UNAVAILABLE)),
8199
8207
  sections: postPurchaseCardSections(widgetId, normalized, report)
8200
8208
  }
8201
8209
  };
@@ -1653,6 +1653,7 @@ var SmartCartComponent = z19.looseObject({
1653
1653
  var SmartCartConfig = z19.looseObject({
1654
1654
  components: z19.array(SmartCartComponent).optional()
1655
1655
  });
1656
+ var EngineSmartCart = z19.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
1656
1657
 
1657
1658
  // src/schema/cabShopConfig.ts
1658
1659
  var CabActiveExperiment = z20.object({
@@ -1750,7 +1751,7 @@ var CabShopConfig = z20.object({
1750
1751
  });
1751
1752
  var CabUserConfig = z20.object({
1752
1753
  shop: CabShopConfig,
1753
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
1754
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
1754
1755
  });
1755
1756
 
1756
1757
  // src/schema/widgets/checkout-and-beyond/image.ts
@@ -6688,6 +6689,23 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6688
6689
  }
6689
6690
  };
6690
6691
 
6692
+ // src/server/postPurchaseUnavailableChoice.ts
6693
+ var choiceUnavailable = (reason) => Object.freeze({ level: "choice", reason });
6694
+ var configurationUnavailable = (reason) => ({
6695
+ level: "configuration",
6696
+ reason
6697
+ });
6698
+ var VARIANT_UNAVAILABLE = choiceUnavailable("The chosen variant is unavailable");
6699
+ var PLAN_UNAVAILABLE = choiceUnavailable("Subscription plan is unavailable");
6700
+ var CHOICE_UNAVAILABLE = choiceUnavailable("Requested purchase choice is unavailable");
6701
+ var GROUP_DEFAULT_UNAVAILABLE = choiceUnavailable("Post-purchase group default is unavailable");
6702
+ var unsupportedPreparation = ({ level, reason }) => ({
6703
+ level,
6704
+ reason,
6705
+ status: "unsupported"
6706
+ });
6707
+ var publicPreparation = (preparation) => preparation.status === "unsupported" ? { reason: preparation.reason, status: "unsupported" } : preparation;
6708
+
6691
6709
  // src/schema/postPurchaseSubscriptionProduct.ts
6692
6710
  import { z as z64 } from "zod";
6693
6711
 
@@ -6826,26 +6844,15 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6826
6844
  })
6827
6845
  });
6828
6846
 
6829
- // src/server/postPurchaseUnavailableChoice.ts
6830
- var VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
6831
- var PLAN_UNAVAILABLE = "Subscription plan is unavailable";
6832
- var CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
6833
- var GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
6834
- var choiceLevel = /* @__PURE__ */ new Set([
6835
- VARIANT_UNAVAILABLE,
6836
- PLAN_UNAVAILABLE,
6837
- CHOICE_UNAVAILABLE,
6838
- GROUP_DEFAULT_UNAVAILABLE
6839
- ]);
6840
- var isChoiceUnavailable = (reason) => reason !== void 0 && choiceLevel.has(reason);
6841
-
6842
6847
  // src/server/postPurchaseVariantAvailable.ts
6843
6848
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6844
6849
 
6845
6850
  // src/server/postPurchaseSubscriptionEligibility.ts
6846
6851
  var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) => {
6847
6852
  const { data, effective, original } = normalized;
6848
- const unavailable = "An eligible subscription plan with supported shipping and discount is required";
6853
+ const unavailable = configurationUnavailable(
6854
+ "An eligible subscription plan with supported shipping and discount is required"
6855
+ );
6849
6856
  if (data.length !== 1 || data[0].variants.length !== 1) return unavailable;
6850
6857
  if (
6851
6858
  // Legacy STS ran whatever the product type: its trigger forced the subscription option on.
@@ -7012,9 +7019,9 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
7012
7019
 
7013
7020
  // src/server/prepareNormalizedSubscription.ts
7014
7021
  var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowCollection = false) => {
7015
- if (!plan) return { reason: PLAN_UNAVAILABLE, status: "unsupported" };
7016
- const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7017
- if (reason) return { reason, status: "unsupported" };
7022
+ if (!plan) return unsupportedPreparation(PLAN_UNAVAILABLE);
7023
+ const unavailable = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7024
+ if (unavailable) return unsupportedPreparation(unavailable);
7018
7025
  const { effective } = normalized;
7019
7026
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
7020
7027
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
@@ -7048,33 +7055,34 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
7048
7055
  // src/server/preparePostPurchaseSubscription.ts
7049
7056
  var preparePostPurchaseSubscription = (settings, response, report) => {
7050
7057
  const normalized = normalizePostPurchaseProduct(settings, response, report);
7051
- return prepareNormalizedSubscription(
7052
- normalized,
7053
- report,
7054
- projectPostPurchasePlanCatalog(normalized, report)?.plans[0]
7058
+ return publicPreparation(
7059
+ prepareNormalizedSubscription(normalized, report, projectPostPurchasePlanCatalog(normalized, report)?.plans[0])
7055
7060
  );
7056
7061
  };
7057
7062
 
7058
7063
  // src/server/postPurchaseProductEligibility.ts
7059
7064
  var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
7060
7065
  const { data, effective, original } = normalized;
7061
- if (data.length !== 1 || data[0].variants.length !== 1) return "Only one fixed product and variant are supported";
7066
+ if (data.length !== 1 || data[0].variants.length !== 1)
7067
+ return configurationUnavailable("Only one fixed product and variant are supported");
7062
7068
  if (effective.productType === "subscription" || [original, effective].some(
7063
7069
  (config) => !allowCollection && config.productLimit !== 1 || config.upgradeToSubscription.allow || !quantity3 && config.quantityInputs.enabled && (config.quantityInputs.minValue !== config.quantityInputs.maxValue || config.quantityInputs.defaultQuantity !== config.quantityInputs.minValue)
7064
7070
  ))
7065
- return "Only fixed one-time configuration is supported";
7071
+ return configurationUnavailable("Only fixed one-time configuration is supported");
7066
7072
  const product = data[0];
7067
7073
  const variant2 = product.variants[0];
7068
7074
  if (product.productGroupVariants.length || requiresOwnSellingPlan(product) || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
7069
- return "A one-time variant without product groups or a required selling plan is required";
7075
+ return configurationUnavailable(
7076
+ "A one-time variant without product groups or a required selling plan is required"
7077
+ );
7070
7078
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
7071
7079
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
7072
7080
  };
7073
7081
 
7074
7082
  // src/server/preparePostPurchaseProduct.ts
7075
7083
  var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection = false) => {
7076
- const reason = oneTimeUnavailable(normalized, allowCollection, quantity3);
7077
- if (reason) return { reason, status: "unsupported" };
7084
+ const unavailable = oneTimeUnavailable(normalized, allowCollection, quantity3);
7085
+ if (unavailable) return unsupportedPreparation(unavailable);
7078
7086
  const { effective } = normalized;
7079
7087
  return PreparedPostPurchaseProduct.parse({
7080
7088
  ...projectPostPurchaseProduct(normalized, report, quantity3),
@@ -7085,7 +7093,7 @@ var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection =
7085
7093
  }
7086
7094
  });
7087
7095
  };
7088
- var preparePostPurchaseProduct = (settings, response, report) => prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report);
7096
+ var preparePostPurchaseProduct = (settings, response, report) => publicPreparation(prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report));
7089
7097
 
7090
7098
  // src/schema/postPurchaseProductView.ts
7091
7099
  import { z as z73 } from "zod";
@@ -7341,7 +7349,7 @@ var postPurchaseCardSections = (widgetId, {
7341
7349
  var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response, report), report);
7342
7350
 
7343
7351
  // src/server/selectPostPurchaseChoice.ts
7344
- var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
7352
+ var preferChoiceLevel = (preferred, other) => preferred.level === "configuration" && other.level === "choice" ? other : preferred;
7345
7353
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
7346
7354
  const { data, effective } = normalized;
7347
7355
  const modes = [];
@@ -7356,7 +7364,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7356
7364
  choice: void 0,
7357
7365
  options: void 0,
7358
7366
  plan: void 0,
7359
- reason: preferChoiceLevel(
7367
+ unavailable: preferChoiceLevel(
7360
7368
  effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
7361
7369
  effective.productType === "one-time" ? subscriptionReason : oneTimeReason
7362
7370
  )
@@ -7370,7 +7378,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7370
7378
  choice: bound,
7371
7379
  options: options2,
7372
7380
  plan: bound ? plan : void 0,
7373
- reason: bound ? void 0 : CHOICE_UNAVAILABLE
7381
+ unavailable: bound ? void 0 : CHOICE_UNAVAILABLE
7374
7382
  };
7375
7383
  };
7376
7384
 
@@ -7386,13 +7394,13 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7386
7394
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7387
7395
  const sts = quantity3?.sts === true;
7388
7396
  const marker = sts ? { sts: true } : {};
7389
- const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7397
+ const preparation = !selected.choice ? unsupportedPreparation(selected.unavailable) : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7390
7398
  if (preparation.status === "unsupported") {
7391
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7399
+ const sections2 = preparation.level === "choice" ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7392
7400
  return PostPurchaseProductView.parse({
7393
7401
  labels,
7394
7402
  planCatalog,
7395
- preparation,
7403
+ preparation: publicPreparation(preparation),
7396
7404
  purchaseOptions,
7397
7405
  sections: sections2,
7398
7406
  ...marker
@@ -8122,7 +8130,7 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
8122
8130
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
8123
8131
  groupCatalog: group.catalog,
8124
8132
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
8125
- preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
8133
+ preparation: publicPreparation(unsupportedPreparation(GROUP_DEFAULT_UNAVAILABLE)),
8126
8134
  sections: postPurchaseCardSections(widgetId, normalized, report)
8127
8135
  }
8128
8136
  };
@@ -1,4 +1,5 @@
1
1
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
2
+ import { type PostPurchaseUnavailable } from '../server/postPurchaseUnavailableChoice';
2
3
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
3
4
  /** The same eligibility rule drives advertised modes and their prepared terms. */
4
- export declare const oneTimeUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "The chosen variant is unavailable" | "Only one fixed product and variant are supported" | "Only fixed one-time configuration is supported" | "A one-time variant without product groups or a required selling plan is required" | undefined;
5
+ export declare const oneTimeUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, allowCollection: boolean, quantity?: PostPurchaseQuantity) => PostPurchaseUnavailable | undefined;
@@ -1,4 +1,5 @@
1
1
  import type { PostPurchasePlanOption } from '../schema/postPurchasePlanCatalog';
2
2
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
+ import { type PostPurchaseUnavailable } from '../server/postPurchaseUnavailableChoice';
3
4
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
4
- export declare const subscriptionUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, plan: PostPurchasePlanOption | undefined, allowCollection: boolean, quantity?: PostPurchaseQuantity) => "The chosen variant is unavailable" | "Subscription plan is unavailable" | "An eligible subscription plan with supported shipping and discount is required" | undefined;
5
+ export declare const subscriptionUnavailable: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, plan: PostPurchasePlanOption | undefined, allowCollection: boolean, quantity?: PostPurchaseQuantity) => PostPurchaseUnavailable | undefined;
@@ -1,12 +1,45 @@
1
+ import type { PostPurchasePreparation } from '../schema/postPurchaseProduct';
1
2
  /**
2
- * Choice-level unavailability: the widget is readable and its card converts, but the choice the buyer is looking
3
- * at cannot be bought right now — its variant is sold out, its plan is gone, the requested mode is not offered,
4
- * or a group has no available default. Like cab-checkout's sold-out card, such a product still carries its tree
5
- * (values resolve blank; Pay shows `notAvailable`). Every other reason is configuration-level and keeps
6
- * `sections: null`.
3
+ * Why a post-purchase card cannot be bought, and at which level. Choice-level: the widget is readable and its
4
+ * card converts, but the choice the buyer is looking at cannot be bought right now — its variant is sold out,
5
+ * its plan is gone, the requested mode is not offered, or a group has no available default. Like cab-checkout's
6
+ * sold-out card, such a product still carries its tree (values resolve blank; Pay shows `notAvailable`).
7
+ * Configuration-level: the widget itself is unsupported, and the view keeps `sections: null`.
8
+ *
9
+ * The eligibility helpers return the level with the reason, so the tree-or-`null` decision reads `level`
10
+ * instead of matching reason text against a list — rewording a reason can no longer move it between levels.
7
11
  */
8
- export declare const VARIANT_UNAVAILABLE = "The chosen variant is unavailable";
9
- export declare const PLAN_UNAVAILABLE = "Subscription plan is unavailable";
10
- export declare const CHOICE_UNAVAILABLE = "Requested purchase choice is unavailable";
11
- export declare const GROUP_DEFAULT_UNAVAILABLE = "Post-purchase group default is unavailable";
12
- export declare const isChoiceUnavailable: (reason: string | undefined) => boolean;
12
+ export type PostPurchaseUnavailable = Readonly<{
13
+ level: 'choice' | 'configuration';
14
+ reason: string;
15
+ }>;
16
+ /** An unsupported preparation that still carries its level, for the server's own tree-or-`null` decision. */
17
+ export type LeveledPostPurchasePreparation = Exclude<PostPurchasePreparation, {
18
+ status: 'unsupported';
19
+ }> | (PostPurchaseUnavailable & {
20
+ status: 'unsupported';
21
+ });
22
+ export declare const configurationUnavailable: (reason: string) => PostPurchaseUnavailable;
23
+ export declare const VARIANT_UNAVAILABLE: Readonly<{
24
+ level: "choice" | "configuration";
25
+ reason: string;
26
+ }>;
27
+ export declare const PLAN_UNAVAILABLE: Readonly<{
28
+ level: "choice" | "configuration";
29
+ reason: string;
30
+ }>;
31
+ export declare const CHOICE_UNAVAILABLE: Readonly<{
32
+ level: "choice" | "configuration";
33
+ reason: string;
34
+ }>;
35
+ export declare const GROUP_DEFAULT_UNAVAILABLE: Readonly<{
36
+ level: "choice" | "configuration";
37
+ reason: string;
38
+ }>;
39
+ export declare const unsupportedPreparation: ({ level, reason }: PostPurchaseUnavailable) => {
40
+ level: "choice" | "configuration";
41
+ reason: string;
42
+ status: "unsupported";
43
+ };
44
+ /** The public preparation contract carries the reason only; the level is a server-side routing fact. */
45
+ export declare const publicPreparation: (preparation: LeveledPostPurchasePreparation) => PostPurchasePreparation;
@@ -1,6 +1,6 @@
1
1
  import type { PostPurchasePlanOption } from '../schema/postPurchasePlanCatalog';
2
- import { type PostPurchasePreparation } from '../schema/postPurchaseProduct';
3
2
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
+ import { type LeveledPostPurchasePreparation } from '../server/postPurchaseUnavailableChoice';
4
4
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
5
5
  import type { Reporter } from '../server/shared';
6
6
  /**
@@ -8,4 +8,4 @@ import type { Reporter } from '../server/shared';
8
8
  * unreadable-review drift. A switch to subscription (STS) charges no initial shipping, only the recurring
9
9
  * shipping (legacy `useSubscriptionPurchaseOptions`, L112/L122).
10
10
  */
11
- export declare const prepareNormalizedSubscription: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, report: Reporter | undefined, plan: PostPurchasePlanOption | undefined, quantity?: PostPurchaseQuantity, allowCollection?: boolean) => PostPurchasePreparation;
11
+ export declare const prepareNormalizedSubscription: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, report: Reporter | undefined, plan: PostPurchasePlanOption | undefined, quantity?: PostPurchaseQuantity, allowCollection?: boolean) => LeveledPostPurchasePreparation;
@@ -647,6 +647,147 @@ export declare const preparePostPurchaseCollectionGroup: (widgetId: number, sett
647
647
  notAvailable: string;
648
648
  };
649
649
  preparation: {
650
+ display: {
651
+ discountedFrom: "price" | "compare_at_price";
652
+ language: {
653
+ description: string;
654
+ descriptionEnabled: boolean;
655
+ };
656
+ primaryImage: {
657
+ description: string;
658
+ source: string;
659
+ };
660
+ productOptions: {
661
+ showShipping: boolean;
662
+ showSubtotal: boolean;
663
+ showTaxes: boolean;
664
+ showTotal: boolean;
665
+ showVariantSelectors: boolean;
666
+ showDeclineAll?: "none" | "both" | "above" | "below" | undefined;
667
+ };
668
+ responseDescription: string;
669
+ reviews: {
670
+ color: string;
671
+ rating: number;
672
+ reviewCount: number;
673
+ reviewsPlural: string;
674
+ reviewsSingular: string;
675
+ size: "small" | "large" | "medium";
676
+ strokeColor: string;
677
+ strokeWidth: number;
678
+ } | null;
679
+ shouldShowSavingText: boolean;
680
+ descriptionDocument?: {
681
+ attrs: {
682
+ blockSpacing?: "none" | "base" | "extraTight" | "tight" | "loose" | "extraLoose" | undefined;
683
+ };
684
+ content: never[] | {
685
+ attrs: {
686
+ textAlign?: "start" | "center" | "end" | undefined;
687
+ };
688
+ content: never[] | {
689
+ text: string;
690
+ type: "text";
691
+ marks?: never[] | ({
692
+ type: "bold";
693
+ } | {
694
+ type: "italic";
695
+ } | {
696
+ attrs: {
697
+ class: string | null;
698
+ href: string;
699
+ rel: string;
700
+ target: string;
701
+ };
702
+ type: "link";
703
+ } | {
704
+ type: "strike";
705
+ } | {
706
+ attrs: {
707
+ color: "success" | "warning" | "critical" | "base" | "decorative" | "subdued" | "accent";
708
+ fontSize: "small" | "base" | "large" | "medium" | "extraSmall" | "extraLarge" | "heading";
709
+ };
710
+ type: "textStyle";
711
+ })[] | undefined;
712
+ }[];
713
+ type: "paragraph";
714
+ }[];
715
+ type: "doc";
716
+ } | undefined;
717
+ gallery?: {
718
+ images: {
719
+ description: string;
720
+ id: number;
721
+ source: string;
722
+ thumbnail: string;
723
+ }[];
724
+ hasMultiple?: boolean | undefined;
725
+ selectedImageId?: number | undefined;
726
+ } | undefined;
727
+ };
728
+ labels: {
729
+ declineAll: string;
730
+ free: string;
731
+ notAvailable: string;
732
+ payNow: string;
733
+ quantity: string;
734
+ recurringShipping: string;
735
+ recurringSubtotal: string;
736
+ save: string;
737
+ shipping: string;
738
+ subtotal: string;
739
+ taxes: string;
740
+ total: string;
741
+ };
742
+ product: {
743
+ title: string;
744
+ id: number;
745
+ variant: {
746
+ title: string;
747
+ compareAtPrice: string | null;
748
+ id: number;
749
+ price: string;
750
+ };
751
+ image?: {
752
+ src: string;
753
+ } | null | undefined;
754
+ description?: string | null | undefined;
755
+ };
756
+ selection: {
757
+ productId: number;
758
+ quantity: number;
759
+ variantId: number;
760
+ sellingPlanId?: number | undefined;
761
+ };
762
+ status: "prepared";
763
+ terms: {
764
+ discount: {
765
+ amount: number;
766
+ title: string;
767
+ type: "none" | "fixed_amount" | "percentage";
768
+ };
769
+ shipping: {
770
+ amount: number;
771
+ title: string;
772
+ type: "fixed_amount" | "free";
773
+ };
774
+ subscription?: {
775
+ initialShippingPrice: number;
776
+ intervalLabel: string;
777
+ name: string;
778
+ recurringShippingPrice: number;
779
+ shippingOption: {
780
+ presentmentTitle: string;
781
+ title: string;
782
+ };
783
+ adjustment?: {
784
+ amount: number;
785
+ type: "price" | "fixed_amount" | "percentage";
786
+ } | undefined;
787
+ sts?: true | undefined;
788
+ } | undefined;
789
+ };
790
+ } | {
650
791
  reason: string;
651
792
  status: "unsupported";
652
793
  };
@@ -1,8 +1,9 @@
1
1
  import { type PostPurchasePreparation } from '../schema/postPurchaseProduct';
2
2
  import { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
+ import { type LeveledPostPurchasePreparation } from '../server/postPurchaseUnavailableChoice';
3
4
  import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
4
5
  import type { Reporter } from '../server/shared';
5
6
  /** The one-time sibling of `prepareNormalizedSubscription`; `report` receives unreadable-review drift. */
6
- export declare const prepareNormalizedProduct: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, report?: Reporter, quantity?: PostPurchaseQuantity, allowCollection?: boolean) => PostPurchasePreparation;
7
+ export declare const prepareNormalizedProduct: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, report?: Reporter, quantity?: PostPurchaseQuantity, allowCollection?: boolean) => LeveledPostPurchasePreparation;
7
8
  /** Pure preparation only: the authenticated caller owns selected-entry binding and session expiry. */
8
9
  export declare const preparePostPurchaseProduct: (settings: unknown, response: unknown, report?: Reporter) => PostPurchasePreparation;
@@ -5,7 +5,7 @@ import type { PostPurchaseQuantity } from '../server/readPostPurchaseQuantity';
5
5
  import type { Reporter } from '../server/shared';
6
6
  /**
7
7
  * Convert from the same effective settings snapshot; no product values enter the section tree. A choice-level
8
- * unavailable product keeps its tree (`isChoiceUnavailable`); a configuration-level one answers `sections: null`.
8
+ * unavailable product keeps its tree (its `level` is `choice`); a configuration-level one answers `sections: null`.
9
9
  */
10
10
  export declare const prepareNormalizedPostPurchaseView: (widgetId: number, normalized: ReturnType<typeof normalizePostPurchaseProduct>, report?: Reporter, choice?: PostPurchaseChoice, quantity?: PostPurchaseQuantity, allowCollection?: boolean) => PostPurchaseProductView;
11
11
  export declare const preparePostPurchaseView: (widgetId: number, settings: unknown, response: unknown, report?: Reporter, choice?: PostPurchaseChoice) => PostPurchaseProductView;
@@ -10,7 +10,10 @@ export declare const selectPostPurchaseChoice: (normalized: ReturnType<typeof no
10
10
  choice: undefined;
11
11
  options: undefined;
12
12
  plan: undefined;
13
- reason: string;
13
+ unavailable: Readonly<{
14
+ level: "choice" | "configuration";
15
+ reason: string;
16
+ }>;
14
17
  } | {
15
18
  choice: {
16
19
  mode: "one-time";
@@ -39,5 +42,8 @@ export declare const selectPostPurchaseChoice: (normalized: ReturnType<typeof no
39
42
  discountPercentage?: number | undefined;
40
43
  intervalFrequency?: string | undefined;
41
44
  } | undefined;
42
- reason: string | undefined;
45
+ unavailable: Readonly<{
46
+ level: "choice" | "configuration";
47
+ reason: string;
48
+ }> | undefined;
43
49
  };
@@ -11,12 +11,12 @@ export declare const REBUY_ENGINE_HOST = "rebuyengine.com";
11
11
  * {@link UpstreamError} on a non-2xx response so orchestrators can interpret the status.
12
12
  *
13
13
  * Defaults to GET with `params`/`search` as the query string. `headers` apply regardless of method —
14
- * implementations must not gate them behind a `method === 'POST'` branch. Note the only current library
15
- * caller sending `headers` is the analytics POST ingest (REB-21688 moved market context off the
16
- * REB-21102 headers onto engine params, retiring the GET+headers case and the test that witnessed it),
17
- * so a POST-gated implementation would pass every test in this repo today; the invariant stands so a
18
- * future GET caller's headers can't silently drop. Pass `method: 'POST'` with `body` (JSON body) for
19
- * write-style calls like analytics ingest.
14
+ * implementations must not gate them behind a `method === 'POST'` branch. Both current library callers
15
+ * sending `headers` are POSTs: the analytics ingest and B2B availability, which forwards the buyer's session
16
+ * token as `Authorization` (REB-21688 moved market context off the REB-21102 headers onto engine params,
17
+ * retiring the GET+headers case and the test that witnessed it). So a POST-gated implementation would pass
18
+ * every test in this repo today; the invariant stands so a future GET caller's headers can't silently drop.
19
+ * Pass `method: 'POST'` with `body` (JSON body) for write-style calls like analytics ingest.
20
20
  */
21
21
  export type UpstreamFetch = (path: string, params: Record<string, string>, options?: {
22
22
  body?: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.61.0",
3
+ "version": "3.62.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.",
@@ -136,7 +136,7 @@
136
136
  "build:watch": "npm run clean && npm run build:types && node esbuild.config.mjs --watch",
137
137
  "check:dist": "node scripts/verify-dist.mjs",
138
138
  "check:node": "node scripts/assert-node-supported.mjs",
139
- "check-types": "tsc --noEmit",
139
+ "check-types": "tsc --noEmit && tsc -p tsconfig.renderer.json",
140
140
  "clean": "rm -rf dist",
141
141
  "deploy": "npm run build && npx semantic-release --debug --no-ci",
142
142
  "deploy:dryrun": "npm run deploy -- --dry-run",