@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
@@ -486,6 +486,7 @@ var borderWidths = ["base", "medium", "thick"];
486
486
  var BorderWidth = freezeEnum(borderWidths);
487
487
  var buttonActions = [
488
488
  "acceptOffer",
489
+ "addGift",
489
490
  "addToOrder",
490
491
  "declineAllOffers",
491
492
  "declineOffer",
@@ -495,6 +496,7 @@ var buttonActions = [
495
496
  "goToPrevPage",
496
497
  "goToURL",
497
498
  "removeFromOrder",
499
+ "removeGift",
498
500
  "selectVariant",
499
501
  "showPagination",
500
502
  "switchToSubscription",
@@ -624,6 +626,90 @@ var VariantSelector = freezeEnum(variantSelectors);
624
626
  var verticalAlignments = ["top", "middle", "bottom"];
625
627
  var VerticalAlignment = freezeEnum(verticalAlignments);
626
628
 
629
+ // src/schema/widgets/checkout-and-beyond/icons.ts
630
+ var iconNames = [
631
+ "alert-circle",
632
+ "alert-triangle",
633
+ "alert-triangle-filled",
634
+ "arrow-down",
635
+ "arrow-left",
636
+ "arrow-right",
637
+ "arrow-up",
638
+ "arrow-up-right",
639
+ "bag",
640
+ "bullet",
641
+ "calendar",
642
+ "camera",
643
+ "caret-down",
644
+ "cart",
645
+ "cash-dollar",
646
+ "categories",
647
+ "check",
648
+ "check-circle",
649
+ "check-circle-filled",
650
+ "chevron-down",
651
+ "chevron-left",
652
+ "chevron-right",
653
+ "chevron-up",
654
+ "circle",
655
+ "clipboard",
656
+ "clock",
657
+ "credit-card",
658
+ "delete",
659
+ "delivered",
660
+ "delivery",
661
+ "disabled",
662
+ "discount",
663
+ "edit",
664
+ "email",
665
+ "empty",
666
+ "external",
667
+ "filter",
668
+ "geolocation",
669
+ "gift-card",
670
+ "globe",
671
+ "grid",
672
+ "image",
673
+ "info",
674
+ "info-filled",
675
+ "list-bulleted",
676
+ "location",
677
+ "lock",
678
+ "map",
679
+ "menu",
680
+ "menu-horizontal",
681
+ "menu-vertical",
682
+ "minus",
683
+ "mobile",
684
+ "note",
685
+ "order",
686
+ "organization",
687
+ "plus",
688
+ "profile",
689
+ "question-circle",
690
+ "question-circle-filled",
691
+ "reorder",
692
+ "reset",
693
+ "return",
694
+ "savings",
695
+ "search",
696
+ "settings",
697
+ "star",
698
+ "star-filled",
699
+ "star-half",
700
+ "store",
701
+ "truck",
702
+ "upload",
703
+ "x",
704
+ "x-circle",
705
+ "x-circle-filled"
706
+ ];
707
+ var IconName = freezeEnum(iconNames);
708
+ var iconSizes = ["base", "large", "large-100", "small", "small-100", "small-200"];
709
+ var IconSize = freezeEnum(iconSizes);
710
+ var iconTones = ["auto", "critical", "custom", "info", "neutral", "success", "warning"];
711
+ var IconTone = freezeEnum(iconTones);
712
+
627
713
  // src/schema/widgets/checkout-and-beyond/regex.ts
628
714
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
629
715
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
@@ -734,12 +820,44 @@ var CABLayoutSection = z8.object({
734
820
  width: z8.number().default(100)
735
821
  });
736
822
 
823
+ // src/schema/widgets/checkout-and-beyond/utils.ts
824
+ import { isPlainObject, mapValues } from "es-toolkit";
825
+ import { every, get, isArray, isString, map, size, toPairs } from "es-toolkit/compat";
826
+ var isHTML = (html = "") => {
827
+ if (typeof window !== "undefined") {
828
+ const parser = new DOMParser();
829
+ const doc = parser.parseFromString(html, "text/html");
830
+ const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
831
+ return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
832
+ }
833
+ return html.search(HTML_TAGS_REGEX) !== -1;
834
+ };
835
+ var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text: text2 }) => isHTML(text2)));
836
+ var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject(input) && hasHTMLInDoc(input));
837
+ var NO_HTML = { message: "HTML is not supported" };
838
+
737
839
  // src/schema/widgets/checkout-and-beyond/banner.ts
