@rebuy/rebuy 3.34.0 → 3.35.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.
Files changed (30) hide show
  1. package/dist/index.cjs +878 -794
  2. package/dist/index.mjs +879 -795
  3. package/dist/schema/index.d.ts +5 -0
  4. package/dist/schema/postPurchaseGroupCatalog.d.ts +56 -0
  5. package/dist/schema/postPurchaseOfferEligibility.d.ts +32 -0
  6. package/dist/schema/postPurchaseOfferPresentation.d.ts +386 -0
  7. package/dist/schema/postPurchaseProductSource.d.ts +10 -5
  8. package/dist/schema/postPurchaseQuantity.d.ts +13 -0
  9. package/dist/schema/postPurchaseVariantDefault.d.ts +17 -0
  10. package/dist/schema/postPurchaseVariantDefaultContext.d.ts +10 -0
  11. package/dist/server/__fixtures__/postPurchaseGroup.d.ts +75 -0
  12. package/dist/server/classifyPostPurchaseOffer.d.ts +3 -0
  13. package/dist/server/createPostPurchaseOrderAttributes.d.ts +9 -0
  14. package/dist/server/index.cjs +605 -192
  15. package/dist/server/index.d.ts +8 -1
  16. package/dist/server/index.mjs +605 -192
  17. package/dist/server/postPurchaseMemberEligibility.d.ts +7 -0
  18. package/dist/server/postPurchaseOfferCopy.d.ts +3 -0
  19. package/dist/server/postPurchaseProductEligibility.d.ts +3 -7
  20. package/dist/server/postPurchaseSubscriptionEligibility.d.ts +2 -3
  21. package/dist/server/prepareNormalizedSubscription.d.ts +2 -3
  22. package/dist/server/preparePostPurchaseGroupDeclineDisplay.d.ts +12 -0
  23. package/dist/server/preparePostPurchaseOfferPresentation.d.ts +137 -0
  24. package/dist/server/projectPostPurchaseProduct.d.ts +5 -7
  25. package/dist/server/readPostPurchaseGroup.d.ts +101 -0
  26. package/dist/server/readPostPurchaseGroupSelection.d.ts +110 -0
  27. package/dist/server/readPostPurchaseQuantity.d.ts +27 -0
  28. package/dist/server/resolvePostPurchaseMemberDefault.d.ts +8 -0
  29. package/dist/server/selectPostPurchaseChoice.d.ts +2 -3
  30. package/package.json +1 -1
@@ -627,7 +627,7 @@ import { v7 as uuidv713 } from "uuid";
627
627
  import { z as z27 } from "zod";
628
628
 
629
629
  // src/schema/widgets/checkout-and-beyond/common.ts
630
- var freezeEnum = (values) => Object.freeze(Object.fromEntries(values.map((v) => [v, v])));
630
+ var freezeEnum = (values2) => Object.freeze(Object.fromEntries(values2.map((v) => [v, v])));
631
631
  var bannerColors = ["info", "success", "warning", "critical"];
632
632
  var BannerColor = freezeEnum(bannerColors);
633
633
  var borderRadii = ["none", "small", "base", "large", "fullyRounded"];
@@ -2093,15 +2093,15 @@ var CABImageSectionFields = z27.object({
2093
2093
  width: z27.number().default(100)
2094
2094
  });
2095
2095
  var iconNameSet = new Set(iconNames);
