@rebuy/rebuy 3.56.2 → 3.56.3

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
@@ -206,6 +206,7 @@ __export(src_exports, {
206
206
  PostPurchaseOfferEligibilityContext: () => PostPurchaseOfferEligibilityContext,
207
207
  PostPurchaseOfferPresentation: () => PostPurchaseOfferPresentation,
208
208
  PostPurchaseOfferSkipReason: () => PostPurchaseOfferSkipReason,
209
+ PostPurchaseOfferUnavailableReason: () => PostPurchaseOfferUnavailableReason,
209
210
  PostPurchaseOptions: () => PostPurchaseOptions,
210
211
  PostPurchaseOrderAttributes: () => PostPurchaseOrderAttributes,
211
212
  PostPurchaseOrderIdSchema: () => PostPurchaseOrderIdSchema,
@@ -4792,6 +4793,12 @@ var OfferPreparationResponseSchema = import_zod77.z.object({
4792
4793
  var import_zod78 = require("zod");
4793
4794
  var selection2 = PreparedPostPurchaseProduct.shape.selection;
4794
4795
  var PostPurchaseOfferSkipReason = import_zod78.z.enum(["empty", "no-eligible-subscription"]);
4796
+ var PostPurchaseOfferUnavailableReason = import_zod78.z.enum([
4797
+ "catalog-unreadable",
4798
+ "context-unreadable",
4799
+ "eligible-sts",
4800
+ "settings-unreadable"
4801
+ ]);
4795
4802
  var PostPurchaseOfferEligibilityContext = import_zod78.z.object({
4796
4803
  excludedProductIds: PostPurchaseExcludedProductIds,
4797
4804
  originalLineItems: import_zod78.z.array(selection2.pick({ productId: true, sellingPlanId: true, variantId: true }))
@@ -4812,7 +4819,8 @@ var PostPurchaseOfferEligibility = import_zod78.z.discriminatedUnion("status", [
4812
4819
  status: import_zod78.z.literal("retain")
4813
4820
  }),
4814
4821
  import_zod78.z.object({ reason: PostPurchaseOfferSkipReason, status: import_zod78.z.literal("skip") }),
4815
- import_zod78.z.object({ reason: import_zod78.z.literal("eligible-sts").optional(), status: import_zod78.z.literal("unavailable") })
4822
+ /** The classifier always names a reason; it stays optional so a reasonless answer from before still parses. */
4823
+ import_zod78.z.object({ reason: PostPurchaseOfferUnavailableReason.optional(), status: import_zod78.z.literal("unavailable") })
4816
4824
  ]);
4817
4825
 
4818
4826
  // src/schema/postPurchaseOfferPresentation.ts
package/dist/index.mjs CHANGED
@@ -4313,6 +4313,12 @@ var OfferPreparationResponseSchema = z77.object({
4313
4313
  import { z as z78 } from "zod";
4314
4314
  var selection2 = PreparedPostPurchaseProduct.shape.selection;
4315
4315
  var PostPurchaseOfferSkipReason = z78.enum(["empty", "no-eligible-subscription"]);
4316
+ var PostPurchaseOfferUnavailableReason = z78.enum([
4317
+ "catalog-unreadable",
4318
+ "context-unreadable",
4319
+ "eligible-sts",
4320
+ "settings-unreadable"
4321
+ ]);
4316
4322
  var PostPurchaseOfferEligibilityContext = z78.object({
4317
4323
  excludedProductIds: PostPurchaseExcludedProductIds,
4318
4324
  originalLineItems: z78.array(selection2.pick({ productId: true, sellingPlanId: true, variantId: true }))
@@ -4333,7 +4339,8 @@ var PostPurchaseOfferEligibility = z78.discriminatedUnion("status", [
4333
4339
  status: z78.literal("retain")
4334
4340
  }),
4335
4341
  z78.object({ reason: PostPurchaseOfferSkipReason, status: z78.literal("skip") }),
4336
- z78.object({ reason: z78.literal("eligible-sts").optional(), status: z78.literal("unavailable") })
4342
+ /** The classifier always names a reason; it stays optional so a reasonless answer from before still parses. */
4343
+ z78.object({ reason: PostPurchaseOfferUnavailableReason.optional(), status: z78.literal("unavailable") })
4337
4344
  ]);
4338
4345
 
4339
4346
  // src/schema/postPurchaseOfferPresentation.ts
@@ -6772,6 +6779,7 @@ export {
6772
6779
  PostPurchaseOfferEligibilityContext,
6773
6780
  PostPurchaseOfferPresentation,
6774
6781
  PostPurchaseOfferSkipReason,
6782
+ PostPurchaseOfferUnavailableReason,
6775
6783
  PostPurchaseOptions,
6776
6784
  PostPurchaseOrderAttributes,
6777
6785
  PostPurchaseOrderIdSchema,
@@ -3,6 +3,19 @@ export declare const PostPurchaseOfferSkipReason: z.ZodEnum<{
3
3
  empty: "empty";
4
4
  "no-eligible-subscription": "no-eligible-subscription";
5
5
  }>;
6
+ /**
7
+ * Why an Offer could not be classified (REB-25054). `eligible-sts` is an STS candidate classified without the STS
8
+ * opt-in; each other reason names what was unreadable: the caller's context, the merchant's settings, or the
9
+ * catalog (the data source's products, or its metadata overrides). An ordinary product reason (sold out,
10
+ * plan-only, no variants, undeclared options) never answers `unavailable`: that member is ineligible instead.
11
+ */
12
+ export declare const PostPurchaseOfferUnavailableReason: z.ZodEnum<{
13
+ "catalog-unreadable": "catalog-unreadable";
14
+ "context-unreadable": "context-unreadable";
15
+ "eligible-sts": "eligible-sts";
16
+ "settings-unreadable": "settings-unreadable";
17
+ }>;
18
+ export type PostPurchaseOfferUnavailableReason = z.infer<typeof PostPurchaseOfferUnavailableReason>;
6
19
  export declare const PostPurchaseOfferEligibilityContext: z.ZodObject<{
7
20
  excludedProductIds: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
8
21
  originalLineItems: z.ZodArray<z.ZodObject<{
@@ -49,7 +62,12 @@ export declare const PostPurchaseOfferEligibility: z.ZodDiscriminatedUnion<[z.Zo
49
62
  }>;
50
63
  status: z.ZodLiteral<"skip">;
51
64
  }, z.core.$strip>, z.ZodObject<{
52
- reason: z.ZodOptional<z.ZodLiteral<"eligible-sts">>;
65
+ reason: z.ZodOptional<z.ZodEnum<{
66
+ "catalog-unreadable": "catalog-unreadable";
67
+ "context-unreadable": "context-unreadable";
68
+ "eligible-sts": "eligible-sts";
69
+ "settings-unreadable": "settings-unreadable";
70
+ }>>;
53
71
  status: z.ZodLiteral<"unavailable">;
54
72
  }, z.core.$strip>], "status">;
55
73
  export type PostPurchaseOfferEligibility = z.infer<typeof PostPurchaseOfferEligibility>;
@@ -7501,11 +7501,11 @@ var PostPurchaseMemberPointer = PostPurchaseProgressSchema.shape.members.element
7501
7501
 
7502
7502
  // src/server/normalizePostPurchaseCollection.ts
7503
7503
  var import_zod82 = require("zod");
7504
+ var PostPurchaseProductLimit = import_zod82.z.number().int().positive();
7504
7505
  var normalizePostPurchaseCollection = (settings, response, excludedProductIds, report) => {
7505
7506
  const { dropped, normalized, products } = normalizePostPurchaseSource(settings, response, report);
7506
- const limit = import_zod82.z.number().int().positive();
7507
- limit.parse(normalized.original.productLimit);
7508
- const cap = limit.parse(normalized.effective.productLimit);
7507
+ PostPurchaseProductLimit.parse(normalized.original.productLimit);
7508
+ const cap = PostPurchaseProductLimit.parse(normalized.effective.productLimit);
7509
7509
  const prefix = underPostPurchaseCap(products, cap);
7510
7510
  const unread = underPostPurchaseCap(dropped, cap);
7511
7511
  if (unread.length && !prefix.length) throw new import_zod82.ZodError(unread.flatMap(({ issues }) => issues));
@@ -7517,6 +7517,8 @@ var normalizePostPurchaseCollection = (settings, response, excludedProductIds, r
7517
7517
  };
7518
7518
 
7519
7519
  // src/server/postPurchaseVariantCatalog.ts
7520
+ var PostPurchaseVariantOptionMismatch = class extends Error {
7521
+ };
7520
7522
  var projectPostPurchaseVariantCatalog = (product, settings) => {
7521
7523
  const options2 = PostPurchaseVariantCatalog.shape.options.parse(product.options === void 0 ? [] : product.options).sort((a, b) => a.position - b.position);
7522
7524
  const variants = product.variants.map((variant2) => ({
@@ -7530,7 +7532,7 @@ var projectPostPurchaseVariantCatalog = (product, settings) => {
7530
7532
  throw new Error("Ambiguous post-purchase variant catalog");
7531
7533
  for (const tuple of tuples) {
7532
7534
  if (options2.some(({ position, values: values2 }) => !values2.some((value) => value === tuple[position - 1])) || tuple.some((value, index) => value != null && !options2[index]))
7533
- throw new Error("Variant option tuple does not match the catalog");
7535
+ throw new PostPurchaseVariantOptionMismatch("Variant option tuple does not match the catalog");
7534
7536
  }
7535
7537
  return PostPurchaseVariantCatalog.parse({
7536
7538
  displayMode: settings.productOptions.variantSelectorDisplayMode,
@@ -7553,6 +7555,12 @@ var projectPostPurchaseVariantCatalog = (product, settings) => {
7553
7555
  var import_zod83 = require("zod");
7554
7556
  var selection2 = PreparedPostPurchaseProduct.shape.selection;
7555
7557
  var PostPurchaseOfferSkipReason = import_zod83.z.enum(["empty", "no-eligible-subscription"]);
7558
+ var PostPurchaseOfferUnavailableReason = import_zod83.z.enum([
7559
+ "catalog-unreadable",
7560
+ "context-unreadable",
7561
+ "eligible-sts",
7562
+ "settings-unreadable"
7563
+ ]);
7556
7564
  var PostPurchaseOfferEligibilityContext = import_zod83.z.object({
7557
7565
  excludedProductIds: PostPurchaseExcludedProductIds,
7558
7566
  originalLineItems: import_zod83.z.array(selection2.pick({ productId: true, sellingPlanId: true, variantId: true }))
@@ -7573,7 +7581,8 @@ var PostPurchaseOfferEligibility = import_zod83.z.discriminatedUnion("status", [
7573
7581
  status: import_zod83.z.literal("retain")
7574
7582
  }),
7575
7583
  import_zod83.z.object({ reason: PostPurchaseOfferSkipReason, status: import_zod83.z.literal("skip") }),
7576
- import_zod83.z.object({ reason: import_zod83.z.literal("eligible-sts").optional(), status: import_zod83.z.literal("unavailable") })
7584
+ /** The classifier always names a reason; it stays optional so a reasonless answer from before still parses. */
7585
+ import_zod83.z.object({ reason: PostPurchaseOfferUnavailableReason.optional(), status: import_zod83.z.literal("unavailable") })
7577
7586
  ]);
7578
7587
 
7579
7588
  // src/schema/postPurchaseQuantity.ts
@@ -8057,14 +8066,13 @@ var preparePostPurchaseCollectionView = (widgetId, settings, response, report, r
8057
8066
  };
8058
8067
 
8059
8068
  // src/server/postPurchaseMemberEligibility.ts
8060
- var PostPurchaseInventoryUnavailable = class extends Error {
8061
- };
8062
- var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8069
+ var eligibility = (normalized, product, excluded) => {
8063
8070
  if (product.productGroupVariants.length) {
8064
8071
  const group = readPostPurchaseGroup(product, normalized.effective, excluded);
8072
+ if (!group.entries.length) return void 0;
8065
8073
  return group.entries.map(({ product: option }) => postPurchaseMemberEligibility(normalized, option)).some(Boolean);
8066
8074
  }
8067
- if (!product.variants.length) throw new Error("Unsupported catalog");
8075
+ if (!product.variants.length) return false;
8068
8076
  const variants = projectPostPurchaseVariantCatalog(product, normalized.effective).variants;
8069
8077
  const catalogs = product.variants.map((variant2) => {
8070
8078
  const catalog = projectPostPurchasePlanCatalog({
@@ -8072,23 +8080,31 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8072
8080
  data: [{ ...product, variants: [variant2] }]
8073
8081
  });
8074
8082
  if (!catalog) throw new Error("Unreadable subscription catalog");
8075
- if (normalized.effective.productType === "one-time" && product.sellingPlanGroups.some((group) => group.requiresSellingPlan))
8076
- throw new Error("Unsupported purchase mode");
8077
8083
  return catalog;
8078
8084
  });
8079
- if (!variants.some((variant2) => variant2.available))
8080
- throw new PostPurchaseInventoryUnavailable("Unavailable inventory");
8081
8085
  return catalogs.some(
8082
8086
  (catalog, index) => variants[index].available && (normalized.effective.productType !== "subscription" && !catalog.requiresSellingPlan || normalized.effective.productType !== "one-time" && catalog.plans.length > 0)
8083
8087
  );
8084
8088
  };
8089
+ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8090
+ try {
8091
+ return eligibility(normalized, product, excluded);
8092
+ } catch (error) {
8093
+ if (error instanceof PostPurchaseVariantOptionMismatch) return false;
8094
+ throw error;
8095
+ }
8096
+ };
8085
8097
 
8086
8098
  // src/server/classifyPostPurchaseOffer.ts
8087
8099
  var identities = (members) => members.map(({ product, productIndex }) => ({ productId: product.id, productIndex }));
8088
8100
  var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8101
+ let unreadable = "context-unreadable";
8089
8102
  try {
8090
8103
  const trusted = PostPurchaseOfferEligibilityContext.parse(context);
8091
8104
  const { sts: stsMode = false } = PostPurchaseStsOptions.parse(options2 ?? {});
8105
+ unreadable = "settings-unreadable";
8106
+ PostPurchaseProductLimit.parse(PostPurchaseProductSettings.parse(deepCamelCase(settings)).productLimit);
8107
+ unreadable = "catalog-unreadable";
8092
8108
  const { members, normalized } = normalizePostPurchaseCollection(settings, response);
8093
8109
  const configurations = [normalized.original, normalized.effective];
8094
8110
  const stsEnabled = configurations.some((config) => config.upgradeToSubscription.allow);
@@ -8101,33 +8117,28 @@ var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8101
8117
  ))
8102
8118
  return { reason: "eligible-sts", status: "unavailable" };
8103
8119
  if (normalized.effective.productType !== "one-time" || sts) {
8104
- configurations.forEach((config) => PostPurchaseSubscriptionSettings.parse(config.subscription));
8120
+ unreadable = "settings-unreadable";
8121
+ PostPurchaseSubscriptionSettings.parse(normalized.original.subscription);
8122
+ unreadable = "catalog-unreadable";
8123
+ PostPurchaseSubscriptionSettings.parse(normalized.effective.subscription);
8105
8124
  }
8106
8125
  const lines = trusted.originalLineItems;
8107
8126
  const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines)) : [];
8108
8127
  if (stsMembers.length) return { members: identities(stsMembers), mode: "sts", status: "retain" };
8109
- const classified = members.map((member) => {
8110
- try {
8111
- return {
8112
- ...member,
8113
- eligible: postPurchaseMemberEligibility(normalized, member.product, trusted.excludedProductIds)
8114
- };
8115
- } catch (error) {
8116
- if (error instanceof PostPurchaseInventoryUnavailable && !member.product.productGroupVariants.length && trusted.excludedProductIds.includes(member.product.id))
8117
- return { ...member, eligible: false };
8118
- throw error;
8119
- }
8120
- });
8128
+ const classified = members.map((member) => ({
8129
+ ...member,
8130
+ eligible: postPurchaseMemberEligibility(normalized, member.product, trusted.excludedProductIds)
8131
+ }));
8121
8132
  if (!members.length) return { reason: "empty", status: "skip" };
8122
8133
  if (stsEnabled) return { reason: "no-eligible-subscription", status: "skip" };
8123
8134
  const candidates = classified.filter(
8124
- ({ product }) => product.productGroupVariants.length ? readPostPurchaseGroup(product, normalized.effective, trusted.excludedProductIds).entries.length > 0 : !trusted.excludedProductIds.includes(product.id)
8135
+ ({ eligible, product }) => product.productGroupVariants.length ? eligible !== void 0 : !trusted.excludedProductIds.includes(product.id)
8125
8136
  );
8126
8137
  if (!candidates.length) return { reason: "empty", status: "skip" };
8127
8138
  const retained = candidates.filter(({ eligible }) => eligible);
8128
8139
  return retained.length ? { members: identities(retained), status: "retain" } : { reason: "no-eligible-subscription", status: "skip" };
8129
8140
  } catch {
8130
- return { status: "unavailable" };
8141
+ return { reason: unreadable, status: "unavailable" };
8131
8142
  }
8132
8143
  };
8133
8144
 
@@ -7428,11 +7428,11 @@ var PostPurchaseMemberPointer = PostPurchaseProgressSchema.shape.members.element
7428
7428
 
7429
7429
  // src/server/normalizePostPurchaseCollection.ts
7430
7430
  import { z as z82, ZodError } from "zod";
7431
+ var PostPurchaseProductLimit = z82.number().int().positive();
7431
7432
  var normalizePostPurchaseCollection = (settings, response, excludedProductIds, report) => {
7432
7433
  const { dropped, normalized, products } = normalizePostPurchaseSource(settings, response, report);
7433
- const limit = z82.number().int().positive();
7434
- limit.parse(normalized.original.productLimit);
7435
- const cap = limit.parse(normalized.effective.productLimit);
7434
+ PostPurchaseProductLimit.parse(normalized.original.productLimit);
7435
+ const cap = PostPurchaseProductLimit.parse(normalized.effective.productLimit);
7436
7436
  const prefix = underPostPurchaseCap(products, cap);
7437
7437
  const unread = underPostPurchaseCap(dropped, cap);
7438
7438
  if (unread.length && !prefix.length) throw new ZodError(unread.flatMap(({ issues }) => issues));
@@ -7444,6 +7444,8 @@ var normalizePostPurchaseCollection = (settings, response, excludedProductIds, r
7444
7444
  };
7445
7445
 
7446
7446
  // src/server/postPurchaseVariantCatalog.ts
7447
+ var PostPurchaseVariantOptionMismatch = class extends Error {
7448
+ };
7447
7449
  var projectPostPurchaseVariantCatalog = (product, settings) => {
7448
7450
  const options2 = PostPurchaseVariantCatalog.shape.options.parse(product.options === void 0 ? [] : product.options).sort((a, b) => a.position - b.position);
7449
7451
  const variants = product.variants.map((variant2) => ({
@@ -7457,7 +7459,7 @@ var projectPostPurchaseVariantCatalog = (product, settings) => {
7457
7459
  throw new Error("Ambiguous post-purchase variant catalog");
7458
7460
  for (const tuple of tuples) {
7459
7461
  if (options2.some(({ position, values: values2 }) => !values2.some((value) => value === tuple[position - 1])) || tuple.some((value, index) => value != null && !options2[index]))
7460
- throw new Error("Variant option tuple does not match the catalog");
7462
+ throw new PostPurchaseVariantOptionMismatch("Variant option tuple does not match the catalog");
7461
7463
  }
7462
7464
  return PostPurchaseVariantCatalog.parse({
7463
7465
  displayMode: settings.productOptions.variantSelectorDisplayMode,
@@ -7480,6 +7482,12 @@ var projectPostPurchaseVariantCatalog = (product, settings) => {
7480
7482
  import { z as z83 } from "zod";
7481
7483
  var selection2 = PreparedPostPurchaseProduct.shape.selection;
7482
7484
  var PostPurchaseOfferSkipReason = z83.enum(["empty", "no-eligible-subscription"]);
7485
+ var PostPurchaseOfferUnavailableReason = z83.enum([
7486
+ "catalog-unreadable",
7487
+ "context-unreadable",
7488
+ "eligible-sts",
7489
+ "settings-unreadable"
7490
+ ]);
7483
7491
  var PostPurchaseOfferEligibilityContext = z83.object({
7484
7492
  excludedProductIds: PostPurchaseExcludedProductIds,
7485
7493
  originalLineItems: z83.array(selection2.pick({ productId: true, sellingPlanId: true, variantId: true }))
@@ -7500,7 +7508,8 @@ var PostPurchaseOfferEligibility = z83.discriminatedUnion("status", [
7500
7508
  status: z83.literal("retain")
7501
7509
  }),
7502
7510
  z83.object({ reason: PostPurchaseOfferSkipReason, status: z83.literal("skip") }),
7503
- z83.object({ reason: z83.literal("eligible-sts").optional(), status: z83.literal("unavailable") })
7511
+ /** The classifier always names a reason; it stays optional so a reasonless answer from before still parses. */
7512
+ z83.object({ reason: PostPurchaseOfferUnavailableReason.optional(), status: z83.literal("unavailable") })
7504
7513
  ]);
7505
7514
 
7506
7515
  // src/schema/postPurchaseQuantity.ts
@@ -7984,14 +7993,13 @@ var preparePostPurchaseCollectionView = (widgetId, settings, response, report, r
7984
7993
  };
7985
7994
 
7986
7995
  // src/server/postPurchaseMemberEligibility.ts
7987
- var PostPurchaseInventoryUnavailable = class extends Error {
7988
- };
7989
- var postPurchaseMemberEligibility = (normalized, product, excluded) => {
7996
+ var eligibility = (normalized, product, excluded) => {
7990
7997
  if (product.productGroupVariants.length) {
7991
7998
  const group = readPostPurchaseGroup(product, normalized.effective, excluded);
7999
+ if (!group.entries.length) return void 0;
7992
8000
  return group.entries.map(({ product: option }) => postPurchaseMemberEligibility(normalized, option)).some(Boolean);
7993
8001
  }
7994
- if (!product.variants.length) throw new Error("Unsupported catalog");
8002
+ if (!product.variants.length) return false;
7995
8003
  const variants = projectPostPurchaseVariantCatalog(product, normalized.effective).variants;
7996
8004
  const catalogs = product.variants.map((variant2) => {
7997
8005
  const catalog = projectPostPurchasePlanCatalog({
@@ -7999,23 +8007,31 @@ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
7999
8007
  data: [{ ...product, variants: [variant2] }]
8000
8008
  });
8001
8009
  if (!catalog) throw new Error("Unreadable subscription catalog");
8002
- if (normalized.effective.productType === "one-time" && product.sellingPlanGroups.some((group) => group.requiresSellingPlan))
8003
- throw new Error("Unsupported purchase mode");
8004
8010
  return catalog;
8005
8011
  });
8006
- if (!variants.some((variant2) => variant2.available))
8007
- throw new PostPurchaseInventoryUnavailable("Unavailable inventory");
8008
8012
  return catalogs.some(
8009
8013
  (catalog, index) => variants[index].available && (normalized.effective.productType !== "subscription" && !catalog.requiresSellingPlan || normalized.effective.productType !== "one-time" && catalog.plans.length > 0)
8010
8014
  );
8011
8015
  };
8016
+ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
8017
+ try {
8018
+ return eligibility(normalized, product, excluded);
8019
+ } catch (error) {
8020
+ if (error instanceof PostPurchaseVariantOptionMismatch) return false;
8021
+ throw error;
8022
+ }
8023
+ };
8012
8024
 
8013
8025
  // src/server/classifyPostPurchaseOffer.ts
8014
8026
  var identities = (members) => members.map(({ product, productIndex }) => ({ productId: product.id, productIndex }));
8015
8027
  var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8028
+ let unreadable = "context-unreadable";
8016
8029
  try {
8017
8030
  const trusted = PostPurchaseOfferEligibilityContext.parse(context);
8018
8031
  const { sts: stsMode = false } = PostPurchaseStsOptions.parse(options2 ?? {});
8032
+ unreadable = "settings-unreadable";
8033
+ PostPurchaseProductLimit.parse(PostPurchaseProductSettings.parse(deepCamelCase(settings)).productLimit);
8034
+ unreadable = "catalog-unreadable";
8019
8035
  const { members, normalized } = normalizePostPurchaseCollection(settings, response);
8020
8036
  const configurations = [normalized.original, normalized.effective];
8021
8037
  const stsEnabled = configurations.some((config) => config.upgradeToSubscription.allow);
@@ -8028,33 +8044,28 @@ var classifyPostPurchaseOffer = (settings, response, context, options2) => {
8028
8044
  ))
8029
8045
  return { reason: "eligible-sts", status: "unavailable" };
8030
8046
  if (normalized.effective.productType !== "one-time" || sts) {
8031
- configurations.forEach((config) => PostPurchaseSubscriptionSettings.parse(config.subscription));
8047
+ unreadable = "settings-unreadable";
8048
+ PostPurchaseSubscriptionSettings.parse(normalized.original.subscription);
8049
+ unreadable = "catalog-unreadable";
8050
+ PostPurchaseSubscriptionSettings.parse(normalized.effective.subscription);
8032
8051
  }
8033
8052
  const lines = trusted.originalLineItems;
8034
8053
  const stsMembers = sts ? members.filter(({ product }) => postPurchaseStsLine(normalized, product, lines)) : [];
8035
8054
  if (stsMembers.length) return { members: identities(stsMembers), mode: "sts", status: "retain" };
8036
- const classified = members.map((member) => {
8037
- try {
8038
- return {
8039
- ...member,
8040
- eligible: postPurchaseMemberEligibility(normalized, member.product, trusted.excludedProductIds)
8041
- };
8042
- } catch (error) {
8043
- if (error instanceof PostPurchaseInventoryUnavailable && !member.product.productGroupVariants.length && trusted.excludedProductIds.includes(member.product.id))
8044
- return { ...member, eligible: false };
8045
- throw error;
8046
- }
8047
- });
8055
+ const classified = members.map((member) => ({
8056
+ ...member,
8057
+ eligible: postPurchaseMemberEligibility(normalized, member.product, trusted.excludedProductIds)
8058
+ }));
8048
8059
  if (!members.length) return { reason: "empty", status: "skip" };
8049
8060
  if (stsEnabled) return { reason: "no-eligible-subscription", status: "skip" };
8050
8061
  const candidates = classified.filter(
8051
- ({ product }) => product.productGroupVariants.length ? readPostPurchaseGroup(product, normalized.effective, trusted.excludedProductIds).entries.length > 0 : !trusted.excludedProductIds.includes(product.id)
8062
+ ({ eligible, product }) => product.productGroupVariants.length ? eligible !== void 0 : !trusted.excludedProductIds.includes(product.id)
8052
8063
  );
8053
8064
  if (!candidates.length) return { reason: "empty", status: "skip" };
8054
8065
  const retained = candidates.filter(({ eligible }) => eligible);
8055
8066
  return retained.length ? { members: identities(retained), status: "retain" } : { reason: "no-eligible-subscription", status: "skip" };
8056
8067
  } catch {
8057
- return { status: "unavailable" };
8068
+ return { reason: unreadable, status: "unavailable" };
8058
8069
  }
8059
8070
  };
8060
8071
 
@@ -1,5 +1,8 @@
1
+ import { z } from 'zod';
1
2
  import { PostPurchaseExcludedProductIds } from '../schema/postPurchaseProductCollectionView';
2
3
  import type { Reporter } from '../server/shared';
4
+ /** A collection's `productLimit`, original or effective: a positive whole number. */
5
+ export declare const PostPurchaseProductLimit: z.ZodNumber;
3
6
  /**
4
7
  * Preserve the original capped positions before applying product-wide exclusions. `productLimit` caps raw
5
8
  * positions, and a member's `productIndex` is its raw position: a product the schema dropped leaves its slot
@@ -1,7 +1,13 @@
1
1
  import type { PostPurchaseExcludedProductIds } from '../schema/postPurchaseProductCollectionView';
2
2
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
- /** The member's catalog is readable, but no variant is in stock. */
4
- export declare class PostPurchaseInventoryUnavailable extends Error {
5
- }
6
- /** Catalog facts only: preparation still owns defaults, quantity, terms, and presentation. */
7
- export declare const postPurchaseMemberEligibility: (normalized: ReturnType<typeof normalizePostPurchaseProduct>, product: ReturnType<typeof normalizePostPurchaseProduct>["data"][number], excluded?: PostPurchaseExcludedProductIds) => boolean;
3
+ type Normalized = ReturnType<typeof normalizePostPurchaseProduct>;
4
+ /**
5
+ * Catalog facts only: preparation still owns defaults, quantity, terms, and presentation. `false` is an ordinary
6
+ * product reason (REB-25054): no available variant, plan-only on a one-time Offer, or option values the product
7
+ * does not declare, for the member or, for a group, every sibling. A sibling with undeclared options leaves its
8
+ * whole group ineligible, as preparation cannot read that group either. `undefined` is a group whose every
9
+ * sibling is excluded, so nothing is left to offer; the group is read before exclusions apply, though, so an
10
+ * excluded sibling with undeclared options still answers `false`. A throw is unreadable data.
11
+ */
12
+ export declare const postPurchaseMemberEligibility: (normalized: Normalized, product: Normalized["data"][number], excluded?: PostPurchaseExcludedProductIds) => boolean | undefined;
13
+ export {};
@@ -1,3 +1,9 @@
1
1
  import { PostPurchaseVariantCatalog } from '../schema/postPurchaseVariantCatalog';
2
2
  import type { normalizePostPurchaseProduct } from '../server/normalizePostPurchaseProduct';
3
+ /**
4
+ * A variant carries an option value its product does not declare (an optionless product, say): the merchandise is
5
+ * readable but cannot be offered as it stands, so member eligibility leaves it out rather than failing the Offer.
6
+ */
7
+ export declare class PostPurchaseVariantOptionMismatch extends Error {
8
+ }
3
9
  export declare const projectPostPurchaseVariantCatalog: (product: ReturnType<typeof normalizePostPurchaseProduct>["data"][number], settings: ReturnType<typeof normalizePostPurchaseProduct>["effective"]) => PostPurchaseVariantCatalog;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.56.2",
3
+ "version": "3.56.3",
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.",