738
840
  var CABBannerSection = z9.lazy(
739
841
  () => CABLayoutSection.omit({ sectionType: true }).extend({
740
842
  buttonField: z9.enum(buttonFields).optional(),
741
843
  color: z9.enum(bannerColors).default("info"),
742
844
  dismissible: z9.boolean().default(true),
845
+ /**
846
+ * The `s-banner` heading — localized content (a buyer can see a merchant-composed banner). Absent OR
847
+ * blank ⇒ no heading; the renderer emits no heading slot for `''` rather than an empty one.
848
+ *
849
+ * Merchant-composed buyer-facing plain text, so it rejects HTML at the boundary the same way
850
+ * `CABImageSection.altText` and `CABTextSection.content` do (the unchecked localized records elsewhere —
851
+ * `progressBar`'s gift labels, `reviews`' plurals — are converter/constant-sourced, not merchant input).
852
+ *
853
+ * The refinement is a last-resort ASSERTION, not the normalization boundary: `normalizeTextContent` only
854
+ * rewrites `sectionType: 'text'` nodes' `content`, so nothing strips markup or canonicalizes `{token}`
855
+ * here. A producer must strip upstream the way `composeOffer` does for `altText`. Get it wrong and
856
+ * `salvageSections` stubs the whole banner (children included) with a drift report — degraded, reported,
857
+ * not a 500. Nothing emits `heading` yet; the first converter that maps merchant banner copy into it
858
+ * either strips first or teaches `normalizeTextContent` to strip this field.
859
+ */
860
+ heading: z9.record(z9.string(), z9.string().refine(checkForHTML, NO_HTML)).optional(),
743
861
  sectionType: z9.literal(SectionType.banner).default(SectionType.banner)
744
862
  })
745
863
  );
@@ -752,24 +870,6 @@ import { z as z11 } from "zod";
752
870
  import { forEach, isString as isString2 } from "es-toolkit/compat";
753
871
  import { v7 as uuidv72 } from "uuid";
754
872
  import { z as z10 } from "zod";
755
-
756
- // src/schema/widgets/checkout-and-beyond/utils.ts
757
- import { isPlainObject, mapValues } from "es-toolkit";
758
- import { every, get, isArray, isString, map, size, toPairs } from "es-toolkit/compat";
759
- var isHTML = (html = "") => {
760
- if (typeof window !== "undefined") {
761
- const parser = new DOMParser();
762
- const doc = parser.parseFromString(html, "text/html");
763
- const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
764
- return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
765
- }
766
- return html.search(HTML_TAGS_REGEX) !== -1;
767
- };
768
- var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text: text2 }) => isHTML(text2)));
769
- var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject(input) && hasHTMLInDoc(input));
770
- var NO_HTML = { message: "HTML is not supported" };
771
-
772
- // src/schema/widgets/checkout-and-beyond/text.ts
773
873
  var enumOrOmit = (...vals) => z10.string().transform((val) => vals.includes(val) ? val : void 0).optional();
