@rebuy/rebuy 3.16.3 → 3.17.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/dist/gwp/index.cjs +4 -0
- package/dist/gwp/index.mjs +4 -0
- package/dist/gwp/types.d.ts +27 -5
- package/dist/server/giftValidation.d.ts +17 -19
- package/dist/server/giftWidgets.d.ts +37 -0
- package/dist/server/index.cjs +215 -100
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.mjs +215 -100
- package/dist/server/requestSchemas.d.ts +34 -1
- package/dist/server/sgwpCapacity.d.ts +25 -0
- package/dist/server/sgwpValidation.d.ts +19 -0
- package/dist/server/shared.d.ts +5 -4
- package/dist/server/shopSubtotal.d.ts +25 -0
- package/package.json +1 -1
package/dist/gwp/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/gwp/index.ts
|
|
21
21
|
var gwp_exports = {};
|
|
22
22
|
__export(gwp_exports, {
|
|
23
|
+
GIFT_KINDS: () => GIFT_KINDS,
|
|
23
24
|
getValidGwpProductIds: () => getValidGwpProductIds,
|
|
24
25
|
hasLostDiscount: () => hasLostDiscount,
|
|
25
26
|
hasUnsupportedRuleTypes: () => hasUnsupportedRuleTypes,
|
|
@@ -50,3 +51,6 @@ var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matched
|
|
|
50
51
|
// src/gwp/shouldRemoveGift.ts
|
|
51
52
|
var hasLostDiscount = (gift) => !gift.discounted && gift.cost > 0;
|
|
52
53
|
var shouldRemoveGift = (gift, validIds) => !validIds.has(gift.productId) || hasLostDiscount(gift);
|
|
54
|
+
|
|
55
|
+
// src/gwp/types.ts
|
|
56
|
+
var GIFT_KINDS = ["gwp", "sgwp"];
|
package/dist/gwp/index.mjs
CHANGED
|
@@ -19,7 +19,11 @@ var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matched
|
|
|
19
19
|
// src/gwp/shouldRemoveGift.ts
|
|
20
20
|
var hasLostDiscount = (gift) => !gift.discounted && gift.cost > 0;
|
|
21
21
|
var shouldRemoveGift = (gift, validIds) => !validIds.has(gift.productId) || hasLostDiscount(gift);
|
|
22
|
+
|
|
23
|
+
// src/gwp/types.ts
|
|
24
|
+
var GIFT_KINDS = ["gwp", "sgwp"];
|
|
22
25
|
export {
|
|
26
|
+
GIFT_KINDS,
|
|
23
27
|
getValidGwpProductIds,
|
|
24
28
|
hasLostDiscount,
|
|
25
29
|
hasUnsupportedRuleTypes,
|
package/dist/gwp/types.d.ts
CHANGED
|
@@ -1,17 +1,39 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The attribution a gift line carries, as the wire tags it: `gwp` = `Rebuy Gift with Purchase`, `sgwp` =
|
|
3
|
+
* `Rebuy Selectable Gift with Purchase`. `gwp` lines run the GWP rules; `sgwp` lines are judged against their
|
|
4
|
+
* widget's milestones (REB-24363).
|
|
5
|
+
*/
|
|
6
|
+
export declare const GIFT_KINDS: readonly ["gwp", "sgwp"];
|
|
7
|
+
export type GiftKind = (typeof GIFT_KINDS)[number];
|
|
8
|
+
/**
|
|
9
|
+
* A gift cart line the client asks the server to validate (GWP or selectable GWP — see {@link GiftKind}).
|
|
10
|
+
* This is the WIRE shape — what a client sends, `z.input` of the server's `GiftInputSchema` — so the two
|
|
11
|
+
* REB-24363 fields are optional here: a client that predates them sends GWP lines only, and the schema
|
|
12
|
+
* defaults fill them (`kind: 'gwp'`, `quantity: 1`) before the server reads either. The parsed twin
|
|
13
|
+
* (`GiftInput`, `./server`) carries both as required.
|
|
14
|
+
*/
|
|
2
15
|
export type GwpGift = {
|
|
3
16
|
/**
|
|
4
|
-
* The gift line's total in the buyer's MARKET currency, MAJOR units (e.g. `19.99`) — a
|
|
5
|
-
* Function-discounted (free) gift is `0`.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
17
|
+
* The gift line's total in the buyer's MARKET (presentment) currency, MAJOR units (e.g. `19.99`) — a
|
|
18
|
+
* Function-discounted (free) gift is `0`. Send the Shopify cart-line total as-is; both the unit and the
|
|
19
|
+
* magnitude are contractual. A `gwp` line only has its zero/non-zero read (`cost > 0` ⇒ the gift lost
|
|
20
|
+
* its discount), but an `sgwp` line's cost is subtracted from the subtotal its milestones are judged
|
|
21
|
+
* against (×100, ÷ the FX rate), so a minor-unit or unit-price value there would over-subtract and
|
|
22
|
+
* silently strip earned gifts (REB-24363).
|
|
8
23
|
*/
|
|
9
24
|
cost: number;
|
|
10
25
|
/** Whether the Shopify Function discount is currently allocated to the line. */
|
|
11
26
|
discounted: boolean;
|
|
27
|
+
/** Which attribution the line carries; omitted by clients that predate the field (reads as `gwp`). */
|
|
28
|
+
kind?: GiftKind;
|
|
12
29
|
/** Shopify cart-line id to remove. */
|
|
13
30
|
lineId: string;
|
|
14
31
|
productId: number;
|
|
32
|
+
/**
|
|
33
|
+
* The line's unit count; the selectable-GWP verdict trims surplus units against milestone capacity.
|
|
34
|
+
* Omitted by clients that predate the field (reads as one unit — a GWP verdict is whole-line anyway).
|
|
35
|
+
*/
|
|
36
|
+
quantity?: number;
|
|
15
37
|
/** The Rebuy widget that added the gift (`_widget_id`). */
|
|
16
38
|
widgetId: number;
|
|
17
39
|
};
|
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import { type GiftValidationInput, type ServerContext } from '../server/shared';
|
|
1
|
+
import { type GiftValidationInput, type GiftValidationResponse, type ServerContext } from '../server/shared';
|
|
2
2
|
/**
|
|
3
|
-
* Validates gift lines
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Validates the gift lines and returns the receipt. The two gift kinds get independent verdicts, each
|
|
4
|
+
* filling its own list — so a line id can never land in both: GWP (`gwp`) lines are judged whole-line by
|
|
5
|
+
* their widget's rules and fill `remove` ({@link validateGwpGifts}); selectable GWP (`sgwp`) lines are
|
|
6
|
+
* trimmed to their widget's milestone capacity and fill `adjust` (target quantity, `0` = remove —
|
|
7
|
+
* {@link validateSelectableGifts}). A selectable gift may be legitimately priced, so the GWP charged-gift
|
|
8
|
+
* rule never touches it. Both halves fail safe per widget, so a degraded answer for one widget never
|
|
9
|
+
* disturbs another's verdict.
|
|
6
10
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* matching the React extension). **Discount loss** ({@link hasLostDiscount}: `!discounted && cost > 0`) is cart-observable
|
|
16
|
-
* and needs NO engine answer, so it removes the gift on EVERY path, degraded or not — a buyer must not keep
|
|
17
|
-
* paying for a "gift" because the rule engine was degraded. (React removed on any successful fetch; this
|
|
18
|
-
* splits the observable discount-loss out of the qualification fail-safe rather than gating both behind it.)
|
|
11
|
+
* The receipt's one-verdict-per-line invariant (`GiftValidationResponseSchema`) is upheld HERE, not by the
|
|
12
|
+
* client: nothing stops a buggy watcher sending one `lineId` twice — under both kinds (a line carrying
|
|
13
|
+
* both attributions, or a double push), or as `sgwp` under two widgets — which would otherwise land it in
|
|
14
|
+
* both lists, or twice in `adjust` with conflicting targets, and fail the response schema at the route — a
|
|
15
|
+
* 500 where the feature promises fail-safe. The strictest verdict wins: removal over any target (a removed
|
|
16
|
+
* line has no quantity left), and the smallest target among duplicates (each is a trim, so the smallest can
|
|
17
|
+
* never exceed what was sent). `remove` is likewise named once per line (a `gwp` line sent under two
|
|
18
|
+
* widgets is removed by both legs), so a client issuing one cart mutation per entry never removes twice.
|
|
19
19
|
*/
|
|
20
|
-
export declare const validateGifts: (input: GiftValidationInput, ctx: ServerContext) => Promise<
|
|
21
|
-
remove: string[];
|
|
22
|
-
}>;
|
|
20
|
+
export declare const validateGifts: (input: GiftValidationInput, ctx: ServerContext) => Promise<GiftValidationResponse>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type GiftKind } from '../gwp';
|
|
3
|
+
import { type GiftInput, type Reporter, type ServerContext } from '../server/shared';
|
|
4
|
+
/**
|
|
5
|
+
* One selectable-GWP milestone, RAW (snake_case) as the retired React validator read it: `unlock_price` in
|
|
6
|
+
* shop MAJOR units, `selectable_product_quantity` in units (a non-negative whole count — a fractional or
|
|
7
|
+
* negative capacity is nonsense, so it is malformed rather than summed).
|
|
8
|
+
*
|
|
9
|
+
* `unlock_price` is deliberately NOT bounded below. A negative or zero price is a legitimate "always
|
|
10
|
+
* unlocked" tier (the progress-bar converter tolerates the same via `Number(...) || 0`), and it only
|
|
11
|
+
* grants ITS OWN capacity. Rejecting it would mark the whole list malformed, which keeps every gift the
|
|
12
|
+
* widget added — unlimited capacity, a strictly worse degradation than the one avoided.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SgwpMilestoneSchema: z.ZodObject<{
|
|
15
|
+
selectable_product_quantity: z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodCoercedNumber<string | number>>, z.ZodNumber>;
|
|
16
|
+
unlock_price: z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodCoercedNumber<string | number>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
/** One parsed selectable-GWP milestone: the shop-major-unit unlock price and the gift units it grants. */
|
|
19
|
+
export type SgwpMilestone = z.infer<typeof SgwpMilestoneSchema>;
|
|
20
|
+
/**
|
|
21
|
+
* One gift widget's raw settings from the engine (the `data` envelope unwrapped, so a deleted widget's
|
|
22
|
+
* `{ data: null }` reads as `null`). Throws on an upstream failure — every caller fails safe (keeps the
|
|
23
|
+
* widget's gifts), so the throw is the signal, not a bug.
|
|
24
|
+
*/
|
|
25
|
+
export declare const fetchGiftWidgetSettings: (widgetId: number, shop: string, ctx: ServerContext) => Promise<unknown>;
|
|
26
|
+
/** A GWP widget's data-source path from its raw settings `endpoint` (uniform across widget types). */
|
|
27
|
+
export declare const readDataSourcePath: (settings: unknown) => string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* A selectable-GWP widget's milestones from its raw settings. Absent or empty milestones are an empty list
|
|
30
|
+
* (the widget has nothing to judge against — silent). A MALFORMED list also reads as empty, because the
|
|
31
|
+
* fail-safe direction is to keep the gifts, but that is a settings-contract drift, so it is reported — with
|
|
32
|
+
* the first issue's path AND message, since the likeliest real drift (`milestones` is an object, not an
|
|
33
|
+
* array) fails at the top level, where the path is empty and only the message says what arrived.
|
|
34
|
+
*/
|
|
35
|
+
export declare const readMilestones: (settings: unknown, report?: Reporter) => SgwpMilestone[];
|
|
36
|
+
/** The gifts of one `kind`, grouped by widget in request order (insertion-ordered map; lines keep their order within a widget). */
|
|
37
|
+
export declare const groupGiftsByWidget: (gifts: GiftInput[], kind: GiftKind) => Map<number, GiftInput[]>;
|
package/dist/server/index.cjs
CHANGED
|
@@ -25,8 +25,10 @@ __export(server_exports, {
|
|
|
25
25
|
CartInputSchema: () => CartInputSchema,
|
|
26
26
|
CartLineInputSchema: () => CartLineInputSchema,
|
|
27
27
|
DataSourceInputSchema: () => DataSourceInputSchema,
|
|
28
|
+
GiftAdjustmentSchema: () => GiftAdjustmentSchema,
|
|
28
29
|
GiftInputSchema: () => GiftInputSchema,
|
|
29
30
|
GiftValidationInputSchema: () => GiftValidationInputSchema,
|
|
31
|
+
GiftValidationResponseSchema: () => GiftValidationResponseSchema,
|
|
30
32
|
MonetizeInputSchema: () => MonetizeInputSchema,
|
|
31
33
|
MonetizeTransactionItemSchema: () => MonetizeTransactionItemSchema,
|
|
32
34
|
NotFoundError: () => NotFoundError,
|
|
@@ -71,6 +73,9 @@ var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matched
|
|
|
71
73
|
var hasLostDiscount = (gift) => !gift.discounted && gift.cost > 0;
|
|
72
74
|
var shouldRemoveGift = (gift, validIds) => !validIds.has(gift.productId) || hasLostDiscount(gift);
|
|
73
75
|
|
|
76
|
+
// src/gwp/types.ts
|
|
77
|
+
var GIFT_KINDS = ["gwp", "sgwp"];
|
|
78
|
+
|
|
74
79
|
// src/schema/metadata.ts
|
|
75
80
|
var import_zod2 = require("zod");
|
|
76
81
|
|
|
@@ -430,6 +435,14 @@ var NotFoundError = class extends Error {
|
|
|
430
435
|
}
|
|
431
436
|
};
|
|
432
437
|
|
|
438
|
+
// src/server/shopSubtotal.ts
|
|
439
|
+
var shopCurrencyRate = (cart) => {
|
|
440
|
+
const rate = cart.currencyRate ?? 1;
|
|
441
|
+
return Number.isFinite(rate) && rate > 0 ? rate : 1;
|
|
442
|
+
};
|
|
443
|
+
var shopSubtotalCents = (cart) => Math.round((cart.subtotal - (cart.discountTotal ?? 0)) / shopCurrencyRate(cart));
|
|
444
|
+
var toShopCents = (majorPresentment, rate) => Math.round(majorPresentment * 100 / rate);
|
|
445
|
+
|
|
433
446
|
// src/schema/cabShopConfig.ts
|
|
434
447
|
var import_zod6 = require("zod");
|
|
435
448
|
|
|
@@ -3923,9 +3936,7 @@ var buildEngineParams = (input, key) => {
|
|
|
3923
3936
|
variant_id: variantId
|
|
3924
3937
|
}));
|
|
3925
3938
|
const itemCount = cart.items.reduce((total, { quantity }) => total + quantity, 0);
|
|
3926
|
-
const
|
|
3927
|
-
const rate = Number.isFinite(currencyRate) && currencyRate > 0 ? currencyRate : 1;
|
|
3928
|
-
const subtotal = Math.round((cart.subtotal - (cart.discountTotal ?? 0)) / rate);
|
|
3939
|
+
const subtotal = shopSubtotalCents(cart);
|
|
3929
3940
|
return {
|
|
3930
3941
|
cart: {
|
|
3931
3942
|
attributes: encodeAttributes(cart.attributes),
|
|
@@ -4398,26 +4409,92 @@ var convertWidgetSettings = async (input, ctx) => {
|
|
|
4398
4409
|
return experiment && isCABTree(result) ? { ...result, experiment } : result;
|
|
4399
4410
|
};
|
|
4400
4411
|
|
|
4401
|
-
// src/server/
|
|
4402
|
-
var
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4412
|
+
// src/server/giftWidgets.ts
|
|
4413
|
+
var import_zod29 = require("zod");
|
|
4414
|
+
var SettingsNumber = import_zod29.z.union([
|
|
4415
|
+
import_zod29.z.number(),
|
|
4416
|
+
import_zod29.z.string().trim().regex(/^-?\d+(\.\d+)?$/)
|
|
4417
|
+
]).pipe(import_zod29.z.coerce.number());
|
|
4418
|
+
var SgwpMilestoneSchema = import_zod29.z.object({
|
|
4419
|
+
selectable_product_quantity: SettingsNumber.pipe(import_zod29.z.number().int().nonnegative()),
|
|
4420
|
+
unlock_price: SettingsNumber
|
|
4421
|
+
});
|
|
4422
|
+
var fetchGiftWidgetSettings = async (widgetId, shop, ctx) => unwrapData(await ctx.fetchUpstream("/api/v1/widgets/settings", { id: String(widgetId), shop }, { host: ctx.host }));
|
|
4423
|
+
var readDataSourcePath = (settings) => {
|
|
4424
|
+
const endpoint = deepCamelCase(settings)?.endpoint;
|
|
4406
4425
|
return typeof endpoint === "string" && endpoint ? endpoint : void 0;
|
|
4407
4426
|
};
|
|
4408
|
-
var
|
|
4427
|
+
var readMilestones = (settings, report) => {
|
|
4428
|
+
const raw = settings && typeof settings === "object" ? settings.milestones : void 0;
|
|
4429
|
+
if (raw == null) return [];
|
|
4430
|
+
const parsed = import_zod29.z.array(SgwpMilestoneSchema).safeParse(raw);
|
|
4431
|
+
if (parsed.success) return parsed.data;
|
|
4432
|
+
report?.({
|
|
4433
|
+
detail: [
|
|
4434
|
+
"selectable-gift milestones unreadable",
|
|
4435
|
+
...parsed.error.issues.slice(0, 1).map((issue) => [issue.path.join("."), issue.message].filter(Boolean).join(": "))
|
|
4436
|
+
].join(": "),
|
|
4437
|
+
kind: "giftMilestones"
|
|
4438
|
+
});
|
|
4439
|
+
return [];
|
|
4440
|
+
};
|
|
4441
|
+
var groupGiftsByWidget = (gifts, kind) => {
|
|
4409
4442
|
const byWidget = /* @__PURE__ */ new Map();
|
|
4410
4443
|
for (const gift of gifts) {
|
|
4444
|
+
if (gift.kind !== kind) continue;
|
|
4411
4445
|
byWidget.set(gift.widgetId, [...byWidget.get(gift.widgetId) ?? [], gift]);
|
|
4412
4446
|
}
|
|
4413
4447
|
return byWidget;
|
|
4414
4448
|
};
|
|
4415
|
-
|
|
4449
|
+
|
|
4450
|
+
// src/server/sgwpCapacity.ts
|
|
4451
|
+
var sgwpQualifyingSubtotalCents = (cart, gifts) => {
|
|
4452
|
+
const giftMajor = gifts.filter((gift) => gift.kind === "sgwp").reduce((total, gift) => total + gift.cost, 0);
|
|
4453
|
+
return shopSubtotalCents(cart) - toShopCents(giftMajor, shopCurrencyRate(cart));
|
|
4454
|
+
};
|
|
4455
|
+
var unlockedCapacity = (milestones, subtotalCents) => milestones.reduce(
|
|
4456
|
+
(capacity, milestone) => Math.round(milestone.unlock_price * 100) <= subtotalCents ? capacity + milestone.selectable_product_quantity : capacity,
|
|
4457
|
+
0
|
|
4458
|
+
);
|
|
4459
|
+
|
|
4460
|
+
// src/server/sgwpValidation.ts
|
|
4461
|
+
var computeSgwpAdjustments = (gifts, capacity) => {
|
|
4462
|
+
let surplus = gifts.reduce((units, gift) => units + gift.quantity, 0) - capacity;
|
|
4463
|
+
const adjust = [];
|
|
4464
|
+
for (const gift of gifts) {
|
|
4465
|
+
if (surplus <= 0) break;
|
|
4466
|
+
const trimmed = Math.min(gift.quantity, surplus);
|
|
4467
|
+
adjust.push({ lineId: gift.lineId, quantity: gift.quantity - trimmed });
|
|
4468
|
+
surplus -= trimmed;
|
|
4469
|
+
}
|
|
4470
|
+
return adjust;
|
|
4471
|
+
};
|
|
4472
|
+
var validateSelectableGifts = async (input, ctx) => {
|
|
4473
|
+
const byWidget = groupGiftsByWidget(input.gifts, "sgwp");
|
|
4474
|
+
if (byWidget.size === 0) return [];
|
|
4475
|
+
const subtotalCents = sgwpQualifyingSubtotalCents(input.cart, input.gifts);
|
|
4416
4476
|
const perWidget = await Promise.all(
|
|
4417
|
-
[...
|
|
4477
|
+
[...byWidget].map(async ([widgetId, gifts]) => {
|
|
4478
|
+
try {
|
|
4479
|
+
const settings = await fetchGiftWidgetSettings(widgetId, input.shop, ctx);
|
|
4480
|
+
const milestones = readMilestones(settings, ctx.reportDrift);
|
|
4481
|
+
if (milestones.length === 0) return [];
|
|
4482
|
+
return computeSgwpAdjustments(gifts, unlockedCapacity(milestones, subtotalCents));
|
|
4483
|
+
} catch {
|
|
4484
|
+
return [];
|
|
4485
|
+
}
|
|
4486
|
+
})
|
|
4487
|
+
);
|
|
4488
|
+
return perWidget.flat();
|
|
4489
|
+
};
|
|
4490
|
+
|
|
4491
|
+
// src/server/giftValidation.ts
|
|
4492
|
+
var validateGwpGifts = async (input, ctx) => {
|
|
4493
|
+
const perWidget = await Promise.all(
|
|
4494
|
+
[...groupGiftsByWidget(input.gifts, "gwp")].map(async ([widgetId, gifts]) => {
|
|
4418
4495
|
const discountLost = gifts.filter(hasLostDiscount).map((gift) => gift.lineId);
|
|
4419
4496
|
try {
|
|
4420
|
-
const dataSourcePath = await
|
|
4497
|
+
const dataSourcePath = readDataSourcePath(await fetchGiftWidgetSettings(widgetId, input.shop, ctx));
|
|
4421
4498
|
if (!dataSourcePath) return discountLost;
|
|
4422
4499
|
const { metadata } = await fetchDataSourceResults(
|
|
4423
4500
|
{
|
|
@@ -4450,7 +4527,17 @@ var validateGifts = async (input, ctx) => {
|
|
|
4450
4527
|
}
|
|
4451
4528
|
})
|
|
4452
4529
|
);
|
|
4453
|
-
return
|
|
4530
|
+
return perWidget.flat();
|
|
4531
|
+
};
|
|
4532
|
+
var validateGifts = async (input, ctx) => {
|
|
4533
|
+
const [remove, adjust] = await Promise.all([validateGwpGifts(input, ctx), validateSelectableGifts(input, ctx)]);
|
|
4534
|
+
const removed = new Set(remove);
|
|
4535
|
+
const targets = /* @__PURE__ */ new Map();
|
|
4536
|
+
for (const { lineId, quantity } of adjust) {
|
|
4537
|
+
if (removed.has(lineId)) continue;
|
|
4538
|
+
targets.set(lineId, Math.min(quantity, targets.get(lineId) ?? quantity));
|
|
4539
|
+
}
|
|
4540
|
+
return { adjust: [...targets].map(([lineId, quantity]) => ({ lineId, quantity })), remove: [...removed] };
|
|
4454
4541
|
};
|
|
4455
4542
|
|
|
4456
4543
|
// src/server/analytics.ts
|
|
@@ -4572,7 +4659,7 @@ var postAnalyticsEvents = async (input, ctx) => {
|
|
|
4572
4659
|
};
|
|
4573
4660
|
|
|
4574
4661
|
// src/server/requestSchemas.ts
|
|
4575
|
-
var
|
|
4662
|
+
var import_zod30 = require("zod");
|
|
4576
4663
|
var REQUEST_BOUNDS = {
|
|
4577
4664
|
/** Cart-level attributes per cart. */
|
|
4578
4665
|
CART_ATTRIBUTES_MAX: 100,
|
|
@@ -4603,42 +4690,42 @@ var REQUEST_BOUNDS = {
|
|
|
4603
4690
|
/** Rich ads transaction lines — the engine's ads contract. */
|
|
4604
4691
|
TRANSACTION_ITEMS_MAX: 50
|
|
4605
4692
|
};
|
|
4606
|
-
var KeyValue =
|
|
4607
|
-
key:
|
|
4608
|
-
value:
|
|
4693
|
+
var KeyValue = import_zod30.z.object({
|
|
4694
|
+
key: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
|
|
4695
|
+
value: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_VALUE_MAX).optional()
|
|
4609
4696
|
});
|
|
4610
|
-
var CartLineInputSchema =
|
|
4611
|
-
productId:
|
|
4612
|
-
properties:
|
|
4613
|
-
quantity:
|
|
4614
|
-
subscriptionId:
|
|
4615
|
-
variantId:
|
|
4697
|
+
var CartLineInputSchema = import_zod30.z.object({
|
|
4698
|
+
productId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4699
|
+
properties: import_zod30.z.array(KeyValue).max(REQUEST_BOUNDS.LINE_PROPERTIES_MAX).optional(),
|
|
4700
|
+
quantity: import_zod30.z.number(),
|
|
4701
|
+
subscriptionId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4702
|
+
variantId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX)
|
|
4616
4703
|
});
|
|
4617
|
-
var CartInputSchema =
|
|
4618
|
-
attributes:
|
|
4704
|
+
var CartInputSchema = import_zod30.z.object({
|
|
4705
|
+
attributes: import_zod30.z.array(KeyValue).max(REQUEST_BOUNDS.CART_ATTRIBUTES_MAX).optional(),
|
|
4619
4706
|
/**
|
|
4620
4707
|
* Presentment-per-shop-currency FX rate (buyer price ÷ shop price, e.g. `1.35` for a USD shop
|
|
4621
4708
|
* selling in CAD). Money rule thresholds are authored in SHOP currency, so the engine subtotal is
|
|
4622
4709
|
* divided by this before evaluation. Omit (or send 1) when the cart is already in the shop's currency.
|
|
4623
4710
|
*/
|
|
4624
|
-
currencyRate:
|
|
4711
|
+
currencyRate: import_zod30.z.number().optional(),
|
|
4625
4712
|
/** Applied discount codes; the engine evaluates `discount_code` rules against them (sent as `[{code}]`). */
|
|
4626
|
-
discountCodes:
|
|
4713
|
+
discountCodes: import_zod30.z.array(import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX)).max(REQUEST_BOUNDS.DISCOUNT_CODES_MAX).optional(),
|
|
4627
4714
|
/** Presentment cents of cart-level discount allocations, subtracted from `subtotal` for the engine. */
|
|
4628
|
-
discountTotal:
|
|
4629
|
-
items:
|
|
4715
|
+
discountTotal: import_zod30.z.number().optional(),
|
|
4716
|
+
items: import_zod30.z.array(CartLineInputSchema).max(REQUEST_BOUNDS.CART_ITEMS_MAX),
|
|
4630
4717
|
/** Presentment subtotal in cents. */
|
|
4631
|
-
subtotal:
|
|
4718
|
+
subtotal: import_zod30.z.number()
|
|
4632
4719
|
});
|
|
4633
|
-
var UserConfigInputSchema =
|
|
4634
|
-
var DataSourceInputSchema =
|
|
4720
|
+
var UserConfigInputSchema = import_zod30.z.object({ shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX) });
|
|
4721
|
+
var DataSourceInputSchema = import_zod30.z.object({
|
|
4635
4722
|
cart: CartInputSchema,
|
|
4636
4723
|
/**
|
|
4637
4724
|
* Numeric Shopify company-location id when the buyer is a B2B session (`buyerIdentity.purchasingCompany`).
|
|
4638
4725
|
* Present ⇒ the orchestrator filters results by the location's catalog via admin-api (REB-23713); the
|
|
4639
4726
|
* client's country-context market check is bypassed for B2B (it false-negatives B2B-catalog products).
|
|
4640
4727
|
*/
|
|
4641
|
-
companyLocationId:
|
|
4728
|
+
companyLocationId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4642
4729
|
/**
|
|
4643
4730
|
* Buyer's market country (ISO 3166-1 alpha-2) — forwarded to the engine as the `country_code` PARAM
|
|
4644
4731
|
* when the shop's markets toggle is on (REB-21688; the sink was the `X-Country-Code` header under
|
|
@@ -4647,16 +4734,16 @@ var DataSourceInputSchema = import_zod29.z.object({
|
|
|
4647
4734
|
* uppercase (the wire value is forwarded verbatim as the param) so a case-sensitive engine lookup
|
|
4648
4735
|
* can't silently miss a lowercase code — `adsEngine.ts` uppercases for the same reason.
|
|
4649
4736
|
*/
|
|
4650
|
-
country:
|
|
4651
|
-
customerId:
|
|
4652
|
-
dataSourcePath:
|
|
4737
|
+
country: import_zod30.z.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(),
|
|
4738
|
+
customerId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4739
|
+
dataSourcePath: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
|
|
4653
4740
|
/**
|
|
4654
4741
|
* Engine `filter_inputs` override: `'no'` keeps in-cart (input) products in the results/rule
|
|
4655
4742
|
* outputs — gift validation sends it so a gift's own product can appear in its answer. Omitted = the
|
|
4656
4743
|
* engine's default filtering.
|
|
4657
4744
|
*/
|
|
4658
|
-
filterInputs:
|
|
4659
|
-
integrations:
|
|
4745
|
+
filterInputs: import_zod30.z.enum(["no", "yes"]).optional(),
|
|
4746
|
+
integrations: import_zod30.z.record(import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), import_zod30.z.boolean()).refine((record) => Object.keys(record).length <= REQUEST_BOUNDS.INTEGRATION_KEYS_MAX, {
|
|
4660
4747
|
message: `Too many integration keys (max ${REQUEST_BOUNDS.INTEGRATION_KEYS_MAX})`
|
|
4661
4748
|
}).optional(),
|
|
4662
4749
|
/**
|
|
@@ -4669,20 +4756,31 @@ var DataSourceInputSchema = import_zod29.z.object({
|
|
|
4669
4756
|
* request header) fails the regex. Callers should send the buyer's resolved locale (e.g. Shopify
|
|
4670
4757
|
* checkout's `localization.language.isoCode`), not the request header verbatim.
|
|
4671
4758
|
*/
|
|
4672
|
-
language:
|
|
4673
|
-
limit:
|
|
4674
|
-
productType:
|
|
4675
|
-
shop:
|
|
4676
|
-
visitorId:
|
|
4759
|
+
language: import_zod30.z.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(),
|
|
4760
|
+
limit: import_zod30.z.number().max(REQUEST_BOUNDS.LIMIT_MAX),
|
|
4761
|
+
productType: import_zod30.z.enum(["both", "one-time", "subscription"]).optional(),
|
|
4762
|
+
shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4763
|
+
visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4677
4764
|
});
|
|
4678
|
-
var GiftInputSchema =
|
|
4679
|
-
cost:
|
|
4680
|
-
discounted:
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4765
|
+
var GiftInputSchema = import_zod30.z.object({
|
|
4766
|
+
cost: import_zod30.z.number(),
|
|
4767
|
+
discounted: import_zod30.z.boolean(),
|
|
4768
|
+
/**
|
|
4769
|
+
* Which gift attribution the line carries (REB-24363). Defaults to `gwp` because a client that predates
|
|
4770
|
+
* the field only ever sent GWP lines — without the default an older extension's request would 422.
|
|
4771
|
+
*/
|
|
4772
|
+
kind: import_zod30.z.enum(GIFT_KINDS).default("gwp"),
|
|
4773
|
+
lineId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4774
|
+
productId: import_zod30.z.number(),
|
|
4775
|
+
/**
|
|
4776
|
+
* The line's unit count (REB-24363) — the selectable-GWP verdict trims surplus units against milestone
|
|
4777
|
+
* capacity. The second deliberate default: a client predating the field sent only GWP lines, whose
|
|
4778
|
+
* verdict is whole-line, so a missing quantity reads as one unit rather than 422ing the older request.
|
|
4779
|
+
*/
|
|
4780
|
+
quantity: import_zod30.z.number().int().min(1).default(1),
|
|
4781
|
+
widgetId: import_zod30.z.number()
|
|
4684
4782
|
});
|
|
4685
|
-
var GiftValidationInputSchema =
|
|
4783
|
+
var GiftValidationInputSchema = import_zod30.z.object({
|
|
4686
4784
|
cart: CartInputSchema,
|
|
4687
4785
|
/**
|
|
4688
4786
|
* Buyer's market country — threaded into the per-widget data-source evaluations, which forward it to
|
|
@@ -4691,59 +4789,76 @@ var GiftValidationInputSchema = import_zod29.z.object({
|
|
|
4691
4789
|
* turns markets off); regex-bound and uppercase-normalized for the same reasons as
|
|
4692
4790
|
* `DataSourceInputSchema`'s `country`.
|
|
4693
4791
|
*/
|
|
4694
|
-
country:
|
|
4695
|
-
customerId:
|
|
4696
|
-
gifts:
|
|
4792
|
+
country: import_zod30.z.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(),
|
|
4793
|
+
customerId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4794
|
+
gifts: import_zod30.z.array(GiftInputSchema).max(REQUEST_BOUNDS.GIFTS_MAX),
|
|
4697
4795
|
/**
|
|
4698
4796
|
* Buyer's checkout language — threaded into the per-widget data-source evaluations, which normalize
|
|
4699
4797
|
* and forward it as the engine `locale` param under the same markets-toggle gate (REB-21688). MUST be
|
|
4700
4798
|
* a single BCP-47 tag, for the same reason as `DataSourceInputSchema`'s `language` above.
|
|
4701
4799
|
*/
|
|
4702
|
-
language:
|
|
4703
|
-
shop:
|
|
4704
|
-
visitorId:
|
|
4800
|
+
language: import_zod30.z.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(),
|
|
4801
|
+
shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4802
|
+
visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4803
|
+
});
|
|
4804
|
+
var GiftAdjustmentSchema = import_zod30.z.object({
|
|
4805
|
+
lineId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4806
|
+
quantity: import_zod30.z.number().int().nonnegative()
|
|
4705
4807
|
});
|
|
4706
|
-
var
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4808
|
+
var GiftValidationResponseSchema = import_zod30.z.object({
|
|
4809
|
+
adjust: import_zod30.z.array(GiftAdjustmentSchema).max(REQUEST_BOUNDS.GIFTS_MAX).default([]),
|
|
4810
|
+
remove: import_zod30.z.array(import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX)).max(REQUEST_BOUNDS.GIFTS_MAX).refine(
|
|
4811
|
+
(remove) => new Set(remove).size === remove.length,
|
|
4812
|
+
"a line id may appear at most once in `remove`"
|
|
4813
|
+
)
|
|
4814
|
+
}).refine(
|
|
4815
|
+
({ adjust, remove }) => !adjust.some(({ lineId }) => remove.includes(lineId)),
|
|
4816
|
+
"a line id may not appear in both `remove` and `adjust`"
|
|
4817
|
+
).refine(
|
|
4818
|
+
({ adjust }) => new Set(adjust.map(({ lineId }) => lineId)).size === adjust.length,
|
|
4819
|
+
"a line id may appear at most once in `adjust`"
|
|
4820
|
+
);
|
|
4821
|
+
var MonetizeTransactionItemSchema = import_zod30.z.object({
|
|
4822
|
+
id: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4823
|
+
priceCents: import_zod30.z.number(),
|
|
4824
|
+
quantity: import_zod30.z.number(),
|
|
4825
|
+
title: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX),
|
|
4826
|
+
type: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX)
|
|
4712
4827
|
});
|
|
4713
|
-
var MonetizeInputSchema =
|
|
4828
|
+
var MonetizeInputSchema = import_zod30.z.object({
|
|
4714
4829
|
cart: CartInputSchema,
|
|
4715
4830
|
/** Client-minted render-beacon correlation id; forwarded to the ads engine as `client_request_id`. */
|
|
4716
|
-
clientRequestId:
|
|
4717
|
-
country:
|
|
4718
|
-
currency:
|
|
4831
|
+
clientRequestId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4832
|
+
country: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4833
|
+
currency: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4719
4834
|
/** Ads-targeting identity/geo/locale fields (React parity); folded into the engine query verbatim. */
|
|
4720
|
-
customerEmail:
|
|
4721
|
-
customerFirstName:
|
|
4722
|
-
customerId:
|
|
4723
|
-
customerLastName:
|
|
4724
|
-
language:
|
|
4835
|
+
customerEmail: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
|
|
4836
|
+
customerFirstName: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
|
|
4837
|
+
customerId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4838
|
+
customerLastName: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
|
|
4839
|
+
language: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4725
4840
|
/** Buyer-facing order name (e.g. `#1001`) for the server-composed "Order … Confirmed" chrome line. */
|
|
4726
|
-
orderName:
|
|
4841
|
+
orderName: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_TEXT_MAX).optional(),
|
|
4727
4842
|
/** Ads surface label, forwarded verbatim; `customer_account` is measured/floored as its own surface (the account-area order list). */
|
|
4728
|
-
placement:
|
|
4843
|
+
placement: import_zod30.z.enum(["customer_account", "order_status", "thank_you_page"]),
|
|
4729
4844
|
/** Privacy Policy footer href — the Monetize widget's `privacy_policy_url` setting (React parity). */
|
|
4730
|
-
privacyPolicyUrl:
|
|
4845
|
+
privacyPolicyUrl: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_URL_MAX).optional(),
|
|
4731
4846
|
/** 30-min rolling session id (React parity); used for the ads `session_id`, else the visitor id. */
|
|
4732
|
-
sessionId:
|
|
4733
|
-
shop:
|
|
4847
|
+
sessionId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4848
|
+
shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4734
4849
|
/** Editor/QA indicator; OR-ed into the ads-engine `testing` flag so it stays out of billing/reporting. */
|
|
4735
|
-
testing:
|
|
4736
|
-
timezone:
|
|
4850
|
+
testing: import_zod30.z.boolean().optional(),
|
|
4851
|
+
timezone: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4737
4852
|
/** Decorated (numeric) order id → the ads `transaction_id`. */
|
|
4738
|
-
transactionId:
|
|
4853
|
+
transactionId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4739
4854
|
/** Rich per-line ads items; capped at the engine's ads contract. Preferred over `cart.items`. */
|
|
4740
|
-
transactionItems:
|
|
4855
|
+
transactionItems: import_zod30.z.array(MonetizeTransactionItemSchema).max(REQUEST_BOUNDS.TRANSACTION_ITEMS_MAX).optional(),
|
|
4741
4856
|
/** Order total in presentment cents (React prefers total over subtotal); else `cart.subtotal`. */
|
|
4742
|
-
transactionTotal:
|
|
4743
|
-
visitorId:
|
|
4744
|
-
zipCode:
|
|
4857
|
+
transactionTotal: import_zod30.z.number().optional(),
|
|
4858
|
+
visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4859
|
+
zipCode: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4745
4860
|
});
|
|
4746
|
-
var WidgetSettingsInputSchema =
|
|
4861
|
+
var WidgetSettingsInputSchema = import_zod30.z.object({
|
|
4747
4862
|
/**
|
|
4748
4863
|
* Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
|
|
4749
4864
|
* `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
|
|
@@ -4751,9 +4866,9 @@ var WidgetSettingsInputSchema = import_zod29.z.object({
|
|
|
4751
4866
|
* and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
|
|
4752
4867
|
* the read to un-busted, never 400 a widget render.
|
|
4753
4868
|
*/
|
|
4754
|
-
cacheKey:
|
|
4755
|
-
id:
|
|
4756
|
-
key:
|
|
4869
|
+
cacheKey: import_zod30.z.number().int().positive().optional().catch(void 0),
|
|
4870
|
+
id: import_zod30.z.number(),
|
|
4871
|
+
key: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4757
4872
|
/**
|
|
4758
4873
|
* Monetize context for inline hydration: when present AND the widget converts to a Monetize marker,
|
|
4759
4874
|
* the server fetches/composes the offer cards inside this same request (no second `/cab/monetize`
|
|
@@ -4762,23 +4877,23 @@ var WidgetSettingsInputSchema = import_zod29.z.object({
|
|
|
4762
4877
|
* `privacyPolicyUrl` comes from the widget settings server-side (more authoritative than a relay).
|
|
4763
4878
|
*/
|
|
4764
4879
|
monetize: MonetizeInputSchema.omit({ privacyPolicyUrl: true, shop: true }).optional(),
|
|
4765
|
-
shop:
|
|
4880
|
+
shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4766
4881
|
/** Stable per-buyer id; when present, enables sticky server-side A/B variant selection. */
|
|
4767
|
-
visitorId:
|
|
4882
|
+
visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4768
4883
|
});
|
|
4769
|
-
var AnalyticsEventInputSchema =
|
|
4770
|
-
name:
|
|
4771
|
-
properties:
|
|
4772
|
-
sentAt:
|
|
4884
|
+
var AnalyticsEventInputSchema = import_zod30.z.object({
|
|
4885
|
+
name: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
|
|
4886
|
+
properties: import_zod30.z.record(import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), import_zod30.z.unknown()).optional(),
|
|
4887
|
+
sentAt: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4773
4888
|
});
|
|
4774
|
-
var AnalyticsInputSchema =
|
|
4889
|
+
var AnalyticsInputSchema = import_zod30.z.object({
|
|
4775
4890
|
/** Rebuy cart token (the `r_cart_token` cart attribute), when available — primary aggregation key. */
|
|
4776
|
-
cartToken:
|
|
4891
|
+
cartToken: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4777
4892
|
/** Shopify checkout token, when available — drives cart/checkout aggregation downstream. */
|
|
4778
|
-
checkoutToken:
|
|
4779
|
-
events:
|
|
4780
|
-
shop:
|
|
4781
|
-
visitorId:
|
|
4893
|
+
checkoutToken: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4894
|
+
events: import_zod30.z.array(AnalyticsEventInputSchema).max(REQUEST_BOUNDS.EVENTS_MAX),
|
|
4895
|
+
shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4896
|
+
visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4782
4897
|
/** The widget id (smartBlockId) the events belong to. */
|
|
4783
|
-
widgetId:
|
|
4898
|
+
widgetId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4784
4899
|
});
|