@tokenoftrust/storefront-runner 2.2.45 → 2.2.46

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.
@@ -0,0 +1,14 @@
1
+ import type { TenantConfig } from "@tot/public-runtime";
2
+
3
+ /**
4
+ * The tenant's own public origins: `https://<host>` for every declared domain and the canonical
5
+ * domain, deduped and sorted. The one derivation every image scan uses — the Worker's capture and
6
+ * review scans read it directly, and the static publish scan reads it from the bundle manifest
7
+ * (the render reports it to the materializer) — so an absolute reference to the tenant's own site
8
+ * is classified by path everywhere, never as a third-party hotlink in one place only.
9
+ */
10
+ export function declaredTenantOrigins(tenant: Pick<TenantConfig, "domains" | "canonicalDomain">): string[] {
11
+ const hosts = [...(tenant.domains ?? []), tenant.canonicalDomain]
12
+ .filter((host): host is string => typeof host === "string" && host.length > 0);
13
+ return [...new Set(hosts.map((host) => `https://${host.toLowerCase()}`))].sort();
14
+ }
@@ -96,6 +96,7 @@ import {
96
96
  } from "@/lib/integration/renderIntegrationState";
97
97
  import { recordActivity } from "@/lib/activity/recordActivity";
98
98
  import { middlewareRewriteTargetV1 } from "./routeBranch.js";
99
+ import { declaredTenantOrigins } from "@/lib/tenantOrigins";
99
100
  import {
100
101
  applyInternalContentResponsePolicy,
101
102
  internalContentGrantMatchesPlan,
@@ -142,6 +143,9 @@ export interface RenderChainInput {
142
143
  internalContentGrant?: InternalContentGrant;
143
144
  }
144
145
 
146
+ /** Materialize-only response header: the tenant's declared origins, comma-separated. */
147
+ export const TENANT_ORIGINS_CAPTURE_HEADER = "x-tot-tenant-origins";
148
+
145
149
  export async function runRenderChain(input: RenderChainInput): Promise<Response> {
146
150
  const { context, next, host, url, tenant, basePath, renderPath, cache, kv } = input;
147
151
  const { request, locals } = context;
@@ -817,6 +821,11 @@ export async function runRenderChain(input: RenderChainInput): Promise<Response>
817
821
  HTML_CACHE_POLICY_CAPTURE_HEADER,
818
822
  formatHtmlCacheControl(locals.tenant?.serving?.htmlCachePolicy),
819
823
  );
824
+ // The tenant's declared origins — the same `domains` the Worker's own image scans use. The
825
+ // materializer ships images a page references absolutely on them (og:image is always
826
+ // absolute), and the publish scan classifies such references by path instead of as
827
+ // third-party hotlinks.
828
+ response.headers.set(TENANT_ORIGINS_CAPTURE_HEADER, declaredTenantOrigins(tenant).join(","));
820
829
  }
821
830
  if (isInternalPublication) applyInternalContentResponsePolicy(response.headers);
822
831
  // Stamp a fresh per-response nonce into this cold response's body + CSP header
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.2.45",
3
+ "version": "2.2.46",
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",