@tokenoftrust/storefront-runner 2.4.12 → 2.4.14

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.
@@ -91,6 +91,15 @@ export const CATALOG_MODEL = {
91
91
  "locale?": "string",
92
92
  accepted_at: "string",
93
93
  facts: "CatalogFact[]",
94
+ "setup_policy?": "CatalogSetupProvenance",
95
+ },
96
+ },
97
+ CatalogSetupPriceAcceptance: { fields: { engine_version: "string", policy_revision: "number", value: "number", input: "Record<string, unknown>" } },
98
+ CatalogSetupSkuAcceptance: { fields: { engine_version: "string", policy_revision: "number", value: "string", input: "Record<string, unknown>" } },
99
+ CatalogSetupProvenance: {
100
+ fields: {
101
+ tenant_id: "string", currency: "Currency",
102
+ "pricing?": "CatalogSetupPriceAcceptance", "sku?": "CatalogSetupSkuAcceptance",
94
103
  },
95
104
  },
96
105
  CatalogOverrideField: { alias: '"title" | "description_html" | "images" | "vendor" | "product_type"' },
@@ -29,6 +29,23 @@ import { priceForPurchaseOption } from "@tot/public-runtime";
29
29
  import { formatPrice } from "@/lib/format";
30
30
  import { useCheckoutHandoffCorrelation } from "@/components/islands/useCheckoutHandoffCorrelation";
31
31
 
