@rebuy/rebuy 3.61.0 → 3.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -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>;
@@ -1433,16 +1433,19 @@ var readOkendoReviews = (find) => {
1433
1433
 
1434
1434
  // src/server/postPurchaseScalarReviews.ts
1435
1435
  var import_zod16 = require("zod");
1436
+ var NO_RATING = "null";
1437
+ var readCount = (value) => PostPurchaseReviewCount.parse(value);
1436
1438
  var readScalarReviews = (find, namespace, ratingKey, countKey) => {
1437
1439
  const rating = find(namespace, ratingKey);
1438
1440
  const count = find(namespace, countKey);
1439
1441
  if (!rating || !count) return null;
1442
+ if (rating.value === NO_RATING && readReviewField(count, readCount) === 0) return null;
1440
1443
  return {
1441
1444
  rating: readReviewField(
1442
1445
  rating,
1443
1446
  (value) => PostPurchaseCommonReviewRating.parse({ value: import_zod16.z.string().parse(value) }).value
1444
1447
  ),
1445
- reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
1448
+ reviewCount: readReviewField(count, readCount)
1446
1449
  };
1447
1450
  };
1448
1451
 
@@ -1726,6 +1729,7 @@ var SmartCartComponent = import_zod19.z.looseObject({
1726
1729
  var SmartCartConfig = import_zod19.z.looseObject({
1727
1730
  components: import_zod19.z.array(SmartCartComponent).optional()
1728
1731
  });
1732
+ var EngineSmartCart = import_zod19.z.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
1729
1733
 
1730
1734
  // src/schema/cabShopConfig.ts
1731
1735
  var CabActiveExperiment = import_zod20.z.object({
@@ -1823,7 +1827,7 @@ var CabShopConfig = import_zod20.z.object({
1823
1827
  });
1824
1828
  var CabUserConfig = import_zod20.z.object({
1825
1829
  shop: CabShopConfig,
1826
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
1830
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
1827
1831
  });
1828
1832
 
1829
1833
  // src/schema/widgets/checkout-and-beyond/image.ts
@@ -6761,6 +6765,23 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6761
6765
  }
6762
6766
  };
6763
6767
 
6768
+ // src/server/postPurchaseUnavailableChoice.ts
6769
+ var choiceUnavailable = (reason) => Object.freeze({ level: "choice", reason });
6770
+ var configurationUnavailable = (reason) => ({
6771
+ level: "configuration",
6772
+ reason
6773
+ });
6774
+ var VARIANT_UNAVAILABLE = choiceUnavailable("The chosen variant is unavailable");
6775
+ var PLAN_UNAVAILABLE = choiceUnavailable("Subscription plan is unavailable");
6776
+ var CHOICE_UNAVAILABLE = choiceUnavailable("Requested purchase choice is unavailable");
6777
+ var GROUP_DEFAULT_UNAVAILABLE = choiceUnavailable("Post-purchase group default is unavailable");
6778
+ var unsupportedPreparation = ({ level, reason }) => ({
6779
+ level,
6780
+ reason,
6781
+ status: "unsupported"
6782
+ });
6783
+ var publicPreparation = (preparation) => preparation.status === "unsupported" ? { reason: preparation.reason, status: "unsupported" } : preparation;
6784
+
6764
6785
  // src/schema/postPurchaseSubscriptionProduct.ts
6765
6786
  var import_zod64 = require("zod");
6766
6787
 
@@ -6899,26 +6920,15 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6899
6920
  })
6900
6921
  });
6901
6922
 
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
6923
  // src/server/postPurchaseVariantAvailable.ts
6916
6924
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6917
6925
 
6918
6926
  // src/server/postPurchaseSubscriptionEligibility.ts
