@tokenoftrust/storefront-runner 2.4.60 → 2.4.62

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.
@@ -204,7 +204,7 @@ function readSourcedEvidence(
204
204
  const asOfValue = verification[`${key}AsOf`];
205
205
  return {
206
206
  state,
207
- source: typeof sourceValue === "string" && sourceValue.length > 0 ? sourceValue : "checkout-order",
207
+ source: typeof sourceValue === "string" && sourceValue.length > 0 ? sourceValue : "absent",
208
208
  asOf: typeof asOfValue === "string" ? asOfValue : null,
209
209
  };
210
210
  }
@@ -266,13 +266,14 @@ export function releaseOf(order: ScrappyOrderRecord): OrderRelease {
266
266
  return { state: ready ? "ready" : "held", ready, blocks };
267
267
  }
268
268
 
269
- function checkoutRecord(order: CheckoutOrderSummary): ScrappyOrderRecord {
269
+ function checkoutRecord(order: CheckoutOrderSummary, scope: CommerceScope): ScrappyOrderRecord {
270
+ if (scope.tenantId.length === 0) throw new Error("checkout scope has no tenant");
270
271
  const payment: PaymentState = order.payment;
271
272
  return {
272
273
  id: order.id,
273
274
  placedAt: order.placedAt,
274
- tenantId: "",
275
- stage: "preview",
275
+ tenantId: scope.tenantId,
276
+ stage: scope.stage,
276
277
  currency: order.currency,
277
278
  payment: { state: payment, source: "checkout-order", asOf: order.placedAt },
278
279
  tax: readSourcedEvidence(order.verification, "tax"),
@@ -294,24 +295,16 @@ function checkoutRecord(order: CheckoutOrderSummary): ScrappyOrderRecord {
294
295
  }
295
296
 
296
297
  /** Checkout DTO assessment. `verification: null` reports UNKNOWN and held. */
297
- export function assessCheckoutOrder(order: CheckoutOrderSummary): CheckoutReadiness {
298
- const record = checkoutRecord(order);
299
- const release = order.verification == null
300
- ? heldRelease(releaseOf(record))
301
- : releaseOf(record);
298
+ export function assessCheckoutOrder(order: CheckoutOrderSummary, scope: CommerceScope): CheckoutReadiness {
299
+ const record = checkoutRecord(order, scope);
302
300
  return {
303
301
  columns: readinessColumns(record),
304
302
  evidence: evidenceOf(record),
305
- release,
303
+ release: releaseOf(record),
306
304
  riskLevel: deriveRisk(order.verification, order.blocker),
307
305
  };
308
306
  }
309
307
 
310
- function heldRelease(release: OrderRelease): OrderRelease {
311
- if (release.state === "held") return release;
312
- return { state: "held", ready: false, blocks: [...release.blocks, "evidence"] };
313
- }
314
-
315
308
  export function mapSummaryToRow(s: CheckoutOrderSummary): OrderRow {
316
309
  const verificationMissing = s.verification == null;
317
310
  const shipState: OrderShipState = verificationMissing ? "hold_review_required" : s.fulfillment;
@@ -346,8 +339,8 @@ function columnCheck(
346
339
  return { key: column.key, value: column.state, ...copy };
347
340
  }
348
341
 
349
- function deriveReadinessChecks(d: CheckoutOrderDetail): OrderReadinessCheck[] {
350
- const report = assessCheckoutOrder(d);
342
+ function deriveReadinessChecks(d: CheckoutOrderDetail, scope: CommerceScope): OrderReadinessCheck[] {
343
+ const report = assessCheckoutOrder(d, scope);
351
344
  const column = (key: OrderReadinessColumn["key"]) => report.columns.find((item) => item.key === key)!;
352
345
  const payment = column("payment");
353
346
  const tax = column("tax");
@@ -444,8 +437,8 @@ function deriveEvidenceTimeline(d: CheckoutOrderDetail): OrderEvidenceEvent[] {
444
437
  return events;
445
438
  }
446
439
 
447
- export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow): OrderDetail {
448
- const report = assessCheckoutOrder(d);
440
+ export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow, scope: CommerceScope): OrderDetail {
441
+ const report = assessCheckoutOrder(d, scope);
449
442
  return {
450
443
  ...row,
451
444
  lineItems: d.lineItems.map((li) => ({
@@ -457,7 +450,7 @@ export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow): OrderD
457
450
  shipping: d.totals.shipping,
458
451
  estimatedTax: d.totals.estimatedTax,
459
452
  taxesAndFees: d.totals.taxesAndFees,
460
- readinessChecks: deriveReadinessChecks(d),
453
+ readinessChecks: deriveReadinessChecks(d, scope),
461
454
  evidenceTimeline: deriveEvidenceTimeline(d),
462
455
  columns: report.columns,
463
456
  release: report.release,
@@ -481,13 +474,14 @@ export function mapOrdersToViewModel(
481
474
  targetTenant: string,
482
475
  summaries: CheckoutOrderSummary[],
483
476
  details: Record<string, CheckoutOrderDetail>,
477
+ scope: CommerceScope,
484
478
  ): OrdersViewModel {
485
479
  const rows = summaries.map(mapSummaryToRow);
486
480
  const detailsById: Record<string, OrderDetail> = {};
487
481
  for (const row of rows) {
488
482
  const d = details[row.id];
489
483
  detailsById[row.id] = d
490
- ? mapDetailToDetail(d, row)
484
+ ? mapDetailToDetail(d, row, scope)
491
485
  : {
492
486
  ...row,
493
487
  lineItems: [],
@@ -520,11 +514,25 @@ export function mapOrdersToViewModel(
520
514
  }
521
515
 
522
516
  export function emptyOrdersViewModel(targetTenant: string): OrdersViewModel {
523
- return mapOrdersToViewModel(targetTenant, [], {});
517
+ const rows: OrderRow[] = [];
518
+ return {
519
+ targetTenant,
520
+ queues: buildQueues(rows),
521
+ rows,
522
+ detailsById: {},
523
+ today: {
524
+ grossSales: 0,
525
+ paidOrders: 0,
526
+ averageOrder: 0,
527
+ refunds: 0,
528
+ oldestOpenAt: null,
529
+ },
530
+ };
524
531
  }
525
532
 
526
533
  export interface GetOrdersViewModelOptions {
527
534
  targetTenant: string;
535
+ scope: CommerceScope;
528
536
  client: CheckoutOrderClient;
529
537
  filters?: Parameters<CheckoutOrderClient["listOrders"]>[0];
530
538
  }
@@ -569,7 +577,7 @@ export async function getOrdersViewModelResult(opts: GetOrdersViewModelOptions):
569
577
  );
570
578
  return {
571
579
  status: orders.length ? "loaded" : "empty",
572
- vm: mapOrdersToViewModel(opts.targetTenant, orders, details),
580
+ vm: mapOrdersToViewModel(opts.targetTenant, orders, details, opts.scope),
573
581
  page,
574
582
  };
575
583
  } catch {
@@ -11,13 +11,13 @@ import { readEnv } from "@/lib/env";
11
11
  import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
12
12
  import {
13
13
  commerceActorForAdmin,
14
- commerceStageForCustomization,
15
14
  createCheckoutOrderClient,
16
15
  orderApiBaseUrl,
17
16
  OrderApiError,
18
17
  projectCheckoutOrder,
19
18
  } from "@/lib/admin/orderClient";
20
19
  import { assessCheckoutOrder } from "@/lib/admin/services/orders";
20
+ import { commerceStageForEnv } from "@/lib/fulfillment/fulfillmentService";
21
21
 
22
22
  export const prerender = false;
23
23
 
@@ -46,25 +46,27 @@ export async function GET(context: APIContext): Promise<Response> {
46
46
  const bearer = await readEnv("FOXY_ORDER_API_KEY");
47
47
  if (!bearer) return jsonError("not_provisioned", 503);
48
48
 
49
+ const stage = commerceStageForEnv(customizationEnv);
50
+ if (!stage) return jsonError("stage-unknown", 409);
51
+ const tenantId = tenant.appDomain.trim();
52
+ if (!tenantId) return jsonError("not_provisioned", 503);
53
+
49
54
  const client = createCheckoutOrderClient({
50
55
  baseUrl: orderApiBaseUrl(customizationEnv),
51
- appDomain: tenant.appDomain,
56
+ appDomain: tenantId,
52
57
  bearer,
53
58
  });
54
59
 
55
60
  try {
56
- const stage = commerceStageForCustomization(customizationEnv);
61
+ const scope = { tenantId, stage };
57
62
  const actor = commerceActorForAdmin({
58
- tenantId: tenant.appDomain,
63
+ tenantId,
59
64
  stage,
60
65
  owner: access.entry.isOwnerViewer,
61
66
  admitted: access.entry.admitted,
62
67
  });
63
- const order = projectCheckoutOrder(await client.getOrder(orderId), actor, {
64
- tenantId: tenant.appDomain,
65
- stage,
66
- });
67
- const readiness = assessCheckoutOrder(order);
68
+ const order = projectCheckoutOrder(await client.getOrder(orderId), actor, scope);
69
+ const readiness = assessCheckoutOrder(order, scope);
68
70
  return new Response(JSON.stringify({ order, readiness }), { headers: JSON_HEADERS });
69
71
  } catch (err) {
70
72
  if (err instanceof OrderApiError) return jsonError(err.code, err.status);
@@ -17,6 +17,7 @@ import {
17
17
  orderApiBaseUrl,
18
18
  OrderApiError,
19
19
  } from "@/lib/admin/orderClient";
20
+ import { commerceStageForEnv } from "@/lib/fulfillment/fulfillmentService";
20
21
  import { assessCheckoutOrder, parseOrderFilters } from "@/lib/admin/services/orders";
21
22
 
22
23
  export const prerender = false;
@@ -49,21 +50,27 @@ export async function GET(context: APIContext): Promise<Response> {
49
50
  return jsonError("not_provisioned", 503);
50
51
  }
51
52
 
53
+ const stage = commerceStageForEnv(customizationEnv);
54
+ if (!stage) return jsonError("stage-unknown", 409);
55
+ const tenantId = tenant.appDomain.trim();
56
+ if (!tenantId) return jsonError("not_provisioned", 503);
57
+
52
58
  const url = new URL(context.request.url);
53
59
  // Tenant comes from the host, never from a query parameter.
54
60
  const filters = parseOrderFilters(url.searchParams);
55
61
 
56
62
  const client = createCheckoutOrderClient({
57
63
  baseUrl: orderApiBaseUrl(customizationEnv),
58
- appDomain: tenant.appDomain,
64
+ appDomain: tenantId,
59
65
  bearer,
60
66
  });
61
67
 
62
68
  try {
63
69
  const data = await client.listOrders(filters);
70
+ const scope = { tenantId, stage };
64
71
  const orders = data.orders.map((order) => ({
65
72
  ...order,
66
- readiness: assessCheckoutOrder(order),
73
+ readiness: assessCheckoutOrder(order, scope),
67
74
  }));
68
75
  return new Response(JSON.stringify({ ...data, orders }), { headers: JSON_HEADERS });
69
76
  } catch (err) {
@@ -21,8 +21,7 @@ import ReviewSummary from "@/components/commerce/ReviewSummary.astro";
21
21
  import ReviewList from "@/components/commerce/ReviewList.astro";
22
22
  import { isOnSale, isNew, isAvailable, fromPrice } from "@/lib/tot/query";
23
23
  import {
24
- buildVariantCartPayload,
25
- priceForPurchaseOption,
24
+ buildMemberSignedCartPayload,
26
25
  workerCartApiPath,
27
26
  normalizePurchaseOptions,
28
27
  visiblePurchaseOptions,
@@ -243,18 +242,13 @@ if (commerce) {
243
242
  async (v) => {
244
243
  const payloads = await Promise.all(
245
244
  purchaseOptions.map(async (purchaseOption) => {
246
- const effective = priceForPurchaseOption(
247
- v.price,
248
- purchaseOption,
249
- pricingCtx,
250
- );
251
245
  return [
252
246
  purchaseOption.id,
253
- await buildVariantCartPayload(product, v, {
247
+ await buildMemberSignedCartPayload(product, v, {
254
248
  mode: commerce.mode,
255
249
  secret: signing.secret,
256
250
  purchaseOption,
257
- signedPrice: effective.price,
251
+ pricing: pricingCtx,
258
252
  autopilotEnabled,
259
253
  tenantId: tenant.tenant_id,
260
254
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.4.60",
3
+ "version": "2.4.62",
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",
@@ -276,6 +276,8 @@ export interface MemberSignedCartPayloadOptions {
276
276
  * is off. Callers should still pre-filter with {@link visiblePurchaseOptions}.
277
277
  */
278
278
  autopilotEnabled: boolean;
279
+ /** Tenant id written onto the signed `tenant` field when present. */
280
+ tenantId?: string;
279
281
  }
280
282
 
281
283
  /**
@@ -309,6 +311,7 @@ export async function buildMemberSignedCartPayload(
309
311
  purchaseOption: opts.purchaseOption,
310
312
  signedPrice: effective.price,
311
313
  autopilotEnabled: opts.autopilotEnabled,
314
+ tenantId: opts.tenantId,
312
315
  });
313
316
  }
314
317