@rebuy/rebuy 3.56.0 → 3.56.2

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
@@ -2769,6 +2769,8 @@ var Product2 = ProductBase.extend({
2769
2769
  metafields: import_zod19.z.unknown().optional(),
2770
2770
  options: import_zod19.z.unknown().optional(),
2771
2771
  productGroupVariants: import_zod19.z.array(import_zod19.z.unknown()).default([]),
2772
+ // REB-25048: the v2 data source flags a subscription-only product here and sends no selling plan groups
2773
+ requiresSellingPlan: import_zod19.z.boolean().nullish(),
2772
2774
  selectedVariant: import_zod19.z.unknown().optional(),
2773
2775
  sellingPlanGroups: import_zod19.z.array(
2774
2776
  OfferProduct.shape.sellingPlanGroups.unwrap().element.extend({
package/dist/index.mjs CHANGED
@@ -2290,6 +2290,8 @@ var Product2 = ProductBase.extend({
2290
2290
  metafields: z19.unknown().optional(),
2291
2291
  options: z19.unknown().optional(),
2292
2292
  productGroupVariants: z19.array(z19.unknown()).default([]),
2293
+ // REB-25048: the v2 data source flags a subscription-only product here and sends no selling plan groups
2294
+ requiresSellingPlan: z19.boolean().nullish(),
2293
2295
  selectedVariant: z19.unknown().optional(),
2294
2296
  sellingPlanGroups: z19.array(
2295
2297
  OfferProduct.shape.sellingPlanGroups.unwrap().element.extend({
@@ -42,6 +42,7 @@ export declare const PostPurchaseProductSource: z.ZodObject<{
42
42
  metafields: z.ZodOptional<z.ZodUnknown>;
43
43
  options: z.ZodOptional<z.ZodUnknown>;
44
44
  productGroupVariants: z.ZodDefault<z.ZodArray<z.ZodUnknown>>;
45
+ requiresSellingPlan: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
45
46
  selectedVariant: z.ZodOptional<z.ZodUnknown>;
46
47
  sellingPlanGroups: z.ZodDefault<z.ZodArray<z.ZodObject<{
47
48
  discountAmount: z.ZodOptional<z.ZodUnknown>;
@@ -6128,6 +6128,8 @@ var Product2 = ProductBase.extend({
6128
6128
  metafields: import_zod43.z.unknown().optional(),
6129
6129
  options: import_zod43.z.unknown().optional(),
6130
6130
  productGroupVariants: import_zod43.z.array(import_zod43.z.unknown()).default([]),
6131
+ // REB-25048: the v2 data source flags a subscription-only product here and sends no selling plan groups
6132
+ requiresSellingPlan: import_zod43.z.boolean().nullish(),
6131
6133
  selectedVariant: import_zod43.z.unknown().optional(),
6132
6134
  sellingPlanGroups: import_zod43.z.array(
6133
6135
  OfferProduct.shape.sellingPlanGroups.unwrap().element.extend({
@@ -6187,7 +6189,14 @@ var readPostPurchaseProductSource = (response, report) => {
6187
6189
  const dropped = [];
6188
6190
  const products = data.flatMap((raw, productIndex) => {
6189
6191
  const parsed = Product3.safeParse(raw);
6190
- if (parsed.success) return [{ product: parsed.data, productIndex }];
6192
+ if (parsed.success) {
6193
+ if (parsed.data.requiresSellingPlan && !parsed.data.sellingPlanGroups.length)
6194
+ drift.push({
6195
+ detail: `Dropped post-purchase product ${parsed.data.id}: data.${productIndex}.requiresSellingPlan`,
6196
+ kind: "product"
6197
+ });
6198
+ return [{ product: parsed.data, productIndex }];
6199
+ }
6191
6200
  const id = Identified.safeParse(raw);
6192
6201
  const issues = parsed.error.issues.map((issue) => ({ ...issue, path: ["data", productIndex, ...issue.path] }));
6193
6202
  const path = issues[0].path.join(".");
@@ -6213,14 +6222,14 @@ var normalizePostPurchaseSource = (settings, response, report) => {
6213
6222
  }
6214
6223
  const effective = PostPurchaseProductSettings.parse((0, import_compat7.merge)({}, original, overrides));
6215
6224
  const data = products.map(({ product }) => product);
6216
- return { dropped, normalized: { data, effective, metadata, original }, products };
6225
+ const emptiedByDrop = !data.length && dropped.length > 0;
6226
+ return { dropped, normalized: { data, effective, emptiedByDrop, metadata, original }, products };
6217
6227
  };
6218
6228
  var underPostPurchaseCap = (entries, cap) => entries.filter(({ productIndex }) => productIndex < cap);
6219
6229
  var normalizePostPurchaseProduct = (settings, response, report) => {
6220
6230
  const { normalized, products } = normalizePostPurchaseSource(settings, response, report);
6221
- const { productLimit } = normalized.effective;
6222
- const readable = productLimit < 1 || underPostPurchaseCap(products, productLimit).length > 0;
6223
- return readable ? normalized : { ...normalized, data: [] };
6231
+ const capped = normalized.effective.productLimit === 1 && products.length > 0 && !underPostPurchaseCap(products, 1).length;
6232
+ return capped ? { ...normalized, data: [], emptiedByDrop: true } : normalized;
6224
6233
  };
6225
6234
 
6226
6235
  // src/pricing/amountToCents.ts
@@ -6376,6 +6385,7 @@ var readPostPurchaseModernPlans = (raw, allocations) => {
6376
6385
  var projectPostPurchasePlanCatalog = (normalized, report) => {
6377
6386
  const { data } = normalized;
6378
6387
  if (data.length === 1 && data[0].variants.length > 1) return null;
6388
+ if (normalized.emptiedByDrop && !data.length) return null;
6379
6389
  try {
6380
6390
  if (data.length !== 1 || data[0].variants.length !== 1) throw new Error("Ambiguous product");
6381
6391
  const product = data[0];
@@ -6397,7 +6407,8 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6397
6407
  discountPercentage: postPurchasePlanDiscountPercentage(variant2.price, plan.adjustment)
6398
6408
  })),
6399
6409
  productId: product.id,
6400
- requiresSellingPlan: required[0] ?? false,
6410
+ // REB-25048: a v2 product flags the requirement on itself and carries no selling plan groups
6411
+ requiresSellingPlan: (required[0] ?? false) || product.requiresSellingPlan === true,
6401
6412
  variantId: variant2.id
6402
6413
  });
6403
6414
  } catch {
@@ -6950,7 +6961,7 @@ var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
6950
6961
  return "Only fixed one-time configuration is supported";
6951
6962
  const product = data[0];
6952
6963
  const variant2 = product.variants[0];
6953
- if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6964
+ if (product.productGroupVariants.length || product.requiresSellingPlan === true || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6954
6965
  return "A one-time variant without product groups or a required selling plan is required";
6955
6966
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6956
6967
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
@@ -8061,7 +8072,7 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8061
8072
  data: [{ ...product, variants: [variant2] }]
8062
8073
  });
8063
8074
  if (!catalog) throw new Error("Unreadable subscription catalog");
8064
- if (normalized.effective.productType === "one-time" && catalog.requiresSellingPlan)
8075
+ if (normalized.effective.productType === "one-time" && product.sellingPlanGroups.some((group) => group.requiresSellingPlan))
8065
8076
  throw new Error("Unsupported purchase mode");
8066
8077
  return catalog;
8067
8078
  });
@@ -6055,6 +6055,8 @@ var Product2 = ProductBase.extend({
6055
6055
  metafields: z43.unknown().optional(),
6056
6056
  options: z43.unknown().optional(),
6057
6057
  productGroupVariants: z43.array(z43.unknown()).default([]),
6058
+ // REB-25048: the v2 data source flags a subscription-only product here and sends no selling plan groups
6059
+ requiresSellingPlan: z43.boolean().nullish(),
6058
6060
  selectedVariant: z43.unknown().optional(),
6059
6061
  sellingPlanGroups: z43.array(
6060
6062
  OfferProduct.shape.sellingPlanGroups.unwrap().element.extend({
@@ -6114,7 +6116,14 @@ var readPostPurchaseProductSource = (response, report) => {
6114
6116
  const dropped = [];
6115
6117
  const products = data.flatMap((raw, productIndex) => {
6116
6118
  const parsed = Product3.safeParse(raw);
6117
- if (parsed.success) return [{ product: parsed.data, productIndex }];
6119
+ if (parsed.success) {
6120
+ if (parsed.data.requiresSellingPlan && !parsed.data.sellingPlanGroups.length)
6121
+ drift.push({
6122
+ detail: `Dropped post-purchase product ${parsed.data.id}: data.${productIndex}.requiresSellingPlan`,
6123
+ kind: "product"
6124
+ });
6125
+ return [{ product: parsed.data, productIndex }];
6126
+ }
6118
6127
  const id = Identified.safeParse(raw);
6119
6128
  const issues = parsed.error.issues.map((issue) => ({ ...issue, path: ["data", productIndex, ...issue.path] }));
6120
6129
  const path = issues[0].path.join(".");
@@ -6140,14 +6149,14 @@ var normalizePostPurchaseSource = (settings, response, report) => {
6140
6149
  }
6141
6150
  const effective = PostPurchaseProductSettings.parse(merge({}, original, overrides));
6142
6151
  const data = products.map(({ product }) => product);
6143
- return { dropped, normalized: { data, effective, metadata, original }, products };
6152
+ const emptiedByDrop = !data.length && dropped.length > 0;
6153
+ return { dropped, normalized: { data, effective, emptiedByDrop, metadata, original }, products };
6144
6154
  };
6145
6155
  var underPostPurchaseCap = (entries, cap) => entries.filter(({ productIndex }) => productIndex < cap);
6146
6156
  var normalizePostPurchaseProduct = (settings, response, report) => {
6147
6157
  const { normalized, products } = normalizePostPurchaseSource(settings, response, report);
6148
- const { productLimit } = normalized.effective;
6149
- const readable = productLimit < 1 || underPostPurchaseCap(products, productLimit).length > 0;
6150
- return readable ? normalized : { ...normalized, data: [] };
6158
+ const capped = normalized.effective.productLimit === 1 && products.length > 0 && !underPostPurchaseCap(products, 1).length;
6159
+ return capped ? { ...normalized, data: [], emptiedByDrop: true } : normalized;
6151
6160
  };
6152
6161
 
6153
6162
  // src/pricing/amountToCents.ts
@@ -6303,6 +6312,7 @@ var readPostPurchaseModernPlans = (raw, allocations) => {
6303
6312
  var projectPostPurchasePlanCatalog = (normalized, report) => {
6304
6313
  const { data } = normalized;
6305
6314
  if (data.length === 1 && data[0].variants.length > 1) return null;
6315
+ if (normalized.emptiedByDrop && !data.length) return null;
6306
6316
  try {
6307
6317
  if (data.length !== 1 || data[0].variants.length !== 1) throw new Error("Ambiguous product");
6308
6318
  const product = data[0];
@@ -6324,7 +6334,8 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
6324
6334
  discountPercentage: postPurchasePlanDiscountPercentage(variant2.price, plan.adjustment)
6325
6335
  })),
6326
6336
  productId: product.id,
6327
- requiresSellingPlan: required[0] ?? false,
6337
+ // REB-25048: a v2 product flags the requirement on itself and carries no selling plan groups
6338
+ requiresSellingPlan: (required[0] ?? false) || product.requiresSellingPlan === true,
6328
6339
  variantId: variant2.id
6329
6340
  });
6330
6341
  } catch {
@@ -6877,7 +6888,7 @@ var oneTimeUnavailable = (normalized, allowCollection, quantity3) => {
6877
6888
  return "Only fixed one-time configuration is supported";
6878
6889
  const product = data[0];
6879
6890
  const variant2 = product.variants[0];
6880
- if (product.productGroupVariants.length || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6891
+ if (product.productGroupVariants.length || product.requiresSellingPlan === true || product.sellingPlanGroups?.some((group) => group.requiresSellingPlan))
6881
6892
  return "A one-time variant without product groups or a required selling plan is required";
6882
6893
  if (!postPurchaseVariantAvailable(variant2)) return VARIANT_UNAVAILABLE;
6883
6894
  if (variant2.productId !== product.id) throw new Error("Product variant identity mismatch");
@@ -7988,7 +7999,7 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
7988
7999
  data: [{ ...product, variants: [variant2] }]
7989
8000
  });
7990
8001
  if (!catalog) throw new Error("Unreadable subscription catalog");
7991
- if (normalized.effective.productType === "one-time" && catalog.requiresSellingPlan)
8002
+ if (normalized.effective.productType === "one-time" && product.sellingPlanGroups.some((group) => group.requiresSellingPlan))
7992
8003
  throw new Error("Unsupported purchase mode");
7993
8004
  return catalog;
7994
8005
  });
@@ -51,6 +51,7 @@ export declare const normalizePostPurchaseCollection: (settings: unknown, respon
51
51
  } | undefined;
52
52
  metafields?: unknown;
53
53
  options?: unknown;
54
+ requiresSellingPlan?: boolean | null | undefined;
54
55
  selectedVariant?: unknown;
55
56
  };
56
57
  productIndex: number;
@@ -96,6 +97,7 @@ export declare const normalizePostPurchaseCollection: (settings: unknown, respon
96
97
  } | undefined;
97
98
  metafields?: unknown;
98
99
  options?: unknown;
100
+ requiresSellingPlan?: boolean | null | undefined;
99
101
  selectedVariant?: unknown;
100
102
  }[];
101
103
  effective: {
@@ -175,6 +177,7 @@ export declare const normalizePostPurchaseCollection: (settings: unknown, respon
175
177
  subscriptionIsDefault?: boolean | undefined;
176
178
  blocks?: unknown[] | undefined;
177
179
  };
180
+ emptiedByDrop: boolean;
178
181
  metadata: {
179
182
  widget: Record<string, unknown>;
180
183
  };
@@ -3,7 +3,8 @@ import type { Reporter } from '../server/shared';
3
3
  /**
4
4
  * The normalized read plus each surviving product's raw `productIndex`, which a collection publishes as member
5
5
  * identity. `data` holds only the products the shared schema accepts; `report` hears each dropped one as drift.
6
- * `dropped` is each rejected product's raw `productIndex` and zod issues.
6
+ * `dropped` is each rejected product's raw `productIndex` and zod issues. `emptiedByDrop` marks a `data` left
7
+ * empty only because its products were dropped (and so already reported), not because the source had none.
7
8
  */
8
9
  export declare const normalizePostPurchaseSource: (settings: unknown, response: unknown, report?: Reporter) => {
9
10
  dropped: {
@@ -51,6 +52,7 @@ export declare const normalizePostPurchaseSource: (settings: unknown, response:
51
52
  } | undefined;
52
53
  metafields?: unknown;
53
54
  options?: unknown;
55
+ requiresSellingPlan?: boolean | null | undefined;
54
56
  selectedVariant?: unknown;
55
57
  }[];
56
58
  effective: {
@@ -130,6 +132,7 @@ export declare const normalizePostPurchaseSource: (settings: unknown, response:
130
132
  subscriptionIsDefault?: boolean | undefined;
131
133
  blocks?: unknown[] | undefined;
132
134
  };
135
+ emptiedByDrop: boolean;
133
136
  metadata: {
134
137
  widget: Record<string, unknown>;
135
138
  };
@@ -252,6 +255,7 @@ export declare const normalizePostPurchaseSource: (settings: unknown, response:
252
255
  } | undefined;
253
256
  metafields?: unknown;
254
257
  options?: unknown;
258
+ requiresSellingPlan?: boolean | null | undefined;
255
259
  selectedVariant?: unknown;
256
260
  };
257
261
  productIndex: number;
@@ -270,8 +274,9 @@ export declare const underPostPurchaseCap: <T extends {
270
274
  * is empty (as the collection keeps no member), so `[bad, good]` with a limit of 1 prepares nothing. Otherwise
271
275
  * `data` keeps every survivor, so a source with more than one readable product still reads as ambiguous rather
272
276
  * than having products discarded. Each dropped product, past the cap or not, was already reported as drift.
273
- * A limit under 1 is a configuration fault, not a cap: `data` stays whole so eligibility rejects it with the
274
- * configuration reason instead of the catalog reporting drift for a readable product.
277
+ * Only a limit of exactly 1 caps: any other limit (0, negative, fractional, or above 1) is a configuration
278
+ * the standalone path refuses anyway, so `data` stays whole and eligibility rejects it with the configuration
279
+ * reason instead of the catalog reporting drift for a readable product.
275
280
  */
276
281
  export declare const normalizePostPurchaseProduct: (settings: unknown, response: unknown, report?: Reporter) => {
277
282
  data: {
@@ -314,6 +319,7 @@ export declare const normalizePostPurchaseProduct: (settings: unknown, response:
314
319
  } | undefined;
315
320
  metafields?: unknown;
316
321
  options?: unknown;
322
+ requiresSellingPlan?: boolean | null | undefined;
317
323
  selectedVariant?: unknown;
318
324
  }[];
319
325
  effective: {
@@ -393,6 +399,7 @@ export declare const normalizePostPurchaseProduct: (settings: unknown, response:
393
399
  subscriptionIsDefault?: boolean | undefined;
394
400
  blocks?: unknown[] | undefined;
395
401
  };
402
+ emptiedByDrop: boolean;
396
403
  metadata: {
397
404
  widget: Record<string, unknown>;
398
405
  };
@@ -8,6 +8,7 @@ import type { Reporter } from '../server/shared';
8
8
  * A catalog binds to exactly one variant, so callers that need plans narrow the product to the chosen variant first.
9
9
  * A single product with several variants is a normal unnarrowed shape: it answers null without a drift report. Only
10
10
  * genuinely malformed merchandise (no products, several products, no variants, a foreign variant) or unreadable plan
11
- * data reports `Unreadable post-purchase plan catalog`.
11
+ * data reports `Unreadable post-purchase plan catalog`. No products because every one under the cap was dropped is
12
+ * not a second fault: each drop was already reported, so it answers null without another report.
12
13
  */
13
14
  export declare const projectPostPurchasePlanCatalog: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, report?: Reporter) => PostPurchasePlanCatalog | null;
@@ -60,6 +60,7 @@ export declare const postPurchaseStsLine: <T extends Line>(normalized: Normalize
60
60
  } | undefined;
61
61
  metafields?: unknown;
62
62
  options?: unknown;
63
+ requiresSellingPlan?: boolean | null | undefined;
63
64
  selectedVariant?: unknown;
64
65
  }[];
65
66
  line: T;
@@ -651,6 +651,7 @@ export declare const prepareMember: (widgetId: number, settings: unknown, respon
651
651
  } | undefined;
652
652
  metafields?: unknown;
653
653
  options?: unknown;
654
+ requiresSellingPlan?: boolean | null | undefined;
654
655
  selectedVariant?: unknown;
655
656
  }[];
656
657
  effective: {
@@ -730,6 +731,7 @@ export declare const prepareMember: (widgetId: number, settings: unknown, respon
730
731
  subscriptionIsDefault?: boolean | undefined;
731
732
  blocks?: unknown[] | undefined;
732
733
  };
734
+ emptiedByDrop: boolean;
733
735
  metadata: {
734
736
  widget: Record<string, unknown>;
735
737
  };
@@ -1467,6 +1469,7 @@ export declare const prepareMember: (widgetId: number, settings: unknown, respon
1467
1469
  } | undefined;
1468
1470
  metafields?: unknown;
1469
1471
  options?: unknown;
1472
+ requiresSellingPlan?: boolean | null | undefined;
1470
1473
  selectedVariant?: unknown;
1471
1474
  }[];
1472
1475
  effective: {
@@ -1546,6 +1549,7 @@ export declare const prepareMember: (widgetId: number, settings: unknown, respon
1546
1549
  subscriptionIsDefault?: boolean | undefined;
1547
1550
  blocks?: unknown[] | undefined;
1548
1551
  };
1552
+ emptiedByDrop: boolean;
1549
1553
  metadata: {
1550
1554
  widget: Record<string, unknown>;
1551
1555
  };
@@ -656,6 +656,7 @@ export declare const preparePostPurchaseStsMember: (widgetId: number, settings:
656
656
  } | undefined;
657
657
  metafields?: unknown;
658
658
  options?: unknown;
659
+ requiresSellingPlan?: boolean | null | undefined;
659
660
  selectedVariant?: unknown;
660
661
  }[];
661
662
  effective: {
@@ -735,6 +736,7 @@ export declare const preparePostPurchaseStsMember: (widgetId: number, settings:
735
736
  subscriptionIsDefault?: boolean | undefined;
736
737
  blocks?: unknown[] | undefined;
737
738
  };
739
+ emptiedByDrop: boolean;
738
740
  metadata: {
739
741
  widget: Record<string, unknown>;
740
742
  };
@@ -159,6 +159,7 @@ export declare const projectPostPurchaseProduct: ({ data, effective, metadata, o
159
159
  } | undefined;
160
160
  metafields?: unknown;
161
161
  options?: unknown;
162
+ requiresSellingPlan?: boolean | null | undefined;
162
163
  selectedVariant?: unknown;
163
164
  };
164
165
  selection: {
@@ -75,6 +75,7 @@ export declare const readPostPurchaseGroup: (product: ReturnType<typeof normaliz
75
75
  productQuantity?: number | undefined;
76
76
  } | undefined;
77
77
  metafields?: unknown;
78
+ requiresSellingPlan?: boolean | null | undefined;
78
79
  selectedVariant?: unknown;
79
80
  };
80
81
  variantCatalog: {
@@ -81,6 +81,7 @@ export declare const readPostPurchaseGroupSelection: (settings: unknown, respons
81
81
  productQuantity?: number | undefined;
82
82
  } | undefined;
83
83
  metafields?: unknown;
84
+ requiresSellingPlan?: boolean | null | undefined;
84
85
  selectedVariant?: unknown;
85
86
  }[];
86
87
  metadata: {
@@ -9,6 +9,10 @@ import type { Reporter } from '../server/shared';
9
9
  * one request (a re-parse or clone per projection reports the drops again). The envelope (an object, `data`
10
10
  * an array, readable `metadata`) still throws.
11
11
  *
12
+ * A product that parses but requires a selling plan on itself with no selling plan groups (a v2 subscription-only
13
+ * product, REB-25048) is kept for eligibility to drop, and reported here as the same once-per-response drift,
14
+ * naming its `requiresSellingPlan` path: it has no plan to sell it with and may never be sold one-time.
15
+ *
12
16
  * Every survivor keeps `productIndex`, its position in the raw `data` array: Decision, Render and Offer
13
17
  * members identify a product by that index, so a drop never renumbers the products after it. `dropped` keeps
14
18
  * each drop's `productIndex` and its zod issues with paths from the source root, so a caller can fail a source
@@ -63,6 +67,7 @@ export declare const readPostPurchaseProductSource: (response: unknown, report?:
63
67
  } | undefined;
64
68
  metafields?: unknown;
65
69
  options?: unknown;
70
+ requiresSellingPlan?: boolean | null | undefined;
66
71
  selectedVariant?: unknown;
67
72
  };
68
73
  productIndex: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.56.0",
3
+ "version": "3.56.2",
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.",