@rebuy/rebuy 3.12.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +34 -9
- package/dist/index.mjs +34 -9
- package/dist/schema/cabShopConfig.d.ts +2 -0
- package/dist/server/adsEngine.d.ts +4 -1
- package/dist/server/index.cjs +67 -34
- package/dist/server/index.mjs +67 -34
- package/dist/server/monetizeOffers.d.ts +4 -2
- package/dist/server/requestSchemas.d.ts +1 -1
- package/dist/transforms/contentBlockV1/convertContentBlockTextToV2.d.ts +3 -1
- package/dist/transforms/index.cjs +20 -9
- package/dist/transforms/index.mjs +20 -9
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1162,6 +1162,20 @@ var CabShopConfig = import_zod2.z.object({
|
|
|
1162
1162
|
apiKey: import_zod2.z.string(),
|
|
1163
1163
|
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
1164
1164
|
billingVersion: import_zod2.z.string().nullable().catch(null).default(null),
|
|
1165
|
+
/**
|
|
1166
|
+
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
1167
|
+
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
1168
|
+
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
1169
|
+
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
1170
|
+
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
1171
|
+
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
1172
|
+
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
1173
|
+
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
1174
|
+
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
1175
|
+
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
1176
|
+
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
1177
|
+
*/
|
|
1178
|
+
cacheKey: import_zod2.z.union([import_zod2.z.number(), import_zod2.z.string()]).transform((value) => Number(value)).pipe(import_zod2.z.number().int().positive()).optional().catch(void 0),
|
|
1165
1179
|
/**
|
|
1166
1180
|
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
1167
1181
|
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
@@ -3994,7 +4008,7 @@ var normalizeTokens = (text, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
3994
4008
|
|
|
3995
4009
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
3996
4010
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
3997
|
-
var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
4011
|
+
var convertContentBlockTextToV2 = (language, text = literalText, textAlign = "center") => {
|
|
3998
4012
|
const description = language?.description;
|
|
3999
4013
|
const superTitle = language?.superTitle;
|
|
4000
4014
|
const title = language?.title;
|
|
@@ -4006,20 +4020,20 @@ var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
|
4006
4020
|
fontSize: "extraLarge",
|
|
4007
4021
|
marks: [{ type: "bold" }],
|
|
4008
4022
|
text: text("superTitle", superTitle),
|
|
4009
|
-
textAlign
|
|
4023
|
+
textAlign
|
|
4010
4024
|
}) : null,
|
|
4011
4025
|
title ? convertTextToTiptapParagraph({
|
|
4012
4026
|
color: "base",
|
|
4013
4027
|
fontSize: "medium",
|
|
4014
4028
|
marks: [{ type: "bold" }],
|
|
4015
4029
|
text: text("title", title),
|
|
4016
|
-
textAlign
|
|
4030
|
+
textAlign
|
|
4017
4031
|
}) : null,
|
|
4018
4032
|
description ? convertTextToTiptapParagraph({
|
|
4019
4033
|
color: "base",
|
|
4020
4034
|
fontSize: "base",
|
|
4021
4035
|
text: text("description", description),
|
|
4022
|
-
textAlign
|
|
4036
|
+
textAlign
|
|
4023
4037
|
}) : null
|
|
4024
4038
|
].filter((p) => p !== null),
|
|
4025
4039
|
type: "doc"
|
|
@@ -4736,6 +4750,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
4736
4750
|
const defaultValue = header[field];
|
|
4737
4751
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
4738
4752
|
});
|
|
4753
|
+
var headerNode = (header, textAlign) => ({
|
|
4754
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
4755
|
+
sectionType: "text"
|
|
4756
|
+
});
|
|
4757
|
+
var buildHeaderNavRow = (header) => ({
|
|
4758
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
4759
|
+
direction: "grid",
|
|
4760
|
+
grid: { columns: ["fill", "auto"] },
|
|
4761
|
+
name: "header",
|
|
4762
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
4763
|
+
sectionType: "layout",
|
|
4764
|
+
spacing: "base"
|
|
4765
|
+
});
|
|
4739
4766
|
var HAS_PRODUCTS = "hasProducts";
|
|
4740
4767
|
var hasProductsMutators = () => [
|
|
4741
4768
|
{
|
|
@@ -4828,6 +4855,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4828
4855
|
const pageSize = Math.max(columns, 1);
|
|
4829
4856
|
const limit = settings.limit ?? void 0;
|
|
4830
4857
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
4858
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
4831
4859
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
4832
4860
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
4833
4861
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -4885,7 +4913,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4885
4913
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
4886
4914
|
...listLayout,
|
|
4887
4915
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
4888
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
4916
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
4889
4917
|
sectionType: "carousel"
|
|
4890
4918
|
};
|
|
4891
4919
|
return CABRootSection.parse({
|
|
@@ -4895,10 +4923,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4895
4923
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
4896
4924
|
...name && { name },
|
|
4897
4925
|
previewMode: settings.previewMode,
|
|
4898
|
-
sections: [
|
|
4899
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
4900
|
-
display
|
|
4901
|
-
],
|
|
4926
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
4902
4927
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
4903
4928
|
tracking: settings.tracking,
|
|
4904
4929
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
package/dist/index.mjs
CHANGED
|
@@ -876,6 +876,20 @@ var CabShopConfig = z2.object({
|
|
|
876
876
|
apiKey: z2.string(),
|
|
877
877
|
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
878
878
|
billingVersion: z2.string().nullable().catch(null).default(null),
|
|
879
|
+
/**
|
|
880
|
+
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
881
|
+
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
882
|
+
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
883
|
+
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
884
|
+
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
885
|
+
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
886
|
+
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
887
|
+
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
888
|
+
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
889
|
+
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
890
|
+
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
891
|
+
*/
|
|
892
|
+
cacheKey: z2.union([z2.number(), z2.string()]).transform((value) => Number(value)).pipe(z2.number().int().positive()).optional().catch(void 0),
|
|
879
893
|
/**
|
|
880
894
|
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
881
895
|
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
@@ -3708,7 +3722,7 @@ var normalizeTokens = (text, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
3708
3722
|
|
|
3709
3723
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
3710
3724
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
3711
|
-
var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
3725
|
+
var convertContentBlockTextToV2 = (language, text = literalText, textAlign = "center") => {
|
|
3712
3726
|
const description = language?.description;
|
|
3713
3727
|
const superTitle = language?.superTitle;
|
|
3714
3728
|
const title = language?.title;
|
|
@@ -3720,20 +3734,20 @@ var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
|
3720
3734
|
fontSize: "extraLarge",
|
|
3721
3735
|
marks: [{ type: "bold" }],
|
|
3722
3736
|
text: text("superTitle", superTitle),
|
|
3723
|
-
textAlign
|
|
3737
|
+
textAlign
|
|
3724
3738
|
}) : null,
|
|
3725
3739
|
title ? convertTextToTiptapParagraph({
|
|
3726
3740
|
color: "base",
|
|
3727
3741
|
fontSize: "medium",
|
|
3728
3742
|
marks: [{ type: "bold" }],
|
|
3729
3743
|
text: text("title", title),
|
|
3730
|
-
textAlign
|
|
3744
|
+
textAlign
|
|
3731
3745
|
}) : null,
|
|
3732
3746
|
description ? convertTextToTiptapParagraph({
|
|
3733
3747
|
color: "base",
|
|
3734
3748
|
fontSize: "base",
|
|
3735
3749
|
text: text("description", description),
|
|
3736
|
-
textAlign
|
|
3750
|
+
textAlign
|
|
3737
3751
|
}) : null
|
|
3738
3752
|
].filter((p) => p !== null),
|
|
3739
3753
|
type: "doc"
|
|
@@ -4450,6 +4464,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
4450
4464
|
const defaultValue = header[field];
|
|
4451
4465
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
4452
4466
|
});
|
|
4467
|
+
var headerNode = (header, textAlign) => ({
|
|
4468
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
4469
|
+
sectionType: "text"
|
|
4470
|
+
});
|
|
4471
|
+
var buildHeaderNavRow = (header) => ({
|
|
4472
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
4473
|
+
direction: "grid",
|
|
4474
|
+
grid: { columns: ["fill", "auto"] },
|
|
4475
|
+
name: "header",
|
|
4476
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
4477
|
+
sectionType: "layout",
|
|
4478
|
+
spacing: "base"
|
|
4479
|
+
});
|
|
4453
4480
|
var HAS_PRODUCTS = "hasProducts";
|
|
4454
4481
|
var hasProductsMutators = () => [
|
|
4455
4482
|
{
|
|
@@ -4542,6 +4569,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4542
4569
|
const pageSize = Math.max(columns, 1);
|
|
4543
4570
|
const limit = settings.limit ?? void 0;
|
|
4544
4571
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
4572
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
4545
4573
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
4546
4574
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
4547
4575
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -4599,7 +4627,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4599
4627
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
4600
4628
|
...listLayout,
|
|
4601
4629
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
4602
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
4630
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
4603
4631
|
sectionType: "carousel"
|
|
4604
4632
|
};
|
|
4605
4633
|
return CABRootSection.parse({
|
|
@@ -4609,10 +4637,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
4609
4637
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
4610
4638
|
...name && { name },
|
|
4611
4639
|
previewMode: settings.previewMode,
|
|
4612
|
-
sections: [
|
|
4613
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
4614
|
-
display
|
|
4615
|
-
],
|
|
4640
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
4616
4641
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
4617
4642
|
tracking: settings.tracking,
|
|
4618
4643
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
|
@@ -39,6 +39,7 @@ export declare const CabShopConfig: z.ZodObject<{
|
|
|
39
39
|
}, z.core.$strip>>>>>;
|
|
40
40
|
apiKey: z.ZodString;
|
|
41
41
|
billingVersion: z.ZodDefault<z.ZodCatch<z.ZodNullable<z.ZodString>>>;
|
|
42
|
+
cacheKey: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>, z.ZodNumber>>>;
|
|
42
43
|
currency: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
43
44
|
markets: z.ZodPipe<z.ZodDefault<z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
44
45
|
enabled: z.ZodBoolean;
|
|
@@ -76,6 +77,7 @@ export declare const CabUserConfig: z.ZodObject<{
|
|
|
76
77
|
}, z.core.$strip>>>>>;
|
|
77
78
|
apiKey: z.ZodString;
|
|
78
79
|
billingVersion: z.ZodDefault<z.ZodCatch<z.ZodNullable<z.ZodString>>>;
|
|
80
|
+
cacheKey: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>, z.ZodNumber>>>;
|
|
79
81
|
currency: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
80
82
|
markets: z.ZodPipe<z.ZodDefault<z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
81
83
|
enabled: z.ZodBoolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { type CabUserConfig } from '../schema/cabShopConfig';
|
|
2
3
|
import { type MonetizeInput, type ServerContext } from '../server/shared';
|
|
3
4
|
/**
|
|
4
5
|
* A single ads-engine offer. The `*_url` fields are tracking endpoints the engine returns fully
|
|
@@ -34,5 +35,7 @@ export declare const buildAdsParams: (input: MonetizeInput, publisherKey: string
|
|
|
34
35
|
* Fetch live ad offers from the Rebuy ads engine. Resolves the shop's publisher key server-side (and
|
|
35
36
|
* returns `[]` when the shop isn't enrolled — no ads, not an error). A non-prod upstream sends the
|
|
36
37
|
* staging supply key; a non-prod/editor/QA-email request is flagged `testing` (excluded from billing).
|
|
38
|
+
* `prefetched` is a caller's already-resolved shop config (inline hydration threads it) — consulted only
|
|
39
|
+
* AFTER the gates below, so a gated-out request still costs zero upstream calls either way.
|
|
37
40
|
*/
|
|
38
|
-
export declare const fetchAdsEngineOffers: (input: MonetizeInput, ctx: ServerContext) => Promise<AdsEngineOffer[]>;
|
|
41
|
+
export declare const fetchAdsEngineOffers: (input: MonetizeInput, ctx: ServerContext, prefetched?: CabUserConfig) => Promise<AdsEngineOffer[]>;
|
package/dist/server/index.cjs
CHANGED
|
@@ -582,6 +582,20 @@ var CabShopConfig = import_zod6.z.object({
|
|
|
582
582
|
apiKey: import_zod6.z.string(),
|
|
583
583
|
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
584
584
|
billingVersion: import_zod6.z.string().nullable().catch(null).default(null),
|
|
585
|
+
/**
|
|
586
|
+
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
587
|
+
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
588
|
+
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
589
|
+
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
590
|
+
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
591
|
+
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
592
|
+
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
593
|
+
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
594
|
+
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
595
|
+
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
596
|
+
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
597
|
+
*/
|
|
598
|
+
cacheKey: import_zod6.z.union([import_zod6.z.number(), import_zod6.z.string()]).transform((value) => Number(value)).pipe(import_zod6.z.number().int().positive()).optional().catch(void 0),
|
|
585
599
|
/**
|
|
586
600
|
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
587
601
|
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
@@ -2134,7 +2148,7 @@ var normalizeTokens = (text2, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
2134
2148
|
|
|
2135
2149
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
2136
2150
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
2137
|
-
var convertContentBlockTextToV2 = (language, text2 = literalText) => {
|
|
2151
|
+
var convertContentBlockTextToV2 = (language, text2 = literalText, textAlign = "center") => {
|
|
2138
2152
|
const description = language?.description;
|
|
2139
2153
|
const superTitle = language?.superTitle;
|
|
2140
2154
|
const title = language?.title;
|
|
@@ -2146,20 +2160,20 @@ var convertContentBlockTextToV2 = (language, text2 = literalText) => {
|
|
|
2146
2160
|
fontSize: "extraLarge",
|
|
2147
2161
|
marks: [{ type: "bold" }],
|
|
2148
2162
|
text: text2("superTitle", superTitle),
|
|
2149
|
-
textAlign
|
|
2163
|
+
textAlign
|
|
2150
2164
|
}) : null,
|
|
2151
2165
|
title ? convertTextToTiptapParagraph({
|
|
2152
2166
|
color: "base",
|
|
2153
2167
|
fontSize: "medium",
|
|
2154
2168
|
marks: [{ type: "bold" }],
|
|
2155
2169
|
text: text2("title", title),
|
|
2156
|
-
textAlign
|
|
2170
|
+
textAlign
|
|
2157
2171
|
}) : null,
|
|
2158
2172
|
description ? convertTextToTiptapParagraph({
|
|
2159
2173
|
color: "base",
|
|
2160
2174
|
fontSize: "base",
|
|
2161
2175
|
text: text2("description", description),
|
|
2162
|
-
textAlign
|
|
2176
|
+
textAlign
|
|
2163
2177
|
}) : null
|
|
2164
2178
|
].filter((p) => p !== null),
|
|
2165
2179
|
type: "doc"
|
|
@@ -3273,6 +3287,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
3273
3287
|
const defaultValue = header[field];
|
|
3274
3288
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
3275
3289
|
});
|
|
3290
|
+
var headerNode = (header, textAlign) => ({
|
|
3291
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
3292
|
+
sectionType: "text"
|
|
3293
|
+
});
|
|
3294
|
+
var buildHeaderNavRow = (header) => ({
|
|
3295
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
3296
|
+
direction: "grid",
|
|
3297
|
+
grid: { columns: ["fill", "auto"] },
|
|
3298
|
+
name: "header",
|
|
3299
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
3300
|
+
sectionType: "layout",
|
|
3301
|
+
spacing: "base"
|
|
3302
|
+
});
|
|
3276
3303
|
var HAS_PRODUCTS = "hasProducts";
|
|
3277
3304
|
var hasProductsMutators = () => [
|
|
3278
3305
|
{
|
|
@@ -3365,6 +3392,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3365
3392
|
const pageSize = Math.max(columns, 1);
|
|
3366
3393
|
const limit = settings.limit ?? void 0;
|
|
3367
3394
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
3395
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
3368
3396
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
3369
3397
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
3370
3398
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -3422,7 +3450,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3422
3450
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
3423
3451
|
...listLayout,
|
|
3424
3452
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
3425
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
3453
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
3426
3454
|
sectionType: "carousel"
|
|
3427
3455
|
};
|
|
3428
3456
|
return CABRootSection.parse({
|
|
@@ -3432,10 +3460,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3432
3460
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
3433
3461
|
...name && { name },
|
|
3434
3462
|
previewMode: settings.previewMode,
|
|
3435
|
-
sections: [
|
|
3436
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
3437
|
-
display
|
|
3438
|
-
],
|
|
3463
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
3439
3464
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
3440
3465
|
tracking: settings.tracking,
|
|
3441
3466
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
|
@@ -4008,10 +4033,10 @@ var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddr
|
|
|
4008
4033
|
...userAgent && { user_agent: userAgent }
|
|
4009
4034
|
};
|
|
4010
4035
|
};
|
|
4011
|
-
var fetchAdsEngineOffers = async (input, ctx) => {
|
|
4036
|
+
var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
|
|
4012
4037
|
if (!input.country || !SUPPORTED_COUNTRIES.has(input.country.toUpperCase())) return [];
|
|
4013
4038
|
if (!(input.sessionId || input.visitorId)) return [];
|
|
4014
|
-
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
4039
|
+
const { shop } = prefetched ?? await fetchUserConfig({ shop: input.shop }, ctx);
|
|
4015
4040
|
const publisherKey = shop.monetize?.publisherKey;
|
|
4016
4041
|
if (!publisherKey) return [];
|
|
4017
4042
|
const isProduction = ctx.host === REBUY_ENGINE_HOST;
|
|
@@ -4169,8 +4194,10 @@ var composeOfferCard = (offer, input) => {
|
|
|
4169
4194
|
};
|
|
4170
4195
|
|
|
4171
4196
|
// src/server/monetizeOffers.ts
|
|
4172
|
-
var fetchMonetizeOffers = async (input, ctx) => {
|
|
4173
|
-
const composed = (await fetchAdsEngineOffers(input, ctx)).map(
|
|
4197
|
+
var fetchMonetizeOffers = async (input, ctx, prefetched) => {
|
|
4198
|
+
const composed = (await fetchAdsEngineOffers(input, ctx, prefetched)).map(
|
|
4199
|
+
(offer) => composeOfferCard(offer, input)
|
|
4200
|
+
);
|
|
4174
4201
|
const offers = composed.filter((card) => card !== void 0);
|
|
4175
4202
|
if (offers.length < composed.length)
|
|
4176
4203
|
ctx.reportDrift?.({
|
|
@@ -4225,8 +4252,8 @@ var dispatchWidgetTransform = (id, normalized, report) => {
|
|
|
4225
4252
|
if (isOfferV1(normalized)) return convertOfferToV2({ id, name: normalized.name, settings: normalized });
|
|
4226
4253
|
return normalized;
|
|
4227
4254
|
};
|
|
4228
|
-
var fetchProgressBarContext = async (shop, ctx) => {
|
|
4229
|
-
const config = await fetchUserConfig({ shop }, ctx).catch(() => void 0);
|
|
4255
|
+
var fetchProgressBarContext = async (shop, ctx, prefetched) => {
|
|
4256
|
+
const config = prefetched === void 0 ? await fetchUserConfig({ shop }, ctx).catch(() => void 0) : prefetched;
|
|
4230
4257
|
if (!config) return {};
|
|
4231
4258
|
if (config.shop.billingVersion === "v6" && config.shop.activePackages.some((pkg) => pkg.shortName === "")) {
|
|
4232
4259
|
ctx.reportDrift?.({
|
|
@@ -4265,7 +4292,7 @@ var findMonetizeMarker = (section) => {
|
|
|
4265
4292
|
}
|
|
4266
4293
|
return void 0;
|
|
4267
4294
|
};
|
|
4268
|
-
var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
4295
|
+
var hydrateMonetizeMarkers = async (converted, input, ctx, config) => {
|
|
4269
4296
|
if (!input.monetize || !isCABTree(converted)) return;
|
|
4270
4297
|
const first = findMonetizeMarker(converted);
|
|
4271
4298
|
if (!first) return;
|
|
@@ -4277,7 +4304,8 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
|
4277
4304
|
shop: input.shop,
|
|
4278
4305
|
visitorId: input.monetize.visitorId || input.visitorId
|
|
4279
4306
|
},
|
|
4280
|
-
ctx
|
|
4307
|
+
ctx,
|
|
4308
|
+
config ?? void 0
|
|
4281
4309
|
);
|
|
4282
4310
|
first.offers = offers;
|
|
4283
4311
|
} catch (err) {
|
|
@@ -4287,7 +4315,7 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
|
4287
4315
|
});
|
|
4288
4316
|
}
|
|
4289
4317
|
};
|
|
4290
|
-
var fetchAndConvert = async (id, input, ctx) => {
|
|
4318
|
+
var fetchAndConvert = async (id, input, ctx, config) => {
|
|
4291
4319
|
const params = { id: String(id), shop: input.shop };
|
|
4292
4320
|
if (input.cacheKey != null) params.cache_key = String(input.cacheKey);
|
|
4293
4321
|
if (input.key) params.key = input.key;
|
|
@@ -4304,34 +4332,32 @@ var fetchAndConvert = async (id, input, ctx) => {
|
|
|
4304
4332
|
if (isMissingUpstreamPayload(data)) throw new NotFoundError(`Widget not found: ${id}`);
|
|
4305
4333
|
const normalized = convertNumericObjects(deepCamelCase(data));
|
|
4306
4334
|
if (isProgressBarV1(normalized) && normalized.barSettings.barType === "smart-cart") {
|
|
4307
|
-
const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx);
|
|
4335
|
+
const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4308
4336
|
return convertSmartCartProgressBarToV2({ bars: bars ?? [], id, progressColor, settings: normalized });
|
|
4309
4337
|
}
|
|
4310
4338
|
if (isConvertibleProgressBarV1(normalized)) {
|
|
4311
|
-
const { progressColor } = await fetchProgressBarContext(input.shop, ctx);
|
|
4339
|
+
const { progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4312
4340
|
return convertProgressBarToV2({ id, progressColor, settings: normalized });
|
|
4313
4341
|
}
|
|
4314
4342
|
const converted = dispatchWidgetTransform(id, normalized, ctx.reportDrift);
|
|
4315
4343
|
const colorless = isCABRootSection(normalized) ? colorlessProgressBars(converted) : [];
|
|
4316
4344
|
if (colorless.length > 0) {
|
|
4317
|
-
const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx);
|
|
4345
|
+
const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4318
4346
|
if (color) for (const bar of colorless) bar.progressColor = color;
|
|
4319
4347
|
}
|
|
4320
4348
|
return converted;
|
|
4321
4349
|
};
|
|
4322
|
-
var resolveWidgetExperiment =
|
|
4323
|
-
if (!input.visitorId) return void 0;
|
|
4324
|
-
try {
|
|
4325
|
-
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
4326
|
-
return resolveExperiment(shop.activeExperiments, input.id, input.visitorId);
|
|
4327
|
-
} catch {
|
|
4328
|
-
return void 0;
|
|
4329
|
-
}
|
|
4330
|
-
};
|
|
4350
|
+
var resolveWidgetExperiment = (input, config) => input.visitorId && config ? resolveExperiment(config.shop.activeExperiments, input.id, input.visitorId) : void 0;
|
|
4331
4351
|
var convertWidgetSettings = async (input, ctx) => {
|
|
4332
|
-
const
|
|
4333
|
-
const
|
|
4334
|
-
|
|
4352
|
+
const config = input.cacheKey != null && !input.visitorId ? void 0 : await fetchUserConfig({ shop: input.shop }, ctx).catch(() => null);
|
|
4353
|
+
const experiment = resolveWidgetExperiment(input, config);
|
|
4354
|
+
const result = await fetchAndConvert(
|
|
4355
|
+
experiment?.elementId ?? input.id,
|
|
4356
|
+
{ ...input, cacheKey: input.cacheKey ?? config?.shop.cacheKey },
|
|
4357
|
+
ctx,
|
|
4358
|
+
config
|
|
4359
|
+
);
|
|
4360
|
+
await hydrateMonetizeMarkers(result, input, ctx, config);
|
|
4335
4361
|
return experiment && isCABTree(result) ? { ...result, experiment } : result;
|
|
4336
4362
|
};
|
|
4337
4363
|
|
|
@@ -4675,7 +4701,14 @@ var MonetizeInputSchema = import_zod29.z.object({
|
|
|
4675
4701
|
zipCode: import_zod29.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4676
4702
|
});
|
|
4677
4703
|
var WidgetSettingsInputSchema = import_zod29.z.object({
|
|
4678
|
-
|
|
4704
|
+
/**
|
|
4705
|
+
* Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
|
|
4706
|
+
* `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
|
|
4707
|
+
* an unconstrained `0`/negative/float here would go upstream as a constant never-rotating `cache_key`
|
|
4708
|
+
* and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
|
|
4709
|
+
* the read to un-busted, never 400 a widget render.
|
|
4710
|
+
*/
|
|
4711
|
+
cacheKey: import_zod29.z.number().int().positive().optional().catch(void 0),
|
|
4679
4712
|
id: import_zod29.z.number(),
|
|
4680
4713
|
key: import_zod29.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4681
4714
|
/**
|
package/dist/server/index.mjs
CHANGED
|
@@ -534,6 +534,20 @@ var CabShopConfig = z6.object({
|
|
|
534
534
|
apiKey: z6.string(),
|
|
535
535
|
/** The shop's billing generation; the smart-cart entitlement gate only fires for `'v6'`. Missing/drift ⇒ `null` (fail open, ungated). */
|
|
536
536
|
billingVersion: z6.string().nullable().catch(null).default(null),
|
|
537
|
+
/**
|
|
538
|
+
* The shop's rotating cache-buster: `onsite_cache_key` internally — a unix timestamp the engine bumps,
|
|
539
|
+
* and the edge copy of this config purges, on every widget/settings/theme edit — but serialized on the
|
|
540
|
+
* wire as `cache_key`, which `deepCamelCase` maps to this field (renaming it `onsiteCacheKey` would
|
|
541
|
+
* sever the mapping silently; the wire-level assertion in `userConfig.test.ts` is what fails loudly).
|
|
542
|
+
* Threaded onto the widgets/settings read as `cache_key` so the engine marks the response
|
|
543
|
+
* immutable-cacheable and the `cached.*` edge serves it fresh-by-URL. The engine sends a numeric
|
|
544
|
+
* string — coerce — but admit only number/string inputs and constrain to a positive integer: a bare
|
|
545
|
+
* coercion folds `null`/`''`/`false` to `0` and `true` to `1` — "valid" numbers that would go upstream
|
|
546
|
+
* as a constant never-rotating buster and pin the edge copy permanently stale. A real key is always a
|
|
547
|
+
* positive int sent as number or string, so everything else lands on `.catch()` and degrades that read
|
|
548
|
+
* to un-busted (origin-served — slower, never stale), never a parse fail.
|
|
549
|
+
*/
|
|
550
|
+
cacheKey: z6.union([z6.number(), z6.string()]).transform((value) => Number(value)).pipe(z6.number().int().positive()).optional().catch(void 0),
|
|
537
551
|
/**
|
|
538
552
|
* The shop's base-currency ISO code (e.g. `'USD'`). CAB derives the shop minor-unit exponent from it
|
|
539
553
|
* (`currencyDecimals`) so a Functions-applied fixed discount — whose amount is in shop minor units —
|
|
@@ -2086,7 +2100,7 @@ var normalizeTokens = (text2, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
2086
2100
|
|
|
2087
2101
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
2088
2102
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
2089
|
-
var convertContentBlockTextToV2 = (language, text2 = literalText) => {
|
|
2103
|
+
var convertContentBlockTextToV2 = (language, text2 = literalText, textAlign = "center") => {
|
|
2090
2104
|
const description = language?.description;
|
|
2091
2105
|
const superTitle = language?.superTitle;
|
|
2092
2106
|
const title = language?.title;
|
|
@@ -2098,20 +2112,20 @@ var convertContentBlockTextToV2 = (language, text2 = literalText) => {
|
|
|
2098
2112
|
fontSize: "extraLarge",
|
|
2099
2113
|
marks: [{ type: "bold" }],
|
|
2100
2114
|
text: text2("superTitle", superTitle),
|
|
2101
|
-
textAlign
|
|
2115
|
+
textAlign
|
|
2102
2116
|
}) : null,
|
|
2103
2117
|
title ? convertTextToTiptapParagraph({
|
|
2104
2118
|
color: "base",
|
|
2105
2119
|
fontSize: "medium",
|
|
2106
2120
|
marks: [{ type: "bold" }],
|
|
2107
2121
|
text: text2("title", title),
|
|
2108
|
-
textAlign
|
|
2122
|
+
textAlign
|
|
2109
2123
|
}) : null,
|
|
2110
2124
|
description ? convertTextToTiptapParagraph({
|
|
2111
2125
|
color: "base",
|
|
2112
2126
|
fontSize: "base",
|
|
2113
2127
|
text: text2("description", description),
|
|
2114
|
-
textAlign
|
|
2128
|
+
textAlign
|
|
2115
2129
|
}) : null
|
|
2116
2130
|
].filter((p) => p !== null),
|
|
2117
2131
|
type: "doc"
|
|
@@ -3225,6 +3239,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
3225
3239
|
const defaultValue = header[field];
|
|
3226
3240
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
3227
3241
|
});
|
|
3242
|
+
var headerNode = (header, textAlign) => ({
|
|
3243
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
3244
|
+
sectionType: "text"
|
|
3245
|
+
});
|
|
3246
|
+
var buildHeaderNavRow = (header) => ({
|
|
3247
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
3248
|
+
direction: "grid",
|
|
3249
|
+
grid: { columns: ["fill", "auto"] },
|
|
3250
|
+
name: "header",
|
|
3251
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
3252
|
+
sectionType: "layout",
|
|
3253
|
+
spacing: "base"
|
|
3254
|
+
});
|
|
3228
3255
|
var HAS_PRODUCTS = "hasProducts";
|
|
3229
3256
|
var hasProductsMutators = () => [
|
|
3230
3257
|
{
|
|
@@ -3317,6 +3344,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3317
3344
|
const pageSize = Math.max(columns, 1);
|
|
3318
3345
|
const limit = settings.limit ?? void 0;
|
|
3319
3346
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
3347
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
3320
3348
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
3321
3349
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
3322
3350
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -3374,7 +3402,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3374
3402
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
3375
3403
|
...listLayout,
|
|
3376
3404
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
3377
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
3405
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
3378
3406
|
sectionType: "carousel"
|
|
3379
3407
|
};
|
|
3380
3408
|
return CABRootSection.parse({
|
|
@@ -3384,10 +3412,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3384
3412
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
3385
3413
|
...name && { name },
|
|
3386
3414
|
previewMode: settings.previewMode,
|
|
3387
|
-
sections: [
|
|
3388
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
3389
|
-
display
|
|
3390
|
-
],
|
|
3415
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
3391
3416
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
3392
3417
|
tracking: settings.tracking,
|
|
3393
3418
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
|
@@ -3960,10 +3985,10 @@ var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddr
|
|
|
3960
3985
|
...userAgent && { user_agent: userAgent }
|
|
3961
3986
|
};
|
|
3962
3987
|
};
|
|
3963
|
-
var fetchAdsEngineOffers = async (input, ctx) => {
|
|
3988
|
+
var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
|
|
3964
3989
|
if (!input.country || !SUPPORTED_COUNTRIES.has(input.country.toUpperCase())) return [];
|
|
3965
3990
|
if (!(input.sessionId || input.visitorId)) return [];
|
|
3966
|
-
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
3991
|
+
const { shop } = prefetched ?? await fetchUserConfig({ shop: input.shop }, ctx);
|
|
3967
3992
|
const publisherKey = shop.monetize?.publisherKey;
|
|
3968
3993
|
if (!publisherKey) return [];
|
|
3969
3994
|
const isProduction = ctx.host === REBUY_ENGINE_HOST;
|
|
@@ -4121,8 +4146,10 @@ var composeOfferCard = (offer, input) => {
|
|
|
4121
4146
|
};
|
|
4122
4147
|
|
|
4123
4148
|
// src/server/monetizeOffers.ts
|
|
4124
|
-
var fetchMonetizeOffers = async (input, ctx) => {
|
|
4125
|
-
const composed = (await fetchAdsEngineOffers(input, ctx)).map(
|
|
4149
|
+
var fetchMonetizeOffers = async (input, ctx, prefetched) => {
|
|
4150
|
+
const composed = (await fetchAdsEngineOffers(input, ctx, prefetched)).map(
|
|
4151
|
+
(offer) => composeOfferCard(offer, input)
|
|
4152
|
+
);
|
|
4126
4153
|
const offers = composed.filter((card) => card !== void 0);
|
|
4127
4154
|
if (offers.length < composed.length)
|
|
4128
4155
|
ctx.reportDrift?.({
|
|
@@ -4177,8 +4204,8 @@ var dispatchWidgetTransform = (id, normalized, report) => {
|
|
|
4177
4204
|
if (isOfferV1(normalized)) return convertOfferToV2({ id, name: normalized.name, settings: normalized });
|
|
4178
4205
|
return normalized;
|
|
4179
4206
|
};
|
|
4180
|
-
var fetchProgressBarContext = async (shop, ctx) => {
|
|
4181
|
-
const config = await fetchUserConfig({ shop }, ctx).catch(() => void 0);
|
|
4207
|
+
var fetchProgressBarContext = async (shop, ctx, prefetched) => {
|
|
4208
|
+
const config = prefetched === void 0 ? await fetchUserConfig({ shop }, ctx).catch(() => void 0) : prefetched;
|
|
4182
4209
|
if (!config) return {};
|
|
4183
4210
|
if (config.shop.billingVersion === "v6" && config.shop.activePackages.some((pkg) => pkg.shortName === "")) {
|
|
4184
4211
|
ctx.reportDrift?.({
|
|
@@ -4217,7 +4244,7 @@ var findMonetizeMarker = (section) => {
|
|
|
4217
4244
|
}
|
|
4218
4245
|
return void 0;
|
|
4219
4246
|
};
|
|
4220
|
-
var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
4247
|
+
var hydrateMonetizeMarkers = async (converted, input, ctx, config) => {
|
|
4221
4248
|
if (!input.monetize || !isCABTree(converted)) return;
|
|
4222
4249
|
const first = findMonetizeMarker(converted);
|
|
4223
4250
|
if (!first) return;
|
|
@@ -4229,7 +4256,8 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
|
4229
4256
|
shop: input.shop,
|
|
4230
4257
|
visitorId: input.monetize.visitorId || input.visitorId
|
|
4231
4258
|
},
|
|
4232
|
-
ctx
|
|
4259
|
+
ctx,
|
|
4260
|
+
config ?? void 0
|
|
4233
4261
|
);
|
|
4234
4262
|
first.offers = offers;
|
|
4235
4263
|
} catch (err) {
|
|
@@ -4239,7 +4267,7 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
|
|
|
4239
4267
|
});
|
|
4240
4268
|
}
|
|
4241
4269
|
};
|
|
4242
|
-
var fetchAndConvert = async (id, input, ctx) => {
|
|
4270
|
+
var fetchAndConvert = async (id, input, ctx, config) => {
|
|
4243
4271
|
const params = { id: String(id), shop: input.shop };
|
|
4244
4272
|
if (input.cacheKey != null) params.cache_key = String(input.cacheKey);
|
|
4245
4273
|
if (input.key) params.key = input.key;
|
|
@@ -4256,34 +4284,32 @@ var fetchAndConvert = async (id, input, ctx) => {
|
|
|
4256
4284
|
if (isMissingUpstreamPayload(data)) throw new NotFoundError(`Widget not found: ${id}`);
|
|
4257
4285
|
const normalized = convertNumericObjects(deepCamelCase(data));
|
|
4258
4286
|
if (isProgressBarV1(normalized) && normalized.barSettings.barType === "smart-cart") {
|
|
4259
|
-
const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx);
|
|
4287
|
+
const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4260
4288
|
return convertSmartCartProgressBarToV2({ bars: bars ?? [], id, progressColor, settings: normalized });
|
|
4261
4289
|
}
|
|
4262
4290
|
if (isConvertibleProgressBarV1(normalized)) {
|
|
4263
|
-
const { progressColor } = await fetchProgressBarContext(input.shop, ctx);
|
|
4291
|
+
const { progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4264
4292
|
return convertProgressBarToV2({ id, progressColor, settings: normalized });
|
|
4265
4293
|
}
|
|
4266
4294
|
const converted = dispatchWidgetTransform(id, normalized, ctx.reportDrift);
|
|
4267
4295
|
const colorless = isCABRootSection(normalized) ? colorlessProgressBars(converted) : [];
|
|
4268
4296
|
if (colorless.length > 0) {
|
|
4269
|
-
const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx);
|
|
4297
|
+
const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx, config);
|
|
4270
4298
|
if (color) for (const bar of colorless) bar.progressColor = color;
|
|
4271
4299
|
}
|
|
4272
4300
|
return converted;
|
|
4273
4301
|
};
|
|
4274
|
-
var resolveWidgetExperiment =
|
|
4275
|
-
if (!input.visitorId) return void 0;
|
|
4276
|
-
try {
|
|
4277
|
-
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
4278
|
-
return resolveExperiment(shop.activeExperiments, input.id, input.visitorId);
|
|
4279
|
-
} catch {
|
|
4280
|
-
return void 0;
|
|
4281
|
-
}
|
|
4282
|
-
};
|
|
4302
|
+
var resolveWidgetExperiment = (input, config) => input.visitorId && config ? resolveExperiment(config.shop.activeExperiments, input.id, input.visitorId) : void 0;
|
|
4283
4303
|
var convertWidgetSettings = async (input, ctx) => {
|
|
4284
|
-
const
|
|
4285
|
-
const
|
|
4286
|
-
|
|
4304
|
+
const config = input.cacheKey != null && !input.visitorId ? void 0 : await fetchUserConfig({ shop: input.shop }, ctx).catch(() => null);
|
|
4305
|
+
const experiment = resolveWidgetExperiment(input, config);
|
|
4306
|
+
const result = await fetchAndConvert(
|
|
4307
|
+
experiment?.elementId ?? input.id,
|
|
4308
|
+
{ ...input, cacheKey: input.cacheKey ?? config?.shop.cacheKey },
|
|
4309
|
+
ctx,
|
|
4310
|
+
config
|
|
4311
|
+
);
|
|
4312
|
+
await hydrateMonetizeMarkers(result, input, ctx, config);
|
|
4287
4313
|
return experiment && isCABTree(result) ? { ...result, experiment } : result;
|
|
4288
4314
|
};
|
|
4289
4315
|
|
|
@@ -4627,7 +4653,14 @@ var MonetizeInputSchema = z29.object({
|
|
|
4627
4653
|
zipCode: z29.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4628
4654
|
});
|
|
4629
4655
|
var WidgetSettingsInputSchema = z29.object({
|
|
4630
|
-
|
|
4656
|
+
/**
|
|
4657
|
+
* Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
|
|
4658
|
+
* `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
|
|
4659
|
+
* an unconstrained `0`/negative/float here would go upstream as a constant never-rotating `cache_key`
|
|
4660
|
+
* and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
|
|
4661
|
+
* the read to un-busted, never 400 a widget render.
|
|
4662
|
+
*/
|
|
4663
|
+
cacheKey: z29.number().int().positive().optional().catch(void 0),
|
|
4631
4664
|
id: z29.number(),
|
|
4632
4665
|
key: z29.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4633
4666
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CabUserConfig } from '../schema/cabShopConfig';
|
|
1
2
|
import { type CABSection } from '../schema/widgets/checkout-and-beyond';
|
|
2
3
|
import { type MonetizeInput, type ServerContext } from '../server/shared';
|
|
3
4
|
export type MonetizeOffers = {
|
|
@@ -8,6 +9,7 @@ export type MonetizeOffers = {
|
|
|
8
9
|
* offer card the extension renders directly. The header chrome (experiment copy + order-confirmed
|
|
9
10
|
* line) is baked inside every card — inside its border, as React draws it — so the response no longer
|
|
10
11
|
* carries a separate `header` (older extension builds read it as optional and render nothing). Empty
|
|
11
|
-
* when the shop isn't ad-enrolled.
|
|
12
|
+
* when the shop isn't ad-enrolled. `prefetched` is a caller's already-resolved shop config, passed
|
|
13
|
+
* through to the ads layer's publisher-key lookup so inline hydration doesn't refetch it.
|
|
12
14
|
*/
|
|
13
|
-
export declare const fetchMonetizeOffers: (input: MonetizeInput, ctx: ServerContext) => Promise<MonetizeOffers>;
|
|
15
|
+
export declare const fetchMonetizeOffers: (input: MonetizeInput, ctx: ServerContext, prefetched?: CabUserConfig) => Promise<MonetizeOffers>;
|
|
@@ -233,7 +233,7 @@ export declare const MonetizeInputSchema: z.ZodObject<{
|
|
|
233
233
|
}, z.core.$strip>;
|
|
234
234
|
/** Input for `convertWidgetSettings`. */
|
|
235
235
|
export declare const WidgetSettingsInputSchema: z.ZodObject<{
|
|
236
|
-
cacheKey: z.ZodOptional<z.ZodNumber
|
|
236
|
+
cacheKey: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
237
237
|
id: z.ZodNumber;
|
|
238
238
|
key: z.ZodOptional<z.ZodString>;
|
|
239
239
|
monetize: z.ZodOptional<z.ZodObject<{
|
|
@@ -7,5 +7,7 @@ export type HeaderTextResolver = (field: HeaderField, value: string) => string;
|
|
|
7
7
|
* A paragraph per truthy field, styled as the legacy header stack. `text` swaps what each paragraph
|
|
8
8
|
* carries — the offers converter hands over `{{token}}` references so data sources can override the
|
|
9
9
|
* copy — while the truthiness gate (which fields exist at all) stays the merchant's authored copy.
|
|
10
|
+
* `textAlign` follows placement: legacy centers the header above a list but left-aligns it inside a
|
|
11
|
+
* carousel's chrome row (`HeaderBlock`'s `inlineAlignment = isCarousel ? 'start' : 'center'`).
|
|
10
12
|
*/
|
|
11
|
-
export declare const convertContentBlockTextToV2: (language: V1Language | undefined, text?: HeaderTextResolver) => TiptapDocument;
|
|
13
|
+
export declare const convertContentBlockTextToV2: (language: V1Language | undefined, text?: HeaderTextResolver, textAlign?: "center" | "start") => TiptapDocument;
|
|
@@ -1571,7 +1571,7 @@ var normalizeTokens = (text, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
1571
1571
|
|
|
1572
1572
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
1573
1573
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
1574
|
-
var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
1574
|
+
var convertContentBlockTextToV2 = (language, text = literalText, textAlign = "center") => {
|
|
1575
1575
|
const description = language?.description;
|
|
1576
1576
|
const superTitle = language?.superTitle;
|
|
1577
1577
|
const title = language?.title;
|
|
@@ -1583,20 +1583,20 @@ var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
|
1583
1583
|
fontSize: "extraLarge",
|
|
1584
1584
|
marks: [{ type: "bold" }],
|
|
1585
1585
|
text: text("superTitle", superTitle),
|
|
1586
|
-
textAlign
|
|
1586
|
+
textAlign
|
|
1587
1587
|
}) : null,
|
|
1588
1588
|
title ? convertTextToTiptapParagraph({
|
|
1589
1589
|
color: "base",
|
|
1590
1590
|
fontSize: "medium",
|
|
1591
1591
|
marks: [{ type: "bold" }],
|
|
1592
1592
|
text: text("title", title),
|
|
1593
|
-
textAlign
|
|
1593
|
+
textAlign
|
|
1594
1594
|
}) : null,
|
|
1595
1595
|
description ? convertTextToTiptapParagraph({
|
|
1596
1596
|
color: "base",
|
|
1597
1597
|
fontSize: "base",
|
|
1598
1598
|
text: text("description", description),
|
|
1599
|
-
textAlign
|
|
1599
|
+
textAlign
|
|
1600
1600
|
}) : null
|
|
1601
1601
|
].filter((p) => p !== null),
|
|
1602
1602
|
type: "doc"
|
|
@@ -3141,6 +3141,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
3141
3141
|
const defaultValue = header[field];
|
|
3142
3142
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
3143
3143
|
});
|
|
3144
|
+
var headerNode = (header, textAlign) => ({
|
|
3145
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
3146
|
+
sectionType: "text"
|
|
3147
|
+
});
|
|
3148
|
+
var buildHeaderNavRow = (header) => ({
|
|
3149
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
3150
|
+
direction: "grid",
|
|
3151
|
+
grid: { columns: ["fill", "auto"] },
|
|
3152
|
+
name: "header",
|
|
3153
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
3154
|
+
sectionType: "layout",
|
|
3155
|
+
spacing: "base"
|
|
3156
|
+
});
|
|
3144
3157
|
var HAS_PRODUCTS = "hasProducts";
|
|
3145
3158
|
var hasProductsMutators = () => [
|
|
3146
3159
|
{
|
|
@@ -3233,6 +3246,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3233
3246
|
const pageSize = Math.max(columns, 1);
|
|
3234
3247
|
const limit = settings.limit ?? void 0;
|
|
3235
3248
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
3249
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
3236
3250
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
3237
3251
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
3238
3252
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -3290,7 +3304,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3290
3304
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
3291
3305
|
...listLayout,
|
|
3292
3306
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
3293
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
3307
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
3294
3308
|
sectionType: "carousel"
|
|
3295
3309
|
};
|
|
3296
3310
|
return CABRootSection.parse({
|
|
@@ -3300,10 +3314,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3300
3314
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
3301
3315
|
...name && { name },
|
|
3302
3316
|
previewMode: settings.previewMode,
|
|
3303
|
-
sections: [
|
|
3304
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
3305
|
-
display
|
|
3306
|
-
],
|
|
3317
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
3307
3318
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
3308
3319
|
tracking: settings.tracking,
|
|
3309
3320
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
|
@@ -1514,7 +1514,7 @@ var normalizeTokens = (text, knownTokens = DEFAULT_KNOWN_TOKENS) => {
|
|
|
1514
1514
|
|
|
1515
1515
|
// src/transforms/contentBlockV1/convertContentBlockTextToV2.ts
|
|
1516
1516
|
var literalText = (_field, value) => normalizeTokens(value);
|
|
1517
|
-
var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
1517
|
+
var convertContentBlockTextToV2 = (language, text = literalText, textAlign = "center") => {
|
|
1518
1518
|
const description = language?.description;
|
|
1519
1519
|
const superTitle = language?.superTitle;
|
|
1520
1520
|
const title = language?.title;
|
|
@@ -1526,20 +1526,20 @@ var convertContentBlockTextToV2 = (language, text = literalText) => {
|
|
|
1526
1526
|
fontSize: "extraLarge",
|
|
1527
1527
|
marks: [{ type: "bold" }],
|
|
1528
1528
|
text: text("superTitle", superTitle),
|
|
1529
|
-
textAlign
|
|
1529
|
+
textAlign
|
|
1530
1530
|
}) : null,
|
|
1531
1531
|
title ? convertTextToTiptapParagraph({
|
|
1532
1532
|
color: "base",
|
|
1533
1533
|
fontSize: "medium",
|
|
1534
1534
|
marks: [{ type: "bold" }],
|
|
1535
1535
|
text: text("title", title),
|
|
1536
|
-
textAlign
|
|
1536
|
+
textAlign
|
|
1537
1537
|
}) : null,
|
|
1538
1538
|
description ? convertTextToTiptapParagraph({
|
|
1539
1539
|
color: "base",
|
|
1540
1540
|
fontSize: "base",
|
|
1541
1541
|
text: text("description", description),
|
|
1542
|
-
textAlign
|
|
1542
|
+
textAlign
|
|
1543
1543
|
}) : null
|
|
1544
1544
|
].filter((p) => p !== null),
|
|
1545
1545
|
type: "doc"
|
|
@@ -3084,6 +3084,19 @@ var headerVariables = (header) => HEADER_FIELDS.flatMap((field) => {
|
|
|
3084
3084
|
const defaultValue = header[field];
|
|
3085
3085
|
return defaultValue === void 0 ? [] : [{ defaultValue, name: field, type: VariableType.text }];
|
|
3086
3086
|
});
|
|
3087
|
+
var headerNode = (header, textAlign) => ({
|
|
3088
|
+
content: { en: convertContentBlockTextToV2(header, headerText, textAlign) },
|
|
3089
|
+
sectionType: "text"
|
|
3090
|
+
});
|
|
3091
|
+
var buildHeaderNavRow = (header) => ({
|
|
3092
|
+
alignment: { horizontal: "start", vertical: "bottom" },
|
|
3093
|
+
direction: "grid",
|
|
3094
|
+
grid: { columns: ["fill", "auto"] },
|
|
3095
|
+
name: "header",
|
|
3096
|
+
sections: [headerNode(header, "start"), buildCarouselNav()],
|
|
3097
|
+
sectionType: "layout",
|
|
3098
|
+
spacing: "base"
|
|
3099
|
+
});
|
|
3087
3100
|
var HAS_PRODUCTS = "hasProducts";
|
|
3088
3101
|
var hasProductsMutators = () => [
|
|
3089
3102
|
{
|
|
@@ -3176,6 +3189,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3176
3189
|
const pageSize = Math.max(columns, 1);
|
|
3177
3190
|
const limit = settings.limit ?? void 0;
|
|
3178
3191
|
const showsNav = !!layout?.carousel && !paginationOff(layout.showPaginations) && (limit === void 0 || limit > pageSize);
|
|
3192
|
+
const headerInChrome = showsNav && hasHeaderText(header);
|
|
3179
3193
|
const fitsOneRow = limit !== void 0 && limit <= pageSize;
|
|
3180
3194
|
const sideBySide = direction === "grid" || direction === "columns";
|
|
3181
3195
|
const offersDirection = sideBySide && (showsNav || fitsOneRow) ? "horizontal" : "vertical";
|
|
@@ -3233,7 +3247,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3233
3247
|
itemsAtOnce: Math.max(showsNav ? columns : limit ?? ENGINE_LIMIT_CEILING, 1),
|
|
3234
3248
|
...listLayout,
|
|
3235
3249
|
padding: gridChrome ? paddingFor(containerBorder.style) : "none",
|
|
3236
|
-
sections: showsNav ? [buildCarouselNav(), offers] : [offers],
|
|
3250
|
+
sections: showsNav ? [headerInChrome ? buildHeaderNavRow(header) : buildCarouselNav(), offers] : [offers],
|
|
3237
3251
|
sectionType: "carousel"
|
|
3238
3252
|
};
|
|
3239
3253
|
return CABRootSection.parse({
|
|
@@ -3243,10 +3257,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
|
|
|
3243
3257
|
// The widget name is required by the admin save schema; carry the legacy widget's name through.
|
|
3244
3258
|
...name && { name },
|
|
3245
3259
|
previewMode: settings.previewMode,
|
|
3246
|
-
sections: [
|
|
3247
|
-
...hasHeaderText(header) ? [{ content: { en: convertContentBlockTextToV2(header, headerText) }, sectionType: "text" }] : [],
|
|
3248
|
-
display
|
|
3249
|
-
],
|
|
3260
|
+
sections: [...hasHeaderText(header) && !headerInChrome ? [headerNode(header, "center")] : [], display],
|
|
3250
3261
|
// CABTracking's schema fallbacks fill missing or null fields and the missing object; malformed values throw.
|
|
3251
3262
|
tracking: settings.tracking,
|
|
3252
3263
|
type: SHOPIFY_CHECKOUT_EXTENSION,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebuy/rebuy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rebuy, Inc.",
|