@rsc-kit/core 0.30.39 → 0.30.42

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/dist/hostCalls.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // function, the deferred-release ordering in streaming.ts still applies, and a
11
11
  // reply may still carry `revalidate` — a Go host that answers this endpoint
12
12
  // gets the same semantics the socket has, without the framing.
13
- import { headers as incomingHeaders, passSetCookies } from './request.js';
13
+ import { headers as incomingHeaders, passSetCookies, renderingAsNobody } from './request.js';
14
14
  import { revalidate } from './revalidate.js';
15
15
  import { ActionRefusal, ActionValidationError } from './action.js';
16
16
  import { redirect } from './redirect.js';
@@ -59,6 +59,11 @@ export function httpHostCalls(options) {
59
59
  'content-type': 'application/json',
60
60
  'x-rsc-host-secret': secret,
61
61
  };
62
+ // A page a shared cache may keep renders as nobody: no cookie, no
63
+ // authorization, so what the backend answers is the same for everyone.
64
+ // Asked before reading the request, so the read is not counted as one.
65
+ if (renderingAsNobody())
66
+ return headers;
62
67
  // Outside a render — a prerender pass, or a host calling one directly —
63
68
  // there is no request to forward from, and that is not an error. The call
64
69
  // simply carries no session, which is exactly what a build-time render
