@rebuy/rebuy 3.23.0 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,7 @@ Node `^22.14.0 || ^24.10.0 || ^26.0.0` for development; the published artifacts
17
17
  | Subpath | Contents |
18
18
  | --- | --- |
19
19
  | `@rebuy/rebuy` | Root barrel: schemas, client helpers, utilities |
20
+ | `@rebuy/rebuy/cab` | zod-free CAB renderer helpers shared by every renderer (`contentText`, `tiptapPlainText`, `tiptapRuns`, `asLayout`, `idFromGid`, `toCents`, `decoratedOrderId`) |
20
21
  | `@rebuy/rebuy/client` | `RebuyClient` API client |
21
22
  | `@rebuy/rebuy/gwp` | Gift-with-purchase validation helpers |
22
23
  | `@rebuy/rebuy/pricing` | Variant pricing/discount forecast (`priceVariant`, `currencyDecimals`) |
@@ -0,0 +1,12 @@
1
+ import type { CABLayoutSection } from '../schema/widgets/checkout-and-beyond/layout';
2
+ /**
3
+ * Render a layout-derived section (cart-line, offers, carousel, banner) as a plain layout; the variable
4
+ * fields are dropped — the scope was already mounted and the visibility binding already evaluated at
5
+ * dispatch, and `mutators` already has its host (a re-dispatch must not double any of them).
6
+ *
7
+ * The literal (checked against `SectionType`) rather than the runtime enum: `common.ts` is not tree-shakeable,
8
+ * so importing one constant would drag every CAB enum into each renderer that bundles this subpath.
9
+ */
10
+ export declare const asLayout: <T extends {
11
+ sectionType: string;
12
+ }>(section: T) => CABLayoutSection;
@@ -0,0 +1,8 @@
1
+ import { type Variables } from '../cab/interpolateAndSanitize';
2
+ import type { CABTextSection } from '../schema/widgets/checkout-and-beyond/text';
3
+ /**
4
+ * A text section's buyer-language copy as ONE interpolated, sanitized plain string — rich content flattens
5
+ * to bare text (marks and structure drop). The shared reading behind button label slots and the `content`
6
+ * mutator trigger, so "what does this copy say" has a single answer.
7
+ */
8
+ export declare const contentText: (content: CABTextSection["content"], language?: string, variables?: Variables) => string;
@@ -0,0 +1,2 @@
1
+ /** Numeric part of the decorated order id (`gid://shopify/Order(Identity)/123` → `123`) → ads `transaction_id`. */
2
+ export declare const decoratedOrderId: (orderId: string | undefined) => string | undefined;
@@ -0,0 +1,2 @@
1
+ /** `gid://shopify/X/123` → `123` — the engine speaks numeric ids, Shopify's APIs speak gids. */
2
+ export declare const idFromGid: (gid: string) => string;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/cab/index.ts
21
+ var cab_exports = {};
22
+ __export(cab_exports, {
23
+ asLayout: () => asLayout,
24
+ contentText: () => contentText,
25
+ decoratedOrderId: () => decoratedOrderId,
26
+ idFromGid: () => idFromGid,
27
+ interpolateAndSanitize: () => interpolateAndSanitize,
28
+ isTiptapDoc: () => isTiptapDoc,
29
+ selectLanguage: () => selectLanguage,
30
+ tiptapPlainText: () => tiptapPlainText,
31
+ tiptapRuns: () => tiptapRuns,
32
+ toCents: () => toCents
33
+ });
34
+ module.exports = __toCommonJS(cab_exports);
35
+
36
+ // src/cab/asLayout.ts
37
+ var asLayout = (section) => ({
38
+ ...section,
39
+ mutators: void 0,
40
+ sectionType: "layout",
41
+ variables: void 0,
42
+ visibility: void 0
43
+ });
44
+
45
+ // src/schema/widgets/checkout-and-beyond/regex.ts
46
+ var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
47
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
48
+ var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
49
+
50
+ // src/cab/interpolateAndSanitize.ts
51
+ var TOKEN_REGEX = /{{1,2}\s*([^{}\s]+)\s*}{1,2}/g;
52
+ var toCamelCase = (key) => key.replace(/_([a-z0-9])/g, (_, char) => char.toUpperCase());
53
+ var resolveKey = (key, variables) => {
54
+ if (Object.hasOwn(variables, key)) return key;
55
+ const camel = toCamelCase(key);
56
+ if (Object.hasOwn(variables, camel)) return camel;
57
+ const lower = camel.toLowerCase();
58
+ return Object.keys(variables).find((name) => name.toLowerCase() === lower);
59
+ };
60
+ var interpolateAndSanitize = (input, variables) => (input ?? "").replace(TOKEN_REGEX, (match, key) => {
61
+ if (!variables) return match;
62
+ const resolved = resolveKey(key, variables);
63
+ return resolved === void 0 ? match : String(variables[resolved] ?? "");
64
+ }).replace(HTML_TAGS_REGEX, "");
65
+
66
+ // src/cab/selectLanguage.ts
67
+ var FALLBACK_LANGUAGE = "en";
68
+ var selectLanguage = (content, language) => {
69
+ if (!content) return void 0;
70
+ if (language && Object.hasOwn(content, language)) return content[language];
71
+ if (Object.hasOwn(content, FALLBACK_LANGUAGE)) return content[FALLBACK_LANGUAGE];
72
+ return Object.values(content)[0];
73
+ };
74
+
75
+ // src/cab/listOf.ts
76
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
77
+
78
+ // src/cab/tiptapPlain.ts
79
+ var isTiptapDoc = (value) => !!value && typeof value === "object" && value.type === "doc";
80
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
81
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
82
+ ).join(" ").trim();
83
+
84
+ // src/cab/contentText.ts
85
+ var contentText = (content, language, variables) => {
86
+ const selected = selectLanguage(content, language);
87
+ const text = typeof selected === "string" ? selected : isTiptapDoc(selected) ? tiptapPlainText(selected) : "";
88
+ return interpolateAndSanitize(text, variables);
89
+ };
90
+
91
+ // src/cab/decoratedOrderId.ts
92
+ var decoratedOrderId = (orderId) => orderId?.match(/\/Order(?:Identity)?\/(\d+)$/)?.[1] ?? orderId;
93
+
94
+ // src/cab/gid.ts
95
+ var idFromGid = (gid) => gid.slice(gid.lastIndexOf("/") + 1);
96
+
97
+ // src/transforms/htmlToTiptap/isValidHref.ts
98
+ var RELATIVE_BASE = "https://x.invalid";
99
+ var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
100
+ var isValidHref = (href) => {
101
+ if (!href) return false;
102
+ if (href === "#") return true;
103
+ if (DYNAMIC_TOKEN_REGEX.test(href)) return true;
104
+ if (href.startsWith("/")) {
105
+ try {
106
+ return new URL(href, RELATIVE_BASE).origin === RELATIVE_BASE;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+ try {
112
+ const url = new URL(href);
113
+ return ALLOWED_PROTOCOLS.has(url.protocol);
114
+ } catch {
115
+ return false;
116
+ }
117
+ };
118
+
119
+ // src/transforms/htmlToTiptap/linkSafety.ts
120
+ var DEFAULT_LINK_TARGET = "_blank";
121
+ var DEFAULT_LINK_REL = "noopener noreferrer nofollow";
122
+ var ensureSafeRel = (rel, target) => {
123
+ if (target !== DEFAULT_LINK_TARGET) return rel;
124
+ const sourceTokens = (rel ?? "").split(/\s+/).filter(Boolean);
125
+ if (sourceTokens.length === 0) return DEFAULT_LINK_REL;
126
+ const tokens = new Set(sourceTokens.filter((token) => token.toLowerCase() !== "opener"));
127
+ tokens.add("noopener");
128
+ tokens.add("noreferrer");
129
+ return Array.from(tokens).join(" ");
130
+ };
131
+
132
+ // src/cab/tiptapRuns.ts
133
+ var HEADING = "heading";
134
+ var isHeadingMark = (mark) => mark.type === "textStyle" && mark.attrs?.fontSize === HEADING;
135
+ var isRenderableHref = (href) => isValidHref(href) && !DYNAMIC_TOKEN_ANYWHERE_REGEX.test(href);
136
+ var toRun = (node, variables) => {
137
+ const run = {
138
+ bold: false,
139
+ italic: false,
140
+ strike: false,
141
+ text: interpolateAndSanitize(node.text ?? "", variables)
142
+ };
143
+ let link;
144
+ let textStyle;
145
+ for (const mark of listOf(node.marks)) {
146
+ if (mark.type === "bold") run.bold = true;
147
+ else if (mark.type === "italic") run.italic = true;
148
+ else if (mark.type === "strike") run.strike = true;
149
+ else if (mark.type === "link") link = mark;
150
+ else if (mark.type === "textStyle") textStyle = mark;
151
+ }
152
+ const color = textStyle?.attrs?.color;
153
+ const fontSize = textStyle?.attrs?.fontSize;
154
+ if (color) run.color = color;
155
+ if (fontSize && fontSize !== HEADING) run.fontSize = fontSize;
156
+ if (link) {
157
+ const href = interpolateAndSanitize(link.attrs?.href ?? "", variables);
158
+ if (isRenderableHref(href)) {
159
+ const target = link.attrs?.target ?? DEFAULT_LINK_TARGET;
160
+ const rel = ensureSafeRel(link.attrs?.rel, target);
161
+ run.href = href;
162
+ if (rel) run.rel = rel;
163
+ if (target === DEFAULT_LINK_TARGET) run.target = DEFAULT_LINK_TARGET;
164
+ }
165
+ }
166
+ return run;
167
+ };
168
+ var tiptapRuns = (paragraph, variables) => {
169
+ const content = listOf(paragraph?.content);
170
+ return {
171
+ heading: content.some((node) => listOf(node.marks).some(isHeadingMark)),
172
+ runs: content.map((node) => toRun(node, variables))
173
+ };
174
+ };
175
+
176
+ // src/cab/toCents.ts
177
+ var toCents = (amount) => Math.round(Number(amount ?? 0) * 100);
@@ -0,0 +1,9 @@
1
+ export * from '../cab/asLayout';
2
+ export * from '../cab/contentText';
3
+ export * from '../cab/decoratedOrderId';
4
+ export * from '../cab/gid';
5
+ export * from '../cab/interpolateAndSanitize';
6
+ export * from '../cab/selectLanguage';
7
+ export * from '../cab/tiptapPlain';
8
+ export * from '../cab/tiptapRuns';
9
+ export * from '../cab/toCents';
@@ -0,0 +1,154 @@
1
+ // src/cab/asLayout.ts
2
+ var asLayout = (section) => ({
3
+ ...section,
4
+ mutators: void 0,
5
+ sectionType: "layout",
6
+ variables: void 0,
7
+ visibility: void 0
8
+ });
9
+
10
+ // src/schema/widgets/checkout-and-beyond/regex.ts
11
+ var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
12
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
13
+ var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
14
+
15
+ // src/cab/interpolateAndSanitize.ts
16
+ var TOKEN_REGEX = /{{1,2}\s*([^{}\s]+)\s*}{1,2}/g;
17
+ var toCamelCase = (key) => key.replace(/_([a-z0-9])/g, (_, char) => char.toUpperCase());
18
+ var resolveKey = (key, variables) => {
19
+ if (Object.hasOwn(variables, key)) return key;
20
+ const camel = toCamelCase(key);
21
+ if (Object.hasOwn(variables, camel)) return camel;
22
+ const lower = camel.toLowerCase();
23
+ return Object.keys(variables).find((name) => name.toLowerCase() === lower);
24
+ };
25
+ var interpolateAndSanitize = (input, variables) => (input ?? "").replace(TOKEN_REGEX, (match, key) => {
26
+ if (!variables) return match;
27
+ const resolved = resolveKey(key, variables);
28
+ return resolved === void 0 ? match : String(variables[resolved] ?? "");
29
+ }).replace(HTML_TAGS_REGEX, "");
30
+
31
+ // src/cab/selectLanguage.ts
32
+ var FALLBACK_LANGUAGE = "en";
33
+ var selectLanguage = (content, language) => {
34
+ if (!content) return void 0;
35
+ if (language && Object.hasOwn(content, language)) return content[language];
36
+ if (Object.hasOwn(content, FALLBACK_LANGUAGE)) return content[FALLBACK_LANGUAGE];
37
+ return Object.values(content)[0];
38
+ };
39
+
40
+ // src/cab/listOf.ts
41
+ var listOf = (value) => Array.isArray(value) ? value.filter((item) => !!item) : [];
42
+
43
+ // src/cab/tiptapPlain.ts
44
+ var isTiptapDoc = (value) => !!value && typeof value === "object" && value.type === "doc";
45
+ var tiptapPlainText = (doc) => listOf(doc?.content).map(
46
+ (paragraph) => listOf(paragraph.content).map((node) => node.text ?? "").join("")
47
+ ).join(" ").trim();
48
+
49
+ // src/cab/contentText.ts
50
+ var contentText = (content, language, variables) => {
51
+ const selected = selectLanguage(content, language);
52
+ const text = typeof selected === "string" ? selected : isTiptapDoc(selected) ? tiptapPlainText(selected) : "";
53
+ return interpolateAndSanitize(text, variables);
54
+ };
55
+
56
+ // src/cab/decoratedOrderId.ts
57
+ var decoratedOrderId = (orderId) => orderId?.match(/\/Order(?:Identity)?\/(\d+)$/)?.[1] ?? orderId;
58
+
59
+ // src/cab/gid.ts
60
+ var idFromGid = (gid) => gid.slice(gid.lastIndexOf("/") + 1);
61
+
62
+ // src/transforms/htmlToTiptap/isValidHref.ts
63
+ var RELATIVE_BASE = "https://x.invalid";
64
+ var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
65
+ var isValidHref = (href) => {
66
+ if (!href) return false;
67
+ if (href === "#") return true;
68
+ if (DYNAMIC_TOKEN_REGEX.test(href)) return true;
69
+ if (href.startsWith("/")) {
70
+ try {
71
+ return new URL(href, RELATIVE_BASE).origin === RELATIVE_BASE;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ try {
77
+ const url = new URL(href);
78
+ return ALLOWED_PROTOCOLS.has(url.protocol);
79
+ } catch {
80
+ return false;
81
+ }
82
+ };
83
+
84
+ // src/transforms/htmlToTiptap/linkSafety.ts
85
+ var DEFAULT_LINK_TARGET = "_blank";
86
+ var DEFAULT_LINK_REL = "noopener noreferrer nofollow";
87
+ var ensureSafeRel = (rel, target) => {
88
+ if (target !== DEFAULT_LINK_TARGET) return rel;
89
+ const sourceTokens = (rel ?? "").split(/\s+/).filter(Boolean);
90
+ if (sourceTokens.length === 0) return DEFAULT_LINK_REL;
91
+ const tokens = new Set(sourceTokens.filter((token) => token.toLowerCase() !== "opener"));
92
+ tokens.add("noopener");
93
+ tokens.add("noreferrer");
94
+ return Array.from(tokens).join(" ");
95
+ };
96
+
97
+ // src/cab/tiptapRuns.ts
98
+ var HEADING = "heading";
99
+ var isHeadingMark = (mark) => mark.type === "textStyle" && mark.attrs?.fontSize === HEADING;
100
+ var isRenderableHref = (href) => isValidHref(href) && !DYNAMIC_TOKEN_ANYWHERE_REGEX.test(href);
101
+ var toRun = (node, variables) => {
102
+ const run = {
103
+ bold: false,
104
+ italic: false,
105
+ strike: false,
106
+ text: interpolateAndSanitize(node.text ?? "", variables)
107
+ };
108
+ let link;
109
+ let textStyle;
110
+ for (const mark of listOf(node.marks)) {
111
+ if (mark.type === "bold") run.bold = true;
112
+ else if (mark.type === "italic") run.italic = true;
113
+ else if (mark.type === "strike") run.strike = true;
114
+ else if (mark.type === "link") link = mark;
115
+ else if (mark.type === "textStyle") textStyle = mark;
116
+ }
117
+ const color = textStyle?.attrs?.color;
118
+ const fontSize = textStyle?.attrs?.fontSize;
119
+ if (color) run.color = color;
120
+ if (fontSize && fontSize !== HEADING) run.fontSize = fontSize;
121
+ if (link) {
122
+ const href = interpolateAndSanitize(link.attrs?.href ?? "", variables);
123
+ if (isRenderableHref(href)) {
124
+ const target = link.attrs?.target ?? DEFAULT_LINK_TARGET;
125
+ const rel = ensureSafeRel(link.attrs?.rel, target);
126
+ run.href = href;
127
+ if (rel) run.rel = rel;
128
+ if (target === DEFAULT_LINK_TARGET) run.target = DEFAULT_LINK_TARGET;
129
+ }
130
+ }
131
+ return run;
132
+ };
133
+ var tiptapRuns = (paragraph, variables) => {
134
+ const content = listOf(paragraph?.content);
135
+ return {
136
+ heading: content.some((node) => listOf(node.marks).some(isHeadingMark)),
137
+ runs: content.map((node) => toRun(node, variables))
138
+ };
139
+ };
140
+
141
+ // src/cab/toCents.ts
142
+ var toCents = (amount) => Math.round(Number(amount ?? 0) * 100);
143
+ export {
144
+ asLayout,
145
+ contentText,
146
+ decoratedOrderId,
147
+ idFromGid,
148
+ interpolateAndSanitize,
149
+ isTiptapDoc,
150
+ selectLanguage,
151
+ tiptapPlainText,
152
+ tiptapRuns,
153
+ toCents
154
+ };
@@ -0,0 +1,8 @@
1
+ export type Variables = Record<string, string | number | null | undefined>;
2
+ /**
3
+ * Interpolate tokens then strip HTML (with the same tag-shaped regex the schema validates against, so the
4
+ * client never mangles copy the server accepts) — defense-in-depth, since token values can carry HTML from
5
+ * cart/merchant settings. Unknown tokens are left intact so missing bindings surface during dev (and so
6
+ * incidental single-brace text that names no variable is never touched).
7
+ */
8
+ export declare const interpolateAndSanitize: (input: string, variables?: Variables) => string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Raw-payload tolerance for the list-shaped Tiptap fields. `TiptapDocument.content`,
3
+ * `TiptapParagraph.content` and `TiptapText.marks` are each
4
+ * `z.union([z.array(…), z.strictObject({}).transform(() => [])])` — that second arm exists because stored
5
+ * payloads serialize an empty list as `{}` — and any of them can carry `null` holes. The CAB renderer
6
+ * helpers read UNPARSED payloads, so none of those shapes may throw; a malformed list degrades to empty
7
+ * rather than killing the section around it.
8
+ */
9
+ export declare const listOf: <T>(value: unknown) => T[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Fallback chain: requested language, then 'en', then first available value.
3
+ *
4
+ * `Object.hasOwn`, never `in` — `in` would resolve an inherited `Object.prototype` key (`constructor`,
5
+ * `toString`) as if it were a translation. Far less reachable than in `interpolateAndSanitize`, since a
6
+ * language code is not merchant-authored, but the two must agree on what counts as present.
7
+ */
8
+ export declare const selectLanguage: <T>(content: Record<string, T> | undefined | null, language: string | undefined) => T | undefined;
@@ -0,0 +1,10 @@
1
+ import type { TiptapDocument } from '../schema/widgets/checkout-and-beyond/text';
2
+ /** Locale-resolved text content is either a plain string or a Tiptap document. */
3
+ export declare const isTiptapDoc: (value: unknown) => value is TiptapDocument;
4
+ /**
5
+ * The document's bare text — nodes concatenated, paragraphs space-joined; marks and structure drop.
6
+ *
7
+ * Every level goes through `listOf`, including the doc itself, because this reads raw payloads: the `{}`
8
+ * empty-list arm and `null` holes are both shapes the schema accepts, at any depth.
9
+ */
10
+ export declare const tiptapPlainText: (doc: TiptapDocument) => string;
@@ -0,0 +1,40 @@
1
+ import { type Variables } from '../cab/interpolateAndSanitize';
2
+ import type { InlineTextSizeName, TextColorName } from '../schema/widgets/checkout-and-beyond/common';
3
+ import type { TiptapParagraph } from '../schema/widgets/checkout-and-beyond/text';
4
+ /**
5
+ * One text run of a paragraph with its marks resolved to facts. Tokens (`color`, `fontSize`) are the
6
+ * payload's — mapping them to px/hex/tone is each renderer's. `bold` is reported even inside a heading;
7
+ * whether the heading element is already bold is the renderer's to know.
8
+ *
9
+ * A run carrying `href` is safe to hand to an anchor as-is: the scheme is allowlisted and, for
10
+ * `target: '_blank'`, `rel` is guaranteed to contain `noopener noreferrer`. Render BOTH — dropping `rel`
11
+ * re-opens reverse tabnabbing on merchant-authored links, which is the per-renderer knowledge this
12
+ * subpath exists to delete. A link whose href fails the allowlist yields a run with no `href`/`rel`/
13
+ * `target` at all, so it renders as plain text rather than as a live unsafe link.
14
+ */
15
+ export type TiptapRun = {
16
+ bold: boolean;
17
+ color?: TextColorName;
18
+ fontSize?: InlineTextSizeName;
19
+ href?: string;
20
+ italic: boolean;
21
+ rel?: string;
22
+ strike: boolean;
23
+ target?: '_blank';
24
+ text: string;
25
+ };
26
+ /** `heading` promotes the WHOLE paragraph (a block signal, never an inline size — see `textSizeNames`). */
27
+ export type TiptapParagraphRuns = {
28
+ heading: boolean;
29
+ runs: TiptapRun[];
30
+ };
31
+ /**
32
+ * A Tiptap paragraph as ordered styled runs plus its heading promotion — the one mark reading every
33
+ * renderer shares.
34
+ *
35
+ * Takes a payload in EITHER shape: schema-parsed, or raw off the wire. Malformed lists degrade to empty
36
+ * rather than throwing — including a nullish paragraph, so a renderer can map this straight over a raw
37
+ * `doc.content` — and the two link fields the schema defaults (`target`, `rel`) are resolved to the same
38
+ * effective values a parse would have produced, so a run never depends on whether the caller parsed.
39
+ */
40
+ export declare const tiptapRuns: (paragraph: TiptapParagraph, variables?: Variables) => TiptapParagraphRuns;
@@ -0,0 +1,2 @@
1
+ /** Presentment amount → integer cents (engine money unit); matches the cart-context subtotal conversion. */
2
+ export declare const toCents: (amount: string | number | undefined) => number;
package/dist/index.cjs CHANGED
@@ -48,6 +48,7 @@ __export(src_exports, {
48
48
  CABImageSectionFields: () => CABImageSectionFields,
49
49
  CABIntegrations: () => CABIntegrations,
50
50
  CABLayoutSection: () => CABLayoutSection,
51
+ CABMonetizeAds: () => CABMonetizeAds,
51
52
  CABMonetizeSection: () => CABMonetizeSection,
52
53
  CABNumberVariable: () => CABNumberVariable,
53
54
  CABOffersSection: () => CABOffersSection,
@@ -90,6 +91,7 @@ __export(src_exports, {
90
91
  ContentOperator: () => ContentOperator,
91
92
  DEFAULT_ENDPOINTS: () => DEFAULT_ENDPOINTS,
92
93
  DESCRIPTION_LABELS: () => DESCRIPTION_LABELS,
94
+ DYNAMIC_TOKEN_ANYWHERE_REGEX: () => DYNAMIC_TOKEN_ANYWHERE_REGEX,
93
95
  DYNAMIC_TOKEN_REGEX: () => DYNAMIC_TOKEN_REGEX,
94
96
  Direction: () => Direction,
95
97
  DiscountSource: () => DiscountSource,
@@ -1936,6 +1938,7 @@ var import_compat = require("es-toolkit/compat");
1936
1938
  // src/schema/widgets/checkout-and-beyond/regex.ts
1937
1939
  var ARRAY_INDEX_STRING = /^(0|[1-9]\d*)$/;
1938
1940
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
1941
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
1939
1942
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
1940
1943
  var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
1941
1944
  var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
@@ -2619,8 +2622,14 @@ var CABDiscountSection = import_zod22.z.object({
2619
2622
  // src/schema/widgets/checkout-and-beyond/monetize.ts
2620
2623
  var import_uuid9 = require("uuid");
2621
2624
  var import_zod23 = require("zod");
2625
+ var CABMonetizeAds = import_zod23.z.object({
2626
+ requestId: import_zod23.z.string().optional(),
2627
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
2628
+ testing: import_zod23.z.boolean().default(false)
2629
+ });
2622
2630
  var CABMonetizeSection = import_zod23.z.object({
2623
2631
  ...CABVariableFields,
2632
+ ads: CABMonetizeAds.optional(),
2624
2633
  name: import_zod23.z.string().optional(),
2625
2634
  /**
2626
2635
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
package/dist/index.mjs CHANGED
@@ -1642,6 +1642,7 @@ import { get as get2, isString } from "es-toolkit/compat";
1642
1642
  // src/schema/widgets/checkout-and-beyond/regex.ts
1643
1643
  var ARRAY_INDEX_STRING = /^(0|[1-9]\d*)$/;
1644
1644
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
1645
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
1645
1646
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
1646
1647
  var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
1647
1648
  var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
@@ -2325,8 +2326,14 @@ var CABDiscountSection = z22.object({
2325
2326
  // src/schema/widgets/checkout-and-beyond/monetize.ts
2326
2327
  import { v7 as uuidv78 } from "uuid";
2327
2328
  import { z as z23 } from "zod";
2329
+ var CABMonetizeAds = z23.object({
2330
+ requestId: z23.string().optional(),
2331
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
2332
+ testing: z23.boolean().default(false)
2333
+ });
2328
2334
  var CABMonetizeSection = z23.object({
2329
2335
  ...CABVariableFields,
2336
+ ads: CABMonetizeAds.optional(),
2330
2337
  name: z23.string().optional(),
2331
2338
  /**
2332
2339
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
@@ -4873,6 +4880,7 @@ export {
4873
4880
  CABImageSectionFields,
4874
4881
  CABIntegrations,
4875
4882
  CABLayoutSection,
4883
+ CABMonetizeAds,
4876
4884
  CABMonetizeSection,
4877
4885
  CABNumberVariable,
4878
4886
  CABOffersSection,
@@ -4915,6 +4923,7 @@ export {
4915
4923
  ContentOperator,
4916
4924
  DEFAULT_ENDPOINTS,
4917
4925
  DESCRIPTION_LABELS,
4926
+ DYNAMIC_TOKEN_ANYWHERE_REGEX,
4918
4927
  DYNAMIC_TOKEN_REGEX,
4919
4928
  Direction,
4920
4929
  DiscountSource,
@@ -45,6 +45,7 @@ __export(checkout_and_beyond_exports, {
45
45
  CABImageSectionFields: () => CABImageSectionFields,
46
46
  CABIntegrations: () => CABIntegrations,
47
47
  CABLayoutSection: () => CABLayoutSection,
48
+ CABMonetizeAds: () => CABMonetizeAds,
48
49
  CABMonetizeSection: () => CABMonetizeSection,
49
50
  CABNumberVariable: () => CABNumberVariable,
50
51
  CABOffersSection: () => CABOffersSection,
@@ -79,6 +80,7 @@ __export(checkout_and_beyond_exports, {
79
80
  ContentOperator: () => ContentOperator,
80
81
  DEFAULT_ENDPOINTS: () => DEFAULT_ENDPOINTS,
81
82
  DESCRIPTION_LABELS: () => DESCRIPTION_LABELS,
83
+ DYNAMIC_TOKEN_ANYWHERE_REGEX: () => DYNAMIC_TOKEN_ANYWHERE_REGEX,
82
84
  DYNAMIC_TOKEN_REGEX: () => DYNAMIC_TOKEN_REGEX,
83
85
  Direction: () => Direction,
84
86
  DiscountSource: () => DiscountSource,
@@ -510,6 +512,7 @@ var IconTone = freezeEnum(iconTones);
510
512
  // src/schema/widgets/checkout-and-beyond/regex.ts
511
513
  var ARRAY_INDEX_STRING = /^(0|[1-9]\d*)$/;
512
514
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
515
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
513
516
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
514
517
  var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
515
518
  var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
@@ -1132,8 +1135,14 @@ var CABDiscountSection = import_zod12.z.object({
1132
1135
  // src/schema/widgets/checkout-and-beyond/monetize.ts
1133
1136
  var import_uuid8 = require("uuid");
1134
1137
  var import_zod13 = require("zod");
1138
+ var CABMonetizeAds = import_zod13.z.object({
1139
+ requestId: import_zod13.z.string().optional(),
1140
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
1141
+ testing: import_zod13.z.boolean().default(false)
1142
+ });
1135
1143
  var CABMonetizeSection = import_zod13.z.object({
1136
1144
  ...CABVariableFields,
1145
+ ads: CABMonetizeAds.optional(),
1137
1146
  name: import_zod13.z.string().optional(),
1138
1147
  /**
1139
1148
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
@@ -306,6 +306,7 @@ var IconTone = freezeEnum(iconTones);
306
306
  // src/schema/widgets/checkout-and-beyond/regex.ts
307
307
  var ARRAY_INDEX_STRING = /^(0|[1-9]\d*)$/;
308
308
  var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
309
+ var DYNAMIC_TOKEN_ANYWHERE_REGEX = /\{\{\s*[A-Za-z0-9_]+\s*\}\}/;
309
310
  var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
310
311
  var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
311
312
  var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
@@ -928,8 +929,14 @@ var CABDiscountSection = z12.object({
928
929
  // src/schema/widgets/checkout-and-beyond/monetize.ts
929
930
  import { v7 as uuidv78 } from "uuid";
930
931
  import { z as z13 } from "zod";
932
+ var CABMonetizeAds = z13.object({
933
+ requestId: z13.string().optional(),
934
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
935
+ testing: z13.boolean().default(false)
936
+ });
931
937
  var CABMonetizeSection = z13.object({
932
938
  ...CABVariableFields,
939
+ ads: CABMonetizeAds.optional(),
933
940
  name: z13.string().optional(),
934
941
  /**
935
942
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
@@ -1908,6 +1915,7 @@ export {
1908
1915
  CABImageSectionFields,
1909
1916
  CABIntegrations,
1910
1917
  CABLayoutSection,
1918
+ CABMonetizeAds,
1911
1919
  CABMonetizeSection,
1912
1920
  CABNumberVariable,
1913
1921
  CABOffersSection,
@@ -1942,6 +1950,7 @@ export {
1942
1950
  ContentOperator,
1943
1951
  DEFAULT_ENDPOINTS,
1944
1952
  DESCRIPTION_LABELS,
1953
+ DYNAMIC_TOKEN_ANYWHERE_REGEX,
1945
1954
  DYNAMIC_TOKEN_REGEX,
1946
1955
  Direction,
1947
1956
  DiscountSource,
@@ -966,8 +966,14 @@ var CABDiscountSection = import_zod15.z.object({
966
966
  // src/schema/widgets/checkout-and-beyond/monetize.ts
967
967
  var import_uuid8 = require("uuid");
968
968
  var import_zod16 = require("zod");
969
+ var CABMonetizeAds = import_zod16.z.object({
970
+ requestId: import_zod16.z.string().optional(),
971
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
972
+ testing: import_zod16.z.boolean().default(false)
973
+ });
969
974
  var CABMonetizeSection = import_zod16.z.object({
970
975
  ...CABVariableFields,
976
+ ads: CABMonetizeAds.optional(),
971
977
  name: import_zod16.z.string().optional(),
972
978
  /**
973
979
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
@@ -941,8 +941,14 @@ var CABDiscountSection = z15.object({
941
941
  // src/schema/widgets/checkout-and-beyond/monetize.ts
942
942
  import { v7 as uuidv78 } from "uuid";
943
943
  import { z as z16 } from "zod";
944
+ var CABMonetizeAds = z16.object({
945
+ requestId: z16.string().optional(),
946
+ /** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
947
+ testing: z16.boolean().default(false)
948
+ });
944
949
  var CABMonetizeSection = z16.object({
945
950
  ...CABVariableFields,
951
+ ads: CABMonetizeAds.optional(),
946
952
  name: z16.string().optional(),
947
953
  /**
948
954
  * Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`