@rebuy/rebuy 3.59.0 → 3.60.1
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/index.cjs +17 -1
- package/dist/index.mjs +17 -1
- package/dist/schema/checkout-and-beyond/constants.cjs +2 -0
- package/dist/schema/checkout-and-beyond/constants.mjs +2 -0
- package/dist/schema/checkout-and-beyond/index.cjs +2 -0
- package/dist/schema/checkout-and-beyond/index.mjs +2 -0
- package/dist/schema/offerProduct.d.ts +16 -0
- package/dist/schema/postPurchaseProductSettings.d.ts +1 -0
- package/dist/schema/widgets/checkout-and-beyond/common.d.ts +10 -1
- package/dist/schema/widgets/post-purchase-offer/postPurchaseOffer.d.ts +1 -0
- package/dist/schema/widgets/post-purchase-offer/postPurchaseSettings.d.ts +1 -0
- package/dist/schema/widgets/post-purchase-offer/postPurchaseWidget.d.ts +1 -0
- package/dist/server/index.cjs +1754 -1675
- package/dist/server/index.mjs +1710 -1631
- package/dist/server/normalizePostPurchaseCollection.d.ts +2 -0
- package/dist/server/normalizePostPurchaseProduct.d.ts +4 -0
- package/dist/server/offerProductReviews.d.ts +13 -0
- package/dist/server/postPurchaseReviewData.d.ts +9 -0
- package/dist/server/preparePostPurchaseMemberView.d.ts +4 -0
- package/dist/server/preparePostPurchaseStsMember.d.ts +2 -0
- package/dist/server/projectPostPurchaseProduct.d.ts +1 -0
- package/dist/server/shared.d.ts +2 -2
- package/dist/transforms/index.cjs +3 -0
- package/dist/transforms/index.mjs +3 -0
- package/dist/transforms/postPurchase/productCardSettings.d.ts +1 -0
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -310,6 +310,10 @@ var OfferProductVariant = z3.object({
|
|
|
310
310
|
price: z3.string(),
|
|
311
311
|
title: z3.string()
|
|
312
312
|
});
|
|
313
|
+
var OfferProductReviews = z3.object({
|
|
314
|
+
rating: z3.number().positive().max(5),
|
|
315
|
+
reviewCount: z3.number().int().nonnegative()
|
|
316
|
+
});
|
|
313
317
|
var OfferProduct = z3.object({
|
|
314
318
|
description: z3.string().optional(),
|
|
315
319
|
discount: OfferProductDiscount.optional(),
|
|
@@ -325,6 +329,7 @@ var OfferProduct = z3.object({
|
|
|
325
329
|
link: z3.string().optional(),
|
|
326
330
|
metafields: z3.array(z3.object({ key: z3.string(), namespace: z3.string(), value: z3.string() })).optional(),
|
|
327
331
|
options: z3.array(OfferProductOption),
|
|
332
|
+
reviews: OfferProductReviews.nullish().catch(void 0),
|
|
328
333
|
sellingPlanGroups: z3.array(z3.object({ requiresSellingPlan: z3.boolean().optional() })).optional(),
|
|
329
334
|
title: z3.string(),
|
|
330
335
|
/** At least one variant — a zero-variant product renders an offer card with no addable merchandise (no Add CTA). `keepValidProduct` drops it with a `variants` drift signal rather than shipping a dead card. */
|
|
@@ -333,6 +338,27 @@ var OfferProduct = z3.object({
|
|
|
333
338
|
|
|
334
339
|
// src/server/b2bAvailability.ts
|
|
335
340
|
import { z as z4 } from "zod";
|
|
341
|
+
|
|
342
|
+
// src/server/shared.ts
|
|
343
|
+
var REBUY_ENGINE_HOST = "rebuyengine.com";
|
|
344
|
+
var unwrapData = (body) => body && typeof body === "object" && "data" in body ? body.data : body;
|
|
345
|
+
var isEmptyObject = (x) => x == null || typeof x === "object" && !Array.isArray(x) && Object.keys(x).length === 0;
|
|
346
|
+
var UpstreamError = class extends Error {
|
|
347
|
+
constructor(status, body, message) {
|
|
348
|
+
super(message ?? `Upstream returned ${status}`);
|
|
349
|
+
this.body = body;
|
|
350
|
+
this.name = "UpstreamError";
|
|
351
|
+
this.status = status;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
var NotFoundError = class extends Error {
|
|
355
|
+
constructor(message) {
|
|
356
|
+
super(message);
|
|
357
|
+
this.name = "NotFoundError";
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/server/b2bAvailability.ts
|
|
336
362
|
var B2B_AVAILABILITY_PATH = "/admin/api/v1/shop/b2b-catalog-availability";
|
|
337
363
|
var productGid = (product) => `gid://shopify/Product/${String(product.id)}`;
|
|
338
364
|
var Verdict = z4.record(z4.string(), z4.boolean());
|
|
@@ -340,9 +366,18 @@ var B2bAvailabilityResponse = z4.object({
|
|
|
340
366
|
availability: Verdict.optional(),
|
|
341
367
|
data: z4.object({ availability: Verdict.optional() }).optional()
|
|
342
368
|
}).transform(({ availability, data }) => availability ?? data?.availability ?? null);
|
|
369
|
+
var MAX_REASON_LENGTH = 120;
|
|
370
|
+
var failureReason = (error) => {
|
|
371
|
+
if (!(error instanceof Error)) return "unknown error";
|
|
372
|
+
const cut = error.message.indexOf(": ");
|
|
373
|
+
const label2 = (cut === -1 ? error.message : error.message.slice(0, cut)).slice(0, MAX_REASON_LENGTH);
|
|
374
|
+
if (!(error instanceof UpstreamError)) return label2 || error.name;
|
|
375
|
+
return label2 && label2 !== `Upstream returned ${error.status}` ? `upstream ${error.status}: ${label2}` : `upstream ${error.status}`;
|
|
376
|
+
};
|
|
343
377
|
var filterByB2bAvailability = async (products, input, ctx) => {
|
|
344
378
|
if (!input.companyLocationId || products.length === 0) return products;
|
|
345
379
|
let verdict;
|
|
380
|
+
let reason = "unreadable verdict";
|
|
346
381
|
try {
|
|
347
382
|
const response = await ctx.fetchUpstream(
|
|
348
383
|
B2B_AVAILABILITY_PATH,
|
|
@@ -361,12 +396,13 @@ var filterByB2bAvailability = async (products, input, ctx) => {
|
|
|
361
396
|
);
|
|
362
397
|
const parsed = B2bAvailabilityResponse.safeParse(response);
|
|
363
398
|
verdict = parsed.success ? parsed.data : null;
|
|
364
|
-
} catch {
|
|
399
|
+
} catch (error) {
|
|
365
400
|
verdict = null;
|
|
401
|
+
reason = failureReason(error);
|
|
366
402
|
}
|
|
367
403
|
if (verdict === null) {
|
|
368
404
|
ctx.reportDrift?.({
|
|
369
|
-
detail:
|
|
405
|
+
detail: `b2b availability check failed (${reason}) \u2014 products served unfiltered`,
|
|
370
406
|
kind: "b2bAvailability"
|
|
371
407
|
});
|
|
372
408
|
return products;
|
|
@@ -374,265 +410,48 @@ var filterByB2bAvailability = async (products, input, ctx) => {
|
|
|
374
410
|
return products.filter((product) => verdict[productGid(product)] !== false);
|
|
375
411
|
};
|
|
376
412
|
|
|
377
|
-
// src/server/
|
|
378
|
-
|
|
379
|
-
var unwrapData = (body) => body && typeof body === "object" && "data" in body ? body.data : body;
|
|
380
|
-
var isEmptyObject = (x) => x == null || typeof x === "object" && !Array.isArray(x) && Object.keys(x).length === 0;
|
|
381
|
-
var UpstreamError = class extends Error {
|
|
382
|
-
constructor(status, body, message) {
|
|
383
|
-
super(message ?? `Upstream returned ${status}`);
|
|
384
|
-
this.body = body;
|
|
385
|
-
this.name = "UpstreamError";
|
|
386
|
-
this.status = status;
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
var NotFoundError = class extends Error {
|
|
390
|
-
constructor(message) {
|
|
391
|
-
super(message);
|
|
392
|
-
this.name = "NotFoundError";
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// src/server/shopSubtotal.ts
|
|
397
|
-
var shopCurrencyRate = (cart) => {
|
|
398
|
-
const rate = cart.currencyRate ?? 1;
|
|
399
|
-
return Number.isFinite(rate) && rate > 0 ? rate : 1;
|
|
400
|
-
};
|
|
401
|
-
var shopSubtotalCents = (cart) => Math.round((cart.subtotal - (cart.discountTotal ?? 0)) / shopCurrencyRate(cart));
|
|
402
|
-
var toShopCents = (majorPresentment, rate) => Math.round(majorPresentment * 100 / rate);
|
|
413
|
+
// src/server/postPurchaseReviewData.ts
|
|
414
|
+
import { z as z17 } from "zod";
|
|
403
415
|
|
|
404
|
-
// src/schema/
|
|
405
|
-
import { z as
|
|
416
|
+
// src/schema/postPurchaseProductReviews.ts
|
|
417
|
+
import { z as z12 } from "zod";
|
|
406
418
|
|
|
407
|
-
// src/schema/
|
|
408
|
-
|
|
419
|
+
// src/schema/widgets/post-purchase-offer/postPurchaseSettings.ts
|
|
420
|
+
import { z as z11 } from "zod";
|
|
409
421
|
|
|
410
|
-
// src/schema/
|
|
422
|
+
// src/schema/widgets/post-purchase-offer/common.ts
|
|
423
|
+
import { replace, toNumber, toString } from "es-toolkit/compat";
|
|
411
424
|
import { z as z5 } from "zod";
|
|
412
|
-
var
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
variantIds: z5.array(z5.coerce.number()).optional()
|
|
418
|
-
});
|
|
419
|
-
var SmartCartTierExclusion = z5.looseObject({
|
|
420
|
-
collectionIds: z5.array(z5.coerce.number()).optional().catch(void 0),
|
|
421
|
-
productIds: z5.array(z5.coerce.number()).optional().catch(void 0),
|
|
422
|
-
tags: z5.array(z5.string()).optional().catch(void 0),
|
|
423
|
-
type: z5.string().optional().catch(void 0)
|
|
424
|
-
});
|
|
425
|
-
var SmartCartTier = z5.looseObject({
|
|
426
|
-
allProducts: z5.array(SmartCartTierProduct).optional(),
|
|
427
|
-
/** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
|
|
428
|
-
customTierLabel: z5.boolean().optional(),
|
|
429
|
-
customTierLabelText: z5.string().optional(),
|
|
430
|
-
decorativeAmountReachedLabel: z5.boolean().optional(),
|
|
431
|
-
decorativeAmountReachedLabelText: z5.string().optional(),
|
|
432
|
-
decorativeAmountRemainingLabel: z5.boolean().optional(),
|
|
433
|
-
decorativeAmountRemainingLabelText: z5.string().optional(),
|
|
434
|
-
/** Order-discount reward: `'fixedAmount'` (shop-currency dollars) or `'percentage'` (whole percent). */
|
|
435
|
-
discountAmountFromTotal: z5.coerce.number().optional(),
|
|
436
|
-
discountAmountReachedLabel: z5.boolean().optional(),
|
|
437
|
-
discountAmountReachedLabelText: z5.string().optional(),
|
|
438
|
-
discountAmountRemainingLabel: z5.boolean().optional(),
|
|
439
|
-
discountAmountRemainingLabelText: z5.string().optional(),
|
|
440
|
-
discountType: z5.string().optional(),
|
|
441
|
-
/**
|
|
442
|
-
* onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
|
|
443
|
-
* `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
|
|
444
|
-
* one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
|
|
445
|
-
*
|
|
446
|
-
* `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
|
|
447
|
-
* (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
|
|
448
|
-
* `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
|
|
449
|
-
* types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
|
|
450
|
-
* A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
|
|
451
|
-
*/
|
|
452
|
-
exclusions: z5.array(SmartCartTierExclusion).optional().catch(void 0),
|
|
453
|
-
exclusionsEnabled: z5.boolean().optional().catch(void 0),
|
|
454
|
-
filterOosVariants: z5.boolean().optional(),
|
|
455
|
-
id: z5.string().optional(),
|
|
456
|
-
/**
|
|
457
|
-
* A `null`/absent minimum stays `undefined` (NOT coerced to `0`): a threshold-0 tier is always reached
|
|
458
|
-
* and would auto-add its gift to every cart. onsite's `isTierDataValid` drops a non-numeric minimum for
|
|
459
|
-
* exactly this reason; the converter's `renderableTiers` mirrors that (`Number.isFinite`). A literal `0`
|
|
460
|
-
* is kept — onsite keeps it too. DELIBERATE DIVERGENCE from onsite for string minimums: onsite's
|
|
461
|
-
* `isTierDataValid` is a strict `typeof === 'number'`, so it drops `'50'`; the converter instead coerces
|
|
462
|
-
* `'50' → 50` and keeps the tier (a numeric-string minimum is a valid threshold, not a data defect).
|
|
463
|
-
*/
|
|
464
|
-
minimum: z5.preprocess((value) => value == null ? void 0 : value, z5.coerce.number().optional()),
|
|
465
|
-
presentmentCurrencies: z5.looseObject({ enabled: z5.boolean().optional() }).optional(),
|
|
466
|
-
productAmountReachedLabel: z5.boolean().optional(),
|
|
467
|
-
productAmountReachedLabelText: z5.string().optional(),
|
|
468
|
-
productAmountRemainingLabel: z5.boolean().optional(),
|
|
469
|
-
productAmountRemainingLabelText: z5.string().optional(),
|
|
470
|
-
products: z5.array(SmartCartTierProduct).optional(),
|
|
471
|
-
/**
|
|
472
|
-
* onsite `product_tier_element_size`: the step-row icon's square size as a px STRING (`'18px'` …
|
|
473
|
-
* `'36px'` — the admin's five-value Size control, default `'18px'`). React stamps it onto the icon
|
|
474
|
-
* SVG's `width`/`height` (`getProgressStepIconSize`), so the milestone renders at the merchant's
|
|
475
|
-
* chosen size; the converter turns it into `CABProgressTier.iconSizePx` for the CAB renderer.
|
|
476
|
-
*
|
|
477
|
-
* `.catch(undefined)` for the same blast-radius reason as `productTierImage` below: this key was
|
|
478
|
-
* previously unknown-and-ignored by the `looseObject`, and `SmartCartConfig` is parsed
|
|
479
|
-
* `.nullish().catch(undefined)` with no intermediate catch, so one drifted value (a bare number, a
|
|
480
|
-
* `'2rem'`) would drop EVERY progress bar for the shop rather than just this setting. Failing open to
|
|
481
|
-
* `undefined` lands on the documented 18px default — strictly safer than that blast radius. The
|
|
482
|
-
* converter parses the number out and clamps it, so a junk-but-string value degrades the same way.
|
|
483
|
-
*/
|
|
484
|
-
productTierElementSize: z5.string().optional().catch(void 0),
|
|
485
|
-
/**
|
|
486
|
-
* onsite `product_tier_image`: on a product (gift) tier, show the gift's product image in the step-row
|
|
487
|
-
* slot instead of the generic bag icon (React `TierProgressIcon` swaps in the image when this is set and
|
|
488
|
-
* an image resolves). Per-tier; the client resolves the image (config carries only product IDs).
|
|
489
|
-
*
|
|
490
|
-
* `.catch(undefined)` is load-bearing. This key was previously unknown-and-ignored by the `looseObject`;
|
|
491
|
-
* declaring it as a real boolean means a stored non-boolean (`1` / `"1"` / `"true"`) would now FAIL the
|
|
492
|
-
* tier — and since `SmartCartConfig` is parsed `.nullish().catch(undefined)` (userConfig/cabShopConfig)
|
|
493
|
-
* with no intermediate catch, one drifted flag would drop EVERY progress bar for the shop, not just the
|
|
494
|
-
* flag. The converter only reads it for truthiness (`gift && tier.productTierImage`), so failing open to
|
|
495
|
-
* `undefined` (bag-icon fallback, the documented default) is strictly safer than that blast radius.
|
|
496
|
-
*/
|
|
497
|
-
productTierImage: z5.boolean().optional().catch(void 0),
|
|
498
|
-
type: z5.string().optional(),
|
|
499
|
-
/**
|
|
500
|
-
* onsite's PER-TIER unit switch (`unlock_condition_type`): `'item_quantity'` makes `minimum` an
|
|
501
|
-
* item count (checked before any money handling — no ×100, no FX, no presentment overrides,
|
|
502
|
-
* mirroring onsite `getTierMinimum`); anything else (or absent) keeps the money semantics.
|
|
503
|
-
*/
|
|
504
|
-
unlockConditionType: z5.string().optional()
|
|
505
|
-
});
|
|
506
|
-
var SmartCartBar = z5.looseObject({
|
|
507
|
-
countryCodes: z5.array(z5.string()).optional(),
|
|
508
|
-
/**
|
|
509
|
-
* onsite `exclude_discount_in_subtotal`: measure progress against the PRE-discount subtotal so an
|
|
510
|
-
* applied code/order discount doesn't lower progress. The client adds cart discount allocations back.
|
|
511
|
-
*/
|
|
512
|
-
excludeDiscountInSubtotal: z5.boolean().optional(),
|
|
513
|
-
/** Products excluded from the progress subtotal; the client subtracts their variants' cart cost. */
|
|
514
|
-
excludedProducts: z5.array(z5.looseObject({ variantIds: z5.array(z5.coerce.number()).optional() })).optional(),
|
|
515
|
-
freeShippingOnSubscription: z5.boolean().optional(),
|
|
516
|
-
geolocationEnabled: z5.boolean().optional(),
|
|
517
|
-
id: z5.string().optional(),
|
|
518
|
-
language: z5.looseObject({
|
|
519
|
-
freeShippingReached: z5.string().optional(),
|
|
520
|
-
freeShippingRemaining: z5.string().optional(),
|
|
521
|
-
giftAreaTitle: z5.string().optional()
|
|
522
|
-
}).optional(),
|
|
523
|
-
name: z5.string().optional(),
|
|
524
|
-
tiers: z5.array(SmartCartTier).optional()
|
|
525
|
-
});
|
|
526
|
-
var SmartCartComponent = z5.looseObject({
|
|
527
|
-
settings: z5.looseObject({ bars: z5.array(SmartCartBar).optional() }).optional(),
|
|
528
|
-
type: z5.string().optional()
|
|
529
|
-
});
|
|
530
|
-
var SmartCartConfig = z5.looseObject({
|
|
531
|
-
components: z5.array(SmartCartComponent).optional()
|
|
532
|
-
});
|
|
425
|
+
var strictNumber = z5.union([
|
|
426
|
+
z5.number(),
|
|
427
|
+
z5.string().transform((value) => toNumber(replace(toString(value), /[^0-9.]/g, "")) || 0),
|
|
428
|
+
z5.unknown().transform(() => 0)
|
|
429
|
+
]);
|
|
533
430
|
|
|
534
|
-
// src/schema/
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
var
|
|
548
|
-
/**
|
|
549
|
-
* Per-element resilient: parse as `unknown[]` (non-array → `[]`), then keep only the experiments that
|
|
550
|
-
* validate. A single drifted experiment drops itself instead of `.catch`-ing the WHOLE array to `[]` and
|
|
551
|
-
* silently disabling every live A/B test fleet-wide.
|
|
552
|
-
*/
|
|
553
|
-
activeExperiments: z6.array(z6.unknown()).catch([]).default([]).transform(
|
|
554
|
-
(experiments) => experiments.flatMap((experiment) => {
|
|
555
|
-
const result = CabActiveExperiment.safeParse(experiment);
|
|
556
|
-
return result.success ? [result.data] : [];
|
|
557
|
-
})
|
|
558
|
-
),
|
|
559
|
-
/**
|
|
560
|
-
* Only the two fields the smart-cart entitlement gate reads (`shortName`/`isActive`). Per-element
|
|
561
|
-
* resilient like `activeExperiments`: a drifted package entry degrades to an inert `''`/false one that
|
|
562
|
-
* matches nothing, so a real active `cart-merchandising` package still gates correctly and a sibling
|
|
563
|
-
* drift never fails the shared config parse. Note the salvage is fail-CLOSED, not fail-open, for an
|
|
564
|
-
* entitled v6 shop: a malformed whole array (⇒ `[]`) or an inerted `cart-merchandising` entry (e.g.
|
|
565
|
-
* `is_active: 1` ⇒ inactive) makes the gate find no active package and drop every bar. That matches
|
|
566
|
-
* React's "no proof of entitlement ⇒ render null", but it silently removes bars — so the server gate
|
|
567
|
-
* ({@link file://../server/widgetSettings.ts} `fetchProgressBarContext`) emits a `shopConfig` drift
|
|
568
|
-
* when it sees the inert-entry sentinel (`shortName === ''`). The whole-array `[]` case leaves no
|
|
569
|
-
* sentinel and is not separately signalable here.
|
|
570
|
-
*/
|
|
571
|
-
activePackages: z6.array(z6.object({ isActive: z6.boolean(), shortName: z6.string() }).catch({ isActive: false, shortName: "" })).catch([]).default([]),
|
|
572
|
-
apiKey: z6.string(),
|
|
573
|
-
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
574
|
-
billingVersion: z6.string().nullable().catch(null).default(null),
|
|
575
|
-
/**
|
|
576
|
-
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
577
|
-
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
578
|
-
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
579
|
-
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
580
|
-
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
581
|
-
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
582
|
-
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
583
|
-
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
584
|
-
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
585
|
-
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
586
|
-
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
587
|
-
*/
|
|
588
|
-
cacheKey: z6.union([z6.number(), z6.string()]).transform((value) => Number(value)).pipe(z6.number().int().positive()).optional().catch(void 0),
|
|
589
|
-
/**
|
|
590
|
-
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
591
|
-
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
592
|
-
* converts to the buyer's currency exactly in a cross-decimal market. `.catch()` like its siblings: a
|
|
593
|
-
* missing/renamed currency degrades to "unknown" (client falls back to buyer decimals), never a parse fail.
|
|
594
|
-
*/
|
|
595
|
-
currency: z6.string().optional().catch(void 0),
|
|
596
|
-
/**
|
|
597
|
-
* The merchant's "Use Markets in Data Sources" toggle. The engine (CI2) never reads it on the serve
|
|
598
|
-
* path, so the proxy honouring it before sending market context is what makes the setting mean
|
|
599
|
-
* anything (REB-21688). Fail-CLOSED, unlike the fail-open siblings: a missing/null/drifted field reads
|
|
600
|
-
* as markets OFF — market context silently dropped (engine serves shop-default market), never sent
|
|
601
|
-
* against the merchant's setting. The OFF-reading salvages stay distinguishable: missing AND null are
|
|
602
|
-
* the engine's normal absent vocabulary (`.nullish()`, like `monetize`/`billingVersion`) and read as
|
|
603
|
-
* the plain default — not drift — while only a drifted SHAPE `.catch`es, to the {@link DRIFTED_MARKETS}
|
|
604
|
-
* identity sentinel, so `fetchUserConfig` can emit a `shopConfig` drift for the one case that means an
|
|
605
|
-
* engine rename/retype; otherwise a markets-enabled merchant degrading to shop-default pricing would
|
|
606
|
-
* be indistinguishable from markets genuinely off. The missing-key case is EXPLICIT via `.default()`
|
|
607
|
-
* (a factory, so parses never share a mutable object) rather than inferred from zod running an
|
|
608
|
-
* optional key's transform on `undefined`; `null` flows past the default through `.nullish()` into the
|
|
609
|
-
* transform. `Readonly` on the output type because the drifted salvage IS frozen — an assignment to
|
|
610
|
-
* `markets.enabled` downstream would throw only for drifted shops in production, so surface direct
|
|
611
|
-
* writes to tsc (a guard for the direct case only: TS doesn't check `readonly` in assignability, so
|
|
612
|
-
* laundering through a mutable-shaped alias still compiles).
|
|
613
|
-
* The inner `enabled` stays strict on purpose: `{ enabled: null }` is a genuine retype and drifting is
|
|
614
|
-
* the correct answer (the editor-path `ShopConfig.markets` would hard-fail the same payload).
|
|
615
|
-
*/
|
|
616
|
-
markets: z6.object({ enabled: z6.boolean() }).nullish().catch(DRIFTED_MARKETS).default(() => ({ enabled: false })).transform((markets) => markets ?? { enabled: false }),
|
|
617
|
-
monetize: z6.object({ publisherKey: z6.string().nullable() }).nullish().catch(void 0),
|
|
618
|
-
shopId: z6.number().optional().catch(void 0),
|
|
619
|
-
/**
|
|
620
|
-
* The merchant's display name ("Gift Basket Store") — the Monetize header copy reads it.
|
|
621
|
-
* `.catch()` like its siblings: a missing/drifted name degrades the header to the
|
|
622
|
-
* myshopify-domain slug (the pre-CAB React fallback), never a parse fail. An empty string is a
|
|
623
|
-
* VALID parse — the slug fallback for `''` (and whitespace/tag-only names) lives in
|
|
624
|
-
* composeOffer's `||` chain, not here.
|
|
625
|
-
*/
|
|
626
|
-
shopName: z6.string().nullish().catch(void 0)
|
|
627
|
-
});
|
|
628
|
-
var CabUserConfig = z6.object({
|
|
629
|
-
shop: CabShopConfig,
|
|
630
|
-
smartCart: SmartCartConfig.nullish().catch(void 0)
|
|
631
|
-
});
|
|
431
|
+
// src/schema/widgets/post-purchase-offer/constants.ts
|
|
432
|
+
import { z as z6 } from "zod";
|
|
433
|
+
var blockTypes = [
|
|
434
|
+
"callout_banner",
|
|
435
|
+
"image",
|
|
436
|
+
"image_content",
|
|
437
|
+
"pay_button",
|
|
438
|
+
"product_information",
|
|
439
|
+
"separator",
|
|
440
|
+
"text",
|
|
441
|
+
"timer"
|
|
442
|
+
];
|
|
443
|
+
var blockType = z6.enum(blockTypes);
|
|
444
|
+
var BlockType = blockType.enum;
|
|
632
445
|
|
|
633
|
-
// src/schema/widgets/
|
|
634
|
-
import {
|
|
635
|
-
import {
|
|
446
|
+
// src/schema/widgets/post-purchase-offer/postPurchaseBlocks.ts
|
|
447
|
+
import { get as get2, isEqual } from "es-toolkit/compat";
|
|
448
|
+
import { v4 as uuidv4 } from "uuid";
|
|
449
|
+
import { z as z10 } from "zod";
|
|
450
|
+
|
|
451
|
+
// src/schema/widgets/checkout-and-beyond/text.ts
|
|
452
|
+
import { forEach, isString as isString2 } from "es-toolkit/compat";
|
|
453
|
+
import { v7 as uuidv7 } from "uuid";
|
|
454
|
+
import { z as z9 } from "zod";
|
|
636
455
|
|
|
637
456
|
// src/schema/widgets/checkout-and-beyond/common.ts
|
|
638
457
|
var freezeEnum = (values2) => Object.freeze(Object.fromEntries(values2.map((v) => [v, v])));
|
|
@@ -786,6 +605,7 @@ var sectionTypes = [
|
|
|
786
605
|
"variants"
|
|
787
606
|
];
|
|
788
607
|
var SectionType = freezeEnum(sectionTypes);
|
|
608
|
+
var SALVAGED_SECTION_TYPE = "unknown";
|
|
789
609
|
var spacings = ["none", "extraTight", "tight", "base", "loose", "extraLoose"];
|
|
790
610
|
var Spacing = freezeEnum(spacings);
|
|
791
611
|
var textAlignments = ["start", "center", "end"];
|
|
@@ -836,6 +656,1064 @@ var widgetTypes = [
|
|
|
836
656
|
];
|
|
837
657
|
var WidgetType = freezeEnum(widgetTypes);
|
|
838
658
|
|
|
659
|
+
// src/schema/widgets/checkout-and-beyond/regex.ts
|
|
660
|
+
var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
|
|
661
|
+
var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
662
|
+
var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
|
|
663
|
+
var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
|
|
664
|
+
|
|
665
|
+
// src/schema/widgets/checkout-and-beyond/utils.ts
|
|
666
|
+
import { isPlainObject as isPlainObject2 } from "es-toolkit";
|
|
667
|
+
import { get, isString } from "es-toolkit/compat";
|
|
668
|
+
|
|
669
|
+
// src/transforms/convertNumericObjects.ts
|
|
670
|
+
import { isPlainObject } from "es-toolkit";
|
|
671
|
+
var ARRAY_INDEX_STRING = /^(?:0|[1-9]\d*)$/;
|
|
672
|
+
var hasOnlyNumericKeys = (input, requireCompleteRun) => {
|
|
673
|
+
const keys = Object.keys(input);
|
|
674
|
+
if (keys.length === 0) return false;
|
|
675
|
+
if (!keys.every((key) => ARRAY_INDEX_STRING.test(key) && Number.isSafeInteger(Number(key)))) return false;
|
|
676
|
+
if (!requireCompleteRun) return true;
|
|
677
|
+
const sorted = keys.map(Number).sort((a, b) => a - b);
|
|
678
|
+
return sorted.every((n, i) => n === i);
|
|
679
|
+
};
|
|
680
|
+
var numericObjectToArray = (input) => Object.entries(input).sort(([a], [b]) => Number(a) - Number(b)).map(([, value]) => value);
|
|
681
|
+
var convert = (input, requireCompleteRun) => {
|
|
682
|
+
if (Array.isArray(input)) return input.map((item) => convert(item, requireCompleteRun));
|
|
683
|
+
if (isPlainObject(input)) {
|
|
684
|
+
if (hasOnlyNumericKeys(input, requireCompleteRun)) {
|
|
685
|
+
return numericObjectToArray(input).map((item) => convert(item, requireCompleteRun));
|
|
686
|
+
}
|
|
687
|
+
const out = {};
|
|
688
|
+
for (const [key, value] of Object.entries(input)) out[key] = convert(value, requireCompleteRun);
|
|
689
|
+
return out;
|
|
690
|
+
}
|
|
691
|
+
return input;
|
|
692
|
+
};
|
|
693
|
+
var convertNumericObjects = (input) => convert(input, true);
|
|
694
|
+
|
|
695
|
+
// src/schema/widgets/checkout-and-beyond/utils.ts
|
|
696
|
+
var isHTML = (html = "") => {
|
|
697
|
+
if (typeof window !== "undefined") {
|
|
698
|
+
const parser = new DOMParser();
|
|
699
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
700
|
+
const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
|
|
701
|
+
return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
|
|
702
|
+
}
|
|
703
|
+
return html.search(HTML_TAGS_REGEX) !== -1;
|
|
704
|
+
};
|
|
705
|
+
var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text: text2 }) => isHTML(text2)));
|
|
706
|
+
var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject2(input) && hasHTMLInDoc(input));
|
|
707
|
+
var NO_HTML = { message: "HTML is not supported" };
|
|
708
|
+
var isSectionType = (sectionType) => (section) => get(section, "sectionType") === sectionType;
|
|
709
|
+
var isBannerType = isSectionType(SectionType.banner);
|
|
710
|
+
var isButtonType = isSectionType(SectionType.button);
|
|
711
|
+
var isCarouselType = isSectionType(SectionType.carousel);
|
|
712
|
+
var isCartLineType = isSectionType(SectionType.cartLine);
|
|
713
|
+
var isDataSourceType = isSectionType(
|
|
714
|
+
SectionType.dataSource
|
|
715
|
+
);
|
|
716
|
+
var isDiscountType = isSectionType(SectionType.discount);
|
|
717
|
+
var isImageType = isSectionType(SectionType.image);
|
|
718
|
+
var isLayoutType = isSectionType(SectionType.layout);
|
|
719
|
+
var isMonetizeType = isSectionType(SectionType.monetize);
|
|
720
|
+
var isOffersType = isSectionType(SectionType.offers);
|
|
721
|
+
var isProductType = isSectionType(SectionType.product);
|
|
722
|
+
var isProductsType = isSectionType(SectionType.products);
|
|
723
|
+
var isProgressBarType = isSectionType(
|
|
724
|
+
SectionType.progressBar
|
|
725
|
+
);
|
|
726
|
+
var isQuantityType = isSectionType(SectionType.quantity);
|
|
727
|
+
var isReviewsType = isSectionType(SectionType.reviews);
|
|
728
|
+
var isTextType = isSectionType(SectionType.text);
|
|
729
|
+
var isVariantsType = isSectionType(SectionType.variants);
|
|
730
|
+
|
|
731
|
+
// src/schema/widgets/checkout-and-beyond/variables.ts
|
|
732
|
+
import { z as z8 } from "zod";
|
|
733
|
+
|
|
734
|
+
// src/schema/widgets/checkout-and-beyond/rule.ts
|
|
735
|
+
import { z as z7 } from "zod";
|
|
736
|
+
var ruleTypes = [
|
|
737
|
+
"cart",
|
|
738
|
+
"cart_count",
|
|
739
|
+
"cart_item_count",
|
|
740
|
+
"cart_line_count",
|
|
741
|
+
"cart_subtotal",
|
|
742
|
+
"collection",
|
|
743
|
+
"customer",
|
|
744
|
+
"customer_order_count",
|
|
745
|
+
"customer_tag",
|
|
746
|
+
"customer_total_spent",
|
|
747
|
+
"date",
|
|
748
|
+
"product",
|
|
749
|
+
"product_tag",
|
|
750
|
+
"product_title",
|
|
751
|
+
"product_type",
|
|
752
|
+
"product_vendor"
|
|
753
|
+
];
|
|
754
|
+
var ruleType = z7.enum(ruleTypes);
|
|
755
|
+
var RuleType = ruleType.enum;
|
|
756
|
+
var ruleOperators = [
|
|
757
|
+
"after",
|
|
758
|
+
"anything",
|
|
759
|
+
"before",
|
|
760
|
+
"contains",
|
|
761
|
+
"contains_any",
|
|
762
|
+
"does_not_contain",
|
|
763
|
+
"does_not_contain_any",
|
|
764
|
+
"equals",
|
|
765
|
+
"does_not_equal",
|
|
766
|
+
"greater_than",
|
|
767
|
+
"less_than"
|
|
768
|
+
];
|
|
769
|
+
var ruleOperator = z7.enum(ruleOperators);
|
|
770
|
+
var RuleOperator = ruleOperator.enum;
|
|
771
|
+
var RuleCondition = z7.object({
|
|
772
|
+
operator: z7.enum(ruleOperators).default(RuleOperator.anything),
|
|
773
|
+
products: z7.union([z7.array(z7.unknown()), z7.strictObject({}).transform(() => [])]).default(() => []),
|
|
774
|
+
type: z7.enum(ruleTypes).default(RuleType.cart_subtotal),
|
|
775
|
+
value: z7.coerce.string().default("")
|
|
776
|
+
});
|
|
777
|
+
var RuleLogicBlock = z7.object({
|
|
778
|
+
rules: z7.array(RuleCondition).default(() => [])
|
|
779
|
+
});
|
|
780
|
+
var CABRulesetEntry = z7.object({
|
|
781
|
+
exitIfMatched: z7.boolean().default(true),
|
|
782
|
+
logic: z7.array(RuleLogicBlock).default(() => []),
|
|
783
|
+
output: z7.array(
|
|
784
|
+
z7.object({
|
|
785
|
+
key: z7.string().default("should_show"),
|
|
786
|
+
type: z7.literal("data_json").default("data_json"),
|
|
787
|
+
value: z7.string().default("true")
|
|
788
|
+
})
|
|
789
|
+
).default(() => [{ key: "should_show", type: "data_json", value: "true" }])
|
|
790
|
+
});
|
|
791
|
+
var CABRuleset = z7.union([z7.array(CABRulesetEntry), z7.strictObject({}).transform(() => [])]).default(() => []);
|
|
792
|
+
|
|
793
|
+
// src/schema/widgets/checkout-and-beyond/variables.ts
|
|
794
|
+
var variableEffect = z8.enum(variableEffects);
|
|
795
|
+
var CABVariableSeed = z8.object({
|
|
796
|
+
dataSourceId: z8.number().nullable().default(null),
|
|
797
|
+
ruleset: CABRuleset
|
|
798
|
+
});
|
|
799
|
+
var CABBooleanVariable = z8.object({
|
|
800
|
+
/**
|
|
801
|
+
* Holds until the first seed verdict lands (and forever, for unseeded variables). Omitted, it derives
|
|
802
|
+
* from seededness — `false` with a `seed` (visibility is EARNED), `true` without — via the transform
|
|
803
|
+
* below; a cross-field default has no `.default()` spelling. An explicit value always wins.
|
|
804
|
+
*/
|
|
805
|
+
defaultValue: z8.boolean().optional(),
|
|
806
|
+
name: z8.string().min(1),
|
|
807
|
+
seed: CABVariableSeed.optional(),
|
|
808
|
+
/** Defaulted so every stored declaration (all boolean before `text` existed) parses unchanged. */
|
|
809
|
+
type: z8.literal(VariableType.boolean).default(VariableType.boolean)
|
|
810
|
+
});
|
|
811
|
+
var CABTextVariable = z8.object({
|
|
812
|
+
defaultValue: z8.string(),
|
|
813
|
+
name: z8.string().min(1),
|
|
814
|
+
seed: z8.never().optional(),
|
|
815
|
+
type: z8.literal(VariableType.text)
|
|
816
|
+
});
|
|
817
|
+
var CABNumberVariable = z8.object({
|
|
818
|
+
defaultValue: z8.number(),
|
|
819
|
+
name: z8.string().min(1),
|
|
820
|
+
seed: z8.never().optional(),
|
|
821
|
+
type: z8.literal(VariableType.number)
|
|
822
|
+
});
|
|
823
|
+
var CABVariable = z8.union([CABTextVariable, CABNumberVariable, CABBooleanVariable]).transform(
|
|
824
|
+
(variable) => variable.type === VariableType.boolean ? { ...variable, defaultValue: variable.defaultValue ?? variable.seed === void 0 } : variable
|
|
825
|
+
);
|
|
826
|
+
var CABVariableEffect = z8.object({
|
|
827
|
+
effect: variableEffect.default(VariableEffect.toggle),
|
|
828
|
+
variable: z8.string().min(1)
|
|
829
|
+
});
|
|
830
|
+
var CABVisibility = z8.object({
|
|
831
|
+
variable: z8.string().min(1),
|
|
832
|
+
visibleWhen: z8.boolean().default(true)
|
|
833
|
+
});
|
|
834
|
+
var contentOperator = z8.enum(contentOperators);
|
|
835
|
+
var resultCountOperator = z8.enum(resultCountOperators);
|
|
836
|
+
var resultTagOperator = z8.enum(resultTagOperators);
|
|
837
|
+
var mutatorEvent = z8.enum(mutatorEvents);
|
|
838
|
+
var CABContentTrigger = z8.object({
|
|
839
|
+
operator: contentOperator.default(ContentOperator.is_empty),
|
|
840
|
+
type: z8.literal("content")
|
|
841
|
+
});
|
|
842
|
+
var CABResultCountTrigger = z8.object({
|
|
843
|
+
operator: resultCountOperator.default(ResultCountOperator.equals),
|
|
844
|
+
type: z8.literal("result_count"),
|
|
845
|
+
value: z8.number().default(0)
|
|
846
|
+
});
|
|
847
|
+
var CABResultTagTrigger = z8.object({
|
|
848
|
+
operator: resultTagOperator.default(ResultTagOperator.contains),
|
|
849
|
+
type: z8.literal("result_tag"),
|
|
850
|
+
value: z8.string().min(1)
|
|
851
|
+
});
|
|
852
|
+
var CABEventTrigger = z8.object({
|
|
853
|
+
event: mutatorEvent,
|
|
854
|
+
type: z8.literal("event")
|
|
855
|
+
});
|
|
856
|
+
var CABVariableTrigger = z8.discriminatedUnion("type", [
|
|
857
|
+
CABContentTrigger,
|
|
858
|
+
CABEventTrigger,
|
|
859
|
+
CABResultCountTrigger,
|
|
860
|
+
CABResultTagTrigger
|
|
861
|
+
]);
|
|
862
|
+
var CABVariableMutator = z8.object({
|
|
863
|
+
effect: variableEffect.default(VariableEffect.set_false),
|
|
864
|
+
variable: z8.string().min(1),
|
|
865
|
+
when: CABVariableTrigger
|
|
866
|
+
});
|
|
867
|
+
var CABVariableFields = {
|
|
868
|
+
mutators: z8.array(CABVariableMutator).optional(),
|
|
869
|
+
variables: z8.array(CABVariable).refine((variables) => new Set(variables.map(({ name }) => name)).size === variables.length, {
|
|
870
|
+
message: "variable names must be unique within a section"
|
|
871
|
+
}).optional(),
|
|
872
|
+
visibility: CABVisibility.optional()
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
// src/schema/widgets/checkout-and-beyond/text.ts
|
|
876
|
+
var enumOrOmit = (...vals) => z9.string().transform((val) => vals.includes(val) ? val : void 0).optional();
|
|
877
|
+
var TiptapText = z9.object({
|
|
878
|
+
marks: z9.union([
|
|
879
|
+
z9.array(
|
|
880
|
+
z9.discriminatedUnion("type", [
|
|
881
|
+
z9.object({ type: z9.literal("bold") }),
|
|
882
|
+
z9.object({ type: z9.literal("italic") }),
|
|
883
|
+
z9.object({
|
|
884
|
+
attrs: z9.object({
|
|
885
|
+
class: z9.string().nullable(),
|
|
886
|
+
href: z9.union([
|
|
887
|
+
z9.url(),
|
|
888
|
+
z9.literal("#"),
|
|
889
|
+
z9.string().regex(DYNAMIC_TOKEN_REGEX),
|
|
890
|
+
z9.string().regex(ROOT_RELATIVE_PATH_REGEX)
|
|
891
|
+
]),
|
|
892
|
+
rel: z9.string().default("noopener noreferrer nofollow"),
|
|
893
|
+
target: z9.string().default("_blank")
|
|
894
|
+
}),
|
|
895
|
+
type: z9.literal("link")
|
|
896
|
+
}),
|
|
897
|
+
z9.object({ type: z9.literal("strike") }),
|
|
898
|
+
z9.object({
|
|
899
|
+
attrs: z9.object({
|
|
900
|
+
color: enumOrOmit(...textColorNames).default(TextColorName.base),
|
|
901
|
+
fontSize: enumOrOmit(...textSizeNames).default(TextSizeName.base)
|
|
902
|
+
}),
|
|
903
|
+
type: z9.literal("textStyle")
|
|
904
|
+
})
|
|
905
|
+
])
|
|
906
|
+
),
|
|
907
|
+
z9.strictObject({}).transform(() => [])
|
|
908
|
+
]).optional(),
|
|
909
|
+
text: z9.string().default(""),
|
|
910
|
+
type: z9.literal("text").default("text")
|
|
911
|
+
});
|
|
912
|
+
var TiptapParagraph = z9.object({
|
|
913
|
+
attrs: z9.object({
|
|
914
|
+
textAlign: enumOrOmit(...textAlignments)
|
|
915
|
+
}).default({ textAlign: TextAlignment.start }),
|
|
916
|
+
content: z9.union([z9.array(TiptapText), z9.strictObject({}).transform(() => [])]).default(() => []),
|
|
917
|
+
type: z9.literal("paragraph").default("paragraph")
|
|
918
|
+
});
|
|
919
|
+
var TiptapDocument = z9.object({
|
|
920
|
+
attrs: z9.object({
|
|
921
|
+
blockSpacing: enumOrOmit(...spacings)
|
|
922
|
+
}).default({ blockSpacing: Spacing.base }),
|
|
923
|
+
content: z9.union([z9.array(TiptapParagraph), z9.strictObject({}).transform(() => [])]).default(() => [TiptapParagraph.parse({})]),
|
|
924
|
+
type: z9.literal("doc").default("doc")
|
|
925
|
+
});
|
|
926
|
+
var CABTextTier = z9.object({
|
|
927
|
+
threshold: z9.number().int().nonnegative(),
|
|
928
|
+
thresholds: z9.record(z9.string(), z9.number().int().nonnegative()).default(() => ({})),
|
|
929
|
+
tierId: z9.string().optional(),
|
|
930
|
+
tierType: z9.enum(progressTierTypes).default(ProgressTierType.shipping)
|
|
931
|
+
});
|
|
932
|
+
var CABTextSection = z9.object({
|
|
933
|
+
...CABVariableFields,
|
|
934
|
+
buttonField: z9.enum(buttonFields).optional(),
|
|
935
|
+
content: z9.record(z9.string(), z9.union([z9.string(), TiptapDocument]).refine(checkForHTML, NO_HTML).optional()).default({ en: TiptapDocument.parse({}) }).optional(),
|
|
936
|
+
name: z9.string().optional(),
|
|
937
|
+
optionId: z9.number().optional(),
|
|
938
|
+
sectionId: z9.uuid().default(() => uuidv7()),
|
|
939
|
+
sectionType: z9.literal(SectionType.text).default(SectionType.text),
|
|
940
|
+
tier: CABTextTier.optional()
|
|
941
|
+
}).superRefine(({ buttonField, content }, ctx) => {
|
|
942
|
+
if (buttonField === ButtonField.destinationUrl && content) {
|
|
943
|
+
forEach(content, (value, lang) => {
|
|
944
|
+
if (value && isString2(value)) {
|
|
945
|
+
if (DYNAMIC_TOKEN_REGEX.test(value)) return;
|
|
946
|
+
try {
|
|
947
|
+
new URL(value);
|
|
948
|
+
} catch {
|
|
949
|
+
ctx.addIssue({
|
|
950
|
+
code: "custom",
|
|
951
|
+
message: "Invalid URL",
|
|
952
|
+
path: ["content", lang]
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// src/schema/widgets/post-purchase-offer/postPurchaseBlocks.ts
|
|
961
|
+
var TextAlignment2 = z10.union([z10.enum(["leading", "center", "trailing"]), z10.unknown().transform(() => "leading")]);
|
|
962
|
+
var TextSize = z10.union([
|
|
963
|
+
z10.enum(["xsmall", "small", "base", "default", "medium", "large", "xlarge"]),
|
|
964
|
+
z10.unknown().transform(() => "medium")
|
|
965
|
+
]);
|
|
966
|
+
var CalloutBannerBlock = z10.object({
|
|
967
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
968
|
+
name: z10.string().optional(),
|
|
969
|
+
settings: z10.object({
|
|
970
|
+
superTitle: z10.union([z10.string(), TiptapDocument]).default(() => "Limited-time Offer"),
|
|
971
|
+
title: z10.union([z10.string(), TiptapDocument]).default(() => "Add this customer favorite to your order")
|
|
972
|
+
}),
|
|
973
|
+
styles: z10.object({
|
|
974
|
+
superTitleEmphasized: z10.boolean().default(false),
|
|
975
|
+
superTitleSize: TextSize.default("large"),
|
|
976
|
+
textAlignment: TextAlignment2.default("center"),
|
|
977
|
+
titleEmphasized: z10.boolean().default(true),
|
|
978
|
+
titleSize: TextSize.default("large")
|
|
979
|
+
}),
|
|
980
|
+
type: z10.literal(BlockType.callout_banner).default(BlockType.callout_banner)
|
|
981
|
+
});
|
|
982
|
+
var ImageBlock = z10.object({
|
|
983
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
984
|
+
name: z10.string().optional(),
|
|
985
|
+
settings: z10.union([
|
|
986
|
+
z10.object({
|
|
987
|
+
/** This isn't used in the post-purchase offer widget */
|
|
988
|
+
alt: z10.string().optional().default(""),
|
|
989
|
+
image: z10.union([z10.literal(""), z10.url()]).default(""),
|
|
990
|
+
imageSize: z10.number().default(20),
|
|
991
|
+
imageType: z10.enum(["URL", "UPLOAD"]).default("URL")
|
|
992
|
+
}),
|
|
993
|
+
z10.array(z10.never()).transform(() => ({}))
|
|
994
|
+
]),
|
|
995
|
+
type: z10.literal(BlockType.image).default(BlockType.image)
|
|
996
|
+
});
|
|
997
|
+
var ImageContentBlock = z10.object({
|
|
998
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
999
|
+
name: z10.string().optional(),
|
|
1000
|
+
settings: z10.object({
|
|
1001
|
+
alt: z10.string().optional().default(""),
|
|
1002
|
+
description: z10.union([z10.string(), TiptapDocument, z10.null()]).default(
|
|
1003
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
|
1004
|
+
),
|
|
1005
|
+
descriptionSize: TextSize.default("medium"),
|
|
1006
|
+
horizontalAlignment: TextAlignment2.default("leading"),
|
|
1007
|
+
image: z10.union([z10.literal(""), z10.url()]).default(""),
|
|
1008
|
+
imagePlacement: z10.enum(["bottom", "left", "right", "top"]).optional().default("left"),
|
|
1009
|
+
imageSize: z10.number().default(20),
|
|
1010
|
+
superTitle: z10.union([z10.string(), TiptapDocument]).default("Lorem Ipsum!"),
|
|
1011
|
+
superTitleSize: TextSize.default("medium"),
|
|
1012
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1013
|
+
swapContent: z10.boolean().optional().default(false),
|
|
1014
|
+
title: z10.union([z10.string(), TiptapDocument]).nullable().default("Lorem ipsum dolor sit amet"),
|
|
1015
|
+
titleSize: TextSize.default("medium")
|
|
1016
|
+
}),
|
|
1017
|
+
type: z10.literal(BlockType.image_content).default(BlockType.image_content)
|
|
1018
|
+
});
|
|
1019
|
+
var PayButtonBlock = z10.object({
|
|
1020
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
1021
|
+
name: z10.string().optional(),
|
|
1022
|
+
settings: z10.union([
|
|
1023
|
+
z10.object({
|
|
1024
|
+
label: z10.string().default("Pay Now")
|
|
1025
|
+
}),
|
|
1026
|
+
z10.array(z10.never()).transform(() => ({
|
|
1027
|
+
label: "Pay Now"
|
|
1028
|
+
}))
|
|
1029
|
+
]),
|
|
1030
|
+
styles: z10.union([
|
|
1031
|
+
z10.object({
|
|
1032
|
+
buttonWidth: z10.number().min(10).max(100).default(50)
|
|
1033
|
+
}),
|
|
1034
|
+
z10.array(z10.never()).transform(() => ({
|
|
1035
|
+
buttonWidth: 50
|
|
1036
|
+
}))
|
|
1037
|
+
]),
|
|
1038
|
+
type: z10.literal(BlockType.pay_button).default(BlockType.pay_button)
|
|
1039
|
+
});
|
|
1040
|
+
var ProductInformationBlock = z10.object({
|
|
1041
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1042
|
+
hideProductImage: z10.boolean().optional().default(false),
|
|
1043
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
1044
|
+
name: z10.string().optional(),
|
|
1045
|
+
type: z10.literal(BlockType.product_information).default(BlockType.product_information)
|
|
1046
|
+
});
|
|
1047
|
+
var SeparatorBlock = z10.object({
|
|
1048
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
1049
|
+
name: z10.string().optional(),
|
|
1050
|
+
type: z10.literal(BlockType.separator).default(BlockType.separator)
|
|
1051
|
+
});
|
|
1052
|
+
var TextBlock = z10.object({
|
|
1053
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
1054
|
+
name: z10.string().optional(),
|
|
1055
|
+
settings: z10.object({
|
|
1056
|
+
description: z10.union([z10.string(), TiptapDocument]).optional().default(
|
|
1057
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
|
1058
|
+
),
|
|
1059
|
+
/** This isn't used in the post-purchase offer widget */
|
|
1060
|
+
descriptionSize: TextSize.default("medium"),
|
|
1061
|
+
horizontalAlignment: TextAlignment2.default("leading"),
|
|
1062
|
+
superTitle: z10.union([z10.string(), TiptapDocument]).default("Super Title"),
|
|
1063
|
+
/** This isn't used in the post-purchase offer widget */
|
|
1064
|
+
superTitleSize: TextSize.default("medium"),
|
|
1065
|
+
title: z10.union([z10.string(), TiptapDocument]).nullable().default("Lorem ipsum dolor sit amet."),
|
|
1066
|
+
/** This isn't used in the post-purchase offer widget */
|
|
1067
|
+
titleSize: TextSize.default("medium")
|
|
1068
|
+
}),
|
|
1069
|
+
type: z10.literal(BlockType.text).default(BlockType.text)
|
|
1070
|
+
});
|
|
1071
|
+
var TimerBlock = z10.object({
|
|
1072
|
+
id: z10.string().optional().default(() => uuidv4()),
|
|
1073
|
+
name: z10.string().optional(),
|
|
1074
|
+
settings: z10.object({
|
|
1075
|
+
durationMinutes: z10.number().min(0).default(10),
|
|
1076
|
+
durationSeconds: z10.union([z10.number().min(0), z10.unknown().transform(() => 0)]).default(0),
|
|
1077
|
+
endOffer: z10.boolean().default(true),
|
|
1078
|
+
title: z10.union([z10.string(), TiptapDocument]).default("Hurry \u2014 your special offer ends in:")
|
|
1079
|
+
}),
|
|
1080
|
+
styles: z10.object({
|
|
1081
|
+
textAlignment: TextAlignment2.default("center"),
|
|
1082
|
+
timerBackgroundColor: z10.enum(["info", "warning", "success", "critical"]).default("warning"),
|
|
1083
|
+
timerEmphasized: z10.union([z10.boolean(), z10.unknown().transform(() => true)]).default(true),
|
|
1084
|
+
timerSize: TextSize.default("medium"),
|
|
1085
|
+
timerWidth: z10.number().min(10).max(100).optional().default(100),
|
|
1086
|
+
titleEmphasized: z10.union([z10.boolean(), z10.unknown().transform(() => true)]).default(false),
|
|
1087
|
+
titleSize: TextSize.default("medium")
|
|
1088
|
+
}),
|
|
1089
|
+
type: z10.literal(BlockType.timer).default(BlockType.timer)
|
|
1090
|
+
});
|
|
1091
|
+
var PostPurchaseBlock = z10.discriminatedUnion("type", [
|
|
1092
|
+
CalloutBannerBlock,
|
|
1093
|
+
ImageBlock,
|
|
1094
|
+
ImageContentBlock,
|
|
1095
|
+
PayButtonBlock,
|
|
1096
|
+
ProductInformationBlock,
|
|
1097
|
+
SeparatorBlock,
|
|
1098
|
+
TextBlock,
|
|
1099
|
+
TimerBlock
|
|
1100
|
+
]);
|
|
1101
|
+
|
|
1102
|
+
// src/schema/widgets/post-purchase-offer/postPurchaseSettings.ts
|
|
1103
|
+
var PostPurchaseSettings = z11.object({
|
|
1104
|
+
advance: z11.object({ removePartialOrder: z11.boolean().default(true) }).optional().default(() => ({ removePartialOrder: true })),
|
|
1105
|
+
blocks: z11.union([z11.array(PostPurchaseBlock), z11.unknown().transform(() => [])]).default(() => [
|
|
1106
|
+
PostPurchaseBlock.parse({ settings: {}, styles: {}, type: BlockType.timer }),
|
|
1107
|
+
PostPurchaseBlock.parse({ settings: {}, styles: {}, type: BlockType.callout_banner }),
|
|
1108
|
+
PostPurchaseBlock.parse({ type: BlockType.product_information })
|
|
1109
|
+
]),
|
|
1110
|
+
columnLayout: z11.boolean().optional(),
|
|
1111
|
+
discount: z11.object({
|
|
1112
|
+
amount: strictNumber.default(30),
|
|
1113
|
+
shouldShowSavingText: z11.boolean().optional().default(true),
|
|
1114
|
+
type: z11.enum(["none", "fixed_amount", "percentage"]).default("none")
|
|
1115
|
+
}),
|
|
1116
|
+
discountedFrom: z11.enum(["price", "compare_at_price"]).optional().default("price"),
|
|
1117
|
+
endpoint: z11.string().optional(),
|
|
1118
|
+
integrations: z11.object({
|
|
1119
|
+
judgeme: z11.boolean().default(false),
|
|
1120
|
+
junip: z11.boolean().optional().default(false),
|
|
1121
|
+
klaviyo: z11.boolean().default(false),
|
|
1122
|
+
loox: z11.boolean().default(false),
|
|
1123
|
+
okendo: z11.boolean().default(false),
|
|
1124
|
+
opinew: z11.boolean().optional().default(false),
|
|
1125
|
+
reviewsio: z11.boolean().optional().default(false),
|
|
1126
|
+
stamped: z11.boolean().default(false),
|
|
1127
|
+
yotpo: z11.boolean().default(false)
|
|
1128
|
+
}),
|
|
1129
|
+
key: z11.string().optional(),
|
|
1130
|
+
language: z11.object({
|
|
1131
|
+
description: z11.union([z11.string(), z11.unknown().transform(() => "")]).default(""),
|
|
1132
|
+
descriptionEnabled: z11.boolean().optional().default(false),
|
|
1133
|
+
descriptionRte: z11.object({
|
|
1134
|
+
content: z11.array(z11.unknown()),
|
|
1135
|
+
type: z11.string()
|
|
1136
|
+
}).optional(),
|
|
1137
|
+
descriptionSpacing: z11.enum(["none", "xtight", "tight", "default", "loose", "xloose"]).optional(),
|
|
1138
|
+
displayCtaPrice: z11.boolean().default(true),
|
|
1139
|
+
global: z11.object({
|
|
1140
|
+
declineAll: z11.string().optional().default("Decline All"),
|
|
1141
|
+
declineThisOffer: z11.string().default("Decline this offer"),
|
|
1142
|
+
free: z11.string().optional().default("Free"),
|
|
1143
|
+
notAvailable: z11.string().optional().default("Not available"),
|
|
1144
|
+
/** Label for the one-time choice beside `subscriptionOption` in the purchase-type selector */
|
|
1145
|
+
oneTimePurchase: z11.string().optional().default("One-time Purchase"),
|
|
1146
|
+
payNow: z11.string().default("Pay Now"),
|
|
1147
|
+
payNowUpgradeToSubscription: z11.string().optional().default("Upgrade to Subscription"),
|
|
1148
|
+
quantity: z11.string().default("Quantity"),
|
|
1149
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1150
|
+
recurringShipping: z11.string().optional().default("Recurring shipping"),
|
|
1151
|
+
recurringSubtotal: z11.string().default("Recurring subtotal"),
|
|
1152
|
+
reviewsPlural: z11.string().optional().default("Reviews"),
|
|
1153
|
+
reviewsSingular: z11.string().optional().default("Review"),
|
|
1154
|
+
save: z11.union([z11.string(), z11.unknown().transform(() => "Save")]).optional().default("Save"),
|
|
1155
|
+
shipping: z11.string().default("Shipping"),
|
|
1156
|
+
subscriptionOption: z11.string().optional().default("Subscribe and Save"),
|
|
1157
|
+
subtotal: z11.string().default("Subtotal"),
|
|
1158
|
+
taxes: z11.string().default("Taxes"),
|
|
1159
|
+
total: z11.string().default("Total")
|
|
1160
|
+
}).optional().default(() => ({
|
|
1161
|
+
declineAll: "Decline All",
|
|
1162
|
+
declineThisOffer: "Decline this offer",
|
|
1163
|
+
free: "Free",
|
|
1164
|
+
notAvailable: "Not available",
|
|
1165
|
+
oneTimePurchase: "One-time Purchase",
|
|
1166
|
+
payNow: "Pay Now",
|
|
1167
|
+
payNowUpgradeToSubscription: "Upgrade to Subscription",
|
|
1168
|
+
quantity: "Quantity",
|
|
1169
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1170
|
+
recurringShipping: "Recurring shipping",
|
|
1171
|
+
recurringSubtotal: "Recurring subtotal",
|
|
1172
|
+
reviewsPlural: "Reviews",
|
|
1173
|
+
reviewsSingular: "Review",
|
|
1174
|
+
save: "Save",
|
|
1175
|
+
shipping: "Shipping",
|
|
1176
|
+
subscriptionOption: "Subscribe and Save",
|
|
1177
|
+
subtotal: "Subtotal",
|
|
1178
|
+
taxes: "Taxes",
|
|
1179
|
+
total: "Total"
|
|
1180
|
+
})),
|
|
1181
|
+
globalTranslation: z11.string().optional().default("english")
|
|
1182
|
+
}),
|
|
1183
|
+
metafields: z11.boolean().default(false),
|
|
1184
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1185
|
+
offer: z11.object({
|
|
1186
|
+
count: z11.number().default(1)
|
|
1187
|
+
}),
|
|
1188
|
+
offersLayout: z11.object({
|
|
1189
|
+
itemsPerPage: z11.number().optional(),
|
|
1190
|
+
type: z11.enum(["list", "grid", "carousel"]).default("list")
|
|
1191
|
+
}).optional().default({
|
|
1192
|
+
type: "list"
|
|
1193
|
+
}),
|
|
1194
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1195
|
+
previewMode: z11.boolean().default(false),
|
|
1196
|
+
productLimit: z11.number().optional().default(1),
|
|
1197
|
+
productOptions: z11.object({
|
|
1198
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1199
|
+
hideOutOfStockVariants: z11.boolean().optional().default(false),
|
|
1200
|
+
matchVariant: z11.boolean().optional().default(true),
|
|
1201
|
+
showDeclineAll: z11.enum(["above", "below", "both", "none"]).optional(),
|
|
1202
|
+
showShipping: z11.boolean().optional().default(true),
|
|
1203
|
+
showSubtotal: z11.boolean().optional().default(true),
|
|
1204
|
+
showTaxes: z11.boolean().optional().default(true),
|
|
1205
|
+
showTotal: z11.boolean().optional().default(true),
|
|
1206
|
+
showVariantSelectors: z11.boolean().optional().default(true),
|
|
1207
|
+
variantSelectorDisplayMode: z11.enum(["separate", "single"]).optional().default("separate")
|
|
1208
|
+
}),
|
|
1209
|
+
productType: z11.union([z11.enum(["one-time", "subscription", "both"]), z11.literal("").transform(() => "one-time")]).default("one-time"),
|
|
1210
|
+
productTypeSelector: z11.enum(["dropdown", "radio"]).optional().default("radio"),
|
|
1211
|
+
quantityInputs: z11.object({
|
|
1212
|
+
defaultQuantity: z11.union([z11.number(), z11.null().transform(() => 1)]).default(1),
|
|
1213
|
+
enabled: z11.boolean().default(true),
|
|
1214
|
+
maxValue: z11.union([z11.number(), z11.null().transform(() => 15)]).default(15),
|
|
1215
|
+
minValue: z11.union([z11.number().min(0), z11.unknown().transform(() => 0)]).default(1)
|
|
1216
|
+
}),
|
|
1217
|
+
shipping: z11.object({
|
|
1218
|
+
amount: strictNumber.default(10),
|
|
1219
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1220
|
+
enabled: z11.boolean().default(true),
|
|
1221
|
+
title: z11.string().optional().default("Shipping Fees"),
|
|
1222
|
+
type: z11.enum(["free", "fixed_amount"]).default("free")
|
|
1223
|
+
}),
|
|
1224
|
+
styles: z11.object({
|
|
1225
|
+
starColor: z11.string().optional().default("#fadb14"),
|
|
1226
|
+
starSize: z11.union([z11.enum(["small", "medium", "large"]), z11.unknown().transform(() => "large")]).optional().default("large"),
|
|
1227
|
+
starStrokeColor: z11.string().optional().default("#fadb14"),
|
|
1228
|
+
strokeWidth: z11.string().optional().default("1")
|
|
1229
|
+
}).optional(),
|
|
1230
|
+
subImages: z11.number().nullish().default(8),
|
|
1231
|
+
subscription: z11.object({
|
|
1232
|
+
discount: z11.object({
|
|
1233
|
+
amount: strictNumber.default(30),
|
|
1234
|
+
shouldShowSavingText: z11.boolean().optional().default(true),
|
|
1235
|
+
type: z11.enum(["none", "fixed_amount", "percentage"]).default("none")
|
|
1236
|
+
}),
|
|
1237
|
+
shipping: z11.object({
|
|
1238
|
+
amount: strictNumber.default(1),
|
|
1239
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1240
|
+
enabled: z11.boolean().default(true),
|
|
1241
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1242
|
+
recurringAmount: strictNumber.default(2),
|
|
1243
|
+
shippingTitle: z11.string().default("Shipping Fees"),
|
|
1244
|
+
type: z11.enum(["free", "fixed_amount"]).default("free")
|
|
1245
|
+
})
|
|
1246
|
+
}).optional().default({
|
|
1247
|
+
discount: {
|
|
1248
|
+
amount: 30,
|
|
1249
|
+
shouldShowSavingText: true,
|
|
1250
|
+
type: "none"
|
|
1251
|
+
},
|
|
1252
|
+
shipping: {
|
|
1253
|
+
amount: 1,
|
|
1254
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1255
|
+
enabled: true,
|
|
1256
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1257
|
+
recurringAmount: 2,
|
|
1258
|
+
shippingTitle: "Shipping Fees",
|
|
1259
|
+
type: "free"
|
|
1260
|
+
}
|
|
1261
|
+
}),
|
|
1262
|
+
subscriptionIsDefault: z11.boolean().optional(),
|
|
1263
|
+
/** This isn't configurable in the admin-nextjs editor */
|
|
1264
|
+
template: z11.string().optional().default(""),
|
|
1265
|
+
type: z11.literal("shopify_post_purchase_extension").default("shopify_post_purchase_extension"),
|
|
1266
|
+
upgradeToSubscription: z11.object({
|
|
1267
|
+
acceptButtonText: z11.string().optional().default("Upgrade"),
|
|
1268
|
+
allow: z11.boolean().optional().default(false),
|
|
1269
|
+
emailRefundNotice: z11.boolean().optional().default(false),
|
|
1270
|
+
errorBannerText: z11.string().optional().default("This subscription cannot be added - limit one per order")
|
|
1271
|
+
}).optional().default(() => ({
|
|
1272
|
+
acceptButtonText: "Upgrade",
|
|
1273
|
+
allow: false,
|
|
1274
|
+
emailRefundNotice: false,
|
|
1275
|
+
errorBannerText: "This subscription cannot be added - limit one per order"
|
|
1276
|
+
}))
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
// src/schema/postPurchaseProductReviews.ts
|
|
1280
|
+
var decimal = z12.union([
|
|
1281
|
+
z12.number(),
|
|
1282
|
+
z12.string().regex(/^\d+(?:\.\d+)?$/).transform(Number)
|
|
1283
|
+
]);
|
|
1284
|
+
var PostPurchaseCommonReviewRating = z12.object({ value: decimal.pipe(z12.number().min(0).max(5)) });
|
|
1285
|
+
var PostPurchaseKlaviyoReviewRating = PostPurchaseCommonReviewRating.extend({
|
|
1286
|
+
// Legacy validates key presence, then displays the raw value on five stars without rescaling.
|
|
1287
|
+
scale_max: z12.unknown().nonoptional(),
|
|
1288
|
+
scale_min: z12.unknown().nonoptional()
|
|
1289
|
+
});
|
|
1290
|
+
var PostPurchaseReviewCount = z12.string().regex(/^\d+(?:\.\d+)?$/).transform(Number).pipe(z12.number().int().nonnegative());
|
|
1291
|
+
var PostPurchaseReviewStyles = PostPurchaseSettings.shape.styles.unwrap().extend({
|
|
1292
|
+
strokeWidth: decimal.pipe(z12.number().nonnegative()).default(1)
|
|
1293
|
+
}).prefault({});
|
|
1294
|
+
var PostPurchaseProductReviews = z12.object({
|
|
1295
|
+
color: z12.string(),
|
|
1296
|
+
rating: z12.number().positive().max(5),
|
|
1297
|
+
reviewCount: z12.number().int().nonnegative(),
|
|
1298
|
+
reviewsPlural: z12.string(),
|
|
1299
|
+
reviewsSingular: z12.string(),
|
|
1300
|
+
size: z12.enum(["small", "medium", "large"]),
|
|
1301
|
+
strokeColor: z12.string(),
|
|
1302
|
+
strokeWidth: z12.number().nonnegative()
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
// src/server/postPurchaseJudgeMeReviews.ts
|
|
1306
|
+
import { z as z14 } from "zod";
|
|
1307
|
+
|
|
1308
|
+
// src/server/postPurchaseReviewFields.ts
|
|
1309
|
+
import { z as z13 } from "zod";
|
|
1310
|
+
var Field = z13.object({ key: z13.string(), namespace: z13.string(), value: z13.unknown().optional() });
|
|
1311
|
+
var reviewFields = (raw) => {
|
|
1312
|
+
const fields4 = z13.array(z13.unknown()).parse(raw).flatMap((field) => {
|
|
1313
|
+
const parsed = Field.safeParse(field);
|
|
1314
|
+
return parsed.success ? [parsed.data] : [];
|
|
1315
|
+
});
|
|
1316
|
+
return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
|
|
1317
|
+
};
|
|
1318
|
+
var ReviewFieldError = class extends Error {
|
|
1319
|
+
constructor(field) {
|
|
1320
|
+
super(`Unreadable review field ${field}`);
|
|
1321
|
+
this.field = field;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
var readReviewField = (field, read) => {
|
|
1325
|
+
try {
|
|
1326
|
+
return read(field.value);
|
|
1327
|
+
} catch {
|
|
1328
|
+
throw new ReviewFieldError(`${field.namespace}.${field.key}`);
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
// src/server/postPurchaseJudgeMeReviews.ts
|
|
1333
|
+
var readJudgeMeReviews = (find) => {
|
|
1334
|
+
const field = find("judgeme", "badge");
|
|
1335
|
+
if (!field) return null;
|
|
1336
|
+
return readReviewField(field, (value) => {
|
|
1337
|
+
const html = z14.string().parse(value);
|
|
1338
|
+
const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
|
|
1339
|
+
const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
|
|
1340
|
+
if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
|
|
1341
|
+
return {
|
|
1342
|
+
rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
|
|
1343
|
+
reviewCount: PostPurchaseReviewCount.parse(count[1])
|
|
1344
|
+
};
|
|
1345
|
+
});
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
// src/server/postPurchaseOkendoReviews.ts
|
|
1349
|
+
import { z as z15 } from "zod";
|
|
1350
|
+
var Summary = z15.object({
|
|
1351
|
+
reviewAverageValue: PostPurchaseCommonReviewRating.shape.value,
|
|
1352
|
+
reviewCount: z15.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
|
|
1353
|
+
});
|
|
1354
|
+
var readOkendoReviews = (find) => {
|
|
1355
|
+
const field = find("okendo", "summaryData");
|
|
1356
|
+
if (!field) return null;
|
|
1357
|
+
const summary = readReviewField(field, (value) => Summary.parse(JSON.parse(z15.string().parse(value))));
|
|
1358
|
+
return { rating: summary.reviewAverageValue, reviewCount: summary.reviewCount };
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
// src/server/postPurchaseScalarReviews.ts
|
|
1362
|
+
import { z as z16 } from "zod";
|
|
1363
|
+
var readScalarReviews = (find, namespace, ratingKey, countKey) => {
|
|
1364
|
+
const rating = find(namespace, ratingKey);
|
|
1365
|
+
const count = find(namespace, countKey);
|
|
1366
|
+
if (!rating || !count) return null;
|
|
1367
|
+
return {
|
|
1368
|
+
rating: readReviewField(
|
|
1369
|
+
rating,
|
|
1370
|
+
(value) => PostPurchaseCommonReviewRating.parse({ value: z16.string().parse(value) }).value
|
|
1371
|
+
),
|
|
1372
|
+
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/server/postPurchaseReviewData.ts
|
|
1377
|
+
var readStandardReviews = (find) => {
|
|
1378
|
+
const rating = find("reviews", "rating");
|
|
1379
|
+
const count = find("reviews", "rating_count");
|
|
1380
|
+
if (!rating || !count) return null;
|
|
1381
|
+
return {
|
|
1382
|
+
rating: readReviewField(rating, (raw) => {
|
|
1383
|
+
const value = z17.string().parse(raw);
|
|
1384
|
+
return PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value;
|
|
1385
|
+
}),
|
|
1386
|
+
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
1387
|
+
};
|
|
1388
|
+
};
|
|
1389
|
+
var readPostPurchaseReviewData = (provider, find) => {
|
|
1390
|
+
if (provider === "yotpo") return readScalarReviews(find, "yotpo", "reviews_average", "reviews_count");
|
|
1391
|
+
if (provider === "stamped") return readScalarReviews(find, "stamped", "reviews_average", "reviews_count");
|
|
1392
|
+
if (provider === "judgeme") return readJudgeMeReviews(find);
|
|
1393
|
+
if (provider === "okendo") return readOkendoReviews(find);
|
|
1394
|
+
if (provider === "opinew") return readScalarReviews(find, "opinew_metafields", "reviews_average", "reviews_count");
|
|
1395
|
+
if (provider === "reviewsio")
|
|
1396
|
+
return readScalarReviews(find, "reviewscouk", "rating", "total") ?? readStandardReviews(find);
|
|
1397
|
+
if (provider === "loox") return readScalarReviews(find, "loox", "avg_rating", "num_reviews");
|
|
1398
|
+
if (provider === "junip") return readScalarReviews(find, "junip", "rating_average", "rating_count");
|
|
1399
|
+
const rating = find("reviews", "rating");
|
|
1400
|
+
const count = find("reviews", "rating_count");
|
|
1401
|
+
if (!rating || !count) return null;
|
|
1402
|
+
return {
|
|
1403
|
+
rating: readReviewField(
|
|
1404
|
+
rating,
|
|
1405
|
+
(value) => PostPurchaseKlaviyoReviewRating.parse(JSON.parse(z17.string().parse(value))).value
|
|
1406
|
+
),
|
|
1407
|
+
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1410
|
+
|
|
1411
|
+
// src/server/postPurchaseReviewProvider.ts
|
|
1412
|
+
import { z as z18 } from "zod";
|
|
1413
|
+
var IntegrationInput = z18.record(z18.string(), z18.unknown()).prefault({});
|
|
1414
|
+
var precedence = [
|
|
1415
|
+
"klaviyo",
|
|
1416
|
+
"judgeme",
|
|
1417
|
+
"junip",
|
|
1418
|
+
"loox",
|
|
1419
|
+
"okendo",
|
|
1420
|
+
"opinew",
|
|
1421
|
+
"reviewsio",
|
|
1422
|
+
"stamped",
|
|
1423
|
+
"yotpo"
|
|
1424
|
+
];
|
|
1425
|
+
var ReviewsIoFlag = z18.preprocess((value) => {
|
|
1426
|
+
if (typeof value === "string") {
|
|
1427
|
+
const flag = value.toLowerCase();
|
|
1428
|
+
if (flag === "true" || flag === "yes") return true;
|
|
1429
|
+
if (flag === "false" || flag === "no") return false;
|
|
1430
|
+
}
|
|
1431
|
+
return value;
|
|
1432
|
+
}, PostPurchaseSettings.shape.integrations.shape.reviewsio);
|
|
1433
|
+
var selectPostPurchaseReviewProvider = (raw) => {
|
|
1434
|
+
const integrations = IntegrationInput.parse(raw);
|
|
1435
|
+
for (const provider of precedence) {
|
|
1436
|
+
const schema = provider === "reviewsio" ? ReviewsIoFlag : PostPurchaseSettings.shape.integrations.shape[provider];
|
|
1437
|
+
if (schema.parse(integrations[provider])) return provider;
|
|
1438
|
+
}
|
|
1439
|
+
return null;
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
// src/server/offerProductReviews.ts
|
|
1443
|
+
var readOrReport = (read, productId2, report) => {
|
|
1444
|
+
try {
|
|
1445
|
+
return read();
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
if (!(error instanceof ReviewFieldError)) throw error;
|
|
1448
|
+
report?.({
|
|
1449
|
+
detail: "Unreadable offer product reviews metafield",
|
|
1450
|
+
field: error.field,
|
|
1451
|
+
kind: "reviews",
|
|
1452
|
+
productId: productId2
|
|
1453
|
+
});
|
|
1454
|
+
return null;
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
var readReviews = (provider, find, productId2, report) => (provider === "klaviyo" ? null : readOrReport(() => readPostPurchaseReviewData(provider, find), productId2, report)) ?? readOrReport(() => readStandardReviews(find), productId2, report);
|
|
1458
|
+
var projectOfferProductReviews = (integrations, { id: productId2, metafields }, report) => {
|
|
1459
|
+
let provider;
|
|
1460
|
+
try {
|
|
1461
|
+
provider = selectPostPurchaseReviewProvider(integrations);
|
|
1462
|
+
} catch {
|
|
1463
|
+
return void 0;
|
|
1464
|
+
}
|
|
1465
|
+
if (!provider) return void 0;
|
|
1466
|
+
try {
|
|
1467
|
+
if (!Array.isArray(metafields)) return null;
|
|
1468
|
+
const data = readReviews(provider, reviewFields(metafields), productId2, report);
|
|
1469
|
+
return data && data.rating > 0 ? data : null;
|
|
1470
|
+
} catch {
|
|
1471
|
+
report?.({ detail: "Unreadable offer product reviews", kind: "product" });
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
// src/server/shopSubtotal.ts
|
|
1477
|
+
var shopCurrencyRate = (cart) => {
|
|
1478
|
+
const rate = cart.currencyRate ?? 1;
|
|
1479
|
+
return Number.isFinite(rate) && rate > 0 ? rate : 1;
|
|
1480
|
+
};
|
|
1481
|
+
var shopSubtotalCents = (cart) => Math.round((cart.subtotal - (cart.discountTotal ?? 0)) / shopCurrencyRate(cart));
|
|
1482
|
+
var toShopCents = (majorPresentment, rate) => Math.round(majorPresentment * 100 / rate);
|
|
1483
|
+
|
|
1484
|
+
// src/schema/cabShopConfig.ts
|
|
1485
|
+
import { z as z20 } from "zod";
|
|
1486
|
+
|
|
1487
|
+
// src/schema/driftedMarkets.ts
|
|
1488
|
+
var DRIFTED_MARKETS = Object.freeze({ enabled: false });
|
|
1489
|
+
|
|
1490
|
+
// src/schema/smartCart.ts
|
|
1491
|
+
import { z as z19 } from "zod";
|
|
1492
|
+
var SmartCartTierProduct = z19.looseObject({
|
|
1493
|
+
handle: z19.string().optional(),
|
|
1494
|
+
id: z19.coerce.number().optional(),
|
|
1495
|
+
productId: z19.coerce.number().optional(),
|
|
1496
|
+
title: z19.string().optional(),
|
|
1497
|
+
variantIds: z19.array(z19.coerce.number()).optional()
|
|
1498
|
+
});
|
|
1499
|
+
var SmartCartTierExclusion = z19.looseObject({
|
|
1500
|
+
collectionIds: z19.array(z19.coerce.number()).optional().catch(void 0),
|
|
1501
|
+
productIds: z19.array(z19.coerce.number()).optional().catch(void 0),
|
|
1502
|
+
tags: z19.array(z19.string()).optional().catch(void 0),
|
|
1503
|
+
type: z19.string().optional().catch(void 0)
|
|
1504
|
+
});
|
|
1505
|
+
var SmartCartTier = z19.looseObject({
|
|
1506
|
+
allProducts: z19.array(SmartCartTierProduct).optional(),
|
|
1507
|
+
/** Merchant-set tier label (e.g. "Free gift", "$100 reward"), shown on the step row when enabled. */
|
|
1508
|
+
customTierLabel: z19.boolean().optional(),
|
|
1509
|
+
customTierLabelText: z19.string().optional(),
|
|
1510
|
+
decorativeAmountReachedLabel: z19.boolean().optional(),
|
|
1511
|
+
decorativeAmountReachedLabelText: z19.string().optional(),
|
|
1512
|
+
decorativeAmountRemainingLabel: z19.boolean().optional(),
|
|
1513
|
+
decorativeAmountRemainingLabelText: z19.string().optional(),
|
|
1514
|
+
/** Order-discount reward: `'fixedAmount'` (shop-currency dollars) or `'percentage'` (whole percent). */
|
|
1515
|
+
discountAmountFromTotal: z19.coerce.number().optional(),
|
|
1516
|
+
discountAmountReachedLabel: z19.boolean().optional(),
|
|
1517
|
+
discountAmountReachedLabelText: z19.string().optional(),
|
|
1518
|
+
discountAmountRemainingLabel: z19.boolean().optional(),
|
|
1519
|
+
discountAmountRemainingLabelText: z19.string().optional(),
|
|
1520
|
+
discountType: z19.string().optional(),
|
|
1521
|
+
/**
|
|
1522
|
+
* onsite `exclusions` + `exclusions_enabled` (REB-22305): per-tier rules the converter carries onto
|
|
1523
|
+
* `CABProgressTier.exclusions`. `.catch(undefined)` for the same blast-radius reason as `productTierImage`:
|
|
1524
|
+
* one drifted value must degrade to "no tier exclusions", never drop every progress bar for the shop.
|
|
1525
|
+
*
|
|
1526
|
+
* `exclusionsEnabled` is a STRICT boolean on purpose, even though failing open here changes the tier's math
|
|
1527
|
+
* (a non-boolean reads as disabled) rather than just an icon. onsite reads its sibling tier flags strictly —
|
|
1528
|
+
* `shouldFilterOutOfStockVariantsFromTier` is `tier?.filter_oos_variants === true`, and its `progressBar.ts`
|
|
1529
|
+
* types every tier flag as `boolean` — so a stored `1` / `"1"` / `"true"` is "disabled" on the onsite bar too.
|
|
1530
|
+
* A truthiness preprocess would make CAB exclude where onsite does not: the opposite parity break.
|
|
1531
|
+
*/
|
|
1532
|
+
exclusions: z19.array(SmartCartTierExclusion).optional().catch(void 0),
|
|
1533
|
+
exclusionsEnabled: z19.boolean().optional().catch(void 0),
|
|
1534
|
+
filterOosVariants: z19.boolean().optional(),
|
|
1535
|
+
id: z19.string().optional(),
|
|
1536
|
+
/**
|
|
1537
|
+
* A `null`/absent minimum stays `undefined` (NOT coerced to `0`): a threshold-0 tier is always reached
|
|
1538
|
+
* and would auto-add its gift to every cart. onsite's `isTierDataValid` drops a non-numeric minimum for
|
|
1539
|
+
* exactly this reason; the converter's `renderableTiers` mirrors that (`Number.isFinite`). A literal `0`
|
|
1540
|
+
* is kept — onsite keeps it too. DELIBERATE DIVERGENCE from onsite for string minimums: onsite's
|
|
1541
|
+
* `isTierDataValid` is a strict `typeof === 'number'`, so it drops `'50'`; the converter instead coerces
|
|
1542
|
+
* `'50' → 50` and keeps the tier (a numeric-string minimum is a valid threshold, not a data defect).
|
|
1543
|
+
*/
|
|
1544
|
+
minimum: z19.preprocess((value) => value == null ? void 0 : value, z19.coerce.number().optional()),
|
|
1545
|
+
presentmentCurrencies: z19.looseObject({ enabled: z19.boolean().optional() }).optional(),
|
|
1546
|
+
productAmountReachedLabel: z19.boolean().optional(),
|
|
1547
|
+
productAmountReachedLabelText: z19.string().optional(),
|
|
1548
|
+
productAmountRemainingLabel: z19.boolean().optional(),
|
|
1549
|
+
productAmountRemainingLabelText: z19.string().optional(),
|
|
1550
|
+
products: z19.array(SmartCartTierProduct).optional(),
|
|
1551
|
+
/**
|
|
1552
|
+
* onsite `product_tier_element_size`: the step-row icon's square size as a px STRING (`'18px'` …
|
|
1553
|
+
* `'36px'` — the admin's five-value Size control, default `'18px'`). React stamps it onto the icon
|
|
1554
|
+
* SVG's `width`/`height` (`getProgressStepIconSize`), so the milestone renders at the merchant's
|
|
1555
|
+
* chosen size; the converter turns it into `CABProgressTier.iconSizePx` for the CAB renderer.
|
|
1556
|
+
*
|
|
1557
|
+
* `.catch(undefined)` for the same blast-radius reason as `productTierImage` below: this key was
|
|
1558
|
+
* previously unknown-and-ignored by the `looseObject`, and `SmartCartConfig` is parsed
|
|
1559
|
+
* `.nullish().catch(undefined)` with no intermediate catch, so one drifted value (a bare number, a
|
|
1560
|
+
* `'2rem'`) would drop EVERY progress bar for the shop rather than just this setting. Failing open to
|
|
1561
|
+
* `undefined` lands on the documented 18px default — strictly safer than that blast radius. The
|
|
1562
|
+
* converter parses the number out and clamps it, so a junk-but-string value degrades the same way.
|
|
1563
|
+
*/
|
|
1564
|
+
productTierElementSize: z19.string().optional().catch(void 0),
|
|
1565
|
+
/**
|
|
1566
|
+
* onsite `product_tier_image`: on a product (gift) tier, show the gift's product image in the step-row
|
|
1567
|
+
* slot instead of the generic bag icon (React `TierProgressIcon` swaps in the image when this is set and
|
|
1568
|
+
* an image resolves). Per-tier; the client resolves the image (config carries only product IDs).
|
|
1569
|
+
*
|
|
1570
|
+
* `.catch(undefined)` is load-bearing. This key was previously unknown-and-ignored by the `looseObject`;
|
|
1571
|
+
* declaring it as a real boolean means a stored non-boolean (`1` / `"1"` / `"true"`) would now FAIL the
|
|
1572
|
+
* tier — and since `SmartCartConfig` is parsed `.nullish().catch(undefined)` (userConfig/cabShopConfig)
|
|
1573
|
+
* with no intermediate catch, one drifted flag would drop EVERY progress bar for the shop, not just the
|
|
1574
|
+
* flag. The converter only reads it for truthiness (`gift && tier.productTierImage`), so failing open to
|
|
1575
|
+
* `undefined` (bag-icon fallback, the documented default) is strictly safer than that blast radius.
|
|
1576
|
+
*/
|
|
1577
|
+
productTierImage: z19.boolean().optional().catch(void 0),
|
|
1578
|
+
type: z19.string().optional(),
|
|
1579
|
+
/**
|
|
1580
|
+
* onsite's PER-TIER unit switch (`unlock_condition_type`): `'item_quantity'` makes `minimum` an
|
|
1581
|
+
* item count (checked before any money handling — no ×100, no FX, no presentment overrides,
|
|
1582
|
+
* mirroring onsite `getTierMinimum`); anything else (or absent) keeps the money semantics.
|
|
1583
|
+
*/
|
|
1584
|
+
unlockConditionType: z19.string().optional()
|
|
1585
|
+
});
|
|
1586
|
+
var SmartCartBar = z19.looseObject({
|
|
1587
|
+
countryCodes: z19.array(z19.string()).optional(),
|
|
1588
|
+
/**
|
|
1589
|
+
* onsite `exclude_discount_in_subtotal`: measure progress against the PRE-discount subtotal so an
|
|
1590
|
+
* applied code/order discount doesn't lower progress. The client adds cart discount allocations back.
|
|
1591
|
+
*/
|
|
1592
|
+
excludeDiscountInSubtotal: z19.boolean().optional(),
|
|
1593
|
+
/** Products excluded from the progress subtotal; the client subtracts their variants' cart cost. */
|
|
1594
|
+
excludedProducts: z19.array(z19.looseObject({ variantIds: z19.array(z19.coerce.number()).optional() })).optional(),
|
|
1595
|
+
freeShippingOnSubscription: z19.boolean().optional(),
|
|
1596
|
+
geolocationEnabled: z19.boolean().optional(),
|
|
1597
|
+
id: z19.string().optional(),
|
|
1598
|
+
language: z19.looseObject({
|
|
1599
|
+
freeShippingReached: z19.string().optional(),
|
|
1600
|
+
freeShippingRemaining: z19.string().optional(),
|
|
1601
|
+
giftAreaTitle: z19.string().optional()
|
|
1602
|
+
}).optional(),
|
|
1603
|
+
name: z19.string().optional(),
|
|
1604
|
+
tiers: z19.array(SmartCartTier).optional()
|
|
1605
|
+
});
|
|
1606
|
+
var SmartCartComponent = z19.looseObject({
|
|
1607
|
+
settings: z19.looseObject({ bars: z19.array(SmartCartBar).optional() }).optional(),
|
|
1608
|
+
type: z19.string().optional()
|
|
1609
|
+
});
|
|
1610
|
+
var SmartCartConfig = z19.looseObject({
|
|
1611
|
+
components: z19.array(SmartCartComponent).optional()
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
// src/schema/cabShopConfig.ts
|
|
1615
|
+
var CabActiveExperiment = z20.object({
|
|
1616
|
+
data: z20.array(
|
|
1617
|
+
z20.object({
|
|
1618
|
+
elementId: z20.coerce.number(),
|
|
1619
|
+
id: z20.coerce.number(),
|
|
1620
|
+
traffic: z20.coerce.number()
|
|
1621
|
+
})
|
|
1622
|
+
),
|
|
1623
|
+
id: z20.coerce.number(),
|
|
1624
|
+
placeholderId: z20.coerce.number().nullable(),
|
|
1625
|
+
type: z20.string()
|
|
1626
|
+
});
|
|
1627
|
+
var CabShopConfig = z20.object({
|
|
1628
|
+
/**
|
|
1629
|
+
* Per-element resilient: parse as `unknown[]` (non-array → `[]`), then keep only the experiments that
|
|
1630
|
+
* validate. A single drifted experiment drops itself instead of `.catch`-ing the WHOLE array to `[]` and
|
|
1631
|
+
* silently disabling every live A/B test fleet-wide.
|
|
1632
|
+
*/
|
|
1633
|
+
activeExperiments: z20.array(z20.unknown()).catch([]).default([]).transform(
|
|
1634
|
+
(experiments) => experiments.flatMap((experiment) => {
|
|
1635
|
+
const result = CabActiveExperiment.safeParse(experiment);
|
|
1636
|
+
return result.success ? [result.data] : [];
|
|
1637
|
+
})
|
|
1638
|
+
),
|
|
1639
|
+
/**
|
|
1640
|
+
* Only the two fields the smart-cart entitlement gate reads (`shortName`/`isActive`). Per-element
|
|
1641
|
+
* resilient like `activeExperiments`: a drifted package entry degrades to an inert `''`/false one that
|
|
1642
|
+
* matches nothing, so a real active `cart-merchandising` package still gates correctly and a sibling
|
|
1643
|
+
* drift never fails the shared config parse. Note the salvage is fail-CLOSED, not fail-open, for an
|
|
1644
|
+
* entitled v6 shop: a malformed whole array (⇒ `[]`) or an inerted `cart-merchandising` entry (e.g.
|
|
1645
|
+
* `is_active: 1` ⇒ inactive) makes the gate find no active package and drop every bar. That matches
|
|
1646
|
+
* React's "no proof of entitlement ⇒ render null", but it silently removes bars — so the server gate
|
|
1647
|
+
* ({@link file://../server/widgetSettings.ts} `fetchProgressBarContext`) emits a `shopConfig` drift
|
|
1648
|
+
* when it sees the inert-entry sentinel (`shortName === ''`). The whole-array `[]` case leaves no
|
|
1649
|
+
* sentinel and is not separately signalable here.
|
|
1650
|
+
*/
|
|
1651
|
+
activePackages: z20.array(z20.object({ isActive: z20.boolean(), shortName: z20.string() }).catch({ isActive: false, shortName: "" })).catch([]).default([]),
|
|
1652
|
+
apiKey: z20.string(),
|
|
1653
|
+
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
1654
|
+
billingVersion: z20.string().nullable().catch(null).default(null),
|
|
1655
|
+
/**
|
|
1656
|
+
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
1657
|
+
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
1658
|
+
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
1659
|
+
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
1660
|
+
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
1661
|
+
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
1662
|
+
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
1663
|
+
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
1664
|
+
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
1665
|
+
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
1666
|
+
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
1667
|
+
*/
|
|
1668
|
+
cacheKey: z20.union([z20.number(), z20.string()]).transform((value) => Number(value)).pipe(z20.number().int().positive()).optional().catch(void 0),
|
|
1669
|
+
/**
|
|
1670
|
+
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
1671
|
+
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
1672
|
+
* converts to the buyer's currency exactly in a cross-decimal market. `.catch()` like its siblings: a
|
|
1673
|
+
* missing/renamed currency degrades to "unknown" (client falls back to buyer decimals), never a parse fail.
|
|
1674
|
+
*/
|
|
1675
|
+
currency: z20.string().optional().catch(void 0),
|
|
1676
|
+
/**
|
|
1677
|
+
* The merchant's "Use Markets in Data Sources" toggle. The engine (CI2) never reads it on the serve
|
|
1678
|
+
* path, so the proxy honouring it before sending market context is what makes the setting mean
|
|
1679
|
+
* anything (REB-21688). Fail-CLOSED, unlike the fail-open siblings: a missing/null/drifted field reads
|
|
1680
|
+
* as markets OFF — market context silently dropped (engine serves shop-default market), never sent
|
|
1681
|
+
* against the merchant's setting. The OFF-reading salvages stay distinguishable: missing AND null are
|
|
1682
|
+
* the engine's normal absent vocabulary (`.nullish()`, like `monetize`/`billingVersion`) and read as
|
|
1683
|
+
* the plain default — not drift — while only a drifted SHAPE `.catch`es, to the {@link DRIFTED_MARKETS}
|
|
1684
|
+
* identity sentinel, so `fetchUserConfig` can emit a `shopConfig` drift for the one case that means an
|
|
1685
|
+
* engine rename/retype; otherwise a markets-enabled merchant degrading to shop-default pricing would
|
|
1686
|
+
* be indistinguishable from markets genuinely off. The missing-key case is EXPLICIT via `.default()`
|
|
1687
|
+
* (a factory, so parses never share a mutable object) rather than inferred from zod running an
|
|
1688
|
+
* optional key's transform on `undefined`; `null` flows past the default through `.nullish()` into the
|
|
1689
|
+
* transform. `Readonly` on the output type because the drifted salvage IS frozen — an assignment to
|
|
1690
|
+
* `markets.enabled` downstream would throw only for drifted shops in production, so surface direct
|
|
1691
|
+
* writes to tsc (a guard for the direct case only: TS doesn't check `readonly` in assignability, so
|
|
1692
|
+
* laundering through a mutable-shaped alias still compiles).
|
|
1693
|
+
* The inner `enabled` stays strict on purpose: `{ enabled: null }` is a genuine retype and drifting is
|
|
1694
|
+
* the correct answer (the editor-path `ShopConfig.markets` would hard-fail the same payload).
|
|
1695
|
+
*/
|
|
1696
|
+
markets: z20.object({ enabled: z20.boolean() }).nullish().catch(DRIFTED_MARKETS).default(() => ({ enabled: false })).transform((markets) => markets ?? { enabled: false }),
|
|
1697
|
+
monetize: z20.object({ publisherKey: z20.string().nullable() }).nullish().catch(void 0),
|
|
1698
|
+
shopId: z20.number().optional().catch(void 0),
|
|
1699
|
+
/**
|
|
1700
|
+
* The merchant's display name ("Gift Basket Store") — the Monetize header copy reads it.
|
|
1701
|
+
* `.catch()` like its siblings: a missing/drifted name degrades the header to the
|
|
1702
|
+
* myshopify-domain slug (the pre-CAB React fallback), never a parse fail. An empty string is a
|
|
1703
|
+
* VALID parse — the slug fallback for `''` (and whitespace/tag-only names) lives in
|
|
1704
|
+
* composeOffer's `||` chain, not here.
|
|
1705
|
+
*/
|
|
1706
|
+
shopName: z20.string().nullish().catch(void 0)
|
|
1707
|
+
});
|
|
1708
|
+
var CabUserConfig = z20.object({
|
|
1709
|
+
shop: CabShopConfig,
|
|
1710
|
+
smartCart: SmartCartConfig.nullish().catch(void 0)
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
// src/schema/widgets/checkout-and-beyond/image.ts
|
|
1714
|
+
import { v7 as uuidv713 } from "uuid";
|
|
1715
|
+
import { z as z39 } from "zod";
|
|
1716
|
+
|
|
839
1717
|
// src/schema/widgets/checkout-and-beyond/icons.ts
|
|
840
1718
|
var iconNames = [
|
|
841
1719
|
"alert-circle",
|
|
@@ -921,283 +1799,65 @@ var iconTones = ["auto", "critical", "custom", "info", "neutral", "success", "wa
|
|
|
921
1799
|
var IconTone = freezeEnum(iconTones);
|
|
922
1800
|
|
|
923
1801
|
// src/schema/widgets/checkout-and-beyond/imageThumbnails.ts
|
|
924
|
-
import { z as
|
|
925
|
-
var CABImageThumbnails =
|
|
926
|
-
limit:
|
|
927
|
-
perLine:
|
|
1802
|
+
import { z as z21 } from "zod";
|
|
1803
|
+
var CABImageThumbnails = z21.object({
|
|
1804
|
+
limit: z21.number().int().min(1).default(8),
|
|
1805
|
+
perLine: z21.number().int().min(1).max(8).default(4)
|
|
928
1806
|
});
|
|
929
1807
|
|
|
930
|
-
// src/schema/widgets/checkout-and-beyond/regex.ts
|
|
931
|
-
var DYNAMIC_TOKEN_REGEX = /^\{\{\s*[A-Za-z]+\s*\}\}$/;
|
|
932
|
-
var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
933
|
-
var ROOT_RELATIVE_PATH_REGEX = /^\/(?!\/)/;
|
|
934
|
-
var HTML_TAGS_REGEX = /<\/?[a-zA-Z][^>]*>/g;
|
|
935
|
-
|
|
936
1808
|
// src/schema/widgets/checkout-and-beyond/shared.ts
|
|
937
|
-
import { z as
|
|
1809
|
+
import { z as z38 } from "zod";
|
|
938
1810
|
|
|
939
1811
|
// src/schema/widgets/checkout-and-beyond/banner.ts
|
|
940
|
-
import { z as
|
|
941
|
-
|
|
942
|
-
// src/schema/widgets/checkout-and-beyond/layout.ts
|
|
943
|
-
import { v7 as uuidv7 } from "uuid";
|
|
944
|
-
import { z as z10 } from "zod";
|
|
945
|
-
|
|
946
|
-
// src/schema/widgets/checkout-and-beyond/variables.ts
|
|
947
|
-
import { z as z9 } from "zod";
|
|
948
|
-
|
|
949
|
-
// src/schema/widgets/checkout-and-beyond/rule.ts
|
|
950
|
-
import { z as z8 } from "zod";
|
|
951
|
-
var ruleTypes = [
|
|
952
|
-
"cart",
|
|
953
|
-
"cart_count",
|
|
954
|
-
"cart_item_count",
|
|
955
|
-
"cart_line_count",
|
|
956
|
-
"cart_subtotal",
|
|
957
|
-
"collection",
|
|
958
|
-
"customer",
|
|
959
|
-
"customer_order_count",
|
|
960
|
-
"customer_tag",
|
|
961
|
-
"customer_total_spent",
|
|
962
|
-
"date",
|
|
963
|
-
"product",
|
|
964
|
-
"product_tag",
|
|
965
|
-
"product_title",
|
|
966
|
-
"product_type",
|
|
967
|
-
"product_vendor"
|
|
968
|
-
];
|
|
969
|
-
var ruleType = z8.enum(ruleTypes);
|
|
970
|
-
var RuleType = ruleType.enum;
|
|
971
|
-
var ruleOperators = [
|
|
972
|
-
"after",
|
|
973
|
-
"anything",
|
|
974
|
-
"before",
|
|
975
|
-
"contains",
|
|
976
|
-
"contains_any",
|
|
977
|
-
"does_not_contain",
|
|
978
|
-
"does_not_contain_any",
|
|
979
|
-
"equals",
|
|
980
|
-
"does_not_equal",
|
|
981
|
-
"greater_than",
|
|
982
|
-
"less_than"
|
|
983
|
-
];
|
|
984
|
-
var ruleOperator = z8.enum(ruleOperators);
|
|
985
|
-
var RuleOperator = ruleOperator.enum;
|
|
986
|
-
var RuleCondition = z8.object({
|
|
987
|
-
operator: z8.enum(ruleOperators).default(RuleOperator.anything),
|
|
988
|
-
products: z8.union([z8.array(z8.unknown()), z8.strictObject({}).transform(() => [])]).default(() => []),
|
|
989
|
-
type: z8.enum(ruleTypes).default(RuleType.cart_subtotal),
|
|
990
|
-
value: z8.coerce.string().default("")
|
|
991
|
-
});
|
|
992
|
-
var RuleLogicBlock = z8.object({
|
|
993
|
-
rules: z8.array(RuleCondition).default(() => [])
|
|
994
|
-
});
|
|
995
|
-
var CABRulesetEntry = z8.object({
|
|
996
|
-
exitIfMatched: z8.boolean().default(true),
|
|
997
|
-
logic: z8.array(RuleLogicBlock).default(() => []),
|
|
998
|
-
output: z8.array(
|
|
999
|
-
z8.object({
|
|
1000
|
-
key: z8.string().default("should_show"),
|
|
1001
|
-
type: z8.literal("data_json").default("data_json"),
|
|
1002
|
-
value: z8.string().default("true")
|
|
1003
|
-
})
|
|
1004
|
-
).default(() => [{ key: "should_show", type: "data_json", value: "true" }])
|
|
1005
|
-
});
|
|
1006
|
-
var CABRuleset = z8.union([z8.array(CABRulesetEntry), z8.strictObject({}).transform(() => [])]).default(() => []);
|
|
1007
|
-
|
|
1008
|
-
// src/schema/widgets/checkout-and-beyond/variables.ts
|
|
1009
|
-
var variableEffect = z9.enum(variableEffects);
|
|
1010
|
-
var CABVariableSeed = z9.object({
|
|
1011
|
-
dataSourceId: z9.number().nullable().default(null),
|
|
1012
|
-
ruleset: CABRuleset
|
|
1013
|
-
});
|
|
1014
|
-
var CABBooleanVariable = z9.object({
|
|
1015
|
-
/**
|
|
1016
|
-
* Holds until the first seed verdict lands (and forever, for unseeded variables). Omitted, it derives
|
|
1017
|
-
* from seededness — `false` with a `seed` (visibility is EARNED), `true` without — via the transform
|
|
1018
|
-
* below; a cross-field default has no `.default()` spelling. An explicit value always wins.
|
|
1019
|
-
*/
|
|
1020
|
-
defaultValue: z9.boolean().optional(),
|
|
1021
|
-
name: z9.string().min(1),
|
|
1022
|
-
seed: CABVariableSeed.optional(),
|
|
1023
|
-
/** Defaulted so every stored declaration (all boolean before `text` existed) parses unchanged. */
|
|
1024
|
-
type: z9.literal(VariableType.boolean).default(VariableType.boolean)
|
|
1025
|
-
});
|
|
1026
|
-
var CABTextVariable = z9.object({
|
|
1027
|
-
defaultValue: z9.string(),
|
|
1028
|
-
name: z9.string().min(1),
|
|
1029
|
-
seed: z9.never().optional(),
|
|
1030
|
-
type: z9.literal(VariableType.text)
|
|
1031
|
-
});
|
|
1032
|
-
var CABNumberVariable = z9.object({
|
|
1033
|
-
defaultValue: z9.number(),
|
|
1034
|
-
name: z9.string().min(1),
|
|
1035
|
-
seed: z9.never().optional(),
|
|
1036
|
-
type: z9.literal(VariableType.number)
|
|
1037
|
-
});
|
|
1038
|
-
var CABVariable = z9.union([CABTextVariable, CABNumberVariable, CABBooleanVariable]).transform(
|
|
1039
|
-
(variable) => variable.type === VariableType.boolean ? { ...variable, defaultValue: variable.defaultValue ?? variable.seed === void 0 } : variable
|
|
1040
|
-
);
|
|
1041
|
-
var CABVariableEffect = z9.object({
|
|
1042
|
-
effect: variableEffect.default(VariableEffect.toggle),
|
|
1043
|
-
variable: z9.string().min(1)
|
|
1044
|
-
});
|
|
1045
|
-
var CABVisibility = z9.object({
|
|
1046
|
-
variable: z9.string().min(1),
|
|
1047
|
-
visibleWhen: z9.boolean().default(true)
|
|
1048
|
-
});
|
|
1049
|
-
var contentOperator = z9.enum(contentOperators);
|
|
1050
|
-
var resultCountOperator = z9.enum(resultCountOperators);
|
|
1051
|
-
var resultTagOperator = z9.enum(resultTagOperators);
|
|
1052
|
-
var mutatorEvent = z9.enum(mutatorEvents);
|
|
1053
|
-
var CABContentTrigger = z9.object({
|
|
1054
|
-
operator: contentOperator.default(ContentOperator.is_empty),
|
|
1055
|
-
type: z9.literal("content")
|
|
1056
|
-
});
|
|
1057
|
-
var CABResultCountTrigger = z9.object({
|
|
1058
|
-
operator: resultCountOperator.default(ResultCountOperator.equals),
|
|
1059
|
-
type: z9.literal("result_count"),
|
|
1060
|
-
value: z9.number().default(0)
|
|
1061
|
-
});
|
|
1062
|
-
var CABResultTagTrigger = z9.object({
|
|
1063
|
-
operator: resultTagOperator.default(ResultTagOperator.contains),
|
|
1064
|
-
type: z9.literal("result_tag"),
|
|
1065
|
-
value: z9.string().min(1)
|
|
1066
|
-
});
|
|
1067
|
-
var CABEventTrigger = z9.object({
|
|
1068
|
-
event: mutatorEvent,
|
|
1069
|
-
type: z9.literal("event")
|
|
1070
|
-
});
|
|
1071
|
-
var CABVariableTrigger = z9.discriminatedUnion("type", [
|
|
1072
|
-
CABContentTrigger,
|
|
1073
|
-
CABEventTrigger,
|
|
1074
|
-
CABResultCountTrigger,
|
|
1075
|
-
CABResultTagTrigger
|
|
1076
|
-
]);
|
|
1077
|
-
var CABVariableMutator = z9.object({
|
|
1078
|
-
effect: variableEffect.default(VariableEffect.set_false),
|
|
1079
|
-
variable: z9.string().min(1),
|
|
1080
|
-
when: CABVariableTrigger
|
|
1081
|
-
});
|
|
1082
|
-
var CABVariableFields = {
|
|
1083
|
-
mutators: z9.array(CABVariableMutator).optional(),
|
|
1084
|
-
variables: z9.array(CABVariable).refine((variables) => new Set(variables.map(({ name }) => name)).size === variables.length, {
|
|
1085
|
-
message: "variable names must be unique within a section"
|
|
1086
|
-
}).optional(),
|
|
1087
|
-
visibility: CABVisibility.optional()
|
|
1088
|
-
};
|
|
1812
|
+
import { z as z23 } from "zod";
|
|
1089
1813
|
|
|
1090
1814
|
// src/schema/widgets/checkout-and-beyond/layout.ts
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1815
|
+
import { v7 as uuidv72 } from "uuid";
|
|
1816
|
+
import { z as z22 } from "zod";
|
|
1817
|
+
var GridItem = z22.union([z22.literal("auto"), z22.literal("fill"), z22.string().regex(/^\d+%$/), z22.number()]);
|
|
1818
|
+
var CABLayoutWrapping = z22.object({
|
|
1819
|
+
breakAt: z22.number().positive(),
|
|
1820
|
+
maxPerLine: z22.number().int().positive()
|
|
1095
1821
|
});
|
|
1096
|
-
var CABLayoutSection =
|
|
1822
|
+
var CABLayoutSection = z22.object({
|
|
1097
1823
|
...CABVariableFields,
|
|
1098
|
-
alignment:
|
|
1099
|
-
horizontal:
|
|
1100
|
-
vertical:
|
|
1824
|
+
alignment: z22.object({
|
|
1825
|
+
horizontal: z22.enum(horizontalAlignments).default(HorizontalAlignment.start),
|
|
1826
|
+
vertical: z22.enum(verticalAlignments).default(VerticalAlignment.top)
|
|
1101
1827
|
}).default({
|
|
1102
1828
|
horizontal: HorizontalAlignment.start,
|
|
1103
1829
|
vertical: VerticalAlignment.top
|
|
1104
1830
|
}),
|
|
1105
|
-
border:
|
|
1106
|
-
buttonField:
|
|
1107
|
-
direction:
|
|
1831
|
+
border: z22.lazy(() => CABBorder),
|
|
1832
|
+
buttonField: z22.enum(buttonFields).optional(),
|
|
1833
|
+
direction: z22.enum(directions).default(Direction.rows),
|
|
1108
1834
|
/**
|
|
1109
1835
|
* Platform chrome around the section, not a layout behavior: `callout` is a subdued box with block borders
|
|
1110
1836
|
* (the legacy post-purchase callout banner), `offer-content` the post-purchase offer's own content card.
|
|
1111
1837
|
*/
|
|
1112
|
-
frame:
|
|
1113
|
-
grid:
|
|
1114
|
-
columns:
|
|
1115
|
-
rows:
|
|
1838
|
+
frame: z22.enum(layoutFrames).optional(),
|
|
1839
|
+
grid: z22.object({
|
|
1840
|
+
columns: z22.union([z22.array(GridItem), z22.undefined()]).default(() => ["auto"]),
|
|
1841
|
+
rows: z22.union([z22.array(GridItem), z22.undefined()]).default(() => ["auto"])
|
|
1116
1842
|
}).default({ columns: ["auto"], rows: ["auto"] }).optional(),
|
|
1117
|
-
name:
|
|
1118
|
-
padding:
|
|
1119
|
-
sectionId:
|
|
1120
|
-
sections:
|
|
1121
|
-
sectionType:
|
|
1122
|
-
spacing:
|
|
1843
|
+
name: z22.string().optional(),
|
|
1844
|
+
padding: z22.enum(spacings).default(Spacing.none),
|
|
1845
|
+
sectionId: z22.uuid().default(() => uuidv72()),
|
|
1846
|
+
sections: z22.union([z22.array(z22.lazy(() => CABSection)), z22.strictObject({}).transform(() => [])]).default(() => []),
|
|
1847
|
+
sectionType: z22.literal(SectionType.layout).default(SectionType.layout),
|
|
1848
|
+
spacing: z22.enum(spacings).default(Spacing.base),
|
|
1123
1849
|
/** Fire-and-forget impression pixel POSTed once when the section is shown (e.g. a Monetize offer view). */
|
|
1124
|
-
viewUrl:
|
|
1125
|
-
width:
|
|
1850
|
+
viewUrl: z22.string().optional(),
|
|
1851
|
+
width: z22.number().default(100),
|
|
1126
1852
|
wrapping: CABLayoutWrapping.optional()
|
|
1127
1853
|
});
|
|
1128
1854
|
|
|
1129
|
-
// src/schema/widgets/checkout-and-beyond/utils.ts
|
|
1130
|
-
import { isPlainObject as isPlainObject2 } from "es-toolkit";
|
|
1131
|
-
import { get, isString } from "es-toolkit/compat";
|
|
1132
|
-
|
|
1133
|
-
// src/transforms/convertNumericObjects.ts
|
|
1134
|
-
import { isPlainObject } from "es-toolkit";
|
|
1135
|
-
var ARRAY_INDEX_STRING = /^(?:0|[1-9]\d*)$/;
|
|
1136
|
-
var hasOnlyNumericKeys = (input, requireCompleteRun) => {
|
|
1137
|
-
const keys = Object.keys(input);
|
|
1138
|
-
if (keys.length === 0) return false;
|
|
1139
|
-
if (!keys.every((key) => ARRAY_INDEX_STRING.test(key) && Number.isSafeInteger(Number(key)))) return false;
|
|
1140
|
-
if (!requireCompleteRun) return true;
|
|
1141
|
-
const sorted = keys.map(Number).sort((a, b) => a - b);
|
|
1142
|
-
return sorted.every((n, i) => n === i);
|
|
1143
|
-
};
|
|
1144
|
-
var numericObjectToArray = (input) => Object.entries(input).sort(([a], [b]) => Number(a) - Number(b)).map(([, value]) => value);
|
|
1145
|
-
var convert = (input, requireCompleteRun) => {
|
|
1146
|
-
if (Array.isArray(input)) return input.map((item) => convert(item, requireCompleteRun));
|
|
1147
|
-
if (isPlainObject(input)) {
|
|
1148
|
-
if (hasOnlyNumericKeys(input, requireCompleteRun)) {
|
|
1149
|
-
return numericObjectToArray(input).map((item) => convert(item, requireCompleteRun));
|
|
1150
|
-
}
|
|
1151
|
-
const out = {};
|
|
1152
|
-
for (const [key, value] of Object.entries(input)) out[key] = convert(value, requireCompleteRun);
|
|
1153
|
-
return out;
|
|
1154
|
-
}
|
|
1155
|
-
return input;
|
|
1156
|
-
};
|
|
1157
|
-
var convertNumericObjects = (input) => convert(input, true);
|
|
1158
|
-
|
|
1159
|
-
// src/schema/widgets/checkout-and-beyond/utils.ts
|
|
1160
|
-
var isHTML = (html = "") => {
|
|
1161
|
-
if (typeof window !== "undefined") {
|
|
1162
|
-
const parser = new DOMParser();
|
|
1163
|
-
const doc = parser.parseFromString(html, "text/html");
|
|
1164
|
-
const hasHTML = (nodeList) => Array.from(nodeList).some((node) => node.nodeType === 1);
|
|
1165
|
-
return hasHTML(doc.body.childNodes) || hasHTML(doc.head.childNodes);
|
|
1166
|
-
}
|
|
1167
|
-
return html.search(HTML_TAGS_REGEX) !== -1;
|
|
1168
|
-
};
|
|
1169
|
-
var hasHTMLInDoc = (doc) => doc.content.some(({ content = [] }) => content.some(({ text: text2 }) => isHTML(text2)));
|
|
1170
|
-
var checkForHTML = (input) => !(isString(input) && isHTML(input) || isPlainObject2(input) && hasHTMLInDoc(input));
|
|
1171
|
-
var NO_HTML = { message: "HTML is not supported" };
|
|
1172
|
-
var isSectionType = (sectionType) => (section) => get(section, "sectionType") === sectionType;
|
|
1173
|
-
var isBannerType = isSectionType(SectionType.banner);
|
|
1174
|
-
var isButtonType = isSectionType(SectionType.button);
|
|
1175
|
-
var isCarouselType = isSectionType(SectionType.carousel);
|
|
1176
|
-
var isCartLineType = isSectionType(SectionType.cartLine);
|
|
1177
|
-
var isDataSourceType = isSectionType(
|
|
1178
|
-
SectionType.dataSource
|
|
1179
|
-
);
|
|
1180
|
-
var isDiscountType = isSectionType(SectionType.discount);
|
|
1181
|
-
var isImageType = isSectionType(SectionType.image);
|
|
1182
|
-
var isLayoutType = isSectionType(SectionType.layout);
|
|
1183
|
-
var isMonetizeType = isSectionType(SectionType.monetize);
|
|
1184
|
-
var isOffersType = isSectionType(SectionType.offers);
|
|
1185
|
-
var isProductType = isSectionType(SectionType.product);
|
|
1186
|
-
var isProductsType = isSectionType(SectionType.products);
|
|
1187
|
-
var isProgressBarType = isSectionType(
|
|
1188
|
-
SectionType.progressBar
|
|
1189
|
-
);
|
|
1190
|
-
var isQuantityType = isSectionType(SectionType.quantity);
|
|
1191
|
-
var isReviewsType = isSectionType(SectionType.reviews);
|
|
1192
|
-
var isTextType = isSectionType(SectionType.text);
|
|
1193
|
-
var isVariantsType = isSectionType(SectionType.variants);
|
|
1194
|
-
|
|
1195
1855
|
// src/schema/widgets/checkout-and-beyond/banner.ts
|
|
1196
|
-
var CABBannerSection =
|
|
1856
|
+
var CABBannerSection = z23.lazy(
|
|
1197
1857
|
() => CABLayoutSection.omit({ sectionType: true }).extend({
|
|
1198
|
-
buttonField:
|
|
1199
|
-
color:
|
|
1200
|
-
dismissible:
|
|
1858
|
+
buttonField: z23.enum(buttonFields).optional(),
|
|
1859
|
+
color: z23.enum(bannerColors).default("info"),
|
|
1860
|
+
dismissible: z23.boolean().default(true),
|
|
1201
1861
|
/**
|
|
1202
1862
|
* The `s-banner` heading — localized content (a buyer can see a merchant-composed banner). Absent OR
|
|
1203
1863
|
* blank ⇒ no heading; the renderer emits no heading slot for `''` rather than an empty one.
|
|
@@ -1213,140 +1873,50 @@ var CABBannerSection = z11.lazy(
|
|
|
1213
1873
|
* not a 500. Nothing emits `heading` yet; the first converter that maps merchant banner copy into it
|
|
1214
1874
|
* either strips first or teaches `normalizeTextContent` to strip this field.
|
|
1215
1875
|
*/
|
|
1216
|
-
heading:
|
|
1217
|
-
sectionType:
|
|
1876
|
+
heading: z23.record(z23.string(), z23.string().refine(checkForHTML, NO_HTML)).optional(),
|
|
1877
|
+
sectionType: z23.literal(SectionType.banner).default(SectionType.banner)
|
|
1218
1878
|
})
|
|
1219
1879
|
);
|
|
1220
1880
|
|
|
1221
1881
|
// src/schema/widgets/checkout-and-beyond/button.ts
|
|
1222
1882
|
import { v7 as uuidv73 } from "uuid";
|
|
1223
|
-
import { z as
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
import { forEach, isString as isString2 } from "es-toolkit/compat";
|
|
1227
|
-
import { v7 as uuidv72 } from "uuid";
|
|
1228
|
-
import { z as z12 } from "zod";
|
|
1229
|
-
var enumOrOmit = (...vals) => z12.string().transform((val) => vals.includes(val) ? val : void 0).optional();
|
|
1230
|
-
var TiptapText = z12.object({
|
|
1231
|
-
marks: z12.union([
|
|
1232
|
-
z12.array(
|
|
1233
|
-
z12.discriminatedUnion("type", [
|
|
1234
|
-
z12.object({ type: z12.literal("bold") }),
|
|
1235
|
-
z12.object({ type: z12.literal("italic") }),
|
|
1236
|
-
z12.object({
|
|
1237
|
-
attrs: z12.object({
|
|
1238
|
-
class: z12.string().nullable(),
|
|
1239
|
-
href: z12.union([
|
|
1240
|
-
z12.url(),
|
|
1241
|
-
z12.literal("#"),
|
|
1242
|
-
z12.string().regex(DYNAMIC_TOKEN_REGEX),
|
|
1243
|
-
z12.string().regex(ROOT_RELATIVE_PATH_REGEX)
|
|
1244
|
-
]),
|
|
1245
|
-
rel: z12.string().default("noopener noreferrer nofollow"),
|
|
1246
|
-
target: z12.string().default("_blank")
|
|
1247
|
-
}),
|
|
1248
|
-
type: z12.literal("link")
|
|
1249
|
-
}),
|
|
1250
|
-
z12.object({ type: z12.literal("strike") }),
|
|
1251
|
-
z12.object({
|
|
1252
|
-
attrs: z12.object({
|
|
1253
|
-
color: enumOrOmit(...textColorNames).default(TextColorName.base),
|
|
1254
|
-
fontSize: enumOrOmit(...textSizeNames).default(TextSizeName.base)
|
|
1255
|
-
}),
|
|
1256
|
-
type: z12.literal("textStyle")
|
|
1257
|
-
})
|
|
1258
|
-
])
|
|
1259
|
-
),
|
|
1260
|
-
z12.strictObject({}).transform(() => [])
|
|
1261
|
-
]).optional(),
|
|
1262
|
-
text: z12.string().default(""),
|
|
1263
|
-
type: z12.literal("text").default("text")
|
|
1264
|
-
});
|
|
1265
|
-
var TiptapParagraph = z12.object({
|
|
1266
|
-
attrs: z12.object({
|
|
1267
|
-
textAlign: enumOrOmit(...textAlignments)
|
|
1268
|
-
}).default({ textAlign: TextAlignment.start }),
|
|
1269
|
-
content: z12.union([z12.array(TiptapText), z12.strictObject({}).transform(() => [])]).default(() => []),
|
|
1270
|
-
type: z12.literal("paragraph").default("paragraph")
|
|
1271
|
-
});
|
|
1272
|
-
var TiptapDocument = z12.object({
|
|
1273
|
-
attrs: z12.object({
|
|
1274
|
-
blockSpacing: enumOrOmit(...spacings)
|
|
1275
|
-
}).default({ blockSpacing: Spacing.base }),
|
|
1276
|
-
content: z12.union([z12.array(TiptapParagraph), z12.strictObject({}).transform(() => [])]).default(() => [TiptapParagraph.parse({})]),
|
|
1277
|
-
type: z12.literal("doc").default("doc")
|
|
1278
|
-
});
|
|
1279
|
-
var CABTextTier = z12.object({
|
|
1280
|
-
threshold: z12.number().int().nonnegative(),
|
|
1281
|
-
thresholds: z12.record(z12.string(), z12.number().int().nonnegative()).default(() => ({})),
|
|
1282
|
-
tierId: z12.string().optional(),
|
|
1283
|
-
tierType: z12.enum(progressTierTypes).default(ProgressTierType.shipping)
|
|
1284
|
-
});
|
|
1285
|
-
var CABTextSection = z12.object({
|
|
1286
|
-
...CABVariableFields,
|
|
1287
|
-
buttonField: z12.enum(buttonFields).optional(),
|
|
1288
|
-
content: z12.record(z12.string(), z12.union([z12.string(), TiptapDocument]).refine(checkForHTML, NO_HTML).optional()).default({ en: TiptapDocument.parse({}) }).optional(),
|
|
1289
|
-
name: z12.string().optional(),
|
|
1290
|
-
optionId: z12.number().optional(),
|
|
1291
|
-
sectionId: z12.uuid().default(() => uuidv72()),
|
|
1292
|
-
sectionType: z12.literal(SectionType.text).default(SectionType.text),
|
|
1293
|
-
tier: CABTextTier.optional()
|
|
1294
|
-
}).superRefine(({ buttonField, content }, ctx) => {
|
|
1295
|
-
if (buttonField === ButtonField.destinationUrl && content) {
|
|
1296
|
-
forEach(content, (value, lang) => {
|
|
1297
|
-
if (value && isString2(value)) {
|
|
1298
|
-
if (DYNAMIC_TOKEN_REGEX.test(value)) return;
|
|
1299
|
-
try {
|
|
1300
|
-
new URL(value);
|
|
1301
|
-
} catch {
|
|
1302
|
-
ctx.addIssue({
|
|
1303
|
-
code: "custom",
|
|
1304
|
-
message: "Invalid URL",
|
|
1305
|
-
path: ["content", lang]
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
});
|
|
1312
|
-
|
|
1313
|
-
// src/schema/widgets/checkout-and-beyond/button.ts
|
|
1314
|
-
var CABButtonContent = z13.lazy(
|
|
1315
|
-
() => z13.discriminatedUnion("sectionType", [CABImageSection, CABLayoutSection, CABTextSection])
|
|
1883
|
+
import { z as z24 } from "zod";
|
|
1884
|
+
var CABButtonContent = z24.lazy(
|
|
1885
|
+
() => z24.discriminatedUnion("sectionType", [CABImageSection, CABLayoutSection, CABTextSection])
|
|
1316
1886
|
);
|
|
1317
|
-
var CABButtonSection =
|
|
1887
|
+
var CABButtonSection = z24.object({
|
|
1318
1888
|
...CABVariableFields,
|
|
1319
|
-
action:
|
|
1320
|
-
buttonStyle:
|
|
1321
|
-
custom:
|
|
1322
|
-
color:
|
|
1323
|
-
height:
|
|
1324
|
-
width:
|
|
1889
|
+
action: z24.union([z24.literal(""), z24.enum(buttonActions)]).default(""),
|
|
1890
|
+
buttonStyle: z24.enum(buttonStyles).default(ButtonStyle.primary),
|
|
1891
|
+
custom: z24.object({
|
|
1892
|
+
color: z24.string().regex(HEX_COLOR_REGEX).default("#005bd3"),
|
|
1893
|
+
height: z24.number().default(52),
|
|
1894
|
+
width: z24.number().default(300)
|
|
1325
1895
|
}).default({
|
|
1326
1896
|
color: "#005bd3",
|
|
1327
1897
|
height: 52,
|
|
1328
1898
|
width: 300
|
|
1329
1899
|
}),
|
|
1330
|
-
disableDowngrade:
|
|
1900
|
+
disableDowngrade: z24.boolean().default(false),
|
|
1331
1901
|
/**
|
|
1332
1902
|
* What labels a `switchToSubscription` button's frequency options. `planName` shows the selling plan's
|
|
1333
1903
|
* own name (legacy post-purchase, L115) and falls back to the `subscriptionOption` template for a plan
|
|
1334
1904
|
* without one; absent or `template` is the template alone (the checkout card). Optional and additive
|
|
1335
1905
|
* (REB-24846); an unknown value reads as absent.
|
|
1336
1906
|
*/
|
|
1337
|
-
frequencyLabel:
|
|
1338
|
-
name:
|
|
1339
|
-
optionId:
|
|
1340
|
-
popover:
|
|
1341
|
-
alignment:
|
|
1342
|
-
position:
|
|
1907
|
+
frequencyLabel: z24.enum(["planName", "template"]).optional().catch(void 0),
|
|
1908
|
+
name: z24.string().optional(),
|
|
1909
|
+
optionId: z24.number().optional(),
|
|
1910
|
+
popover: z24.object({
|
|
1911
|
+
alignment: z24.enum(textAlignments).default(TextAlignment.center),
|
|
1912
|
+
position: z24.enum(popoverPositions).default(PopoverPosition.blockStart)
|
|
1343
1913
|
}).optional().default(() => ({
|
|
1344
1914
|
alignment: TextAlignment.center,
|
|
1345
1915
|
position: PopoverPosition.blockStart
|
|
1346
1916
|
})),
|
|
1347
|
-
sectionId:
|
|
1348
|
-
sections:
|
|
1349
|
-
sectionType:
|
|
1917
|
+
sectionId: z24.uuid().default(() => uuidv73()),
|
|
1918
|
+
sections: z24.union([z24.array(CABButtonContent), z24.strictObject({}).transform(() => [])]).default(() => []),
|
|
1919
|
+
sectionType: z24.literal(SectionType.button).default(SectionType.button),
|
|
1350
1920
|
/**
|
|
1351
1921
|
* The smart-cart tier this button acts on (`addGift`/`removeGift`): scopes the click to one tier's gift. The
|
|
1352
1922
|
* client resolves the tier's gift (productId) from the progress-bar context by this `tierId` — the button node
|
|
@@ -1361,25 +1931,25 @@ var CABButtonSection = z13.object({
|
|
|
1361
1931
|
* lookup finds no tier and the click resolves to nothing. Safe to bound — the field is new, so there are no
|
|
1362
1932
|
* stored payloads to grandfather, and the producer is client code, not a merchant form.
|
|
1363
1933
|
*/
|
|
1364
|
-
tier:
|
|
1934
|
+
tier: z24.object({ tierId: z24.string().min(1) }).optional(),
|
|
1365
1935
|
/** Fire-and-forget pixel POSTed when the button is clicked (e.g. a Monetize decline). */
|
|
1366
|
-
trackingUrl:
|
|
1936
|
+
trackingUrl: z24.string().optional(),
|
|
1367
1937
|
/**
|
|
1368
1938
|
* Secondary effects applied to in-scope variables on click, alongside (never instead of) the primary
|
|
1369
1939
|
* `action` — an `addToOrder` button can also flip a variable. Applied in array order.
|
|
1370
1940
|
*/
|
|
1371
|
-
variableEffects:
|
|
1941
|
+
variableEffects: z24.array(CABVariableEffect).optional()
|
|
1372
1942
|
});
|
|
1373
1943
|
|
|
1374
1944
|
// src/schema/widgets/checkout-and-beyond/carousel.ts
|
|
1375
|
-
import { z as
|
|
1945
|
+
import { z as z29 } from "zod";
|
|
1376
1946
|
|
|
1377
1947
|
// src/schema/widgets/checkout-and-beyond/offers.ts
|
|
1378
|
-
import { z as
|
|
1948
|
+
import { z as z28 } from "zod";
|
|
1379
1949
|
|
|
1380
1950
|
// src/schema/widgets/checkout-and-beyond/dataSource.ts
|
|
1381
1951
|
import { v7 as uuidv74 } from "uuid";
|
|
1382
|
-
import { z as
|
|
1952
|
+
import { z as z25 } from "zod";
|
|
1383
1953
|
|
|
1384
1954
|
// src/schema/widgets/checkout-and-beyond/constants.ts
|
|
1385
1955
|
import { uniqBy } from "es-toolkit";
|
|
@@ -1433,77 +2003,77 @@ var targetAreas = {
|
|
|
1433
2003
|
};
|
|
1434
2004
|
|
|
1435
2005
|
// src/schema/widgets/checkout-and-beyond/dataSource.ts
|
|
1436
|
-
var CABDiscount =
|
|
1437
|
-
amount:
|
|
1438
|
-
discountedBy:
|
|
1439
|
-
discountedFrom:
|
|
1440
|
-
type:
|
|
1441
|
-
});
|
|
1442
|
-
var CABIntegrations =
|
|
1443
|
-
judgeme:
|
|
1444
|
-
junip:
|
|
1445
|
-
klaviyo:
|
|
1446
|
-
loox:
|
|
1447
|
-
okendo:
|
|
1448
|
-
opinew:
|
|
1449
|
-
reviewsio:
|
|
1450
|
-
stamped:
|
|
1451
|
-
yotpo:
|
|
1452
|
-
});
|
|
1453
|
-
var CABVariantLabels =
|
|
1454
|
-
variantOption:
|
|
1455
|
-
variantOptions:
|
|
1456
|
-
});
|
|
1457
|
-
var CABDataSourceSection =
|
|
1458
|
-
dataSourceId:
|
|
1459
|
-
dataSourcePath:
|
|
2006
|
+
var CABDiscount = z25.object({
|
|
2007
|
+
amount: z25.union([z25.number(), z25.string()]).catch(0),
|
|
2008
|
+
discountedBy: z25.string().optional(),
|
|
2009
|
+
discountedFrom: z25.enum(discountSources).optional().catch(void 0),
|
|
2010
|
+
type: z25.enum(discountTypes).catch("none")
|
|
2011
|
+
});
|
|
2012
|
+
var CABIntegrations = z25.object({
|
|
2013
|
+
judgeme: z25.boolean().default(false),
|
|
2014
|
+
junip: z25.boolean().default(false),
|
|
2015
|
+
klaviyo: z25.boolean().default(false),
|
|
2016
|
+
loox: z25.boolean().default(false),
|
|
2017
|
+
okendo: z25.boolean().default(false),
|
|
2018
|
+
opinew: z25.boolean().default(false),
|
|
2019
|
+
reviewsio: z25.boolean().default(false),
|
|
2020
|
+
stamped: z25.boolean().default(false),
|
|
2021
|
+
yotpo: z25.boolean().default(false)
|
|
2022
|
+
});
|
|
2023
|
+
var CABVariantLabels = z25.object({
|
|
2024
|
+
variantOption: z25.string().optional().catch(void 0),
|
|
2025
|
+
variantOptions: z25.string().optional().catch(void 0)
|
|
2026
|
+
});
|
|
2027
|
+
var CABDataSourceSection = z25.object({
|
|
2028
|
+
dataSourceId: z25.number().nullable().default(null),
|
|
2029
|
+
dataSourcePath: z25.string().default(DEFAULT_ENDPOINTS[0].value),
|
|
1460
2030
|
discount: CABDiscount.optional(),
|
|
1461
2031
|
integrations: CABIntegrations.optional(),
|
|
1462
|
-
limit:
|
|
1463
|
-
matchVariant:
|
|
1464
|
-
matchVariantOutOfStock:
|
|
1465
|
-
name:
|
|
1466
|
-
productType:
|
|
1467
|
-
sectionId:
|
|
1468
|
-
sectionType:
|
|
2032
|
+
limit: z25.number().default(1),
|
|
2033
|
+
matchVariant: z25.boolean().default(false),
|
|
2034
|
+
matchVariantOutOfStock: z25.boolean().default(false),
|
|
2035
|
+
name: z25.string().default(DEFAULT_ENDPOINTS[0].label),
|
|
2036
|
+
productType: z25.enum(productTypes).optional(),
|
|
2037
|
+
sectionId: z25.uuid().default(() => uuidv74()),
|
|
2038
|
+
sectionType: z25.literal(SectionType.dataSource).default(SectionType.dataSource),
|
|
1469
2039
|
/** Default subscribe-vs-one-time for `productType: both` (legacy `subscription_is_default`). */
|
|
1470
|
-
subscriptionIsDefault:
|
|
1471
|
-
treatAsGwp:
|
|
2040
|
+
subscriptionIsDefault: z25.boolean().default(false),
|
|
2041
|
+
treatAsGwp: z25.boolean().default(false),
|
|
1472
2042
|
variantLabels: CABVariantLabels.optional().catch(void 0)
|
|
1473
2043
|
});
|
|
1474
2044
|
|
|
1475
2045
|
// src/schema/widgets/checkout-and-beyond/products.ts
|
|
1476
2046
|
import { v7 as uuidv76 } from "uuid";
|
|
1477
|
-
import { z as
|
|
2047
|
+
import { z as z27 } from "zod";
|
|
1478
2048
|
|
|
1479
2049
|
// src/schema/widgets/checkout-and-beyond/product.ts
|
|
1480
2050
|
import { v7 as uuidv75 } from "uuid";
|
|
1481
|
-
import { z as
|
|
1482
|
-
var CABVariantContent =
|
|
1483
|
-
() =>
|
|
2051
|
+
import { z as z26 } from "zod";
|
|
2052
|
+
var CABVariantContent = z26.lazy(
|
|
2053
|
+
() => z26.discriminatedUnion("sectionType", [CABButtonSection, CABTextSection])
|
|
1484
2054
|
);
|
|
1485
|
-
var CABProductSection =
|
|
2055
|
+
var CABProductSection = z26.object({
|
|
1486
2056
|
...CABVariableFields,
|
|
1487
|
-
name:
|
|
1488
|
-
productId:
|
|
1489
|
-
sectionId:
|
|
1490
|
-
sections:
|
|
1491
|
-
sectionType:
|
|
1492
|
-
selectors:
|
|
1493
|
-
variantMode:
|
|
2057
|
+
name: z26.string().optional(),
|
|
2058
|
+
productId: z26.number().nullable().default(null),
|
|
2059
|
+
sectionId: z26.uuid().default(() => uuidv75()),
|
|
2060
|
+
sections: z26.union([z26.array(CABVariantContent), z26.strictObject({}).transform(() => [])]).default(() => []),
|
|
2061
|
+
sectionType: z26.literal(SectionType.product).default(SectionType.product),
|
|
2062
|
+
selectors: z26.record(z26.string(), z26.enum(variantSelectors).default(VariantSelector.menu)).default(() => ({})),
|
|
2063
|
+
variantMode: z26.enum(variantModes).default(VariantMode.multiple)
|
|
1494
2064
|
});
|
|
1495
2065
|
|
|
1496
2066
|
// src/schema/widgets/checkout-and-beyond/products.ts
|
|
1497
|
-
var CABProductsSection =
|
|
2067
|
+
var CABProductsSection = z27.object({
|
|
1498
2068
|
...CABVariableFields,
|
|
1499
|
-
name:
|
|
1500
|
-
sectionId:
|
|
1501
|
-
sections:
|
|
1502
|
-
sectionType:
|
|
2069
|
+
name: z27.string().optional(),
|
|
2070
|
+
sectionId: z27.uuid().default(() => uuidv76()),
|
|
2071
|
+
sections: z27.union([z27.array(z27.lazy(() => CABProductSection)), z27.strictObject({}).transform(() => [])]).default(() => []),
|
|
2072
|
+
sectionType: z27.literal(SectionType.products).default(SectionType.products)
|
|
1503
2073
|
});
|
|
1504
2074
|
|
|
1505
2075
|
// src/schema/widgets/checkout-and-beyond/offers.ts
|
|
1506
|
-
var CABOffersSection =
|
|
2076
|
+
var CABOffersSection = z28.lazy(
|
|
1507
2077
|
() => CABLayoutSection.omit({ sections: true, sectionType: true }).extend({
|
|
1508
2078
|
/**
|
|
1509
2079
|
* A rule between consecutive cards in the vertical list — legacy checkout-offers' `<Divider />`,
|
|
@@ -1511,8 +2081,8 @@ var CABOffersSection = z17.lazy(
|
|
|
1511
2081
|
* run of products). Optional, not defaulted: only the v1 converter sets it (REB-24265); a
|
|
1512
2082
|
* hand-built tree keeps React CAB's dividerless rendering.
|
|
1513
2083
|
*/
|
|
1514
|
-
divider:
|
|
1515
|
-
sections:
|
|
2084
|
+
divider: z28.boolean().optional(),
|
|
2085
|
+
sections: z28.union([z28.array(z28.lazy(() => CABSection)), z28.strictObject({}).transform(() => [])]).default(() => [
|
|
1516
2086
|
CABBannerSection.parse({
|
|
1517
2087
|
buttonField: "undoAddToOrder",
|
|
1518
2088
|
color: "success",
|
|
@@ -1526,77 +2096,77 @@ var CABOffersSection = z17.lazy(
|
|
|
1526
2096
|
CABDataSourceSection.parse({}),
|
|
1527
2097
|
CABProductsSection.parse({})
|
|
1528
2098
|
]),
|
|
1529
|
-
sectionType:
|
|
2099
|
+
sectionType: z28.literal(SectionType.offers).default(SectionType.offers)
|
|
1530
2100
|
})
|
|
1531
2101
|
);
|
|
1532
2102
|
|
|
1533
2103
|
// src/schema/widgets/checkout-and-beyond/carousel.ts
|
|
1534
|
-
var CABCarouselSection =
|
|
2104
|
+
var CABCarouselSection = z29.lazy(
|
|
1535
2105
|
() => CABLayoutSection.omit({ sections: true, sectionType: true }).extend({
|
|
1536
|
-
autoAdvanceInterval:
|
|
1537
|
-
itemsAtOnce:
|
|
2106
|
+
autoAdvanceInterval: z29.number().default(0),
|
|
2107
|
+
itemsAtOnce: z29.number().min(1).default(1),
|
|
1538
2108
|
/** Card arrangement for the offers child (`horizontal` = side-by-side) — NOT the carousel's own `direction`. */
|
|
1539
|
-
offersDirection:
|
|
1540
|
-
sections:
|
|
1541
|
-
sectionType:
|
|
2109
|
+
offersDirection: z29.enum(offersDirections).default("vertical"),
|
|
2110
|
+
sections: z29.union([z29.array(z29.lazy(() => CABSection)), z29.strictObject({}).transform(() => [])]).default(() => [CABOffersSection.parse({})]),
|
|
2111
|
+
sectionType: z29.literal(SectionType.carousel).default(SectionType.carousel)
|
|
1542
2112
|
})
|
|
1543
2113
|
);
|
|
1544
2114
|
|
|
1545
2115
|
// src/schema/widgets/checkout-and-beyond/cartLine.ts
|
|
1546
|
-
import { z as
|
|
1547
|
-
var CABCartLineSection =
|
|
2116
|
+
import { z as z30 } from "zod";
|
|
2117
|
+
var CABCartLineSection = z30.lazy(
|
|
1548
2118
|
() => CABLayoutSection.omit({ sections: true, sectionType: true }).extend({
|
|
1549
|
-
sections:
|
|
1550
|
-
sectionType:
|
|
2119
|
+
sections: z30.union([z30.array(z30.lazy(() => CABSection)), z30.strictObject({}).transform(() => [])]).default(() => [CABButtonSection.parse({ action: ButtonAction.switchToSubscription })]),
|
|
2120
|
+
sectionType: z30.literal(SectionType.cartLine).default(SectionType.cartLine)
|
|
1551
2121
|
})
|
|
1552
2122
|
);
|
|
1553
2123
|
|
|
1554
2124
|
// src/schema/widgets/checkout-and-beyond/discount.ts
|
|
1555
2125
|
import { v7 as uuidv77 } from "uuid";
|
|
1556
|
-
import { z as
|
|
1557
|
-
var CABDiscountSection =
|
|
2126
|
+
import { z as z31 } from "zod";
|
|
2127
|
+
var CABDiscountSection = z31.object({
|
|
1558
2128
|
...CABVariableFields,
|
|
1559
|
-
amount:
|
|
1560
|
-
discountedBy:
|
|
1561
|
-
discountedFrom:
|
|
1562
|
-
message:
|
|
1563
|
-
name:
|
|
1564
|
-
quantity:
|
|
1565
|
-
sectionId:
|
|
1566
|
-
sectionType:
|
|
1567
|
-
type:
|
|
2129
|
+
amount: z31.union([z31.number(), z31.string()]).catch(0),
|
|
2130
|
+
discountedBy: z31.string().optional(),
|
|
2131
|
+
discountedFrom: z31.enum(discountSources).optional().catch(void 0),
|
|
2132
|
+
message: z31.string().optional(),
|
|
2133
|
+
name: z31.string().optional(),
|
|
2134
|
+
quantity: z31.number().default(1),
|
|
2135
|
+
sectionId: z31.uuid().default(() => uuidv77()),
|
|
2136
|
+
sectionType: z31.literal(SectionType.discount).default(SectionType.discount),
|
|
2137
|
+
type: z31.enum(discountTypes).catch("none")
|
|
1568
2138
|
});
|
|
1569
2139
|
|
|
1570
2140
|
// src/schema/widgets/checkout-and-beyond/monetize.ts
|
|
1571
2141
|
import { v7 as uuidv78 } from "uuid";
|
|
1572
|
-
import { z as
|
|
1573
|
-
var CABMonetizeAds =
|
|
1574
|
-
requestId:
|
|
2142
|
+
import { z as z32 } from "zod";
|
|
2143
|
+
var CABMonetizeAds = z32.object({
|
|
2144
|
+
requestId: z32.string().optional(),
|
|
1575
2145
|
/** Defaulted here, never in the beacon: absence of information is `ads` itself being absent. */
|
|
1576
|
-
testing:
|
|
2146
|
+
testing: z32.boolean().default(false)
|
|
1577
2147
|
});
|
|
1578
|
-
var CABMonetizeSection =
|
|
2148
|
+
var CABMonetizeSection = z32.object({
|
|
1579
2149
|
...CABVariableFields,
|
|
1580
2150
|
ads: CABMonetizeAds.optional(),
|
|
1581
|
-
name:
|
|
2151
|
+
name: z32.string().optional(),
|
|
1582
2152
|
/**
|
|
1583
2153
|
* Server-hydrated offer cards (inline hydration): when `/cab/sections` was asked with a `monetize`
|
|
1584
2154
|
* context block, the composed cards ride the marker itself — `[]` means "hydrated, no fill" (render
|
|
1585
2155
|
* nothing, skip the fetch); ABSENT means "not hydrated" (the client fetches via `/cab/monetize`).
|
|
1586
2156
|
*/
|
|
1587
|
-
offers:
|
|
2157
|
+
offers: z32.array(z32.lazy(() => CABSection)).optional(),
|
|
1588
2158
|
/** Privacy Policy footer href (the widget's `privacy_policy_url` setting); read server-side for hydration, relayed by the client only on the legacy `/cab/monetize` fallback. */
|
|
1589
|
-
privacyPolicyUrl:
|
|
1590
|
-
sectionId:
|
|
1591
|
-
sectionType:
|
|
2159
|
+
privacyPolicyUrl: z32.string().optional(),
|
|
2160
|
+
sectionId: z32.uuid().default(() => uuidv78()),
|
|
2161
|
+
sectionType: z32.literal(SectionType.monetize).default(SectionType.monetize),
|
|
1592
2162
|
/** Ads-taxonomy sub-vertical id (the widget's `sub_vertical` setting); paired with `vertical`. */
|
|
1593
|
-
subVertical:
|
|
2163
|
+
subVertical: z32.string().optional(),
|
|
1594
2164
|
/** Ads-taxonomy vertical id (the widget's `vertical` setting) — with `subVertical`, the admin Monetize editor's two configurables. */
|
|
1595
|
-
vertical:
|
|
2165
|
+
vertical: z32.string().optional()
|
|
1596
2166
|
});
|
|
1597
2167
|
|
|
1598
2168
|
// src/schema/widgets/checkout-and-beyond/progressBar.ts
|
|
1599
|
-
import { z as
|
|
2169
|
+
import { z as z33 } from "zod";
|
|
1600
2170
|
|
|
1601
2171
|
// src/schema/widgets/checkout-and-beyond/giftLabels.ts
|
|
1602
2172
|
var GIFT_LABELS = {
|
|
@@ -1715,34 +2285,34 @@ var GIFT_LABELS = {
|
|
|
1715
2285
|
};
|
|
1716
2286
|
|
|
1717
2287
|
// src/schema/widgets/checkout-and-beyond/progressBar.ts
|
|
1718
|
-
var CABProgressTierExclusion =
|
|
1719
|
-
collectionIds:
|
|
1720
|
-
productIds:
|
|
1721
|
-
tags:
|
|
1722
|
-
type:
|
|
2288
|
+
var CABProgressTierExclusion = z33.object({
|
|
2289
|
+
collectionIds: z33.array(z33.number().int().positive()).default(() => []),
|
|
2290
|
+
productIds: z33.array(z33.number().int().positive()).default(() => []),
|
|
2291
|
+
tags: z33.array(z33.string().min(1)).default(() => []),
|
|
2292
|
+
type: z33.enum(progressTierExclusionTypes)
|
|
1723
2293
|
});
|
|
1724
|
-
var CABProgressTier =
|
|
2294
|
+
var CABProgressTier = z33.object({
|
|
1725
2295
|
/**
|
|
1726
2296
|
* Order-discount tiers only (onsite `discount`): the reward the order-discount Function applies at
|
|
1727
2297
|
* cart time, carried so the client can resolve the `{{discountAmount}}` token in this tier's copy.
|
|
1728
2298
|
* `fixedAmount`: shop-currency CENTS (FX-scaled by the client, like {@link CABProgressTier.threshold});
|
|
1729
2299
|
* `percentage`: the whole-percent number rendered raw (`{n}%`). Absent on non-discount tiers.
|
|
1730
2300
|
*/
|
|
1731
|
-
discount:
|
|
2301
|
+
discount: z33.object({ amount: z33.number().nonnegative(), type: z33.enum(["fixedAmount", "percentage"]) }).optional(),
|
|
1732
2302
|
/**
|
|
1733
2303
|
* Per-tier exclusion rules (onsite `exclusions` when `exclusions_enabled`): lines they match don't count
|
|
1734
2304
|
* toward this tier. Empty (the default, and the converter's output for a disabled or ruleless tier) means
|
|
1735
2305
|
* the tier measures the plain bar-level adjusted progress. Consumers read ONLY the list named by the rule's
|
|
1736
2306
|
* `type` — the converter leaves the other two at `[]`, so a populated off-type list never reaches the tree.
|
|
1737
2307
|
*/
|
|
1738
|
-
exclusions:
|
|
2308
|
+
exclusions: z33.array(CABProgressTierExclusion).default(() => []),
|
|
1739
2309
|
/**
|
|
1740
2310
|
* Smart-cart product (gift) tiers only (onsite `filter_oos_variants`): when set, the client drops this
|
|
1741
2311
|
* tier entirely once its gift has NO in-stock eligible variant — the milestone, fill slot and gift
|
|
1742
2312
|
* controls all disappear, mirroring onsite removing the tier from `bar.tiers` before it renders. Absent
|
|
1743
2313
|
* on shipping/discount/decorative tiers and left off by default (the tier renders regardless of stock).
|
|
1744
2314
|
*/
|
|
1745
|
-
filterOosVariants:
|
|
2315
|
+
filterOosVariants: z33.boolean().default(false),
|
|
1746
2316
|
/**
|
|
1747
2317
|
* Smart-cart product (gift) tiers only: the reward unlocked at this tier. The client enriches
|
|
1748
2318
|
* `productId`/`variantIds` to live variants (availability, price) and adds the chosen one to the cart
|
|
@@ -1750,10 +2320,10 @@ var CABProgressTier = z22.object({
|
|
|
1750
2320
|
* tiers and on tiers with no configured product. Cart add/remove is a later stage; carried here so the
|
|
1751
2321
|
* tree is complete.
|
|
1752
2322
|
*/
|
|
1753
|
-
gift:
|
|
1754
|
-
productId:
|
|
1755
|
-
title:
|
|
1756
|
-
variantIds:
|
|
2323
|
+
gift: z33.object({
|
|
2324
|
+
productId: z33.number().int().positive(),
|
|
2325
|
+
title: z33.string().optional(),
|
|
2326
|
+
variantIds: z33.array(z33.number().int().positive()).default(() => [])
|
|
1757
2327
|
}).optional(),
|
|
1758
2328
|
/**
|
|
1759
2329
|
* The step-row icon's square size in PIXELS (onsite `product_tier_element_size`, an admin px string the
|
|
@@ -1773,7 +2343,7 @@ var CABProgressTier = z22.object({
|
|
|
1773
2343
|
* renderer never spells a fallback), which also makes a tree authored before this field render at
|
|
1774
2344
|
* React's size rather than the old hardcoded 20.
|
|
1775
2345
|
*/
|
|
1776
|
-
iconSizePx:
|
|
2346
|
+
iconSizePx: z33.number().int().min(1).max(64).default(18),
|
|
1777
2347
|
/**
|
|
1778
2348
|
* The step-row label at this tier's slot (onsite `getTierLabel`): the merchant custom label, else the
|
|
1779
2349
|
* type default — 'Free Shipping' or '{n}% Discount'. A PRODUCT tier gets NO converter label: its name is
|
|
@@ -1783,35 +2353,35 @@ var CABProgressTier = z22.object({
|
|
|
1783
2353
|
* turns a blank label into an absent key, and a client-authored blank means the same thing — the renderer
|
|
1784
2354
|
* emits no caption slot rather than an empty one). Plain text — a compact icon+label.
|
|
1785
2355
|
*/
|
|
1786
|
-
label:
|
|
2356
|
+
label: z33.record(z33.string(), z33.string()).optional(),
|
|
1787
2357
|
/**
|
|
1788
2358
|
* Smart-cart product (gift) tiers only (onsite `product_tier_image`): show the gift's product image in
|
|
1789
2359
|
* the step-row slot instead of the generic bag icon. The converter can't supply the image URL (config
|
|
1790
2360
|
* carries only product IDs), so the client resolves it (authoritative Shopify featured image, React
|
|
1791
2361
|
* parity) and falls back to the bag icon when the flag is off or no image resolves. Default false.
|
|
1792
2362
|
*/
|
|
1793
|
-
productTierImage:
|
|
2363
|
+
productTierImage: z33.boolean().default(false),
|
|
1794
2364
|
/**
|
|
1795
2365
|
* The tier's goal, measured per `unit`. `currency` (default): SHOP-currency cents — the canonical
|
|
1796
2366
|
* fallback: when the buyer's presentment currency has no entry in `thresholds`, the client
|
|
1797
2367
|
* multiplies this by the live FX rate (converter maps the v1 `unlock_price`, e.g. `75` → `7500`).
|
|
1798
2368
|
* `item`: a raw item count — no cents, no FX, no `thresholds` overrides.
|
|
1799
2369
|
*/
|
|
1800
|
-
threshold:
|
|
2370
|
+
threshold: z33.number().int().nonnegative(),
|
|
1801
2371
|
/**
|
|
1802
2372
|
* Per-presentment-currency goal overrides — lowercased ISO currency code → cents (e.g. `{ cad: 9900 }`).
|
|
1803
2373
|
* When the buyer's currency has an entry, the client uses it verbatim INSTEAD of FX-converting
|
|
1804
2374
|
* `threshold`, so merchants show intentional, clean per-market amounts (Shopify Markets) rather than a
|
|
1805
2375
|
* converted base. Defaults to `{}` — no overrides means always fall back to `threshold`.
|
|
1806
2376
|
*/
|
|
1807
|
-
thresholds:
|
|
2377
|
+
thresholds: z33.record(z33.string(), z33.number().int().nonnegative()).default(() => ({})),
|
|
1808
2378
|
/**
|
|
1809
2379
|
* Smart-cart tier identifier, mirrored onto the gift cart line's `_tier` attribute so the client can
|
|
1810
2380
|
* match an in-cart gift back to its tier (add on reach, remove on downgrade/decline). Absent on the
|
|
1811
2381
|
* shipping bar (single unlabeled tier, no gift).
|
|
1812
2382
|
*/
|
|
1813
|
-
tierId:
|
|
1814
|
-
tierType:
|
|
2383
|
+
tierId: z33.string().optional(),
|
|
2384
|
+
tierType: z33.enum(progressTierTypes).default(ProgressTierType.shipping),
|
|
1815
2385
|
/**
|
|
1816
2386
|
* What this tier's progress is measured in — PER TIER (onsite's `unlock_condition_type` model, NOT
|
|
1817
2387
|
* the React port's bar-level `tiers[0]` shortcut). `currency` compares money cents against the
|
|
@@ -1822,9 +2392,9 @@ var CABProgressTier = z22.object({
|
|
|
1822
2392
|
* count carries no injected noun (no English `item`/`items` inflection on localized copy); merchant
|
|
1823
2393
|
* template text supplies grammatically-neutral wording around the token.
|
|
1824
2394
|
*/
|
|
1825
|
-
unit:
|
|
2395
|
+
unit: z33.enum(progressTierUnits).default(ProgressTierUnit.currency)
|
|
1826
2396
|
});
|
|
1827
|
-
var CABProgressBarSection =
|
|
2397
|
+
var CABProgressBarSection = z33.lazy(
|
|
1828
2398
|
() => CABLayoutSection.omit({ sectionType: true }).extend({
|
|
1829
2399
|
/**
|
|
1830
2400
|
* The onsite bar id (smart-cart TPB bars only). The client mirrors it to the cart's `_barId` attribute
|
|
@@ -1833,35 +2403,35 @@ var CABProgressBarSection = z22.lazy(
|
|
|
1833
2403
|
* checkout reaches the extension without it, so the client writes it or the tier gift's 100% discount
|
|
1834
2404
|
* (and any tier order discount) never applies. Absent on the v1 shipping bar (no gift tiers, no id).
|
|
1835
2405
|
*/
|
|
1836
|
-
barId:
|
|
2406
|
+
barId: z33.string().optional(),
|
|
1837
2407
|
/** Empty = shown everywhere; otherwise uppercase ISO country codes. */
|
|
1838
|
-
countryCodes:
|
|
2408
|
+
countryCodes: z33.array(z33.string().transform((code) => code.trim().toUpperCase())).default(() => []),
|
|
1839
2409
|
/**
|
|
1840
2410
|
* Uppercase ISO codes where a higher-precedence geo bar applies, so this (default, non-geo) bar is
|
|
1841
2411
|
* hidden there — replicates React `getbar`'s geo-match-over-default precedence. Empty = never excluded.
|
|
1842
2412
|
*/
|
|
1843
|
-
excludeCountryCodes:
|
|
2413
|
+
excludeCountryCodes: z33.array(z33.string().transform((code) => code.trim().toUpperCase())).default(() => []),
|
|
1844
2414
|
/**
|
|
1845
2415
|
* Measure progress against the PRE-discount subtotal (onsite `exclude_discount_in_subtotal`, and
|
|
1846
2416
|
* forced on for bars with a discount tier so its own reward can't un-reach the tier). The client
|
|
1847
2417
|
* adds the cart's discount allocations back to the live subtotal.
|
|
1848
2418
|
*/
|
|
1849
|
-
excludeDiscountInSubtotal:
|
|
2419
|
+
excludeDiscountInSubtotal: z33.boolean().default(false),
|
|
1850
2420
|
/** Variant ids the client subtracts from the adjusted subtotal (React `bar.excluded_products`). */
|
|
1851
|
-
excludedVariantIds:
|
|
2421
|
+
excludedVariantIds: z33.array(z33.number().int().positive()).default(() => []),
|
|
1852
2422
|
/**
|
|
1853
2423
|
* When the cart holds a subscription item, treat the bar as reached regardless of subtotal (React
|
|
1854
2424
|
* `free_shipping_on_subscription(s)`). `freeShippingOnAll` is already a zero-threshold tier instead.
|
|
1855
2425
|
*/
|
|
1856
|
-
freeShippingOnSubscription:
|
|
2426
|
+
freeShippingOnSubscription: z33.boolean().default(false),
|
|
1857
2427
|
/** Localized heading over the gift controls (onsite `gift_area_title`, default 'FREE GIFTS'). */
|
|
1858
|
-
giftAreaTitle:
|
|
2428
|
+
giftAreaTitle: z33.record(z33.string(), z33.string()).default(() => ({ en: "FREE GIFTS" })),
|
|
1859
2429
|
/** Localized gift-control labels (add/remove/heading); a tier's gift `title` beats `yourGift`. */
|
|
1860
|
-
giftLabels:
|
|
1861
|
-
addGift:
|
|
1862
|
-
remove:
|
|
1863
|
-
selectOptions:
|
|
1864
|
-
yourGift:
|
|
2430
|
+
giftLabels: z33.object({
|
|
2431
|
+
addGift: z33.record(z33.string(), z33.string()).default(() => ({ ...GIFT_LABELS.addGift })),
|
|
2432
|
+
remove: z33.record(z33.string(), z33.string()).default(() => ({ ...GIFT_LABELS.remove })),
|
|
2433
|
+
selectOptions: z33.record(z33.string(), z33.string()).default(() => ({ ...GIFT_LABELS.selectOptions })),
|
|
2434
|
+
yourGift: z33.record(z33.string(), z33.string()).default(() => ({ ...GIFT_LABELS.yourGift }))
|
|
1865
2435
|
}).default(() => ({
|
|
1866
2436
|
addGift: { ...GIFT_LABELS.addGift },
|
|
1867
2437
|
remove: { ...GIFT_LABELS.remove },
|
|
@@ -1869,38 +2439,38 @@ var CABProgressBarSection = z22.lazy(
|
|
|
1869
2439
|
yourGift: { ...GIFT_LABELS.yourGift }
|
|
1870
2440
|
})),
|
|
1871
2441
|
/** The bar's a11y label; the merchant names it in the editor, else this default (React parity). */
|
|
1872
|
-
name:
|
|
2442
|
+
name: z33.string().default("Order progress"),
|
|
1873
2443
|
/**
|
|
1874
2444
|
* Bar fill color. An explicit (widget-authored) value is used verbatim; when absent the server
|
|
1875
2445
|
* fills it with the live shop theme's `buttonBackground` (React `useRebuyTheme` parity). Still
|
|
1876
2446
|
* absent after that ⇒ the theme fetch failed and the client renders its platform-default bar —
|
|
1877
2447
|
* optional, not defaulted, so "merchant chose no color" stays distinguishable from any real value.
|
|
1878
2448
|
*/
|
|
1879
|
-
progressColor:
|
|
1880
|
-
sectionType:
|
|
2449
|
+
progressColor: z33.string().optional(),
|
|
2450
|
+
sectionType: z33.literal(SectionType.progressBar).default(SectionType.progressBar),
|
|
1881
2451
|
/**
|
|
1882
2452
|
* The bar's milestones in the merchant's CONFIGURED order — the renderer does NOT sort them (022
|
|
1883
2453
|
* O-B2: sorting by resolved threshold reversed configured milestones on out-of-order or mixed-unit
|
|
1884
2454
|
* count-vs-cents bars). Drives the fill (even slot per tier) and, for smart-cart product tiers, the
|
|
1885
2455
|
* gift add/remove. The visual tier display lives in `sections` (composable), NOT here.
|
|
1886
2456
|
*/
|
|
1887
|
-
tiers:
|
|
2457
|
+
tiers: z33.union([z33.array(CABProgressTier), z33.strictObject({}).transform(() => [])]).default(() => [])
|
|
1888
2458
|
})
|
|
1889
2459
|
);
|
|
1890
2460
|
|
|
1891
2461
|
// src/schema/widgets/checkout-and-beyond/quantity.ts
|
|
1892
2462
|
import { v7 as uuidv79 } from "uuid";
|
|
1893
|
-
import { z as
|
|
1894
|
-
var CABQuantitySection =
|
|
2463
|
+
import { z as z34 } from "zod";
|
|
2464
|
+
var CABQuantitySection = z34.object({
|
|
1895
2465
|
...CABVariableFields,
|
|
1896
|
-
errorMessages:
|
|
1897
|
-
|
|
1898
|
-
|
|
2466
|
+
errorMessages: z34.record(
|
|
2467
|
+
z34.string(),
|
|
2468
|
+
z34.object({
|
|
1899
2469
|
/** `{{step}}` is interpolated at render — the off-increment message React never gave a merchant key. */
|
|
1900
|
-
increment:
|
|
1901
|
-
max:
|
|
1902
|
-
min:
|
|
1903
|
-
neg:
|
|
2470
|
+
increment: z34.string().optional(),
|
|
2471
|
+
max: z34.string().optional(),
|
|
2472
|
+
min: z34.string().optional(),
|
|
2473
|
+
neg: z34.string().optional()
|
|
1904
2474
|
})
|
|
1905
2475
|
).default({
|
|
1906
2476
|
en: {
|
|
@@ -1910,18 +2480,18 @@ var CABQuantitySection = z23.object({
|
|
|
1910
2480
|
neg: "Quantity cannot be negative"
|
|
1911
2481
|
}
|
|
1912
2482
|
}),
|
|
1913
|
-
inputType:
|
|
1914
|
-
max:
|
|
1915
|
-
min:
|
|
2483
|
+
inputType: z34.enum(quantityInputs).default(QuantityInput.select),
|
|
2484
|
+
max: z34.number().min(1).max(100).default(10),
|
|
2485
|
+
min: z34.number().min(1).default(1),
|
|
1916
2486
|
/** The a11y label for the quantity control; the merchant names it in the editor, else this default. */
|
|
1917
|
-
name:
|
|
1918
|
-
sectionId:
|
|
1919
|
-
sectionType:
|
|
2487
|
+
name: z34.string().default("Quantity"),
|
|
2488
|
+
sectionId: z34.uuid().default(() => uuidv79()),
|
|
2489
|
+
sectionType: z34.literal(SectionType.quantity).default(SectionType.quantity)
|
|
1920
2490
|
});
|
|
1921
2491
|
|
|
1922
2492
|
// src/schema/widgets/checkout-and-beyond/reviews.ts
|
|
1923
2493
|
import { v7 as uuidv710 } from "uuid";
|
|
1924
|
-
import { z as
|
|
2494
|
+
import { z as z35 } from "zod";
|
|
1925
2495
|
|
|
1926
2496
|
// src/schema/widgets/checkout-and-beyond/reviewLabels.ts
|
|
1927
2497
|
var REVIEW_LABELS = {
|
|
@@ -1984,49 +2554,49 @@ var REVIEW_LABELS = {
|
|
|
1984
2554
|
};
|
|
1985
2555
|
|
|
1986
2556
|
// src/schema/widgets/checkout-and-beyond/reviews.ts
|
|
1987
|
-
var CABReviewsSection =
|
|
2557
|
+
var CABReviewsSection = z35.object({
|
|
1988
2558
|
...CABVariableFields,
|
|
1989
|
-
color:
|
|
2559
|
+
color: z35.string().default("#fadb14"),
|
|
1990
2560
|
/**
|
|
1991
2561
|
* Localized review-count templates (`{{reviewCount}}`), one/other plural forms; the client picks the
|
|
1992
2562
|
* form via `Intl.PluralRules`. Defaults here (schema-first) so every tree localizes without converter
|
|
1993
2563
|
* or merchant work.
|
|
1994
2564
|
*/
|
|
1995
|
-
countLabels:
|
|
1996
|
-
one:
|
|
1997
|
-
other:
|
|
2565
|
+
countLabels: z35.object({
|
|
2566
|
+
one: z35.record(z35.string(), z35.string()).default(() => ({ ...REVIEW_LABELS.one })),
|
|
2567
|
+
other: z35.record(z35.string(), z35.string()).default(() => ({ ...REVIEW_LABELS.other }))
|
|
1998
2568
|
}).default(() => ({ one: { ...REVIEW_LABELS.one }, other: { ...REVIEW_LABELS.other } })),
|
|
1999
|
-
name:
|
|
2000
|
-
sectionId:
|
|
2001
|
-
sectionType:
|
|
2002
|
-
size:
|
|
2569
|
+
name: z35.string().optional(),
|
|
2570
|
+
sectionId: z35.uuid().default(() => uuidv710()),
|
|
2571
|
+
sectionType: z35.literal(SectionType.reviews).default(SectionType.reviews),
|
|
2572
|
+
size: z35.enum(reviewsSizes).default(ReviewsSize.default),
|
|
2003
2573
|
/**
|
|
2004
2574
|
* Star outline color, interpolated into a style by the renderer, so it is hex-constrained like
|
|
2005
2575
|
* `CABButtonSection.color`. The legacy source (`PostPurchaseSettings.styles.starStrokeColor`) is a free
|
|
2006
2576
|
* string: its converter must validate and fall back to this default rather than let one odd value drop
|
|
2007
2577
|
* the whole reviews section.
|
|
2008
2578
|
*/
|
|
2009
|
-
strokeColor:
|
|
2579
|
+
strokeColor: z35.string().regex(HEX_COLOR_REGEX).default("#fadb14"),
|
|
2010
2580
|
/** Star outline width in px; `0` draws no outline. The legacy `styles.strokeWidth` is a STRING ('1') — its converter coerces. */
|
|
2011
|
-
strokeWidth:
|
|
2581
|
+
strokeWidth: z35.number().nonnegative().default(0)
|
|
2012
2582
|
});
|
|
2013
2583
|
|
|
2014
2584
|
// src/schema/widgets/checkout-and-beyond/separator.ts
|
|
2015
2585
|
import { v7 as uuidv711 } from "uuid";
|
|
2016
|
-
import { z as
|
|
2017
|
-
var CABSeparatorSection =
|
|
2586
|
+
import { z as z36 } from "zod";
|
|
2587
|
+
var CABSeparatorSection = z36.object({
|
|
2018
2588
|
...CABVariableFields,
|
|
2019
|
-
sectionId:
|
|
2020
|
-
sectionType:
|
|
2589
|
+
sectionId: z36.uuid().default(() => uuidv711()),
|
|
2590
|
+
sectionType: z36.literal(SectionType.separator).default(SectionType.separator)
|
|
2021
2591
|
});
|
|
2022
2592
|
|
|
2023
2593
|
// src/schema/widgets/checkout-and-beyond/variants.ts
|
|
2024
2594
|
import { isEmpty, pickBy, slice } from "es-toolkit/compat";
|
|
2025
2595
|
import { v7 as uuidv712 } from "uuid";
|
|
2026
|
-
import { z as
|
|
2027
|
-
var CABVariantsSection =
|
|
2596
|
+
import { z as z37 } from "zod";
|
|
2597
|
+
var CABVariantsSection = z37.object({
|
|
2028
2598
|
...CABVariableFields,
|
|
2029
|
-
hideOutOfStockVariants:
|
|
2599
|
+
hideOutOfStockVariants: z37.boolean().default(false),
|
|
2030
2600
|
/**
|
|
2031
2601
|
* The menu's own label per buyer locale (`selectLanguage`), for the whole-variant (`single`) and the
|
|
2032
2602
|
* product-group menus; `multiple` labels each option's menu by its name. Optional and additive
|
|
@@ -2037,30 +2607,30 @@ var CABVariantsSection = z26.object({
|
|
|
2037
2607
|
* `selectLanguage` returning `''` — which a renderer's `??` fallback would show as an empty menu label.
|
|
2038
2608
|
* A label left with no entries reads as absent.
|
|
2039
2609
|
*/
|
|
2040
|
-
label:
|
|
2610
|
+
label: z37.record(z37.string(), z37.string()).transform((label2) => {
|
|
2041
2611
|
const filled = pickBy(label2, (value) => value.trim() !== "");
|
|
2042
2612
|
return isEmpty(filled) ? void 0 : filled;
|
|
2043
2613
|
}).optional().catch(void 0),
|
|
2044
|
-
name:
|
|
2045
|
-
sectionId:
|
|
2046
|
-
sectionType:
|
|
2047
|
-
selector:
|
|
2614
|
+
name: z37.string().optional(),
|
|
2615
|
+
sectionId: z37.uuid().default(() => uuidv712()),
|
|
2616
|
+
sectionType: z37.literal(SectionType.variants).default(SectionType.variants),
|
|
2617
|
+
selector: z37.enum(slice(variantSelectors, 0, 3)).default(VariantSelector.menu),
|
|
2048
2618
|
/** `productGroup` is additive (REB-24817): a renderer without group support must render nothing for it. */
|
|
2049
|
-
variantMode:
|
|
2619
|
+
variantMode: z37.enum(variantsModes).default(VariantsMode.multiple)
|
|
2050
2620
|
});
|
|
2051
2621
|
|
|
2052
2622
|
// src/schema/widgets/checkout-and-beyond/shared.ts
|
|
2053
|
-
var CABBorder =
|
|
2054
|
-
radius:
|
|
2055
|
-
style:
|
|
2056
|
-
width:
|
|
2623
|
+
var CABBorder = z38.object({
|
|
2624
|
+
radius: z38.enum(borderRadii).default(BorderRadius.base),
|
|
2625
|
+
style: z38.enum(borderStyles).default(BorderStyle.none),
|
|
2626
|
+
width: z38.enum(borderWidths).default(BorderWidth.base)
|
|
2057
2627
|
}).default({
|
|
2058
2628
|
radius: BorderRadius.base,
|
|
2059
2629
|
style: BorderStyle.none,
|
|
2060
2630
|
width: BorderWidth.base
|
|
2061
2631
|
});
|
|
2062
|
-
var CABSection =
|
|
2063
|
-
() =>
|
|
2632
|
+
var CABSection = z38.lazy(
|
|
2633
|
+
() => z38.discriminatedUnion("sectionType", [
|
|
2064
2634
|
CABBannerSection,
|
|
2065
2635
|
CABButtonSection,
|
|
2066
2636
|
CABCarouselSection,
|
|
@@ -2083,17 +2653,17 @@ var CABSection = z27.lazy(
|
|
|
2083
2653
|
);
|
|
2084
2654
|
|
|
2085
2655
|
// src/schema/widgets/checkout-and-beyond/image.ts
|
|
2086
|
-
var CABImageSectionFields =
|
|
2656
|
+
var CABImageSectionFields = z39.object({
|
|
2087
2657
|
...CABVariableFields,
|
|
2088
|
-
altText:
|
|
2658
|
+
altText: z39.string().refine(checkForHTML, NO_HTML).default(""),
|
|
2089
2659
|
/**
|
|
2090
2660
|
* Image width÷height (e.g. `46` for the progress-bar fill); `null` renders at natural size. Any POSITIVE
|
|
2091
2661
|
* ratio — never limited to 1, but never `0`/negative either: a renderer dividing by it would get `Infinity`,
|
|
2092
2662
|
* and CSS `aspect-ratio` drops a negative value and silently reverts to natural size.
|
|
2093
2663
|
*/
|
|
2094
|
-
aspectRatio:
|
|
2095
|
-
border:
|
|
2096
|
-
buttonField:
|
|
2664
|
+
aspectRatio: z39.number().positive().nullable().default(null),
|
|
2665
|
+
border: z39.lazy(() => CABBorder),
|
|
2666
|
+
buttonField: z39.enum(buttonFields).optional(),
|
|
2097
2667
|
/**
|
|
2098
2668
|
* `icon` renders `source` as a single `s-icon` by name (the uber-image path covers icons too), reading
|
|
2099
2669
|
* `iconSize`/`iconTone`. The other values are a harmless informational holdover from the previous
|
|
@@ -2112,30 +2682,30 @@ var CABImageSectionFields = z28.object({
|
|
|
2112
2682
|
* behavior degraded gracefully and told us about it. The client-assembled half of the invariant was never
|
|
2113
2683
|
* the risky half.
|
|
2114
2684
|
*/
|
|
2115
|
-
category:
|
|
2685
|
+
category: z39.enum(["gallery", "icon", "icons", "payment-methods", "secure-checkout"]).nullable().default(null),
|
|
2116
2686
|
/** `icon`-mode only: the `s-icon` `size`; `null` ⇒ the component default (`base`). Ignored by image treatments. */
|
|
2117
|
-
iconSize:
|
|
2687
|
+
iconSize: z39.enum(iconSizes).nullable().default(null),
|
|
2118
2688
|
/** `icon`-mode only: the `s-icon` `tone`; `null` ⇒ the component default (`auto`). Ignored by image treatments. */
|
|
2119
|
-
iconTone:
|
|
2120
|
-
name:
|
|
2121
|
-
naturalHeight:
|
|
2122
|
-
naturalWidth:
|
|
2689
|
+
iconTone: z39.enum(iconTones).nullable().default(null),
|
|
2690
|
+
name: z39.string().optional(),
|
|
2691
|
+
naturalHeight: z39.number().default(0),
|
|
2692
|
+
naturalWidth: z39.number().default(0),
|
|
2123
2693
|
/**
|
|
2124
2694
|
* `contain` (the component default): dimensionless images render in s-image's 1/1 default frame, and
|
|
2125
2695
|
* `cover` would crop non-square art (payment badges lost their edges — 15993); `contain` letterboxes.
|
|
2126
2696
|
*/
|
|
2127
|
-
objectFit:
|
|
2128
|
-
sectionId:
|
|
2129
|
-
sectionType:
|
|
2697
|
+
objectFit: z39.enum(objectFits).default(ObjectFit.contain),
|
|
2698
|
+
sectionId: z39.uuid().default(() => uuidv713()),
|
|
2699
|
+
sectionType: z39.literal(SectionType.image).default(SectionType.image),
|
|
2130
2700
|
/**
|
|
2131
2701
|
* An image URL or dynamic token, an empty default, or — when `category: 'icon'` — an icon NAME. Icon mode
|
|
2132
2702
|
* takes a name or `''` only, NOT a token: no renderer resolves a token to an icon today, so accepting one
|
|
2133
2703
|
* would promise a capability that doesn't exist. Widen the refine's carve-out to `DYNAMIC_TOKEN_REGEX` when a
|
|
2134
2704
|
* server-chosen tier icon actually needs it.
|
|
2135
2705
|
*/
|
|
2136
|
-
source:
|
|
2706
|
+
source: z39.union([z39.url(), z39.literal(""), z39.string().regex(DYNAMIC_TOKEN_REGEX), z39.enum(iconNames)]).default(""),
|
|
2137
2707
|
thumbnails: CABImageThumbnails.optional(),
|
|
2138
|
-
width:
|
|
2708
|
+
width: z39.number().default(100)
|
|
2139
2709
|
});
|
|
2140
2710
|
var iconNameSet = new Set(iconNames);
|
|
2141
2711
|
var isIconName = (source2) => iconNameSet.has(source2);
|
|
@@ -2286,7 +2856,7 @@ var convertContentBlockTextToV2 = (language3, text2 = literalText, textAlign = "
|
|
|
2286
2856
|
};
|
|
2287
2857
|
|
|
2288
2858
|
// src/schema/widgets/checkout-and-beyond/root.ts
|
|
2289
|
-
import { z as
|
|
2859
|
+
import { z as z40 } from "zod";
|
|
2290
2860
|
|
|
2291
2861
|
// src/schema/widgets/checkout-and-beyond/uiLabels.ts
|
|
2292
2862
|
var UI_LABELS = {
|
|
@@ -2306,8 +2876,8 @@ var UI_LABELS = {
|
|
|
2306
2876
|
};
|
|
2307
2877
|
|
|
2308
2878
|
// src/schema/widgets/checkout-and-beyond/root.ts
|
|
2309
|
-
var localized = (fallback) =>
|
|
2310
|
-
var CABUILabels =
|
|
2879
|
+
var localized = (fallback) => z40.record(z40.string(), z40.string()).default(() => ({ ...fallback })).transform((labels) => Object.keys(labels).length > 0 ? labels : { ...fallback });
|
|
2880
|
+
var CABUILabels = z40.object({
|
|
2311
2881
|
currentPage: localized(UI_LABELS.currentPage),
|
|
2312
2882
|
guest: localized(UI_LABELS.guest),
|
|
2313
2883
|
loadingOptions: localized(UI_LABELS.loadingOptions),
|
|
@@ -2332,37 +2902,37 @@ var CABUILabels = z29.object({
|
|
|
2332
2902
|
subscriptionOptions: { ...UI_LABELS.subscriptionOptions },
|
|
2333
2903
|
updating: { ...UI_LABELS.updating }
|
|
2334
2904
|
}));
|
|
2335
|
-
var TargetArea =
|
|
2336
|
-
fill:
|
|
2337
|
-
flip:
|
|
2338
|
-
icon:
|
|
2339
|
-
label:
|
|
2340
|
-
width:
|
|
2905
|
+
var TargetArea = z40.object({
|
|
2906
|
+
fill: z40.boolean().optional(),
|
|
2907
|
+
flip: z40.enum(["both", "horizontal", "vertical"]).optional(),
|
|
2908
|
+
icon: z40.string(),
|
|
2909
|
+
label: z40.string(),
|
|
2910
|
+
width: z40.string()
|
|
2341
2911
|
}).default(targetAreas[EditorMode.checkoutExtension][1]);
|
|
2342
|
-
var CABTracking =
|
|
2343
|
-
enableAttribution:
|
|
2344
|
-
enableSource:
|
|
2345
|
-
enableWidget:
|
|
2912
|
+
var CABTracking = z40.object({
|
|
2913
|
+
enableAttribution: z40.boolean().nullish().transform((value) => value ?? true),
|
|
2914
|
+
enableSource: z40.boolean().nullish().transform((value) => value ?? true),
|
|
2915
|
+
enableWidget: z40.boolean().nullish().transform((value) => value ?? true)
|
|
2346
2916
|
}).nullish().transform((value) => value ?? { enableAttribution: true, enableSource: true, enableWidget: true });
|
|
2347
|
-
var CABExperiment =
|
|
2348
|
-
elementId:
|
|
2349
|
-
experimentId:
|
|
2350
|
-
experimentType:
|
|
2351
|
-
selectedId:
|
|
2917
|
+
var CABExperiment = z40.object({
|
|
2918
|
+
elementId: z40.number(),
|
|
2919
|
+
experimentId: z40.number(),
|
|
2920
|
+
experimentType: z40.string(),
|
|
2921
|
+
selectedId: z40.number()
|
|
2352
2922
|
});
|
|
2353
2923
|
var CABRootSection = CABLayoutSection.extend({
|
|
2354
|
-
editorMode:
|
|
2924
|
+
editorMode: z40.enum(editorModes).default(EditorMode.checkoutExtension),
|
|
2355
2925
|
experiment: CABExperiment.optional(),
|
|
2356
2926
|
// Same rule as CABTracking above: the v1 converters pass raw legacy `previewMode` through, so a
|
|
2357
2927
|
// `null` must fall back rather than throw the widget; malformed values still throw.
|
|
2358
|
-
previewMode:
|
|
2359
|
-
storeId:
|
|
2928
|
+
previewMode: z40.boolean().nullish().transform((value) => value ?? false),
|
|
2929
|
+
storeId: z40.number().nullable().default(null),
|
|
2360
2930
|
targetArea: TargetArea.nullable(),
|
|
2361
2931
|
tracking: CABTracking,
|
|
2362
|
-
type:
|
|
2932
|
+
type: z40.enum(widgetTypes).default(WidgetType.ui_extension_content_block),
|
|
2363
2933
|
uiLabels: CABUILabels,
|
|
2364
|
-
version:
|
|
2365
|
-
widgetId:
|
|
2934
|
+
version: z40.literal(2).default(2),
|
|
2935
|
+
widgetId: z40.number().nullable().default(null)
|
|
2366
2936
|
});
|
|
2367
2937
|
|
|
2368
2938
|
// src/transforms/v1Location.ts
|
|
@@ -3855,172 +4425,10 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3855
4425
|
var parseShielded = (schema, raw, transform = (input) => input) => schema.parse(transform(convertNumericObjects(deepCamelCase(raw))));
|
|
3856
4426
|
|
|
3857
4427
|
// src/transforms/postPurchase/convertMessagePreview.ts
|
|
3858
|
-
import { z as
|
|
4428
|
+
import { z as z47 } from "zod";
|
|
3859
4429
|
|
|
3860
4430
|
// src/transforms/postPurchase/convertCalloutBlock.ts
|
|
3861
|
-
import { z as
|
|
3862
|
-
|
|
3863
|
-
// src/schema/widgets/post-purchase-offer/postPurchaseBlocks.ts
|
|
3864
|
-
import { get as get2, isEqual } from "es-toolkit/compat";
|
|
3865
|
-
import { v4 as uuidv4 } from "uuid";
|
|
3866
|
-
import { z as z31 } from "zod";
|
|
3867
|
-
|
|
3868
|
-
// src/schema/widgets/post-purchase-offer/constants.ts
|
|
3869
|
-
import { z as z30 } from "zod";
|
|
3870
|
-
var blockTypes = [
|
|
3871
|
-
"callout_banner",
|
|
3872
|
-
"image",
|
|
3873
|
-
"image_content",
|
|
3874
|
-
"pay_button",
|
|
3875
|
-
"product_information",
|
|
3876
|
-
"separator",
|
|
3877
|
-
"text",
|
|
3878
|
-
"timer"
|
|
3879
|
-
];
|
|
3880
|
-
var blockType = z30.enum(blockTypes);
|
|
3881
|
-
var BlockType = blockType.enum;
|
|
3882
|
-
|
|
3883
|
-
// src/schema/widgets/post-purchase-offer/postPurchaseBlocks.ts
|
|
3884
|
-
var TextAlignment2 = z31.union([z31.enum(["leading", "center", "trailing"]), z31.unknown().transform(() => "leading")]);
|
|
3885
|
-
var TextSize = z31.union([
|
|
3886
|
-
z31.enum(["xsmall", "small", "base", "default", "medium", "large", "xlarge"]),
|
|
3887
|
-
z31.unknown().transform(() => "medium")
|
|
3888
|
-
]);
|
|
3889
|
-
var CalloutBannerBlock = z31.object({
|
|
3890
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3891
|
-
name: z31.string().optional(),
|
|
3892
|
-
settings: z31.object({
|
|
3893
|
-
superTitle: z31.union([z31.string(), TiptapDocument]).default(() => "Limited-time Offer"),
|
|
3894
|
-
title: z31.union([z31.string(), TiptapDocument]).default(() => "Add this customer favorite to your order")
|
|
3895
|
-
}),
|
|
3896
|
-
styles: z31.object({
|
|
3897
|
-
superTitleEmphasized: z31.boolean().default(false),
|
|
3898
|
-
superTitleSize: TextSize.default("large"),
|
|
3899
|
-
textAlignment: TextAlignment2.default("center"),
|
|
3900
|
-
titleEmphasized: z31.boolean().default(true),
|
|
3901
|
-
titleSize: TextSize.default("large")
|
|
3902
|
-
}),
|
|
3903
|
-
type: z31.literal(BlockType.callout_banner).default(BlockType.callout_banner)
|
|
3904
|
-
});
|
|
3905
|
-
var ImageBlock = z31.object({
|
|
3906
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3907
|
-
name: z31.string().optional(),
|
|
3908
|
-
settings: z31.union([
|
|
3909
|
-
z31.object({
|
|
3910
|
-
/** This isn't used in the post-purchase offer widget */
|
|
3911
|
-
alt: z31.string().optional().default(""),
|
|
3912
|
-
image: z31.union([z31.literal(""), z31.url()]).default(""),
|
|
3913
|
-
imageSize: z31.number().default(20),
|
|
3914
|
-
imageType: z31.enum(["URL", "UPLOAD"]).default("URL")
|
|
3915
|
-
}),
|
|
3916
|
-
z31.array(z31.never()).transform(() => ({}))
|
|
3917
|
-
]),
|
|
3918
|
-
type: z31.literal(BlockType.image).default(BlockType.image)
|
|
3919
|
-
});
|
|
3920
|
-
var ImageContentBlock = z31.object({
|
|
3921
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3922
|
-
name: z31.string().optional(),
|
|
3923
|
-
settings: z31.object({
|
|
3924
|
-
alt: z31.string().optional().default(""),
|
|
3925
|
-
description: z31.union([z31.string(), TiptapDocument, z31.null()]).default(
|
|
3926
|
-
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
|
3927
|
-
),
|
|
3928
|
-
descriptionSize: TextSize.default("medium"),
|
|
3929
|
-
horizontalAlignment: TextAlignment2.default("leading"),
|
|
3930
|
-
image: z31.union([z31.literal(""), z31.url()]).default(""),
|
|
3931
|
-
imagePlacement: z31.enum(["bottom", "left", "right", "top"]).optional().default("left"),
|
|
3932
|
-
imageSize: z31.number().default(20),
|
|
3933
|
-
superTitle: z31.union([z31.string(), TiptapDocument]).default("Lorem Ipsum!"),
|
|
3934
|
-
superTitleSize: TextSize.default("medium"),
|
|
3935
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
3936
|
-
swapContent: z31.boolean().optional().default(false),
|
|
3937
|
-
title: z31.union([z31.string(), TiptapDocument]).nullable().default("Lorem ipsum dolor sit amet"),
|
|
3938
|
-
titleSize: TextSize.default("medium")
|
|
3939
|
-
}),
|
|
3940
|
-
type: z31.literal(BlockType.image_content).default(BlockType.image_content)
|
|
3941
|
-
});
|
|
3942
|
-
var PayButtonBlock = z31.object({
|
|
3943
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3944
|
-
name: z31.string().optional(),
|
|
3945
|
-
settings: z31.union([
|
|
3946
|
-
z31.object({
|
|
3947
|
-
label: z31.string().default("Pay Now")
|
|
3948
|
-
}),
|
|
3949
|
-
z31.array(z31.never()).transform(() => ({
|
|
3950
|
-
label: "Pay Now"
|
|
3951
|
-
}))
|
|
3952
|
-
]),
|
|
3953
|
-
styles: z31.union([
|
|
3954
|
-
z31.object({
|
|
3955
|
-
buttonWidth: z31.number().min(10).max(100).default(50)
|
|
3956
|
-
}),
|
|
3957
|
-
z31.array(z31.never()).transform(() => ({
|
|
3958
|
-
buttonWidth: 50
|
|
3959
|
-
}))
|
|
3960
|
-
]),
|
|
3961
|
-
type: z31.literal(BlockType.pay_button).default(BlockType.pay_button)
|
|
3962
|
-
});
|
|
3963
|
-
var ProductInformationBlock = z31.object({
|
|
3964
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
3965
|
-
hideProductImage: z31.boolean().optional().default(false),
|
|
3966
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3967
|
-
name: z31.string().optional(),
|
|
3968
|
-
type: z31.literal(BlockType.product_information).default(BlockType.product_information)
|
|
3969
|
-
});
|
|
3970
|
-
var SeparatorBlock = z31.object({
|
|
3971
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3972
|
-
name: z31.string().optional(),
|
|
3973
|
-
type: z31.literal(BlockType.separator).default(BlockType.separator)
|
|
3974
|
-
});
|
|
3975
|
-
var TextBlock = z31.object({
|
|
3976
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3977
|
-
name: z31.string().optional(),
|
|
3978
|
-
settings: z31.object({
|
|
3979
|
-
description: z31.union([z31.string(), TiptapDocument]).optional().default(
|
|
3980
|
-
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
|
3981
|
-
),
|
|
3982
|
-
/** This isn't used in the post-purchase offer widget */
|
|
3983
|
-
descriptionSize: TextSize.default("medium"),
|
|
3984
|
-
horizontalAlignment: TextAlignment2.default("leading"),
|
|
3985
|
-
superTitle: z31.union([z31.string(), TiptapDocument]).default("Super Title"),
|
|
3986
|
-
/** This isn't used in the post-purchase offer widget */
|
|
3987
|
-
superTitleSize: TextSize.default("medium"),
|
|
3988
|
-
title: z31.union([z31.string(), TiptapDocument]).nullable().default("Lorem ipsum dolor sit amet."),
|
|
3989
|
-
/** This isn't used in the post-purchase offer widget */
|
|
3990
|
-
titleSize: TextSize.default("medium")
|
|
3991
|
-
}),
|
|
3992
|
-
type: z31.literal(BlockType.text).default(BlockType.text)
|
|
3993
|
-
});
|
|
3994
|
-
var TimerBlock = z31.object({
|
|
3995
|
-
id: z31.string().optional().default(() => uuidv4()),
|
|
3996
|
-
name: z31.string().optional(),
|
|
3997
|
-
settings: z31.object({
|
|
3998
|
-
durationMinutes: z31.number().min(0).default(10),
|
|
3999
|
-
durationSeconds: z31.union([z31.number().min(0), z31.unknown().transform(() => 0)]).default(0),
|
|
4000
|
-
endOffer: z31.boolean().default(true),
|
|
4001
|
-
title: z31.union([z31.string(), TiptapDocument]).default("Hurry \u2014 your special offer ends in:")
|
|
4002
|
-
}),
|
|
4003
|
-
styles: z31.object({
|
|
4004
|
-
textAlignment: TextAlignment2.default("center"),
|
|
4005
|
-
timerBackgroundColor: z31.enum(["info", "warning", "success", "critical"]).default("warning"),
|
|
4006
|
-
timerEmphasized: z31.union([z31.boolean(), z31.unknown().transform(() => true)]).default(true),
|
|
4007
|
-
timerSize: TextSize.default("medium"),
|
|
4008
|
-
timerWidth: z31.number().min(10).max(100).optional().default(100),
|
|
4009
|
-
titleEmphasized: z31.union([z31.boolean(), z31.unknown().transform(() => true)]).default(false),
|
|
4010
|
-
titleSize: TextSize.default("medium")
|
|
4011
|
-
}),
|
|
4012
|
-
type: z31.literal(BlockType.timer).default(BlockType.timer)
|
|
4013
|
-
});
|
|
4014
|
-
var PostPurchaseBlock = z31.discriminatedUnion("type", [
|
|
4015
|
-
CalloutBannerBlock,
|
|
4016
|
-
ImageBlock,
|
|
4017
|
-
ImageContentBlock,
|
|
4018
|
-
PayButtonBlock,
|
|
4019
|
-
ProductInformationBlock,
|
|
4020
|
-
SeparatorBlock,
|
|
4021
|
-
TextBlock,
|
|
4022
|
-
TimerBlock
|
|
4023
|
-
]);
|
|
4431
|
+
import { z as z41 } from "zod";
|
|
4024
4432
|
|
|
4025
4433
|
// src/transforms/postPurchase/styledTextRun.ts
|
|
4026
4434
|
var sizes = {
|
|
@@ -4044,7 +4452,7 @@ var styledTextRun = (text2, emphasized, size) => ({
|
|
|
4044
4452
|
// src/transforms/postPurchase/convertCalloutBlock.ts
|
|
4045
4453
|
var fields = ["superTitle", "title"];
|
|
4046
4454
|
var Shell = CalloutBannerBlock.pick({ styles: true }).extend({
|
|
4047
|
-
settings:
|
|
4455
|
+
settings: z41.object({ superTitle: z41.unknown().optional(), title: z41.unknown().optional() })
|
|
4048
4456
|
});
|
|
4049
4457
|
var convertCalloutBlock = (raw, report) => {
|
|
4050
4458
|
const omit = (detail) => {
|
|
@@ -4079,7 +4487,7 @@ var convertCalloutBlock = (raw, report) => {
|
|
|
4079
4487
|
};
|
|
4080
4488
|
|
|
4081
4489
|
// src/transforms/postPurchase/convertImageFrame.ts
|
|
4082
|
-
import { z as
|
|
4490
|
+
import { z as z42 } from "zod";
|
|
4083
4491
|
|
|
4084
4492
|
// src/transforms/postPurchase/sizeImageSource.ts
|
|
4085
4493
|
var sizeImageSource = (source2, size) => {
|
|
@@ -4091,7 +4499,7 @@ var sizeImageSource = (source2, size) => {
|
|
|
4091
4499
|
};
|
|
4092
4500
|
|
|
4093
4501
|
// src/transforms/postPurchase/convertImageFrame.ts
|
|
4094
|
-
var Source =
|
|
4502
|
+
var Source = z42.url().default("https://cdn.shopify.com/s/files/1/0605/0555/5177/files/placeholder.jpg?v=1657762972");
|
|
4095
4503
|
var DEFAULT_IMAGE_SIZE = 20;
|
|
4096
4504
|
var convertImageFrame = (raw) => {
|
|
4097
4505
|
const parsed = ImageBlock.shape.settings.options[0].safeParse(raw);
|
|
@@ -4137,7 +4545,7 @@ var convertImageBlock = (raw, report) => {
|
|
|
4137
4545
|
};
|
|
4138
4546
|
|
|
4139
4547
|
// src/transforms/postPurchase/convertImageContentBlock.ts
|
|
4140
|
-
import { z as
|
|
4548
|
+
import { z as z44 } from "zod";
|
|
4141
4549
|
|
|
4142
4550
|
// src/transforms/postPurchase/convertImageContentText.ts
|
|
4143
4551
|
var fields2 = ["superTitle", "title", "description"];
|
|
@@ -4165,283 +4573,95 @@ var convertImageContentText = (settings, report) => {
|
|
|
4165
4573
|
});
|
|
4166
4574
|
return CABLayoutSection.parse({ alignment: { horizontal }, sections });
|
|
4167
4575
|
};
|
|
4168
|
-
|
|
4169
|
-
// src/transforms/postPurchase/imageContentSettings.ts
|
|
4170
|
-
import { z as
|
|
4171
|
-
var ImageContentSettings = ImageContentBlock.shape.settings.pick({
|
|
4172
|
-
descriptionSize: true,
|
|
4173
|
-
horizontalAlignment: true,
|
|
4174
|
-
superTitleSize: true,
|
|
4175
|
-
swapContent: true,
|
|
4176
|
-
titleSize: true
|
|
4177
|
-
}).extend({
|
|
4178
|
-
alt:
|
|
4179
|
-
description:
|
|
4180
|
-
image:
|
|
4181
|
-
imagePlacement: ImageContentBlock.shape.settings.shape.imagePlacement.unwrap().nullable(),
|
|
4182
|
-
imageSize: ImageContentBlock.shape.settings.shape.imageSize.pipe(
|
|
4183
|
-
superTitle:
|
|
4184
|
-
title:
|
|
4185
|
-
});
|
|
4186
|
-
|
|
4187
|
-
// src/transforms/postPurchase/convertImageContentBlock.ts
|
|
4188
|
-
var Shell2 = z35.object({ settings: ImageContentSettings, type: z35.literal("image_content") });
|
|
4189
|
-
var PROPORTIONAL_BREAK_AT = 500;
|
|
4190
|
-
var convertImageContentBlock = (raw, report) => {
|
|
4191
|
-
const shell = Shell2.safeParse(raw);
|
|
4192
|
-
if (!shell.success) {
|
|
4193
|
-
report?.({ detail: "Unreadable post-purchase image-content block", kind: "section" });
|
|
4194
|
-
return [];
|
|
4195
|
-
}
|
|
4196
|
-
const settings = shell.data.settings;
|
|
4197
|
-
const alt = typeof settings.alt === "string" ? settings.alt : void 0;
|
|
4198
|
-
if (settings.alt !== void 0 && alt === void 0) {
|
|
4199
|
-
report?.({ detail: "Unreadable post-purchase image-content alt text", kind: "section" });
|
|
4200
|
-
}
|
|
4201
|
-
const image = convertImageFrame({ alt, image: settings.image, imageSize: settings.imageSize });
|
|
4202
|
-
if (!image) report?.({ detail: "Unreadable post-purchase image-content image", kind: "section" });
|
|
4203
|
-
const text2 = convertImageContentText(settings, report);
|
|
4204
|
-
const textFrame = CABLayoutSection.parse({ direction: "grid", grid: { columns: ["100%"] }, sections: [text2] });
|
|
4205
|
-
const placement = settings.imagePlacement ?? (settings.swapContent ? "right" : "left");
|
|
4206
|
-
const imageFirst = placement === "left" || placement === "top";
|
|
4207
|
-
const sections = imageFirst ? [image, textFrame] : [textFrame, image];
|
|
4208
|
-
const ordered = sections.filter((section) => section !== null);
|
|
4209
|
-
const inner = placement === "left" || placement === "right" ? CABLayoutSection.parse({
|
|
4210
|
-
sections: ordered,
|
|
4211
|
-
spacing: "loose",
|
|
4212
|
-
wrapping: {
|
|
4213
|
-
breakAt: settings.imageSize <= 1 ? PROPORTIONAL_BREAK_AT : settings.imageSize,
|
|
4214
|
-
maxPerLine: 2
|
|
4215
|
-
}
|
|
4216
|
-
}) : CABLayoutSection.parse({
|
|
4217
|
-
direction: "grid",
|
|
4218
|
-
grid: { columns: ["100%"] },
|
|
4219
|
-
sections: [CABLayoutSection.parse({ sections: ordered })],
|
|
4220
|
-
width: 95
|
|
4221
|
-
});
|
|
4222
|
-
return [CABLayoutSection.parse({ frame: "offer-content", sections: [inner], spacing: "none" })];
|
|
4223
|
-
};
|
|
4224
|
-
|
|
4225
|
-
// src/transforms/postPurchase/productCardPay.ts
|
|
4226
|
-
import { mapValues } from "es-toolkit";
|
|
4227
|
-
var PAY_NOW = { en: "Pay Now" };
|
|
4228
|
-
var DECLINE_THIS_OFFER = { en: "Decline this offer" };
|
|
4229
|
-
var NOT_AVAILABLE_DEFAULTS = ["Not available"];
|
|
4230
|
-
var soldOutSlot = ({ language: language3 }) => labelSlot("soldOut", OFFER_LABELS.soldOut, language3.global.notAvailable, [], NOT_AVAILABLE_DEFAULTS);
|
|
4231
|
-
var productCardPay = (settings) => {
|
|
4232
|
-
const payNow = labelSlot("payNow", PAY_NOW, settings.language.global.payNow);
|
|
4233
|
-
const priced = settings.language.displayCtaPrice ? { ...payNow, content: mapValues(payNow.content, (label2) => `${label2} \xB7 {{total}}`) } : payNow;
|
|
4234
|
-
return [addToOrderButton("primary", [priced, soldOutSlot(settings)])];
|
|
4235
|
-
};
|
|
4236
|
-
var productCardDecline = ({ language: language3 }) => [
|
|
4237
|
-
{
|
|
4238
|
-
action: "declineOffer",
|
|
4239
|
-
buttonStyle: "secondary",
|
|
4240
|
-
sections: [labelSlot("declineOffer", DECLINE_THIS_OFFER, language3.global.declineThisOffer)],
|
|
4241
|
-
sectionType: "button"
|
|
4242
|
-
}
|
|
4243
|
-
];
|
|
4244
|
-
|
|
4245
|
-
// src/transforms/postPurchase/productCardSettings.ts
|
|
4246
|
-
import { mapValues as mapValues2 } from "es-toolkit";
|
|
4247
|
-
import { z as z38 } from "zod";
|
|
4248
|
-
|
|
4249
|
-
// src/schema/widgets/post-purchase-offer/postPurchaseSettings.ts
|
|
4250
|
-
import { z as z37 } from "zod";
|
|
4251
|
-
|
|
4252
|
-
// src/schema/widgets/post-purchase-offer/common.ts
|
|
4253
|
-
import { replace, toNumber, toString } from "es-toolkit/compat";
|
|
4254
|
-
import { z as z36 } from "zod";
|
|
4255
|
-
var strictNumber = z36.union([
|
|
4256
|
-
z36.number(),
|
|
4257
|
-
z36.string().transform((value) => toNumber(replace(toString(value), /[^0-9.]/g, "")) || 0),
|
|
4258
|
-
z36.unknown().transform(() => 0)
|
|
4259
|
-
]);
|
|
4260
|
-
|
|
4261
|
-
// src/schema/widgets/post-purchase-offer/postPurchaseSettings.ts
|
|
4262
|
-
var PostPurchaseSettings = z37.object({
|
|
4263
|
-
advance: z37.object({ removePartialOrder: z37.boolean().default(true) }).optional().default(() => ({ removePartialOrder: true })),
|
|
4264
|
-
blocks: z37.union([z37.array(PostPurchaseBlock), z37.unknown().transform(() => [])]).default(() => [
|
|
4265
|
-
PostPurchaseBlock.parse({ settings: {}, styles: {}, type: BlockType.timer }),
|
|
4266
|
-
PostPurchaseBlock.parse({ settings: {}, styles: {}, type: BlockType.callout_banner }),
|
|
4267
|
-
PostPurchaseBlock.parse({ type: BlockType.product_information })
|
|
4268
|
-
]),
|
|
4269
|
-
columnLayout: z37.boolean().optional(),
|
|
4270
|
-
discount: z37.object({
|
|
4271
|
-
amount: strictNumber.default(30),
|
|
4272
|
-
shouldShowSavingText: z37.boolean().optional().default(true),
|
|
4273
|
-
type: z37.enum(["none", "fixed_amount", "percentage"]).default("none")
|
|
4274
|
-
}),
|
|
4275
|
-
discountedFrom: z37.enum(["price", "compare_at_price"]).optional().default("price"),
|
|
4276
|
-
endpoint: z37.string().optional(),
|
|
4277
|
-
integrations: z37.object({
|
|
4278
|
-
judgeme: z37.boolean().default(false),
|
|
4279
|
-
junip: z37.boolean().optional().default(false),
|
|
4280
|
-
klaviyo: z37.boolean().default(false),
|
|
4281
|
-
loox: z37.boolean().default(false),
|
|
4282
|
-
okendo: z37.boolean().default(false),
|
|
4283
|
-
opinew: z37.boolean().optional().default(false),
|
|
4284
|
-
reviewsio: z37.boolean().optional().default(false),
|
|
4285
|
-
stamped: z37.boolean().default(false),
|
|
4286
|
-
yotpo: z37.boolean().default(false)
|
|
4287
|
-
}),
|
|
4288
|
-
key: z37.string().optional(),
|
|
4289
|
-
language: z37.object({
|
|
4290
|
-
description: z37.union([z37.string(), z37.unknown().transform(() => "")]).default(""),
|
|
4291
|
-
descriptionEnabled: z37.boolean().optional().default(false),
|
|
4292
|
-
descriptionRte: z37.object({
|
|
4293
|
-
content: z37.array(z37.unknown()),
|
|
4294
|
-
type: z37.string()
|
|
4295
|
-
}).optional(),
|
|
4296
|
-
descriptionSpacing: z37.enum(["none", "xtight", "tight", "default", "loose", "xloose"]).optional(),
|
|
4297
|
-
displayCtaPrice: z37.boolean().default(true),
|
|
4298
|
-
global: z37.object({
|
|
4299
|
-
declineAll: z37.string().optional().default("Decline All"),
|
|
4300
|
-
declineThisOffer: z37.string().default("Decline this offer"),
|
|
4301
|
-
free: z37.string().optional().default("Free"),
|
|
4302
|
-
notAvailable: z37.string().optional().default("Not available"),
|
|
4303
|
-
payNow: z37.string().default("Pay Now"),
|
|
4304
|
-
payNowUpgradeToSubscription: z37.string().optional().default("Upgrade to Subscription"),
|
|
4305
|
-
quantity: z37.string().default("Quantity"),
|
|
4306
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4307
|
-
recurringShipping: z37.string().optional().default("Recurring shipping"),
|
|
4308
|
-
recurringSubtotal: z37.string().default("Recurring subtotal"),
|
|
4309
|
-
reviewsPlural: z37.string().optional().default("Reviews"),
|
|
4310
|
-
reviewsSingular: z37.string().optional().default("Review"),
|
|
4311
|
-
save: z37.union([z37.string(), z37.unknown().transform(() => "Save")]).optional().default("Save"),
|
|
4312
|
-
shipping: z37.string().default("Shipping"),
|
|
4313
|
-
subscriptionOption: z37.string().optional().default("Subscribe and Save"),
|
|
4314
|
-
subtotal: z37.string().default("Subtotal"),
|
|
4315
|
-
taxes: z37.string().default("Taxes"),
|
|
4316
|
-
total: z37.string().default("Total")
|
|
4317
|
-
}).optional().default(() => ({
|
|
4318
|
-
declineAll: "Decline All",
|
|
4319
|
-
declineThisOffer: "Decline this offer",
|
|
4320
|
-
free: "Free",
|
|
4321
|
-
notAvailable: "Not available",
|
|
4322
|
-
payNow: "Pay Now",
|
|
4323
|
-
payNowUpgradeToSubscription: "Upgrade to Subscription",
|
|
4324
|
-
quantity: "Quantity",
|
|
4325
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4326
|
-
recurringShipping: "Recurring shipping",
|
|
4327
|
-
recurringSubtotal: "Recurring subtotal",
|
|
4328
|
-
reviewsPlural: "Reviews",
|
|
4329
|
-
reviewsSingular: "Review",
|
|
4330
|
-
save: "Save",
|
|
4331
|
-
shipping: "Shipping",
|
|
4332
|
-
subscriptionOption: "Subscribe and Save",
|
|
4333
|
-
subtotal: "Subtotal",
|
|
4334
|
-
taxes: "Taxes",
|
|
4335
|
-
total: "Total"
|
|
4336
|
-
})),
|
|
4337
|
-
globalTranslation: z37.string().optional().default("english")
|
|
4338
|
-
}),
|
|
4339
|
-
metafields: z37.boolean().default(false),
|
|
4340
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4341
|
-
offer: z37.object({
|
|
4342
|
-
count: z37.number().default(1)
|
|
4343
|
-
}),
|
|
4344
|
-
offersLayout: z37.object({
|
|
4345
|
-
itemsPerPage: z37.number().optional(),
|
|
4346
|
-
type: z37.enum(["list", "grid", "carousel"]).default("list")
|
|
4347
|
-
}).optional().default({
|
|
4348
|
-
type: "list"
|
|
4349
|
-
}),
|
|
4350
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4351
|
-
previewMode: z37.boolean().default(false),
|
|
4352
|
-
productLimit: z37.number().optional().default(1),
|
|
4353
|
-
productOptions: z37.object({
|
|
4354
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4355
|
-
hideOutOfStockVariants: z37.boolean().optional().default(false),
|
|
4356
|
-
matchVariant: z37.boolean().optional().default(true),
|
|
4357
|
-
showDeclineAll: z37.enum(["above", "below", "both", "none"]).optional(),
|
|
4358
|
-
showShipping: z37.boolean().optional().default(true),
|
|
4359
|
-
showSubtotal: z37.boolean().optional().default(true),
|
|
4360
|
-
showTaxes: z37.boolean().optional().default(true),
|
|
4361
|
-
showTotal: z37.boolean().optional().default(true),
|
|
4362
|
-
showVariantSelectors: z37.boolean().optional().default(true),
|
|
4363
|
-
variantSelectorDisplayMode: z37.enum(["separate", "single"]).optional().default("separate")
|
|
4364
|
-
}),
|
|
4365
|
-
productType: z37.union([z37.enum(["one-time", "subscription", "both"]), z37.literal("").transform(() => "one-time")]).default("one-time"),
|
|
4366
|
-
productTypeSelector: z37.enum(["dropdown", "radio"]).optional().default("radio"),
|
|
4367
|
-
quantityInputs: z37.object({
|
|
4368
|
-
defaultQuantity: z37.union([z37.number(), z37.null().transform(() => 1)]).default(1),
|
|
4369
|
-
enabled: z37.boolean().default(true),
|
|
4370
|
-
maxValue: z37.union([z37.number(), z37.null().transform(() => 15)]).default(15),
|
|
4371
|
-
minValue: z37.union([z37.number().min(0), z37.unknown().transform(() => 0)]).default(1)
|
|
4372
|
-
}),
|
|
4373
|
-
shipping: z37.object({
|
|
4374
|
-
amount: strictNumber.default(10),
|
|
4375
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4376
|
-
enabled: z37.boolean().default(true),
|
|
4377
|
-
title: z37.string().optional().default("Shipping Fees"),
|
|
4378
|
-
type: z37.enum(["free", "fixed_amount"]).default("free")
|
|
4379
|
-
}),
|
|
4380
|
-
styles: z37.object({
|
|
4381
|
-
starColor: z37.string().optional().default("#fadb14"),
|
|
4382
|
-
starSize: z37.union([z37.enum(["small", "medium", "large"]), z37.unknown().transform(() => "large")]).optional().default("large"),
|
|
4383
|
-
starStrokeColor: z37.string().optional().default("#fadb14"),
|
|
4384
|
-
strokeWidth: z37.string().optional().default("1")
|
|
4385
|
-
}).optional(),
|
|
4386
|
-
subImages: z37.number().nullish().default(8),
|
|
4387
|
-
subscription: z37.object({
|
|
4388
|
-
discount: z37.object({
|
|
4389
|
-
amount: strictNumber.default(30),
|
|
4390
|
-
shouldShowSavingText: z37.boolean().optional().default(true),
|
|
4391
|
-
type: z37.enum(["none", "fixed_amount", "percentage"]).default("none")
|
|
4392
|
-
}),
|
|
4393
|
-
shipping: z37.object({
|
|
4394
|
-
amount: strictNumber.default(1),
|
|
4395
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4396
|
-
enabled: z37.boolean().default(true),
|
|
4397
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4398
|
-
recurringAmount: strictNumber.default(2),
|
|
4399
|
-
shippingTitle: z37.string().default("Shipping Fees"),
|
|
4400
|
-
type: z37.enum(["free", "fixed_amount"]).default("free")
|
|
4401
|
-
})
|
|
4402
|
-
}).optional().default({
|
|
4403
|
-
discount: {
|
|
4404
|
-
amount: 30,
|
|
4405
|
-
shouldShowSavingText: true,
|
|
4406
|
-
type: "none"
|
|
4407
|
-
},
|
|
4408
|
-
shipping: {
|
|
4409
|
-
amount: 1,
|
|
4410
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4411
|
-
enabled: true,
|
|
4412
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4413
|
-
recurringAmount: 2,
|
|
4414
|
-
shippingTitle: "Shipping Fees",
|
|
4415
|
-
type: "free"
|
|
4416
|
-
}
|
|
4417
|
-
}),
|
|
4418
|
-
subscriptionIsDefault: z37.boolean().optional(),
|
|
4419
|
-
/** This isn't configurable in the admin-nextjs editor */
|
|
4420
|
-
template: z37.string().optional().default(""),
|
|
4421
|
-
type: z37.literal("shopify_post_purchase_extension").default("shopify_post_purchase_extension"),
|
|
4422
|
-
upgradeToSubscription: z37.object({
|
|
4423
|
-
acceptButtonText: z37.string().optional().default("Upgrade"),
|
|
4424
|
-
allow: z37.boolean().optional().default(false),
|
|
4425
|
-
emailRefundNotice: z37.boolean().optional().default(false),
|
|
4426
|
-
errorBannerText: z37.string().optional().default("This subscription cannot be added - limit one per order")
|
|
4427
|
-
}).optional().default(() => ({
|
|
4428
|
-
acceptButtonText: "Upgrade",
|
|
4429
|
-
allow: false,
|
|
4430
|
-
emailRefundNotice: false,
|
|
4431
|
-
errorBannerText: "This subscription cannot be added - limit one per order"
|
|
4432
|
-
}))
|
|
4576
|
+
|
|
4577
|
+
// src/transforms/postPurchase/imageContentSettings.ts
|
|
4578
|
+
import { z as z43 } from "zod";
|
|
4579
|
+
var ImageContentSettings = ImageContentBlock.shape.settings.pick({
|
|
4580
|
+
descriptionSize: true,
|
|
4581
|
+
horizontalAlignment: true,
|
|
4582
|
+
superTitleSize: true,
|
|
4583
|
+
swapContent: true,
|
|
4584
|
+
titleSize: true
|
|
4585
|
+
}).extend({
|
|
4586
|
+
alt: z43.unknown().optional(),
|
|
4587
|
+
description: z43.unknown().optional(),
|
|
4588
|
+
image: z43.unknown().optional(),
|
|
4589
|
+
imagePlacement: ImageContentBlock.shape.settings.shape.imagePlacement.unwrap().nullable(),
|
|
4590
|
+
imageSize: ImageContentBlock.shape.settings.shape.imageSize.pipe(z43.number().positive()),
|
|
4591
|
+
superTitle: z43.unknown().optional(),
|
|
4592
|
+
title: z43.unknown().optional()
|
|
4433
4593
|
});
|
|
4434
4594
|
|
|
4595
|
+
// src/transforms/postPurchase/convertImageContentBlock.ts
|
|
4596
|
+
var Shell2 = z44.object({ settings: ImageContentSettings, type: z44.literal("image_content") });
|
|
4597
|
+
var PROPORTIONAL_BREAK_AT = 500;
|
|
4598
|
+
var convertImageContentBlock = (raw, report) => {
|
|
4599
|
+
const shell = Shell2.safeParse(raw);
|
|
4600
|
+
if (!shell.success) {
|
|
4601
|
+
report?.({ detail: "Unreadable post-purchase image-content block", kind: "section" });
|
|
4602
|
+
return [];
|
|
4603
|
+
}
|
|
4604
|
+
const settings = shell.data.settings;
|
|
4605
|
+
const alt = typeof settings.alt === "string" ? settings.alt : void 0;
|
|
4606
|
+
if (settings.alt !== void 0 && alt === void 0) {
|
|
4607
|
+
report?.({ detail: "Unreadable post-purchase image-content alt text", kind: "section" });
|
|
4608
|
+
}
|
|
4609
|
+
const image = convertImageFrame({ alt, image: settings.image, imageSize: settings.imageSize });
|
|
4610
|
+
if (!image) report?.({ detail: "Unreadable post-purchase image-content image", kind: "section" });
|
|
4611
|
+
const text2 = convertImageContentText(settings, report);
|
|
4612
|
+
const textFrame = CABLayoutSection.parse({ direction: "grid", grid: { columns: ["100%"] }, sections: [text2] });
|
|
4613
|
+
const placement = settings.imagePlacement ?? (settings.swapContent ? "right" : "left");
|
|
4614
|
+
const imageFirst = placement === "left" || placement === "top";
|
|
4615
|
+
const sections = imageFirst ? [image, textFrame] : [textFrame, image];
|
|
4616
|
+
const ordered = sections.filter((section) => section !== null);
|
|
4617
|
+
const inner = placement === "left" || placement === "right" ? CABLayoutSection.parse({
|
|
4618
|
+
sections: ordered,
|
|
4619
|
+
spacing: "loose",
|
|
4620
|
+
wrapping: {
|
|
4621
|
+
breakAt: settings.imageSize <= 1 ? PROPORTIONAL_BREAK_AT : settings.imageSize,
|
|
4622
|
+
maxPerLine: 2
|
|
4623
|
+
}
|
|
4624
|
+
}) : CABLayoutSection.parse({
|
|
4625
|
+
direction: "grid",
|
|
4626
|
+
grid: { columns: ["100%"] },
|
|
4627
|
+
sections: [CABLayoutSection.parse({ sections: ordered })],
|
|
4628
|
+
width: 95
|
|
4629
|
+
});
|
|
4630
|
+
return [CABLayoutSection.parse({ frame: "offer-content", sections: [inner], spacing: "none" })];
|
|
4631
|
+
};
|
|
4632
|
+
|
|
4633
|
+
// src/transforms/postPurchase/productCardPay.ts
|
|
4634
|
+
import { mapValues } from "es-toolkit";
|
|
4635
|
+
var PAY_NOW = { en: "Pay Now" };
|
|
4636
|
+
var DECLINE_THIS_OFFER = { en: "Decline this offer" };
|
|
4637
|
+
var NOT_AVAILABLE_DEFAULTS = ["Not available"];
|
|
4638
|
+
var soldOutSlot = ({ language: language3 }) => labelSlot("soldOut", OFFER_LABELS.soldOut, language3.global.notAvailable, [], NOT_AVAILABLE_DEFAULTS);
|
|
4639
|
+
var productCardPay = (settings) => {
|
|
4640
|
+
const payNow = labelSlot("payNow", PAY_NOW, settings.language.global.payNow);
|
|
4641
|
+
const priced = settings.language.displayCtaPrice ? { ...payNow, content: mapValues(payNow.content, (label2) => `${label2} \xB7 {{total}}`) } : payNow;
|
|
4642
|
+
return [addToOrderButton("primary", [priced, soldOutSlot(settings)])];
|
|
4643
|
+
};
|
|
4644
|
+
var productCardDecline = ({ language: language3 }) => [
|
|
4645
|
+
{
|
|
4646
|
+
action: "declineOffer",
|
|
4647
|
+
buttonStyle: "secondary",
|
|
4648
|
+
sections: [labelSlot("declineOffer", DECLINE_THIS_OFFER, language3.global.declineThisOffer)],
|
|
4649
|
+
sectionType: "button"
|
|
4650
|
+
}
|
|
4651
|
+
];
|
|
4652
|
+
|
|
4435
4653
|
// src/transforms/postPurchase/productCardSettings.ts
|
|
4654
|
+
import { mapValues as mapValues2 } from "es-toolkit";
|
|
4655
|
+
import { z as z45 } from "zod";
|
|
4436
4656
|
var { shape } = PostPurchaseSettings;
|
|
4437
|
-
var Labels =
|
|
4657
|
+
var Labels = z45.object(
|
|
4438
4658
|
mapValues2(shape.language.shape.global.unwrap().unwrap().shape, (label2) => label2.catch(() => label2.parse(void 0)))
|
|
4439
4659
|
);
|
|
4440
|
-
var Language =
|
|
4660
|
+
var Language = z45.object({
|
|
4441
4661
|
displayCtaPrice: shape.language.shape.displayCtaPrice.catch(true),
|
|
4442
4662
|
global: Labels.catch(() => Labels.parse({}))
|
|
4443
4663
|
});
|
|
4444
|
-
var Fields =
|
|
4664
|
+
var Fields = z45.object({
|
|
4445
4665
|
discount: shape.discount.catch(() => shape.discount.parse({})),
|
|
4446
4666
|
language: Language.catch(() => Language.parse({})),
|
|
4447
4667
|
productOptions: shape.productOptions.catch(() => shape.productOptions.parse({})),
|
|
@@ -4725,7 +4945,7 @@ var convertSeparatorBlock = (raw, report) => {
|
|
|
4725
4945
|
};
|
|
4726
4946
|
|
|
4727
4947
|
// src/transforms/postPurchase/convertTextBlock.ts
|
|
4728
|
-
import { z as
|
|
4948
|
+
import { z as z46 } from "zod";
|
|
4729
4949
|
var fields3 = ["superTitle", "title", "description"];
|
|
4730
4950
|
var styles = {
|
|
4731
4951
|
description: [{ attrs: { color: "base", fontSize: "small" }, type: "textStyle" }],
|
|
@@ -4733,13 +4953,13 @@ var styles = {
|
|
|
4733
4953
|
title: [{ type: "bold" }, { attrs: { color: "base", fontSize: "large" }, type: "textStyle" }]
|
|
4734
4954
|
};
|
|
4735
4955
|
var settingsSchema = TextBlock.shape.settings;
|
|
4736
|
-
var Shell3 =
|
|
4956
|
+
var Shell3 = z46.object({
|
|
4737
4957
|
settings: settingsSchema.pick({ horizontalAlignment: true }).extend({
|
|
4738
|
-
description:
|
|
4739
|
-
superTitle:
|
|
4740
|
-
title:
|
|
4958
|
+
description: z46.unknown().optional(),
|
|
4959
|
+
superTitle: z46.unknown().optional(),
|
|
4960
|
+
title: z46.unknown().optional()
|
|
4741
4961
|
}),
|
|
4742
|
-
type:
|
|
4962
|
+
type: z46.literal("text")
|
|
4743
4963
|
});
|
|
4744
4964
|
var convertTextBlock = (raw, report) => {
|
|
4745
4965
|
const shell = Shell3.safeParse(raw);
|
|
@@ -4811,11 +5031,11 @@ var convertTimerBlock = (raw, report) => {
|
|
|
4811
5031
|
};
|
|
4812
5032
|
|
|
4813
5033
|
// src/transforms/postPurchase/convertMessagePreview.ts
|
|
4814
|
-
var MessageWidget =
|
|
4815
|
-
blocks:
|
|
4816
|
-
type:
|
|
5034
|
+
var MessageWidget = z47.object({
|
|
5035
|
+
blocks: z47.array(z47.unknown()).min(1),
|
|
5036
|
+
type: z47.literal(WidgetType.shopify_post_purchase_extension)
|
|
4817
5037
|
});
|
|
4818
|
-
var BlockType2 =
|
|
5038
|
+
var BlockType2 = z47.object({ type: z47.string() });
|
|
4819
5039
|
var converters = {
|
|
4820
5040
|
callout_banner: convertCalloutBlock,
|
|
4821
5041
|
image: convertImageBlock,
|
|
@@ -5327,6 +5547,24 @@ var reportUnreadableVerdict = (metadata, report) => {
|
|
|
5327
5547
|
if (RuleVerdict.safeParse(metadata).success || isDefinitiveRuleAnswer(metadata)) return;
|
|
5328
5548
|
report?.({ detail: "rule verdict lacks readable custom.shouldShow", kind: "ruleVerdict" });
|
|
5329
5549
|
};
|
|
5550
|
+
var NAMED_ENVELOPE_KEYS = /* @__PURE__ */ new Set(["data", "error", "errors", "message", "metadata", "products", "status"]);
|
|
5551
|
+
var describeShape = (value) => {
|
|
5552
|
+
if (Array.isArray(value)) return `array (${value.length} items)`;
|
|
5553
|
+
if (typeof value !== "object") return typeof value;
|
|
5554
|
+
const keys = Object.keys(value);
|
|
5555
|
+
if (keys.length === 0) return "object (no keys)";
|
|
5556
|
+
if (keys.every((key) => /^\d+$/.test(key))) return `object (${keys.length} numeric keys)`;
|
|
5557
|
+
const named = keys.filter((key) => NAMED_ENVELOPE_KEYS.has(key)).sort();
|
|
5558
|
+
const others = keys.length - named.length;
|
|
5559
|
+
return `object (${[...named, ...others ? [`${others} other keys`] : []].join(", ")})`;
|
|
5560
|
+
};
|
|
5561
|
+
var describePath = (dataSourcePath) => /^(?:\/[\w-]{1,64}){1,6}$/.test(dataSourcePath) ? dataSourcePath : "an unrecognized path";
|
|
5562
|
+
var withReviews = (product, integrations, report) => {
|
|
5563
|
+
const { reviews: engineReviews, ...rest } = product;
|
|
5564
|
+
const reviews = projectOfferProductReviews(integrations, product, report);
|
|
5565
|
+
if (reviews !== void 0) return { ...rest, reviews };
|
|
5566
|
+
return engineReviews === void 0 ? product : rest;
|
|
5567
|
+
};
|
|
5330
5568
|
var dedupeById = (products) => uniqBy2(products, (product) => product.id);
|
|
5331
5569
|
var MAX_REQUEST_LINE_BYTES = 32e3;
|
|
5332
5570
|
var requestLineBytes = (path, search) => new TextEncoder().encode(`GET ${path}?${search} HTTP/1.1`).length;
|
|
@@ -5351,9 +5589,18 @@ var fetchDataSourceResults = async (input, ctx) => {
|
|
|
5351
5589
|
const rawProducts = Array.isArray(raw.data) ? deepCamelCase(raw.data) : [];
|
|
5352
5590
|
const metadata = raw.metadata == null ? null : deepCamelCase(raw.metadata);
|
|
5353
5591
|
if (input.limit === 0) reportUnreadableVerdict(metadata, ctx.reportDrift);
|
|
5354
|
-
if (input.limit !== 0
|
|
5355
|
-
|
|
5356
|
-
|
|
5592
|
+
if (input.limit !== 0) {
|
|
5593
|
+
const shape2 = upstream != null && (typeof upstream !== "object" || Array.isArray(upstream)) ? `body is ${describeShape(upstream)}` : raw.data != null && !Array.isArray(raw.data) ? `data is ${describeShape(raw.data)}` : void 0;
|
|
5594
|
+
if (shape2) {
|
|
5595
|
+
ctx.reportDrift?.({
|
|
5596
|
+
detail: `data-source products payload is not an array: ${shape2} from ${describePath(input.dataSourcePath)}`,
|
|
5597
|
+
kind: "dataSource"
|
|
5598
|
+
});
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
const products = dedupeById(rawProducts.filter((product) => keepValidProduct(product, ctx.reportDrift))).map(
|
|
5602
|
+
(product) => withReviews(product, input.integrations, ctx.reportDrift)
|
|
5603
|
+
);
|
|
5357
5604
|
return {
|
|
5358
5605
|
metadata,
|
|
5359
5606
|
/** B2B sessions filter by the company-location catalog server-side (the client bypasses its market hide). */
|
|
@@ -5363,7 +5610,7 @@ var fetchDataSourceResults = async (input, ctx) => {
|
|
|
5363
5610
|
};
|
|
5364
5611
|
|
|
5365
5612
|
// src/server/adsEngine.ts
|
|
5366
|
-
import { z as
|
|
5613
|
+
import { z as z48 } from "zod";
|
|
5367
5614
|
var SUPPLY_KEY = { production: "AQF5RKLR", staging: "Z56LQ7PS" };
|
|
5368
5615
|
var OFFER_LIMIT = 4;
|
|
5369
5616
|
var SUPPORTED_COUNTRIES = /* @__PURE__ */ new Set(["US", "CA", "GB"]);
|
|
@@ -5376,30 +5623,30 @@ var transactionItems = (input) => (input.transactionItems?.map(({ id, priceCents
|
|
|
5376
5623
|
title,
|
|
5377
5624
|
type
|
|
5378
5625
|
})) ?? input.cart.items.map(({ productId: productId2, quantity: quantity3 }) => ({ id: productId2, quantity: quantity3 }))).slice(0, MAX_TRANSACTION_ITEMS);
|
|
5379
|
-
var AdsEngineOffer =
|
|
5626
|
+
var AdsEngineOffer = z48.object({
|
|
5380
5627
|
/** Pill-style value prop ("Get $45 Cashback"). Carried through the parse; the card does not render it yet (REB-24429 — pill design pending). */
|
|
5381
|
-
callout:
|
|
5382
|
-
click_url:
|
|
5383
|
-
cta:
|
|
5628
|
+
callout: z48.string().catch(""),
|
|
5629
|
+
click_url: z48.string(),
|
|
5630
|
+
cta: z48.string(),
|
|
5384
5631
|
/** `omitempty` on the ad server — a bid without it must not fail the parse and drop the whole batch (composeOffer falls back to 'No Thanks'). */
|
|
5385
|
-
decline_cta:
|
|
5386
|
-
decline_url:
|
|
5387
|
-
description:
|
|
5632
|
+
decline_cta: z48.string().catch(""),
|
|
5633
|
+
decline_url: z48.string(),
|
|
5634
|
+
description: z48.string(),
|
|
5388
5635
|
/** Short legal disclosure (FDIC / auto-renewal / FTC-affiliation lines). Rendered subdued under the CTAs. */
|
|
5389
|
-
disclaimer:
|
|
5636
|
+
disclaimer: z48.string().catch(""),
|
|
5390
5637
|
/** Long legal text behind the disclosure toggle in the composed card (REB-24429). */
|
|
5391
|
-
extended_disclaimer:
|
|
5392
|
-
headline:
|
|
5393
|
-
id:
|
|
5638
|
+
extended_disclaimer: z48.string().catch(""),
|
|
5639
|
+
headline: z48.string(),
|
|
5640
|
+
id: z48.string(),
|
|
5394
5641
|
/** Advertiser-supplied image alt; the composed card falls back to the stripped headline when empty. */
|
|
5395
|
-
image_alt:
|
|
5396
|
-
image_url:
|
|
5642
|
+
image_alt: z48.string().catch(""),
|
|
5643
|
+
image_url: z48.string().optional(),
|
|
5397
5644
|
/** The serving network's own privacy page (Falcon, REB-24744); the card prefers it over the widget setting. A bad value drops only this field, never the offer. */
|
|
5398
|
-
privacy_url:
|
|
5399
|
-
view_url:
|
|
5645
|
+
privacy_url: z48.string().optional().catch(void 0),
|
|
5646
|
+
view_url: z48.string()
|
|
5400
5647
|
});
|
|
5401
|
-
var AdsEngineResponse =
|
|
5402
|
-
offers:
|
|
5648
|
+
var AdsEngineResponse = z48.object({
|
|
5649
|
+
offers: z48.array(z48.unknown()).default([]).transform(
|
|
5403
5650
|
(offers) => offers.flatMap((offer) => {
|
|
5404
5651
|
const result = AdsEngineOffer.safeParse(offer);
|
|
5405
5652
|
return result.success ? [result.data] : [];
|
|
@@ -5411,8 +5658,8 @@ var AdsEngineResponse = z41.object({
|
|
|
5411
5658
|
* offers-testing fixture on a test store forces it, REB-24467). Both optional: an older engine or a
|
|
5412
5659
|
* non-JSON body must not fail the parse and drop the offers.
|
|
5413
5660
|
*/
|
|
5414
|
-
request_id:
|
|
5415
|
-
testing:
|
|
5661
|
+
request_id: z48.string().optional().catch(void 0),
|
|
5662
|
+
testing: z48.boolean().optional().catch(void 0)
|
|
5416
5663
|
});
|
|
5417
5664
|
var adsHost = (host) => `offers.${host}`;
|
|
5418
5665
|
var isTestingRequest = (input, isProduction) => !isProduction || !!input.testing || input.customerEmail?.toLowerCase() === TEST_CUSTOMER_EMAIL;
|
|
@@ -5728,7 +5975,6 @@ var fetchMonetizeOffers = async (input, ctx, prefetched) => {
|
|
|
5728
5975
|
|
|
5729
5976
|
// src/server/salvageSections.ts
|
|
5730
5977
|
import { v7 as uuidv714 } from "uuid";
|
|
5731
|
-
var STUB_SECTION_TYPE = "unknown";
|
|
5732
5978
|
var knownSectionTypes = new Set(sectionTypes);
|
|
5733
5979
|
var hasSectionsArray = (raw) => typeof raw === "object" && raw !== null && Array.isArray(raw.sections);
|
|
5734
5980
|
var stubSection = (raw, report) => {
|
|
@@ -5741,7 +5987,7 @@ var stubSection = (raw, report) => {
|
|
|
5741
5987
|
});
|
|
5742
5988
|
return {
|
|
5743
5989
|
sectionId: typeof rawId === "string" && rawId ? rawId : uuidv714(),
|
|
5744
|
-
sectionType: typeof original === "string" && !knownSectionTypes.has(original) ? original :
|
|
5990
|
+
sectionType: typeof original === "string" && !knownSectionTypes.has(original) ? original : SALVAGED_SECTION_TYPE
|
|
5745
5991
|
};
|
|
5746
5992
|
};
|
|
5747
5993
|
var salvageSection = (raw, report) => {
|
|
@@ -5884,13 +6130,13 @@ var convertWidgetSettings = async (input, ctx) => {
|
|
|
5884
6130
|
};
|
|
5885
6131
|
|
|
5886
6132
|
// src/server/giftWidgets.ts
|
|
5887
|
-
import { z as
|
|
5888
|
-
var SettingsNumber =
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
]).pipe(
|
|
5892
|
-
var SgwpMilestoneSchema =
|
|
5893
|
-
selectable_product_quantity: SettingsNumber.pipe(
|
|
6133
|
+
import { z as z49 } from "zod";
|
|
6134
|
+
var SettingsNumber = z49.union([
|
|
6135
|
+
z49.number(),
|
|
6136
|
+
z49.string().trim().regex(/^-?\d+(\.\d+)?$/)
|
|
6137
|
+
]).pipe(z49.coerce.number());
|
|
6138
|
+
var SgwpMilestoneSchema = z49.object({
|
|
6139
|
+
selectable_product_quantity: SettingsNumber.pipe(z49.number().int().nonnegative()),
|
|
5894
6140
|
unlock_price: SettingsNumber
|
|
5895
6141
|
});
|
|
5896
6142
|
var fetchGiftWidgetSettings = async (widgetId, shop, ctx) => unwrapData(await ctx.fetchUpstream("/api/v1/widgets/settings", { id: String(widgetId), shop }, { host: ctx.host }));
|
|
@@ -5901,7 +6147,7 @@ var readDataSourcePath = (settings) => {
|
|
|
5901
6147
|
var readMilestones = (settings, report) => {
|
|
5902
6148
|
const raw = settings && typeof settings === "object" ? settings.milestones : void 0;
|
|
5903
6149
|
if (raw == null) return [];
|
|
5904
|
-
const parsed =
|
|
6150
|
+
const parsed = z49.array(SgwpMilestoneSchema).safeParse(raw);
|
|
5905
6151
|
if (parsed.success) return parsed.data;
|
|
5906
6152
|
report?.({
|
|
5907
6153
|
detail: [
|
|
@@ -6016,16 +6262,16 @@ var validateGifts = async (input, ctx) => {
|
|
|
6016
6262
|
|
|
6017
6263
|
// src/server/normalizePostPurchaseProduct.ts
|
|
6018
6264
|
import { merge } from "es-toolkit/compat";
|
|
6019
|
-
import { z as
|
|
6265
|
+
import { z as z53 } from "zod";
|
|
6020
6266
|
|
|
6021
6267
|
// src/schema/postPurchaseProductSettings.ts
|
|
6022
|
-
import { z as
|
|
6268
|
+
import { z as z51 } from "zod";
|
|
6023
6269
|
|
|
6024
6270
|
// src/schema/postPurchaseProductSource.ts
|
|
6025
|
-
import { z as
|
|
6026
|
-
var PostPurchaseAmount =
|
|
6027
|
-
var PostPurchaseProductId =
|
|
6028
|
-
var PostPurchaseVariantId =
|
|
6271
|
+
import { z as z50 } from "zod";
|
|
6272
|
+
var PostPurchaseAmount = z50.string().regex(/^\d+(?:\.\d+)?$/).refine((value) => Number.isFinite(Number(value)));
|
|
6273
|
+
var PostPurchaseProductId = z50.number().int().positive();
|
|
6274
|
+
var PostPurchaseVariantId = z50.number().int().positive();
|
|
6029
6275
|
var ProductBase = OfferProduct.pick({ description: true, id: true, image: true, title: true }).extend({
|
|
6030
6276
|
description: OfferProduct.shape.description.nullable(),
|
|
6031
6277
|
id: PostPurchaseProductId
|
|
@@ -6033,51 +6279,51 @@ var ProductBase = OfferProduct.pick({ description: true, id: true, image: true,
|
|
|
6033
6279
|
var SourceVariant = OfferProduct.shape.variants.element.pick({ id: true, title: true }).extend({
|
|
6034
6280
|
compareAtPrice: PostPurchaseAmount.nullable(),
|
|
6035
6281
|
id: PostPurchaseVariantId,
|
|
6036
|
-
imageId:
|
|
6037
|
-
inventoryManagement:
|
|
6038
|
-
inventoryPolicy:
|
|
6039
|
-
inventoryQuantity:
|
|
6040
|
-
option1:
|
|
6041
|
-
option2:
|
|
6042
|
-
option3:
|
|
6282
|
+
imageId: z50.number().int().nonnegative().nullish(),
|
|
6283
|
+
inventoryManagement: z50.string().nullable(),
|
|
6284
|
+
inventoryPolicy: z50.string(),
|
|
6285
|
+
inventoryQuantity: z50.number(),
|
|
6286
|
+
option1: z50.unknown().optional(),
|
|
6287
|
+
option2: z50.unknown().optional(),
|
|
6288
|
+
option3: z50.unknown().optional(),
|
|
6043
6289
|
price: PostPurchaseAmount,
|
|
6044
6290
|
productId: PostPurchaseProductId,
|
|
6045
|
-
sellingPlanAllocations:
|
|
6291
|
+
sellingPlanAllocations: z50.unknown().optional()
|
|
6046
6292
|
});
|
|
6047
6293
|
var Product2 = ProductBase.extend({
|
|
6048
|
-
images:
|
|
6049
|
-
|
|
6294
|
+
images: z50.array(
|
|
6295
|
+
z50.object({ alt: z50.string().optional().catch(void 0), id: z50.number().int().positive(), src: z50.string() })
|
|
6050
6296
|
).nullish(),
|
|
6051
|
-
metadata:
|
|
6052
|
-
productQuantity:
|
|
6053
|
-
|
|
6054
|
-
|
|
6297
|
+
metadata: z50.object({
|
|
6298
|
+
productQuantity: z50.union([
|
|
6299
|
+
z50.number().int().positive(),
|
|
6300
|
+
z50.string().regex(/^[1-9]\d*$/).transform(Number)
|
|
6055
6301
|
]).optional()
|
|
6056
6302
|
}).optional(),
|
|
6057
|
-
metafields:
|
|
6058
|
-
options:
|
|
6059
|
-
productGroupVariants:
|
|
6303
|
+
metafields: z50.unknown().optional(),
|
|
6304
|
+
options: z50.unknown().optional(),
|
|
6305
|
+
productGroupVariants: z50.array(z50.unknown()).default([]),
|
|
6060
6306
|
// REB-25048: the v2 data source flags a subscription-only product here and sends no selling plan groups
|
|
6061
|
-
requiresSellingPlan:
|
|
6062
|
-
selectedVariant:
|
|
6063
|
-
sellingPlanGroups:
|
|
6307
|
+
requiresSellingPlan: z50.boolean().nullish(),
|
|
6308
|
+
selectedVariant: z50.unknown().optional(),
|
|
6309
|
+
sellingPlanGroups: z50.array(
|
|
6064
6310
|
OfferProduct.shape.sellingPlanGroups.unwrap().element.extend({
|
|
6065
|
-
discountAmount:
|
|
6066
|
-
discountType:
|
|
6067
|
-
id:
|
|
6068
|
-
requiresSellingPlan:
|
|
6069
|
-
sellingPlans:
|
|
6311
|
+
discountAmount: z50.unknown().optional(),
|
|
6312
|
+
discountType: z50.unknown().optional(),
|
|
6313
|
+
id: z50.unknown().optional(),
|
|
6314
|
+
requiresSellingPlan: z50.boolean().default(false),
|
|
6315
|
+
sellingPlans: z50.unknown().optional()
|
|
6070
6316
|
})
|
|
6071
6317
|
).default([]),
|
|
6072
|
-
variants:
|
|
6318
|
+
variants: z50.array(SourceVariant)
|
|
6073
6319
|
});
|
|
6074
|
-
var PostPurchaseProductSource =
|
|
6075
|
-
data:
|
|
6076
|
-
metadata:
|
|
6320
|
+
var PostPurchaseProductSource = z50.object({
|
|
6321
|
+
data: z50.array(Product2),
|
|
6322
|
+
metadata: z50.object({ widget: z50.record(z50.string(), z50.unknown()).default({}) }).default({ widget: {} })
|
|
6077
6323
|
});
|
|
6078
6324
|
|
|
6079
6325
|
// src/schema/postPurchaseProductSettings.ts
|
|
6080
|
-
var amount =
|
|
6326
|
+
var amount = z51.union([z51.number().nonnegative(), PostPurchaseAmount.transform(Number)]);
|
|
6081
6327
|
var PostPurchaseProductSettings = PostPurchaseSettings.pick({
|
|
6082
6328
|
discountedFrom: true,
|
|
6083
6329
|
language: true,
|
|
@@ -6089,18 +6335,18 @@ var PostPurchaseProductSettings = PostPurchaseSettings.pick({
|
|
|
6089
6335
|
subscriptionIsDefault: true,
|
|
6090
6336
|
upgradeToSubscription: true
|
|
6091
6337
|
}).extend({
|
|
6092
|
-
advance:
|
|
6093
|
-
blocks:
|
|
6338
|
+
advance: z51.unknown().default({}),
|
|
6339
|
+
blocks: z51.array(z51.unknown()).optional(),
|
|
6094
6340
|
discount: PostPurchaseSettings.shape.discount.extend({ amount: amount.default(30) }),
|
|
6095
|
-
integrations:
|
|
6341
|
+
integrations: z51.unknown().default({}),
|
|
6096
6342
|
shipping: PostPurchaseSettings.shape.shipping.extend({ amount: amount.default(10) }),
|
|
6097
|
-
styles:
|
|
6343
|
+
styles: z51.unknown().default({}),
|
|
6098
6344
|
subImages: PostPurchaseSettings.shape.subImages.catch(null),
|
|
6099
|
-
subscription:
|
|
6345
|
+
subscription: z51.unknown().default(PostPurchaseSettings.shape.subscription.parse(void 0))
|
|
6100
6346
|
});
|
|
6101
6347
|
|
|
6102
6348
|
// src/server/readPostPurchaseProductSource.ts
|
|
6103
|
-
import { z as
|
|
6349
|
+
import { z as z52 } from "zod";
|
|
6104
6350
|
|
|
6105
6351
|
// src/server/postPurchaseResponseDrift.ts
|
|
6106
6352
|
var heard = /* @__PURE__ */ new WeakMap();
|
|
@@ -6126,9 +6372,9 @@ var requiresOwnSellingPlan = (product) => product.requiresSellingPlan === true;
|
|
|
6126
6372
|
var requiresUnsuppliedSellingPlan = (product) => requiresOwnSellingPlan(product) && !product.sellingPlanGroups?.length;
|
|
6127
6373
|
|
|
6128
6374
|
// src/server/readPostPurchaseProductSource.ts
|
|
6129
|
-
var Envelope = PostPurchaseProductSource.extend({ data:
|
|
6375
|
+
var Envelope = PostPurchaseProductSource.extend({ data: z52.array(z52.unknown()) });
|
|
6130
6376
|
var Product3 = PostPurchaseProductSource.shape.data.element;
|
|
6131
|
-
var Identified =
|
|
6377
|
+
var Identified = z52.object({ id: PostPurchaseProductId });
|
|
6132
6378
|
var readPostPurchaseProductSource = (response, report) => {
|
|
6133
6379
|
const { data, metadata } = Envelope.parse(deepCamelCase(response));
|
|
6134
6380
|
const drift = [];
|
|
@@ -6163,9 +6409,9 @@ var normalizePostPurchaseSource = (settings, response, report) => {
|
|
|
6163
6409
|
const original = PostPurchaseProductSettings.parse(deepCamelCase(settings));
|
|
6164
6410
|
const { dropped, metadata, products } = readPostPurchaseProductSource(response, report);
|
|
6165
6411
|
const overrides = Object.fromEntries(Object.entries(metadata.widget).filter(([key]) => key in original));
|
|
6166
|
-
const discount =
|
|
6412
|
+
const discount = z53.object({ type: z53.string() }).safeParse(overrides.discount);
|
|
6167
6413
|
if (discount.success && discount.data.type === "fixed") {
|
|
6168
|
-
overrides.discount = { ...
|
|
6414
|
+
overrides.discount = { ...z53.record(z53.string(), z53.unknown()).parse(overrides.discount), type: "fixed_amount" };
|
|
6169
6415
|
}
|
|
6170
6416
|
const effective = PostPurchaseProductSettings.parse(merge({}, original, overrides));
|
|
6171
6417
|
const data = products.map(({ product }) => product);
|
|
@@ -6202,52 +6448,52 @@ var postPurchasePlanDiscountPercentage = (price, adjustment) => {
|
|
|
6202
6448
|
};
|
|
6203
6449
|
|
|
6204
6450
|
// src/schema/postPurchasePlanCatalog.ts
|
|
6205
|
-
import { z as
|
|
6451
|
+
import { z as z56 } from "zod";
|
|
6206
6452
|
|
|
6207
6453
|
// src/schema/postPurchasePlanAdjustment.ts
|
|
6208
|
-
import { z as
|
|
6454
|
+
import { z as z55 } from "zod";
|
|
6209
6455
|
|
|
6210
6456
|
// src/schema/postPurchaseDiscount.ts
|
|
6211
|
-
import { z as
|
|
6212
|
-
var Discount =
|
|
6213
|
-
amount:
|
|
6214
|
-
title:
|
|
6215
|
-
type:
|
|
6457
|
+
import { z as z54 } from "zod";
|
|
6458
|
+
var Discount = z54.object({
|
|
6459
|
+
amount: z54.number().nonnegative(),
|
|
6460
|
+
title: z54.string(),
|
|
6461
|
+
type: z54.enum(["none", "fixed_amount", "percentage"])
|
|
6216
6462
|
});
|
|
6217
6463
|
var validPostPurchaseDiscount = (discount) => discount.type !== "percentage" || discount.amount <= 100;
|
|
6218
6464
|
var PostPurchaseDiscount = Discount.refine(validPostPurchaseDiscount);
|
|
6219
6465
|
|
|
6220
6466
|
// src/schema/postPurchasePlanAdjustment.ts
|
|
6221
|
-
var PostPurchasePlanAdjustment =
|
|
6467
|
+
var PostPurchasePlanAdjustment = z55.object({
|
|
6222
6468
|
amount: PostPurchaseDiscount.shape.amount,
|
|
6223
|
-
type:
|
|
6469
|
+
type: z55.enum(["percentage", "fixed_amount", "price"])
|
|
6224
6470
|
}).refine(({ amount: amount2, type }) => type === "price" || validPostPurchaseDiscount({ amount: amount2, type }));
|
|
6225
6471
|
|
|
6226
6472
|
// src/schema/postPurchasePlanCatalog.ts
|
|
6227
|
-
var PostPurchasePlanOption =
|
|
6473
|
+
var PostPurchasePlanOption = z56.object({
|
|
6228
6474
|
adjustment: PostPurchasePlanAdjustment.optional(),
|
|
6229
6475
|
/** cab-checkout's `discountPercentage`: the plan's own whole-percent saving on the regular price (`{{subscriptionDiscount}}`). */
|
|
6230
|
-
discountPercentage:
|
|
6231
|
-
id:
|
|
6232
|
-
intervalFrequency:
|
|
6233
|
-
intervalLabel:
|
|
6234
|
-
name:
|
|
6476
|
+
discountPercentage: z56.number().int().min(0).max(100).optional(),
|
|
6477
|
+
id: z56.number().int().positive(),
|
|
6478
|
+
intervalFrequency: z56.string().regex(/^[1-9]\d*$/).optional(),
|
|
6479
|
+
intervalLabel: z56.string().min(1),
|
|
6480
|
+
name: z56.string().min(1)
|
|
6235
6481
|
});
|
|
6236
|
-
var PostPurchasePlanCatalog =
|
|
6237
|
-
plans:
|
|
6238
|
-
productId:
|
|
6239
|
-
requiresSellingPlan:
|
|
6240
|
-
variantId:
|
|
6482
|
+
var PostPurchasePlanCatalog = z56.object({
|
|
6483
|
+
plans: z56.array(PostPurchasePlanOption),
|
|
6484
|
+
productId: z56.number().int().positive(),
|
|
6485
|
+
requiresSellingPlan: z56.boolean(),
|
|
6486
|
+
variantId: z56.number().int().positive()
|
|
6241
6487
|
});
|
|
6242
6488
|
|
|
6243
6489
|
// src/server/postPurchaseLegacyPlans.ts
|
|
6244
|
-
import { z as
|
|
6490
|
+
import { z as z58 } from "zod";
|
|
6245
6491
|
|
|
6246
6492
|
// src/server/postPurchasePlanAdjustment.ts
|
|
6247
|
-
import { z as
|
|
6248
|
-
var SourceAdjustment =
|
|
6493
|
+
import { z as z57 } from "zod";
|
|
6494
|
+
var SourceAdjustment = z57.object({
|
|
6249
6495
|
amount: PostPurchaseProductSettings.shape.discount.shape.amount.unwrap(),
|
|
6250
|
-
type:
|
|
6496
|
+
type: z57.enum(["percentage", "fixed_amount", "amount", "price", "none", "fixed"])
|
|
6251
6497
|
});
|
|
6252
6498
|
var readPostPurchasePlanAdjustment = (raw) => {
|
|
6253
6499
|
const source2 = SourceAdjustment.parse(raw);
|
|
@@ -6266,13 +6512,13 @@ var readPostPurchasePlanAdjustment = (raw) => {
|
|
|
6266
6512
|
var Plan = PostPurchasePlanOption.pick({ id: true, name: true }).extend({
|
|
6267
6513
|
orderIntervalFrequency: PostPurchasePlanOption.shape.intervalFrequency.unwrap(),
|
|
6268
6514
|
orderIntervalUnitType: PostPurchasePlanOption.shape.intervalLabel,
|
|
6269
|
-
shopifyVariantId:
|
|
6515
|
+
shopifyVariantId: z58.string().regex(/^[1-9]\d*$/).nullish()
|
|
6270
6516
|
});
|
|
6271
|
-
var Group =
|
|
6272
|
-
discountAmount:
|
|
6273
|
-
discountType:
|
|
6274
|
-
id:
|
|
6275
|
-
sellingPlans:
|
|
6517
|
+
var Group = z58.object({
|
|
6518
|
+
discountAmount: z58.unknown(),
|
|
6519
|
+
discountType: z58.unknown(),
|
|
6520
|
+
id: z58.number().int().positive(),
|
|
6521
|
+
sellingPlans: z58.array(z58.object({ shopifyVariantId: Plan.shape.shopifyVariantId }).passthrough())
|
|
6276
6522
|
});
|
|
6277
6523
|
var readPostPurchaseLegacyPlans = (raw, variantId) => {
|
|
6278
6524
|
const group = Group.pick({ id: true, sellingPlans: true }).parse(raw);
|
|
@@ -6293,22 +6539,22 @@ var readPostPurchaseLegacyPlans = (raw, variantId) => {
|
|
|
6293
6539
|
};
|
|
6294
6540
|
|
|
6295
6541
|
// src/server/postPurchaseModernPlans.ts
|
|
6296
|
-
import { z as
|
|
6297
|
-
var Groups =
|
|
6298
|
-
|
|
6299
|
-
id:
|
|
6300
|
-
sellingPlans:
|
|
6542
|
+
import { z as z59 } from "zod";
|
|
6543
|
+
var Groups = z59.array(
|
|
6544
|
+
z59.object({
|
|
6545
|
+
id: z59.string().min(1),
|
|
6546
|
+
sellingPlans: z59.unknown()
|
|
6301
6547
|
})
|
|
6302
6548
|
);
|
|
6303
|
-
var PostPurchasePlanAllocations =
|
|
6549
|
+
var PostPurchasePlanAllocations = z59.array(z59.object({ sellingPlanGroupId: z59.string().min(1) }));
|
|
6304
6550
|
var Allocations = PostPurchasePlanAllocations.default([]);
|
|
6305
|
-
var Plans =
|
|
6551
|
+
var Plans = z59.array(
|
|
6306
6552
|
PostPurchasePlanOption.pick({ id: true, name: true }).extend({
|
|
6307
|
-
options:
|
|
6308
|
-
priceAdjustments:
|
|
6553
|
+
options: z59.array(z59.object({ value: z59.string().min(1) })).min(1),
|
|
6554
|
+
priceAdjustments: z59.array(z59.unknown())
|
|
6309
6555
|
})
|
|
6310
6556
|
);
|
|
6311
|
-
var FirstAdjustment =
|
|
6557
|
+
var FirstAdjustment = z59.object({
|
|
6312
6558
|
value: PostPurchaseProductSettings.shape.discount.shape.amount.unwrap(),
|
|
6313
6559
|
valueType: PostPurchasePlanAdjustment.shape.type
|
|
6314
6560
|
});
|
|
@@ -6366,72 +6612,45 @@ var projectPostPurchasePlanCatalog = (normalized, report) => {
|
|
|
6366
6612
|
};
|
|
6367
6613
|
|
|
6368
6614
|
// src/schema/postPurchaseSubscriptionProduct.ts
|
|
6369
|
-
import { z as
|
|
6615
|
+
import { z as z64 } from "zod";
|
|
6370
6616
|
|
|
6371
6617
|
// src/schema/postPurchaseProduct.ts
|
|
6372
|
-
import { z as
|
|
6618
|
+
import { z as z63 } from "zod";
|
|
6373
6619
|
|
|
6374
6620
|
// src/schema/postPurchaseProductDisplay.ts
|
|
6375
|
-
import { z as
|
|
6621
|
+
import { z as z61 } from "zod";
|
|
6376
6622
|
|
|
6377
6623
|
// src/schema/postPurchaseProductGallery.ts
|
|
6378
|
-
import { z as
|
|
6379
|
-
var PostPurchaseProductGallery =
|
|
6624
|
+
import { z as z60 } from "zod";
|
|
6625
|
+
var PostPurchaseProductGallery = z60.object({
|
|
6380
6626
|
/**
|
|
6381
6627
|
* Show the thumbnail row: the product had more than one image before `images` was capped at the
|
|
6382
6628
|
* sub-image limit, and that limit allows at least one thumbnail. It stays true when the cap leaves one
|
|
6383
6629
|
* image (legacy checks the count before slicing). Older replies omit it; fall back to `images.length > 1`.
|
|
6384
6630
|
*/
|
|
6385
|
-
hasMultiple:
|
|
6386
|
-
images:
|
|
6387
|
-
|
|
6388
|
-
description:
|
|
6389
|
-
id:
|
|
6390
|
-
source:
|
|
6391
|
-
thumbnail:
|
|
6631
|
+
hasMultiple: z60.boolean().optional(),
|
|
6632
|
+
images: z60.array(
|
|
6633
|
+
z60.object({
|
|
6634
|
+
description: z60.string(),
|
|
6635
|
+
id: z60.number().int().positive(),
|
|
6636
|
+
source: z60.string(),
|
|
6637
|
+
thumbnail: z60.string()
|
|
6392
6638
|
})
|
|
6393
6639
|
),
|
|
6394
|
-
selectedImageId:
|
|
6395
|
-
});
|
|
6396
|
-
|
|
6397
|
-
// src/schema/postPurchaseProductReviews.ts
|
|
6398
|
-
import { z as z54 } from "zod";
|
|
6399
|
-
var decimal = z54.union([
|
|
6400
|
-
z54.number(),
|
|
6401
|
-
z54.string().regex(/^\d+(?:\.\d+)?$/).transform(Number)
|
|
6402
|
-
]);
|
|
6403
|
-
var PostPurchaseCommonReviewRating = z54.object({ value: decimal.pipe(z54.number().min(0).max(5)) });
|
|
6404
|
-
var PostPurchaseKlaviyoReviewRating = PostPurchaseCommonReviewRating.extend({
|
|
6405
|
-
// Legacy validates key presence, then displays the raw value on five stars without rescaling.
|
|
6406
|
-
scale_max: z54.unknown().nonoptional(),
|
|
6407
|
-
scale_min: z54.unknown().nonoptional()
|
|
6408
|
-
});
|
|
6409
|
-
var PostPurchaseReviewCount = z54.string().regex(/^\d+(?:\.\d+)?$/).transform(Number).pipe(z54.number().int().nonnegative());
|
|
6410
|
-
var PostPurchaseReviewStyles = PostPurchaseSettings.shape.styles.unwrap().extend({
|
|
6411
|
-
strokeWidth: decimal.pipe(z54.number().nonnegative()).default(1)
|
|
6412
|
-
}).prefault({});
|
|
6413
|
-
var PostPurchaseProductReviews = z54.object({
|
|
6414
|
-
color: z54.string(),
|
|
6415
|
-
rating: z54.number().positive().max(5),
|
|
6416
|
-
reviewCount: z54.number().int().nonnegative(),
|
|
6417
|
-
reviewsPlural: z54.string(),
|
|
6418
|
-
reviewsSingular: z54.string(),
|
|
6419
|
-
size: z54.enum(["small", "medium", "large"]),
|
|
6420
|
-
strokeColor: z54.string(),
|
|
6421
|
-
strokeWidth: z54.number().nonnegative()
|
|
6640
|
+
selectedImageId: z60.number().int().positive().optional()
|
|
6422
6641
|
});
|
|
6423
6642
|
|
|
6424
6643
|
// src/schema/postPurchaseProductDisplay.ts
|
|
6425
6644
|
var language = PostPurchaseSettings.shape.language;
|
|
6426
|
-
var PostPurchasePrimaryImageSource =
|
|
6645
|
+
var PostPurchasePrimaryImageSource = z61.string().default(
|
|
6427
6646
|
"https://cdn.shopify.com/shopifycloud/shopify/assets/no-image-2048-5e88c1b20e087fb7bbe9a3771824e743c244f437e4f8ba93bbf7b11b53f7824c.gif"
|
|
6428
6647
|
);
|
|
6429
|
-
var PostPurchaseProductDisplay =
|
|
6648
|
+
var PostPurchaseProductDisplay = z61.object({
|
|
6430
6649
|
descriptionDocument: TiptapDocument.optional(),
|
|
6431
6650
|
discountedFrom: PostPurchaseSettings.shape.discountedFrom,
|
|
6432
6651
|
gallery: PostPurchaseProductGallery.optional(),
|
|
6433
6652
|
language: language.pick({ description: true, descriptionEnabled: true }),
|
|
6434
|
-
primaryImage:
|
|
6653
|
+
primaryImage: z61.object({ description: z61.string(), source: z61.string() }),
|
|
6435
6654
|
productOptions: PostPurchaseSettings.shape.productOptions.pick({
|
|
6436
6655
|
showDeclineAll: true,
|
|
6437
6656
|
showShipping: true,
|
|
@@ -6446,16 +6665,16 @@ var PostPurchaseProductDisplay = z55.object({
|
|
|
6446
6665
|
});
|
|
6447
6666
|
|
|
6448
6667
|
// src/schema/postPurchaseSubscription.ts
|
|
6449
|
-
import { z as
|
|
6450
|
-
var PostPurchaseSubscriptionTerms =
|
|
6668
|
+
import { z as z62 } from "zod";
|
|
6669
|
+
var PostPurchaseSubscriptionTerms = z62.object({
|
|
6451
6670
|
adjustment: PostPurchasePlanAdjustment.optional(),
|
|
6452
|
-
initialShippingPrice:
|
|
6453
|
-
intervalLabel:
|
|
6454
|
-
name:
|
|
6455
|
-
recurringShippingPrice:
|
|
6456
|
-
shippingOption:
|
|
6671
|
+
initialShippingPrice: z62.number().nonnegative(),
|
|
6672
|
+
intervalLabel: z62.string().min(1),
|
|
6673
|
+
name: z62.string().min(1),
|
|
6674
|
+
recurringShippingPrice: z62.number().nonnegative(),
|
|
6675
|
+
shippingOption: z62.object({ presentmentTitle: z62.string(), title: z62.string() }),
|
|
6457
6676
|
/** A switch to subscription (STS): the one subscription that charges no initial shipping (L122). */
|
|
6458
|
-
sts:
|
|
6677
|
+
sts: z62.literal(true).optional()
|
|
6459
6678
|
});
|
|
6460
6679
|
|
|
6461
6680
|
// src/schema/postPurchaseProduct.ts
|
|
@@ -6465,52 +6684,52 @@ var PostPurchaseVariant = OfferProduct.shape.variants.element.pick({ id: true, t
|
|
|
6465
6684
|
id: PostPurchaseVariantId,
|
|
6466
6685
|
price: PostPurchaseAmount
|
|
6467
6686
|
});
|
|
6468
|
-
var PreparedPostPurchaseProduct =
|
|
6687
|
+
var PreparedPostPurchaseProduct = z63.object({
|
|
6469
6688
|
display: PostPurchaseProductDisplay,
|
|
6470
|
-
labels:
|
|
6689
|
+
labels: z63.object({
|
|
6471
6690
|
declineAll: global.declineAll,
|
|
6472
|
-
free:
|
|
6691
|
+
free: z63.string(),
|
|
6473
6692
|
notAvailable: global.notAvailable,
|
|
6474
|
-
payNow:
|
|
6475
|
-
quantity:
|
|
6693
|
+
payNow: z63.string(),
|
|
6694
|
+
quantity: z63.string(),
|
|
6476
6695
|
recurringShipping: global.recurringShipping,
|
|
6477
6696
|
recurringSubtotal: global.recurringSubtotal,
|
|
6478
|
-
save:
|
|
6479
|
-
shipping:
|
|
6480
|
-
subtotal:
|
|
6481
|
-
taxes:
|
|
6482
|
-
total:
|
|
6697
|
+
save: z63.string(),
|
|
6698
|
+
shipping: z63.string(),
|
|
6699
|
+
subtotal: z63.string(),
|
|
6700
|
+
taxes: z63.string(),
|
|
6701
|
+
total: z63.string()
|
|
6483
6702
|
}),
|
|
6484
6703
|
product: OfferProduct.pick({ description: true, id: true, image: true, title: true }).extend({
|
|
6485
6704
|
description: OfferProduct.shape.description.nullable(),
|
|
6486
6705
|
id: PostPurchaseProductId,
|
|
6487
6706
|
variant: PostPurchaseVariant
|
|
6488
6707
|
}),
|
|
6489
|
-
selection:
|
|
6708
|
+
selection: z63.object({
|
|
6490
6709
|
productId: PostPurchaseProductId,
|
|
6491
|
-
quantity:
|
|
6492
|
-
sellingPlanId:
|
|
6710
|
+
quantity: z63.number().int().positive(),
|
|
6711
|
+
sellingPlanId: z63.number().int().positive().optional(),
|
|
6493
6712
|
variantId: PostPurchaseVariantId
|
|
6494
6713
|
}),
|
|
6495
|
-
status:
|
|
6496
|
-
terms:
|
|
6714
|
+
status: z63.literal("prepared"),
|
|
6715
|
+
terms: z63.object({
|
|
6497
6716
|
discount: PostPurchaseDiscount,
|
|
6498
|
-
shipping:
|
|
6499
|
-
amount:
|
|
6500
|
-
title:
|
|
6501
|
-
type:
|
|
6717
|
+
shipping: z63.object({
|
|
6718
|
+
amount: z63.number().nonnegative(),
|
|
6719
|
+
title: z63.string(),
|
|
6720
|
+
type: z63.enum(["free", "fixed_amount"])
|
|
6502
6721
|
}),
|
|
6503
6722
|
subscription: PostPurchaseSubscriptionTerms.optional()
|
|
6504
6723
|
})
|
|
6505
6724
|
});
|
|
6506
|
-
var PostPurchasePreparation =
|
|
6725
|
+
var PostPurchasePreparation = z63.discriminatedUnion("status", [
|
|
6507
6726
|
PreparedPostPurchaseProduct,
|
|
6508
|
-
|
|
6727
|
+
z63.object({ reason: z63.string(), status: z63.literal("unsupported") })
|
|
6509
6728
|
]);
|
|
6510
6729
|
|
|
6511
6730
|
// src/schema/postPurchaseSubscriptionProduct.ts
|
|
6512
6731
|
var PreparedPostPurchaseSubscription = PreparedPostPurchaseProduct.extend({
|
|
6513
|
-
selection: PreparedPostPurchaseProduct.shape.selection.extend({ sellingPlanId:
|
|
6732
|
+
selection: PreparedPostPurchaseProduct.shape.selection.extend({ sellingPlanId: z64.number().int().positive() }),
|
|
6514
6733
|
terms: PreparedPostPurchaseProduct.shape.terms.extend({
|
|
6515
6734
|
subscription: PostPurchaseSubscriptionTerms
|
|
6516
6735
|
}).refine(
|
|
@@ -6570,7 +6789,7 @@ var subscriptionUnavailable = (normalized, plan, allowCollection, quantity3) =>
|
|
|
6570
6789
|
};
|
|
6571
6790
|
|
|
6572
6791
|
// src/server/postPurchaseProductDisplay.ts
|
|
6573
|
-
import { z as
|
|
6792
|
+
import { z as z66 } from "zod";
|
|
6574
6793
|
|
|
6575
6794
|
// src/server/postPurchasePrimaryImage.ts
|
|
6576
6795
|
var postPurchasePrimaryImage = (product) => {
|
|
@@ -6596,7 +6815,7 @@ var postPurchaseProductGallery = (product, limit) => {
|
|
|
6596
6815
|
};
|
|
6597
6816
|
|
|
6598
6817
|
// src/transforms/postPurchase/convertDescriptionRte.ts
|
|
6599
|
-
import { z as
|
|
6818
|
+
import { z as z65 } from "zod";
|
|
6600
6819
|
var alignments = { center: "center", left: "start", right: "end" };
|
|
6601
6820
|
var colors = ["critical", "subdued", "success", "warning"];
|
|
6602
6821
|
var sizes2 = ["extraSmall", "small", "medium", "large", "extraLarge"];
|
|
@@ -6608,19 +6827,19 @@ var spacings2 = {
|
|
|
6608
6827
|
xloose: "extraLoose",
|
|
6609
6828
|
xtight: "extraTight"
|
|
6610
6829
|
};
|
|
6611
|
-
var Block =
|
|
6612
|
-
attrs:
|
|
6613
|
-
content:
|
|
6614
|
-
type:
|
|
6615
|
-
});
|
|
6616
|
-
var Run =
|
|
6617
|
-
var Inline =
|
|
6618
|
-
attrs:
|
|
6619
|
-
color:
|
|
6620
|
-
emphasis:
|
|
6621
|
-
size:
|
|
6830
|
+
var Block = z65.object({
|
|
6831
|
+
attrs: z65.object({ alignment: z65.enum(["left", "center", "right"]).optional().catch(void 0) }).catch({}),
|
|
6832
|
+
content: z65.array(z65.unknown()).catch([]),
|
|
6833
|
+
type: z65.literal("ShopifyTextBlock")
|
|
6834
|
+
});
|
|
6835
|
+
var Run = z65.object({ marks: z65.array(z65.unknown()).catch([]), text: z65.string().min(1), type: z65.literal("text") });
|
|
6836
|
+
var Inline = z65.object({
|
|
6837
|
+
attrs: z65.object({
|
|
6838
|
+
color: z65.enum(colors).optional().catch(void 0),
|
|
6839
|
+
emphasis: z65.array(z65.unknown()).catch([]),
|
|
6840
|
+
size: z65.enum(sizes2).optional().catch(void 0)
|
|
6622
6841
|
}),
|
|
6623
|
-
type:
|
|
6842
|
+
type: z65.literal("ShopifyTextInline")
|
|
6624
6843
|
});
|
|
6625
6844
|
var marks = (raw) => {
|
|
6626
6845
|
const found = raw.map((mark) => Inline.safeParse(mark)).find(({ success }) => success)?.data;
|
|
@@ -6653,7 +6872,7 @@ var convertDescriptionRte = (rte, spacing) => TiptapDocument.parse({
|
|
|
6653
6872
|
});
|
|
6654
6873
|
|
|
6655
6874
|
// src/server/postPurchaseProductDisplay.ts
|
|
6656
|
-
var ResponseLanguage =
|
|
6875
|
+
var ResponseLanguage = z66.object({ description: PostPurchaseProductDisplay.shape.responseDescription }).default({ description: "" });
|
|
6657
6876
|
var projectPostPurchaseProductDisplay = (effective, metadata, product) => PostPurchaseProductDisplay.parse({
|
|
6658
6877
|
descriptionDocument: effective.language.descriptionRte && convertDescriptionRte(effective.language.descriptionRte, effective.language.descriptionSpacing),
|
|
6659
6878
|
discountedFrom: effective.discountedFrom,
|
|
@@ -6665,146 +6884,6 @@ var projectPostPurchaseProductDisplay = (effective, metadata, product) => PostPu
|
|
|
6665
6884
|
shouldShowSavingText: effective.discount.shouldShowSavingText
|
|
6666
6885
|
});
|
|
6667
6886
|
|
|
6668
|
-
// src/server/postPurchaseReviewData.ts
|
|
6669
|
-
import { z as z65 } from "zod";
|
|
6670
|
-
|
|
6671
|
-
// src/server/postPurchaseJudgeMeReviews.ts
|
|
6672
|
-
import { z as z62 } from "zod";
|
|
6673
|
-
|
|
6674
|
-
// src/server/postPurchaseReviewFields.ts
|
|
6675
|
-
import { z as z61 } from "zod";
|
|
6676
|
-
var Field = z61.object({ key: z61.string(), namespace: z61.string(), value: z61.unknown().optional() });
|
|
6677
|
-
var reviewFields = (raw) => {
|
|
6678
|
-
const fields4 = z61.array(z61.unknown()).parse(raw).flatMap((field) => {
|
|
6679
|
-
const parsed = Field.safeParse(field);
|
|
6680
|
-
return parsed.success ? [parsed.data] : [];
|
|
6681
|
-
});
|
|
6682
|
-
return (namespace, key) => fields4.find((field) => field.namespace === namespace && field.key === key);
|
|
6683
|
-
};
|
|
6684
|
-
var ReviewFieldError = class extends Error {
|
|
6685
|
-
constructor(field) {
|
|
6686
|
-
super(`Unreadable review field ${field}`);
|
|
6687
|
-
this.field = field;
|
|
6688
|
-
}
|
|
6689
|
-
};
|
|
6690
|
-
var readReviewField = (field, read) => {
|
|
6691
|
-
try {
|
|
6692
|
-
return read(field.value);
|
|
6693
|
-
} catch {
|
|
6694
|
-
throw new ReviewFieldError(`${field.namespace}.${field.key}`);
|
|
6695
|
-
}
|
|
6696
|
-
};
|
|
6697
|
-
|
|
6698
|
-
// src/server/postPurchaseJudgeMeReviews.ts
|
|
6699
|
-
var readJudgeMeReviews = (find) => {
|
|
6700
|
-
const field = find("judgeme", "badge");
|
|
6701
|
-
if (!field) return null;
|
|
6702
|
-
return readReviewField(field, (value) => {
|
|
6703
|
-
const html = z62.string().parse(value);
|
|
6704
|
-
const rating = html.match(/data-average-rating=['"](\d+(?:\.\d+)?)['"]/);
|
|
6705
|
-
const count = html.match(/data-number-of-reviews=['"](\d+)['"]/);
|
|
6706
|
-
if (!rating || !count) throw new Error("Unreadable Judge.me attributes");
|
|
6707
|
-
return {
|
|
6708
|
-
rating: PostPurchaseCommonReviewRating.parse({ value: rating[1] }).value,
|
|
6709
|
-
reviewCount: PostPurchaseReviewCount.parse(count[1])
|
|
6710
|
-
};
|
|
6711
|
-
});
|
|
6712
|
-
};
|
|
6713
|
-
|
|
6714
|
-
// src/server/postPurchaseOkendoReviews.ts
|
|
6715
|
-
import { z as z63 } from "zod";
|
|
6716
|
-
var Summary = z63.object({
|
|
6717
|
-
reviewAverageValue: PostPurchaseCommonReviewRating.shape.value,
|
|
6718
|
-
reviewCount: z63.union([PostPurchaseProductReviews.shape.reviewCount, PostPurchaseReviewCount])
|
|
6719
|
-
});
|
|
6720
|
-
var readOkendoReviews = (find) => {
|
|
6721
|
-
const field = find("okendo", "summaryData");
|
|
6722
|
-
if (!field) return null;
|
|
6723
|
-
const summary = readReviewField(field, (value) => Summary.parse(JSON.parse(z63.string().parse(value))));
|
|
6724
|
-
return { rating: summary.reviewAverageValue, reviewCount: summary.reviewCount };
|
|
6725
|
-
};
|
|
6726
|
-
|
|
6727
|
-
// src/server/postPurchaseScalarReviews.ts
|
|
6728
|
-
import { z as z64 } from "zod";
|
|
6729
|
-
var readScalarReviews = (find, namespace, ratingKey, countKey) => {
|
|
6730
|
-
const rating = find(namespace, ratingKey);
|
|
6731
|
-
const count = find(namespace, countKey);
|
|
6732
|
-
if (!rating || !count) return null;
|
|
6733
|
-
return {
|
|
6734
|
-
rating: readReviewField(
|
|
6735
|
-
rating,
|
|
6736
|
-
(value) => PostPurchaseCommonReviewRating.parse({ value: z64.string().parse(value) }).value
|
|
6737
|
-
),
|
|
6738
|
-
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
6739
|
-
};
|
|
6740
|
-
};
|
|
6741
|
-
|
|
6742
|
-
// src/server/postPurchaseReviewData.ts
|
|
6743
|
-
var readStandardReviews = (find) => {
|
|
6744
|
-
const rating = find("reviews", "rating");
|
|
6745
|
-
const count = find("reviews", "rating_count");
|
|
6746
|
-
if (!rating || !count) return null;
|
|
6747
|
-
return {
|
|
6748
|
-
rating: readReviewField(rating, (raw) => {
|
|
6749
|
-
const value = z65.string().parse(raw);
|
|
6750
|
-
return PostPurchaseCommonReviewRating.parse(/^\d+(?:\.\d+)?$/.test(value) ? { value } : JSON.parse(value)).value;
|
|
6751
|
-
}),
|
|
6752
|
-
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
6753
|
-
};
|
|
6754
|
-
};
|
|
6755
|
-
var readPostPurchaseReviewData = (provider, find) => {
|
|
6756
|
-
if (provider === "yotpo") return readScalarReviews(find, "yotpo", "reviews_average", "reviews_count");
|
|
6757
|
-
if (provider === "stamped") return readScalarReviews(find, "stamped", "reviews_average", "reviews_count");
|
|
6758
|
-
if (provider === "judgeme") return readJudgeMeReviews(find);
|
|
6759
|
-
if (provider === "okendo") return readOkendoReviews(find);
|
|
6760
|
-
if (provider === "opinew") return readScalarReviews(find, "opinew_metafields", "reviews_average", "reviews_count");
|
|
6761
|
-
if (provider === "reviewsio")
|
|
6762
|
-
return readScalarReviews(find, "reviewscouk", "rating", "total") ?? readStandardReviews(find);
|
|
6763
|
-
if (provider === "loox") return readScalarReviews(find, "loox", "avg_rating", "num_reviews");
|
|
6764
|
-
if (provider === "junip") return readScalarReviews(find, "junip", "rating_average", "rating_count");
|
|
6765
|
-
const rating = find("reviews", "rating");
|
|
6766
|
-
const count = find("reviews", "rating_count");
|
|
6767
|
-
if (!rating || !count) return null;
|
|
6768
|
-
return {
|
|
6769
|
-
rating: readReviewField(
|
|
6770
|
-
rating,
|
|
6771
|
-
(value) => PostPurchaseKlaviyoReviewRating.parse(JSON.parse(z65.string().parse(value))).value
|
|
6772
|
-
),
|
|
6773
|
-
reviewCount: readReviewField(count, (value) => PostPurchaseReviewCount.parse(value))
|
|
6774
|
-
};
|
|
6775
|
-
};
|
|
6776
|
-
|
|
6777
|
-
// src/server/postPurchaseReviewProvider.ts
|
|
6778
|
-
import { z as z66 } from "zod";
|
|
6779
|
-
var IntegrationInput = z66.record(z66.string(), z66.unknown()).prefault({});
|
|
6780
|
-
var precedence = [
|
|
6781
|
-
"klaviyo",
|
|
6782
|
-
"judgeme",
|
|
6783
|
-
"junip",
|
|
6784
|
-
"loox",
|
|
6785
|
-
"okendo",
|
|
6786
|
-
"opinew",
|
|
6787
|
-
"reviewsio",
|
|
6788
|
-
"stamped",
|
|
6789
|
-
"yotpo"
|
|
6790
|
-
];
|
|
6791
|
-
var ReviewsIoFlag = z66.preprocess((value) => {
|
|
6792
|
-
if (typeof value === "string") {
|
|
6793
|
-
const flag = value.toLowerCase();
|
|
6794
|
-
if (flag === "true" || flag === "yes") return true;
|
|
6795
|
-
if (flag === "false" || flag === "no") return false;
|
|
6796
|
-
}
|
|
6797
|
-
return value;
|
|
6798
|
-
}, PostPurchaseSettings.shape.integrations.shape.reviewsio);
|
|
6799
|
-
var selectPostPurchaseReviewProvider = (raw) => {
|
|
6800
|
-
const integrations = IntegrationInput.parse(raw);
|
|
6801
|
-
for (const provider of precedence) {
|
|
6802
|
-
const schema = provider === "reviewsio" ? ReviewsIoFlag : PostPurchaseSettings.shape.integrations.shape[provider];
|
|
6803
|
-
if (schema.parse(integrations[provider])) return provider;
|
|
6804
|
-
}
|
|
6805
|
-
return null;
|
|
6806
|
-
};
|
|
6807
|
-
|
|
6808
6887
|
// src/server/postPurchaseReviews.ts
|
|
6809
6888
|
var projectPostPurchaseReviews = (settings, { id: productId2, metafields }, report) => {
|
|
6810
6889
|
try {
|