6919
6927
  var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) => {
6920
6928
  const { data, effective, original } = normalized;
6921
- const unavailable = "An eligible subscription plan with supported shipping and discount is required";
6929
+ const unavailable = configurationUnavailable(
6930
+ "An eligible subscription plan with supported shipping and discount is required"
6931
+ );
6922
6932
  if (data.length !== 1 || data[0].variants.length !== 1) return unavailable;
6923
6933
  if (
6924
6934
  // Legacy STS ran whatever the product type: its trigger forced the subscription option on.
@@ -7085,9 +7095,9 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
7085
7095
 
7086
7096
  // src/server/prepareNormalizedSubscription.ts
7087
7097
  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" };
7098
+ if (!plan) return unsupportedPreparation(PLAN_UNAVAILABLE);
7099
+ const unavailable = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7100
+ if (unavailable) return unsupportedPreparation(unavailable);
7091
7101
  const { effective } = normalized;
7092
7102
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
7093
7103
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
@@ -7121,33 +7131,34 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
7121
7131
  // src/server/preparePostPurchaseSubscription.ts
7122
7132
  var preparePostPurchaseSubscription = (settings, response, report) => {
7123
7133
  const normalized = normalizePostPurchaseProduct(settings, response, report);
7124
- return prepareNormalizedSubscription(
7125
- normalized,
7126
- report,
7127
- projectPostPurchasePlanCatalog(normalized, report)?.plans[0]
7134
+ return publicPreparation(
7135
+ prepareNormalizedSubscription(normalized, report, projectPostPurchasePlanCatalog(normalized, report)?.plans[0])
7128
7136
  );
7129
7137
  };
7130
7138
 
7131
7139
  // src/server/postPurchaseProductEligibility.ts
7132
7140
  var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
7133
7141
  const { data, effective, original } = normalized;
7134
- if (data.length !== 1 || data[0].variants.length !== 1) return "Only one fixed product and variant are supported";
7142
+ if (data.length !== 1 || data[0].variants.length !== 1)
7143
+ return configurationUnavailable("Only one fixed product and variant are supported");
7135
7144
  if (effective.productType === "subscription" || [original, effective].some(
7136
7145
  (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
7146
  ))
7138
- return "Only fixed one-time configuration is supported";
7147
+ return configurationUnavailable("Only fixed one-time configuration is supported");
7139
7148
  const product = data[0];
7140
7149
  const variant2 = product.variants[0];
7141
7150
  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";
7151
+ return configurationUnavailable(
7152
+ "A one-time variant without product groups or a required selling plan is required"
7153
+ );
7143
7154
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
7144
7155
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
7145
7156
  };
7146
7157
 
7147
7158
  // src/server/preparePostPurchaseProduct.ts
7148
7159
  var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection = false) => {
7149
- const reason = oneTimeUnavailable(normalized, allowCollection, quantity3);
7150
- if (reason) return { reason, status: "unsupported" };
7160
+ const unavailable = oneTimeUnavailable(normalized, allowCollection, quantity3);
7161
+ if (unavailable) return unsupportedPreparation(unavailable);
7151
7162
  const { effective } = normalized;
7152
7163
  return PreparedPostPurchaseProduct.parse({
7153
7164
  ...projectPostPurchaseProduct(normalized, report, quantity3),
@@ -7158,7 +7169,7 @@ var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection =
7158
7169
  }
7159
7170
  });
7160
7171
  };
7161
- var preparePostPurchaseProduct = (settings, response, report) => prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report);
7172
+ var preparePostPurchaseProduct = (settings, response, report) => publicPreparation(prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report));
7162
7173
 
7163
7174
  // src/schema/postPurchaseProductView.ts
7164
7175
  var import_zod73 = require("zod");
@@ -7414,7 +7425,7 @@ var postPurchaseCardSections = (widgetId, {
7414
7425
  var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response, report), report);
7415
7426
 
7416
7427
  // src/server/selectPostPurchaseChoice.ts
7417
- var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
7428
+ var preferChoiceLevel = (preferred, other) => preferred.level === "configuration" && other.level === "choice" ? other : preferred;
7418
7429
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
7419
7430
  const { data, effective } = normalized;
7420
7431
  const modes = [];
@@ -7429,7 +7440,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7429
7440
  choice: void 0,
7430
7441
  options: void 0,
7431
7442
  plan: void 0,
