@rebuy/rebuy 3.12.1 → 3.13.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 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 —
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 —
@@ -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[]>;
@@ -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 —
@@ -4008,10 +4022,10 @@ var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddr
4008
4022
  ...userAgent && { user_agent: userAgent }
4009
4023
  };
4010
4024
  };
4011
- var fetchAdsEngineOffers = async (input, ctx) => {
4025
+ var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
4012
4026
  if (!input.country || !SUPPORTED_COUNTRIES.has(input.country.toUpperCase())) return [];
4013
4027
  if (!(input.sessionId || input.visitorId)) return [];
4014
- const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
4028
+ const { shop } = prefetched ?? await fetchUserConfig({ shop: input.shop }, ctx);
4015
4029
  const publisherKey = shop.monetize?.publisherKey;
4016
4030
  if (!publisherKey) return [];
4017
4031
  const isProduction = ctx.host === REBUY_ENGINE_HOST;
@@ -4169,8 +4183,10 @@ var composeOfferCard = (offer, input) => {
4169
4183
  };
4170
4184
 
4171
4185
  // src/server/monetizeOffers.ts
4172
- var fetchMonetizeOffers = async (input, ctx) => {
4173
- const composed = (await fetchAdsEngineOffers(input, ctx)).map((offer) => composeOfferCard(offer, input));
4186
+ var fetchMonetizeOffers = async (input, ctx, prefetched) => {
4187
+ const composed = (await fetchAdsEngineOffers(input, ctx, prefetched)).map(
4188
+ (offer) => composeOfferCard(offer, input)
4189
+ );
4174
4190
  const offers = composed.filter((card) => card !== void 0);
4175
4191
  if (offers.length < composed.length)
4176
4192
  ctx.reportDrift?.({
@@ -4225,8 +4241,8 @@ var dispatchWidgetTransform = (id, normalized, report) => {
4225
4241
  if (isOfferV1(normalized)) return convertOfferToV2({ id, name: normalized.name, settings: normalized });
4226
4242
  return normalized;
4227
4243
  };
4228
- var fetchProgressBarContext = async (shop, ctx) => {
4229
- const config = await fetchUserConfig({ shop }, ctx).catch(() => void 0);
4244
+ var fetchProgressBarContext = async (shop, ctx, prefetched) => {
4245
+ const config = prefetched === void 0 ? await fetchUserConfig({ shop }, ctx).catch(() => void 0) : prefetched;
4230
4246
  if (!config) return {};
4231
4247
  if (config.shop.billingVersion === "v6" && config.shop.activePackages.some((pkg) => pkg.shortName === "")) {
4232
4248
  ctx.reportDrift?.({
@@ -4265,7 +4281,7 @@ var findMonetizeMarker = (section) => {
4265
4281
  }
4266
4282
  return void 0;
4267
4283
  };
4268
- var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4284
+ var hydrateMonetizeMarkers = async (converted, input, ctx, config) => {
4269
4285
  if (!input.monetize || !isCABTree(converted)) return;
4270
4286
  const first = findMonetizeMarker(converted);
4271
4287
  if (!first) return;
@@ -4277,7 +4293,8 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4277
4293
  shop: input.shop,
4278
4294
  visitorId: input.monetize.visitorId || input.visitorId
4279
4295
  },
4280
- ctx
4296
+ ctx,
4297
+ config ?? void 0
4281
4298
  );
4282
4299
  first.offers = offers;
4283
4300
  } catch (err) {
@@ -4287,7 +4304,7 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4287
4304
  });
4288
4305
  }
4289
4306
  };
4290
- var fetchAndConvert = async (id, input, ctx) => {
4307
+ var fetchAndConvert = async (id, input, ctx, config) => {
4291
4308
  const params = { id: String(id), shop: input.shop };
4292
4309
  if (input.cacheKey != null) params.cache_key = String(input.cacheKey);
4293
4310
  if (input.key) params.key = input.key;
@@ -4304,34 +4321,32 @@ var fetchAndConvert = async (id, input, ctx) => {
4304
4321
  if (isMissingUpstreamPayload(data)) throw new NotFoundError(`Widget not found: ${id}`);
4305
4322
  const normalized = convertNumericObjects(deepCamelCase(data));
4306
4323
  if (isProgressBarV1(normalized) && normalized.barSettings.barType === "smart-cart") {
4307
- const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx);
4324
+ const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
4308
4325
  return convertSmartCartProgressBarToV2({ bars: bars ?? [], id, progressColor, settings: normalized });
4309
4326
  }
4310
4327
  if (isConvertibleProgressBarV1(normalized)) {
4311
- const { progressColor } = await fetchProgressBarContext(input.shop, ctx);
4328
+ const { progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
4312
4329
  return convertProgressBarToV2({ id, progressColor, settings: normalized });
4313
4330
  }
4314
4331
  const converted = dispatchWidgetTransform(id, normalized, ctx.reportDrift);
4315
4332
  const colorless = isCABRootSection(normalized) ? colorlessProgressBars(converted) : [];
4316
4333
  if (colorless.length > 0) {
4317
- const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx);
4334
+ const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx, config);
4318
4335
  if (color) for (const bar of colorless) bar.progressColor = color;
4319
4336
  }
4320
4337
  return converted;
4321
4338
  };
4322
- var resolveWidgetExperiment = async (input, ctx) => {
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
- };
4339
+ var resolveWidgetExperiment = (input, config) => input.visitorId && config ? resolveExperiment(config.shop.activeExperiments, input.id, input.visitorId) : void 0;
4331
4340
  var convertWidgetSettings = async (input, ctx) => {
4332
- const experiment = await resolveWidgetExperiment(input, ctx);
4333
- const result = await fetchAndConvert(experiment?.elementId ?? input.id, input, ctx);
4334
- await hydrateMonetizeMarkers(result, input, ctx);
4341
+ const config = input.cacheKey != null && !input.visitorId ? void 0 : await fetchUserConfig({ shop: input.shop }, ctx).catch(() => null);
4342
+ const experiment = resolveWidgetExperiment(input, config);
4343
+ const result = await fetchAndConvert(
4344
+ experiment?.elementId ?? input.id,
4345
+ { ...input, cacheKey: input.cacheKey ?? config?.shop.cacheKey },
4346
+ ctx,
4347
+ config
4348
+ );
4349
+ await hydrateMonetizeMarkers(result, input, ctx, config);
4335
4350
  return experiment && isCABTree(result) ? { ...result, experiment } : result;
4336
4351
  };
4337
4352
 
@@ -4675,7 +4690,14 @@ var MonetizeInputSchema = import_zod29.z.object({
4675
4690
  zipCode: import_zod29.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
4676
4691
  });
4677
4692
  var WidgetSettingsInputSchema = import_zod29.z.object({
4678
- cacheKey: import_zod29.z.number().optional(),
4693
+ /**
4694
+ * Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
4695
+ * `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
4696
+ * an unconstrained `0`/negative/float here would go upstream as a constant never-rotating `cache_key`
4697
+ * and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
4698
+ * the read to un-busted, never 400 a widget render.
4699
+ */
4700
+ cacheKey: import_zod29.z.number().int().positive().optional().catch(void 0),
4679
4701
  id: import_zod29.z.number(),
4680
4702
  key: import_zod29.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4681
4703
  /**
@@ -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 —
@@ -3960,10 +3974,10 @@ var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddr
3960
3974
  ...userAgent && { user_agent: userAgent }
3961
3975
  };
3962
3976
  };
3963
- var fetchAdsEngineOffers = async (input, ctx) => {
3977
+ var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
3964
3978
  if (!input.country || !SUPPORTED_COUNTRIES.has(input.country.toUpperCase())) return [];
3965
3979
  if (!(input.sessionId || input.visitorId)) return [];
3966
- const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
3980
+ const { shop } = prefetched ?? await fetchUserConfig({ shop: input.shop }, ctx);
3967
3981
  const publisherKey = shop.monetize?.publisherKey;
3968
3982
  if (!publisherKey) return [];
3969
3983
  const isProduction = ctx.host === REBUY_ENGINE_HOST;
@@ -4121,8 +4135,10 @@ var composeOfferCard = (offer, input) => {
4121
4135
  };
4122
4136
 
4123
4137
  // src/server/monetizeOffers.ts
4124
- var fetchMonetizeOffers = async (input, ctx) => {
4125
- const composed = (await fetchAdsEngineOffers(input, ctx)).map((offer) => composeOfferCard(offer, input));
4138
+ var fetchMonetizeOffers = async (input, ctx, prefetched) => {
4139
+ const composed = (await fetchAdsEngineOffers(input, ctx, prefetched)).map(
4140
+ (offer) => composeOfferCard(offer, input)
4141
+ );
4126
4142
  const offers = composed.filter((card) => card !== void 0);
4127
4143
  if (offers.length < composed.length)
4128
4144
  ctx.reportDrift?.({
@@ -4177,8 +4193,8 @@ var dispatchWidgetTransform = (id, normalized, report) => {
4177
4193
  if (isOfferV1(normalized)) return convertOfferToV2({ id, name: normalized.name, settings: normalized });
4178
4194
  return normalized;
4179
4195
  };
4180
- var fetchProgressBarContext = async (shop, ctx) => {
4181
- const config = await fetchUserConfig({ shop }, ctx).catch(() => void 0);
4196
+ var fetchProgressBarContext = async (shop, ctx, prefetched) => {
4197
+ const config = prefetched === void 0 ? await fetchUserConfig({ shop }, ctx).catch(() => void 0) : prefetched;
4182
4198
  if (!config) return {};
4183
4199
  if (config.shop.billingVersion === "v6" && config.shop.activePackages.some((pkg) => pkg.shortName === "")) {
4184
4200
  ctx.reportDrift?.({
@@ -4217,7 +4233,7 @@ var findMonetizeMarker = (section) => {
4217
4233
  }
4218
4234
  return void 0;
4219
4235
  };
4220
- var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4236
+ var hydrateMonetizeMarkers = async (converted, input, ctx, config) => {
4221
4237
  if (!input.monetize || !isCABTree(converted)) return;
4222
4238
  const first = findMonetizeMarker(converted);
4223
4239
  if (!first) return;
@@ -4229,7 +4245,8 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4229
4245
  shop: input.shop,
4230
4246
  visitorId: input.monetize.visitorId || input.visitorId
4231
4247
  },
4232
- ctx
4248
+ ctx,
4249
+ config ?? void 0
4233
4250
  );
4234
4251
  first.offers = offers;
4235
4252
  } catch (err) {
@@ -4239,7 +4256,7 @@ var hydrateMonetizeMarkers = async (converted, input, ctx) => {
4239
4256
  });
4240
4257
  }
4241
4258
  };
4242
- var fetchAndConvert = async (id, input, ctx) => {
4259
+ var fetchAndConvert = async (id, input, ctx, config) => {
4243
4260
  const params = { id: String(id), shop: input.shop };
4244
4261
  if (input.cacheKey != null) params.cache_key = String(input.cacheKey);
4245
4262
  if (input.key) params.key = input.key;
@@ -4256,34 +4273,32 @@ var fetchAndConvert = async (id, input, ctx) => {
4256
4273
  if (isMissingUpstreamPayload(data)) throw new NotFoundError(`Widget not found: ${id}`);
4257
4274
  const normalized = convertNumericObjects(deepCamelCase(data));
4258
4275
  if (isProgressBarV1(normalized) && normalized.barSettings.barType === "smart-cart") {
4259
- const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx);
4276
+ const { bars, progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
4260
4277
  return convertSmartCartProgressBarToV2({ bars: bars ?? [], id, progressColor, settings: normalized });
4261
4278
  }
4262
4279
  if (isConvertibleProgressBarV1(normalized)) {
4263
- const { progressColor } = await fetchProgressBarContext(input.shop, ctx);
4280
+ const { progressColor } = await fetchProgressBarContext(input.shop, ctx, config);
4264
4281
  return convertProgressBarToV2({ id, progressColor, settings: normalized });
4265
4282
  }
4266
4283
  const converted = dispatchWidgetTransform(id, normalized, ctx.reportDrift);
4267
4284
  const colorless = isCABRootSection(normalized) ? colorlessProgressBars(converted) : [];
4268
4285
  if (colorless.length > 0) {
4269
- const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx);
4286
+ const { progressColor: color } = await fetchProgressBarContext(input.shop, ctx, config);
4270
4287
  if (color) for (const bar of colorless) bar.progressColor = color;
4271
4288
  }
4272
4289
  return converted;
4273
4290
  };
4274
- var resolveWidgetExperiment = async (input, ctx) => {
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
- };
4291
+ var resolveWidgetExperiment = (input, config) => input.visitorId && config ? resolveExperiment(config.shop.activeExperiments, input.id, input.visitorId) : void 0;
4283
4292
  var convertWidgetSettings = async (input, ctx) => {
4284
- const experiment = await resolveWidgetExperiment(input, ctx);
4285
- const result = await fetchAndConvert(experiment?.elementId ?? input.id, input, ctx);
4286
- await hydrateMonetizeMarkers(result, input, ctx);
4293
+ const config = input.cacheKey != null && !input.visitorId ? void 0 : await fetchUserConfig({ shop: input.shop }, ctx).catch(() => null);
4294
+ const experiment = resolveWidgetExperiment(input, config);
4295
+ const result = await fetchAndConvert(
4296
+ experiment?.elementId ?? input.id,
4297
+ { ...input, cacheKey: input.cacheKey ?? config?.shop.cacheKey },
4298
+ ctx,
4299
+ config
4300
+ );
4301
+ await hydrateMonetizeMarkers(result, input, ctx, config);
4287
4302
  return experiment && isCABTree(result) ? { ...result, experiment } : result;
4288
4303
  };
4289
4304
 
@@ -4627,7 +4642,14 @@ var MonetizeInputSchema = z29.object({
4627
4642
  zipCode: z29.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
4628
4643
  });
4629
4644
  var WidgetSettingsInputSchema = z29.object({
4630
- cacheKey: z29.number().optional(),
4645
+ /**
4646
+ * Same domain as `CabShopConfig.cacheKey`, for the same reason: this side WINS the orchestrator's
4647
+ * `input.cacheKey ?? config.shop.cacheKey` (and an explicit value skips the config read entirely), so
4648
+ * an unconstrained `0`/negative/float here would go upstream as a constant never-rotating `cache_key`
4649
+ * and pin the edge copy permanently stale. `.catch(undefined)` because a bogus buster should degrade
4650
+ * the read to un-busted, never 400 a widget render.
4651
+ */
4652
+ cacheKey: z29.number().int().positive().optional().catch(void 0),
4631
4653
  id: z29.number(),
4632
4654
  key: z29.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4633
4655
  /**
@@ -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<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.12.1",
3
+ "version": "3.13.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.",