@rebuy/rebuy 3.48.1 → 3.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -283,6 +283,7 @@ __export(src_exports, {
283
283
  RebuyMonetizeV1: () => RebuyMonetizeV1,
284
284
  RecordedOfferDeclineSchema: () => RecordedOfferDeclineSchema,
285
285
  RecordedPurchaseObservationSchema: () => RecordedPurchaseObservationSchema,
286
+ RecoveredFollowupsSchema: () => RecoveredFollowupsSchema,
286
287
  ReleaseAuthorityRequestSchema: () => ReleaseAuthorityRequestSchema,
287
288
  ReleaseAuthorityResponseSchema: () => ReleaseAuthorityResponseSchema,
288
289
  ReleaseClaimRequestSchema: () => ReleaseClaimRequestSchema,
@@ -2903,6 +2904,7 @@ var PostPurchaseProductChangeset = import_zod23.z.object({
2903
2904
  // src/schema/postPurchaseAdmission.ts
2904
2905
  var PurchaseAdmissionModeSchema = import_zod24.z.enum(["purchase", "upgrade"]);
2905
2906
  var selection = PreparedPostPurchaseProduct.shape.selection;
2907
+ var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
2906
2908
  var PurchaseExpectedSchema = import_zod24.z.object({
2907
2909
  buyerConsentToSubscriptions: import_zod24.z.boolean().optional(),
2908
2910
  changeset: PostPurchaseProductChangeset.extend({
@@ -2912,6 +2914,11 @@ var PurchaseExpectedSchema = import_zod24.z.object({
2912
2914
  member: PostPurchaseMemberIdentity.optional(),
2913
2915
  mode: PurchaseAdmissionModeSchema.optional(),
2914
2916
  progressRevision: import_zod24.z.number().int().nonnegative().optional(),
2917
+ /**
2918
+ * The total the buyer affirmed, stored with the admitted operation so a reloaded client can still
2919
+ * send its follow-ups with it. Optional: a client from before it was added admits without one.
2920
+ */
2921
+ reportedCalculation: EventCalculationSchema.optional(),
2915
2922
  selection
2916
2923
  }).refine(
2917
2924
  ({ member, progressRevision }) => !member || progressRevision !== void 0,
@@ -2948,7 +2955,6 @@ var PurchaseReceiptSchema = import_zod24.z.object({
2948
2955
  var PurchaseResponseSchema = import_zod24.z.object({ data: PurchaseReceiptSchema, successful: import_zod24.z.literal(true) });
2949
2956
 
2950
2957
  // src/schema/postPurchaseAcceptedEvent.ts
2951
- var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
2952
2958
  var AcceptedEventOutcomeSchema = import_zod25.z.enum(["acknowledged", "rejected", "skipped", "unknown"]);
2953
2959
  var AttemptProgressSchema = import_zod25.z.strictObject({
2954
2960
  attemptedAt: PostPurchaseTimestampSchema,
@@ -4835,17 +4841,38 @@ var PostPurchaseProductSettings = PostPurchaseSettings.pick({
4835
4841
 
4836
4842
  // src/schema/postPurchaseRecovery.ts
4837
4843
  var import_zod83 = require("zod");
4844
+ var RecoveredFollowupsSchema = import_zod83.z.strictObject({
4845
+ acceptedEvent: import_zod83.z.union([AcceptedEventAttemptProgressSchema, AcceptedEventResultProgressSchema]).nullable(),
4846
+ acceptedMetafield: import_zod83.z.union([AcceptedMetafieldOutcomeSchema, import_zod83.z.literal("attempted")]).nullable()
4847
+ });
4838
4848
  var PurchaseRecoveryRequestSchema = DecideRequestSchema.strict();
4839
4849
  var PurchaseRecoveryResponseSchema = import_zod83.z.object({
4840
4850
  data: import_zod83.z.object({
4851
+ /**
4852
+ * Strict, so a client pinned before a new optional field rejects a reply that carries it rather than
4853
+ * stripping it. Release the client before rebuy-api starts emitting a field added here.
4854
+ */
4841
4855
  operation: import_zod83.z.object({
4856
+ /** Absent from a rebuy-api that predates it. */
4857
+ followups: RecoveredFollowupsSchema.optional(),
4842
4858
  observation: RecordedPurchaseObservationSchema.nullable(),
4843
4859
  operationId: PurchaseReceiptSchema.shape.operationId,
4860
+ /** The total the buyer affirmed at admission; absent for an operation admitted without one. */
4861
+ reportedCalculation: EventCalculationSchema.optional(),
4844
4862
  upgrade: UpgradeProgressSchema.optional()
4845
4863
  }).strict().refine(({ operationId, upgrade }) => !upgrade || upgrade.operationId === operationId, {
4846
4864
  message: "Upgrade progress belongs to its operation",
4847
4865
  path: ["upgrade", "operationId"]
4848
- }).nullable(),
4866
+ }).refine(
4867
+ ({ followups, operationId }) => !followups?.acceptedEvent || followups.acceptedEvent.operationId === operationId,
4868
+ { message: "Follow-up progress belongs to its operation", path: ["followups", "acceptedEvent"] }
4869
+ ).refine(
4870
+ ({ followups, reportedCalculation }) => !followups?.acceptedEvent || !reportedCalculation || followups.acceptedEvent.reportedCalculation.currencyCode === reportedCalculation.currencyCode && followups.acceptedEvent.reportedCalculation.totalMinor === reportedCalculation.totalMinor,
4871
+ {
4872
+ message: "Follow-up progress carries the admitted calculation",
4873
+ path: ["followups", "acceptedEvent", "reportedCalculation"]
4874
+ }
4875
+ ).nullable(),
4849
4876
  progress: PostPurchaseProgressSchema.nullable().optional()
4850
4877
  }),
4851
4878
  successful: import_zod83.z.literal(true)
package/dist/index.mjs CHANGED
@@ -2432,6 +2432,7 @@ var PostPurchaseProductChangeset = z23.object({
2432
2432
  // src/schema/postPurchaseAdmission.ts
2433
2433
  var PurchaseAdmissionModeSchema = z24.enum(["purchase", "upgrade"]);
2434
2434
  var selection = PreparedPostPurchaseProduct.shape.selection;
2435
+ var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
2435
2436
  var PurchaseExpectedSchema = z24.object({
2436
2437
  buyerConsentToSubscriptions: z24.boolean().optional(),
2437
2438
  changeset: PostPurchaseProductChangeset.extend({
@@ -2441,6 +2442,11 @@ var PurchaseExpectedSchema = z24.object({
2441
2442
  member: PostPurchaseMemberIdentity.optional(),
2442
2443
  mode: PurchaseAdmissionModeSchema.optional(),
2443
2444
  progressRevision: z24.number().int().nonnegative().optional(),
2445
+ /**
2446
+ * The total the buyer affirmed, stored with the admitted operation so a reloaded client can still
2447
+ * send its follow-ups with it. Optional: a client from before it was added admits without one.
2448
+ */
2449
+ reportedCalculation: EventCalculationSchema.optional(),
2444
2450
  selection
2445
2451
  }).refine(
2446
2452
  ({ member, progressRevision }) => !member || progressRevision !== void 0,
@@ -2477,7 +2483,6 @@ var PurchaseReceiptSchema = z24.object({
2477
2483
  var PurchaseResponseSchema = z24.object({ data: PurchaseReceiptSchema, successful: z24.literal(true) });
2478
2484
 
2479
2485
  // src/schema/postPurchaseAcceptedEvent.ts
2480
- var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
2481
2486
  var AcceptedEventOutcomeSchema = z25.enum(["acknowledged", "rejected", "skipped", "unknown"]);
2482
2487
  var AttemptProgressSchema = z25.strictObject({
2483
2488
  attemptedAt: PostPurchaseTimestampSchema,
@@ -4364,17 +4369,38 @@ var PostPurchaseProductSettings = PostPurchaseSettings.pick({
4364
4369
 
4365
4370
  // src/schema/postPurchaseRecovery.ts
4366
4371
  import { z as z83 } from "zod";
4372
+ var RecoveredFollowupsSchema = z83.strictObject({
4373
+ acceptedEvent: z83.union([AcceptedEventAttemptProgressSchema, AcceptedEventResultProgressSchema]).nullable(),
4374
+ acceptedMetafield: z83.union([AcceptedMetafieldOutcomeSchema, z83.literal("attempted")]).nullable()
4375
+ });
4367
4376
  var PurchaseRecoveryRequestSchema = DecideRequestSchema.strict();
4368
4377
  var PurchaseRecoveryResponseSchema = z83.object({
4369
4378
  data: z83.object({
4379
+ /**
4380
+ * Strict, so a client pinned before a new optional field rejects a reply that carries it rather than
4381
+ * stripping it. Release the client before rebuy-api starts emitting a field added here.
4382
+ */
4370
4383
  operation: z83.object({
4384
+ /** Absent from a rebuy-api that predates it. */
4385
+ followups: RecoveredFollowupsSchema.optional(),
4371
4386
  observation: RecordedPurchaseObservationSchema.nullable(),
4372
4387
  operationId: PurchaseReceiptSchema.shape.operationId,
4388
+ /** The total the buyer affirmed at admission; absent for an operation admitted without one. */
4389
+ reportedCalculation: EventCalculationSchema.optional(),
4373
4390
  upgrade: UpgradeProgressSchema.optional()
4374
4391
  }).strict().refine(({ operationId, upgrade }) => !upgrade || upgrade.operationId === operationId, {
4375
4392
  message: "Upgrade progress belongs to its operation",
4376
4393
  path: ["upgrade", "operationId"]
4377
- }).nullable(),
4394
+ }).refine(
4395
+ ({ followups, operationId }) => !followups?.acceptedEvent || followups.acceptedEvent.operationId === operationId,
4396
+ { message: "Follow-up progress belongs to its operation", path: ["followups", "acceptedEvent"] }
4397
+ ).refine(
4398
+ ({ followups, reportedCalculation }) => !followups?.acceptedEvent || !reportedCalculation || followups.acceptedEvent.reportedCalculation.currencyCode === reportedCalculation.currencyCode && followups.acceptedEvent.reportedCalculation.totalMinor === reportedCalculation.totalMinor,
4399
+ {
4400
+ message: "Follow-up progress carries the admitted calculation",
4401
+ path: ["followups", "acceptedEvent", "reportedCalculation"]
4402
+ }
4403
+ ).nullable(),
4378
4404
  progress: PostPurchaseProgressSchema.nullable().optional()
4379
4405
  }),
4380
4406
  successful: z83.literal(true)
@@ -6762,6 +6788,7 @@ export {
6762
6788
  RebuyMonetizeV1,
6763
6789
  RecordedOfferDeclineSchema,
6764
6790
  RecordedPurchaseObservationSchema,
6791
+ RecoveredFollowupsSchema,
6765
6792
  ReleaseAuthorityRequestSchema,
6766
6793
  ReleaseAuthorityResponseSchema,
6767
6794
  ReleaseClaimRequestSchema,
@@ -1,8 +1,4 @@
1
1
  import { z } from 'zod';
2
- export declare const EventCalculationSchema: z.ZodObject<{
3
- currencyCode: z.ZodString;
4
- totalMinor: z.ZodNumber;
5
- }, z.core.$strict>;
6
2
  /**
7
3
  * apiv2's Flow-trigger bridge outcome, shared by every Flow event (accepted, declined, lifecycle, classification).
8
4
  * `skipped` is terminal and intended: apiv2 skips guest orders, whose null customer Flow would refuse.
@@ -72,5 +68,4 @@ export declare const AcceptedEventResponseSchema: z.ZodObject<{
72
68
  }, z.core.$strict>]>;
73
69
  successful: z.ZodLiteral<true>;
74
70
  }, z.core.$strip>;
75
- export type EventCalculation = z.infer<typeof EventCalculationSchema>;
76
71
  export type AcceptedEventOutcome = z.infer<typeof AcceptedEventOutcomeSchema>;
@@ -7,6 +7,11 @@ export declare const PurchaseAdmissionModeSchema: z.ZodEnum<{
7
7
  purchase: "purchase";
8
8
  upgrade: "upgrade";
9
9
  }>;
10
+ /** The buyer's SDK-calculated total for the affirmed terms (`calculateChangeset`), never proof of payment. */
11
+ export declare const EventCalculationSchema: z.ZodObject<{
12
+ currencyCode: z.ZodString;
13
+ totalMinor: z.ZodNumber;
14
+ }, z.core.$strict>;
10
15
  export declare const PurchaseExpectedSchema: z.ZodObject<{
11
16
  buyerConsentToSubscriptions: z.ZodOptional<z.ZodBoolean>;
12
17
  changeset: z.ZodObject<{
@@ -65,6 +70,10 @@ export declare const PurchaseExpectedSchema: z.ZodObject<{
65
70
  upgrade: "upgrade";
66
71
  }>>;
67
72
  progressRevision: z.ZodOptional<z.ZodNumber>;
73
+ reportedCalculation: z.ZodOptional<z.ZodObject<{
74
+ currencyCode: z.ZodString;
75
+ totalMinor: z.ZodNumber;
76
+ }, z.core.$strict>>;
68
77
  selection: z.ZodObject<{
69
78
  productId: z.ZodNumber;
70
79
  quantity: z.ZodNumber;
@@ -133,6 +142,10 @@ export declare const PurchaseRequestSchema: z.ZodObject<{
133
142
  upgrade: "upgrade";
134
143
  }>>;
135
144
  progressRevision: z.ZodOptional<z.ZodNumber>;
145
+ reportedCalculation: z.ZodOptional<z.ZodObject<{
146
+ currencyCode: z.ZodString;
147
+ totalMinor: z.ZodNumber;
148
+ }, z.core.$strict>>;
136
149
  selection: z.ZodObject<{
137
150
  productId: z.ZodNumber;
138
151
  quantity: z.ZodNumber;
@@ -185,6 +198,7 @@ export declare const PurchaseResponseSchema: z.ZodObject<{
185
198
  }, z.core.$strip>;
186
199
  successful: z.ZodLiteral<true>;
187
200
  }, z.core.$strip>;
201
+ export type EventCalculation = z.infer<typeof EventCalculationSchema>;
188
202
  export type PurchaseReceipt = z.infer<typeof PurchaseReceiptSchema>;
189
203
  export type PurchaseExpected = z.infer<typeof PurchaseExpectedSchema>;
190
204
  export type PurchaseRequest = z.infer<typeof PurchaseRequestSchema>;
@@ -1,4 +1,39 @@
1
1
  import { z } from 'zod';
2
+ /**
3
+ * How far the latest operation's follow-ups got: its accepted Flow event (`null` until one is attempted) and
4
+ * the status of the saved accepted-metafield write that covers it (`null` until one does). Progress only: a
5
+ * reloaded client re-runs `/purchases/accepted`, which is idempotent per operation, whatever this says.
6
+ */
7
+ export declare const RecoveredFollowupsSchema: z.ZodObject<{
8
+ acceptedEvent: z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
9
+ attemptedAt: z.ZodNumber;
10
+ reportedCalculation: z.ZodObject<{
11
+ currencyCode: z.ZodString;
12
+ totalMinor: z.ZodNumber;
13
+ }, z.core.$strict>;
14
+ status: z.ZodLiteral<"attempted">;
15
+ operationId: z.ZodUUID;
16
+ }, z.core.$strict>, z.ZodObject<{
17
+ attemptedAt: z.ZodNumber;
18
+ reportedCalculation: z.ZodObject<{
19
+ currencyCode: z.ZodString;
20
+ totalMinor: z.ZodNumber;
21
+ }, z.core.$strict>;
22
+ finishedAt: z.ZodNumber;
23
+ status: z.ZodEnum<{
24
+ unknown: "unknown";
25
+ acknowledged: "acknowledged";
26
+ rejected: "rejected";
27
+ skipped: "skipped";
28
+ }>;
29
+ operationId: z.ZodUUID;
30
+ }, z.core.$strict>]>>;
31
+ acceptedMetafield: z.ZodNullable<z.ZodUnion<readonly [z.ZodEnum<{
32
+ unknown: "unknown";
33
+ acknowledged: "acknowledged";
34
+ rejected: "rejected";
35
+ }>, z.ZodLiteral<"attempted">]>>;
36
+ }, z.core.$strict>;
2
37
  /** A saved observation or its absence, never permission to apply a changeset. */
3
38
  export declare const PurchaseRecoveryRequestSchema: z.ZodObject<{
4
39
  checkoutToken: z.ZodString;
@@ -7,6 +42,36 @@ export declare const PurchaseRecoveryRequestSchema: z.ZodObject<{
7
42
  export declare const PurchaseRecoveryResponseSchema: z.ZodObject<{
8
43
  data: z.ZodObject<{
9
44
  operation: z.ZodNullable<z.ZodObject<{
45
+ followups: z.ZodOptional<z.ZodObject<{
46
+ acceptedEvent: z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
47
+ attemptedAt: z.ZodNumber;
48
+ reportedCalculation: z.ZodObject<{
49
+ currencyCode: z.ZodString;
50
+ totalMinor: z.ZodNumber;
51
+ }, z.core.$strict>;
52
+ status: z.ZodLiteral<"attempted">;
53
+ operationId: z.ZodUUID;
54
+ }, z.core.$strict>, z.ZodObject<{
55
+ attemptedAt: z.ZodNumber;
56
+ reportedCalculation: z.ZodObject<{
57
+ currencyCode: z.ZodString;
58
+ totalMinor: z.ZodNumber;
59
+ }, z.core.$strict>;
60
+ finishedAt: z.ZodNumber;
61
+ status: z.ZodEnum<{
62
+ unknown: "unknown";
63
+ acknowledged: "acknowledged";
64
+ rejected: "rejected";
65
+ skipped: "skipped";
66
+ }>;
67
+ operationId: z.ZodUUID;
68
+ }, z.core.$strict>]>>;
69
+ acceptedMetafield: z.ZodNullable<z.ZodUnion<readonly [z.ZodEnum<{
70
+ unknown: "unknown";
71
+ acknowledged: "acknowledged";
72
+ rejected: "rejected";
73
+ }>, z.ZodLiteral<"attempted">]>>;
74
+ }, z.core.$strict>>;
10
75
  observation: z.ZodNullable<z.ZodObject<{
11
76
  observation: z.ZodUnion<readonly [z.ZodObject<{
12
77
  changesetApplicationsRemaining: z.ZodNumber;
@@ -39,6 +104,10 @@ export declare const PurchaseRecoveryResponseSchema: z.ZodObject<{
39
104
  recordedAt: z.ZodNumber;
40
105
  }, z.core.$strict>>;
41
106
  operationId: z.ZodUUID;
107
+ reportedCalculation: z.ZodOptional<z.ZodObject<{
108
+ currencyCode: z.ZodString;
109
+ totalMinor: z.ZodNumber;
110
+ }, z.core.$strict>>;
42
111
  upgrade: z.ZodOptional<z.ZodObject<{
43
112
  compensation: z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
44
113
  attemptedAt: z.ZodNumber;
@@ -130,3 +199,5 @@ export declare const PurchaseRecoveryResponseSchema: z.ZodObject<{
130
199
  }, z.core.$strip>;
131
200
  successful: z.ZodLiteral<true>;
132
201
  }, z.core.$strip>;
202
+ export type RecoveredFollowups = z.infer<typeof RecoveredFollowupsSchema>;
203
+ export type PurchaseRecoveryResponse = z.infer<typeof PurchaseRecoveryResponseSchema>;
@@ -6635,45 +6635,76 @@ var projectPostPurchaseProductDisplay = (effective, metadata, product) => PostPu
6635
6635
  });
6636
6636
 
6637
6637
  // src/server/postPurchaseReviewData.ts
6638
- var import_zod63 = require("zod");
6638
+ var import_zod64 = require("zod");
6639
6639
 
6640
6640
  // src/server/postPurchaseJudgeMeReviews.ts
6641
+ var import_zod61 = require("zod");
6642
+
6643
+ // src/server/postPurchaseReviewFields.ts
6641
6644
  var import_zod60 = require("zod");
6645
+ var Field = import_zod60.z.object({ key: import_zod60.z.string(), namespace: import_zod60.z.string(), value: import_zod60.z.unknown().optional() });
6646
+ var reviewFields = (raw) => {
6647
+ const fields4 = import_zod60.z.array(import_zod60.z.unknown()).parse(raw).flatMap((field) => {
6648
+ const parsed = Field.safeParse(field);
6649
+ return parsed.success ? [parsed.data] : [];
6650
+ });
6651
+ return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
6652
+ };
6653
+ var ReviewFieldError = class extends Error {
6654
+ constructor(field) {
6655
+ super(`Unreadable review field ${field}`);
6656
+ this.field = field;
6657
+ }
6658
+ };
6659
+ var readReviewField = (field, read) => {
6660
+ try {
6661
+ return read(field.value);
6662
+ } catch {
6663
+ throw new ReviewFieldError(`${field.namespace}.${field.key}`);
6664
+ }
6665
+ };
6666
+
6667
+ // src/server/postPurchaseJudgeMeReviews.ts
6642
6668
  var readJudgeMeReviews = (find) => {
6643
6669
  const field = find("judgeme", "badge");
6644
6670
  if (!field) return null;
6645
- const html = import_zod60.z.string().parse(field.value);
6646
- const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
6647
- const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
6648
- if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
6649
- return {
6650
- rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
6651
- reviewCount: PostPurchaseReviewCount.parse(count[1])
6652
- };
6671
+ return readReviewField(field, (value) => {
6672
+ const html = import_zod61.z.string().parse(value);
6673
+ const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
6674
+ const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
6675
+ if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
6676
+ return {
6677
+ rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
6678
+ reviewCount: PostPurchaseReviewCount.parse(count[1])
6679
+ };
6680
+ });
6653
6681
  };
6654
6682
 
6655
6683
  // src/server/postPurchaseOkendoReviews.ts
6656
- var import_zod61 = require("zod");
6657
- var Summary = import_zod61.z.object({
6684
+ var import_zod62 = require("zod");
6685
+ var Summary = import_zod62.z.object({
6658
6686
  reviewAverageValue: PostPurchaseCommonReviewRating.shape.value,
6659
- reviewCount: import_zod61.z.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
6687
+ reviewCount: import_zod62.z.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
6660
6688
  });
6661
6689
  var readOkendoReviews = (find) => {
6662
6690
  const field = find("okendo", "summaryData");
6663
6691
  if (!field) return null;
6664
- const summary = Summary.parse(JSON.parse(import_zod61.z.string().parse(field.value)));
6692
+ const summary = readReviewField(field, (value) => Summary.parse(JSON.parse(import_zod62.z.string().parse(value))));
6665
6693
  return { rating: summary.reviewAverageValue, reviewCount: summary.reviewCount };
6666
6694
  };
6667
6695
 
6668
6696
  // src/server/postPurchaseScalarReviews.ts
6669
- var import_zod62 = require("zod");
6697
+ var import_zod63 = require("zod");
6670
6698
  var readScalarReviews = (find, namespace, ratingKey, countKey) => {
6671
6699
  const rating = find(namespace, ratingKey);
6672
6700
  const count = find(namespace, countKey);
6673
6701
  if (!rating || !count) return null;
6674
6702
  return {
6675
- rating: PostPurchaseCommonReviewRating.parse({ value: import_zod62.z.string().parse(rating.value) }).value,
6676
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6703
+ rating: readReviewField(
6704
+ rating,
6705
+ (value) => PostPurchaseCommonReviewRating.parse({ value: import_zod63.z.string().parse(value) }).value
6706
+ ),
6707
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6677
6708
  };
6678
6709
  };
6679
6710
 
@@ -6682,10 +6713,12 @@ var readStandardReviews = (find) => {
6682
6713
  const rating = find("reviews", "rating");
6683
6714
  const count = find("reviews", "rating_count");
6684
6715
  if (!rating || !count) return null;
6685
- const value = import_zod63.z.string().parse(rating.value);
6686
6716
  return {
6687
- rating: PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value,
6688
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6717
+ rating: readReviewField(rating, (raw) => {
6718
+ const value = import_zod64.z.string().parse(raw);
6719
+ return PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value;
6720
+ }),
6721
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6689
6722
  };
6690
6723
  };
6691
6724
  var readPostPurchaseReviewData = (provider, find) => {
@@ -6702,22 +6735,14 @@ var readPostPurchaseReviewData = (provider, find) => {
6702
6735
  const count = find("reviews", "rating_count");
6703
6736
  if (!rating || !count) return null;
6704
6737
  return {
6705
- rating: PostPurchaseKlaviyoReviewRating.parse(JSON.parse(import_zod63.z.string().parse(rating.value))).value,
6706
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6738
+ rating: readReviewField(
6739
+ rating,
6740
+ (value) => PostPurchaseKlaviyoReviewRating.parse(JSON.parse(import_zod64.z.string().parse(value))).value
6741
+ ),
6742
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6707
6743
  };
6708
6744
  };
6709
6745
 
6710
- // src/server/postPurchaseReviewFields.ts
6711
- var import_zod64 = require("zod");
6712
- var Field = import_zod64.z.object({ key: import_zod64.z.string(), namespace: import_zod64.z.string(), value: import_zod64.z.unknown().optional() });
6713
- var reviewFields = (raw) => {
6714
- const fields4 = import_zod64.z.array(import_zod64.z.unknown()).parse(raw).flatMap((field) => {
6715
- const parsed = Field.safeParse(field);
6716
- return parsed.success ? [parsed.data] : [];
6717
- });
6718
- return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
6719
- };
6720
-
6721
6746
  // src/server/postPurchaseReviewProvider.ts
6722
6747
  var import_zod65 = require("zod");
6723
6748
  var IntegrationInput = import_zod65.z.record(import_zod65.z.string(), import_zod65.z.unknown()).prefault({});
@@ -6750,7 +6775,7 @@ var selectPostPurchaseReviewProvider = (raw) => {
6750
6775
  };
6751
6776
 
6752
6777
  // src/server/postPurchaseReviews.ts
6753
- var projectPostPurchaseReviews = (settings, metafields, report) => {
6778
+ var projectPostPurchaseReviews = (settings, { id: productId2, metafields }, report) => {
6754
6779
  try {
6755
6780
  const provider = selectPostPurchaseReviewProvider(settings.integrations);
6756
6781
  if (!provider || metafields == null) return null;
@@ -6766,8 +6791,15 @@ var projectPostPurchaseReviews = (settings, metafields, report) => {
6766
6791
  strokeColor: styles2.starStrokeColor,
6767
6792
  strokeWidth: styles2.strokeWidth
6768
6793
  });
6769
- } catch {
6770
- report?.({ detail: "Unreadable post-purchase reviews", kind: "product" });
6794
+ } catch (error) {
6795
+ report?.(
6796
+ error instanceof ReviewFieldError ? {
6797
+ detail: "Unreadable post-purchase reviews metafield",
6798
+ field: error.field,
6799
+ kind: "reviews",
6800
+ productId: productId2
6801
+ } : { detail: "Unreadable post-purchase reviews", kind: "product" }
6802
+ );
6771
6803
  return null;
6772
6804
  }
6773
6805
  };
@@ -6779,7 +6811,7 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
6779
6811
  return {
6780
6812
  display: {
6781
6813
  ...projectPostPurchaseProductDisplay(effective, metadata, product),
6782
- reviews: projectPostPurchaseReviews(effective, product.metafields, report)
6814
+ reviews: projectPostPurchaseReviews(effective, product, report)
6783
6815
  },
6784
6816
  labels: effective.language.global,
6785
6817
  product: { ...product, variant: variant2 },
@@ -7225,6 +7257,7 @@ var PostPurchaseMemberIdentity = import_zod75.z.object({
7225
7257
  // src/schema/postPurchaseAdmission.ts
7226
7258
  var PurchaseAdmissionModeSchema = import_zod76.z.enum(["purchase", "upgrade"]);
7227
7259
  var selection = PreparedPostPurchaseProduct.shape.selection;
7260
+ var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
7228
7261
  var PurchaseExpectedSchema = import_zod76.z.object({
7229
7262
  buyerConsentToSubscriptions: import_zod76.z.boolean().optional(),
7230
7263
  changeset: PostPurchaseProductChangeset.extend({
@@ -7234,6 +7267,11 @@ var PurchaseExpectedSchema = import_zod76.z.object({
7234
7267
  member: PostPurchaseMemberIdentity.optional(),
7235
7268
  mode: PurchaseAdmissionModeSchema.optional(),
7236
7269
  progressRevision: import_zod76.z.number().int().nonnegative().optional(),
7270
+ /**
7271
+ * The total the buyer affirmed, stored with the admitted operation so a reloaded client can still
7272
+ * send its follow-ups with it. Optional: a client from before it was added admits without one.
7273
+ */
7274
+ reportedCalculation: EventCalculationSchema.optional(),
7237
7275
  selection
7238
7276
  }).refine(
7239
7277
  ({ member, progressRevision }) => !member || progressRevision !== void 0,
@@ -7270,7 +7308,6 @@ var PurchaseReceiptSchema = import_zod76.z.object({
7270
7308
  var PurchaseResponseSchema = import_zod76.z.object({ data: PurchaseReceiptSchema, successful: import_zod76.z.literal(true) });
7271
7309
 
7272
7310
  // src/schema/postPurchaseAcceptedEvent.ts
7273
- var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
7274
7311
  var AcceptedEventOutcomeSchema = import_zod77.z.enum(["acknowledged", "rejected", "skipped", "unknown"]);
7275
7312
  var AttemptProgressSchema = import_zod77.z.strictObject({
7276
7313
  attemptedAt: PostPurchaseTimestampSchema,
@@ -6563,45 +6563,76 @@ var projectPostPurchaseProductDisplay = (effective, metadata, product) => PostPu
6563
6563
  });
6564
6564
 
6565
6565
  // src/server/postPurchaseReviewData.ts
6566
- import { z as z63 } from "zod";
6566
+ import { z as z64 } from "zod";
6567
6567
 
6568
6568
  // src/server/postPurchaseJudgeMeReviews.ts
6569
+ import { z as z61 } from "zod";
6570
+
6571
+ // src/server/postPurchaseReviewFields.ts
6569
6572
  import { z as z60 } from "zod";
6573
+ var Field = z60.object({ key: z60.string(), namespace: z60.string(), value: z60.unknown().optional() });
6574
+ var reviewFields = (raw) => {
6575
+ const fields4 = z60.array(z60.unknown()).parse(raw).flatMap((field) => {
6576
+ const parsed = Field.safeParse(field);
6577
+ return parsed.success ? [parsed.data] : [];
6578
+ });
6579
+ return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
6580
+ };
6581
+ var ReviewFieldError = class extends Error {
6582
+ constructor(field) {
6583
+ super(`Unreadable review field ${field}`);
6584
+ this.field = field;
6585
+ }
6586
+ };
6587
+ var readReviewField = (field, read) => {
6588
+ try {
6589
+ return read(field.value);
6590
+ } catch {
6591
+ throw new ReviewFieldError(`${field.namespace}.${field.key}`);
6592
+ }
6593
+ };
6594
+
6595
+ // src/server/postPurchaseJudgeMeReviews.ts
6570
6596
  var readJudgeMeReviews = (find) => {
6571
6597
  const field = find("judgeme", "badge");
6572
6598
  if (!field) return null;
6573
- const html = z60.string().parse(field.value);
6574
- const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
6575
- const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
6576
- if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
6577
- return {
6578
- rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
6579
- reviewCount: PostPurchaseReviewCount.parse(count[1])
6580
- };
6599
+ return readReviewField(field, (value) => {
6600
+ const html = z61.string().parse(value);
6601
+ const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
6602
+ const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
6603
+ if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
6604
+ return {
6605
+ rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
6606
+ reviewCount: PostPurchaseReviewCount.parse(count[1])
6607
+ };
6608
+ });
6581
6609
  };
6582
6610
 
6583
6611
  // src/server/postPurchaseOkendoReviews.ts
6584
- import { z as z61 } from "zod";
6585
- var Summary = z61.object({
6612
+ import { z as z62 } from "zod";
6613
+ var Summary = z62.object({
6586
6614
  reviewAverageValue: PostPurchaseCommonReviewRating.shape.value,
6587
- reviewCount: z61.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
6615
+ reviewCount: z62.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
6588
6616
  });
6589
6617
  var readOkendoReviews = (find) => {
6590
6618
  const field = find("okendo", "summaryData");
6591
6619
  if (!field) return null;
6592
- const summary = Summary.parse(JSON.parse(z61.string().parse(field.value)));
6620
+ const summary = readReviewField(field, (value) => Summary.parse(JSON.parse(z62.string().parse(value))));
6593
6621
  return { rating: summary.reviewAverageValue, reviewCount: summary.reviewCount };
6594
6622
  };
6595
6623
 
6596
6624
  // src/server/postPurchaseScalarReviews.ts
6597
- import { z as z62 } from "zod";
6625
+ import { z as z63 } from "zod";
6598
6626
  var readScalarReviews = (find, namespace, ratingKey, countKey) => {
6599
6627
  const rating = find(namespace, ratingKey);
6600
6628
  const count = find(namespace, countKey);
6601
6629
  if (!rating || !count) return null;
6602
6630
  return {
6603
- rating: PostPurchaseCommonReviewRating.parse({ value: z62.string().parse(rating.value) }).value,
6604
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6631
+ rating: readReviewField(
6632
+ rating,
6633
+ (value) => PostPurchaseCommonReviewRating.parse({ value: z63.string().parse(value) }).value
6634
+ ),
6635
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6605
6636
  };
6606
6637
  };
6607
6638
 
@@ -6610,10 +6641,12 @@ var readStandardReviews = (find) => {
6610
6641
  const rating = find("reviews", "rating");
6611
6642
  const count = find("reviews", "rating_count");
6612
6643
  if (!rating || !count) return null;
6613
- const value = z63.string().parse(rating.value);
6614
6644
  return {
6615
- rating: PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value,
6616
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6645
+ rating: readReviewField(rating, (raw) => {
6646
+ const value = z64.string().parse(raw);
6647
+ return PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value;
6648
+ }),
6649
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6617
6650
  };
6618
6651
  };
6619
6652
  var readPostPurchaseReviewData = (provider, find) => {
@@ -6630,22 +6663,14 @@ var readPostPurchaseReviewData = (provider, find) => {
6630
6663
  const count = find("reviews", "rating_count");
6631
6664
  if (!rating || !count) return null;
6632
6665
  return {
6633
- rating: PostPurchaseKlaviyoReviewRating.parse(JSON.parse(z63.string().parse(rating.value))).value,
6634
- reviewCount: PostPurchaseReviewCount.parse(count.value)
6666
+ rating: readReviewField(
6667
+ rating,
6668
+ (value) => PostPurchaseKlaviyoReviewRating.parse(JSON.parse(z64.string().parse(value))).value
6669
+ ),
6670
+ reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
6635
6671
  };
6636
6672
  };
6637
6673
 
6638
- // src/server/postPurchaseReviewFields.ts
6639
- import { z as z64 } from "zod";
6640
- var Field = z64.object({ key: z64.string(), namespace: z64.string(), value: z64.unknown().optional() });
6641
- var reviewFields = (raw) => {
6642
- const fields4 = z64.array(z64.unknown()).parse(raw).flatMap((field) => {
6643
- const parsed = Field.safeParse(field);
6644
- return parsed.success ? [parsed.data] : [];
6645
- });
6646
- return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
6647
- };
6648
-
6649
6674
  // src/server/postPurchaseReviewProvider.ts
6650
6675
  import { z as z65 } from "zod";
6651
6676
  var IntegrationInput = z65.record(z65.string(), z65.unknown()).prefault({});
@@ -6678,7 +6703,7 @@ var selectPostPurchaseReviewProvider = (raw) => {
6678
6703
  };
6679
6704
 
6680
6705
  // src/server/postPurchaseReviews.ts
6681
- var projectPostPurchaseReviews = (settings, metafields, report) => {
6706
+ var projectPostPurchaseReviews = (settings, { id: productId2, metafields }, report) => {
6682
6707
  try {
6683
6708
  const provider = selectPostPurchaseReviewProvider(settings.integrations);
6684
6709
  if (!provider || metafields == null) return null;
@@ -6694,8 +6719,15 @@ var projectPostPurchaseReviews = (settings, metafields, report) => {
6694
6719
  strokeColor: styles2.starStrokeColor,
6695
6720
  strokeWidth: styles2.strokeWidth
6696
6721
  });
6697
- } catch {
6698
- report?.({ detail: "Unreadable post-purchase reviews", kind: "product" });
6722
+ } catch (error) {
6723
+ report?.(
6724
+ error instanceof ReviewFieldError ? {
6725
+ detail: "Unreadable post-purchase reviews metafield",
6726
+ field: error.field,
6727
+ kind: "reviews",
6728
+ productId: productId2
6729
+ } : { detail: "Unreadable post-purchase reviews", kind: "product" }
6730
+ );
6699
6731
  return null;
6700
6732
  }
6701
6733
  };
@@ -6707,7 +6739,7 @@ var projectPostPurchaseProduct = ({ data, effective, metadata, original }, repor
6707
6739
  return {
6708
6740
  display: {
6709
6741
  ...projectPostPurchaseProductDisplay(effective, metadata, product),
6710
- reviews: projectPostPurchaseReviews(effective, product.metafields, report)
6742
+ reviews: projectPostPurchaseReviews(effective, product, report)
6711
6743
  },
6712
6744
  labels: effective.language.global,
6713
6745
  product: { ...product, variant: variant2 },
@@ -7153,6 +7185,7 @@ var PostPurchaseMemberIdentity = z75.object({
7153
7185
  // src/schema/postPurchaseAdmission.ts
7154
7186
  var PurchaseAdmissionModeSchema = z76.enum(["purchase", "upgrade"]);
7155
7187
  var selection = PreparedPostPurchaseProduct.shape.selection;
7188
+ var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
7156
7189
  var PurchaseExpectedSchema = z76.object({
7157
7190
  buyerConsentToSubscriptions: z76.boolean().optional(),
7158
7191
  changeset: PostPurchaseProductChangeset.extend({
@@ -7162,6 +7195,11 @@ var PurchaseExpectedSchema = z76.object({
7162
7195
  member: PostPurchaseMemberIdentity.optional(),
7163
7196
  mode: PurchaseAdmissionModeSchema.optional(),
7164
7197
  progressRevision: z76.number().int().nonnegative().optional(),
7198
+ /**
7199
+ * The total the buyer affirmed, stored with the admitted operation so a reloaded client can still
7200
+ * send its follow-ups with it. Optional: a client from before it was added admits without one.
7201
+ */
7202
+ reportedCalculation: EventCalculationSchema.optional(),
7165
7203
  selection
7166
7204
  }).refine(
7167
7205
  ({ member, progressRevision }) => !member || progressRevision !== void 0,
@@ -7198,7 +7236,6 @@ var PurchaseReceiptSchema = z76.object({
7198
7236
  var PurchaseResponseSchema = z76.object({ data: PurchaseReceiptSchema, successful: z76.literal(true) });
7199
7237
 
7200
7238
  // src/schema/postPurchaseAcceptedEvent.ts
7201
- var EventCalculationSchema = PostPurchaseProductCosts.pick({ currencyCode: true, totalMinor: true }).strict();
7202
7239
  var AcceptedEventOutcomeSchema = z77.enum(["acknowledged", "rejected", "skipped", "unknown"]);
7203
7240
  var AttemptProgressSchema = z77.strictObject({
7204
7241
  attemptedAt: PostPurchaseTimestampSchema,
@@ -1,4 +1,4 @@
1
- import type { ReviewFields } from '../server/postPurchaseReviewFields';
1
+ import { type ReviewFields } from '../server/postPurchaseReviewFields';
2
2
  /** Read attributes only; merchant badge HTML never enters the prepared display or section tree. */
3
3
  export declare const readJudgeMeReviews: (find: ReviewFields) => {
4
4
  rating: number;
@@ -1,4 +1,4 @@
1
- import type { ReviewFields } from '../server/postPurchaseReviewFields';
1
+ import { type ReviewFields } from '../server/postPurchaseReviewFields';
2
2
  /** Only the selected aggregate's two numeric members enter the prepared review display. */
3
3
  export declare const readOkendoReviews: (find: ReviewFields) => {
4
4
  rating: number;
@@ -1,4 +1,4 @@
1
- import type { ReviewFields } from '../server/postPurchaseReviewFields';
1
+ import { type ReviewFields } from '../server/postPurchaseReviewFields';
2
2
  import type { selectPostPurchaseReviewProvider } from '../server/postPurchaseReviewProvider';
3
3
  /**
4
4
  * Selection is complete before reading data; a missing selected aggregate cannot fall through. Reviews.io reading
@@ -5,3 +5,10 @@ export declare const reviewFields: (raw: unknown) => (namespace: string, key: st
5
5
  value?: unknown;
6
6
  } | undefined;
7
7
  export type ReviewFields = ReturnType<typeof reviewFields>;
8
+ /** A selected review field whose value failed its read. It names the field (`namespace.key`), never the value. */
9
+ export declare class ReviewFieldError extends Error {
10
+ readonly field: string;
11
+ constructor(field: string);
12
+ }
13
+ /** Reads one matched field's value; any failure is rethrown naming only that field. */
14
+ export declare const readReviewField: <T>(field: NonNullable<ReturnType<ReviewFields>>, read: (value: unknown) => T) => T;
@@ -1,5 +1,12 @@
1
1
  import { PostPurchaseProductReviews } from '../schema/postPurchaseProductReviews';
2
2
  import type { PostPurchaseProductSettings } from '../schema/postPurchaseProductSettings';
3
3
  import type { Reporter } from '../server/shared';
4
- /** Reviews are optional display data; neither missing nor malformed aggregates invalidate the offer. */
5
- export declare const projectPostPurchaseReviews: (settings: PostPurchaseProductSettings, metafields: unknown, report?: Reporter) => PostPurchaseProductReviews | null;
4
+ /**
5
+ * Reviews are optional display data; neither missing nor malformed aggregates invalidate the offer. A malformed
6
+ * selected review field reports `reviews` drift naming the product and the field (`namespace.key`, never the
7
+ * value); unreadable flags, metafield containers, styles or output stay sanitized `product` drift.
8
+ */
9
+ export declare const projectPostPurchaseReviews: (settings: PostPurchaseProductSettings, { id: productId, metafields }: {
10
+ id: number;
11
+ metafields?: unknown;
12
+ }, report?: Reporter) => PostPurchaseProductReviews | null;
@@ -1,4 +1,4 @@
1
- import type { ReviewFields } from '../server/postPurchaseReviewFields';
1
+ import { type ReviewFields } from '../server/postPurchaseReviewFields';
2
2
  /** Pair absence is ordinary; invalid selected values throw to the single local recovery boundary. */
3
3
  export declare const readScalarReviews: (find: ReviewFields, namespace: string, ratingKey: string, countKey: string) => {
4
4
  rating: number;
@@ -28,14 +28,21 @@ export type UpstreamFetch = (path: string, params: Record<string, string>, optio
28
28
  /**
29
29
  * A drift signal the orchestrators emit when a per-item payload salvage drops something: a malformed
30
30
  * offer product, an invalid/unknown section stubbed out, an unreadable rule verdict, a shop-config
31
- * entry the shared schema inerted (`shopConfig`), or unreadable selectable-gift milestones
32
- * (`giftMilestones` — the widget's gifts are then kept). Distinct from a hard {@link UpstreamError} — the
31
+ * entry the shared schema inerted (`shopConfig`), unreadable selectable-gift milestones
32
+ * (`giftMilestones` — the widget's gifts are then kept), or a post-purchase product's malformed selected review
33
+ * metafield (`reviews` — the reviews are then omitted; it names the product and the field, never the value). Distinct from a hard {@link UpstreamError} — the
33
34
  * request still succeeds (degraded), so this is the only trace that an upstream contract moved. Optional
34
35
  * on {@link ServerContext}; the host (rebuy-api) wires it to its logger.
35
36
  */
36
37
  export type ServerDrift = {
37
38
  detail: string;
38
39
  kind: 'b2bAvailability' | 'dataSource' | 'giftMilestones' | 'monetizeOffer' | 'product' | 'ruleVerdict' | 'section' | 'shopConfig';
40
+ } | {
41
+ detail: string;
42
+ /** The malformed selected review metafield as `namespace.key`; never its value. */
43
+ field: string;
44
+ kind: 'reviews';
45
+ productId: number;
39
46
  };
40
47
  /** A {@link ServerDrift} sink, as the orchestrators pass it around internally. */
41
48
  export type Reporter = (event: ServerDrift) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.48.1",
3
+ "version": "3.50.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.",