7432
- reason: preferChoiceLevel(
7443
+ unavailable: preferChoiceLevel(
7433
7444
  effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
7434
7445
  effective.productType === "one-time" ? subscriptionReason : oneTimeReason
7435
7446
  )
@@ -7443,7 +7454,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7443
7454
  choice: bound,
7444
7455
  options: options2,
7445
7456
  plan: bound ? plan : void 0,
7446
- reason: bound ? void 0 : CHOICE_UNAVAILABLE
7457
+ unavailable: bound ? void 0 : CHOICE_UNAVAILABLE
7447
7458
  };
7448
7459
  };
7449
7460
 
@@ -7459,13 +7470,13 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7459
7470
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7460
7471
  const sts = quantity3?.sts === true;
7461
7472
  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);
7473
+ const preparation = !selected.choice ? unsupportedPreparation(selected.unavailable) : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7463
7474
  if (preparation.status === "unsupported") {
7464
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7475
+ const sections2 = preparation.level === "choice" ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7465
7476
  return PostPurchaseProductView.parse({
7466
7477
  labels,
7467
7478
  planCatalog,
7468
- preparation,
7479
+ preparation: publicPreparation(preparation),
7469
7480
  purchaseOptions,
7470
7481
  sections: sections2,
7471
7482
  ...marker
@@ -8195,7 +8206,7 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
8195
8206
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
8196
8207
  groupCatalog: group.catalog,
8197
8208
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
8198
- preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
8209
+ preparation: publicPreparation(unsupportedPreparation(GROUP_DEFAULT_UNAVAILABLE)),
8199
8210
  sections: postPurchaseCardSections(widgetId, normalized, report)
8200
8211
  }
8201
8212
  };
@@ -1360,16 +1360,19 @@ var readOkendoReviews = (find) => {
1360
1360
 
1361
1361
  // src/server/postPurchaseScalarReviews.ts
1362
1362
  import { z as z16 } from "zod";
1363
+ var NO_RATING = "null";
1364
+ var readCount = (value) => PostPurchaseReviewCount.parse(value);
1363
1365
  var readScalarReviews = (find, namespace, ratingKey, countKey) => {
1364
1366
  const rating = find(namespace, ratingKey);
1365
1367
  const count = find(namespace, countKey);
1366
1368
  if (!rating || !count) return null;
1369
+ if (rating.value === NO_RATING && readReviewField(count, readCount) === 0) return null;
1367
1370
  return {
1368
1371
  rating: readReviewField(
1369
1372
  rating,
1370
1373
  (value) => PostPurchaseCommonReviewRating.parse({ value: z16.string().parse(value) }).value
1371
1374
  ),
1372
- reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
1375
+ reviewCount: readReviewField(count, readCount)
1373
1376
  };
1374
1377
  };
1375
1378
 
@@ -1653,6 +1656,7 @@ var SmartCartComponent = z19.looseObject({
1653
1656
  var SmartCartConfig = z19.looseObject({
1654
1657
  components: z19.array(SmartCartComponent).optional()
1655
1658
  });
1659
+ var EngineSmartCart = z19.preprocess((value) => value === false ? null : value, SmartCartConfig.nullish());
1656
1660
 
1657
1661
  // src/schema/cabShopConfig.ts
1658
1662
  var CabActiveExperiment = z20.object({
@@ -1750,7 +1754,7 @@ var CabShopConfig = z20.object({
1750
1754
  });
1751
1755
  var CabUserConfig = z20.object({
1752
1756
  shop: CabShopConfig,
1753
- smartCart: SmartCartConfig.nullish().catch(DRIFTED_SMART_CART)
1757
+ smartCart: EngineSmartCart.catch(DRIFTED_SMART_CART)
1754
1758
  });
1755
1759
 
1756
1760
  // src/schema/widgets/checkout-and-beyond/image.ts
@@ -6688,6 +6692,23 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6688
6692
  }
6689
6693
  };
6690
6694
 
6695
+ // src/server/postPurchaseUnavailableChoice.ts
6696
+ var choiceUnavailable = (reason) => Object.freeze({ level: "choice", reason });
6697
+ var configurationUnavailable = (reason) => ({
6698
+ level: "configuration",
6699
+ reason
6700
+ });
6701
+ var VARIANT_UNAVAILABLE = choiceUnavailable("The chosen variant is unavailable");
6702
+ var PLAN_UNAVAILABLE = choiceUnavailable("Subscription plan is unavailable");
6703
+ var CHOICE_UNAVAILABLE = choiceUnavailable("Requested purchase choice is unavailable");
6704
+ var GROUP_DEFAULT_UNAVAILABLE = choiceUnavailable("Post-purchase group default is unavailable");
6705
+ var unsupportedPreparation = ({ level, reason }) => ({
6706
+ level,
6707
+ reason,
6708
+ status: "unsupported"
6709
+ });
6710
+ var publicPreparation = (preparation) => preparation.status === "unsupported" ? { reason: preparation.reason, status: "unsupported" } : preparation;
6711
+
6691
6712
  // src/schema/postPurchaseSubscriptionProduct.ts
6692
6713
  import { z as z64 } from "zod";
6693
6714
 
@@ -6826,26 +6847,15 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6826
6847
  })
6827
6848
  });
