@tokenoftrust/storefront-runner 2.4.42 → 2.4.44
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.
|
@@ -25,7 +25,7 @@ import { getTenantResolver } from "@/config/resolver";
|
|
|
25
25
|
import { readEnv, readKv, readR2 } from "@/lib/runtimeEnv";
|
|
26
26
|
import { resolveCdnForwardRouting, resolveTenantRouting } from "@/lib/tenantRouting";
|
|
27
27
|
import { crossSiteFormRefusal, verifyCdnForward } from "@/lib/cdnForward";
|
|
28
|
-
import { compileRouteTierIndexV1, resolvePublicationRoutePlanV1, type RouteTierIndexV1 } from "@tot/public-runtime";
|
|
28
|
+
import { compileRouteTierIndexV1, resolvePublicationRoutePlanV1, type RouteTierIndexV1 , storefrontModeRequiresSignIn } from "@tot/public-runtime";
|
|
29
29
|
import type { PublicationArtifactBucketV1, TenantConfig } from "@tot/public-runtime";
|
|
30
30
|
import { DEV_DRAFT_COOKIE } from "@/lib/dev/devDraft";
|
|
31
31
|
import { applySecurityHeaders } from "@/lib/securityHeaders";
|
|
@@ -165,7 +165,16 @@ async function serveRequest(
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// ---- The one route classification -----------------------------------------
|
|
168
|
-
|
|
168
|
+
// An EXPLICITLY unpaid store is never fully public: the ONLY way to reach its
|
|
169
|
+
// hosted URL is to sign in, regardless of the demo-gate host list or mode —
|
|
170
|
+
// a billing-state boundary, not a demo feature. storefrontModeRequiresSignIn
|
|
171
|
+
// NEVER fires on a merely-ABSENT storefront.mode (every real tenant today has
|
|
172
|
+
// it absent, same as a fixture/tenant that never engaged the concept) — only
|
|
173
|
+
// on an explicit non-"enabled" value, set deliberately via
|
|
174
|
+
// scripts/tenant/set-storefront-mode.mjs.
|
|
175
|
+
const hostLoginGated =
|
|
176
|
+
(gateMode === "login" && hostInGateList(host, demoGateHosts)) ||
|
|
177
|
+
storefrontModeRequiresSignIn(tenant.storefront?.mode);
|
|
169
178
|
const { branch, route, publication } = resolveMiddlewareBranchV1({
|
|
170
179
|
index: routeTierIndexFor(tenant),
|
|
171
180
|
renderPath,
|
|
@@ -752,11 +752,6 @@ export async function runRenderChain(input: RenderChainInput): Promise<Response>
|
|
|
752
752
|
// store below so cached copies carry them too.
|
|
753
753
|
applySecurityHeaders(response.headers);
|
|
754
754
|
|
|
755
|
-
// Preview (test) output must never be indexed.
|
|
756
|
-
if (customization.noindex) {
|
|
757
|
-
response.headers.set("X-Robots-Tag", "noindex, nofollow");
|
|
758
|
-
}
|
|
759
|
-
|
|
760
755
|
// Strict CSP — now ENFORCED in every environment (Epic S2). The report-only
|
|
761
756
|
// rollout is complete: platform output is CSP-clean — inline styles are
|
|
762
757
|
// externalized (`inlineStylesheets: "never"`), Astro's static inline scripts +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.44",
|
|
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",
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
* - **Tokens are tenant-bound.** A token minted for tenant A cannot preview
|
|
16
16
|
* tenant B (no cross-tenant leakage).
|
|
17
17
|
* - **Tokens expire.** `exp` is checked against the caller-supplied clock.
|
|
18
|
-
* - **Test is never indexable.** The resolution carries `noindex: true` so the
|
|
19
|
-
* middleware can set `X-Robots-Tag: noindex`.
|
|
20
18
|
*/
|
|
21
19
|
|
|
22
20
|
/** Claims carried by a preview token. */
|
|
@@ -251,8 +249,6 @@ export interface EnvResolutionInput {
|
|
|
251
249
|
|
|
252
250
|
export interface EnvResolution {
|
|
253
251
|
env: CustomizationEnv;
|
|
254
|
-
/** Set `X-Robots-Tag: noindex` when true (test is never indexable). */
|
|
255
|
-
noindex: boolean;
|
|
256
252
|
/** When set, the middleware should short-circuit with this status. */
|
|
257
253
|
reject?: { status: number; message: string };
|
|
258
254
|
}
|
|
@@ -277,12 +273,12 @@ export function isPreviewDispatcherPath(pathname: string): boolean {
|
|
|
277
273
|
|
|
278
274
|
/**
|
|
279
275
|
* Decide which customization environment to serve. A valid, unexpired,
|
|
280
|
-
* tenant-matching `__preview`/`tot_preview` token grants `test`
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
276
|
+
* tenant-matching `__preview`/`tot_preview` token grants `test` on ANY host —
|
|
277
|
+
* the token is checked before the host is, so a signed preview link keeps
|
|
278
|
+
* working even when opened on the tenant's normal (non-`preview.*`) host.
|
|
279
|
+
* Without such a token, production hosts always get `live`; preview hosts
|
|
280
|
+
* are auth-gated and reject a missing/invalid/cross-tenant token. The one
|
|
281
|
+
* exception is the token-dispenser path itself (see
|
|
286
282
|
* {@link isPreviewDispatcherPath}) — it renders the dispatcher's own minimal
|
|
287
283
|
* page, never tenant content, so it's always `live`/no-token regardless of
|
|
288
284
|
* host or token, rather than 401ing on the very request that would otherwise
|
|
@@ -292,7 +288,7 @@ export async function resolveCustomizationEnv(
|
|
|
292
288
|
input: EnvResolutionInput,
|
|
293
289
|
): Promise<EnvResolution> {
|
|
294
290
|
if (isPreviewDispatcherPath(input.pathname ?? "")) {
|
|
295
|
-
return { env: "live"
|
|
291
|
+
return { env: "live" };
|
|
296
292
|
}
|
|
297
293
|
|
|
298
294
|
const tokenResult =
|
|
@@ -300,36 +296,32 @@ export async function resolveCustomizationEnv(
|
|
|
300
296
|
? await verifyPreviewToken(input.secret, input.token, input.nowSeconds)
|
|
301
297
|
: null;
|
|
302
298
|
if (tokenResult?.ok && tokenResult.claims.tenantId === input.tenantId) {
|
|
303
|
-
return { env: "test"
|
|
299
|
+
return { env: "test" };
|
|
304
300
|
}
|
|
305
301
|
|
|
306
302
|
if (!input.isPreviewHost) {
|
|
307
|
-
return { env: "live"
|
|
303
|
+
return { env: "live" };
|
|
308
304
|
}
|
|
309
305
|
if (!input.secret) {
|
|
310
306
|
return {
|
|
311
307
|
env: "live",
|
|
312
|
-
noindex: true,
|
|
313
308
|
reject: { status: 503, message: "Preview is not configured." },
|
|
314
309
|
};
|
|
315
310
|
}
|
|
316
311
|
if (!input.token) {
|
|
317
312
|
return {
|
|
318
313
|
env: "live",
|
|
319
|
-
noindex: true,
|
|
320
314
|
reject: { status: 401, message: "Preview token required." },
|
|
321
315
|
};
|
|
322
316
|
}
|
|
323
317
|
if (!tokenResult!.ok) {
|
|
324
318
|
return {
|
|
325
319
|
env: "live",
|
|
326
|
-
noindex: true,
|
|
327
320
|
reject: { status: 401, message: `Invalid preview token (${tokenResult!.reason}).` },
|
|
328
321
|
};
|
|
329
322
|
}
|
|
330
323
|
return {
|
|
331
324
|
env: "live",
|
|
332
|
-
noindex: true,
|
|
333
325
|
reject: { status: 403, message: "Preview token is for a different tenant." },
|
|
334
326
|
};
|
|
335
327
|
}
|
|
@@ -401,6 +401,19 @@ export function storefrontModeAllowsGoLive(mode: string | undefined | null): boo
|
|
|
401
401
|
return mode === STOREFRONT_MODE_ENABLED;
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
+
/**
|
|
405
|
+
* PURE sign-in-gate predicate: must a visitor sign in before this tenant's hosted URL is
|
|
406
|
+
* reachable, on billing grounds alone? True iff the mode is EXPLICITLY set to something other
|
|
407
|
+
* than "enabled" — never on absence. Absence means the storefront-commerce concept has never
|
|
408
|
+
* been provisioned for this tenant at all (a non-commerce tenant, a test fixture, a tenant
|
|
409
|
+
* pre-dating this field), which must behave exactly as it always has; conflating that with a
|
|
410
|
+
* deliberate "not yet paid" is the bug this predicate exists to make impossible to write by hand.
|
|
411
|
+
* Turn this on for a real tenant with scripts/tenant/set-storefront-mode.mjs --mode=disabled.
|
|
412
|
+
*/
|
|
413
|
+
export function storefrontModeRequiresSignIn(mode: string | undefined | null): boolean {
|
|
414
|
+
return mode != null && mode !== STOREFRONT_MODE_ENABLED;
|
|
415
|
+
}
|
|
416
|
+
|
|
404
417
|
/**
|
|
405
418
|
* Per-tenant capability switches. These gate whole surfaces so the same
|
|
406
419
|
* component library renders both a store and a marketing site without any
|