@rebuy/rebuy 2.34.0 → 2.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 (43) hide show
  1. package/dist/index.js +204 -23
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.mjs +204 -23
  4. package/dist/index.mjs.map +4 -4
  5. package/dist/schema/checkout-and-beyond/constants.js +2 -0
  6. package/dist/schema/checkout-and-beyond/constants.js.map +2 -2
  7. package/dist/schema/checkout-and-beyond/constants.mjs +2 -0
  8. package/dist/schema/checkout-and-beyond/constants.mjs.map +2 -2
  9. package/dist/schema/checkout-and-beyond/index.js +183 -6
  10. package/dist/schema/checkout-and-beyond/index.js.map +3 -3
  11. package/dist/schema/checkout-and-beyond/index.mjs +183 -6
  12. package/dist/schema/checkout-and-beyond/index.mjs.map +3 -3
  13. package/dist/schema/widget-data.js +197 -23
  14. package/dist/schema/widget-data.js.map +4 -4
  15. package/dist/schema/widget-data.mjs +197 -23
  16. package/dist/schema/widget-data.mjs.map +3 -3
  17. package/dist/schema/widgets/checkout-and-beyond/banner.d.ts +27 -25
  18. package/dist/schema/widgets/checkout-and-beyond/banner.d.ts.map +1 -1
  19. package/dist/schema/widgets/checkout-and-beyond/button.d.ts +5 -0
  20. package/dist/schema/widgets/checkout-and-beyond/button.d.ts.map +1 -1
  21. package/dist/schema/widgets/checkout-and-beyond/carousel.d.ts +25 -25
  22. package/dist/schema/widgets/checkout-and-beyond/cartLine.d.ts +25 -25
  23. package/dist/schema/widgets/checkout-and-beyond/common.d.ts +3 -1
  24. package/dist/schema/widgets/checkout-and-beyond/common.d.ts.map +1 -1
  25. package/dist/schema/widgets/checkout-and-beyond/icons.d.ts +124 -0
  26. package/dist/schema/widgets/checkout-and-beyond/icons.d.ts.map +1 -0
  27. package/dist/schema/widgets/checkout-and-beyond/image.d.ts +319 -2
  28. package/dist/schema/widgets/checkout-and-beyond/image.d.ts.map +1 -1
  29. package/dist/schema/widgets/checkout-and-beyond/index.d.ts +1 -0
  30. package/dist/schema/widgets/checkout-and-beyond/index.d.ts.map +1 -1
  31. package/dist/schema/widgets/checkout-and-beyond/offers.d.ts +25 -25
  32. package/dist/schema/widgets/checkout-and-beyond/progressBar.d.ts +25 -25
  33. package/dist/schema/widgets/checkout-and-beyond/progressBar.d.ts.map +1 -1
  34. package/dist/server/index.js +193 -25
  35. package/dist/server/index.js.map +4 -4
  36. package/dist/server/index.mjs +193 -25
  37. package/dist/server/index.mjs.map +4 -4
  38. package/dist/transforms/index.js +193 -25
  39. package/dist/transforms/index.js.map +3 -3
  40. package/dist/transforms/index.mjs +193 -25
  41. package/dist/transforms/index.mjs.map +3 -3
  42. package/dist/transforms/smartCart/convertSmartCartProgressBarToV2.d.ts.map +1 -1
  43. package/package.json +1 -1
@@ -14,6 +14,7 @@ var borderWidths = ["base", "medium", "thick"];
14
14
  var BorderWidth = freezeEnum(borderWidths);