6828
6849
 
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
6850
  // src/server/postPurchaseVariantAvailable.ts
6843
6851
  var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6844
6852
 
6845
6853
  // src/server/postPurchaseSubscriptionEligibility.ts
6846
6854
  var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) => {
6847
6855
  const { data, effective, original } = normalized;
6848
- const unavailable = "An eligible subscription plan with supported shipping and discount is required";
6856
+ const unavailable = configurationUnavailable(
6857
+ "An eligible subscription plan with supported shipping and discount is required"
6858
+ );
6849
6859
  if (data.length !== 1 || data[0].variants.length !== 1) return unavailable;
6850
6860
  if (
6851
6861
  // Legacy STS ran whatever the product type: its trigger forced the subscription option on.
@@ -7012,9 +7022,9 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
7012
7022
 
7013
7023
  // src/server/prepareNormalizedSubscription.ts
7014
7024
  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" };
7025
+ if (!plan) return unsupportedPreparation(PLAN_UNAVAILABLE);
7026
+ const unavailable = subscriptionUnavailable(normalized, plan, allowCollection, quantity3);
7027
+ if (unavailable) return unsupportedPreparation(unavailable);
7018
7028
  const { effective } = normalized;
7019
7029
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
7020
7030
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
@@ -7048,33 +7058,34 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
7048
7058
  // src/server/preparePostPurchaseSubscription.ts
7049
7059
  var preparePostPurchaseSubscription = (settings, response, report) => {
7050
7060
  const normalized = normalizePostPurchaseProduct(settings, response, report);
7051
- return prepareNormalizedSubscription(
7052
- normalized,
7053
- report,
7054
- projectPostPurchasePlanCatalog(normalized, report)?.plans[0]
7061
+ return publicPreparation(
7062
+ prepareNormalizedSubscription(normalized, report, projectPostPurchasePlanCatalog(normalized, report)?.plans[0])
7055
7063
  );
7056
7064
  };
7057
7065
 
7058
7066
  // src/server/postPurchaseProductEligibility.ts
7059
7067
  var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
7060
7068
  const { data, effective, original } = normalized;
7061
- if (data.length !== 1 || data[0].variants.length !== 1) return "Only one fixed product and variant are supported";
7069
+ if (data.length !== 1 || data[0].variants.length !== 1)
7070
+ return configurationUnavailable("Only one fixed product and variant are supported");
7062
7071
  if (effective.productType === "subscription" || [original, effective].some(
7063
7072
  (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
7073
  ))
7065
- return "Only fixed one-time configuration is supported";
7074
+ return configurationUnavailable("Only fixed one-time configuration is supported");
7066
7075
  const product = data[0];
7067
7076
  const variant2 = product.variants[0];
7068
7077
  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";
7078
+ return configurationUnavailable(
7079
+ "A one-time variant without product groups or a required selling plan is required"
7080
+ );
7070
7081
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
7071
7082
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
7072
7083
  };
7073
7084
 
7074
7085
  // src/server/preparePostPurchaseProduct.ts
7075
7086
  var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection = false) => {
7076
- const reason = oneTimeUnavailable(normalized, allowCollection, quantity3);
7077
- if (reason) return { reason, status: "unsupported" };
7087
+ const unavailable = oneTimeUnavailable(normalized, allowCollection, quantity3);
7088
+ if (unavailable) return unsupportedPreparation(unavailable);
7078
7089
  const { effective } = normalized;
7079
7090
  return PreparedPostPurchaseProduct.parse({
7080
7091
  ...projectPostPurchaseProduct(normalized, report, quantity3),
@@ -7085,7 +7096,7 @@ var prepareNormalizedProduct = (normalized, report, quantity3, allowCollection =
7085
7096
  }
7086
7097
  });
7087
7098
  };
7088
- var preparePostPurchaseProduct = (settings, response, report) => prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report);
7099
+ var preparePostPurchaseProduct = (settings, response, report) => publicPreparation(prepareNormalizedProduct(normalizePostPurchaseProduct(settings, response, report), report));
7089
7100
 
7090
7101
  // src/schema/postPurchaseProductView.ts
7091
7102
  import { z as z73 } from "zod";
@@ -7341,7 +7352,7 @@ var postPurchaseCardSections = (widgetId, {
7341
7352
  var preparePostPurchaseCardSections = (widgetId, settings, response, report) => postPurchaseCardSections(widgetId, normalizePostPurchaseProduct(settings, response, report), report);
7342
7353
 
7343
7354
  // src/server/selectPostPurchaseChoice.ts
7344
- var preferChoiceLevel = (preferred, other) => !isChoiceUnavailable(preferred) && isChoiceUnavailable(other) ? other : preferred;
7355
+ var preferChoiceLevel = (preferred, other) => preferred.level === "configuration" && other.level === "choice" ? other : preferred;
7345
7356
  var selectPostPurchaseChoice = (normalized, projected, requested, allowCollection, quantity3) => {
7346
7357
  const { data, effective } = normalized;
7347
7358
  const modes = [];
@@ -7356,7 +7367,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7356
7367
  choice: void 0,
7357
7368
  options: void 0,
7358
7369
  plan: void 0,
7359
- reason: preferChoiceLevel(
7370
+ unavailable: preferChoiceLevel(
7360
7371
  effective.productType === "one-time" ? oneTimeReason : subscriptionReason,
7361
7372
  effective.productType === "one-time" ? subscriptionReason : oneTimeReason
7362
7373
  )
@@ -7370,7 +7381,7 @@ var selectPostPurchaseChoice = (normalized, projected, requested, allowCollectio
7370
7381
  choice: bound,
7371
7382
  options: options2,
7372
7383
  plan: bound ? plan : void 0,
7373
- reason: bound ? void 0 : CHOICE_UNAVAILABLE
7384
+ unavailable: bound ? void 0 : CHOICE_UNAVAILABLE
7374
7385
  };
7375
7386
  };
7376
7387
 
@@ -7386,13 +7397,13 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7386
7397
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7387
7398
  const sts = quantity3?.sts === true;
7388
7399
  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);
7400
+ const preparation = !selected.choice ? unsupportedPreparation(selected.unavailable) : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7390
7401
  if (preparation.status === "unsupported") {
7391
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7402
+ const sections2 = preparation.level === "choice" ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7392
7403
  return PostPurchaseProductView.parse({
7393
7404
  labels,
7394
7405
  planCatalog,
7395
- preparation,
7406
+ preparation: publicPreparation(preparation),
7396
7407
  purchaseOptions,
7397
7408
  sections: sections2,
7398
7409
  ...marker
@@ -8122,7 +8133,7 @@ var preparePostPurchaseCollectionGroup = (widgetId, settings, response, member,
8122
8133
  view: prepared ? { ...prepared.view, groupCatalog: prepared.groupCatalog } : {
8123
8134
  groupCatalog: group.catalog,
8124
8135
  labels: { notAvailable: normalized.effective.language.global.notAvailable },
8125
- preparation: { reason: GROUP_DEFAULT_UNAVAILABLE, status: "unsupported" },
8136
+ preparation: publicPreparation(unsupportedPreparation(GROUP_DEFAULT_UNAVAILABLE)),
8126
8137
  sections: postPurchaseCardSections(widgetId, normalized, report)
8127
8138
  }
8128
8139
  };
@@ -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.1",
4
4
  "description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
5
5
  "license": "MIT",
6
6
  "author": "Rebuy, Inc.",
@@ -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",