@tokenoftrust/storefront-runner 2.4.63 → 2.4.64

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.
@@ -87,10 +87,30 @@ export async function readCartSecret(
87
87
  * but the secret is missing, so NO cart may be built. The caller must render
88
88
  * no buy form (never a tamperable / broken one).
89
89
  */
90
+ /** Shopper copy when checkout cannot be signed. No operator setup detail. */
91
+ export const CHECKOUT_UNAVAILABLE_SHOPPER_MESSAGE = "Checkout is unavailable.";
92
+
93
+ /** Product-page alert. The cart signing env var name stays in the server log. */
94
+ export function shopperCheckoutUnavailableHtml(): string {
95
+ return (
96
+ `<p class="mb-3 text-sm text-[var(--color-text)]" role="alert" data-cart-operator-error>` +
97
+ `${CHECKOUT_UNAVAILABLE_SHOPPER_MESSAGE}</p>`
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Message safe to put in an HTTP body. `signing-unavailable` carries operator
103
+ * setup text from the cart runtime; shoppers get the neutral message instead.
104
+ */
105
+ export function shopperCartDenialMessage(code: string, message: string): string {
106
+ if (code === "signing-unavailable") return CHECKOUT_UNAVAILABLE_SHOPPER_MESSAGE;
107
+ return message;
108
+ }
109
+
90
110
  export interface CartSigningResolution {
91
111
  enabled: boolean;
92
112
  secret?: string;
93
- /** Set when signed mode has no secret. Actionable for the operator. No secret value. */
113
+ /** Operator setup detail. Do not write this into shopper HTML or an HTTP body. */
94
114
  operatorError?: string;
95
115
  }
96
116
 
@@ -26,7 +26,7 @@ import {
26
26
  type CartDenialCode,
27
27
  } from "@tot/public-runtime";
28
28
  import { resolveCheckoutHandoffCorrelation } from "@/lib/commerce/checkoutHandoffCorrelation";
29
- import { resolveCartSigning } from "@/lib/checkoutCommerce";
29
+ import { resolveCartSigning, shopperCartDenialMessage } from "@/lib/checkoutCommerce";
30
30
 
31
31
  export const prerender = false;
32
32
 
@@ -88,7 +88,11 @@ async function cartFormPost(context: APIContext): Promise<Response> {
88
88
  }
89
89
  const signing = await resolveCartSigning(commerce);
90
90
  if (!signing.enabled) {
91
- return cartDenial("signing-unavailable", signing.operatorError ?? "Signed checkout is unavailable.", 503);
91
+ return cartDenial(
92
+ "signing-unavailable",
93
+ shopperCartDenialMessage("signing-unavailable", signing.operatorError ?? ""),
94
+ 503,
95
+ );
92
96
  }
93
97
  const form = await context.request.formData();
94
98
  const entries: Array<[string, string]> = [];
@@ -103,7 +107,7 @@ async function cartFormPost(context: APIContext): Promise<Response> {
103
107
  });
104
108
  if (!result.ok) {
105
109
  const status = result.code === "signing-unavailable" ? 503 : 422;
106
- return cartDenial(result.code, result.message, status);
110
+ return cartDenial(result.code, shopperCartDenialMessage(result.code, result.message), status);
107
111
  }
108
112
  return new Response(null, {
109
113
  status: 303,
@@ -37,7 +37,7 @@ import type {
37
37
  MembershipStackingPolicy,
38
38
  PurchaseOptionPricingContext,
39
39
  } from "@tot/public-runtime";
40
- import { resolveCartSigning } from "@/lib/checkoutCommerce";
40
+ import { resolveCartSigning, shopperCheckoutUnavailableHtml } from "@/lib/checkoutCommerce";
41
41
  import { isRegulatedTenant } from "@/lib/compliance/enforcement";
42
42
  import { resolveMemberContext } from "@/lib/membership/eligibility";
43
43
  import { truncate } from "@/lib/format";
@@ -223,14 +223,14 @@ let checkoutCart:
223
223
  byVariantId: Record<string, Record<string, VariantCartPayload>>;
224
224
  }
225
225
  | undefined;
226
- let cartOperatorError: string | undefined;
226
+ let checkoutUnavailable = false;
227
227
  if (commerce) {
228
228
  // Fail closed for production signed checkout: a signed tenant with no
229
229
  // cart-validation secret builds NO cart (never a tamperable form). See
230
230
  // resolveCartSigning — it also logs the misconfiguration loudly.
231
231
  const signing = await resolveCartSigning(commerce);
232
232
  if (!signing.enabled) {
233
- cartOperatorError = signing.operatorError;
233
+ checkoutUnavailable = true;
234
234
  } else {
235
235
  try {
236
236
  const purchaseOptions = visiblePurchaseOptions(
@@ -265,10 +265,9 @@ if (commerce) {
265
265
  byVariantId: Object.fromEntries(entries),
266
266
  };
267
267
  } catch (err) {
268
- cartOperatorError =
269
- err instanceof Error && err.name === "CartSigningUnavailableError"
270
- ? err.message
271
- : undefined;
268
+ if (err instanceof Error && err.name === "CartSigningUnavailableError") {
269
+ checkoutUnavailable = true;
270
+ }
272
271
  console.warn(
273
272
  `[storefront] Invalid checkout purchase options for ${product.handle}; cart disabled for this product.`,
274
273
  );
@@ -386,11 +385,7 @@ const jsonLd = [productJsonLd, breadcrumbLd(crumbs, canonicalBase)];
386
385
  The form action is the worker cart route, so a native submit reaches
387
386
  the server check. A clear browser preflight is not payment authority. */}
388
387
  <div class="mt-7" data-payment-authority="none">
389
- {cartOperatorError && (
390
- <p class="mb-3 text-sm text-[var(--color-text)]" role="alert" data-cart-operator-error>
391
- {cartOperatorError}
392
- </p>
393
- )}
388
+ {checkoutUnavailable && <Fragment set:html={shopperCheckoutUnavailableHtml()} />}
394
389
  <VariantSelector
395
390
  client:load
396
391
  variants={publicSelectorVariants(product.variants)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.4.63",
3
+ "version": "2.4.64",
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",