15
15
  var buttonActions = [
16
16
  "acceptOffer",
17
+ "addGift",
17
18
  "addToOrder",
18
19
  "declineAllOffers",
19
20
  "declineOffer",
@@ -23,6 +24,7 @@ var buttonActions = [
23
24
  "goToPrevPage",
24
25
  "goToURL",
25
26
  "removeFromOrder",
27
+ "removeGift",
26
28
  "selectVariant",
27
29
  "showPagination",
28
30
  "switchToSubscription",
@@ -152,6 +154,90 @@ var VariantSelector = freezeEnum(variantSelectors);
152
154
  var verticalAlignments = ["top", "middle", "bottom"];
153
155
  var VerticalAlignment = freezeEnum(verticalAlignments);
154
156
 
157
+ // src/schema/widgets/checkout-and-beyond/icons.ts
158
+ var iconNames = [
159
+ "alert-circle",
160
+ "alert-triangle",
161
+ "alert-triangle-filled",
162
+ "arrow-down",
163
+ "arrow-left",
164
+ "arrow-right",
165
+ "arrow-up",
166
+ "arrow-up-right",
167
+ "bag",
168
+ "bullet",
169
+ "calendar",
170
+ "camera",
171
+ "caret-down",
172
+ "cart",
173
+ "cash-dollar",
174
+ "categories",
175
+ "check",
176
+ "check-circle",
177
+ "check-circle-filled",
178
+ "chevron-down",
179
+ "chevron-left",
180
+ "chevron-right",
181
+ "chevron-up",
182
+ "circle",
183
+ "clipboard",
184
+ "clock",
185
+ "credit-card",
186
+ "delete",
187
+ "delivered",
188
+ "delivery",
189
+ "disabled",
190
+ "discount",
191
+ "edit",
192
+ "email",
193
+ "empty",
194
+ "external",
195
+ "filter",
196
+ "geolocation",
197
+ "gift-card",
198
+ "globe",
199
+ "grid",
200
+ "image",
201
+ "info",
202
+ "info-filled",
203
+ "list-bulleted",
204
+ "location",
205
+ "lock",
206
+ "map",
207
+ "menu",
208
+ "menu-horizontal",
209
+ "menu-vertical",
210
+ "minus",
211
+ "mobile",
212
+ "note",
213
+ "order",
214
+ "organization",
215
+ "plus",
216
+ "profile",
217
+ "question-circle",
218
+ "question-circle-filled",
219
+ "reorder",
220
+ "reset",
221
+ "return",
222
+ "savings",
223
+ "search",
224
+ "settings",
225
+ "star",
226
+ "star-filled",
227
+ "star-half",
228
+ "store",
229
+ "truck",
230
+ "upload",
231
+ "x",
232
+ "x-circle",
233
+ "x-circle-filled"
234
+ ];
235
+ var IconName = freezeEnum(iconNames);
236
+ var iconSizes = ["base", "large", "large-100", "small", "small-100", "small-200"];
237
+ var IconSize = freezeEnum(iconSizes);
238
+ var iconTones = ["auto", "critical", "custom", "info", "neutral", "success", "warning"];
239
+ var IconTone = freezeEnum(iconTones);
240
+
155
241
  // src/schema/widgets/checkout-and-beyond/regex.ts
156
242
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
157
243
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
@@ -262,12 +348,44 @@ var CABLayoutSection = z2.object({
262
348
  width: z2.number().default(100)
263
349
  });
264
350
 
351
+ // src/schema/widgets/checkout-and-beyond/utils.ts
352
+ import { isPlainObject, mapValues } from "es-toolkit";
353
+ import { every, get, isArray, isString, map, size, toPairs } from "es-toolkit/compat";
354
+ var isHTML = (html = "") => {
355
+ if (typeof window !== "undefined") {
356
+ const parser = new DOMParser();
357
+ const doc = parser.parseFromString(html, "text/html");
358
+ const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
359
+ return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
360
+ }
361
+ return html.search(HTML_TAGS_REGEX) !== -1;
362
+ };
363
+ var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text }) => isHTML(text)));
364
+ var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject(input) && hasHTMLInDoc(input));
365
+ var NO_HTML = { message: "HTML is not supported" };
366
+
265
367
  // src/schema/widgets/checkout-and-beyond/banner.ts
