@tokenoftrust/storefront-runner 2.0.25 → 2.0.26

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.0.25",
3
+ "version": "2.0.26",
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",
@@ -273,18 +273,34 @@ export function isPreviewDispatcherPath(pathname: string): boolean {
273
273
  }
274
274
 
275
275
  /**
276
- * Decide which customization environment to serve. Production hosts always get
277
- * `live` and never consult a token. Preview hosts are auth-gated: a valid,
278
- * unexpired, tenant-matching token yields `test` (noindex); anything else is
279
- * rejected. The one exception is the token-dispenser path itself (see
276
+ * Decide which customization environment to serve. A valid, unexpired,
277
+ * tenant-matching `__preview`/`tot_preview` token grants `test` (noindex) on
278
+ * ANY host — the token is checked before the host is, so a signed preview
279
+ * link keeps working even when opened on the tenant's normal (non-`preview.*`)
280
+ * host. Without such a token, production hosts always get `live`; preview
281
+ * hosts are auth-gated and reject a missing/invalid/cross-tenant token. The
282
+ * one exception is the token-dispenser path itself (see
280
283
  * {@link isPreviewDispatcherPath}) — it renders the dispatcher's own minimal
281
- * page, never tenant content, so it's let through as `live`/no-token rather
282
- * than 401ing on the very request that would otherwise fetch it a token.
284
+ * page, never tenant content, so it's always `live`/no-token regardless of
285
+ * host or token, rather than 401ing on the very request that would otherwise
286
+ * fetch a token.
283
287
  */
284
288
  export async function resolveCustomizationEnv(
285
289
  input: EnvResolutionInput,
286
290
  ): Promise<EnvResolution> {
287
- if (!input.isPreviewHost || isPreviewDispatcherPath(input.pathname ?? "")) {
291
+ if (isPreviewDispatcherPath(input.pathname ?? "")) {
292
+ return { env: "live", noindex: false };
293
+ }
294
+
295
+ const tokenResult =
296
+ input.secret && input.token
297
+ ? await verifyPreviewToken(input.secret, input.token, input.nowSeconds)
298
+ : null;
299
+ if (tokenResult?.ok && tokenResult.claims.tenantId === input.tenantId) {
300
+ return { env: "test", noindex: true };
301
+ }
302
+
303
+ if (!input.isPreviewHost) {
288
304
  return { env: "live", noindex: false };
289
305
  }
290
306
  if (!input.secret) {
@@ -301,20 +317,16 @@ export async function resolveCustomizationEnv(
301
317
  reject: { status: 401, message: "Preview token required." },
302
318
  };
303
319
  }
304
- const result = await verifyPreviewToken(input.secret, input.token, input.nowSeconds);
305
- if (!result.ok) {
320
+ if (!tokenResult!.ok) {
306
321
  return {
307
322
  env: "live",
308
323
  noindex: true,
309
- reject: { status: 401, message: `Invalid preview token (${result.reason}).` },
324
+ reject: { status: 401, message: `Invalid preview token (${tokenResult!.reason}).` },
310
325
  };
311
326
  }
312
- if (result.claims.tenantId !== input.tenantId) {
313
- return {
314
- env: "live",
315
- noindex: true,
316
- reject: { status: 403, message: "Preview token is for a different tenant." },
317
- };
318
- }
319
- return { env: "test", noindex: true };
327
+ return {
328
+ env: "live",
329
+ noindex: true,
330
+ reject: { status: 403, message: "Preview token is for a different tenant." },
331
+ };
320
332
  }