@@ -284,7 +289,9 @@ export function httpHostCalls(options) {
284
289
  function interpret(name, { status, reply, text, setCookies }) {
285
290
  // First, whatever the outcome: a login that then redirects still logged
286
291
  // the visitor in. In the caller's context, where the page's response is.
287
- if (setCookies?.length)
292
+ // Dropped as nobody: a cookie on a page kept for everyone is handed to
293
+ // everyone - and its Set-Cookie alone would make the page private.
294
+ if (setCookies?.length && !renderingAsNobody())
288
295
  passSetCookies(setCookies);
289
296
  // Refusing the input is not the call failing — it is the call answering.
290
297
  //
@@ -313,7 +320,13 @@ export function httpHostCalls(options) {
313
320
  // Raised as the engine's own redirect, so it travels the path every other
314
321
  // redirect travels — a real 3xx above a Suspense boundary, the digest
315
322
  // below one — rather than becoming an error the page has to interpret.
323
+ //
324
+ // What it revalidated is marked first: a backend that switched the team
325
+ // and redirected - Rsc::revalidate('all') then redirect() in PHP - goes
326
+ // to a page whose layout reads the team, and the redirect carries the
327
+ // revalidation to the client only if it was marked before the throw.
316
328
  if (reply?.redirect) {
329
+ markRevalidated(reply.revalidate);
317
330
  redirect(reply.redirect, reply.redirectStatus ?? 307);
318
331
  }
319
332
  if (reply?.unauthenticated) {
@@ -352,17 +365,20 @@ export function httpHostCalls(options) {
352
365
  if (reply === null) {
353
366
  throw new Error(`Host call ${JSON.stringify(name)} returned a body that is not JSON: ${text.slice(0, 200)}`);
354
367
  }
355
- if (reply.revalidate?.length) {
356
- if (onRevalidate)
357
- onRevalidate(reply.revalidate);
358
- // Named by the host over the wire - Rsc::revalidate('orders') in PHP -
359
- // so nothing here can check it; the renderer refuses an unknown name.
360
- else
361
- for (const target of reply.revalidate)
362
- revalidate(target);
363
- }
368
+ markRevalidated(reply.revalidate);
364
369
  return reply.result ?? null;
365
370
  }
371
+ function markRevalidated(targets) {
372
+ if (!targets?.length)
373
+ return;
374
+ if (onRevalidate)
375
+ onRevalidate(targets);
376
+ // Named by the host over the wire - Rsc::revalidate('orders') in PHP -
377
+ // so nothing here can check it; the renderer refuses an unknown name.
378
+ else
379
+ for (const target of targets)
380
+ revalidate(target);
381
+ }
366
382
  return async function hostCall(name, ...args) {
367
383
  const headers = await forwardedHeaders();
368
384
  const settled = await enqueue(name, args, headers);
@@ -1 +1 @@
1
- {"version":3,"file":"hostCalls.js","sourceRoot":"","sources":["../src/hostCalls.ts"],"names":[],"mappings":"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","sourcesContent":["// Host calls over HTTP, so a host in any language can answer them.\n//\n// A server component writing `await rpc('Orders.recent')` needs the host\n// mid-render. The Laravel path answers on a second unix socket with its own\n// framing (PROTOCOL.md, \"The callback channel\"), which every non-JS host has\n// to reimplement before it can render a single page that fetches anything.\n// This is the same conversation over an ordinary POST.\n//\n// The transport is the only thing that changes. The engine still sees one\n// function, the deferred-release ordering in streaming.ts still applies, and a\n// reply may still carry `revalidate` — a Go host that answers this endpoint\n// gets the same semantics the socket has, without the framing.\n\nimport { headers as incomingHeaders, passSetCookies } from './request.js'\nimport { revalidate } from './revalidate.js'\nimport type { RevalidateTarget } from './routes.js'\nimport { ActionRefusal, ActionValidationError } from './action.js'\nimport { redirect } from './redirect.js'\nimport { ServerAuthenticationError, ServerAuthorizationError } from './js/errors.js'\nimport { NotFoundSignal } from './notFound.js'\n\nexport interface HttpHostCallsOptions {\n /**\n * Where calls are POSTed. A loopback address or a unix socket url — see the\n * note on `secret` for why this must not be a public one.\n */\n endpoint: string\n /**\n * Sent as `X-RSC-Host-Secret`, and required rather than optional.\n *\n * This endpoint runs functions by name on behalf of a render. PROTOCOL.md\n * Part 3b draws the line at client-supplied input deciding what RUNS, and an\n * unauthenticated callback endpoint is on the wrong side of it: anyone who\n * can reach it can invoke any registered host function directly, with\n * whatever arguments they like and none of the app's routing in front. A\n * default of \"off\" would be the kind that ships.\n */\n secret: string\n /**\n * Request headers copied from the render's own request onto the call.\n *\n * This is what makes a host call run as the person browsing: the backend's\n * session middleware reads its cookie and finds the same user. Nothing else\n * is forwarded, because everything else is either meaningless to the backend\n * or actively wrong — `content-length` and `content-type` describe this POST,\n * not the page request.\n */\n forwardHeaders?: string[]\n /** Defaults to 30s. A render blocked on a host that never answers is a hung request. */\n timeoutMs?: number\n /** Injectable for tests and for a runtime whose fetch is not global. */\n fetch?: typeof fetch\n /**\n * What to do with whatever a reply reported as invalidated.\n *\n * Defaults to marking it on the engine, which is the only thing that makes\n * `Revalidate(ctx, \"orders\")` on the host side mean anything: an action's\n * answer carries the re-rendered region only if the target reached the\n * revalidation scope before the action returned. Without it a host reports\n * what it dirtied, nothing listens, and the browser is told nothing — the\n * page simply shows stale data with no error anywhere.\n */\n onRevalidate?: (targets: string[]) => void\n}\n\nconst DEFAULT_FORWARDED = ['cookie', 'authorization']\n\nexport interface HostCallReply {\n result?: unknown\n error?: string\n revalidate?: string[]\n /**\n * Field name to messages, when the host refused the input.\n *\n * The same shape everything else here already uses: Laravel's own\n * `$e->errors()`, the socket protocol's `validation_errors`, and what\n * `issuesToErrors` turns a Standard Schema result into. Dot-joined for a\n * nested field, the empty string for a message about the form rather than\n * any one field.\n */\n validationErrors?: Record<string, string[]>\n /** The caller has no session. Becomes the engine's own authentication error. */\n unauthenticated?: boolean\n /** The caller has a session and still may not. */\n unauthorized?: boolean\n /**\n * Where the host says this request should go instead.\n *\n * Answered with a 200 and this field, never as a 3xx: fetch follows a\n * redirect transparently, so a real one would send the host call itself to\n * the destination and hand whatever came back to the render as the\n * function's result.\n */\n redirect?: string\n /**\n * Where an unexpected failure happened in the backend, sent only in\n * development (Laravel's app.debug, Go's CallbackHandler.Debug): the\n * thrown type, its message, and frames newest first.\n */\n debug?: { type?: string; message?: string; trace?: string[] }\n /** The status to redirect with. Defaults to 307, which preserves the method. */\n redirectStatus?: number\n /**\n * The status a refusal should be answered with.\n *\n * A middleware that aborted meant what it aborted with — throttle answers\n * 429, a signed-url check 403 — and collapsing those to 500 makes a\n * rate-limited visitor indistinguishable from a broken server.\n */\n refusalStatus?: number\n /**\n * What a deliberate refusal carries beside its message (`error`): the\n * records blocking a delete, say. Raised as the action client's own\n * refusal, so an action passes it to the page as `result.refusal`, checked\n * against the schema the action declared - see `refuse()`.\n */\n refusalData?: unknown\n}\n\n/**\n * A batch on the wire: several calls in one POST, answered in order.\n *\n * Calls issued in the same tick of a render - sibling components each\n * awaiting rpc() - travel together, so a page's parallel reads cost the host\n * one request rather than one each. A host that has never heard of the\n * envelope answers it as a malformed single call, and the calls are sent one\n * at a time from then on; nothing is lost but the saving.\n */\nexport interface HostCallBatch {\n calls: { function: string; args: unknown[] }[]\n}\n\nexport interface HostCallBatchReply {\n /** One per call, in order. Each carries the status that call would have had. */\n replies: (HostCallReply & { status?: number })[]\n}\n\n/**\n * A batch answered as it goes: one line per call, each as the host finishes\n * it, in whatever order that is.\n *\n * `application/x-ndjson`, each line a reply with the call's `index` in the\n * batch. A host that answers this way lets a fast call resolve while a slow\n * sibling is still running - which is what keeps a page's boundaries\n * streaming independently when their reads travelled together. A host that\n * answers the whole batch at once, as one JSON object of `replies`, is read\n * as before; the saving of the batch stays, the independence does not.\n */\nexport type HostCallBatchLine = HostCallReply & { index: number; status?: number }\n\n/** How many calls one POST carries at most. */\nconst BATCH_LIMIT = 50\n\ntype Settled = { status: number; reply: HostCallReply | null; text: string; setCookies?: string[] }\n\ninterface Pending {\n name: string\n args: unknown[]\n resolve: (settled: Settled) => void\n reject: (error: unknown) => void\n}\n\ninterface Bucket {\n headers: Record<string, string>\n calls: Pending[]\n}\n\n// After the current I/O, before the next timer: what a render issued\n// synchronously - and across the microtasks between one component's awaits -\n// is in the bucket by then. setImmediate where the runtime has it, a zero\n// timer where it does not (a Worker).\nconst nextTick: (fn: () => void) => void =\n typeof setImmediate === 'function' ? (fn) => setImmediate(fn) : (fn) => setTimeout(fn, 0)\n\n/**\n * The function to hand to `installHostFn`, or to `hostCalls` on the JS host.\n */\n/**\n * The backend's trace, on the error the render sees.\n *\n * As its cause - an Error named for the backend's type, whose stack is the\n * backend's frames - so Node, Bun and the dev overlay print it under the\n * renderer's own stack; and appended to that stack, for whatever prints only\n * the one. Before this a failed rpc() pointed at a line of JavaScript, and the\n * PHP or Go that failed was a separate search through another log.\n */\nfunction withBackendTrace(failure: Error, debug: NonNullable<HostCallReply['debug']>): void {\n const frames = (debug.trace ?? []).map((frame) => ' at ' + frame).join('\\n')\n const cause = new Error(debug.message ?? '')\n\n cause.name = 'Backend ' + (debug.type ?? 'Error')\n cause.stack = cause.name + ': ' + (debug.message ?? '') + (frames ? '\\n' + frames : '')\n\n failure.cause = cause\n failure.stack = (failure.stack ?? String(failure)) + '\\nCaused in the backend by ' + cause.stack\n}\n\nexport function httpHostCalls(\n options: HttpHostCallsOptions,\n): (name: string, ...args: unknown[]) => Promise<unknown> {\n const {\n endpoint,\n secret,\n forwardHeaders = DEFAULT_FORWARDED,\n timeoutMs = 30_000,\n fetch: fetchImpl,\n onRevalidate,\n } = options\n\n if (!secret) {\n throw new Error('httpHostCalls requires a secret — see the note on the option.')\n }\n\n const forwarded = forwardHeaders.map((name) => name.toLowerCase())\n\n // One bucket per set of forwarded headers: two visitors' renders in flight\n // at once must not share a request, because the host reads the cookie off\n // the request to know whose session a call runs under.\n const buckets = new Map<string, Bucket>()\n\n async function forwardedHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n 'x-rsc-host-secret': secret,\n }\n\n // Outside a render — a prerender pass, or a host calling one directly —\n // there is no request to forward from, and that is not an error. The call\n // simply carries no session, which is exactly what a build-time render\n // should be doing.\n //\n // Only that one condition is swallowed. A blanket catch here is the wrong\n // shape: anything else going wrong while reading the request would come\n // out as a call with no session, which is not a failure the caller sees —\n // it is the visitor silently becoming anonymous, and the page rendering as\n // though they were logged out.\n try {\n const from = await incomingHeaders()\n\n for (const key of forwarded) {\n const value = from.get(key)\n if (value !== null) headers[key] = value\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n if (!message.startsWith('No request in scope')) throw error\n }\n\n return headers\n }\n\n /** One POST. `label` names what is being sent, for the error messages. */\n async function post(\n body: unknown,\n headers: Record<string, string>,\n label: string,\n ): Promise<{ status: number; text: string; setCookies: string[] }> {\n const response = await send(body, headers, label)\n\n // Read the body before branching on status: a host that reports the error\n // in JSON with a 500 is saying something more useful than \"500\", and\n // throwing on the status alone discards it.\n return {\n status: response.status,\n text: await response.text(),\n // A login's session cookie: kept to put on the page's response.\n setCookies: response.headers.getSetCookie?.() ?? [],\n }\n }\n\n /** The POST itself, headers in hand, body still to read. */\n async function send(body: unknown, headers: Record<string, string>, label: string): Promise<Response> {\n const doFetch = fetchImpl ?? globalThis.fetch\n\n // AbortSignal.timeout is not on every runtime this engine targets, so the\n // controller is written out rather than assumed.\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n\n let response: Response\n\n try {\n response = await doFetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (error) {\n if (controller.signal.aborted) {\n throw new Error(`Host call ${label} timed out after ${timeoutMs}ms`)\n }\n\n throw new Error(\n `Host call ${label} could not reach the host at ${endpoint}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n } finally {\n clearTimeout(timer)\n }\n\n return response\n }\n\n function parse(text: string): HostCallReply | null {\n try {\n return text ? (JSON.parse(text) as HostCallReply) : null\n } catch {\n return null\n }\n }\n\n async function single(name: string, args: unknown[], headers: Record<string, string>): Promise<Settled> {\n const { status, text, setCookies } = await post({ function: name, args }, headers, JSON.stringify(name))\n\n return { status, reply: parse(text), text, setCookies }\n }\n\n /** Read a streamed batch, one reply per line, resolving each call as its line lands. */\n async function resolveAsLinesArrive(body: ReadableStream<Uint8Array>, calls: Pending[]): Promise<void> {\n const answered = new Set<number>()\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffered = ''\n\n const take = (line: string) => {\n const trimmed = line.trim()\n\n if (!trimmed) return\n\n let parsed: Partial<HostCallBatchLine> | null\n\n try {\n parsed = JSON.parse(trimmed) as Partial<HostCallBatchLine>\n } catch {\n return\n }\n\n const index = parsed?.index\n\n if (typeof index !== 'number' || !calls[index] || answered.has(index)) return\n\n const { index: _, status: own, ...reply } = parsed as HostCallBatchLine\n\n answered.add(index)\n calls[index]!.resolve({ status: own ?? 200, reply, text: JSON.stringify(reply) })\n }\n\n try {\n for (;;) {\n const { done, value } = await reader.read()\n\n if (done) break\n\n buffered += decoder.decode(value, { stream: true })\n\n let at: number\n\n while ((at = buffered.indexOf('\\n')) !== -1) {\n take(buffered.slice(0, at))\n buffered = buffered.slice(at + 1)\n }\n }\n\n buffered += decoder.decode()\n take(buffered)\n } catch (error) {\n for (const [i, call] of calls.entries()) {\n if (!answered.has(i)) call.reject(error)\n }\n\n return\n }\n\n // The host closed the batch with a call unanswered: a fault on its side,\n // reported to the call rather than left pending forever.\n for (const [i, call] of calls.entries()) {\n if (!answered.has(i)) {\n call.reject(new Error(`Host call ${JSON.stringify(call.name)} was not answered in its batch`))\n }\n }\n }\n\n /**\n * Send a bucket. One call goes as itself, so a host that speaks only the\n * single shape - or a request that happened to be alone - sees nothing\n * new on the wire.\n */\n async function flush(bucket: Bucket): Promise<void> {\n const { headers, calls } = bucket\n\n if (calls.length === 1) {\n const [only] = calls\n\n try {\n only.resolve(await single(only.name, only.args, headers))\n } catch (error) {\n only.reject(error)\n }\n\n return\n }\n\n const label = `batch of ${calls.length} (${calls.map((c) => c.name).join(', ')})`\n let response: Response\n\n try {\n response = await send(\n { calls: calls.map((c) => ({ function: c.name, args: c.args })) } satisfies HostCallBatch,\n headers,\n label,\n )\n } catch (error) {\n // The host could not be reached, or did not answer in time. Not\n // re-sent one by one: the calls may have run, and an action run twice\n // is worse than one reported as failed.\n for (const call of calls) call.reject(error)\n\n return\n }\n\n // Answered as it goes: each call resolves the moment its line arrives,\n // so a component waiting on a fast read paints while a slow sibling's\n // is still running. The batch saved the round trips; this keeps the\n // streaming the batch would otherwise have cost.\n if (response.status < 400 && (response.headers.get('content-type') ?? '').includes('x-ndjson') && response.body) {\n await resolveAsLinesArrive(response.body, calls)\n\n return\n }\n\n const status = response.status\n const text = await response.text()\n const parsed = parse(text) as Partial<HostCallBatchReply> | null\n const replies = parsed?.replies\n\n if (status < 400 && Array.isArray(replies) && replies.length === calls.length) {\n calls.forEach((call, i) => {\n const { status: own, ...reply } = replies[i]!\n\n call.resolve({ status: own ?? 200, reply, text: JSON.stringify(reply) })\n })\n\n return\n }\n\n // Not a batch answer. Taking batches is part of the protocol - the\n // conformance suite checks it - so this is a backend that does not, or\n // one that failed before reading the calls: wrong secret, down, too many.\n // Every call fails with what the backend said, and none is sent again:\n // they may have run, and an action run twice is worse than one reported.\n const said = parsed && typeof (parsed as HostCallReply).error === 'string' ? (parsed as HostCallReply).error : text.slice(0, 200)\n const failure = new Error(\n `Host call ${label} was not answered as a batch (${status}${said ? `: ${said}` : ''}). ` +\n 'A backend answers { \"calls\": [...] } line by line or as { \"replies\": [...] }: ' +\n 'https://docs.rsc-kit.dev/hosts/your-own-backend#batches',\n )\n\n for (const call of calls) call.reject(failure)\n }\n\n function enqueue(name: string, args: unknown[], headers: Record<string, string>): Promise<Settled> {\n return new Promise<Settled>((resolve, reject) => {\n const key = JSON.stringify(headers)\n let bucket = buckets.get(key)\n\n if (!bucket) {\n bucket = { headers, calls: [] }\n buckets.set(key, bucket)\n\n const mine = bucket\n\n nextTick(() => {\n if (buckets.get(key) === mine) buckets.delete(key)\n\n void flush(mine)\n })\n }\n\n bucket.calls.push({ name, args, resolve, reject })\n\n if (bucket.calls.length >= BATCH_LIMIT) {\n buckets.delete(key)\n\n void flush(bucket)\n }\n })\n }\n\n /**\n * What a reply means, in the caller's own async context.\n *\n * Deliberately not done where the response arrives: a batch is answered in\n * a timer, outside every caller's request scope, and `redirect()` and\n * `revalidate()` both write to that scope. Interpreting here, after the\n * caller's await resumed, puts each reply back inside the render it belongs\n * to.\n */\n function interpret(name: string, { status, reply, text, setCookies }: Settled): unknown {\n // First, whatever the outcome: a login that then redirects still logged\n // the visitor in. In the caller's context, where the page's response is.\n if (setCookies?.length) passSetCookies(setCookies)\n\n // Refusing the input is not the call failing — it is the call answering.\n //\n // Thrown rather than returned, so a handler stops where it is instead of\n // carrying on with data the host rejected. createActionClient catches this\n // on the way out and returns { validationErrors }, which is the shape\n // useForm reads: React serialises a REJECTED server action opaquely, so a\n // validation error that stays thrown reaches the browser as \"an error\n // occurred\" with every field it named gone.\n //\n // Checked before `error`, so a host that sends both is read as a refusal\n // rather than as a failure with no fields.\n //\n // One error for both places a call is made. In an action, the mark is\n // what returns the fields to the form. In a page's render there is no\n // form: refusalStatus makes the page answer 422 rather than 500, and the\n // message says which call refused which fields, where \"Validation failed\"\n // read like an action had run.\n if (reply?.validationErrors) {\n const refused = new ActionValidationError(reply.validationErrors)\n const fields = Object.keys(reply.validationErrors).map((field) => field || '(the input as a whole)')\n\n refused.message = `Host call ${JSON.stringify(name)} refused its input: ${fields.join(', ')}`\n ;(refused as ActionValidationError & { refusalStatus?: number }).refusalStatus = 422\n\n throw refused\n }\n\n // Raised as the engine's own redirect, so it travels the path every other\n // redirect travels — a real 3xx above a Suspense boundary, the digest\n // below one — rather than becoming an error the page has to interpret.\n if (reply?.redirect) {\n redirect(reply.redirect as never, reply.redirectStatus ?? 307)\n }\n\n if (reply?.unauthenticated) {\n throw new ServerAuthenticationError(reply.error ?? 'Unauthenticated.')\n }\n\n if (reply?.unauthorized) {\n throw new ServerAuthorizationError(reply.error ?? 'This action is unauthorized.')\n }\n\n // The backend says there is no such thing - Go's Refuse(404), Laravel's\n // abort(404), a record this caller cannot see. Raised as the engine's\n // own notFound(), so the page answers with not-found.tsx and a 404,\n // exactly as a component calling notFound() would; as an error it\n // reached the page as \"something went wrong\". The backend's message rides\n // on it for an action, which has no not-found page and shows the message.\n if (reply?.refusalStatus === 404) throw new NotFoundSignal(reply.error ?? 'Not found')\n\n // A status is what makes a reply a refusal rather than a failure: the\n // backend's refuse(), Go's Refuse, Laravel's abort(). Its message was\n // written for the person asking - Laravel shows an HttpException's in its\n // own production JSON - so it is raised as one, with or without data, and\n // onError never replaces it. Only refusals carried data at first, and\n // Refuse(503, \"busy\") reached the form as \"Something went wrong.\".\n if (reply?.error !== undefined && (reply.refusalStatus !== undefined || reply.refusalData !== undefined)) {\n throw new ActionRefusal(reply.error, reply.refusalData, reply.refusalStatus ?? 409)\n }\n\n if (reply?.error !== undefined) {\n const failure = new Error(`Host call ${JSON.stringify(name)} failed: ${reply.error}`)\n\n if (reply.debug) withBackendTrace(failure, reply.debug)\n\n throw failure\n }\n\n if (status >= 400) {\n throw new Error(\n `Host call ${JSON.stringify(name)} failed: ${status}`.trim() +\n (text ? ` — ${text.slice(0, 200)}` : ''),\n )\n }\n\n if (reply === null) {\n throw new Error(\n `Host call ${JSON.stringify(name)} returned a body that is not JSON: ${text.slice(0, 200)}`,\n )\n }\n\n if (reply.revalidate?.length) {\n if (onRevalidate) onRevalidate(reply.revalidate)\n // Named by the host over the wire - Rsc::revalidate('orders') in PHP -\n // so nothing here can check it; the renderer refuses an unknown name.\n else for (const target of reply.revalidate) revalidate(target as RevalidateTarget)\n }\n\n return reply.result ?? null\n }\n\n return async function hostCall(name: string, ...args: unknown[]): Promise<unknown> {\n const headers = await forwardedHeaders()\n const settled = await enqueue(name, args, headers)\n\n return interpret(name, settled)\n }\n}\n"]}
1
+ {"version":3,"file":"hostCalls.js","sourceRoot":"","sources":["../src/hostCalls.ts"],"names":[],"mappings":"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","sourcesContent":["// Host calls over HTTP, so a host in any language can answer them.\n//\n// A server component writing `await rpc('Orders.recent')` needs the host\n// mid-render. The Laravel path answers on a second unix socket with its own\n// framing (PROTOCOL.md, \"The callback channel\"), which every non-JS host has\n// to reimplement before it can render a single page that fetches anything.\n// This is the same conversation over an ordinary POST.\n//\n// The transport is the only thing that changes. The engine still sees one\n// function, the deferred-release ordering in streaming.ts still applies, and a\n// reply may still carry `revalidate` — a Go host that answers this endpoint\n// gets the same semantics the socket has, without the framing.\n\nimport { headers as incomingHeaders, passSetCookies, renderingAsNobody } from './request.js'\nimport { revalidate } from './revalidate.js'\nimport type { RevalidateTarget } from './routes.js'\nimport { ActionRefusal, ActionValidationError } from './action.js'\nimport { redirect } from './redirect.js'\nimport { ServerAuthenticationError, ServerAuthorizationError } from './js/errors.js'\nimport { NotFoundSignal } from './notFound.js'\n\nexport interface HttpHostCallsOptions {\n /**\n * Where calls are POSTed. A loopback address or a unix socket url — see the\n * note on `secret` for why this must not be a public one.\n */\n endpoint: string\n /**\n * Sent as `X-RSC-Host-Secret`, and required rather than optional.\n *\n * This endpoint runs functions by name on behalf of a render. PROTOCOL.md\n * Part 3b draws the line at client-supplied input deciding what RUNS, and an\n * unauthenticated callback endpoint is on the wrong side of it: anyone who\n * can reach it can invoke any registered host function directly, with\n * whatever arguments they like and none of the app's routing in front. A\n * default of \"off\" would be the kind that ships.\n */\n secret: string\n /**\n * Request headers copied from the render's own request onto the call.\n *\n * This is what makes a host call run as the person browsing: the backend's\n * session middleware reads its cookie and finds the same user. Nothing else\n * is forwarded, because everything else is either meaningless to the backend\n * or actively wrong — `content-length` and `content-type` describe this POST,\n * not the page request.\n */\n forwardHeaders?: string[]\n /** Defaults to 30s. A render blocked on a host that never answers is a hung request. */\n timeoutMs?: number\n /** Injectable for tests and for a runtime whose fetch is not global. */\n fetch?: typeof fetch\n /**\n * What to do with whatever a reply reported as invalidated.\n *\n * Defaults to marking it on the engine, which is the only thing that makes\n * `Revalidate(ctx, \"orders\")` on the host side mean anything: an action's\n * answer carries the re-rendered region only if the target reached the\n * revalidation scope before the action returned. Without it a host reports\n * what it dirtied, nothing listens, and the browser is told nothing — the\n * page simply shows stale data with no error anywhere.\n */\n onRevalidate?: (targets: string[]) => void\n}\n\nconst DEFAULT_FORWARDED = ['cookie', 'authorization']\n\nexport interface HostCallReply {\n result?: unknown\n error?: string\n revalidate?: string[]\n /**\n * Field name to messages, when the host refused the input.\n *\n * The same shape everything else here already uses: Laravel's own\n * `$e->errors()`, the socket protocol's `validation_errors`, and what\n * `issuesToErrors` turns a Standard Schema result into. Dot-joined for a\n * nested field, the empty string for a message about the form rather than\n * any one field.\n */\n validationErrors?: Record<string, string[]>\n /** The caller has no session. Becomes the engine's own authentication error. */\n unauthenticated?: boolean\n /** The caller has a session and still may not. */\n unauthorized?: boolean\n /**\n * Where the host says this request should go instead.\n *\n * Answered with a 200 and this field, never as a 3xx: fetch follows a\n * redirect transparently, so a real one would send the host call itself to\n * the destination and hand whatever came back to the render as the\n * function's result.\n */\n redirect?: string\n /**\n * Where an unexpected failure happened in the backend, sent only in\n * development (Laravel's app.debug, Go's CallbackHandler.Debug): the\n * thrown type, its message, and frames newest first.\n */\n debug?: { type?: string; message?: string; trace?: string[] }\n /** The status to redirect with. Defaults to 307, which preserves the method. */\n redirectStatus?: number\n /**\n * The status a refusal should be answered with.\n *\n * A middleware that aborted meant what it aborted with — throttle answers\n * 429, a signed-url check 403 — and collapsing those to 500 makes a\n * rate-limited visitor indistinguishable from a broken server.\n */\n refusalStatus?: number\n /**\n * What a deliberate refusal carries beside its message (`error`): the\n * records blocking a delete, say. Raised as the action client's own\n * refusal, so an action passes it to the page as `result.refusal`, checked\n * against the schema the action declared - see `refuse()`.\n */\n refusalData?: unknown\n}\n\n/**\n * A batch on the wire: several calls in one POST, answered in order.\n *\n * Calls issued in the same tick of a render - sibling components each\n * awaiting rpc() - travel together, so a page's parallel reads cost the host\n * one request rather than one each. A host that has never heard of the\n * envelope answers it as a malformed single call, and the calls are sent one\n * at a time from then on; nothing is lost but the saving.\n */\nexport interface HostCallBatch {\n calls: { function: string; args: unknown[] }[]\n}\n\nexport interface HostCallBatchReply {\n /** One per call, in order. Each carries the status that call would have had. */\n replies: (HostCallReply & { status?: number })[]\n}\n\n/**\n * A batch answered as it goes: one line per call, each as the host finishes\n * it, in whatever order that is.\n *\n * `application/x-ndjson`, each line a reply with the call's `index` in the\n * batch. A host that answers this way lets a fast call resolve while a slow\n * sibling is still running - which is what keeps a page's boundaries\n * streaming independently when their reads travelled together. A host that\n * answers the whole batch at once, as one JSON object of `replies`, is read\n * as before; the saving of the batch stays, the independence does not.\n */\nexport type HostCallBatchLine = HostCallReply & { index: number; status?: number }\n\n/** How many calls one POST carries at most. */\nconst BATCH_LIMIT = 50\n\ntype Settled = { status: number; reply: HostCallReply | null; text: string; setCookies?: string[] }\n\ninterface Pending {\n name: string\n args: unknown[]\n resolve: (settled: Settled) => void\n reject: (error: unknown) => void\n}\n\ninterface Bucket {\n headers: Record<string, string>\n calls: Pending[]\n}\n\n// After the current I/O, before the next timer: what a render issued\n// synchronously - and across the microtasks between one component's awaits -\n// is in the bucket by then. setImmediate where the runtime has it, a zero\n// timer where it does not (a Worker).\nconst nextTick: (fn: () => void) => void =\n typeof setImmediate === 'function' ? (fn) => setImmediate(fn) : (fn) => setTimeout(fn, 0)\n\n/**\n * The function to hand to `installHostFn`, or to `hostCalls` on the JS host.\n */\n/**\n * The backend's trace, on the error the render sees.\n *\n * As its cause - an Error named for the backend's type, whose stack is the\n * backend's frames - so Node, Bun and the dev overlay print it under the\n * renderer's own stack; and appended to that stack, for whatever prints only\n * the one. Before this a failed rpc() pointed at a line of JavaScript, and the\n * PHP or Go that failed was a separate search through another log.\n */\nfunction withBackendTrace(failure: Error, debug: NonNullable<HostCallReply['debug']>): void {\n const frames = (debug.trace ?? []).map((frame) => ' at ' + frame).join('\\n')\n const cause = new Error(debug.message ?? '')\n\n cause.name = 'Backend ' + (debug.type ?? 'Error')\n cause.stack = cause.name + ': ' + (debug.message ?? '') + (frames ? '\\n' + frames : '')\n\n failure.cause = cause\n failure.stack = (failure.stack ?? String(failure)) + '\\nCaused in the backend by ' + cause.stack\n}\n\nexport function httpHostCalls(\n options: HttpHostCallsOptions,\n): (name: string, ...args: unknown[]) => Promise<unknown> {\n const {\n endpoint,\n secret,\n forwardHeaders = DEFAULT_FORWARDED,\n timeoutMs = 30_000,\n fetch: fetchImpl,\n onRevalidate,\n } = options\n\n if (!secret) {\n throw new Error('httpHostCalls requires a secret — see the note on the option.')\n }\n\n const forwarded = forwardHeaders.map((name) => name.toLowerCase())\n\n // One bucket per set of forwarded headers: two visitors' renders in flight\n // at once must not share a request, because the host reads the cookie off\n // the request to know whose session a call runs under.\n const buckets = new Map<string, Bucket>()\n\n async function forwardedHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n 'x-rsc-host-secret': secret,\n }\n\n // A page a shared cache may keep renders as nobody: no cookie, no\n // authorization, so what the backend answers is the same for everyone.\n // Asked before reading the request, so the read is not counted as one.\n if (renderingAsNobody()) return headers\n\n // Outside a render — a prerender pass, or a host calling one directly —\n // there is no request to forward from, and that is not an error. The call\n // simply carries no session, which is exactly what a build-time render\n // should be doing.\n //\n // Only that one condition is swallowed. A blanket catch here is the wrong\n // shape: anything else going wrong while reading the request would come\n // out as a call with no session, which is not a failure the caller sees —\n // it is the visitor silently becoming anonymous, and the page rendering as\n // though they were logged out.\n try {\n const from = await incomingHeaders()\n\n for (const key of forwarded) {\n const value = from.get(key)\n if (value !== null) headers[key] = value\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n if (!message.startsWith('No request in scope')) throw error\n }\n\n return headers\n }\n\n /** One POST. `label` names what is being sent, for the error messages. */\n async function post(\n body: unknown,\n headers: Record<string, string>,\n label: string,\n ): Promise<{ status: number; text: string; setCookies: string[] }> {\n const response = await send(body, headers, label)\n\n // Read the body before branching on status: a host that reports the error\n // in JSON with a 500 is saying something more useful than \"500\", and\n // throwing on the status alone discards it.\n return {\n status: response.status,\n text: await response.text(),\n // A login's session cookie: kept to put on the page's response.\n setCookies: response.headers.getSetCookie?.() ?? [],\n }\n }\n\n /** The POST itself, headers in hand, body still to read. */\n async function send(body: unknown, headers: Record<string, string>, label: string): Promise<Response> {\n const doFetch = fetchImpl ?? globalThis.fetch\n\n // AbortSignal.timeout is not on every runtime this engine targets, so the\n // controller is written out rather than assumed.\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n\n let response: Response\n\n try {\n response = await doFetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (error) {\n if (controller.signal.aborted) {\n throw new Error(`Host call ${label} timed out after ${timeoutMs}ms`)\n }\n\n throw new Error(\n `Host call ${label} could not reach the host at ${endpoint}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n } finally {\n clearTimeout(timer)\n }\n\n return response\n }\n\n function parse(text: string): HostCallReply | null {\n try {\n return text ? (JSON.parse(text) as HostCallReply) : null\n } catch {\n return null\n }\n }\n\n async function single(name: string, args: unknown[], headers: Record<string, string>): Promise<Settled> {\n const { status, text, setCookies } = await post({ function: name, args }, headers, JSON.stringify(name))\n\n return { status, reply: parse(text), text, setCookies }\n }\n\n /** Read a streamed batch, one reply per line, resolving each call as its line lands. */\n async function resolveAsLinesArrive(body: ReadableStream<Uint8Array>, calls: Pending[]): Promise<void> {\n const answered = new Set<number>()\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffered = ''\n\n const take = (line: string) => {\n const trimmed = line.trim()\n\n if (!trimmed) return\n\n let parsed: Partial<HostCallBatchLine> | null\n\n try {\n parsed = JSON.parse(trimmed) as Partial<HostCallBatchLine>\n } catch {\n return\n }\n\n const index = parsed?.index\n\n if (typeof index !== 'number' || !calls[index] || answered.has(index)) return\n\n const { index: _, status: own, ...reply } = parsed as HostCallBatchLine\n\n answered.add(index)\n calls[index]!.resolve({ status: own ?? 200, reply, text: JSON.stringify(reply) })\n }\n\n try {\n for (;;) {\n const { done, value } = await reader.read()\n\n if (done) break\n\n buffered += decoder.decode(value, { stream: true })\n\n let at: number\n\n while ((at = buffered.indexOf('\\n')) !== -1) {\n take(buffered.slice(0, at))\n buffered = buffered.slice(at + 1)\n }\n }\n\n buffered += decoder.decode()\n take(buffered)\n } catch (error) {\n for (const [i, call] of calls.entries()) {\n if (!answered.has(i)) call.reject(error)\n }\n\n return\n }\n\n // The host closed the batch with a call unanswered: a fault on its side,\n // reported to the call rather than left pending forever.\n for (const [i, call] of calls.entries()) {\n if (!answered.has(i)) {\n call.reject(new Error(`Host call ${JSON.stringify(call.name)} was not answered in its batch`))\n }\n }\n }\n\n /**\n * Send a bucket. One call goes as itself, so a host that speaks only the\n * single shape - or a request that happened to be alone - sees nothing\n * new on the wire.\n */\n async function flush(bucket: Bucket): Promise<void> {\n const { headers, calls } = bucket\n\n if (calls.length === 1) {\n const [only] = calls\n\n try {\n only.resolve(await single(only.name, only.args, headers))\n } catch (error) {\n only.reject(error)\n }\n\n return\n }\n\n const label = `batch of ${calls.length} (${calls.map((c) => c.name).join(', ')})`\n let response: Response\n\n try {\n response = await send(\n { calls: calls.map((c) => ({ function: c.name, args: c.args })) } satisfies HostCallBatch,\n headers,\n label,\n )\n } catch (error) {\n // The host could not be reached, or did not answer in time. Not\n // re-sent one by one: the calls may have run, and an action run twice\n // is worse than one reported as failed.\n for (const call of calls) call.reject(error)\n\n return\n }\n\n // Answered as it goes: each call resolves the moment its line arrives,\n // so a component waiting on a fast read paints while a slow sibling's\n // is still running. The batch saved the round trips; this keeps the\n // streaming the batch would otherwise have cost.\n if (response.status < 400 && (response.headers.get('content-type') ?? '').includes('x-ndjson') && response.body) {\n await resolveAsLinesArrive(response.body, calls)\n\n return\n }\n\n const status = response.status\n const text = await response.text()\n const parsed = parse(text) as Partial<HostCallBatchReply> | null\n const replies = parsed?.replies\n\n if (status < 400 && Array.isArray(replies) && replies.length === calls.length) {\n calls.forEach((call, i) => {\n const { status: own, ...reply } = replies[i]!\n\n call.resolve({ status: own ?? 200, reply, text: JSON.stringify(reply) })\n })\n\n return\n }\n\n // Not a batch answer. Taking batches is part of the protocol - the\n // conformance suite checks it - so this is a backend that does not, or\n // one that failed before reading the calls: wrong secret, down, too many.\n // Every call fails with what the backend said, and none is sent again:\n // they may have run, and an action run twice is worse than one reported.\n const said = parsed && typeof (parsed as HostCallReply).error === 'string' ? (parsed as HostCallReply).error : text.slice(0, 200)\n const failure = new Error(\n `Host call ${label} was not answered as a batch (${status}${said ? `: ${said}` : ''}). ` +\n 'A backend answers { \"calls\": [...] } line by line or as { \"replies\": [...] }: ' +\n 'https://docs.rsc-kit.dev/hosts/your-own-backend#batches',\n )\n\n for (const call of calls) call.reject(failure)\n }\n\n function enqueue(name: string, args: unknown[], headers: Record<string, string>): Promise<Settled> {\n return new Promise<Settled>((resolve, reject) => {\n const key = JSON.stringify(headers)\n let bucket = buckets.get(key)\n\n if (!bucket) {\n bucket = { headers, calls: [] }\n buckets.set(key, bucket)\n\n const mine = bucket\n\n nextTick(() => {\n if (buckets.get(key) === mine) buckets.delete(key)\n\n void flush(mine)\n })\n }\n\n bucket.calls.push({ name, args, resolve, reject })\n\n if (bucket.calls.length >= BATCH_LIMIT) {\n buckets.delete(key)\n\n void flush(bucket)\n }\n })\n }\n\n /**\n * What a reply means, in the caller's own async context.\n *\n * Deliberately not done where the response arrives: a batch is answered in\n * a timer, outside every caller's request scope, and `redirect()` and\n * `revalidate()` both write to that scope. Interpreting here, after the\n * caller's await resumed, puts each reply back inside the render it belongs\n * to.\n */\n function interpret(name: string, { status, reply, text, setCookies }: Settled): unknown {\n // First, whatever the outcome: a login that then redirects still logged\n // the visitor in. In the caller's context, where the page's response is.\n // Dropped as nobody: a cookie on a page kept for everyone is handed to\n // everyone - and its Set-Cookie alone would make the page private.\n if (setCookies?.length && !renderingAsNobody()) passSetCookies(setCookies)\n\n // Refusing the input is not the call failing — it is the call answering.\n //\n // Thrown rather than returned, so a handler stops where it is instead of\n // carrying on with data the host rejected. createActionClient catches this\n // on the way out and returns { validationErrors }, which is the shape\n // useForm reads: React serialises a REJECTED server action opaquely, so a\n // validation error that stays thrown reaches the browser as \"an error\n // occurred\" with every field it named gone.\n //\n // Checked before `error`, so a host that sends both is read as a refusal\n // rather than as a failure with no fields.\n //\n // One error for both places a call is made. In an action, the mark is\n // what returns the fields to the form. In a page's render there is no\n // form: refusalStatus makes the page answer 422 rather than 500, and the\n // message says which call refused which fields, where \"Validation failed\"\n // read like an action had run.\n if (reply?.validationErrors) {\n const refused = new ActionValidationError(reply.validationErrors)\n const fields = Object.keys(reply.validationErrors).map((field) => field || '(the input as a whole)')\n\n refused.message = `Host call ${JSON.stringify(name)} refused its input: ${fields.join(', ')}`\n ;(refused as ActionValidationError & { refusalStatus?: number }).refusalStatus = 422\n\n throw refused\n }\n\n // Raised as the engine's own redirect, so it travels the path every other\n // redirect travels — a real 3xx above a Suspense boundary, the digest\n // below one — rather than becoming an error the page has to interpret.\n //\n // What it revalidated is marked first: a backend that switched the team\n // and redirected - Rsc::revalidate('all') then redirect() in PHP - goes\n // to a page whose layout reads the team, and the redirect carries the\n // revalidation to the client only if it was marked before the throw.\n if (reply?.redirect) {\n markRevalidated(reply.revalidate)\n redirect(reply.redirect as never, reply.redirectStatus ?? 307)\n }\n\n if (reply?.unauthenticated) {\n throw new ServerAuthenticationError(reply.error ?? 'Unauthenticated.')\n }\n\n if (reply?.unauthorized) {\n throw new ServerAuthorizationError(reply.error ?? 'This action is unauthorized.')\n }\n\n // The backend says there is no such thing - Go's Refuse(404), Laravel's\n // abort(404), a record this caller cannot see. Raised as the engine's\n // own notFound(), so the page answers with not-found.tsx and a 404,\n // exactly as a component calling notFound() would; as an error it\n // reached the page as \"something went wrong\". The backend's message rides\n // on it for an action, which has no not-found page and shows the message.\n if (reply?.refusalStatus === 404) throw new NotFoundSignal(reply.error ?? 'Not found')\n\n // A status is what makes a reply a refusal rather than a failure: the\n // backend's refuse(), Go's Refuse, Laravel's abort(). Its message was\n // written for the person asking - Laravel shows an HttpException's in its\n // own production JSON - so it is raised as one, with or without data, and\n // onError never replaces it. Only refusals carried data at first, and\n // Refuse(503, \"busy\") reached the form as \"Something went wrong.\".\n if (reply?.error !== undefined && (reply.refusalStatus !== undefined || reply.refusalData !== undefined)) {\n throw new ActionRefusal(reply.error, reply.refusalData, reply.refusalStatus ?? 409)\n }\n\n if (reply?.error !== undefined) {\n const failure = new Error(`Host call ${JSON.stringify(name)} failed: ${reply.error}`)\n\n if (reply.debug) withBackendTrace(failure, reply.debug)\n\n throw failure\n }\n\n if (status >= 400) {\n throw new Error(\n `Host call ${JSON.stringify(name)} failed: ${status}`.trim() +\n (text ? ` — ${text.slice(0, 200)}` : ''),\n )\n }\n\n if (reply === null) {\n throw new Error(\n `Host call ${JSON.stringify(name)} returned a body that is not JSON: ${text.slice(0, 200)}`,\n )\n }\n\n markRevalidated(reply.revalidate)\n\n return reply.result ?? null\n }\n\n function markRevalidated(targets: string[] | undefined): void {\n if (!targets?.length) return\n if (onRevalidate) onRevalidate(targets)\n // Named by the host over the wire - Rsc::revalidate('orders') in PHP -\n // so nothing here can check it; the renderer refuses an unknown name.\n else for (const target of targets) revalidate(target as RevalidateTarget)\n }\n\n return async function hostCall(name: string, ...args: unknown[]): Promise<unknown> {\n const headers = await forwardedHeaders()\n const settled = await enqueue(name, args, headers)\n\n return interpret(name, settled)\n }\n}\n"]}
@@ -22,7 +22,7 @@ import { fetchPagePayload } from "./pagePayload.js";
22
22
  import { inlinePayload } from "./inlinePayload.js";
23
23
  import { claimRead, setQueryCodec } from "./queryClient.js";
24
24
  import { clearSegments, dropHidden, isHeld, prerenderSegment, restoreSegments, setSegment } from "./segmentStore.js";
25
- import { cancelPrefetch, forgetOtherPages, isPrefetched, navigate, setApiRoutes, refresh, applyRevalidations, prefetch, retentionKey, setCallServer, setHeldLayouts, setStaticPayloads, setStaticRoutes, seedStaticChain, payloadUrl, setDeserializer, setInterceptManifest, getHeldLayouts, setNavigateHandler, setHeldHandlers, setPrerenderHandler, setReplaceRootHandler, setRestoreHandler, setVersion, } from "./navigate.js";
25
+ import { cancelPrefetch, followActionRedirect, isPrefetched, navigate, setApiRoutes, refresh, applyRevalidations, prefetch, retentionKey, setCallServer, setHeldLayouts, setStaticPayloads, setStaticRoutes, seedStaticChain, bootPayloadUrl, payloadUrl, setDeserializer, setInterceptManifest, getHeldLayouts, setNavigateHandler, setHeldHandlers, setPrerenderHandler, setReplaceRootHandler, setRestoreHandler, setVersion, } from "./navigate.js";
26
26
  /**
27
27
  * A redirect that travelled as an error: the digest of the error itself, or
28
28
  * of the cause React wraps it in when it reports client-rendering a boundary
@@ -129,9 +129,8 @@ export async function createViteRscApp(container = document, interceptEntries =
129
129
  // back showed the page signed out until a reload. What a revalidating
130
130
  // action already drops, a redirecting one drops too.
131
131
  if (isSafeRedirect(err.location)) {
132
- forgetOtherPages();
133
132
  try {
134
- await navigate(err.location, { replace: true });
133
+ await followActionRedirect(err.location, err.revalidated);
135
134
  }
136
135
  catch {
137
136
  // Announced by the navigation itself, as rsc-navigate-error. The
@@ -223,7 +222,7 @@ export async function createViteRscApp(container = document, interceptEntries =
223
222
  const res = inlinePayload() ??
224
223
  (storedPayload
225
224
  ? await fetchPagePayload(storedPayload.getAttribute("content"), fetch, false)
226
- : await fetchPagePayload(payloadUrl(window.location.href)));
225
+ : await fetchPagePayload(bootPayloadUrl(window.location.href)));
227
226
  // Seed the router with the build this DOCUMENT is, so a redeploy is caught
228
227
  // on the next navigation: the host answers 409 to a client of another
229
228
  // build, and the document is loaded again. From the document's own meta
@@ -1 +1 @@
1
- {"version":3,"file":"createViteRscApp.js","sourceRoot":"","sources":["../../src/js/createViteRscApp.ts"],"names":[],"mappings":"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","sourcesContent":["// Vite-engine client bootstrap. Uses @vitejs/plugin-rsc's browser runtime as\n// the Flight deserializer + action encoder, and drives the package's engine-agnostic\n// navigate.ts SPA engine (Link, prefetch, popstate) through it. This replaces\n// the bun engine's createRscApp + the hand-rolled webpack shim — the plugin\n// resolves client references itself.\nimport { SEARCH_PARAMS_FALLBACK } from \"./useSearchParams.js\";\nimport { recoverFromStaleAssets } from \"./staleAssets.js\";\nimport { afterHydration, replayEarlyClicks } from \"./earlyClicks.js\";\nimport { parseRedirectDigest } from \"../redirectDigest.js\";\nimport { isNotFoundDigest } from \"../notFound.js\";\nimport { showDevNotice } from \"./devNotice.js\";\nimport { caughtByLoading } from \"./fallbackReport.js\";\nimport { noteNavigation } from \"./segmentStore.js\";\nimport type { Route } from \"../routes.js\";\nimport { isSafeRedirect } from \"../safeUrl.js\";\nimport {\n createFromReadableStream,\n encodeReply,\n setServerCallback,\n} from \"@vitejs/plugin-rsc/browser\";\nimport { createElement, startTransition } from \"react\";\nimport { createRoot, hydrateRoot } from \"react-dom/client\";\nimport { activityMarkersIn } from \"./activityMarkers.js\";\nimport { ActivityRoot } from \"./ActivityRouter.js\";\nimport {\n ActionRefusedError,\n ServerRedirectError,\n ServerValidationError,\n noteRedirected,\n refusalOf,\n throwForFailedAction,\n} from \"./errors.js\";\nimport { fetchPagePayload } from \"./pagePayload.js\";\nimport { inlinePayload } from \"./inlinePayload.js\";\nimport { claimRead, setQueryCodec } from \"./queryClient.js\";\nimport { clearSegments, dropHidden, isHeld, prerenderSegment, restoreSegments, setSegment } from \"./segmentStore.js\";\nimport type { ReactNode } from \"react\";\nimport {\n cancelPrefetch,\n forgetOtherPages,\n isPrefetched,\n navigate,\n setApiRoutes,\n refresh,\n applyRevalidations,\n prefetch,\n retentionKey,\n setCallServer,\n setHeldLayouts,\n setStaticPayloads,\n setStaticRoutes,\n seedStaticChain,\n payloadUrl,\n setDeserializer,\n setInterceptManifest,\n getHeldLayouts,\n setNavigateHandler,\n setHeldHandlers,\n setPrerenderHandler,\n setReplaceRootHandler,\n setRestoreHandler,\n setVersion,\n} from \"./navigate.js\";\n\n\n/**\n * A redirect that travelled as an error: the digest of the error itself, or\n * of the cause React wraps it in when it reports client-rendering a boundary\n * the server had already failed.\n */\nfunction isRedirect(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest;\n const cause = (error as { cause?: { digest?: unknown } } | null)?.cause;\n\n return (\n parseRedirectDigest(digest) !== null ||\n parseRedirectDigest(cause?.digest) !== null ||\n // A missing page decided inside a boundary is answered the same way, by\n // the same boundary, and is just as little a fault to report.\n isNotFoundDigest(digest) ||\n isNotFoundDigest(cause?.digest)\n );\n}\n\nexport async function createViteRscApp(\n container: Document | Element = document,\n interceptEntries: { urlPattern: string; slot: string }[] = [],\n options: { staticPayloads?: string | null; routes?: unknown[] | null; apiRoutes?: string[] } = {},\n): Promise<void> {\n // The urls a route.ts answers: a link to one is an anchor, never a\n // prefetch, never a payload fetch.\n if (options.apiRoutes) setApiRoutes(options.apiRoutes);\n // An exported build has no server to negotiate with, so payloads live at\n // their own urls rather than behind a header on the page's url.\n setStaticPayloads(options.staticPayloads ?? null);\n // Only an exported build ships this: with no server to negotiate with, the\n // client works out for itself how much of a page it still holds.\n if (options.routes) setStaticRoutes(options.routes as never);\n // The router has to recognise an intercepted link before it asks the server,\n // so the patterns are baked into the generated browser entry. Without them\n // every intercepted route falls through to a full-page navigation.\n setInterceptManifest(interceptEntries);\n\n async function callServer(id: string, args: unknown[]): Promise<unknown> {\n // A read opened a slot before calling the reference, and this is the first\n // point at which the id exists on the client — React keeps it private and\n // hands it over only here. Claiming the slot enqueues the read for the\n // batched GET instead of posting it.\n const read = claimRead(id, args);\n\n if (read) return await read;\n\n const encoded = await encodeReply(args);\n\n // encodeReply returns FormData as soon as an argument contains a File.\n // Sending that as multipart would make PHP consume php://input while\n // parsing it, leaving the action with an empty body — so serialize it to\n // raw bytes under an opaque content-type and send the real one in\n // X-RSC-Content-Type for the worker to rebuild FormData from.\n let body: BodyInit;\n let realContentType: string;\n\n if (encoded instanceof FormData) {\n const serialized = new Response(encoded);\n body = await serialized.arrayBuffer();\n realContentType =\n serialized.headers.get(\"content-type\") ?? \"multipart/form-data\";\n } else {\n body = encoded as BodyInit;\n realContentType = \"text/plain;charset=UTF-8\";\n }\n\n // Where the action was invoked from. The host resolves it to the\n // components that render the page, so anything the action says it\n // invalidated can come back with the answer instead of being fetched\n // afterwards - and what comes back is for this page, not whichever is\n // showing when it lands.\n const from = window.location.pathname + window.location.search;\n\n const res = await fetch(\"/_rsc/action\", {\n method: \"POST\",\n headers: {\n \"X-RSC-Action\": id,\n \"X-RSC-Referer\": from,\n \"X-RSC-Content-Type\": realContentType,\n \"Content-Type\": \"application/octet-stream\",\n \"X-XSRF-TOKEN\": decodeURIComponent(\n document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] ?? \"\",\n ),\n },\n body,\n });\n\n // A failed action does not answer with a Flight stream, and the decoder\n // cannot tell — it reports its own parse failure instead of what the\n // server said.\n try {\n await throwForFailedAction(res);\n } catch (err) {\n // A redirect is an instruction, not something for a form to display:\n // an expired session answers this way, and the destination is the login\n // page rather than a message about one.\n if (err instanceof ServerRedirectError) {\n // The destination came off a response header, so it is checked here\n // too — the engine refuses these at the source, but a proxy or a host\n // in front of it can write whatever it likes. A navigation, not a\n // document load: the layouts stay mounted and the history entry the\n // form was on is replaced, the way a redirect after a POST should\n // be - Back does not return to the submitted form.\n //\n // Awaited, so the action is still pending when the destination's\n // tree is handed to React: the two are one transition, committed\n // together. Not awaited, the action ended first - its optimistic\n // state dropped, pending went false - and the page it redirected to\n // arrived a frame later. A new chat's first message vanished from\n // the thread and came back with its reply.\n //\n // Asked of the server, never revealed from what the router holds. The\n // action may have changed anything the destination reads - a cookie,\n // a membership, a row - and a page kept from before it, or prefetched\n // before it, shows the world as it was: a sign-in that redirected\n // back showed the page signed out until a reload. What a revalidating\n // action already drops, a redirecting one drops too.\n if (isSafeRedirect(err.location)) {\n forgetOtherPages();\n\n try {\n await navigate(err.location as Route, { replace: true });\n } catch {\n // Announced by the navigation itself, as rsc-navigate-error. The\n // action did redirect; that is still its answer.\n }\n }\n\n // Performed, so resolved - not thrown. The call's answer is \"you are\n // being taken somewhere\": an object saying so, the shape an action's\n // result already has, so a caller reading result.validationErrors\n // reads undefined rather than throwing. A caller that awaited the\n // action in a plain startTransition had no catch for a throw, the\n // rejection reached React, and the root unmounted to a white page\n // on every logout.\n noteRedirected(err.location);\n\n return { redirected: err.location };\n }\n\n throw err;\n }\n\n const answer = await createFromReadableStream(res.body!, { callServer });\n const result = unwrapRevalidated(answer, from);\n\n // The backend turned the call down. Thrown, so the code after the await\n // does not take it for what it asked for; the message came in the answer\n // because React strips the message of anything the server throws.\n const refused = refusalOf(result);\n\n if (refused?.errors) throw new ServerValidationError(refused.message, refused.errors);\n if (refused) throw new ActionRefusedError(refused.message, refused.status);\n\n return result;\n }\n\n /**\n * Put anything the action re-rendered on screen, and hand back its result.\n *\n * The trees travel with the answer rather than being fetched afterwards, so\n * the caller sees only what its action returned and never knows the page\n * was updated around it.\n */\n function unwrapRevalidated(answer: unknown, from: string): unknown {\n if (\n answer === null ||\n typeof answer !== \"object\" ||\n !(\"__rscRevalidated\" in answer)\n ) {\n return answer;\n }\n\n const envelope = answer as {\n __rscRevalidated: Record<string, unknown>;\n result: unknown;\n };\n\n applyRevalidations(from, envelope.__rscRevalidated as Record<string, ReactNode>);\n\n return envelope.result;\n }\n\n setDeserializer(createFromReadableStream as never);\n // The query transport speaks Flight too, and reaches the runtime through here\n // rather than importing it: a second import of the browser runtime from the\n // client-component graph is a second client-reference registry, and the\n // symptom is components that render as undefined with nothing logged.\n setQueryCodec({\n deserialize: (stream) =>\n createFromReadableStream(stream, { callServer }) as Promise<unknown>,\n encode: (args) => encodeReply(args) as Promise<string | FormData>,\n // The way back when a read cannot ride in a url. Re-entering callServer is\n // safe: the slot was cleared the moment the reference returned, so this\n // takes the ordinary POST path rather than claiming a read again.\n asAction: (id, args) => callServer(id, args),\n });\n setCallServer(callServer);\n // The plugin's own \"use server\" client stubs route through its registered\n // server callback — register the same transport there too.\n setServerCallback(callServer as never);\n\n // Link / Form / router live in a separate build graph and reach the SPA\n // engine through these globals.\n (window as unknown as { __rsc_navigate: typeof navigate }).__rsc_navigate =\n navigate;\n (window as unknown as { __rsc_prefetch: typeof prefetch }).__rsc_prefetch =\n prefetch;\n (\n window as unknown as { __rsc_cancel_prefetch: typeof cancelPrefetch }\n ).__rsc_cancel_prefetch = cancelPrefetch;\n (\n window as unknown as { __rsc_is_prefetched: typeof isPrefetched }\n ).__rsc_is_prefetched = isPrefetched;\n (window as unknown as { __rsc_refresh: typeof refresh }).__rsc_refresh =\n refresh;\n\n // Hydrate from the RSC endpoint (same url + X-RSC, no version header).\n //\n // Everything from here to the decode is failure handling, because this is\n // the one request with nothing watching it. A page whose shell is already\n // rendered looks fine while this fails, and its fallbacks stay on screen\n // indefinitely with nothing reported anywhere.\n // The payload the document streamed in for itself, when it did - one\n // render per visit, see inlinePayload.ts. A page the build wrote whole\n // fetches its stored payload, as it always did.\n //\n // A page an edge may serve whole names its payload at a hashed asset url\n // (storedPayloadAsset in vite.ts), so booting it asks nothing of the app:\n // the same url with X-RSC would wake a sleeping one on every visit.\n const storedPayload = document.querySelector('meta[name=\"rsc-kit:payload\"]');\n const res =\n inlinePayload() ??\n (storedPayload\n ? await fetchPagePayload(storedPayload.getAttribute(\"content\")!, fetch, false)\n : await fetchPagePayload(payloadUrl(window.location.href)));\n\n // Seed the router with the build this DOCUMENT is, so a redeploy is caught\n // on the next navigation: the host answers 409 to a client of another\n // build, and the document is loaded again. From the document's own meta\n // first - a document a service worker served from its cache is the last\n // build's, while the payload just fetched is this one's, and seeding from\n // the payload would have the old client claim to be new. The header is\n // the fallback for a document with no meta.\n const documentBuild = document.querySelector('meta[name=\"rsc-kit:build\"]')?.getAttribute(\"content\");\n const servedVersion = documentBuild || res.headers.get(\"X-RSC-Version\");\n\n if (servedVersion) {\n setVersion(servedVersion);\n }\n\n // The chain this page is built from, so the next navigation can say what is\n // already mounted and be sent only what changed.\n // A server says what this page is built from; a file server says nothing,\n // so an exported build works it out from the table it was given.\n // An asset says nothing, so the document says it for its payload.\n const servedLayouts = res.headers.get(\"X-RSC-Layouts\") ?? storedPayload?.getAttribute(\"data-layouts\") ?? null;\n\n if (servedLayouts !== null) {\n setHeldLayouts(servedLayouts ? servedLayouts.split(\",\") : []);\n } else if (!seedStaticChain(window.location.href)) {\n setHeldLayouts([]);\n }\n const tree = await createFromReadableStream(res.body!, { callServer });\n\n // Retaining the previous page behind <Activity> needs a wrapper above the\n // page, and React will not hydrate a *document* container through one: the\n // root child of a document has to be <html>, and wrapping it hangs the\n // renderer outright (React 19.2.7). An app whose root layout owns <html>\n // therefore hydrates the tree directly and navigations replace it, as before.\n //\n // Apps that hydrate into an element get retention. Giving it to document-\n // rooted apps means SPA navigation returning only the changed segment rather\n // than a whole document, which is an engine change, not a client one.\n const retains = container !== document;\n\n const shell = retains\n ? createElement(ActivityRoot, {\n initialKey: retentionKey(window.location.href, null),\n initialTree: tree as ReactNode,\n })\n : (tree as ReactNode);\n\n // A document that cannot be hydrated by this tree is client-rendered\n // instead - a repaint, not a freeze. React 19.2 does not recover from a\n // hydration mismatch at an <Activity>: it retries hydrating the boundary,\n // mismatches again, and never returns to the main thread. Every segment\n // boundary is an Activity, one per layout, so a document with none where\n // the page has layouts is that mismatch, guaranteed, before React starts.\n // What produced one: a parameterised route's PPR shell, rendered without\n // a page key and, by a build before this one, without the Activities too.\n // None rather than fewer: a boundary inside a Suspense hole the shell left\n // unfinished has no marker and is client-rendered by React on its own,\n // which is not a mismatch. The stale-asset reload does not apply - the\n // document would come back the same.\n const missing = retains && getHeldLayouts().length > 0 && activityMarkersIn(container) === 0;\n\n if (missing) {\n console.warn(\n \"[rsc-kit] The document has no <Activity> boundaries where the page has layouts, so it cannot be hydrated; rendering the page instead. A PPR shell built by an earlier release lacks them - rebuild.\",\n );\n }\n\n // The payload answered with an error status - the not-found page for a\n // url the shell was served for anyway. It is not the document on screen,\n // so it is rendered, not hydrated: a mismatch at an Activity is the loop\n // above, and this one is certain.\n const disagrees = !res.ok;\n\n const onRecoverableError = (error: unknown, errorInfo: unknown) => {\n // A client component whose chunk the server no longer has fails while\n // hydrating, and React reports that as recoverable: the page is loaded\n // again, with the current names.\n if (recoverFromStaleAssets(error)) return;\n\n // A redirect decided inside a boundary: the server's row carried it as\n // the digest, React client-renders the boundary and reports that it\n // did, and the RedirectBoundary is already performing it. Nothing to\n // read in the console about a page doing what it was told.\n if (isRedirect(error)) return;\n\n // A PPR shell is served with its Suspense boundaries deliberately\n // unfinished — the build aborts the render once the static part is out.\n // React reports that as #419 and client-renders the boundary from the\n // Flight payload, which is the intended path, not a fault to report.\n const message = String((error as { message?: string })?.message ?? error);\n\n if (\n message.includes(\"419\") ||\n message.includes(\"did not finish this Suspense boundary\")\n ) {\n return;\n }\n\n // A view transition the browser cancelled because the tab was not\n // visible. React reports it here; the update still landed, and a tab\n // in the background is not a fault in the page.\n if (message.includes(\"Transition was aborted\")) return;\n\n // A navigation another one overtook: its request was aborted, and a\n // row of its payload that a component was still reading rejects with\n // the abort. React recovers by rendering the root again synchronously\n // and reports the recovery here as #520, with the abort as the cause.\n // The page it recovered to is the one the second navigation asked for;\n // the abort was the design, not a fault.\n const cause = (error as { cause?: { name?: string; message?: string } } | null)?.cause;\n\n if (\n cause?.name === \"AbortError\" ||\n /\\baborted\\b/i.test(cause?.message ?? \"\") ||\n /\\baborted\\b/i.test(message)\n ) {\n return;\n }\n // A page whose query string was read under a boundary carries the\n // fallback there; React reports the recovery on hydration. Under a\n // boundary the developer wrote, that is the designed path and nothing\n // is said. With nothing closer than a loading.tsx, the whole segment\n // showed the fallback until the query arrived - said here, in the\n // page and the console, for whoever is looking at either. Production\n // has no stack to tell the two apart; the build's note on the route is\n // the record there.\n if (\n (error as { digest?: string })?.digest === SEARCH_PARAMS_FALLBACK ||\n message.includes(\"useSearchParams()\")\n ) {\n const stack = (errorInfo as { componentStack?: string } | null)\n ?.componentStack;\n\n if (!caughtByLoading(stack)) return;\n\n const hint =\n \"useSearchParams() was read on the server with nothing closer than a loading.tsx, so the \" +\n \"whole segment showed that fallback until the query arrived. A <Suspense> around the \" +\n \"component that reads keeps the rest of the page painted.\";\n\n showDevNotice(hint, stack);\n console.error(new Error(hint), errorInfo);\n\n return;\n }\n\n console.error(error, errorInfo);\n };\n\n const rootOptions = {\n onRecoverableError,\n // A chunk the deploy no longer serves is not a fault in the page; the\n // document is loaded again, and the new names come with it. Anything\n // else is reported the way React would have.\n onUncaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n\n console.error(error, errorInfo);\n },\n onCaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n // Caught by the RedirectBoundary, which is performing it.\n if (isRedirect(error)) return;\n\n console.error(error, errorInfo);\n },\n };\n\n const root = missing || disagrees\n ? (() => {\n const created = createRoot(container as Element, rootOptions);\n\n created.render(shell);\n\n return created;\n })()\n : hydrateRoot(container, shell, rootOptions);\n\n // Depth 0 is a whole document and replaces the root. Anything deeper is one\n // segment: handing it to the boundary at that depth leaves the layouts above\n // it mounted, which is the point of asking for a partial render at all.\n setNavigateHandler((tree: unknown, key: string, segmentDepth: number, inPlaceOf?: string | null, keepsPrevious?: boolean) => {\n const newTree = tree as ReactNode;\n\n // From here on the boundary animates; the seed commit before this did not.\n noteNavigation();\n\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, newTree, inPlaceOf, keepsPrevious);\n\n return;\n }\n\n clearSegments();\n root.render(newTree);\n });\n\n // The whole document again, in place - revalidate(\"all\"). Not through the\n // handler above: that clears the boundaries and renders the tree as a new\n // page, and a boundary emptied re-keys its Activity to the current url and\n // remounts everything under it. A port's \"added to cart\" went with the\n // form's state that way, on every product reached by a link. Here the root\n // takes the tree in a transition and each boundary hands its page the new\n // children under the key it has - see replaceActive.\n setReplaceRootHandler((tree: unknown) => {\n startTransition(() => root.render(tree as ReactNode));\n });\n\n // Back and forward reveal a page the boundaries are still holding, with the\n // form you were filling in still filled in, and without asking the server.\n setRestoreHandler((key: string, maxAge?: number) =>\n restoreSegments(key, maxAge),\n );\n setHeldHandlers(isHeld, dropHidden);\n\n // A touch or a settled hover: the page is rendered hidden now, and the\n // click reveals it. See warm() in navigate.ts and prerenderSegment.\n setPrerenderHandler((tree: unknown, key: string, segmentDepth: number) => {\n prerenderSegment(segmentDepth, key, tree as ReactNode);\n });\n\n window.addEventListener(\"popstate\", () => {\n // restore: back and forward reveal the page you were on, with its state.\n // Wherever the browser just went; not a literal this app wrote.\n navigate(window.location.href as Route, { replace: true, restore: true });\n });\n\n history.replaceState(\n { rscUrl: window.location.href },\n \"\",\n window.location.href,\n );\n\n // A tap held by the bootstrap script, navigated to once hydration has\n // committed - not now: the runtime's script running is seconds before\n // React can dispatch a click on a phone still downloading chunks, and\n // a tap in that window would be the browser's. See earlyClicks.ts.\n afterHydration(() => replayEarlyClicks((url) => navigate(url as Route)));\n}\n\n"]}
1
+ {"version":3,"file":"createViteRscApp.js","sourceRoot":"","sources":["../../src/js/createViteRscApp.ts"],"names":[],"mappings":"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","sourcesContent":["// Vite-engine client bootstrap. Uses @vitejs/plugin-rsc's browser runtime as\n// the Flight deserializer + action encoder, and drives the package's engine-agnostic\n// navigate.ts SPA engine (Link, prefetch, popstate) through it. This replaces\n// the bun engine's createRscApp + the hand-rolled webpack shim — the plugin\n// resolves client references itself.\nimport { SEARCH_PARAMS_FALLBACK } from \"./useSearchParams.js\";\nimport { recoverFromStaleAssets } from \"./staleAssets.js\";\nimport { afterHydration, replayEarlyClicks } from \"./earlyClicks.js\";\nimport { parseRedirectDigest } from \"../redirectDigest.js\";\nimport { isNotFoundDigest } from \"../notFound.js\";\nimport { showDevNotice } from \"./devNotice.js\";\nimport { caughtByLoading } from \"./fallbackReport.js\";\nimport { noteNavigation } from \"./segmentStore.js\";\nimport type { Route } from \"../routes.js\";\nimport { isSafeRedirect } from \"../safeUrl.js\";\nimport {\n createFromReadableStream,\n encodeReply,\n setServerCallback,\n} from \"@vitejs/plugin-rsc/browser\";\nimport { createElement, startTransition } from \"react\";\nimport { createRoot, hydrateRoot } from \"react-dom/client\";\nimport { activityMarkersIn } from \"./activityMarkers.js\";\nimport { ActivityRoot } from \"./ActivityRouter.js\";\nimport {\n ActionRefusedError,\n ServerRedirectError,\n ServerValidationError,\n noteRedirected,\n refusalOf,\n throwForFailedAction,\n} from \"./errors.js\";\nimport { fetchPagePayload } from \"./pagePayload.js\";\nimport { inlinePayload } from \"./inlinePayload.js\";\nimport { claimRead, setQueryCodec } from \"./queryClient.js\";\nimport { clearSegments, dropHidden, isHeld, prerenderSegment, restoreSegments, setSegment } from \"./segmentStore.js\";\nimport type { ReactNode } from \"react\";\nimport {\n cancelPrefetch,\n followActionRedirect,\n isPrefetched,\n navigate,\n setApiRoutes,\n refresh,\n applyRevalidations,\n prefetch,\n retentionKey,\n setCallServer,\n setHeldLayouts,\n setStaticPayloads,\n setStaticRoutes,\n seedStaticChain,\n bootPayloadUrl,\n payloadUrl,\n setDeserializer,\n setInterceptManifest,\n getHeldLayouts,\n setNavigateHandler,\n setHeldHandlers,\n setPrerenderHandler,\n setReplaceRootHandler,\n setRestoreHandler,\n setVersion,\n} from \"./navigate.js\";\n\n\n/**\n * A redirect that travelled as an error: the digest of the error itself, or\n * of the cause React wraps it in when it reports client-rendering a boundary\n * the server had already failed.\n */\nfunction isRedirect(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest;\n const cause = (error as { cause?: { digest?: unknown } } | null)?.cause;\n\n return (\n parseRedirectDigest(digest) !== null ||\n parseRedirectDigest(cause?.digest) !== null ||\n // A missing page decided inside a boundary is answered the same way, by\n // the same boundary, and is just as little a fault to report.\n isNotFoundDigest(digest) ||\n isNotFoundDigest(cause?.digest)\n );\n}\n\nexport async function createViteRscApp(\n container: Document | Element = document,\n interceptEntries: { urlPattern: string; slot: string }[] = [],\n options: { staticPayloads?: string | null; routes?: unknown[] | null; apiRoutes?: string[] } = {},\n): Promise<void> {\n // The urls a route.ts answers: a link to one is an anchor, never a\n // prefetch, never a payload fetch.\n if (options.apiRoutes) setApiRoutes(options.apiRoutes);\n // An exported build has no server to negotiate with, so payloads live at\n // their own urls rather than behind a header on the page's url.\n setStaticPayloads(options.staticPayloads ?? null);\n // Only an exported build ships this: with no server to negotiate with, the\n // client works out for itself how much of a page it still holds.\n if (options.routes) setStaticRoutes(options.routes as never);\n // The router has to recognise an intercepted link before it asks the server,\n // so the patterns are baked into the generated browser entry. Without them\n // every intercepted route falls through to a full-page navigation.\n setInterceptManifest(interceptEntries);\n\n async function callServer(id: string, args: unknown[]): Promise<unknown> {\n // A read opened a slot before calling the reference, and this is the first\n // point at which the id exists on the client — React keeps it private and\n // hands it over only here. Claiming the slot enqueues the read for the\n // batched GET instead of posting it.\n const read = claimRead(id, args);\n\n if (read) return await read;\n\n const encoded = await encodeReply(args);\n\n // encodeReply returns FormData as soon as an argument contains a File.\n // Sending that as multipart would make PHP consume php://input while\n // parsing it, leaving the action with an empty body — so serialize it to\n // raw bytes under an opaque content-type and send the real one in\n // X-RSC-Content-Type for the worker to rebuild FormData from.\n let body: BodyInit;\n let realContentType: string;\n\n if (encoded instanceof FormData) {\n const serialized = new Response(encoded);\n body = await serialized.arrayBuffer();\n realContentType =\n serialized.headers.get(\"content-type\") ?? \"multipart/form-data\";\n } else {\n body = encoded as BodyInit;\n realContentType = \"text/plain;charset=UTF-8\";\n }\n\n // Where the action was invoked from. The host resolves it to the\n // components that render the page, so anything the action says it\n // invalidated can come back with the answer instead of being fetched\n // afterwards - and what comes back is for this page, not whichever is\n // showing when it lands.\n const from = window.location.pathname + window.location.search;\n\n const res = await fetch(\"/_rsc/action\", {\n method: \"POST\",\n headers: {\n \"X-RSC-Action\": id,\n \"X-RSC-Referer\": from,\n \"X-RSC-Content-Type\": realContentType,\n \"Content-Type\": \"application/octet-stream\",\n \"X-XSRF-TOKEN\": decodeURIComponent(\n document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] ?? \"\",\n ),\n },\n body,\n });\n\n // A failed action does not answer with a Flight stream, and the decoder\n // cannot tell — it reports its own parse failure instead of what the\n // server said.\n try {\n await throwForFailedAction(res);\n } catch (err) {\n // A redirect is an instruction, not something for a form to display:\n // an expired session answers this way, and the destination is the login\n // page rather than a message about one.\n if (err instanceof ServerRedirectError) {\n // The destination came off a response header, so it is checked here\n // too — the engine refuses these at the source, but a proxy or a host\n // in front of it can write whatever it likes. A navigation, not a\n // document load: the layouts stay mounted and the history entry the\n // form was on is replaced, the way a redirect after a POST should\n // be - Back does not return to the submitted form.\n //\n // Awaited, so the action is still pending when the destination's\n // tree is handed to React: the two are one transition, committed\n // together. Not awaited, the action ended first - its optimistic\n // state dropped, pending went false - and the page it redirected to\n // arrived a frame later. A new chat's first message vanished from\n // the thread and came back with its reply.\n //\n // Asked of the server, never revealed from what the router holds. The\n // action may have changed anything the destination reads - a cookie,\n // a membership, a row - and a page kept from before it, or prefetched\n // before it, shows the world as it was: a sign-in that redirected\n // back showed the page signed out until a reload. What a revalidating\n // action already drops, a redirecting one drops too.\n if (isSafeRedirect(err.location)) {\n try {\n await followActionRedirect(err.location, err.revalidated);\n } catch {\n // Announced by the navigation itself, as rsc-navigate-error. The\n // action did redirect; that is still its answer.\n }\n }\n\n // Performed, so resolved - not thrown. The call's answer is \"you are\n // being taken somewhere\": an object saying so, the shape an action's\n // result already has, so a caller reading result.validationErrors\n // reads undefined rather than throwing. A caller that awaited the\n // action in a plain startTransition had no catch for a throw, the\n // rejection reached React, and the root unmounted to a white page\n // on every logout.\n noteRedirected(err.location);\n\n return { redirected: err.location };\n }\n\n throw err;\n }\n\n const answer = await createFromReadableStream(res.body!, { callServer });\n const result = unwrapRevalidated(answer, from);\n\n // The backend turned the call down. Thrown, so the code after the await\n // does not take it for what it asked for; the message came in the answer\n // because React strips the message of anything the server throws.\n const refused = refusalOf(result);\n\n if (refused?.errors) throw new ServerValidationError(refused.message, refused.errors);\n if (refused) throw new ActionRefusedError(refused.message, refused.status);\n\n return result;\n }\n\n /**\n * Put anything the action re-rendered on screen, and hand back its result.\n *\n * The trees travel with the answer rather than being fetched afterwards, so\n * the caller sees only what its action returned and never knows the page\n * was updated around it.\n */\n function unwrapRevalidated(answer: unknown, from: string): unknown {\n if (\n answer === null ||\n typeof answer !== \"object\" ||\n !(\"__rscRevalidated\" in answer)\n ) {\n return answer;\n }\n\n const envelope = answer as {\n __rscRevalidated: Record<string, unknown>;\n result: unknown;\n };\n\n applyRevalidations(from, envelope.__rscRevalidated as Record<string, ReactNode>);\n\n return envelope.result;\n }\n\n setDeserializer(createFromReadableStream as never);\n // The query transport speaks Flight too, and reaches the runtime through here\n // rather than importing it: a second import of the browser runtime from the\n // client-component graph is a second client-reference registry, and the\n // symptom is components that render as undefined with nothing logged.\n setQueryCodec({\n deserialize: (stream) =>\n createFromReadableStream(stream, { callServer }) as Promise<unknown>,\n encode: (args) => encodeReply(args) as Promise<string | FormData>,\n // The way back when a read cannot ride in a url. Re-entering callServer is\n // safe: the slot was cleared the moment the reference returned, so this\n // takes the ordinary POST path rather than claiming a read again.\n asAction: (id, args) => callServer(id, args),\n });\n setCallServer(callServer);\n // The plugin's own \"use server\" client stubs route through its registered\n // server callback — register the same transport there too.\n setServerCallback(callServer as never);\n\n // Link / Form / router live in a separate build graph and reach the SPA\n // engine through these globals.\n (window as unknown as { __rsc_navigate: typeof navigate }).__rsc_navigate =\n navigate;\n (window as unknown as { __rsc_prefetch: typeof prefetch }).__rsc_prefetch =\n prefetch;\n (\n window as unknown as { __rsc_cancel_prefetch: typeof cancelPrefetch }\n ).__rsc_cancel_prefetch = cancelPrefetch;\n (\n window as unknown as { __rsc_is_prefetched: typeof isPrefetched }\n ).__rsc_is_prefetched = isPrefetched;\n (window as unknown as { __rsc_refresh: typeof refresh }).__rsc_refresh =\n refresh;\n\n // Hydrate from the RSC endpoint (same url + X-RSC, no version header).\n //\n // Everything from here to the decode is failure handling, because this is\n // the one request with nothing watching it. A page whose shell is already\n // rendered looks fine while this fails, and its fallbacks stay on screen\n // indefinitely with nothing reported anywhere.\n // The payload the document streamed in for itself, when it did - one\n // render per visit, see inlinePayload.ts. A page the build wrote whole\n // fetches its stored payload, as it always did.\n //\n // A page an edge may serve whole names its payload at a hashed asset url\n // (storedPayloadAsset in vite.ts), so booting it asks nothing of the app:\n // the same url with X-RSC would wake a sleeping one on every visit.\n const storedPayload = document.querySelector('meta[name=\"rsc-kit:payload\"]');\n const res =\n inlinePayload() ??\n (storedPayload\n ? await fetchPagePayload(storedPayload.getAttribute(\"content\")!, fetch, false)\n : await fetchPagePayload(bootPayloadUrl(window.location.href)));\n\n // Seed the router with the build this DOCUMENT is, so a redeploy is caught\n // on the next navigation: the host answers 409 to a client of another\n // build, and the document is loaded again. From the document's own meta\n // first - a document a service worker served from its cache is the last\n // build's, while the payload just fetched is this one's, and seeding from\n // the payload would have the old client claim to be new. The header is\n // the fallback for a document with no meta.\n const documentBuild = document.querySelector('meta[name=\"rsc-kit:build\"]')?.getAttribute(\"content\");\n const servedVersion = documentBuild || res.headers.get(\"X-RSC-Version\");\n\n if (servedVersion) {\n setVersion(servedVersion);\n }\n\n // The chain this page is built from, so the next navigation can say what is\n // already mounted and be sent only what changed.\n // A server says what this page is built from; a file server says nothing,\n // so an exported build works it out from the table it was given.\n // An asset says nothing, so the document says it for its payload.\n const servedLayouts = res.headers.get(\"X-RSC-Layouts\") ?? storedPayload?.getAttribute(\"data-layouts\") ?? null;\n\n if (servedLayouts !== null) {\n setHeldLayouts(servedLayouts ? servedLayouts.split(\",\") : []);\n } else if (!seedStaticChain(window.location.href)) {\n setHeldLayouts([]);\n }\n const tree = await createFromReadableStream(res.body!, { callServer });\n\n // Retaining the previous page behind <Activity> needs a wrapper above the\n // page, and React will not hydrate a *document* container through one: the\n // root child of a document has to be <html>, and wrapping it hangs the\n // renderer outright (React 19.2.7). An app whose root layout owns <html>\n // therefore hydrates the tree directly and navigations replace it, as before.\n //\n // Apps that hydrate into an element get retention. Giving it to document-\n // rooted apps means SPA navigation returning only the changed segment rather\n // than a whole document, which is an engine change, not a client one.\n const retains = container !== document;\n\n const shell = retains\n ? createElement(ActivityRoot, {\n initialKey: retentionKey(window.location.href, null),\n initialTree: tree as ReactNode,\n })\n : (tree as ReactNode);\n\n // A document that cannot be hydrated by this tree is client-rendered\n // instead - a repaint, not a freeze. React 19.2 does not recover from a\n // hydration mismatch at an <Activity>: it retries hydrating the boundary,\n // mismatches again, and never returns to the main thread. Every segment\n // boundary is an Activity, one per layout, so a document with none where\n // the page has layouts is that mismatch, guaranteed, before React starts.\n // What produced one: a parameterised route's PPR shell, rendered without\n // a page key and, by a build before this one, without the Activities too.\n // None rather than fewer: a boundary inside a Suspense hole the shell left\n // unfinished has no marker and is client-rendered by React on its own,\n // which is not a mismatch. The stale-asset reload does not apply - the\n // document would come back the same.\n const missing = retains && getHeldLayouts().length > 0 && activityMarkersIn(container) === 0;\n\n if (missing) {\n console.warn(\n \"[rsc-kit] The document has no <Activity> boundaries where the page has layouts, so it cannot be hydrated; rendering the page instead. A PPR shell built by an earlier release lacks them - rebuild.\",\n );\n }\n\n // The payload answered with an error status - the not-found page for a\n // url the shell was served for anyway. It is not the document on screen,\n // so it is rendered, not hydrated: a mismatch at an Activity is the loop\n // above, and this one is certain.\n const disagrees = !res.ok;\n\n const onRecoverableError = (error: unknown, errorInfo: unknown) => {\n // A client component whose chunk the server no longer has fails while\n // hydrating, and React reports that as recoverable: the page is loaded\n // again, with the current names.\n if (recoverFromStaleAssets(error)) return;\n\n // A redirect decided inside a boundary: the server's row carried it as\n // the digest, React client-renders the boundary and reports that it\n // did, and the RedirectBoundary is already performing it. Nothing to\n // read in the console about a page doing what it was told.\n if (isRedirect(error)) return;\n\n // A PPR shell is served with its Suspense boundaries deliberately\n // unfinished — the build aborts the render once the static part is out.\n // React reports that as #419 and client-renders the boundary from the\n // Flight payload, which is the intended path, not a fault to report.\n const message = String((error as { message?: string })?.message ?? error);\n\n if (\n message.includes(\"419\") ||\n message.includes(\"did not finish this Suspense boundary\")\n ) {\n return;\n }\n\n // A view transition the browser cancelled because the tab was not\n // visible. React reports it here; the update still landed, and a tab\n // in the background is not a fault in the page.\n if (message.includes(\"Transition was aborted\")) return;\n\n // A navigation another one overtook: its request was aborted, and a\n // row of its payload that a component was still reading rejects with\n // the abort. React recovers by rendering the root again synchronously\n // and reports the recovery here as #520, with the abort as the cause.\n // The page it recovered to is the one the second navigation asked for;\n // the abort was the design, not a fault.\n const cause = (error as { cause?: { name?: string; message?: string } } | null)?.cause;\n\n if (\n cause?.name === \"AbortError\" ||\n /\\baborted\\b/i.test(cause?.message ?? \"\") ||\n /\\baborted\\b/i.test(message)\n ) {\n return;\n }\n // A page whose query string was read under a boundary carries the\n // fallback there; React reports the recovery on hydration. Under a\n // boundary the developer wrote, that is the designed path and nothing\n // is said. With nothing closer than a loading.tsx, the whole segment\n // showed the fallback until the query arrived - said here, in the\n // page and the console, for whoever is looking at either. Production\n // has no stack to tell the two apart; the build's note on the route is\n // the record there.\n if (\n (error as { digest?: string })?.digest === SEARCH_PARAMS_FALLBACK ||\n message.includes(\"useSearchParams()\")\n ) {\n const stack = (errorInfo as { componentStack?: string } | null)\n ?.componentStack;\n\n if (!caughtByLoading(stack)) return;\n\n const hint =\n \"useSearchParams() was read on the server with nothing closer than a loading.tsx, so the \" +\n \"whole segment showed that fallback until the query arrived. A <Suspense> around the \" +\n \"component that reads keeps the rest of the page painted.\";\n\n showDevNotice(hint, stack);\n console.error(new Error(hint), errorInfo);\n\n return;\n }\n\n console.error(error, errorInfo);\n };\n\n const rootOptions = {\n onRecoverableError,\n // A chunk the deploy no longer serves is not a fault in the page; the\n // document is loaded again, and the new names come with it. Anything\n // else is reported the way React would have.\n onUncaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n\n console.error(error, errorInfo);\n },\n onCaughtError(error: unknown, errorInfo: unknown) {\n if (recoverFromStaleAssets(error)) return;\n // Caught by the RedirectBoundary, which is performing it.\n if (isRedirect(error)) return;\n\n console.error(error, errorInfo);\n },\n };\n\n const root = missing || disagrees\n ? (() => {\n const created = createRoot(container as Element, rootOptions);\n\n created.render(shell);\n\n return created;\n })()\n : hydrateRoot(container, shell, rootOptions);\n\n // Depth 0 is a whole document and replaces the root. Anything deeper is one\n // segment: handing it to the boundary at that depth leaves the layouts above\n // it mounted, which is the point of asking for a partial render at all.\n setNavigateHandler((tree: unknown, key: string, segmentDepth: number, inPlaceOf?: string | null, keepsPrevious?: boolean) => {\n const newTree = tree as ReactNode;\n\n // From here on the boundary animates; the seed commit before this did not.\n noteNavigation();\n\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, newTree, inPlaceOf, keepsPrevious);\n\n return;\n }\n\n clearSegments();\n root.render(newTree);\n });\n\n // The whole document again, in place - revalidate(\"all\"). Not through the\n // handler above: that clears the boundaries and renders the tree as a new\n // page, and a boundary emptied re-keys its Activity to the current url and\n // remounts everything under it. A port's \"added to cart\" went with the\n // form's state that way, on every product reached by a link. Here the root\n // takes the tree in a transition and each boundary hands its page the new\n // children under the key it has - see replaceActive.\n setReplaceRootHandler((tree: unknown) => {\n startTransition(() => root.render(tree as ReactNode));\n });\n\n // Back and forward reveal a page the boundaries are still holding, with the\n // form you were filling in still filled in, and without asking the server.\n setRestoreHandler((key: string, maxAge?: number) =>\n restoreSegments(key, maxAge),\n );\n setHeldHandlers(isHeld, dropHidden);\n\n // A touch or a settled hover: the page is rendered hidden now, and the\n // click reveals it. See warm() in navigate.ts and prerenderSegment.\n setPrerenderHandler((tree: unknown, key: string, segmentDepth: number) => {\n prerenderSegment(segmentDepth, key, tree as ReactNode);\n });\n\n window.addEventListener(\"popstate\", () => {\n // restore: back and forward reveal the page you were on, with its state.\n // Wherever the browser just went; not a literal this app wrote.\n navigate(window.location.href as Route, { replace: true, restore: true });\n });\n\n history.replaceState(\n { rscUrl: window.location.href },\n \"\",\n window.location.href,\n );\n\n // A tap held by the bootstrap script, navigated to once hydration has\n // committed - not now: the runtime's script running is seconds before\n // React can dispatch a click on a phone still downloading chunks, and\n // a tap in that window would be the browser's. See earlyClicks.ts.\n afterHydration(() => replayEarlyClicks((url) => navigate(url as Route)));\n}\n\n"]}
@@ -73,7 +73,9 @@ export declare class ServerRedirectError extends Error {
73
73
  /** Whichever copy of this module threw it; see KIND. */
74
74
  static [Symbol.hasInstance](value: unknown): boolean;
75
75
  readonly location: string;
76
- constructor(location: string);
76
+ /** What the action revalidated before it redirected. */
77
+ readonly revalidated: string[];
78
+ constructor(location: string, revalidated?: string[]);
77
79
  }
78
80
  export declare class ServerAuthenticationError extends Error {
79
81
  /** Whichever copy of this module threw it; see KIND. */
package/dist/js/errors.js CHANGED
@@ -134,11 +134,14 @@ export class ServerRedirectError extends Error {
134
134
  return isKind(value, "ServerRedirectError");
135
135
  }
136
136
  location;
137
- constructor(location) {
137
+ /** What the action revalidated before it redirected. */
138
+ revalidated;
139
+ constructor(location, revalidated = []) {
138
140
  super(`Server action redirected to ${location}`);
139
141
  this.name = "ServerRedirectError";
140
142
  mark(this, "ServerRedirectError");
141
143
  this.location = location;
144
+ this.revalidated = revalidated;
142
145
  }
143
146
  }
144
147
  export class ServerAuthenticationError extends Error {
@@ -204,7 +207,7 @@ export async function throwForFailedAction(response) {
204
207
  // empty body, and the form waited forever.
205
208
  const location = response.headers.get("X-RSC-Redirect");
206
209
  if (location !== null && location !== "") {
207
- throw new ServerRedirectError(location);
210
+ throw new ServerRedirectError(location, response.headers.get("X-RSC-Revalidate")?.split(",") ?? []);
208
211
  }
209
212
  if (response.ok)
210
213
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/js/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which of these an error is, readable by every copy of this module.\n *\n * The app's code and the server's are bundled apart, so an app can hold one\n * copy of these classes while the rpc() client throws from another - and\n * instanceof compares classes, so a server component's\n * `error instanceof ServerAuthenticationError` never matched an rpc() that\n * failed because the visitor was signed out. Each instance is marked under a\n * registry symbol both copies share, and instanceof reads the mark.\n */\nconst KIND = Symbol.for(\"rsc-kit.error-kind\");\n\nfunction mark(error: Error, kind: string): void {\n Object.defineProperty(error, KIND, { value: kind });\n}\n\nfunction isKind(value: unknown, kind: string): boolean {\n return typeof value === \"object\" && value !== null && (value as Record<symbol, unknown>)[KIND] === kind;\n}\n\nexport class ServerValidationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerValidationError\");\n }\n\n public readonly errors: Record<string, string[]>;\n\n constructor(message: string, errors: Record<string, string[]>) {\n super(message);\n this.name = \"ServerValidationError\";\n mark(this, \"ServerValidationError\");\n this.errors = errors;\n }\n\n /** The messages about one input or another, by field name - a nested one dot-joined. */\n get fieldErrors(): Record<string, string[]> {\n const { \"\": _form, ...fields } = this.errors;\n\n return fields;\n }\n\n /** The messages about the input as a whole, kept under \"\" in `errors`. */\n get formErrors(): string[] {\n return this.errors[\"\"] ?? [];\n }\n}\n\n/**\n * What a call to a server action resolves with when the answer was a\n * redirect: not the action's result, and never `T`.\n *\n * Resolved rather than thrown. The navigation is already under way, and a\n * caller that awaits inside `startTransition` with no catch would have its\n * rejection reach React and unmount the root - a white page on every logout.\n * So the type says what the value can be, and `isRedirected` narrows it.\n */\nexport interface Redirected {\n /** Where the visitor is being taken. */\n redirected: string;\n}\n\n/**\n * Whether this is the answer to a redirect, rather than the result you asked\n * for.\n *\n * const secret = await databasesReveal(id);\n * if (isRedirected(secret)) return; // the page is on its way to /login\n * copy(secret.Password);\n *\n * A void action needs it too: `await stub(); toast.success()` runs after the\n * redirect has started, and shows success for a write that never happened.\n */\nexport function isRedirected(value: unknown): value is Redirected {\n return typeof value === \"object\" && value !== null && typeof (value as Redirected).redirected === \"string\";\n}\n\n/**\n * A backend turned a call to a server action down, and said why.\n *\n * Thrown to whoever awaited the stub, so the code after the await - a success\n * toast, a navigation to the id it expected - does not run for a write that did\n * not happen. Not for a redirect, which resolves: that is the page being taken\n * somewhere, and a rejection inside a transition with no catch unmounts the root.\n *\n * Built in the browser from an answer the server returned, because React strips\n * the message of anything a server action throws. `<Form>` shows it as\n * `formError`, and `useAction` as `serverError`.\n */\nexport class ActionRefusedError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ActionRefusedError\");\n }\n\n /** The status the backend gave it: 429 for a throttle, 503 for maintenance. */\n public readonly status: number;\n\n constructor(message: string, status: number = 409) {\n super(message);\n this.name = \"ActionRefusedError\";\n mark(this, \"ActionRefusedError\");\n this.status = status;\n }\n}\n\n/** Marks an answer that is a refusal, so a plain function's result is not mistaken for one. */\nexport const REFUSED = \"__rscRefused\";\n\n/**\n * The refusal a server action answered with, or null for a result.\n *\n * Only a plain `\"use server\"` function is answered this way - the stub the build\n * writes for a backend action is one. An action built with `createActionClient()`\n * returns its failures as its result and is never thrown for.\n */\nexport function refusalOf(\n value: unknown,\n): { message: string; status: number; errors?: Record<string, string[]> } | null {\n if (typeof value !== \"object\" || value === null) return null;\n\n const answer = value as { [REFUSED]?: unknown; serverError?: unknown; validationErrors?: unknown };\n\n if (typeof answer[REFUSED] !== \"number\") return null;\n\n // The input was refused field by field: fieldErrors() in a plain function, a\n // ValidationException behind a backend stub.\n if (typeof answer.validationErrors === \"object\" && answer.validationErrors !== null) {\n return {\n message: \"Validation failed\",\n status: answer[REFUSED],\n errors: answer.validationErrors as Record<string, string[]>,\n };\n }\n\n if (typeof answer.serverError !== \"string\") return null;\n\n return { message: answer.serverError, status: answer[REFUSED] };\n}\n\n/** An action answered with a location instead of a result. */\n/**\n * The redirect the last action answered with, for the one caller that asks.\n *\n * An action that redirects resolves - the navigation is already under way\n * and the caller has nothing to do - which leaves a form that wants to know\n * whether to say \"saved\" with no way to tell a redirect from a void answer.\n * callServer notes the destination here; <Form> clears it before its action\n * runs and reads it, always, right after its await - so a form only ever sees\n * a redirect its own action made. A note nobody reads (an action called\n * outside a Form) is cleared by the next form's submit instead of being taken\n * for that form's redirect. Nothing else needs to read it.\n */\nlet lastRedirect: string | null = null;\n\nexport function noteRedirected(location: string): void {\n lastRedirect = location;\n}\n\n/** The redirect the action just performed, if it did - read once. */\nexport function redirectedTo(): string | null {\n const location = lastRedirect;\n\n lastRedirect = null;\n\n return location;\n}\n\nexport class ServerRedirectError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerRedirectError\");\n }\n\n public readonly location: string;\n\n constructor(location: string) {\n super(`Server action redirected to ${location}`);\n this.name = \"ServerRedirectError\";\n mark(this, \"ServerRedirectError\");\n this.location = location;\n }\n}\n\nexport class ServerAuthenticationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerAuthenticationError\");\n }\n\n constructor(message: string = \"Unauthenticated.\") {\n super(message);\n this.name = \"ServerAuthenticationError\";\n mark(this, \"ServerAuthenticationError\");\n }\n}\n\nexport class ServerAuthorizationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerAuthorizationError\");\n }\n\n constructor(message: string = \"This action is unauthorized.\") {\n super(message);\n this.name = \"ServerAuthorizationError\";\n mark(this, \"ServerAuthorizationError\");\n }\n}\n\nexport class ServerDumpError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerDumpError\");\n }\n\n constructor() {\n super(\"Server returned a dump response.\");\n this.name = \"ServerDumpError\";\n mark(this, \"ServerDumpError\");\n }\n}\n\nexport class ServerSessionExpiredError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerSessionExpiredError\");\n }\n\n constructor(message: string = \"Your session has expired. Please refresh the page.\") {\n super(message);\n this.name = \"ServerSessionExpiredError\";\n mark(this, \"ServerSessionExpiredError\");\n }\n}\n\n/**\n * Turn a failed server-action response into the error it describes.\n *\n * A server action that does not succeed answers with JSON or a redirect\n * header rather than a Flight stream. Passing one of those to the Flight\n * decoder does not produce the server's message — it produces an internal\n * parser failure (\"enqueueModel is not a function\") or a truncated read\n * (\"Connection closed.\"), which is what reached onError before this existed.\n *\n * Returns without throwing when the response is a stream to be decoded.\n */\nexport async function throwForFailedAction(response: Response): Promise<void> {\n // Before the status: a redirect the action asked for is a 204 with the\n // destination in this header - an ok answer with no Flight in it - and\n // an expired session's is a 401. Read first, the header decides for both.\n // Read after `ok`, the 204 fell through to the Flight decoder with an\n // empty body, and the form waited forever.\n const location = response.headers.get(\"X-RSC-Redirect\");\n\n if (location !== null && location !== \"\") {\n throw new ServerRedirectError(location);\n }\n\n if (response.ok) return;\n\n if (response.status === 422) {\n const payload = (await response.json().catch(() => null)) as\n | { message?: string; errors?: Record<string, string[]> }\n | null;\n\n throw new ServerValidationError(\n payload?.message ?? \"Validation failed\",\n payload?.errors ?? {},\n );\n }\n\n if (response.status === 413) {\n throw new Error(\n \"Server action refused: the request body is larger than the server accepts. \" +\n \"A file upload is the usual cause; the limit is the host's maxActionBody.\",\n );\n }\n\n throw new Error(`Server action failed with ${response.status}`);\n}\n\n/**\n * Reject a payload response that is not one.\n *\n * The page a PPR route serves is a static shell: real HTML, status 200, with\n * its Suspense fallbacks showing. Everything below them arrives in a second\n * request. If that request fails there is nothing on screen to say so — the\n * skeletons simply stay, for ever — and handing the failure body to the Flight\n * decoder reports the decoder's confusion rather than the status.\n */\nexport function throwForFailedPayload(response: Response): void {\n if (response.ok) return;\n\n // A 404 that is a payload is the not-found page, rendered: a url a\n // pattern's shell answered with a 200 and a page that, run for real,\n // found no row - a subcategory under the wrong category. The page is\n // what the visitor should see; refusing it left the shell's content on\n // screen with nothing hydrated behind it, and every tap dead.\n if (isPayload(response)) return;\n\n throw new Error(`RSC payload request failed with ${response.status}`);\n}\n\n/** Whether a response is a Flight payload, whatever its status. */\nexport function isPayload(response: Response): boolean {\n return (response.headers.get(\"Content-Type\") ?? \"\").includes(\"text/x-component\");\n}\n\n/**\n * Announce a failure that nothing else will.\n *\n * Dispatched as well as logged: an app that wants to replace a stuck skeleton\n * with something honest has no other way to find out.\n */\nexport function reportClientFailure(scope: string, error: unknown): void {\n if (typeof window !== \"undefined\") {\n window.dispatchEvent(new CustomEvent(\"rsc-client-error\", { detail: { scope, error } }));\n }\n\n console.error(`[rsc-kit] ${scope}`, error);\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/js/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which of these an error is, readable by every copy of this module.\n *\n * The app's code and the server's are bundled apart, so an app can hold one\n * copy of these classes while the rpc() client throws from another - and\n * instanceof compares classes, so a server component's\n * `error instanceof ServerAuthenticationError` never matched an rpc() that\n * failed because the visitor was signed out. Each instance is marked under a\n * registry symbol both copies share, and instanceof reads the mark.\n */\nconst KIND = Symbol.for(\"rsc-kit.error-kind\");\n\nfunction mark(error: Error, kind: string): void {\n Object.defineProperty(error, KIND, { value: kind });\n}\n\nfunction isKind(value: unknown, kind: string): boolean {\n return typeof value === \"object\" && value !== null && (value as Record<symbol, unknown>)[KIND] === kind;\n}\n\nexport class ServerValidationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerValidationError\");\n }\n\n public readonly errors: Record<string, string[]>;\n\n constructor(message: string, errors: Record<string, string[]>) {\n super(message);\n this.name = \"ServerValidationError\";\n mark(this, \"ServerValidationError\");\n this.errors = errors;\n }\n\n /** The messages about one input or another, by field name - a nested one dot-joined. */\n get fieldErrors(): Record<string, string[]> {\n const { \"\": _form, ...fields } = this.errors;\n\n return fields;\n }\n\n /** The messages about the input as a whole, kept under \"\" in `errors`. */\n get formErrors(): string[] {\n return this.errors[\"\"] ?? [];\n }\n}\n\n/**\n * What a call to a server action resolves with when the answer was a\n * redirect: not the action's result, and never `T`.\n *\n * Resolved rather than thrown. The navigation is already under way, and a\n * caller that awaits inside `startTransition` with no catch would have its\n * rejection reach React and unmount the root - a white page on every logout.\n * So the type says what the value can be, and `isRedirected` narrows it.\n */\nexport interface Redirected {\n /** Where the visitor is being taken. */\n redirected: string;\n}\n\n/**\n * Whether this is the answer to a redirect, rather than the result you asked\n * for.\n *\n * const secret = await databasesReveal(id);\n * if (isRedirected(secret)) return; // the page is on its way to /login\n * copy(secret.Password);\n *\n * A void action needs it too: `await stub(); toast.success()` runs after the\n * redirect has started, and shows success for a write that never happened.\n */\nexport function isRedirected(value: unknown): value is Redirected {\n return typeof value === \"object\" && value !== null && typeof (value as Redirected).redirected === \"string\";\n}\n\n/**\n * A backend turned a call to a server action down, and said why.\n *\n * Thrown to whoever awaited the stub, so the code after the await - a success\n * toast, a navigation to the id it expected - does not run for a write that did\n * not happen. Not for a redirect, which resolves: that is the page being taken\n * somewhere, and a rejection inside a transition with no catch unmounts the root.\n *\n * Built in the browser from an answer the server returned, because React strips\n * the message of anything a server action throws. `<Form>` shows it as\n * `formError`, and `useAction` as `serverError`.\n */\nexport class ActionRefusedError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ActionRefusedError\");\n }\n\n /** The status the backend gave it: 429 for a throttle, 503 for maintenance. */\n public readonly status: number;\n\n constructor(message: string, status: number = 409) {\n super(message);\n this.name = \"ActionRefusedError\";\n mark(this, \"ActionRefusedError\");\n this.status = status;\n }\n}\n\n/** Marks an answer that is a refusal, so a plain function's result is not mistaken for one. */\nexport const REFUSED = \"__rscRefused\";\n\n/**\n * The refusal a server action answered with, or null for a result.\n *\n * Only a plain `\"use server\"` function is answered this way - the stub the build\n * writes for a backend action is one. An action built with `createActionClient()`\n * returns its failures as its result and is never thrown for.\n */\nexport function refusalOf(\n value: unknown,\n): { message: string; status: number; errors?: Record<string, string[]> } | null {\n if (typeof value !== \"object\" || value === null) return null;\n\n const answer = value as { [REFUSED]?: unknown; serverError?: unknown; validationErrors?: unknown };\n\n if (typeof answer[REFUSED] !== \"number\") return null;\n\n // The input was refused field by field: fieldErrors() in a plain function, a\n // ValidationException behind a backend stub.\n if (typeof answer.validationErrors === \"object\" && answer.validationErrors !== null) {\n return {\n message: \"Validation failed\",\n status: answer[REFUSED],\n errors: answer.validationErrors as Record<string, string[]>,\n };\n }\n\n if (typeof answer.serverError !== \"string\") return null;\n\n return { message: answer.serverError, status: answer[REFUSED] };\n}\n\n/** An action answered with a location instead of a result. */\n/**\n * The redirect the last action answered with, for the one caller that asks.\n *\n * An action that redirects resolves - the navigation is already under way\n * and the caller has nothing to do - which leaves a form that wants to know\n * whether to say \"saved\" with no way to tell a redirect from a void answer.\n * callServer notes the destination here; <Form> clears it before its action\n * runs and reads it, always, right after its await - so a form only ever sees\n * a redirect its own action made. A note nobody reads (an action called\n * outside a Form) is cleared by the next form's submit instead of being taken\n * for that form's redirect. Nothing else needs to read it.\n */\nlet lastRedirect: string | null = null;\n\nexport function noteRedirected(location: string): void {\n lastRedirect = location;\n}\n\n/** The redirect the action just performed, if it did - read once. */\nexport function redirectedTo(): string | null {\n const location = lastRedirect;\n\n lastRedirect = null;\n\n return location;\n}\n\nexport class ServerRedirectError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerRedirectError\");\n }\n\n public readonly location: string;\n /** What the action revalidated before it redirected. */\n public readonly revalidated: string[];\n\n constructor(location: string, revalidated: string[] = []) {\n super(`Server action redirected to ${location}`);\n this.name = \"ServerRedirectError\";\n mark(this, \"ServerRedirectError\");\n this.location = location;\n this.revalidated = revalidated;\n }\n}\n\nexport class ServerAuthenticationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerAuthenticationError\");\n }\n\n constructor(message: string = \"Unauthenticated.\") {\n super(message);\n this.name = \"ServerAuthenticationError\";\n mark(this, \"ServerAuthenticationError\");\n }\n}\n\nexport class ServerAuthorizationError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerAuthorizationError\");\n }\n\n constructor(message: string = \"This action is unauthorized.\") {\n super(message);\n this.name = \"ServerAuthorizationError\";\n mark(this, \"ServerAuthorizationError\");\n }\n}\n\nexport class ServerDumpError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerDumpError\");\n }\n\n constructor() {\n super(\"Server returned a dump response.\");\n this.name = \"ServerDumpError\";\n mark(this, \"ServerDumpError\");\n }\n}\n\nexport class ServerSessionExpiredError extends Error {\n /** Whichever copy of this module threw it; see KIND. */\n static [Symbol.hasInstance](value: unknown): boolean {\n return isKind(value, \"ServerSessionExpiredError\");\n }\n\n constructor(message: string = \"Your session has expired. Please refresh the page.\") {\n super(message);\n this.name = \"ServerSessionExpiredError\";\n mark(this, \"ServerSessionExpiredError\");\n }\n}\n\n/**\n * Turn a failed server-action response into the error it describes.\n *\n * A server action that does not succeed answers with JSON or a redirect\n * header rather than a Flight stream. Passing one of those to the Flight\n * decoder does not produce the server's message — it produces an internal\n * parser failure (\"enqueueModel is not a function\") or a truncated read\n * (\"Connection closed.\"), which is what reached onError before this existed.\n *\n * Returns without throwing when the response is a stream to be decoded.\n */\nexport async function throwForFailedAction(response: Response): Promise<void> {\n // Before the status: a redirect the action asked for is a 204 with the\n // destination in this header - an ok answer with no Flight in it - and\n // an expired session's is a 401. Read first, the header decides for both.\n // Read after `ok`, the 204 fell through to the Flight decoder with an\n // empty body, and the form waited forever.\n const location = response.headers.get(\"X-RSC-Redirect\");\n\n if (location !== null && location !== \"\") {\n throw new ServerRedirectError(\n location,\n response.headers.get(\"X-RSC-Revalidate\")?.split(\",\") ?? [],\n );\n }\n\n if (response.ok) return;\n\n if (response.status === 422) {\n const payload = (await response.json().catch(() => null)) as\n | { message?: string; errors?: Record<string, string[]> }\n | null;\n\n throw new ServerValidationError(\n payload?.message ?? \"Validation failed\",\n payload?.errors ?? {},\n );\n }\n\n if (response.status === 413) {\n throw new Error(\n \"Server action refused: the request body is larger than the server accepts. \" +\n \"A file upload is the usual cause; the limit is the host's maxActionBody.\",\n );\n }\n\n throw new Error(`Server action failed with ${response.status}`);\n}\n\n/**\n * Reject a payload response that is not one.\n *\n * The page a PPR route serves is a static shell: real HTML, status 200, with\n * its Suspense fallbacks showing. Everything below them arrives in a second\n * request. If that request fails there is nothing on screen to say so — the\n * skeletons simply stay, for ever — and handing the failure body to the Flight\n * decoder reports the decoder's confusion rather than the status.\n */\nexport function throwForFailedPayload(response: Response): void {\n if (response.ok) return;\n\n // A 404 that is a payload is the not-found page, rendered: a url a\n // pattern's shell answered with a 200 and a page that, run for real,\n // found no row - a subcategory under the wrong category. The page is\n // what the visitor should see; refusing it left the shell's content on\n // screen with nothing hydrated behind it, and every tap dead.\n if (isPayload(response)) return;\n\n throw new Error(`RSC payload request failed with ${response.status}`);\n}\n\n/** Whether a response is a Flight payload, whatever its status. */\nexport function isPayload(response: Response): boolean {\n return (response.headers.get(\"Content-Type\") ?? \"\").includes(\"text/x-component\");\n}\n\n/**\n * Announce a failure that nothing else will.\n *\n * Dispatched as well as logged: an app that wants to replace a stuck skeleton\n * with something honest has no other way to find out.\n */\nexport function reportClientFailure(scope: string, error: unknown): void {\n if (typeof window !== \"undefined\") {\n window.dispatchEvent(new CustomEvent(\"rsc-client-error\", { detail: { scope, error } }));\n }\n\n console.error(`[rsc-kit] ${scope}`, error);\n}\n"]}