@rebuy/rebuy 3.56.3 → 3.57.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.
@@ -1,5 +1,8 @@
1
1
  import { type z } from 'zod';
2
2
  import { type PostPurchaseOfferEligibility, PostPurchaseOfferEligibilityContext, PostPurchaseStsOptions } from '../schema/postPurchaseOfferEligibility';
3
+ import type { Reporter } from '../server/shared';
3
4
  export declare const classifyPostPurchaseOffer: (settings: unknown, response: unknown, context: z.input<typeof PostPurchaseOfferEligibilityContext>,
4
5
  /** STS opt-in; absent or `{ sts: false }` keeps every pre-STS outcome. */
5
- options?: PostPurchaseStsOptions) => PostPurchaseOfferEligibility;
6
+ options?: PostPurchaseStsOptions,
7
+ /** Hears each purchased STS candidate whose plan data cannot be read, once per response (REB-25050). */
8
+ report?: Reporter) => PostPurchaseOfferEligibility;
@@ -6172,17 +6172,30 @@ var PostPurchaseProductSettings = PostPurchaseSettings.pick({
6172
6172
 
6173
6173
  // src/server/readPostPurchaseProductSource.ts
6174
6174
  var import_zod45 = require("zod");
6175
- var Envelope = PostPurchaseProductSource.extend({ data: import_zod45.z.array(import_zod45.z.unknown()) });
6176
- var Product3 = PostPurchaseProductSource.shape.data.element;
6177
- var Identified = import_zod45.z.object({ id: PostPurchaseProductId });
6175
+
6176
+ // src/server/postPurchaseResponseDrift.ts
6178
6177
  var heard = /* @__PURE__ */ new WeakMap();
6179
- var firstHearing = (response, report) => {
6180
- const reporters = heard.get(response) ?? /* @__PURE__ */ new WeakSet();
6178
+ var sources = /* @__PURE__ */ new WeakMap();
6179
+ var rememberPostPurchaseProductSource = (product, response, productIndex) => {
6180
+ sources.set(product, { productIndex, response });
6181
+ };
6182
+ var postPurchaseProductSourceOf = (product) => sources.get(product);
6183
+ var reportOncePerResponse = (response, report, events) => {
6184
+ const reporters = heard.get(response) ?? /* @__PURE__ */ new WeakMap();
6181
6185
  heard.set(response, reporters);
6182
- if (reporters.has(report)) return false;
6183
- reporters.add(report);
6184
- return true;
6186
+ const details = reporters.get(report) ?? /* @__PURE__ */ new Set();
6187
+ reporters.set(report, details);
6188
+ for (const event of events) {
6189
+ if (details.has(event.detail)) continue;
6190
+ details.add(event.detail);
6191
+ report(event);
6192
+ }
6185
6193
  };
6194
+
6195
+ // src/server/readPostPurchaseProductSource.ts
6196
+ var Envelope = PostPurchaseProductSource.extend({ data: import_zod45.z.array(import_zod45.z.unknown()) });
6197
+ var Product3 = PostPurchaseProductSource.shape.data.element;
6198
+ var Identified = import_zod45.z.object({ id: PostPurchaseProductId });
6186
6199
  var readPostPurchaseProductSource = (response, report) => {
6187
6200
  const { data, metadata } = Envelope.parse(deepCamelCase(response));
6188
6201
  const drift = [];
@@ -6190,6 +6203,7 @@ var readPostPurchaseProductSource = (response, report) => {
6190
6203
  const products = data.flatMap((raw, productIndex) => {
6191
6204
  const parsed = Product3.safeParse(raw);
6192
6205
  if (parsed.success) {
6206
+ rememberPostPurchaseProductSource(parsed.data, response, productIndex);
6193
6207
  if (parsed.data.requiresSellingPlan && !parsed.data.sellingPlanGroups.length)
6194
6208
  drift.push({
6195
6209
  detail: `Dropped post-purchase product ${parsed.data.id}: data.${productIndex}.requiresSellingPlan`,
@@ -6207,7 +6221,7 @@ var readPostPurchaseProductSource = (response, report) => {
6207
6221
  });
6208
6222
  return [];
6209
6223
  });
6210
- if (report && drift.length && firstHearing(response, report)) drift.forEach((event) => report(event));
6224
+ if (report && drift.length) reportOncePerResponse(response, report, drift);
6211
6225
  return { dropped, metadata, products };
6212
6226
  };
6213
6227
 
@@ -6353,7 +6367,8 @@ var Groups = import_zod52.z.array(
6353
6367
  sellingPlans: import_zod52.z.unknown()
6354
6368
  })
6355
6369
  );
6356
- var Allocations = import_zod52.z.array(import_zod52.z.object({ sellingPlanGroupId: import_zod52.z.string().min(1) })).default([]);
6370
+ var PostPurchasePlanAllocations = import_zod52.z.array(import_zod52.z.object({ sellingPlanGroupId: import_zod52.z.string().min(1) }));
6371
+ var Allocations = PostPurchasePlanAllocations.default([]);
6357
6372
  var Plans = import_zod52.z.array(
6358
6373
  PostPurchasePlanOption.pick({ id: true, name: true }).extend({
6359
6374
  options: import_zod52.z.array(import_zod52.z.object({ value: import_zod52.z.string().min(1) })).min(1),
@@ -7624,18 +7639,30 @@ var readPostPurchaseQuantity = ({
7624
7639
 
7625
7640
  // src/server/postPurchaseSts.ts
7626
7641
  var stsAllowed = ({ effective, original }) => [original, effective].some((config) => config.upgradeToSubscription.allow);
7627
- var postPurchaseStsLine = (normalized, product, lines, variantId) => {
7642
+ var reportUnreadableStsLine = (product, field, report) => {
7643
+ const source2 = postPurchaseProductSourceOf(product);
7644
+ const path = source2 ? `data.${source2.productIndex}.${field}` : field;
7645
+ const detail = `Unreadable post-purchase STS product ${product.id}: ${path}`;
7646
+ reportOncePerResponse(source2?.response ?? product, report, [{ detail, kind: "product" }]);
7647
+ };
7648
+ var postPurchaseStsLine = (normalized, product, lines, variantId, report) => {
7628
7649
  if (!product.sellingPlanGroups?.length || product.productGroupVariants.length) return void 0;
7629
- for (const variant2 of product.variants) {
7650
+ const modern = typeof product.sellingPlanGroups[0].id !== "number";
7651
+ for (const [index, variant2] of product.variants.entries()) {
7630
7652
  const matched = lines.filter((line) => line.sellingPlanId === void 0 && line.variantId === variant2.id);
7631
7653
  const data = [{ ...product, variants: [variant2] }];
7632
- if ((variantId === void 0 || variant2.id === variantId) && matched.length === 1 && postPurchaseVariantAvailable(variant2) && projectPostPurchasePlanCatalog({ ...normalized, data })?.plans.length)
7633
- return { data, line: matched[0] };
7654
+ if ((variantId === void 0 || variant2.id === variantId) && matched.length === 1 && postPurchaseVariantAvailable(variant2)) {
7655
+ const unread = [];
7656
+ if (projectPostPurchasePlanCatalog({ ...normalized, data }, (event) => unread.push(event))?.plans.length)
7657
+ return { data, line: matched[0] };
7658
+ const field = modern && !PostPurchasePlanAllocations.safeParse(variant2.sellingPlanAllocations).success ? `variants.${index}.sellingPlanAllocations` : unread.length ? "sellingPlanGroups" : void 0;
7659
+ if (report && field) reportUnreadableStsLine(product, field, report);
7660
+ }
7634
7661
  }
7635
7662
  };
7636
7663
  var preparePostPurchaseStsView = (widgetId, normalized, product, context, requested, report) => {
7637
7664
  const { originalLineItems } = PostPurchaseStsContext.parse(context);
7638
- const found = postPurchaseStsLine(normalized, product, originalLineItems, requested.variantId);
7665
+ const found = postPurchaseStsLine(normalized, product, originalLineItems, requested.variantId, report);
7639
7666
  if (!found) return null;
7640
7667
  const whole = found.line.quantity;
7641
7668
  if (requested.quantity !== void 0 && requested.quantity !== whole)
@@ -8097,7 +8124,7 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8097
8124
 
8098
8125
  // src/server/classifyPostPurchaseOffer.ts
8099
8126
  var identities = (members) => members.map(({ product, productIndex }) => ({ productId: product.id, productIndex }));
8100
- var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8127
+ var classifyPostPurchaseOffer = (settings, response, context, options2, report) => {
8101
8128
  let unreadable = "context-unreadable";
8102
8129
  try {
8103
8130
  const trusted = PostPurchaseOfferEligibilityContext.parse(context);
@@ -8123,7 +8150,7 @@ var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8123
8150
  PostPurchaseSubscriptionSettings.parse(normalized.effective.subscription);
8124
8151
  }
8125
8152
  const lines = trusted.originalLineItems;
8126
- const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines)) : [];
8153
+ const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines, void 0, report)) : [];
8127
8154
  if (stsMembers.length) return { members: identities(stsMembers), mode: "sts", status: "retain" };
8128
8155
  const classified = members.map((member) => ({
8129
8156
  ...member,
@@ -6099,17 +6099,30 @@ var PostPurchaseProductSettings = PostPurchaseSettings.pick({
6099
6099
 
6100
6100
  // src/server/readPostPurchaseProductSource.ts
6101
6101
  import { z as z45 } from "zod";
6102
- var Envelope = PostPurchaseProductSource.extend({ data: z45.array(z45.unknown()) });
6103
- var Product3 = PostPurchaseProductSource.shape.data.element;
6104
- var Identified = z45.object({ id: PostPurchaseProductId });
6102
+
6103
+ // src/server/postPurchaseResponseDrift.ts
6105
6104
  var heard = /* @__PURE__ */ new WeakMap();
6106
- var firstHearing = (response, report) => {
6107
- const reporters = heard.get(response) ?? /* @__PURE__ */ new WeakSet();
6105
+ var sources = /* @__PURE__ */ new WeakMap();
6106
+ var rememberPostPurchaseProductSource = (product, response, productIndex) => {
6107
+ sources.set(product, { productIndex, response });
6108
+ };
6109
+ var postPurchaseProductSourceOf = (product) => sources.get(product);
6110
+ var reportOncePerResponse = (response, report, events) => {
6111
+ const reporters = heard.get(response) ?? /* @__PURE__ */ new WeakMap();
6108
6112
  heard.set(response, reporters);
6109
- if (reporters.has(report)) return false;
6110
- reporters.add(report);
6111
- return true;
6113
+ const details = reporters.get(report) ?? /* @__PURE__ */ new Set();
6114
+ reporters.set(report, details);
6115
+ for (const event of events) {
6116
+ if (details.has(event.detail)) continue;
6117
+ details.add(event.detail);
6118
+ report(event);
6119
+ }
6112
6120
  };
6121
+
6122
+ // src/server/readPostPurchaseProductSource.ts
6123
+ var Envelope = PostPurchaseProductSource.extend({ data: z45.array(z45.unknown()) });
6124
+ var Product3 = PostPurchaseProductSource.shape.data.element;
6125
+ var Identified = z45.object({ id: PostPurchaseProductId });
6113
6126
  var readPostPurchaseProductSource = (response, report) => {
6114
6127
  const { data, metadata } = Envelope.parse(deepCamelCase(response));
6115
6128
  const drift = [];
@@ -6117,6 +6130,7 @@ var readPostPurchaseProductSource = (response, report) => {
6117
6130
  const products = data.flatMap((raw, productIndex) => {
6118
6131
  const parsed = Product3.safeParse(raw);
6119
6132
  if (parsed.success) {
6133
+ rememberPostPurchaseProductSource(parsed.data, response, productIndex);
6120
6134
  if (parsed.data.requiresSellingPlan && !parsed.data.sellingPlanGroups.length)
6121
6135
  drift.push({
6122
6136
  detail: `Dropped post-purchase product ${parsed.data.id}: data.${productIndex}.requiresSellingPlan`,
@@ -6134,7 +6148,7 @@ var readPostPurchaseProductSource = (response, report) => {
6134
6148
  });
6135
6149
  return [];
6136
6150
  });
6137
- if (report && drift.length && firstHearing(response, report)) drift.forEach((event) => report(event));
6151
+ if (report && drift.length) reportOncePerResponse(response, report, drift);
6138
6152
  return { dropped, metadata, products };
6139
6153
  };
6140
6154
 
@@ -6280,7 +6294,8 @@ var Groups = z52.array(
6280
6294
  sellingPlans: z52.unknown()
6281
6295
  })
6282
6296
  );
6283
- var Allocations = z52.array(z52.object({ sellingPlanGroupId: z52.string().min(1) })).default([]);
6297
+ var PostPurchasePlanAllocations = z52.array(z52.object({ sellingPlanGroupId: z52.string().min(1) }));
6298
+ var Allocations = PostPurchasePlanAllocations.default([]);
6284
6299
  var Plans = z52.array(
6285
6300
  PostPurchasePlanOption.pick({ id: true, name: true }).extend({
6286
6301
  options: z52.array(z52.object({ value: z52.string().min(1) })).min(1),
@@ -7551,18 +7566,30 @@ var readPostPurchaseQuantity = ({
7551
7566
 
7552
7567
  // src/server/postPurchaseSts.ts
7553
7568
  var stsAllowed = ({ effective, original }) => [original, effective].some((config) => config.upgradeToSubscription.allow);
7554
- var postPurchaseStsLine = (normalized, product, lines, variantId) => {
7569
+ var reportUnreadableStsLine = (product, field, report) => {
7570
+ const source2 = postPurchaseProductSourceOf(product);
7571
+ const path = source2 ? `data.${source2.productIndex}.${field}` : field;
7572
+ const detail = `Unreadable post-purchase STS product ${product.id}: ${path}`;
7573
+ reportOncePerResponse(source2?.response ?? product, report, [{ detail, kind: "product" }]);
7574
+ };
7575
+ var postPurchaseStsLine = (normalized, product, lines, variantId, report) => {
7555
7576
  if (!product.sellingPlanGroups?.length || product.productGroupVariants.length) return void 0;
7556
- for (const variant2 of product.variants) {
7577
+ const modern = typeof product.sellingPlanGroups[0].id !== "number";
7578
+ for (const [index, variant2] of product.variants.entries()) {
7557
7579
  const matched = lines.filter((line) => line.sellingPlanId === void 0 && line.variantId === variant2.id);
7558
7580
  const data = [{ ...product, variants: [variant2] }];
7559
- if ((variantId === void 0 || variant2.id === variantId) && matched.length === 1 && postPurchaseVariantAvailable(variant2) && projectPostPurchasePlanCatalog({ ...normalized, data })?.plans.length)
7560
- return { data, line: matched[0] };
7581
+ if ((variantId === void 0 || variant2.id === variantId) && matched.length === 1 && postPurchaseVariantAvailable(variant2)) {
7582
+ const unread = [];
7583
+ if (projectPostPurchasePlanCatalog({ ...normalized, data }, (event) => unread.push(event))?.plans.length)
7584
+ return { data, line: matched[0] };
7585
+ const field = modern && !PostPurchasePlanAllocations.safeParse(variant2.sellingPlanAllocations).success ? `variants.${index}.sellingPlanAllocations` : unread.length ? "sellingPlanGroups" : void 0;
7586
+ if (report && field) reportUnreadableStsLine(product, field, report);
7587
+ }
7561
7588
  }
7562
7589
  };
7563
7590
  var preparePostPurchaseStsView = (widgetId, normalized, product, context, requested, report) => {
7564
7591
  const { originalLineItems } = PostPurchaseStsContext.parse(context);
7565
- const found = postPurchaseStsLine(normalized, product, originalLineItems, requested.variantId);
7592
+ const found = postPurchaseStsLine(normalized, product, originalLineItems, requested.variantId, report);
7566
7593
  if (!found) return null;
7567
7594
  const whole = found.line.quantity;
7568
7595
  if (requested.quantity !== void 0 && requested.quantity !== whole)
@@ -8024,7 +8051,7 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8024
8051
 
8025
8052
  // src/server/classifyPostPurchaseOffer.ts
8026
8053
  var identities = (members) => members.map(({ product, productIndex }) => ({ productId: product.id, productIndex }));
8027
- var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8054
+ var classifyPostPurchaseOffer = (settings, response, context, options2, report) => {
8028
8055
  let unreadable = "context-unreadable";
8029
8056
  try {
8030
8057
  const trusted = PostPurchaseOfferEligibilityContext.parse(context);
@@ -8050,7 +8077,7 @@ var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8050
8077
  PostPurchaseSubscriptionSettings.parse(normalized.effective.subscription);
8051
8078
  }
8052
8079
  const lines = trusted.originalLineItems;
8053
- const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines)) : [];
8080
+ const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines, void 0, report)) : [];
8054
8081
  if (stsMembers.length) return { members: identities(stsMembers), mode: "sts", status: "retain" };
8055
8082
  const classified = members.map((member) => ({
8056
8083
  ...member,
@@ -1,4 +1,9 @@
1
+ import { z } from 'zod';
1
2
  import { PostPurchasePlanOption } from '../schema/postPurchasePlanCatalog';
3
+ /** A variant's readable `selling_plan_allocations`; a missing list reads as none here, but STS reports it. */
4
+ export declare const PostPurchasePlanAllocations: z.ZodArray<z.ZodObject<{
5
+ sellingPlanGroupId: z.ZodString;
6
+ }, z.core.$strip>>;
2
7
  /**
3
8
  * Match allocation groups in product order, then preserve each matching group's plan order. Membership is
4
9
  * group-level by design (frozen-branch parity): Shopify allocates every plan of a group to a member variant, and
@@ -0,0 +1,13 @@
1
+ import type { Reporter, ServerDrift } from '../server/shared';
2
+ /** Records where a parsed product came from, so drift found about it later still names its response and path. */
3
+ export declare const rememberPostPurchaseProductSource: (product: object, response: object, productIndex: number) => void;
4
+ /** The response and raw `productIndex` a parsed product was read from, when it was read by the product source. */
5
+ export declare const postPurchaseProductSourceOf: (product: object) => {
6
+ productIndex: number;
7
+ response: object;
8
+ } | undefined;
9
+ /**
10
+ * Reports each event `report` has not yet heard about `response`, keyed on the `response` object itself: every
11
+ * projection of one request rereads the same object, so a drift is heard once however many projections find it.
12
+ */
13
+ export declare const reportOncePerResponse: (response: object, report: Reporter, events: ServerDrift[]) => void;
@@ -18,8 +18,14 @@ export declare const stsAllowed: ({ effective, original }: Pick<Normalized, "eff
18
18
  * product is never an STS member; (4) the bound variant is the purchased line's, not the product's selected
19
19
  * variant, and subscription is the only mode. The first such variant in catalog order wins, unless `variantId`
20
20
  * names one.
21
+ *
22
+ * `report` hears a candidate (a purchased, in-stock variant on exactly one one-time line) whose plan data cannot be
23
+ * read, since it then silently switches nothing: a modern selling-plan variant whose `sellingPlanAllocations` is
24
+ * missing or malformed, or plan data the catalog cannot read. Each is a `product` drift naming the product id and
25
+ * the dotted path from the source root, never a value, heard once per response like the source's own drops. A
26
+ * readable variant that simply has no plan, or no candidate at all, stays silent.
21
27
  */
22
- export declare const postPurchaseStsLine: <T extends Line>(normalized: Normalized, product: Normalized["data"][number], lines: T[], variantId?: number) => {
28
+ export declare const postPurchaseStsLine: <T extends Line>(normalized: Normalized, product: Normalized["data"][number], lines: T[], variantId?: number, report?: Reporter) => {
23
29
  data: {
24
30
  variants: {
25
31
  title: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.56.3",
3
+ "version": "3.57.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.",