From ba6b003e57afbd231580ba4027624902013640fc Mon Sep 17 00:00:00 2001 From: Jonas Jesus Date: Tue, 18 Aug 2026 01:17:28 -0300 Subject: [PATCH] feat(renderJson): default ?renderJson CORS to open (any origin, no credentials) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ?renderJson is public page data — secrets and request-derived keys are stripped before serialization — so it should be readable cross-origin out of the box, the same as any public API. pageJsonCors now defaults to "*" (Access-Control-Allow- Origin: * without credentials) instead of off. `false` turns it off; a string[] still reflects an allow-listed Origin *with* credentials for personalized cases. Type: string[] | "*" | false. No credentials with "*" (per the CORS spec), so no logged-in/personalized leak — the earlier off-by-default was over-cautious for public content. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/tanstack/src/sdk/workerEntry.ts | 28 +++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/tanstack/src/sdk/workerEntry.ts b/packages/tanstack/src/sdk/workerEntry.ts index cdb8f0a..712956e 100644 --- a/packages/tanstack/src/sdk/workerEntry.ts +++ b/packages/tanstack/src/sdk/workerEntry.ts @@ -245,13 +245,13 @@ export interface DecoWorkerEntryOptions { asJson?: boolean; /** - * Cross-origin origins allowed to read the `?renderJson` response. CORS only - * matters for browser clients on another origin — a native app needs none. - * - unset (default) → no CORS headers (same-origin + native apps only). - * - `string[]` → reflect a matching request Origin, with credentials. - * - `"*"` → allow any origin (without credentials, per the spec). + * Cross-origin access to the `?renderJson` response. It is public page data + * (secrets + request-derived keys are stripped), so it defaults to open. + * - unset (default) / `"*"` → any origin, without credentials (public API). + * - `string[]` → reflect a matching request Origin, *with* credentials. + * - `false` → no CORS headers (same-origin + native apps only). */ - pageJsonCors?: string[] | "*"; + pageJsonCors?: string[] | "*" | false; /** * Build a full segment key from the incoming request. @@ -885,25 +885,27 @@ let _redirectMapRevision: string | null = null; /** * CORS headers for the `?renderJson` response, from - * {@link DecoWorkerEntryOptions.pageJsonCors}. Off by default (native apps need - * no CORS); a list reflects an allow-listed request Origin with credentials; - * `"*"` allows any origin without credentials (per the CORS spec). + * {@link DecoWorkerEntryOptions.pageJsonCors}. `?renderJson` is public page data + * (secrets + request-derived keys are stripped), so the default is `"*"` — any + * origin, no credentials (the standard public-API CORS). `false` turns it off; + * a list reflects an allow-listed request Origin *with* credentials. */ function pageJsonCorsHeaders( request: Request, - cors: string[] | "*" | undefined, + cors: string[] | "*" | false | undefined, ): Record { - if (!cors) return {}; + if (cors === false) return {}; // explicitly disabled + const effective = cors ?? "*"; // default: any origin (no credentials) const base = { "Access-Control-Allow-Methods": "GET, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, If-None-Match, Authorization", "Access-Control-Expose-Headers": "ETag", }; - if (cors === "*") { + if (effective === "*") { return { ...base, "Access-Control-Allow-Origin": "*" }; } const origin = request.headers.get("origin"); - if (origin && cors.includes(origin)) { + if (origin && effective.includes(origin)) { return { ...base, "Access-Control-Allow-Origin": origin,