32
+ export type PublicSelectorVariant = Pick<CatalogVariant,
33
+ "id" | "sku" | "price" | "compare_at_price" | "option_values" | "inventory" | "image_id">;
34
+
35
+ /** An explicit allowlist also excludes future internal variant fields from Astro hydration. */
36
+ export function publicSelectorVariants(variants: readonly CatalogVariant[]): PublicSelectorVariant[] {
37
+ return variants.map((variant) => ({
38
+ id: variant.id,
39
+ ...(variant.sku !== undefined ? { sku: variant.sku } : {}),
40
+ price: variant.price,
41
+ ...(variant.compare_at_price !== undefined ? { compare_at_price: variant.compare_at_price } : {}),
42
+ option_values: { ...variant.option_values },
43
+ inventory: { available: variant.inventory.available,
44
+ ...(variant.inventory.quantity !== undefined ? { quantity: variant.inventory.quantity } : {}) },
45
+ ...(variant.image_id !== undefined ? { image_id: variant.image_id } : {}),
46
+ }));
47
+ }
48
+
32
49
  interface OptionDef {
33
50
  name: string;
34
51
  position: number;
@@ -36,7 +53,7 @@ interface OptionDef {
36
53
  }
37
54
 
38
55
  interface Props {
39
- variants: CatalogVariant[];
56
+ variants: PublicSelectorVariant[];
40
57
  options: OptionDef[];
41
58
  currency: string;
42
59
  locale?: string;
@@ -77,14 +94,14 @@ const LOW_STOCK_THRESHOLD = 5;
77
94
 
78
95
  type Selection = Record<string, string>;
79
96
 
80
- function variantMatches(v: CatalogVariant, sel: Selection): boolean {
97
+ function variantMatches(v: PublicSelectorVariant, sel: Selection): boolean {
81
98
  return Object.entries(sel).every(([name, value]) => v.option_values[name] === value);
82
99
  }
83
100
 
84
101
  function findVariant(
85
- variants: CatalogVariant[],
102
+ variants: PublicSelectorVariant[],
86
103
  sel: Selection,
87
- ): CatalogVariant | undefined {
104
+ ): PublicSelectorVariant | undefined {
88
105
  return variants.find((v) => variantMatches(v, sel));
89
106
  }
90
107
 
@@ -14,7 +14,7 @@ interface UpdatesResponse {
14
14
  ok: boolean;
15
15
  message?: string;
16
16
  updates: Array<{ title: string; acceptedRevisionId: string; latestRevisionId: string }>;
17
- unchecked?: string[];
17
+ unavailable?: Array<{ title: string; message: string }>;
18
18
  request?: Record<string, unknown>;
19
19
  plan: { products: UpdatePlanProduct[] } | null;
20
20
  planFailure?: { message?: string; pinFailures?: Array<{ message: string }> };
@@ -81,8 +81,8 @@ export function initProductsTab(root: HTMLElement): void {
81
81
  return;
82
82
  }
83
83
  const nodes: Node[] = [];
84
- if (body.unchecked?.length) {
85
- nodes.push(el("p", { class: "subtle" }, `${body.unchecked.length} listed package(s) could not be checked against the catalog's latest revision.`));
84
+ if (body.unavailable?.length) {
85
+ nodes.push(el("p", { class: "subtle" }, `Not checked: ${body.unavailable.map((u) => `${u.title} (${u.message})`).join("; ")}.`));
86
86
  }
87
87
  if (body.updates.length === 0) {
88
88
  nodes.push(el("p", {}, "Every listed package is at the catalog's latest revision."));
@@ -124,7 +124,9 @@ export class HttpFoxyOrderClient implements FoxyOrderClient {
124
124
  if (!config.apiKey) throw new FoxyOrderClientError("foxyOrderClient: FOXY_ORDER_API_KEY is not configured");
125
125
  this.baseUrl = config.baseUrl.replace(/\/+$/, "");
126
126
  this.apiKey = config.apiKey;
127
- this.fetchImpl = config.fetchImpl ?? fetch;
127
+ // Called unbound: the Workers runtime refuses fetch invoked with any `this` but the global.
128
+ const doFetch = config.fetchImpl ?? fetch;
129
+ this.fetchImpl = (input, init) => doFetch(input, init);
128
130
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
129
131
  }
130
132
 
@@ -12,7 +12,7 @@ import Badge from "@/components/Badge.astro";
12
12
  import ProductCard from "@/components/ProductCard.astro";
13
13
  import Section from "@/components/Section.astro";
14
14
  import ImageGallery from "@/components/islands/ImageGallery.tsx";
15
- import VariantSelector from "@/components/islands/VariantSelector.tsx";
15
+ import VariantSelector, { publicSelectorVariants } from "@/components/islands/VariantSelector.tsx";
16
16
  import CheckoutComplianceGate from "@/components/islands/CheckoutComplianceGate.tsx";
17
17
  import RecentlyViewed from "@/components/islands/RecentlyViewed.tsx";
18
18
  import NicotineWarning from "@/components/compliance/NicotineWarning.astro";
@@ -361,7 +361,7 @@ const jsonLd = [productJsonLd, breadcrumbLd(crumbs, canonicalBase)];
361
361
  <div class="mt-7">
362
362
  <VariantSelector
363
363
  client:load
364
- variants={product.variants}
364
+ variants={publicSelectorVariants(product.variants)}
365
365
  options={product.options}
366
366
  currency={product.price_range.currency}
367
367
  locale={tenant.locale}
@@ -68,7 +68,7 @@ import HowItWorksSteps from "@/components/subscription/HowItWorksSteps.astro";
68
68
  import SavingsExplainer from "@/components/subscription/SavingsExplainer.astro";
69
69
  import ManageSubscriptionEntry from "@/components/subscription/ManageSubscriptionEntry.astro";
70
70
  import AgeGate from "@/components/islands/AgeGate.tsx";
71
- import VariantSelector from "@/components/islands/VariantSelector.tsx";
71
+ import VariantSelector, { publicSelectorVariants } from "@/components/islands/VariantSelector.tsx";
72
72
  import QuickView from "@/components/islands/QuickView.tsx";
73
73
  import SearchTypeahead from "@/components/islands/SearchTypeahead.tsx";
74
74
  import MobileNav from "@/components/islands/MobileNav.tsx";
@@ -1424,7 +1424,7 @@ const componentVarsInline = [
1424
1424
  <p class="sg-cap" style="margin:22px 0 8px">VariantSelector — PDP option picker (radiogroup + live price/SKU/stock region)</p>
1425
1425
  <VariantSelector
1426
1426
  client:load
1427
- variants={sampleFromProduct.variants}
1427
+ variants={publicSelectorVariants(sampleFromProduct.variants)}
1428
1428
  options={sampleFromProduct.options}
1429
1429
  currency={sampleFromProduct.price_range.currency}
1430
1430
  locale="en-US"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.4.12",
3
+ "version": "2.4.14",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
6
6
  "packageManager": "pnpm@11.9.0",
@@ -168,6 +168,31 @@ export interface CatalogLink {
168
168
  /** ISO 8601. */
169
169
  accepted_at: string;
170
170
  facts: CatalogFact[];
171
+ /** Server-verified defaults adopted by the merchant; replayable without the current policy. */
172
+ setup_policy?: CatalogSetupProvenance;
173
+ }
174
+
175
+ export interface CatalogSetupPriceAcceptance {
176
+ engine_version: string;
177
+ policy_revision: number;
178
+ value: number;
179
+ /** Producer schema validated settings, source facts and resolved sample. */
180
+ input: Record<string, unknown>;
181
+ }
182
+
183
+ export interface CatalogSetupSkuAcceptance {
184
+ engine_version: string;
185
+ policy_revision: number;
186
+ value: string;
187
+ /** Producer schema validated settings and identity/attribute inputs. */
188
+ input: Record<string, unknown>;
189
+ }
190
+
191
+ export interface CatalogSetupProvenance {
192
+ tenant_id: string;
193
+ currency: Currency;
194
+ pricing?: CatalogSetupPriceAcceptance;
195
+ sku?: CatalogSetupSkuAcceptance;
171
196
  }
172
197
 
173
198
  /** Product fields a merchant set away from the accepted catalog content. */