266
368
  var CABBannerSection = z3.lazy(
267
369
  () => CABLayoutSection.omit({ sectionType: true }).extend({
268
370
  buttonField: z3.enum(buttonFields).optional(),
269
371
  color: z3.enum(bannerColors).default("info"),
270
372
  dismissible: z3.boolean().default(true),
373
+ /**
374
+ * The `s-banner` heading — localized content (a buyer can see a merchant-composed banner). Absent OR
375
+ * blank ⇒ no heading; the renderer emits no heading slot for `''` rather than an empty one.
376
+ *
377
+ * Merchant-composed buyer-facing plain text, so it rejects HTML at the boundary the same way
378
+ * `CABImageSection.altText` and `CABTextSection.content` do (the unchecked localized records elsewhere —
379
+ * `progressBar`'s gift labels, `reviews`' plurals — are converter/constant-sourced, not merchant input).
380
+ *
381
+ * The refinement is a last-resort ASSERTION, not the normalization boundary: `normalizeTextContent` only
382
+ * rewrites `sectionType: 'text'` nodes' `content`, so nothing strips markup or canonicalizes `{token}`
383
+ * here. A producer must strip upstream the way `composeOffer` does for `altText`. Get it wrong and
384
+ * `salvageSections` stubs the whole banner (children included) with a drift report — degraded, reported,
385
+ * not a 500. Nothing emits `heading` yet; the first converter that maps merchant banner copy into it
386
+ * either strips first or teaches `normalizeTextContent` to strip this field.
387
+ */
388
+ heading: z3.record(z3.string(), z3.string().refine(checkForHTML, NO_HTML)).optional(),
271
389
  sectionType: z3.literal(SectionType.banner).default(SectionType.banner)
272
390
  })
273
391
  );
@@ -280,24 +398,6 @@ import { z as z5 } from "zod";
280
398
  import { forEach, isString as isString2 } from "es-toolkit/compat";
281
399
  import { v7 as uuidv72 } from "uuid";
282
400
  import { z as z4 } from "zod";
283
-
284
- // src/schema/widgets/checkout-and-beyond/utils.ts
285
- import { isPlainObject, mapValues } from "es-toolkit";
286
- import { every, get, isArray, isString, map, size, toPairs } from "es-toolkit/compat";
287
- var isHTML = (html = "") => {
288
- if (typeof window !== "undefined") {
289
- const parser = new DOMParser();
290
- const doc = parser.parseFromString(html, "text/html");
291
- const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
292
- return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
293
- }
294
- return html.search(HTML_TAGS_REGEX) !== -1;
295
- };
296
- var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text }) => isHTML(text)));
297
- var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject(input) && hasHTMLInDoc(input));
298
- var NO_HTML = { message: "HTML is not supported" };
299
-
300
- // src/schema/widgets/checkout-and-beyond/text.ts
301
401
  var enumOrOmit = (...vals) => z4.string().transform((val) => vals.includes(val) ? val : void 0).optional();
