@tokenoftrust/storefront-runner 2.4.66 → 2.4.68

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.
@@ -477,12 +477,11 @@ export default function VariantSelector({
477
477
  </div>
478
478
  </div>
479
479
 
480
- {/* Add to cart. With a wired checkout store this is a real checkout form —
481
- loader.js turns the submit into the cart drawer + hosted checkout. The
482
- product fields are server-pre-signed (tamper-proof); the quantity
483
- rides along as the selected stepper value. Without a store (marketing
484
- tenant / signed mode missing secret) it degrades to a disabled
485
- placeholder. */}
480
+ {/* Add to cart. With a wired checkout store the form action is the
481
+ checkout-handoff commerce route (`/api/commerce/checkout-handoff`).
482
+ Product fields are server-pre-signed; the quantity is the selected
483
+ stepper value. Without a store (marketing tenant, or signed mode with
484
+ no cart secret) the control is a disabled placeholder. */}
486
485
  {checkoutCart ? (
487
486
  <div class="flex flex-col gap-1.5">
488
487
  <form
@@ -152,14 +152,17 @@ function destinationOf(value: unknown): { region: string; country: string } | nu
152
152
  return { region: region.toUpperCase(), country: country === "" ? "US" : country.toUpperCase() };
153
153
  }
154
154
 
155
- async function catalogOffers(sku: string): Promise<boolean> {
155
+ async function catalogAvailableFor(skus: readonly string[]): Promise<boolean> {
156
+ if (skus.length === 0) return false;
156
157
  const database = await readD1("CATALOG_DB");
157
158
  if (!database) return false;
159
+ const wanted = new Set(skus);
158
160
  try {
159
161
  const products = await listAllActiveProducts(fromD1(database));
160
162
  for (const product of products) {
161
163
  for (const variant of product.variants) {
162
- if (variant.sku === sku && variant.inventory.available === true) return true;
164
+ if (variant.sku === undefined) continue;
165
+ if (wanted.has(variant.sku) && variant.inventory.available === true) return true;
163
166
  }
164
167
  }
165
168
  } catch {
@@ -216,24 +219,7 @@ export async function POST(context: APIContext): Promise<Response> {
216
219
  return deny("malformed-request", 409);
217
220
  }
218
221
 
219
- if (body.scheme === "shared-secret") {
220
- const decision = await evaluateFinalCart({
221
- now: new Date().toISOString(),
222
- routedTenantId: appDomain,
223
- quoted: readCart(body.quoted) ?? emptyCart(appDomain),
224
- presented: readCart(body.presented) ?? emptyCart(appDomain),
225
- evidence: readEvidence(body.evidence),
226
- seam: { status: "clear" },
227
- catalogAvailable: false,
228
- browserConfirmed: body.browserConfirmed === true,
229
- token: { scheme: "shared-secret" },
230
- trustedIssuers: [],
231
- expectedAudience: "",
232
- activeKids: [],
233
- authorities: [],
234
- });
235
- return deny(decision.ok ? "shared-secret" : decision.finding, statusFor(decision.ok ? "shared-secret" : decision.finding), decision.ok ? undefined : decision.owner);
236
- }
222
+ if (body.scheme === "shared-secret") return deny("shared-secret", statusFor("shared-secret"));
237
223
 
238
224
  const quoted = readCart(body.quoted);
239
225
  const presented = readCart(body.presented);
@@ -263,12 +249,7 @@ export async function POST(context: APIContext): Promise<Response> {
263
249
  const exp = typeof decoded.exp === "number" ? new Date(decoded.exp * 1000).toISOString() : "";
264
250
  const audienceClaim = typeof decoded.aud === "string" ? decoded.aud : Array.isArray(decoded.aud) ? decoded.aud[0] ?? "" : "";
265
251
  const stageClaim = claimString(decoded, "stage");
266
- const stage: CommerceStage =
267
- stageClaim === "preview" || stageClaim === "publication"
268
- ? stageClaim
269
- : presented.stage === "preview"
270
- ? "publication"
271
- : "preview";
252
+ if (stageClaim !== "preview" && stageClaim !== "publication") return deny("stage", statusFor("stage"));
272
253
  token = {
273
254
  scheme: "asymmetric-jwks",
274
255
  signatureValid,
@@ -278,7 +259,7 @@ export async function POST(context: APIContext): Promise<Response> {
278
259
  expiresAt: exp,
279
260
  jti: claimString(decoded, "jti"),
280
261
  tenantId: claimString(decoded, "tenantId"),
281
- stage,
262
+ stage: stageClaim,
282
263
  };
283
264
  } catch {
284
265
  token = null;
@@ -301,10 +282,7 @@ export async function POST(context: APIContext): Promise<Response> {
301
282
  );
302
283
  }
303
284
  }
304
- let catalogAvailable = false;
305
- for (const line of presented.lines) {
306
- if (await catalogOffers(line.sku)) catalogAvailable = true;
307
- }
285
+ const catalogAvailable = await catalogAvailableFor(presented.lines.map((line) => line.sku));
308
286
 
309
287
  const decision = await evaluateFinalCart({
310
288
  now,
@@ -353,14 +331,3 @@ export async function POST(context: APIContext): Promise<Response> {
353
331
  });
354
332
  }
355
333
 
356
- function emptyCart(tenantId: string): FinalCartBinding {
357
- return {
358
- tenantId,
359
- stage: "preview",
360
- currency: "USD",
361
- lines: [{ sku: "sku-1", quantity: 1, unitMinor: 0 }],
362
- destinationDigest: "dest-synthetic",
363
- verificationExpiresAt: "1970-01-01T00:00:00.000Z",
364
- taxExpiresAt: "1970-01-01T00:00:00.000Z",
365
- };
366
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.4.66",
3
+ "version": "2.4.68",
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",
@@ -110,13 +110,13 @@ export interface BuildVariantCartPayloadOptions {
110
110
  secret?: string;
111
111
  purchaseOption?: ProductPurchaseOption;
112
112
  /**
113
- * The member-adjusted EFFECTIVE price to sign, computed server-side via
114
- * membership.ts `priceForPurchaseOption` for the shopper's resolved eligibility.
115
- * When set it REPLACES the list/subscription price as the single signed source
116
- * of truth, so a given price is only ever valid for the member context it was
117
- * signed under (see {@link ../membership.ts buildMemberSignedCartPayload}).
118
- * Absent → the list/subscription price (anonymous shopper). Never a client-
119
- * supplied value: only the server, holding the cart secret, ever signs it.
113
+ * Member-adjusted effective price to sign. `buildMemberSignedCartPayload`
114
+ * in membership.ts runs `priceForPurchaseOption` for this request's resolved
115
+ * eligibility and passes that price here. `buildVariantCartPayload` signs it
116
+ * in place of the list or subscription price, so the signature matches only
117
+ * that member context. Absent, the list or subscription price is signed for
118
+ * an anonymous shopper. The client never supplies this value; only the
119
+ * server, holding the cart secret, signs it.
120
120
  */
121
121
  signedPrice?: number;
122
122
  /**