774
874
  var TiptapText = z10.object({
775
875
  marks: z10.union([
@@ -884,6 +984,21 @@ var CABButtonSection = z11.object({
884
984
  sectionId: z11.uuid().default(() => uuidv73()),
885
985
  sections: z11.union([z11.array(CABButtonContent), z11.strictObject({}).transform(() => [])]).default(() => []),
886
986
  sectionType: z11.literal(SectionType.button).default(SectionType.button),
987
+ /**
988
+ * The smart-cart tier this button acts on (`addGift`/`removeGift`): scopes the click to one tier's gift. The
989
+ * client resolves the tier's gift (productId) from the progress-bar context by this `tierId` — the button node
990
+ * stays data, no bespoke per-tier wiring. Absent on non-gift buttons (offer add/remove, navigation, links).
991
+ *
992
+ * Deliberately NOT `CABTextTier` (the shape `CABTextSection.tier` uses): a gift button acts on a tier the bar
993
+ * already resolved, so it needs the id and nothing else. Reusing the text tier would make `threshold`
994
+ * required, leave `tierId` optional, and DEFAULT `tierType` to `'shipping'` — a schema-supplied wrong answer
995
+ * on a gift button. Unknown keys are stripped, so a client spreading a whole tier in still parses.
996
+ *
997
+ * `.min(1)` because an empty id IS the silently-id-less state this shape exists to prevent: the client's
998
+ * lookup finds no tier and the click resolves to nothing. Safe to bound — the field is new, so there are no
999
+ * stored payloads to grandfather, and the producer is client code, not a merchant form.
1000
+ */
1001
+ tier: z11.object({ tierId: z11.string().min(1) }).optional(),
887
1002
  /** Fire-and-forget pixel POSTed when the button is clicked (e.g. a Monetize decline). */
888
1003
  trackingUrl: z11.string().optional()
889
1004
  });
@@ -1257,8 +1372,12 @@ var CABProgressTier = z20.object({
1257
1372
  }).optional(),
1258
1373
  /**
1259
1374
  * The step-row label at this tier's slot (onsite `getTierLabel`): the merchant custom label, else the
1260
- * type default ('Free Shipping' / product title / '{n}% Discount'). Localized content; blank ⇒ no
1261
- * label under the tier's icon. Plain text — the step row is a compact icon+label, not rich copy.
1375
+ * type default — 'Free Shipping' or '{n}% Discount'. A PRODUCT tier gets NO converter label: its name is
1376
+ * the gift PRODUCT title, which the converter can't supply (config carries only product IDs), so the
1377
+ * client resolves it (authoritative Shopify title, React parity) with its own localized generic fallback.
1378
+ * Localized content; absent OR blank ⇒ no label under the tier's icon (the converter's `toTierMeta` already
1379
+ * turns a blank label into an absent key, and a client-authored blank means the same thing — the renderer
1380
+ * emits no caption slot rather than an empty one). Plain text — a compact icon+label.
1262
1381
  */
1263
1382
  label: z20.record(z20.string(), z20.string()).optional(),
1264
1383
  /**
@@ -1519,12 +1638,39 @@ var CABSection = z24.lazy(
1519
1638
  );
1520
1639
 
1521
1640
  // src/schema/widgets/checkout-and-beyond/image.ts
1522
- var CABImageSection = z25.object({
1641
+ var CABImageSectionFields = z25.object({
1523
1642
  altText: z25.string().refine(checkForHTML, NO_HTML).default(""),
1524
- aspectRatio: z25.literal(1).nullable().default(null),
1643
+ /**
1644
+ * Image width÷height (e.g. `46` for the progress-bar fill); `null` renders at natural size. Any POSITIVE
1645
+ * ratio — never limited to 1, but never `0`/negative either: a renderer dividing by it would get `Infinity`,
1646
+ * and CSS `aspect-ratio` drops a negative value and silently reverts to natural size.
1647
+ */
1648
+ aspectRatio: z25.number().positive().nullable().default(null),
1525
1649
  border: z25.lazy(() => CABBorder),
1526
1650
  buttonField: z25.enum(buttonFields).optional(),
1527
- category: z25.enum(["gallery", "icons", "payment-methods", "secure-checkout"]).nullable().default(null),
1651
+ /**
1652
+ * `icon` renders `source` as a single `s-icon` by name (the uber-image path covers icons too), reading
1653
+ * `iconSize`/`iconTone`. The other values are a harmless informational holdover from the previous
1654
+ * implementation — they were never significant to rendering (`icons`, plural, is NOT deprecated, just one of
1655
+ * those inert legacy tags); `icon` is the first value that means anything.
1656
+ *
1657
+ * INVARIANT, encoded by the `.superRefine` below (a refined object IS a valid `z.discriminatedUnion` member
1658
+ * in zod 4 — see `CABTextSection`): `category: 'icon'` ⇒ `source` is an icon name or `''` (icon not yet
1659
+ * chosen); any other category ⇒ `source` is NOT a bare icon name, so it stays a URL/token/`''`.
1660
+ *
1661
+ * Encoded rather than left to the renderer because `source` is ONE union with no category gate, so widening
1662
+ * it with `iconNames` also widened what a STORED payload may contain: 14 of the names are plain English
1663
+ * words (`image`, `x`, `search`, `note`, `order`, `globe`, …), and a legacy `source: 'image'` used to fail
1664
+ * parse — which `salvageSections` turns into a null-rendering stub PLUS a drift report. Ungated it would
1665
+ * instead parse and reach the renderer as `<img src="image">`: a broken image, silently, where the old
1666
+ * behavior degraded gracefully and told us about it. The client-assembled half of the invariant was never
1667
+ * the risky half.
1668
+ */
1669
+ category: z25.enum(["gallery", "icon", "icons", "payment-methods", "secure-checkout"]).nullable().default(null),
1670
+ /** `icon`-mode only: the `s-icon` `size`; `null` ⇒ the component default (`base`). Ignored by image treatments. */
1671
+ iconSize: z25.enum(iconSizes).nullable().default(null),
1672
+ /** `icon`-mode only: the `s-icon` `tone`; `null` ⇒ the component default (`auto`). Ignored by image treatments. */
1673
+ iconTone: z25.enum(iconTones).nullable().default(null),
1528
1674
  name: z25.string().optional(),
1529
1675
  naturalHeight: z25.number().default(0),
1530
1676
  naturalWidth: z25.number().default(0),
@@ -1536,9 +1682,31 @@ var CABImageSection = z25.object({
1536
1682
  rule: CABRule.optional(),
1537
1683
  sectionId: z25.uuid().default(() => uuidv712()),
1538
1684
  sectionType: z25.literal(SectionType.image).default(SectionType.image),
1539
- source: z25.union([z25.url(), z25.literal(""), z25.string().regex(DYNAMIC_TOKEN_REGEX)]).default(""),
1685
+ /**
1686
+ * An image URL or dynamic token, an empty default, or — when `category: 'icon'` — an icon NAME. Icon mode
1687
+ * takes a name or `''` only, NOT a token: no renderer resolves a token to an icon today, so accepting one
1688
+ * would promise a capability that doesn't exist. Widen the refine's carve-out to `DYNAMIC_TOKEN_REGEX` when a
1689
+ * server-chosen tier icon actually needs it.
1690
+ */
1691
+ source: z25.union([z25.url(), z25.literal(""), z25.string().regex(DYNAMIC_TOKEN_REGEX), z25.enum(iconNames)]).default(""),
1540
1692
  width: z25.number().default(100)
1541
1693
  });
1694
+ var iconNameSet = new Set(iconNames);
1695
+ var isIconName = (source) => iconNameSet.has(source);
1696
+ var CABImageSection = CABImageSectionFields.superRefine(({ category, source }, ctx) => {
1697
+ if (category === "icon" && source !== "" && !isIconName(source))
1698
+ ctx.addIssue({
1699
+ code: "custom",
1700
+ message: "icon-mode source must be an icon name",
1701
+ path: ["source"]
1702
+ });
1703
+ if (category !== "icon" && isIconName(source))
1704
+ ctx.addIssue({
1705
+ code: "custom",
1706
+ message: "source must be a URL or dynamic token unless category is `icon`",
1707
+ path: ["source"]
1708
+ });
1709
+ });
1542
1710
 
1543
1711
  // src/transforms/contentBlockV1/stripContentBlockHtml.ts
1544
1712
  var HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
@@ -3016,7 +3184,7 @@ var tierLabel = (tier) => {
3016
3184
  if (type === "shipping") return "Free Shipping";
3017
3185
  if (type === "discount")
3018
3186
  return tier.discountType === "fixedAmount" ? "{{discountAmount}} Off" : `${tier.discountAmountFromTotal ?? 0}% Discount`;
3019
- return tier.products?.[0]?.title ?? tier.allProducts?.[0]?.title ?? "Free Product";
3187
+ return void 0;
3020
3188
  };
3021
3189
  var discountOf = (tier) => {
3022
3190
  if (tier.type !== "discount") return void 0;