2096
- var isIconName = (source) => iconNameSet.has(source);
2097
- var CABImageSection = CABImageSectionFields.superRefine(({ category, source }, ctx) => {
2098
- if (category === "icon" && source !== "" && !isIconName(source))
2096
+ var isIconName = (source2) => iconNameSet.has(source2);
2097
+ var CABImageSection = CABImageSectionFields.superRefine(({ category, source: source2 }, ctx) => {
2098
+ if (category === "icon" && source2 !== "" && !isIconName(source2))
2099
2099
  ctx.addIssue({
2100
2100
  code: "custom",
2101
2101
  message: "icon-mode source must be an icon name",
2102
2102
  path: ["source"]
2103
2103
  });
2104
- if (category !== "icon" && isIconName(source))
2104
+ if (category !== "icon" && isIconName(source2))
2105
2105
  ctx.addIssue({
2106
2106
  code: "custom",
2107
2107
  message: "source must be a URL or dynamic token unless category is `icon`",
@@ -2213,10 +2213,10 @@ var HEADER_PARAGRAPH_STYLES = {
2213
2213
  superTitle: { color: "base", fontSize: "extraLarge", marks: [{ type: "bold" }] },
2214
2214
  title: { color: "base", fontSize: "medium", marks: [{ type: "bold" }] }
2215
2215
  };
2216
- var convertContentBlockTextToV2 = (language2, text2 = literalText, textAlign = "center") => {
2217
- const description = language2?.description;
2218
- const superTitle = language2?.superTitle;
2219
- const title = language2?.title;
2216
+ var convertContentBlockTextToV2 = (language3, text2 = literalText, textAlign = "center") => {
2217
+ const description = language3?.description;
2218
+ const superTitle = language3?.superTitle;
2219
+ const title = language3?.title;
2220
2220
  return TiptapDocument.parse({
2221
2221
  attrs: { blockSpacing: "tight" },
2222
2222
  content: [
@@ -2328,7 +2328,7 @@ var LOCATION_TO_EDITOR_MODE = {
2328
2328
  };
2329
2329
 
2330
2330
  // src/transforms/contentBlockV1/convertContentBlockToV2.ts
2331
- var hasText = (language2) => !!language2 && !!(language2.description || language2.superTitle || language2.title);
2331
+ var hasText = (language3) => !!language3 && !!(language3.description || language3.superTitle || language3.title);
2332
2332
  var convertContentBlockToV2 = ({ id, settings }) => {
2333
2333
  const imageBlocks = settings.imageBlocks ?? [];
2334
2334
  return CABRootSection.parse({
@@ -2601,15 +2601,15 @@ var stripHtmlComments = (html = "") => html.replace(HTML_COMMENT_RE2, "");
2601
2601
  var maybeFlushParagraph = (state) => {
2602
2602
  if (state.currentRuns.length > 0) flushParagraph(state);
2603
2603
  };
2604
- var htmlToTiptap = (html, options = {}) => {
2604
+ var htmlToTiptap = (html, options2 = {}) => {
2605
2605
  const state = {
2606
2606
  currentRuns: [],
2607
- defaultTextStyleMark: buildDefaultTextStyleMark(options.defaultTextStyle),
2607
+ defaultTextStyleMark: buildDefaultTextStyleMark(options2.defaultTextStyle),
2608
2608
  listStack: [],
2609
2609
  paragraphs: [],
2610
2610
  stack: []
2611
2611
  };
2612
- const blockSpacing = options.blockSpacing ?? "base";
2612
+ const blockSpacing = options2.blockSpacing ?? "base";
2613
2613
  if (typeof html !== "string" || !html) return makeDoc([makeParagraph([])], blockSpacing);
2614
2614
  const htmlWithoutComments = stripHtmlComments(html);
2615
2615
  let lastIndex = 0;
@@ -2854,9 +2854,9 @@ var isMonetizeV1 = isWidgetV1(WidgetType.ui_extension_ad);
2854
2854
  var HTML_TAG_RE2 = /<[a-zA-Z]/;
2855
2855
  var containsHtml = (text2) => HTML_TAG_RE2.test(text2);
2856
2856
  var isObject = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2857
- var normalizeTextValue = (value, options = {}) => {
2857
+ var normalizeTextValue = (value, options2 = {}) => {
2858
2858
  const tokenized = normalizeTokens(value);
2859
- return containsHtml(tokenized) || options.defaultTextStyle ? htmlToTiptap(tokenized, options) : decodeTextEntities(tokenized);
2859
+ return containsHtml(tokenized) || options2.defaultTextStyle ? htmlToTiptap(tokenized, options2) : decodeTextEntities(tokenized);
2860
2860
  };
2861
2861
  var normalizeLangValue = (value) => typeof value === "string" ? normalizeTextValue(value) : value;
2862
2862
  var normalizeTextContent = (node) => {
@@ -2889,15 +2889,15 @@ var resolveExperiment = (experiments, placeholderId, visitorId) => {
2889
2889
  if (!experiment || experiment.data.length === 0) return void 0;
2890
2890
  const roll = rollPercent(`${visitorId}:${experiment.id}`);
2891
2891
  let cumulative = 0;
2892
- const variant = experiment.data.find((candidate) => {
2892
+ const variant2 = experiment.data.find((candidate) => {
2893
2893
  cumulative += candidate.traffic;
2894
2894
  return roll < cumulative;
2895
2895
  }) ?? experiment.data[0];
2896
2896
  return {
2897
- elementId: variant.elementId,
2897
+ elementId: variant2.elementId,
2898
2898
  experimentId: experiment.id,
2899
2899
  experimentType: experiment.type,
2900
- selectedId: variant.id
2900
+ selectedId: variant2.id
2901
2901
  };
2902
2902
  };
2903
2903
 
@@ -3345,23 +3345,23 @@ var QUANTITY_INPUT_TYPE = {
3345
3345
  "buttons-manual-input": "number",
3346
3346
  "dropdown-select": "select"
3347
3347
  };
3348
- var buildQuantityErrors = (quantity) => {
3348
+ var buildQuantityErrors = (quantity2) => {
3349
3349
  const en = {
3350
- ...quantity.maxError && { max: normalizeTokens(quantity.maxError, QUANTITY_ERROR_TOKENS) },
3351
- ...quantity.minError && { min: normalizeTokens(quantity.minError, QUANTITY_ERROR_TOKENS) },
3352
- ...quantity.negativeError && { neg: normalizeTokens(quantity.negativeError, QUANTITY_ERROR_TOKENS) }
3350
+ ...quantity2.maxError && { max: normalizeTokens(quantity2.maxError, QUANTITY_ERROR_TOKENS) },
3351
+ ...quantity2.minError && { min: normalizeTokens(quantity2.minError, QUANTITY_ERROR_TOKENS) },
3352
+ ...quantity2.negativeError && { neg: normalizeTokens(quantity2.negativeError, QUANTITY_ERROR_TOKENS) }
3353
3353
  };
3354
3354
  return Object.keys(en).length ? { errorMessages: { en } } : {};
3355
3355
  };
3356
3356
  var buildQuantity = (settings) => {
3357
- const quantity = settings.quantityInputs;
3358
- if (!quantity?.enabled) return [];
3359
- const inputType = quantity.type ? QUANTITY_INPUT_TYPE[quantity.type] : void 0;
3360
- const max = Math.min(Math.max(quantity.maxValue ?? 10, 1), 100);
3361
- const min = Math.min(Math.max(quantity.minValue ?? 1, 1), max);
3357
+ const quantity2 = settings.quantityInputs;
3358
+ if (!quantity2?.enabled) return [];
3359
+ const inputType = quantity2.type ? QUANTITY_INPUT_TYPE[quantity2.type] : void 0;
3360
+ const max = Math.min(Math.max(quantity2.maxValue ?? 10, 1), 100);
3361
+ const min = Math.min(Math.max(quantity2.minValue ?? 1, 1), max);
3362
3362
  return [
3363
3363
  {
3364
- ...buildQuantityErrors(quantity),
3364
+ ...buildQuantityErrors(quantity2),
3365
3365
  ...inputType && { inputType },
3366
3366
  max,
3367
3367
  min,
@@ -3371,7 +3371,7 @@ var buildQuantity = (settings) => {
3371
3371
  };
3372
3372
  var buildSubscriptionButton = (settings) => {
3373
3373
  if (settings.productType !== "both" && settings.productType !== "subscription") return [];
3374
- const language2 = settings.language;
3374
+ const language3 = settings.language;
3375
3375
  const slot = (buttonField, override) => ({
3376
3376
  buttonField,
3377
3377
  content: localizedContent(SWITCH_LABELS[buttonField], override, SWITCH_LABEL_TOKENS2),
@@ -3385,9 +3385,9 @@ var buildSubscriptionButton = (settings) => {
3385
3385
  ...settings.productType === "subscription" && { disableDowngrade: true },
3386
3386
  sections: [
3387
3387
  slot("subscriptionOption", void 0),
3388
- slot("switchToOneTimePurchase", language2?.cartSwitchToOnetime),
3389
- slot("switchToSubscriptionNoDiscount", language2?.upgradeToSubscription),
3390
- slot("switchToSubscriptionWithDiscount", language2?.upgradeToSubscription)
3388
+ slot("switchToOneTimePurchase", language3?.cartSwitchToOnetime),
3389
+ slot("switchToSubscriptionNoDiscount", language3?.upgradeToSubscription),
3390
+ slot("switchToSubscriptionWithDiscount", language3?.upgradeToSubscription)
3391
3391
  ],
3392
3392
  sectionType: "button"
3393
3393
  }
@@ -3442,11 +3442,11 @@ var buildOfferCard = (settings, horizontal) => {
3442
3442
  const description = buildDescription(settings);
3443
3443
  const reviews = buildReviews(settings);
3444
3444
  const variants = buildVariants(settings);
3445
- const quantity = buildQuantity(settings);
3445
+ const quantity2 = buildQuantity(settings);
3446
3446
  const subscription2 = buildSubscriptionButton(settings);
3447
3447
  const reveal = buildRevealButton(settings);
3448
3448
  const addButton = buildAddButton(settings);
3449
- const controls = [...description, ...quantity, ...variants, ...subscription2];
3449
+ const controls = [...description, ...quantity2, ...variants, ...subscription2];
3450
3450
  const info = detailBlock([title, ...variantTitle, ...reviews, price], horizontal, variantTitle.length > 0);
3451
3451
  const card = (sections) => [
3452
3452
  {
@@ -3500,9 +3500,9 @@ var buildCarouselNav = () => ({
3500
3500
  sectionType: "layout"
3501
3501
  });
3502
3502
  var HEADER_FIELDS = ["superTitle", "title", "description"];
3503
- var sanitizedHeader = (language2) => Object.fromEntries(
3503
+ var sanitizedHeader = (language3) => Object.fromEntries(
3504
3504
  HEADER_FIELDS.flatMap((field) => {
3505
- const value = language2?.[field];
3505
+ const value = language3?.[field];
3506
3506
  const sanitized = value ? stripContentBlockHtml(normalizeTokens(value)) : "";
3507
3507
  return sanitized ? [[field, sanitized]] : [];
3508
3508
  })
@@ -3599,7 +3599,7 @@ var coerceIntegrations = (integrations) => Object.fromEntries(
3599
3599
  return [key, value];
3600
3600
  })
3601
3601
  );
3602
- var buildBanner = (language2) => language2?.successBannerEnabled ? [
3602
+ var buildBanner = (language3) => language3?.successBannerEnabled ? [
3603
3603
  {
3604
3604
  /**
3605
3605
  * The banner-level tag is the extraction/routing key on BOTH renderers (React CAB's
@@ -3612,7 +3612,7 @@ var buildBanner = (language2) => language2?.successBannerEnabled ? [
3612
3612
  dismissible: true,
3613
3613
  sections: [
3614
3614
  {
3615
- content: bannerMessageContent(language2.successBannerMessageRte),
3615
+ content: bannerMessageContent(language3.successBannerMessageRte),
3616
3616
  sectionType: "text"
3617
3617
  },
3618
3618
  {
@@ -3929,14 +3929,14 @@ var convertCalloutBlock = (raw, report) => {
3929
3929
  };
3930
3930
  const shell = Shell.safeParse(raw);
3931
3931
  if (!shell.success) return omit("Unreadable post-purchase callout");
3932
- const values = fields.map((field) => ({
3932
+ const values2 = fields.map((field) => ({
3933
3933
  field,
3934
3934
  result: CalloutBannerBlock.shape.settings.shape[field].safeParse(shell.data.settings[field])
3935
3935
  }));
3936
- const styled2 = values.every(({ result }) => result.success && typeof result.data === "string");
3936
+ const styled2 = values2.every(({ result }) => result.success && typeof result.data === "string");
3937
3937
  const styles2 = shell.data.styles;
3938
3938
  const horizontal = styled2 ? { center: "center", leading: "start", trailing: "end" }[styles2.textAlignment] : "center";
3939
- const sections = values.flatMap(({ field, result }) => {
3939
+ const sections = values2.flatMap(({ field, result }) => {
3940
3940
  if (!result.success) return omit(`Unreadable post-purchase callout field: ${field}`);
3941
3941
  const text2 = result.data;
3942
3942
  if (text2 === "") return [];
@@ -3958,12 +3958,12 @@ var convertCalloutBlock = (raw, report) => {
3958
3958
  import { z as z32 } from "zod";
3959
3959
 
3960
3960
  // src/transforms/postPurchase/sizeImageSource.ts
3961
- var sizeImageSource = (source, size) => {
3962
- const extension = source.split(".").pop();
3961
+ var sizeImageSource = (source2, size) => {
3962
+ const extension = source2.split(".").pop();
3963
3963
  for (const previous of ["small", "medium", "large", "original", "master"]) {
3964
- source = source.replace(`_${previous}.${extension}`, `.${extension}`);
3964
+ source2 = source2.replace(`_${previous}.${extension}`, `.${extension}`);
3965
3965
  }
3966
- return source.replace(`.${extension}`, `${size === void 0 ? "" : `_${size}`}.${extension}`);
3966
+ return source2.replace(`.${extension}`, `${size === void 0 ? "" : `_${size}`}.${extension}`);
3967
3967
  };
3968
3968
 
3969
3969
  // src/transforms/postPurchase/convertImageFrame.ts
@@ -3973,9 +3973,9 @@ var convertImageFrame = (raw) => {
3973
3973
  const parsed = ImageBlock.shape.settings.options[0].safeParse(raw);
3974
3974
  if (!parsed.success) return null;
3975
3975
  const settings = parsed.data;
3976
- const source = sizeImageSource(Source.parse(settings.image || void 0));
3977
- const withAlt = CABImageSection.safeParse({ altText: settings.alt, source });
3978
- const image = withAlt.success ? withAlt.data : CABImageSection.parse({ source });
3976
+ const source2 = sizeImageSource(Source.parse(settings.image || void 0));
3977
+ const withAlt = CABImageSection.safeParse({ altText: settings.alt, source: source2 });
3978
+ const image = withAlt.success ? withAlt.data : CABImageSection.parse({ source: source2 });
3979
3979
  const size = settings.imageSize > 0 ? settings.imageSize : DEFAULT_IMAGE_SIZE;
3980
3980
  return CABLayoutSection.parse({
3981
3981
  alignment: { horizontal: "center" },
@@ -4212,19 +4212,19 @@ var convertTextBlock = (raw, report) => {
4212
4212
  report?.({ detail: "Unreadable post-purchase text block", kind: "section" });
4213
4213
  return [];
4214
4214
  }
4215
- const values = fields3.map((field) => ({
4215
+ const values2 = fields3.map((field) => ({
4216
4216
  field,
4217
4217
  result: settingsSchema.shape[field].safeParse(shell.data.settings[field])
4218
4218
  }));
4219
4219
  const horizontal = { center: "center", leading: "start", trailing: "end" }[shell.data.settings.horizontalAlignment];
4220
- const styled2 = values.every(
4220
+ const styled2 = values2.every(
4221
4221
  ({ result }) => result.success && (result.data === null || typeof result.data === "string")
4222
4222
  );
4223
4223
  const omit = (field) => {
4224
4224
  report?.({ detail: `Unreadable post-purchase text field: ${field}`, kind: "section" });
4225
4225
  return [];
4226
4226
  };
4227
- const sections = values.flatMap(({ field, result }) => {
4227
+ const sections = values2.flatMap(({ field, result }) => {
4228
4228
  if (!result.success) return omit(field);
4229
4229
  const text2 = result.data;
4230
4230
  if (text2 === null || text2 === "") return [];
@@ -4458,10 +4458,10 @@ var giftOf = (tier) => {
4458
4458
  if (tier.type !== "product") return void 0;
4459
4459
  const product = tier.products?.[0] ?? tier.allProducts?.[0];
4460
4460
  if (!product) return void 0;
4461
- const productId = product.productId ?? product.id;
4462
- if (productId === void 0) return void 0;
4461
+ const productId2 = product.productId ?? product.id;
4462
+ if (productId2 === void 0) return void 0;
4463
4463
  return {
4464
- productId,
4464
+ productId: productId2,
4465
4465
  title: product.title,
4466
4466
  variantIds: positiveIds(product.variantIds)
4467
4467
  };
@@ -4740,14 +4740,14 @@ function serialize(obj) {
4740
4740
  var encodeAttributes = (pairs = []) => JSON.stringify(Object.fromEntries(pairs.map(({ key, value }) => [key, value ?? ""])));
4741
4741
  var buildEngineParams = (input, key) => {
4742
4742
  const { cart, customerId, filterInputs, integrations, limit, productType, shop, visitorId } = input;
4743
- const items = cart.items.map(({ productId, properties, quantity, subscriptionId, variantId }) => ({
4744
- product_id: productId,
4743
+ const items = cart.items.map(({ productId: productId2, properties, quantity: quantity2, subscriptionId, variantId }) => ({
4744
+ product_id: productId2,
4745
4745
  properties: encodeAttributes(properties),
4746
- quantity,
4746
+ quantity: quantity2,
4747
4747
  ...subscriptionId && { subscription_id: subscriptionId },
4748
4748
  variant_id: variantId
4749
4749
  }));
4750
- const itemCount = cart.items.reduce((total, { quantity }) => total + quantity, 0);
4750
+ const itemCount = cart.items.reduce((total, { quantity: quantity2 }) => total + quantity2, 0);
4751
4751
  const subtotal = shopSubtotalCents(cart);
4752
4752
  return {
4753
4753
  cart: {
@@ -4834,13 +4834,13 @@ var OFFER_LIMIT = 4;
4834
4834
  var SUPPORTED_COUNTRIES = /* @__PURE__ */ new Set(["US", "CA", "GB"]);
4835
4835
  var MAX_TRANSACTION_ITEMS = 50;
4836
4836
  var TEST_CUSTOMER_EMAIL = "test@rebuyengine.com";
4837
- var transactionItems = (input) => (input.transactionItems?.map(({ id, priceCents, quantity, title, type }) => ({
4837
+ var transactionItems = (input) => (input.transactionItems?.map(({ id, priceCents, quantity: quantity2, title, type }) => ({
4838
4838
  id,
4839
4839
  price_cents: priceCents,
4840
- quantity,
4840
+ quantity: quantity2,
4841
4841
  title,
4842
4842
  type
4843
- })) ?? input.cart.items.map(({ productId, quantity }) => ({ id: productId, quantity }))).slice(0, MAX_TRANSACTION_ITEMS);
4843
+ })) ?? input.cart.items.map(({ productId: productId2, quantity: quantity2 }) => ({ id: productId2, quantity: quantity2 }))).slice(0, MAX_TRANSACTION_ITEMS);
4844
4844
  var AdsEngineOffer = z37.object({
4845
4845
  /** Pill-style value prop ("Get $45 Cashback"). Carried through the parse; the card does not render it yet (REB-24429 — pill design pending). */
4846
4846
  callout: z37.string().catch(""),
@@ -4958,9 +4958,9 @@ var label = (text2) => ({
4958
4958
  });
4959
4959
  var text = (content) => ({ content: { en: content }, sectionType: SectionType.text });
4960
4960
  var AS_DOC = { defaultTextStyle: {} };
4961
- var safeTextValue = (raw, options) => {
4962
- const converted = normalizeTextValue(raw, options);
4963
- return checkForHTML(converted) ? converted : normalizeTextValue(htmlToPlainText(raw), options);
4961
+ var safeTextValue = (raw, options2) => {
4962
+ const converted = normalizeTextValue(raw, options2);
4963
+ return checkForHTML(converted) ? converted : normalizeTextValue(htmlToPlainText(raw), options2);
4964
4964
  };
4965
4965
  var headlineText = (raw) => {
4966
4966
  const doc = safeTextValue(raw, AS_DOC);
@@ -5444,11 +5444,11 @@ var validateGifts = async (input, ctx) => {
5444
5444
  const [remove, adjust] = await Promise.all([validateGwpGifts(input, ctx), validateSelectableGifts(input, ctx)]);
5445
5445
  const removed = new Set(remove);
5446
5446
  const targets = /* @__PURE__ */ new Map();
5447
- for (const { lineId, quantity } of adjust) {
5447
+ for (const { lineId, quantity: quantity2 } of adjust) {
5448
5448
  if (removed.has(lineId)) continue;
5449
- targets.set(lineId, Math.min(quantity, targets.get(lineId) ?? quantity));
5449
+ targets.set(lineId, Math.min(quantity2, targets.get(lineId) ?? quantity2));
5450
5450
  }
5451
- return { adjust: [...targets].map(([lineId, quantity]) => ({ lineId, quantity })), remove: [...removed] };
5451
+ return { adjust: [...targets].map(([lineId, quantity2]) => ({ lineId, quantity: quantity2 })), remove: [...removed] };
5452
5452
  };
5453
5453
 
5454
5454
  // src/server/normalizePostPurchaseProduct.ts
@@ -5780,14 +5780,14 @@ var SourceAdjustment = z47.object({
5780
5780
  type: z47.enum(["percentage", "fixed_amount", "amount", "price", "none", "fixed"])
5781
5781
  });
5782
5782
  var readPostPurchasePlanAdjustment = (raw) => {
5783
- const source = SourceAdjustment.parse(raw);
5784
- if (source.type === "none" || source.type === "fixed") {
5785
- if (source.amount !== 0) throw new Error("Unsupported plan adjustment");
5783
+ const source2 = SourceAdjustment.parse(raw);
5784
+ if (source2.type === "none" || source2.type === "fixed") {
5785
+ if (source2.amount !== 0) throw new Error("Unsupported plan adjustment");
5786
5786
  return void 0;
5787
5787
  }
5788
5788
  const adjustment = PostPurchasePlanAdjustment.parse({
5789
- amount: source.amount,
5790
- type: source.type === "amount" ? "fixed_amount" : source.type
5789
+ amount: source2.amount,
5790
+ type: source2.type === "amount" ? "fixed_amount" : source2.type
5791
5791
  });
5792
5792
  return adjustment.amount === 0 && adjustment.type !== "price" ? void 0 : adjustment;
5793
5793
  };
@@ -5864,12 +5864,12 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
5864
5864
  const { data } = normalized;
5865
5865
  if (data.length !== 1 || data[0].variants.length !== 1) throw new Error("Ambiguous product");
5866
5866
  const product = data[0];
5867
- const variant = product.variants[0];
5868
- if (variant.productId !== product.id) throw new Error("Foreign variant");
5867
+ const variant2 = product.variants[0];
5868
+ if (variant2.productId !== product.id) throw new Error("Foreign variant");
5869
5869
  const groups = product.sellingPlanGroups;
5870
5870
  const required = groups.map((group) => group.requiresSellingPlan);
5871
5871
  if (required.some((flag) => flag !== required[0])) throw new Error("Conflicting requirement");
5872
- const plans = typeof groups[0]?.id === "number" ? readPostPurchaseLegacyPlans(groups[0], variant.id) : readPostPurchaseModernPlans(groups, variant.sellingPlanAllocations);
5872
+ const plans = typeof groups[0]?.id === "number" ? readPostPurchaseLegacyPlans(groups[0], variant2.id) : readPostPurchaseModernPlans(groups, variant2.sellingPlanAllocations);
5873
5873
  const unique = /* @__PURE__ */ new Map();
5874
5874
  for (const plan of plans) {
5875
5875
  const previous = unique.get(plan.id);
@@ -5880,7 +5880,7 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
5880
5880
  plans: [...unique.values()],
5881
5881
  productId: product.id,
5882
5882
  requiresSellingPlan: required[0] ?? false,
5883
- variantId: variant.id
5883
+ variantId: variant2.id
5884
5884
  });
5885
5885
  } catch {
5886
5886
  report?.({ detail: "Unreadable post-purchase plan catalog", kind: "product" });
@@ -6020,20 +6020,20 @@ var PostPurchaseSubscriptionSettings = subscription.extend({
6020
6020
  });
6021
6021
 
6022
6022
  // src/server/postPurchaseVariantAvailable.ts
6023
- var postPurchaseVariantAvailable = (variant) => !(variant.inventoryManagement && variant.inventoryPolicy === "deny" && variant.inventoryQuantity <= 0);
6023
+ var postPurchaseVariantAvailable = (variant2) => !(variant2.inventoryManagement && variant2.inventoryPolicy === "deny" && variant2.inventoryQuantity <= 0);
6024
6024
 
6025
6025
  // src/server/postPurchaseSubscriptionEligibility.ts
6026
- var subscriptionUnavailable = (normalized, plan, allowCollection, quantity) => {
6026
+ var subscriptionUnavailable = (normalized, plan, allowCollection, quantity2) => {
6027
6027
  const { data, effective, original } = normalized;
6028
6028
  const unavailable = "An eligible subscription plan with supported shipping and discount is required";
6029
6029
  if (data.length !== 1 || data[0].variants.length !== 1) return unavailable;
6030
6030
  if (effective.productType === "one-time" || [original, effective].some(
6031
- (config) => !allowCollection && config.productLimit !== 1 || config.upgradeToSubscription.allow || !quantity && config.quantityInputs.enabled && (config.quantityInputs.minValue !== config.quantityInputs.maxValue || config.quantityInputs.defaultQuantity !== config.quantityInputs.minValue)
6031
+ (config) => !allowCollection && config.productLimit !== 1 || config.upgradeToSubscription.allow || !quantity2 && config.quantityInputs.enabled && (config.quantityInputs.minValue !== config.quantityInputs.maxValue || config.quantityInputs.defaultQuantity !== config.quantityInputs.minValue)
6032
6032
  ))
6033
6033
  return unavailable;
6034
6034
  const product = data[0];
6035
- const variant = product.variants[0];
6036
- if (product.productGroupVariants.length || !postPurchaseVariantAvailable(variant) || variant.productId !== product.id)
6035
+ const variant2 = product.variants[0];
6036
+ if (product.productGroupVariants.length || !postPurchaseVariantAvailable(variant2) || variant2.productId !== product.id)
6037
6037
  return unavailable;
6038
6038
  const terms = [original, effective].map(
6039
6039
  (config) => PostPurchaseSubscriptionSettings.safeParse(config.subscription)
@@ -6048,8 +6048,8 @@ import { z as z55 } from "zod";
6048
6048
  var postPurchasePrimaryImage = (product) => {
6049
6049
  const variantImageId = product.variants[0].imageId;
6050
6050
  const selectedImage = variantImageId ? product.images?.find((image) => image.id === variantImageId) : void 0;
6051
- const source = PostPurchasePrimaryImageSource.parse((selectedImage ?? product.image)?.src || void 0);
6052
- return { description: product.title, source: sizeImageSource(source, "800x800") };
6051
+ const source2 = PostPurchasePrimaryImageSource.parse((selectedImage ?? product.image)?.src || void 0);
6052
+ return { description: product.title, source: sizeImageSource(source2, "800x800") };
6053
6053
  };
6054
6054
 
6055
6055
  // src/server/postPurchaseProductGallery.ts
@@ -6078,26 +6078,26 @@ var projectPostPurchaseProductDisplay = (effective, metadata, product) => PostPu
6078
6078
  // src/server/projectPostPurchaseProduct.ts
6079
6079
  var projectPostPurchaseProduct = ({ data, effective, metadata, original }, selectedQuantity) => {
6080
6080
  const product = data[0];
6081
- const variant = product.variants[0];
6081
+ const variant2 = product.variants[0];
6082
6082
  return {
6083
6083
  display: projectPostPurchaseProductDisplay(effective, metadata, product),
6084
6084
  labels: effective.language.global,
6085
- product: { ...product, variant },
6085
+ product: { ...product, variant: variant2 },
6086
6086
  selection: {
6087
6087
  productId: product.id,
6088
6088
  quantity: selectedQuantity?.quantity ?? product.metadata?.productQuantity ?? (original.quantityInputs.enabled ? original.quantityInputs.defaultQuantity : 1),
6089
- variantId: variant.id
6089
+ variantId: variant2.id
6090
6090
  }
6091
6091
  };
6092
6092
  };
6093
6093
 
6094
6094
  // src/server/prepareNormalizedSubscription.ts
6095
- var prepareNormalizedSubscription = (normalized, report, plan, quantity, allowCollection = false) => {
6095
+ var prepareNormalizedSubscription = (normalized, report, plan, quantity2, allowCollection = false) => {
6096
6096
  if (!plan) return { reason: "Subscription plan is unavailable", status: "unsupported" };
6097
- const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity);
6097
+ const reason = subscriptionUnavailable(normalized, plan, allowCollection, quantity2);
6098
6098
  if (reason) return { reason, status: "unsupported" };
6099
6099
  const { effective } = normalized;
6100
- const projected = projectPostPurchaseProduct(normalized, quantity);
6100
+ const projected = projectPostPurchaseProduct(normalized, quantity2);
6101
6101
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
6102
6102
  const shippingPrice = shipping.type === "free" ? 0 : shipping.amount;
6103
6103
  return PreparedPostPurchaseSubscription.parse({
@@ -6134,6 +6134,412 @@ var preparePostPurchaseSubscription = (settings, response, report) => {
6134
6134
  );
6135
6135
  };
6136
6136
 
6137
+ // src/schema/postPurchaseOfferEligibility.ts
6138
+ import { z as z57 } from "zod";
6139
+
6140
+ // src/schema/postPurchaseProductCollectionView.ts
6141
+ import { z as z56 } from "zod";
6142
+ var PostPurchaseExcludedProductIds = z56.array(PostPurchaseProductId).default([]);
6143
+
6144
+ // src/schema/postPurchaseOfferEligibility.ts
6145
+ var selection = PreparedPostPurchaseProduct.shape.selection;
6146
+ var PostPurchaseOfferSkipReason = z57.enum(["empty", "no-eligible-subscription"]);
6147
+ var PostPurchaseOfferEligibilityContext = z57.object({
6148
+ excludedProductIds: PostPurchaseExcludedProductIds,
6149
+ originalLineItems: z57.array(selection.pick({ productId: true, sellingPlanId: true, variantId: true }))
6150
+ });
6151
+ var PostPurchaseOfferEligibility = z57.discriminatedUnion("status", [
6152
+ z57.object({
6153
+ members: z57.array(
6154
+ z57.object({ productId: selection.shape.productId, productIndex: z57.number().int().nonnegative() })
6155
+ ),
6156
+ status: z57.literal("retain")
6157
+ }),
6158
+ z57.object({ reason: PostPurchaseOfferSkipReason, status: z57.literal("skip") }),
6159
+ z57.object({ reason: z57.literal("eligible-upgrade").optional(), status: z57.literal("unavailable") })
6160
+ ]);
6161
+
6162
+ // src/server/normalizePostPurchaseCollection.ts
6163
+ import { z as z58 } from "zod";
6164
+ var normalizePostPurchaseCollection = (settings, response, excludedProductIds) => {
6165
+ const normalized = normalizePostPurchaseProduct(settings, response);
6166
+ const limit = z58.number().int().positive();
6167
+ limit.parse(normalized.original.productLimit);
6168
+ const prefix = normalized.data.slice(0, limit.parse(normalized.effective.productLimit));
6169
+ if (new Set(prefix.map(({ id }) => id)).size !== prefix.length)
6170
+ throw new Error("Ambiguous post-purchase collection");
6171
+ const excluded = new Set(PostPurchaseExcludedProductIds.parse(excludedProductIds));
6172
+ const members = prefix.map((product, productIndex) => ({ product, productIndex })).filter(({ product }) => !excluded.has(product.id));
6173
+ return { members, normalized };
6174
+ };
6175
+
6176
+ // src/schema/postPurchaseVariantCatalog.ts
6177
+ import { z as z59 } from "zod";
6178
+ var PostPurchaseVariantSelector = z59.object({ variantId: PostPurchaseVariantId }).strict();
6179
+ var PostPurchaseVariantOption = OfferProduct.shape.options.element.extend({
6180
+ id: z59.number().int().positive(),
6181
+ position: z59.number().int().min(1).max(3)
6182
+ });
6183
+ var PostPurchaseVariantValues = OfferProduct.shape.variants.element.pick({
6184
+ option1: true,
6185
+ option2: true,
6186
+ option3: true
6187
+ });
6188
+ var PostPurchaseVariantCatalog = z59.object({
6189
+ displayMode: PostPurchaseSettings.shape.productOptions.shape.variantSelectorDisplayMode,
6190
+ options: z59.array(PostPurchaseVariantOption),
6191
+ productId: PostPurchaseProductId,
6192
+ showSelectors: PostPurchaseSettings.shape.productOptions.shape.showVariantSelectors,
6193
+ variants: z59.array(
6194
+ PostPurchaseVariantValues.partial().extend({
6195
+ available: z59.boolean(),
6196
+ id: PostPurchaseVariantId,
6197
+ imageId: z59.number().int().nonnegative().nullish(),
6198
+ title: OfferProduct.shape.variants.element.shape.title
6199
+ })
6200
+ )
6201
+ });
6202
+
6203
+ // src/server/postPurchaseVariantCatalog.ts
6204
+ var projectPostPurchaseVariantCatalog = (product, settings) => {
6205
+ const options2 = PostPurchaseVariantCatalog.shape.options.parse(product.options === void 0 ? [] : product.options).sort((a, b) => a.position - b.position);
6206
+ const variants = product.variants.map((variant2) => ({
6207
+ ...variant2,
6208
+ ...PostPurchaseVariantValues.partial().parse(variant2)
6209
+ }));
6210
+ const tuples = variants.map(({ option1, option2, option3 }) => [option1, option2, option3]);
6211
+ if (new Set(product.variants.map(({ id }) => id)).size !== product.variants.length || product.variants.some(({ productId: productId2 }) => productId2 !== product.id) || new Set(options2.map(({ id }) => id)).size !== options2.length || options2.some(
6212
+ ({ position, values: values2 }, index) => position !== index + 1 || new Set(values2).size !== values2.length
6213
+ ) || product.variants.length > 1 && new Set(tuples.map((tuple) => JSON.stringify(tuple))).size !== tuples.length)
6214
+ throw new Error("Ambiguous post-purchase variant catalog");
6215
+ for (const tuple of tuples) {
6216
+ if (options2.some(({ position, values: values2 }) => !values2.some((value) => value === tuple[position - 1])) || tuple.some((value, index) => value != null && !options2[index]))
6217
+ throw new Error("Variant option tuple does not match the catalog");
6218
+ }
6219
+ return PostPurchaseVariantCatalog.parse({
6220
+ displayMode: settings.productOptions.variantSelectorDisplayMode,
6221
+ options: options2,
6222
+ productId: product.id,
6223
+ showSelectors: settings.productOptions.showVariantSelectors,
6224
+ variants: variants.map(({ id, imageId, option1, option2, option3, title, ...inventory }) => ({
6225
+ available: postPurchaseVariantAvailable(inventory),
6226
+ id,
6227
+ imageId,
6228
+ option1,
6229
+ option2,
6230
+ option3,
6231
+ title
6232
+ }))
6233
+ });
6234
+ };
6235
+
6236
+ // src/schema/postPurchaseGroupCatalog.ts
6237
+ import { z as z60 } from "zod";
6238
+ var productId = PreparedPostPurchaseProduct.shape.selection.shape.productId;
6239
+ var source = PostPurchaseProductSource.shape.data.element;
6240
+ var PostPurchaseGroupDescription = source.pick({ description: true, id: true, image: true, images: true, title: true }).extend({
6241
+ description: source.shape.description.default(null),
6242
+ image: source.shape.image.default(null),
6243
+ options: PostPurchaseVariantCatalog.shape.options.default([])
6244
+ });
6245
+ var PostPurchaseGroupCatalog = z60.object({
6246
+ memberProductId: productId,
6247
+ options: z60.array(z60.object({ productId, title: z60.string(), variantCatalog: PostPurchaseVariantCatalog }))
6248
+ });
6249
+ var PostPurchaseGroupSelection = z60.object({
6250
+ memberProductId: productId,
6251
+ productId
6252
+ });
6253
+
6254
+ // src/server/readPostPurchaseGroup.ts
6255
+ var readPostPurchaseGroup = (product, settings, excludedProductIds) => {
6256
+ const descriptions = PostPurchaseGroupDescription.nullable().array().parse(product.productGroupVariants).filter((value) => value !== null);
6257
+ const ids = new Set(descriptions.map((value) => value.id));
6258
+ if (ids.size !== descriptions.length || new Set(product.variants.map(({ id }) => id)).size !== product.variants.length || product.variants.some(({ productId: productId2 }) => !ids.has(productId2)))
6259
+ throw new Error("Ambiguous post-purchase group ownership");
6260
+ const excluded = new Set(PostPurchaseExcludedProductIds.parse(excludedProductIds));
6261
+ const entries = descriptions.map((description) => {
6262
+ const value = {
6263
+ ...product,
6264
+ ...description,
6265
+ images: description.images,
6266
+ productGroupVariants: [],
6267
+ variants: product.variants.filter(({ productId: productId2 }) => productId2 === description.id)
6268
+ };
6269
+ return { product: value, variantCatalog: projectPostPurchaseVariantCatalog(value, settings) };
6270
+ }).filter(({ product: value }) => !excluded.has(value.id));
6271
+ return {
6272
+ catalog: PostPurchaseGroupCatalog.parse({
6273
+ memberProductId: product.id,
6274
+ options: entries.map(({ product: value, variantCatalog }) => ({
6275
+ productId: value.id,
6276
+ title: value.title,
6277
+ variantCatalog
6278
+ }))
6279
+ }),
6280
+ entries
6281
+ };
6282
+ };
6283
+
6284
+ // src/server/postPurchaseMemberEligibility.ts
6285
+ var PostPurchaseInventoryUnavailable = class extends Error {
6286
+ };
6287
+ var postPurchaseMemberEligibility = (normalized, product, excluded) => {
6288
+ if (product.productGroupVariants.length) {
6289
+ const group = readPostPurchaseGroup(product, normalized.effective, excluded);
6290
+ return group.entries.map(({ product: option }) => postPurchaseMemberEligibility(normalized, option)).some(Boolean);
6291
+ }
6292
+ if (!product.variants.length) throw new Error("Unsupported catalog");
6293
+ const variants = projectPostPurchaseVariantCatalog(product, normalized.effective).variants;
6294
+ const catalogs = product.variants.map((variant2) => {
6295
+ const catalog = projectPostPurchasePlanCatalog({
6296
+ ...normalized,
6297
+ data: [{ ...product, variants: [variant2] }]
6298
+ });
6299
+ if (!catalog) throw new Error("Unreadable subscription catalog");
6300
+ if (normalized.effective.productType === "one-time" && catalog.requiresSellingPlan)
6301
+ throw new Error("Unsupported purchase mode");
6302
+ return catalog;
6303
+ });
6304
+ if (!variants.some((variant2) => variant2.available))
6305
+ throw new PostPurchaseInventoryUnavailable("Unavailable inventory");
6306
+ return catalogs.some(
6307
+ (catalog, index) => variants[index].available && (normalized.effective.productType !== "subscription" && !catalog.requiresSellingPlan || normalized.effective.productType !== "one-time" && catalog.plans.length > 0)
6308
+ );
6309
+ };
6310
+
6311
+ // src/server/classifyPostPurchaseOffer.ts
6312
+ var classifyPostPurchaseOffer = (settings, response, context) => {
6313
+ try {
6314
+ const trusted = PostPurchaseOfferEligibilityContext.parse(context);
6315
+ const { members, normalized } = normalizePostPurchaseCollection(settings, response);
6316
+ const configurations = [normalized.original, normalized.effective];
6317
+ const upgrades = configurations.some((config) => config.upgradeToSubscription.allow);
6318
+ const oneTime = new Set(
6319
+ trusted.originalLineItems.filter((line) => line.sellingPlanId === void 0).map((line) => line.variantId)
6320
+ );
6321
+ if (upgrades && members.some(
6322
+ ({ product }) => product.sellingPlanGroups?.length && product.variants.some((variant2) => oneTime.has(variant2.id))
6323
+ ))
6324
+ return { reason: "eligible-upgrade", status: "unavailable" };
6325
+ if (normalized.effective.productType !== "one-time") {
6326
+ configurations.forEach((config) => PostPurchaseSubscriptionSettings.parse(config.subscription));
6327
+ }
6328
+ const classified = members.map((member) => {
6329
+ try {
6330
+ return {
6331
+ ...member,
6332
+ eligible: postPurchaseMemberEligibility(normalized, member.product, trusted.excludedProductIds)
6333
+ };
6334
+ } catch (error) {
6335
+ if (error instanceof PostPurchaseInventoryUnavailable && !member.product.productGroupVariants.length && trusted.excludedProductIds.includes(member.product.id))
6336
+ return { ...member, eligible: false };
6337
+ throw error;
6338
+ }
6339
+ });
6340
+ if (!members.length) return { reason: "empty", status: "skip" };
6341
+ if (upgrades) return { reason: "no-eligible-subscription", status: "skip" };
6342
+ const candidates = classified.filter(
6343
+ ({ product }) => product.productGroupVariants.length ? readPostPurchaseGroup(product, normalized.effective, trusted.excludedProductIds).entries.length > 0 : !trusted.excludedProductIds.includes(product.id)
6344
+ );
6345
+ if (!candidates.length) return { reason: "empty", status: "skip" };
6346
+ const retained = candidates.filter(({ eligible }) => eligible);
6347
+ return retained.length ? {
6348
+ members: retained.map(({ product, productIndex }) => ({
6349
+ productId: product.id,
6350
+ productIndex
6351
+ })),
6352
+ status: "retain"
6353
+ } : { reason: "no-eligible-subscription", status: "skip" };
6354
+ } catch {
6355
+ return { status: "unavailable" };
6356
+ }
6357
+ };
6358
+
6359
+ // src/schema/postPurchaseVariantDefault.ts
6360
+ import { z as z61 } from "zod";
6361
+ var values = PostPurchaseVariantValues.partial().extend({
6362
+ option1: PostPurchaseVariantValues.shape.option1.nullish()
6363
+ });
6364
+ var variant = values.extend({
6365
+ id: PostPurchaseVariantId,
6366
+ inventoryManagement: z61.string().nullable(),
6367
+ inventoryPolicy: z61.string(),
6368
+ inventoryQuantity: z61.number()
6369
+ });
6370
+ var options = z61.array(PostPurchaseVariantOption.pick({ name: true, values: true }));
6371
+ var PostPurchaseVariantDefaultProduct = z61.object({
6372
+ id: PostPurchaseProductId,
6373
+ options,
6374
+ selectedVariant: variant.optional(),
6375
+ variants: z61.array(variant)
6376
+ });
6377
+ var PostPurchaseVariantDefaultSettings = PostPurchaseSettings.shape.productOptions.pick({
6378
+ hideOutOfStockVariants: true,
6379
+ matchVariant: true
6380
+ });
6381
+ var PostPurchaseOrderAttributes = z61.record(z61.string(), z61.array(z61.string())).default({});
6382
+ var PostPurchaseOriginalVariants = z61.array(
6383
+ z61.object({ productId: PostPurchaseProductId, variantId: PostPurchaseVariantId })
6384
+ );
6385
+ var PostPurchaseAttributeProducts = z61.array(
6386
+ z61.object({ id: PostPurchaseProductId, options, variants: z61.array(values.extend({ id: PostPurchaseVariantId })) })
6387
+ );
6388
+
6389
+ // src/server/createPostPurchaseOrderAttributes.ts
6390
+ var createPostPurchaseOrderAttributes = (original, enriched) => {
6391
+ const products = PostPurchaseAttributeProducts.parse(enriched);
6392
+ const attributes = {};
6393
+ for (const item of PostPurchaseOriginalVariants.parse(original)) {
6394
+ const product = products.find(({ id }) => id === item.productId);
6395
+ const variant2 = product?.variants.find(({ id }) => id === item.variantId);
6396
+ if (!variant2 || !product) continue;
6397
+ ["option1", "option2", "option3"].forEach((key, index) => {
6398
+ const value = variant2[key];
6399
+ const option = product.options[index];
6400
+ if (!value || !option) return;
6401
+ const values2 = attributes[option.name] ?? [];
6402
+ if (!values2.includes(value)) attributes[option.name] = [...values2, value];
6403
+ });
6404
+ }
6405
+ return attributes;
6406
+ };
6407
+
6408
+ // src/server/readPostPurchaseGroupSelection.ts
6409
+ var readPostPurchaseGroupSelection = (settings, response, requested, excludedProductIds) => {
6410
+ const selection2 = PostPurchaseGroupSelection.parse(requested);
6411
+ const { members, normalized } = normalizePostPurchaseCollection(settings, response);
6412
+ const member = members.find(({ product }) => product.id === selection2.memberProductId);
6413
+ if (!member || !member.product.productGroupVariants.length) return null;
6414
+ const group = readPostPurchaseGroup(member.product, normalized.effective, excludedProductIds);
6415
+ const entry = group.entries.find(({ product }) => product.id === selection2.productId);
6416
+ if (!entry) return null;
6417
+ return {
6418
+ groupCatalog: group.catalog,
6419
+ memberProductId: member.product.id,
6420
+ productIndex: member.productIndex,
6421
+ reportingProduct: { id: member.product.id, title: member.product.title },
6422
+ response: { data: [entry.product], metadata: normalized.metadata },
6423
+ variantCatalog: entry.variantCatalog
6424
+ };
6425
+ };
6426
+
6427
+ // src/server/preparePostPurchaseGroupDeclineDisplay.ts
6428
+ var preparePostPurchaseGroupDeclineDisplay = (settings, response, requested, excludedProductIds) => {
6429
+ const group = readPostPurchaseGroupSelection(settings, response, requested, excludedProductIds);
6430
+ const variant2 = group?.variantCatalog.variants.find(({ id }) => id === requested.variantId);
6431
+ return group && variant2 ? {
6432
+ display: {
6433
+ productTitle: group.reportingProduct.title,
6434
+ variantId: variant2.id,
6435
+ variantTitle: variant2.title
6436
+ },
6437
+ productIndex: group.productIndex
6438
+ } : null;
6439
+ };
6440
+
6441
+ // src/server/preparePostPurchaseOfferPresentation.ts
6442
+ import { merge as merge2 } from "es-toolkit/compat";
6443
+ import { z as z64 } from "zod";
6444
+
6445
+ // src/schema/postPurchaseOfferPresentation.ts
6446
+ import { z as z62 } from "zod";
6447
+ var language2 = PostPurchaseSettings.shape.language.shape.global.unwrap().unwrap();
6448
+ var PostPurchaseOfferPresentation = z62.strictObject({
6449
+ labels: language2.pick({ declineAll: true, declineThisOffer: true }),
6450
+ layout: PostPurchaseSettings.shape.offersLayout,
6451
+ sections: CABRootSection.nullable(),
6452
+ showDeclineAll: PostPurchaseSettings.shape.productOptions.shape.showDeclineAll
6453
+ });
6454
+
6455
+ // src/server/postPurchaseOfferCopy.ts
6456
+ import { z as z63 } from "zod";
6457
+ var Copy = z63.object({
6458
+ superTitle: CalloutBannerBlock.shape.settings.shape.superTitle.unwrap().optional().catch(void 0),
6459
+ title: CalloutBannerBlock.shape.settings.shape.title.unwrap().optional().catch(void 0)
6460
+ });
6461
+ var Callout = z63.object({ settings: z63.record(z63.string(), z63.unknown()), type: z63.literal("callout_banner") });
6462
+ var postPurchaseOfferCopy = (blocks, metadata) => {
6463
+ const copy = Copy.safeParse(metadata.widget.language);
6464
+ if (!blocks || !copy.success) return blocks;
6465
+ const first = blocks.findIndex((block) => Callout.pick({ type: true }).safeParse(block).success);
6466
+ return blocks.map((block, index) => {
6467
+ if (index !== first) return block;
6468
+ const callout = Callout.passthrough().safeParse(block);
6469
+ if (!callout.success) return block;
6470
+ return {
6471
+ ...callout.data,
6472
+ settings: {
6473
+ ...callout.data.settings,
6474
+ ...copy.data.title && { title: copy.data.title },
6475
+ ...copy.data.superTitle && { superTitle: copy.data.superTitle }
6476
+ }
6477
+ };
6478
+ });
6479
+ };
6480
+
6481
+ // src/server/preparePostPurchaseOfferPresentation.ts
6482
+ var FrameSettings = PostPurchaseSettings.pick({ language: true, offersLayout: true, productOptions: true }).extend({
6483
+ blocks: z64.array(z64.unknown()).optional()
6484
+ });
6485
+ var preparePostPurchaseOfferPresentation = (widgetId, settings, response, report) => {
6486
+ const { metadata } = PostPurchaseProductSource.pick({ metadata: true }).parse(deepCamelCase(response));
6487
+ const original = FrameSettings.parse(deepCamelCase(settings));
6488
+ const effective = FrameSettings.parse(merge2({}, original, metadata.widget));
6489
+ let sections = null;
6490
+ try {
6491
+ sections = convertMessagePreview(
6492
+ widgetId,
6493
+ { blocks: postPurchaseOfferCopy(effective.blocks, metadata), type: "shopify_post_purchase_extension" },
6494
+ report
6495
+ );
6496
+ } catch (error) {
6497
+ if (!(error instanceof NotFoundError)) throw error;
6498
+ }
6499
+ return PostPurchaseOfferPresentation.parse({
6500
+ labels: effective.language.global,
6501
+ layout: effective.offersLayout,
6502
+ sections,
6503
+ showDeclineAll: effective.productOptions.showDeclineAll
6504
+ });
6505
+ };
6506
+
6507
+ // src/schema/postPurchaseQuantity.ts
6508
+ import { z as z65 } from "zod";
6509
+ var quantity = PreparedPostPurchaseProduct.shape.selection.shape.quantity;
6510
+ var PostPurchaseQuantityPolicy = PostPurchaseSettings.shape.quantityInputs.omit({ defaultQuantity: true }).extend({
6511
+ initialQuantity: quantity,
6512
+ maxValue: quantity,
6513
+ minValue: z65.literal(0).transform(() => 1).or(quantity)
6514
+ }).refine(({ maxValue, minValue }) => minValue <= maxValue, "Quantity bounds are reversed");
6515
+
6516
+ // src/server/readPostPurchaseQuantity.ts
6517
+ var PostPurchaseQuantityUnavailable = class extends Error {
6518
+ };
6519
+ var readPostPurchaseQuantity = ({
6520
+ data,
6521
+ effective,
6522
+ original
6523
+ }, selection2) => {
6524
+ const positive = PreparedPostPurchaseProduct.shape.selection.shape.quantity;
6525
+ for (const config of [original, effective]) {
6526
+ if (!config.quantityInputs.enabled) continue;
6527
+ const initialQuantity2 = positive.parse(config.quantityInputs.defaultQuantity);
6528
+ const policy2 = PostPurchaseQuantityPolicy.parse({ ...config.quantityInputs, initialQuantity: initialQuantity2 });
6529
+ if (initialQuantity2 < policy2.minValue || initialQuantity2 > policy2.maxValue)
6530
+ throw new Error("Configured quantity default is outside its bounds");
6531
+ }
6532
+ const initialQuantity = data[0]?.metadata?.productQuantity ?? (original.quantityInputs.enabled ? original.quantityInputs.defaultQuantity : 1);
6533
+ const policy = PostPurchaseQuantityPolicy.parse(
6534
+ effective.quantityInputs.enabled ? { ...effective.quantityInputs, initialQuantity } : { enabled: false, initialQuantity, maxValue: initialQuantity, minValue: initialQuantity }
6535
+ );
6536
+ const requested = "quantity" in selection2 ? selection2.quantity : selection2.expectedQuantity === initialQuantity ? void 0 : selection2.expectedQuantity;
6537
+ const quantity2 = positive.parse(requested ?? initialQuantity);
6538
+ if (requested !== void 0 && (policy.enabled ? quantity2 < policy.minValue || quantity2 > policy.maxValue : quantity2 !== initialQuantity))
6539
+ throw new PostPurchaseQuantityUnavailable("Requested quantity is unavailable");
6540
+ return { policy, quantity: quantity2 };
6541
+ };
6542
+
6137
6543
  // src/server/analytics.ts
6138
6544
  import { chunk } from "es-toolkit";
6139
6545
 
@@ -6178,8 +6584,8 @@ var TAXONOMY = {
6178
6584
  "added-to-cart": {
6179
6585
  /** React credits the add to the A/B-selected variant (`defined_noun_id: selectedId`). */
6180
6586
  buildDefinedNounId: ({ selectedId }) => selectedId != null ? String(selectedId) : void 0,
6181
- buildMeta: ({ productId, variantId }) => ({
6182
- ...productId != null && { [`product_id:${productId}`]: true },
6587
+ buildMeta: ({ productId: productId2, variantId }) => ({
6588
+ ...productId2 != null && { [`product_id:${productId2}`]: true },
6183
6589
  ...variantId != null && { [`variant_id:${variantId}`]: true }
6184
6590
  }),
6185
6591
  buildTags: experimentTags,
@@ -6270,7 +6676,7 @@ var postAnalyticsEvents = async (input, ctx) => {
6270
6676
  };
6271
6677
 
6272
6678
  // src/server/requestSchemas.ts
6273
- import { z as z56 } from "zod";
6679
+ import { z as z66 } from "zod";
6274
6680
  var REQUEST_BOUNDS = {
6275
6681
  /** Cart-level attributes per cart. */
6276
6682
  CART_ATTRIBUTES_MAX: 100,
@@ -6301,42 +6707,42 @@ var REQUEST_BOUNDS = {
6301
6707
  /** Rich ads transaction lines — the engine's ads contract. */
6302
6708
  TRANSACTION_ITEMS_MAX: 50
6303
6709
  };
6304
- var KeyValue = z56.object({
6305
- key: z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6306
- value: z56.string().max(REQUEST_BOUNDS.STRING_VALUE_MAX).optional()
6710
+ var KeyValue = z66.object({
6711
+ key: z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6712
+ value: z66.string().max(REQUEST_BOUNDS.STRING_VALUE_MAX).optional()
6307
6713
  });
6308
- var CartLineInputSchema = z56.object({
6309
- productId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6310
- properties: z56.array(KeyValue).max(REQUEST_BOUNDS.LINE_PROPERTIES_MAX).optional(),
6311
- quantity: z56.number(),
6312
- subscriptionId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6313
- variantId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX)
6714
+ var CartLineInputSchema = z66.object({
6715
+ productId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6716
+ properties: z66.array(KeyValue).max(REQUEST_BOUNDS.LINE_PROPERTIES_MAX).optional(),
6717
+ quantity: z66.number(),
6718
+ subscriptionId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6719
+ variantId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX)
6314
6720
  });
6315
- var CartInputSchema = z56.object({
6316
- attributes: z56.array(KeyValue).max(REQUEST_BOUNDS.CART_ATTRIBUTES_MAX).optional(),
6721
+ var CartInputSchema = z66.object({
6722
+ attributes: z66.array(KeyValue).max(REQUEST_BOUNDS.CART_ATTRIBUTES_MAX).optional(),
6317
6723
  /**
6318
6724
  * Presentment-per-shop-currency FX rate (buyer price ÷ shop price, e.g. `1.35` for a USD shop
6319
6725
  * selling in CAD). Money rule thresholds are authored in SHOP currency, so the engine subtotal is
6320
6726
  * divided by this before evaluation. Omit (or send 1) when the cart is already in the shop's currency.
6321
6727
  */
6322
- currencyRate: z56.number().optional(),
6728
+ currencyRate: z66.number().optional(),
6323
6729
  /** Applied discount codes; the engine evaluates `discount_code` rules against them (sent as `[{code}]`). */
6324
- discountCodes: z56.array(z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX)).max(REQUEST_BOUNDS.DISCOUNT_CODES_MAX).optional(),
6730
+ discountCodes: z66.array(z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX)).max(REQUEST_BOUNDS.DISCOUNT_CODES_MAX).optional(),
6325
6731
  /** Presentment cents of cart-level discount allocations, subtracted from `subtotal` for the engine. */
6326
- discountTotal: z56.number().optional(),
6327
- items: z56.array(CartLineInputSchema).max(REQUEST_BOUNDS.CART_ITEMS_MAX),
6732
+ discountTotal: z66.number().optional(),
6733
+ items: z66.array(CartLineInputSchema).max(REQUEST_BOUNDS.CART_ITEMS_MAX),
6328
6734
  /** Presentment subtotal in cents. */
6329
- subtotal: z56.number()
6735
+ subtotal: z66.number()
6330
6736
  });
6331
- var UserConfigInputSchema = z56.object({ shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX) });
6332
- var DataSourceInputSchema = z56.object({
6737
+ var UserConfigInputSchema = z66.object({ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX) });
6738
+ var DataSourceInputSchema = z66.object({
6333
6739
  cart: CartInputSchema,
6334
6740
  /**
6335
6741
  * Numeric Shopify company-location id when the buyer is a B2B session (`buyerIdentity.purchasingCompany`).
6336
6742
  * Present ⇒ the orchestrator filters results by the location's catalog via admin-api (REB-23713); the
6337
6743
  * client's country-context market check is bypassed for B2B (it false-negatives B2B-catalog products).
6338
6744
  */
6339
- companyLocationId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6745
+ companyLocationId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6340
6746
  /**
6341
6747
  * Buyer's market country (ISO 3166-1 alpha-2) — forwarded to the engine as the `country_code` PARAM
6342
6748
  * when the shop's markets toggle is on (REB-21688; the sink was the `X-Country-Code` header under
@@ -6345,16 +6751,16 @@ var DataSourceInputSchema = z56.object({
6345
6751
  * uppercase (the wire value is forwarded verbatim as the param) so a case-sensitive engine lookup
6346
6752
  * can't silently miss a lowercase code — `adsEngine.ts` uppercases for the same reason.
6347
6753
  */
6348
- country: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
6349
- customerId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6350
- dataSourcePath: z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6754
+ country: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
6755
+ customerId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6756
+ dataSourcePath: z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6351
6757
  /**
6352
6758
  * Engine `filter_inputs` override: `'no'` keeps in-cart (input) products in the results/rule
6353
6759
  * outputs — gift validation sends it so a gift's own product can appear in its answer. Omitted = the
6354
6760
  * engine's default filtering.
6355
6761
  */
6356
- filterInputs: z56.enum(["no", "yes"]).optional(),
6357
- integrations: z56.record(z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z56.boolean()).refine((record) => Object.keys(record).length <= REQUEST_BOUNDS.INTEGRATION_KEYS_MAX, {
6762
+ filterInputs: z66.enum(["no", "yes"]).optional(),
6763
+ integrations: z66.record(z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z66.boolean()).refine((record) => Object.keys(record).length <= REQUEST_BOUNDS.INTEGRATION_KEYS_MAX, {
6358
6764
  message: `Too many integration keys (max ${REQUEST_BOUNDS.INTEGRATION_KEYS_MAX})`
6359
6765
  }).optional(),
6360
6766
  /**
@@ -6367,31 +6773,31 @@ var DataSourceInputSchema = z56.object({
6367
6773
  * request header) fails the regex. Callers should send the buyer's resolved locale (e.g. Shopify
6368
6774
  * checkout's `localization.language.isoCode`), not the request header verbatim.
6369
6775
  */
6370
- language: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
6371
- limit: z56.number().max(REQUEST_BOUNDS.LIMIT_MAX),
6372
- productType: z56.enum(["both", "one-time", "subscription"]).optional(),
6373
- shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6374
- visitorId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6375
- });
6376
- var GiftInputSchema = z56.object({
6377
- cost: z56.number(),
6378
- discounted: z56.boolean(),
6776
+ language: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
6777
+ limit: z66.number().max(REQUEST_BOUNDS.LIMIT_MAX),
6778
+ productType: z66.enum(["both", "one-time", "subscription"]).optional(),
6779
+ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6780
+ visitorId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6781
+ });
6782
+ var GiftInputSchema = z66.object({
6783
+ cost: z66.number(),
6784
+ discounted: z66.boolean(),
6379
6785
  /**
6380
6786
  * Which gift attribution the line carries (REB-24363). Defaults to `gwp` because a client that predates
6381
6787
  * the field only ever sent GWP lines — without the default an older extension's request would 422.
6382
6788
  */
6383
- kind: z56.enum(GIFT_KINDS).default("gwp"),
6384
- lineId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6385
- productId: z56.number(),
6789
+ kind: z66.enum(GIFT_KINDS).default("gwp"),
6790
+ lineId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6791
+ productId: z66.number(),
6386
6792
  /**
6387
6793
  * The line's unit count (REB-24363) — the selectable-GWP verdict trims surplus units against milestone
6388
6794
  * capacity. The second deliberate default: a client predating the field sent only GWP lines, whose
6389
6795
  * verdict is whole-line, so a missing quantity reads as one unit rather than 422ing the older request.
6390
6796
  */
6391
- quantity: z56.number().int().min(1).default(1),
6392
- widgetId: z56.number()
6797
+ quantity: z66.number().int().min(1).default(1),
6798
+ widgetId: z66.number()
6393
6799
  });
6394
- var GiftValidationInputSchema = z56.object({
6800
+ var GiftValidationInputSchema = z66.object({
6395
6801
  cart: CartInputSchema,
6396
6802
  /**
6397
6803
  * Buyer's market country — threaded into the per-widget data-source evaluations, which forward it to
@@ -6400,25 +6806,25 @@ var GiftValidationInputSchema = z56.object({
6400
6806
  * turns markets off); regex-bound and uppercase-normalized for the same reasons as
6401
6807
  * `DataSourceInputSchema`'s `country`.
6402
6808
  */
6403
- country: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
6404
- customerId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6405
- gifts: z56.array(GiftInputSchema).max(REQUEST_BOUNDS.GIFTS_MAX),
6809
+ country: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
6810
+ customerId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6811
+ gifts: z66.array(GiftInputSchema).max(REQUEST_BOUNDS.GIFTS_MAX),
6406
6812
  /**
6407
6813
  * Buyer's checkout language — threaded into the per-widget data-source evaluations, which normalize
6408
6814
  * and forward it as the engine `locale` param under the same markets-toggle gate (REB-21688). MUST be
6409
6815
  * a single BCP-47 tag, for the same reason as `DataSourceInputSchema`'s `language` above.
6410
6816
  */
6411
- language: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
6412
- shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6413
- visitorId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6817
+ language: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
6818
+ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6819
+ visitorId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6414
6820
  });
6415
- var GiftAdjustmentSchema = z56.object({
6416
- lineId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6417
- quantity: z56.number().int().nonnegative()
6821
+ var GiftAdjustmentSchema = z66.object({
6822
+ lineId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6823
+ quantity: z66.number().int().nonnegative()
6418
6824
  });
6419
- var GiftValidationResponseSchema = z56.object({
6420
- adjust: z56.array(GiftAdjustmentSchema).max(REQUEST_BOUNDS.GIFTS_MAX).default([]),
6421
- remove: z56.array(z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX)).max(REQUEST_BOUNDS.GIFTS_MAX).refine(
6825
+ var GiftValidationResponseSchema = z66.object({
6826
+ adjust: z66.array(GiftAdjustmentSchema).max(REQUEST_BOUNDS.GIFTS_MAX).default([]),
6827
+ remove: z66.array(z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX)).max(REQUEST_BOUNDS.GIFTS_MAX).refine(
6422
6828
  (remove) => new Set(remove).size === remove.length,
6423
6829
  "a line id may appear at most once in `remove`"
6424
6830
  )
@@ -6429,47 +6835,47 @@ var GiftValidationResponseSchema = z56.object({
6429
6835
  ({ adjust }) => new Set(adjust.map(({ lineId }) => lineId)).size === adjust.length,
6430
6836
  "a line id may appear at most once in `adjust`"
6431
6837
  );
6432
- var MonetizeTransactionItemSchema = z56.object({
6433
- id: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6434
- priceCents: z56.number(),
6435
- quantity: z56.number(),
6436
- title: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX),
6437
- type: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX)
6438
- });
6439
- var MonetizeInputSchema = z56.object({
6838
+ var MonetizeTransactionItemSchema = z66.object({
6839
+ id: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6840
+ priceCents: z66.number(),
6841
+ quantity: z66.number(),
6842
+ title: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX),
6843
+ type: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX)
6844
+ });
6845
+ var MonetizeInputSchema = z66.object({
6440
6846
  cart: CartInputSchema,
6441
6847
  /** Client-minted render-beacon correlation id; forwarded to the ads engine as `client_request_id`. */
6442
- clientRequestId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6443
- country: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6444
- currency: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6848
+ clientRequestId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6849
+ country: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6850
+ currency: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6445
6851
  /** Ads-targeting identity/geo/locale fields (React parity); folded into the engine query verbatim. */
6446
- customerEmail: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6447
- customerFirstName: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6448
- customerId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6449
- customerLastName: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6450
- language: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6852
+ customerEmail: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6853
+ customerFirstName: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6854
+ customerId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6855
+ customerLastName: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6856
+ language: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6451
6857
  /** Buyer-facing order name (e.g. `#1001`) for the server-composed "Order … Confirmed" chrome line. */
6452
- orderName: z56.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6858
+ orderName: z66.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
6453
6859
  /** Ads surface label, forwarded verbatim; `customer_account` is measured/floored as its own surface (the account-area order list). */
6454
- placement: z56.enum(["customer_account", "order_status", "thank_you_page"]),
6860
+ placement: z66.enum(["customer_account", "order_status", "thank_you_page"]),
6455
6861
  /** Privacy Policy footer href — the Monetize widget's `privacy_policy_url` setting (React parity). */
6456
- privacyPolicyUrl: z56.string().max(REQUEST_BOUNDS.STRING_URL_MAX).optional(),
6862
+ privacyPolicyUrl: z66.string().max(REQUEST_BOUNDS.STRING_URL_MAX).optional(),
6457
6863
  /** 30-min rolling session id (React parity); used for the ads `session_id`, else the visitor id. */
6458
- sessionId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6459
- shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6864
+ sessionId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6865
+ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6460
6866
  /** Editor/QA indicator; OR-ed into the ads-engine `testing` flag so it stays out of billing/reporting. */
6461
- testing: z56.boolean().optional(),
6462
- timezone: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6867
+ testing: z66.boolean().optional(),
6868
+ timezone: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6463
6869
  /** Decorated (numeric) order id → the ads `transaction_id`. */
6464
- transactionId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6870
+ transactionId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6465
6871
  /** Rich per-line ads items; capped at the engine's ads contract. Preferred over `cart.items`. */
6466
- transactionItems: z56.array(MonetizeTransactionItemSchema).max(REQUEST_BOUNDS.TRANSACTION_ITEMS_MAX).optional(),
6872
+ transactionItems: z66.array(MonetizeTransactionItemSchema).max(REQUEST_BOUNDS.TRANSACTION_ITEMS_MAX).optional(),
6467
6873
  /** Order total in presentment cents (React prefers total over subtotal); else `cart.subtotal`. */
6468
- transactionTotal: z56.number().optional(),
6469
- visitorId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6470
- zipCode: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6874
+ transactionTotal: z66.number().optional(),
6875
+ visitorId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6876
+ zipCode: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6471
6877
  });
6472
- var WidgetSettingsInputSchema = z56.object({
6878
+ var WidgetSettingsInputSchema = z66.object({
6473
6879
  /**
6474
6880
  * Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
6475
6881
  * `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
@@ -6477,9 +6883,9 @@ var WidgetSettingsInputSchema = z56.object({
6477
6883
  * and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
6478
6884
  * the read to un-busted, never 400 a widget render.
6479
6885
  */
6480
- cacheKey: z56.number().int().positive().optional().catch(void 0),
6481
- id: z56.number(),
6482
- key: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6886
+ cacheKey: z66.number().int().positive().optional().catch(void 0),
6887
+ id: z66.number(),
6888
+ key: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6483
6889
  /**
6484
6890
  * Monetize context for inline hydration: when present AND the widget converts to a Monetize marker,
6485
6891
  * the server fetches/composes the offer cards inside this same request (no second `/cab/monetize`
@@ -6488,36 +6894,36 @@ var WidgetSettingsInputSchema = z56.object({
6488
6894
  * `privacyPolicyUrl` comes from the widget settings server-side (more authoritative than a relay).
6489
6895
  */
6490
6896
  monetize: MonetizeInputSchema.omit({ privacyPolicyUrl: true, shop: true }).optional(),
6491
- shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6897
+ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6492
6898
  /** Stable per-buyer id; when present, enables sticky server-side A/B variant selection. */
6493
- visitorId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6899
+ visitorId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6494
6900
  });
6495
- var AnalyticsEventInputSchema = z56.object({
6496
- name: z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6497
- properties: z56.record(z56.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z56.unknown()).optional(),
6498
- sentAt: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6901
+ var AnalyticsEventInputSchema = z66.object({
6902
+ name: z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
6903
+ properties: z66.record(z66.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z66.unknown()).optional(),
6904
+ sentAt: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6499
6905
  });
6500
- var AnalyticsSegmentsInputSchema = z56.object({
6501
- device: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
6502
- source: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
6503
- visitor: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0)
6906
+ var AnalyticsSegmentsInputSchema = z66.object({
6907
+ device: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
6908
+ source: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
6909
+ visitor: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0)
6504
6910
  });
6505
- var AnalyticsInputSchema = z56.object({
6911
+ var AnalyticsInputSchema = z66.object({
6506
6912
  /** Rebuy cart token (the `r_cart_token` cart attribute), when available — primary aggregation key. */
6507
- cartToken: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6913
+ cartToken: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6508
6914
  /** Shopify checkout token, when available — drives cart/checkout aggregation downstream. */
6509
- checkoutToken: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6510
- events: z56.array(AnalyticsEventInputSchema).max(REQUEST_BOUNDS.EVENTS_MAX),
6915
+ checkoutToken: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
6916
+ events: z66.array(AnalyticsEventInputSchema).max(REQUEST_BOUNDS.EVENTS_MAX),
6511
6917
  /**
6512
6918
  * Request-scoped like the tokens: one visitor's segments apply to every event in the batch. The container
6513
6919
  * degrades too (`null` is a realistic wire artifact of `attributes ? {…} : null`), so the degrade rule is
6514
6920
  * total: nothing about `segments` can cost the envelope.
6515
6921
  */
6516
6922
  segments: AnalyticsSegmentsInputSchema.optional().catch(void 0),
6517
- shop: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6518
- visitorId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6923
+ shop: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6924
+ visitorId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
6519
6925
  /** The widget id (smartBlockId) the events belong to. */
6520
- widgetId: z56.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6926
+ widgetId: z66.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
6521
6927
  });
6522
6928
  export {
6523
6929
  AnalyticsEventInputSchema,
@@ -6533,18 +6939,25 @@ export {
6533
6939
  MonetizeInputSchema,
6534
6940
  MonetizeTransactionItemSchema,
6535
6941
  NotFoundError,
6942
+ PostPurchaseQuantityUnavailable,
6536
6943
  REBUY_ENGINE_HOST,
6537
6944
  REQUEST_BOUNDS,
6538
6945
  UpstreamError,
6539
6946
  UserConfigInputSchema,
6540
6947
  WidgetSettingsInputSchema,
6541
6948
  buildEngineParams,
6949
+ classifyPostPurchaseOffer,
6542
6950
  convertWidgetSettings,
6951
+ createPostPurchaseOrderAttributes,
6543
6952
  fetchDataSourceResults,
6544
6953
  fetchMonetizeOffers,
6545
6954
  fetchUserConfig,
6546
6955
  postAnalyticsEvents,
6956
+ preparePostPurchaseGroupDeclineDisplay,
6957
+ preparePostPurchaseOfferPresentation,
6547
6958
  preparePostPurchaseSubscription,
6959
+ readPostPurchaseGroupSelection,
6960
+ readPostPurchaseQuantity,
6548
6961
  translateAnalyticsEvent,
6549
6962
  validateGifts
6550
6963
  };