302
402
  var TiptapText = z4.object({
303
403
  marks: z4.union([
@@ -412,6 +512,21 @@ var CABButtonSection = z5.object({
412
512
  sectionId: z5.uuid().default(() => uuidv73()),
413
513
  sections: z5.union([z5.array(CABButtonContent), z5.strictObject({}).transform(() => [])]).default(() => []),
414
514
  sectionType: z5.literal(SectionType.button).default(SectionType.button),
515
+ /**
516
+ * The smart-cart tier this button acts on (`addGift`/`removeGift`): scopes the click to one tier's gift. The
517
+ * client resolves the tier's gift (productId) from the progress-bar context by this `tierId` — the button node
518
+ * stays data, no bespoke per-tier wiring. Absent on non-gift buttons (offer add/remove, navigation, links).
519
+ *
520
+ * Deliberately NOT `CABTextTier` (the shape `CABTextSection.tier` uses): a gift button acts on a tier the bar
521
+ * already resolved, so it needs the id and nothing else. Reusing the text tier would make `threshold`
522
+ * required, leave `tierId` optional, and DEFAULT `tierType` to `'shipping'` — a schema-supplied wrong answer
523
+ * on a gift button. Unknown keys are stripped, so a client spreading a whole tier in still parses.
524
+ *
525
+ * `.min(1)` because an empty id IS the silently-id-less state this shape exists to prevent: the client's
526
+ * lookup finds no tier and the click resolves to nothing. Safe to bound — the field is new, so there are no
527
+ * stored payloads to grandfather, and the producer is client code, not a merchant form.
528
+ */
529
+ tier: z5.object({ tierId: z5.string().min(1) }).optional(),
415
530
  /** Fire-and-forget pixel POSTed when the button is clicked (e.g. a Monetize decline). */
416
531
  trackingUrl: z5.string().optional()
417
532
  });
@@ -785,8 +900,12 @@ var CABProgressTier = z14.object({
785
900
  }).optional(),
786
901
  /**
787
902
  * The step-row label at this tier's slot (onsite `getTierLabel`): the merchant custom label, else the
788
- * type default ('Free Shipping' / product title / '{n}% Discount'). Localized content; blank ⇒ no
789
- * label under the tier's icon. Plain text — the step row is a compact icon+label, not rich copy.
903
+ * type default — 'Free Shipping' or '{n}% Discount'. A PRODUCT tier gets NO converter label: its name is
904
+ * the gift PRODUCT title, which the converter can't supply (config carries only product IDs), so the
905
+ * client resolves it (authoritative Shopify title, React parity) with its own localized generic fallback.
906
+ * Localized content; absent OR blank ⇒ no label under the tier's icon (the converter's `toTierMeta` already
907
+ * turns a blank label into an absent key, and a client-authored blank means the same thing — the renderer
908
+ * emits no caption slot rather than an empty one). Plain text — a compact icon+label.
790
909
  */
791
910
  label: z14.record(z14.string(), z14.string()).optional(),
792
911
  /**
@@ -1047,12 +1166,39 @@ var CABSection = z18.lazy(
1047
1166
  );
1048
1167
 
1049
1168
  // src/schema/widgets/checkout-and-beyond/image.ts
1050
- var CABImageSection = z19.object({
1169
+ var CABImageSectionFields = z19.object({
1051
1170
  altText: z19.string().refine(checkForHTML, NO_HTML).default(""),
1052
- aspectRatio: z19.literal(1).nullable().default(null),
1171
+ /**
1172
+ * Image width÷height (e.g. `46` for the progress-bar fill); `null` renders at natural size. Any POSITIVE
1173
+ * ratio — never limited to 1, but never `0`/negative either: a renderer dividing by it would get `Infinity`,
1174
+ * and CSS `aspect-ratio` drops a negative value and silently reverts to natural size.
1175
+ */
1176
+ aspectRatio: z19.number().positive().nullable().default(null),
1053
1177
  border: z19.lazy(() => CABBorder),
1054
1178
  buttonField: z19.enum(buttonFields).optional(),
1055
- category: z19.enum(["gallery", "icons", "payment-methods", "secure-checkout"]).nullable().default(null),
1179
+ /**
1180
+ * `icon` renders `source` as a single `s-icon` by name (the uber-image path covers icons too), reading
1181
+ * `iconSize`/`iconTone`. The other values are a harmless informational holdover from the previous
1182
+ * implementation — they were never significant to rendering (`icons`, plural, is NOT deprecated, just one of
1183
+ * those inert legacy tags); `icon` is the first value that means anything.
1184
+ *
1185
+ * INVARIANT, encoded by the `.superRefine` below (a refined object IS a valid `z.discriminatedUnion` member
1186
+ * in zod 4 — see `CABTextSection`): `category: 'icon'` ⇒ `source` is an icon name or `''` (icon not yet
1187
+ * chosen); any other category ⇒ `source` is NOT a bare icon name, so it stays a URL/token/`''`.
1188
+ *
1189
+ * Encoded rather than left to the renderer because `source` is ONE union with no category gate, so widening
1190
+ * it with `iconNames` also widened what a STORED payload may contain: 14 of the names are plain English
1191
+ * words (`image`, `x`, `search`, `note`, `order`, `globe`, …), and a legacy `source: 'image'` used to fail
1192
+ * parse — which `salvageSections` turns into a null-rendering stub PLUS a drift report. Ungated it would
1193
+ * instead parse and reach the renderer as `<img src="image">`: a broken image, silently, where the old
1194
+ * behavior degraded gracefully and told us about it. The client-assembled half of the invariant was never
1195
+ * the risky half.
1196
+ */
1197
+ category: z19.enum(["gallery", "icon", "icons", "payment-methods", "secure-checkout"]).nullable().default(null),
1198
+ /** `icon`-mode only: the `s-icon` `size`; `null` ⇒ the component default (`base`). Ignored by image treatments. */
1199
+ iconSize: z19.enum(iconSizes).nullable().default(null),
1200
+ /** `icon`-mode only: the `s-icon` `tone`; `null` ⇒ the component default (`auto`). Ignored by image treatments. */
1201
+ iconTone: z19.enum(iconTones).nullable().default(null),
1056
1202
  name: z19.string().optional(),
1057
1203
  naturalHeight: z19.number().default(0),
1058
1204
  naturalWidth: z19.number().default(0),
@@ -1064,9 +1210,31 @@ var CABImageSection = z19.object({
1064
1210
  rule: CABRule.optional(),
1065
1211
  sectionId: z19.uuid().default(() => uuidv712()),
1066
1212
  sectionType: z19.literal(SectionType.image).default(SectionType.image),
1067
- source: z19.union([z19.url(), z19.literal(""), z19.string().regex(DYNAMIC_TOKEN_REGEX)]).default(""),
1213
+ /**
1214
+ * An image URL or dynamic token, an empty default, or — when `category: 'icon'` — an icon NAME. Icon mode
1215
+ * takes a name or `''` only, NOT a token: no renderer resolves a token to an icon today, so accepting one
1216
+ * would promise a capability that doesn't exist. Widen the refine's carve-out to `DYNAMIC_TOKEN_REGEX` when a
1217
+ * server-chosen tier icon actually needs it.
1218
+ */
1219
+ source: z19.union([z19.url(), z19.literal(""), z19.string().regex(DYNAMIC_TOKEN_REGEX), z19.enum(iconNames)]).default(""),
1068
1220
  width: z19.number().default(100)
1069
1221
  });
1222
+ var iconNameSet = new Set(iconNames);
1223
+ var isIconName = (source) => iconNameSet.has(source);
1224
+ var CABImageSection = CABImageSectionFields.superRefine(({ category, source }, ctx) => {
1225
+ if (category === "icon" && source !== "" && !isIconName(source))
1226
+ ctx.addIssue({
1227
+ code: "custom",
1228
+ message: "icon-mode source must be an icon name",
1229
+ path: ["source"]
1230
+ });
1231
+ if (category !== "icon" && isIconName(source))
1232
+ ctx.addIssue({
1233
+ code: "custom",
1234
+ message: "source must be a URL or dynamic token unless category is `icon`",
1235
+ path: ["source"]
1236
+ });
1237
+ });
1070
1238
 
1071
1239
  // src/transforms/contentBlockV1/stripContentBlockHtml.ts
1072
1240
  var HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
@@ -2979,7 +3147,7 @@ var tierLabel = (tier) => {
2979
3147
  if (type === "shipping") return "Free Shipping";
2980
3148
  if (type === "discount")
2981
3149
  return tier.discountType === "fixedAmount" ? "{{discountAmount}} Off" : `${tier.discountAmountFromTotal ?? 0}% Discount`;
2982
- return tier.products?.[0]?.title ?? tier.allProducts?.[0]?.title ?? "Free Product";
3150
+ return void 0;
2983
3151
  };
2984
3152
  var discountOf = (tier) => {
2985
3153
  if (tier.type !== "discount") return void 0;