@rsc-kit/core 0.30.49 → 0.30.51

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.
@@ -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, 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"]}
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, requestClientIp } 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\n/** The visitor's address, on every host call made for a request. */\nexport const CLIENT_IP_HEADER = 'x-rsc-client-ip'\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 // Who the visitor is, as the server saw them. The backend reads it in\n // place of the address this call came from - which is this renderer's,\n // and the same for everyone - once the secret has said the caller may be\n // believed. Laravel's `$request->ip()`, and every throttle keyed on it.\n const ip = requestClientIp()\n\n if (ip !== null) headers[CLIENT_IP_HEADER] = ip\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"]}
@@ -11,6 +11,10 @@ type JsonSchema = Record<string, unknown>;
11
11
  interface Signature {
12
12
  params: JsonSchema[];
13
13
  }
14
- /** Every function taking one object with named fields; nothing else can be a form. */
14
+ /**
15
+ * Every function taking one object with named fields, and after it at most a
16
+ * record's key: a panel's update is told which record as its second argument,
17
+ * update(values, id). Nothing else can be a form. `x-key` marks the second.
18
+ */
15
19
  export declare function formInputs(types: Record<string, Signature>, defs: Record<string, JsonSchema>): Record<string, JsonSchema>;
16
20
  export {};
@@ -5,11 +5,19 @@ function resolved(schema, defs) {
5
5
  return defs[ref.slice(7)] ?? schema;
6
6
  return Array.isArray(schema.anyOf) ? { ...schema, anyOf: schema.anyOf.map((option) => resolved(option, defs)) } : schema;
7
7
  }
8
- /** Every function taking one object with named fields; nothing else can be a form. */
8
+ const KEY_TYPES = ['string', 'integer', 'number'];
9
+ /**
10
+ * Every function taking one object with named fields, and after it at most a
11
+ * record's key: a panel's update is told which record as its second argument,
12
+ * update(values, id). Nothing else can be a form. `x-key` marks the second.
13
+ */
9
14
  export function formInputs(types, defs) {
10
15
  const inputs = {};
11
16
  for (const [name, signature] of Object.entries(types)) {
12
- if (name.startsWith('__rsc.') || signature.params.length !== 1)
17
+ if (name.startsWith('__rsc.') || signature.params.length < 1 || signature.params.length > 2)
18
+ continue;
19
+ const key = signature.params[1];
20
+ if (key && !KEY_TYPES.includes(String(resolved(key, defs).type)))
13
21
  continue;
14
22
  const input = resolved(signature.params[0], defs);
15
23
  if (input.type !== 'object' || !isMap(input.properties))
@@ -19,6 +27,7 @@ export function formInputs(types, defs) {
19
27
  const fields = Array.isArray(input['x-order']) ? input['x-order'] : Object.keys(properties);
20
28
  inputs[name] = {
21
29
  ...input,
30
+ ...(key ? { 'x-key': true } : {}),
22
31
  properties: Object.fromEntries(fields.map((field) => [field, resolved(properties[field], defs)])),
23
32
  };
24
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hostInputs.js","sourceRoot":"","sources":["../src/hostInputs.ts"],"names":[],"mappings":"AAeA,MAAM,KAAK,GAAG,CAAC,KAAc,EAAuB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAE3H,SAAS,QAAQ,CAAC,MAAkB,EAAE,IAAgC;IACpE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAA;IAEvB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;IAE7F,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAG,MAAM,CAAC,KAAsB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;AAC5I,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,UAAU,CAAC,KAAgC,EAAE,IAAgC;IAC3F,MAAM,MAAM,GAA+B,EAAE,CAAA;IAE7C,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAExE,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAA;QAElD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;YAAE,SAAQ;QAEjE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;QAEnC,wEAAwE;QACxE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAE,KAAK,CAAC,SAAS,CAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAEzG,MAAM,CAAC,IAAI,CAAC,GAAG;YACb,GAAG,KAAK;YACR,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAe,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;SAChH,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["/**\n * What each backend function takes as its one argument, for a form to draw:\n * `defineForm({ action: 'Contact.send' })` has no schema of its own, so the\n * fields come from the type the backend wrote to rsc-host.json.\n *\n * Worked out by the build and written into the server entry, because a Worker\n * has no file to read. References are resolved here, so what a form reads is\n * plain JSON Schema: a Go struct or a PHP class is a `$ref` in the manifest.\n */\ntype JsonSchema = Record<string, unknown>\n\ninterface Signature {\n params: JsonSchema[]\n}\n\nconst isMap = (value: unknown): value is JsonSchema => typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction resolved(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema {\n const ref = schema.$ref\n\n if (typeof ref === 'string' && ref.startsWith('#/defs/')) return defs[ref.slice(7)] ?? schema\n\n return Array.isArray(schema.anyOf) ? { ...schema, anyOf: (schema.anyOf as JsonSchema[]).map((option) => resolved(option, defs)) } : schema\n}\n\n/** Every function taking one object with named fields; nothing else can be a form. */\nexport function formInputs(types: Record<string, Signature>, defs: Record<string, JsonSchema>): Record<string, JsonSchema> {\n const inputs: Record<string, JsonSchema> = {}\n\n for (const [name, signature] of Object.entries(types)) {\n if (name.startsWith('__rsc.') || signature.params.length !== 1) continue\n\n const input = resolved(signature.params[0]!, defs)\n\n if (input.type !== 'object' || !isMap(input.properties)) continue\n\n const properties = input.properties\n\n // Go's schemas are maps, written sorted, so it says the struct's order.\n const fields = Array.isArray(input['x-order']) ? (input['x-order'] as string[]) : Object.keys(properties)\n\n inputs[name] = {\n ...input,\n properties: Object.fromEntries(fields.map((field) => [field, resolved(properties[field] as JsonSchema, defs)])),\n }\n }\n\n return inputs\n}\n"]}
1
+ {"version":3,"file":"hostInputs.js","sourceRoot":"","sources":["../src/hostInputs.ts"],"names":[],"mappings":"AAeA,MAAM,KAAK,GAAG,CAAC,KAAc,EAAuB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAE3H,SAAS,QAAQ,CAAC,MAAkB,EAAE,IAAgC;IACpE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAA;IAEvB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;IAE7F,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAG,MAAM,CAAC,KAAsB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;AAC5I,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;AAEjD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAgC,EAAE,IAAgC;IAC3F,MAAM,MAAM,GAA+B,EAAE,CAAA;IAE7C,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,SAAQ;QAErG,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAE/B,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,SAAQ;QAE1E,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAA;QAElD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;YAAE,SAAQ;QAEjE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;QAEnC,wEAAwE;QACxE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAE,KAAK,CAAC,SAAS,CAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAEzG,MAAM,CAAC,IAAI,CAAC,GAAG;YACb,GAAG,KAAK;YACR,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAe,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;SAChH,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["/**\n * What each backend function takes as its one argument, for a form to draw:\n * `defineForm({ action: 'Contact.send' })` has no schema of its own, so the\n * fields come from the type the backend wrote to rsc-host.json.\n *\n * Worked out by the build and written into the server entry, because a Worker\n * has no file to read. References are resolved here, so what a form reads is\n * plain JSON Schema: a Go struct or a PHP class is a `$ref` in the manifest.\n */\ntype JsonSchema = Record<string, unknown>\n\ninterface Signature {\n params: JsonSchema[]\n}\n\nconst isMap = (value: unknown): value is JsonSchema => typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction resolved(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema {\n const ref = schema.$ref\n\n if (typeof ref === 'string' && ref.startsWith('#/defs/')) return defs[ref.slice(7)] ?? schema\n\n return Array.isArray(schema.anyOf) ? { ...schema, anyOf: (schema.anyOf as JsonSchema[]).map((option) => resolved(option, defs)) } : schema\n}\n\nconst KEY_TYPES = ['string', 'integer', 'number']\n\n/**\n * Every function taking one object with named fields, and after it at most a\n * record's key: a panel's update is told which record as its second argument,\n * update(values, id). Nothing else can be a form. `x-key` marks the second.\n */\nexport function formInputs(types: Record<string, Signature>, defs: Record<string, JsonSchema>): Record<string, JsonSchema> {\n const inputs: Record<string, JsonSchema> = {}\n\n for (const [name, signature] of Object.entries(types)) {\n if (name.startsWith('__rsc.') || signature.params.length < 1 || signature.params.length > 2) continue\n\n const key = signature.params[1]\n\n if (key && !KEY_TYPES.includes(String(resolved(key, defs).type))) continue\n\n const input = resolved(signature.params[0]!, defs)\n\n if (input.type !== 'object' || !isMap(input.properties)) continue\n\n const properties = input.properties\n\n // Go's schemas are maps, written sorted, so it says the struct's order.\n const fields = Array.isArray(input['x-order']) ? (input['x-order'] as string[]) : Object.keys(properties)\n\n inputs[name] = {\n ...input,\n ...(key ? { 'x-key': true } : {}),\n properties: Object.fromEntries(fields.map((field) => [field, resolved(properties[field] as JsonSchema, defs)])),\n }\n }\n\n return inputs\n}\n"]}
@@ -51,6 +51,12 @@ export function withoutPayloadKey(request) {
51
51
  return request;
52
52
  url.searchParams.delete(PAYLOAD_PARAM);
53
53
  const copy = new Request(url, { method: request.method, headers: request.headers, signal: request.signal });
54
+ // The peer address srvx put on the one that arrived. A new Request has no
55
+ // socket, and without this every navigation that carried the key reached
56
+ // the backend as the renderer itself - one throttle for everyone.
57
+ const ip = request.ip;
58
+ if (typeof ip === "string")
59
+ Object.defineProperty(copy, "ip", { value: ip });
54
60
  stripped.set(request, copy);
55
61
  return copy;
56
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"payloadKey.js","sourceRoot":"","sources":["../../src/js/payloadKey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAIpC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,IAAI,IAAI,GAAG,IAAI,CAAC;IAEhB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QAEtC,KAAK,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,OAAqB;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAEtD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAE5D,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IAE1E,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEnC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC;QAAE,OAAO,OAAO,CAAC;IAEzD,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAEvC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE5G,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE5B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,6DAA6D;AAC7D,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAoB,CAAC","sourcesContent":["/**\n * A payload's own url.\n *\n * One url answers with a document or with one of several payloads, told apart\n * by `X-RSC*` request headers. A cache that keys on the url alone - Cloudflare\n * ignores `Vary`, and so does a Cache Rule that caches HTML - would hand a\n * payload to a visitor asking for the document, or the other way round, and\n * the page breaks. So a payload request carries `?_rsc=<key>`, a hash of the\n * headers that shape its answer: a different answer is a different url,\n * whatever the cache in front does with `Vary`.\n *\n * The server strips the parameter before anything reads the url, so a page's\n * search params never see it.\n */\n\nexport const PAYLOAD_PARAM = \"_rsc\";\n\ntype HeaderSource = Headers | Record<string, string>;\n\n/**\n * djb2 over every `X-RSC*` header, in base 36: short, stable, and the same in\n * every runtime. Headers lowercases the names and iterates them sorted, so the\n * client's plain object and the server's Headers give the same key.\n */\nexport function payloadKey(headers: HeaderSource): string {\n let hash = 5381;\n\n new Headers(headers).forEach((value, name) => {\n if (!name.startsWith(\"x-rsc\")) return;\n\n for (const char of `${name}:${value}\\n`) hash = (hash * 33 + char.charCodeAt(0)) | 0;\n });\n\n return (hash >>> 0).toString(36);\n}\n\n/** The url a payload with these headers is asked for at: a path stays a path. */\nexport function keyedPayloadUrl(url: string, headers: HeaderSource): string {\n const parsed = new URL(url, \"http://payload.invalid\");\n\n parsed.searchParams.set(PAYLOAD_PARAM, payloadKey(headers));\n\n return url[0] === \"/\" ? parsed.pathname + parsed.search + parsed.hash : parsed.href;\n}\n\n/**\n * The request as the app should see it: a payload asked for at its own url\n * (`?_rsc=<key>`, see js/payloadKey.ts) loses the key before routing, search\n * params, a redirect's location or a cache key can read it. The key exists for\n * caches in front that ignore Vary; to the page it is the same url.\n */\nexport function withoutPayloadKey(request: Request): Request {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return request;\n\n const kept = stripped.get(request);\n\n if (kept) return kept;\n\n const url = new URL(request.url);\n\n if (!url.searchParams.has(PAYLOAD_PARAM)) return request;\n\n url.searchParams.delete(PAYLOAD_PARAM);\n\n const copy = new Request(url, { method: request.method, headers: request.headers, signal: request.signal });\n\n stripped.set(request, copy);\n\n return copy;\n}\n\n// The same copy for the same request, every time. What the app marks on the\n// request it saw (a page that said notFound()) is asked about by the entry in\n// front, which holds the request as it arrived, key and all.\nconst stripped = new WeakMap<Request, Request>();\n"]}
1
+ {"version":3,"file":"payloadKey.js","sourceRoot":"","sources":["../../src/js/payloadKey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAIpC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,IAAI,IAAI,GAAG,IAAI,CAAC;IAEhB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QAEtC,KAAK,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,OAAqB;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAEtD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAE5D,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IAE1E,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEnC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC;QAAE,OAAO,OAAO,CAAC;IAEzD,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAEvC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE5G,0EAA0E;IAC1E,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,EAAE,GAAI,OAA4B,CAAC,EAAE,CAAC;IAE5C,IAAI,OAAO,EAAE,KAAK,QAAQ;QAAE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAE7E,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE5B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,6DAA6D;AAC7D,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAoB,CAAC","sourcesContent":["/**\n * A payload's own url.\n *\n * One url answers with a document or with one of several payloads, told apart\n * by `X-RSC*` request headers. A cache that keys on the url alone - Cloudflare\n * ignores `Vary`, and so does a Cache Rule that caches HTML - would hand a\n * payload to a visitor asking for the document, or the other way round, and\n * the page breaks. So a payload request carries `?_rsc=<key>`, a hash of the\n * headers that shape its answer: a different answer is a different url,\n * whatever the cache in front does with `Vary`.\n *\n * The server strips the parameter before anything reads the url, so a page's\n * search params never see it.\n */\n\nexport const PAYLOAD_PARAM = \"_rsc\";\n\ntype HeaderSource = Headers | Record<string, string>;\n\n/**\n * djb2 over every `X-RSC*` header, in base 36: short, stable, and the same in\n * every runtime. Headers lowercases the names and iterates them sorted, so the\n * client's plain object and the server's Headers give the same key.\n */\nexport function payloadKey(headers: HeaderSource): string {\n let hash = 5381;\n\n new Headers(headers).forEach((value, name) => {\n if (!name.startsWith(\"x-rsc\")) return;\n\n for (const char of `${name}:${value}\\n`) hash = (hash * 33 + char.charCodeAt(0)) | 0;\n });\n\n return (hash >>> 0).toString(36);\n}\n\n/** The url a payload with these headers is asked for at: a path stays a path. */\nexport function keyedPayloadUrl(url: string, headers: HeaderSource): string {\n const parsed = new URL(url, \"http://payload.invalid\");\n\n parsed.searchParams.set(PAYLOAD_PARAM, payloadKey(headers));\n\n return url[0] === \"/\" ? parsed.pathname + parsed.search + parsed.hash : parsed.href;\n}\n\n/**\n * The request as the app should see it: a payload asked for at its own url\n * (`?_rsc=<key>`, see js/payloadKey.ts) loses the key before routing, search\n * params, a redirect's location or a cache key can read it. The key exists for\n * caches in front that ignore Vary; to the page it is the same url.\n */\nexport function withoutPayloadKey(request: Request): Request {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return request;\n\n const kept = stripped.get(request);\n\n if (kept) return kept;\n\n const url = new URL(request.url);\n\n if (!url.searchParams.has(PAYLOAD_PARAM)) return request;\n\n url.searchParams.delete(PAYLOAD_PARAM);\n\n const copy = new Request(url, { method: request.method, headers: request.headers, signal: request.signal });\n\n // The peer address srvx put on the one that arrived. A new Request has no\n // socket, and without this every navigation that carried the key reached\n // the backend as the renderer itself - one throttle for everyone.\n const ip = (request as { ip?: unknown }).ip;\n\n if (typeof ip === \"string\") Object.defineProperty(copy, \"ip\", { value: ip });\n\n stripped.set(request, copy);\n\n return copy;\n}\n\n// The same copy for the same request, every time. What the app marks on the\n// request it saw (a page that said notFound()) is asked about by the entry in\n// front, which holds the request as it arrived, key and all.\nconst stripped = new WeakMap<Request, Request>();\n"]}
package/dist/request.d.ts CHANGED
@@ -3,6 +3,7 @@ export type { ServedRequest } from "./served.js";
3
3
  export type RequestLike = Request | {
4
4
  url: string;
5
5
  headers: Record<string, string>;
6
+ ip?: string;
6
7
  } | null;
7
8
  /**
8
9
  * The request's headers.
@@ -132,6 +133,30 @@ export declare function connection(): Promise<void>;
132
133
  * the render it serves is per request already.
133
134
  */
134
135
  export declare function requestSearch(): string | null;
136
+ /**
137
+ * The visitor's address, or null outside a request and when the server did not
138
+ * say. Not a read of the request: it goes to the backend on every host call,
139
+ * where `$request->ip()` and a throttle keyed on it are about the visitor
140
+ * rather than about this renderer, which every visitor would otherwise share.
141
+ */
142
+ export declare function requestClientIp(): string | null;
143
+ /**
144
+ * The backend guard checks already passed during this request, or null
145
+ * outside one. A stored page is guarded before the store is looked in, and
146
+ * when nothing is stored the render guards it again: two calls for one page,
147
+ * and a `throttle:6,1` that let three visits through. One visitor, one page,
148
+ * one answer - asked once.
149
+ */
150
+ export declare function guardsPassed(): Set<string> | null;
151
+ /**
152
+ * The url the visitor asked for, as they typed it: the request's own, under the
153
+ * host and scheme a proxy in front forwarded when one did. Null outside a
154
+ * request. For the backend's route middleware, which judges the page rather
155
+ * than the call asking about it - Laravel's `signed` checks a signature over
156
+ * exactly this. Not a read of the request: a route with backend middleware is
157
+ * never stored for everyone anyway.
158
+ */
159
+ export declare function requestPageUrl(): string | null;
135
160
  /**
136
161
  * Whether the request being rendered is a crawler's, which is answered with
137
162
  * the finished document - see crawler.ts. False when there is no request.
package/dist/request.js CHANGED
@@ -456,6 +456,76 @@ export function requestSearch() {
456
456
  return null;
457
457
  }
458
458
  }
459
+ /**
460
+ * The visitor's address, or null outside a request and when the server did not
461
+ * say. Not a read of the request: it goes to the backend on every host call,
462
+ * where `$request->ip()` and a throttle keyed on it are about the visitor
463
+ * rather than about this renderer, which every visitor would otherwise share.
464
+ */
465
+ export function requestClientIp() {
466
+ const store = scope()?.getStore();
467
+ return store?.request ? store.ip : null;
468
+ }
469
+ /**
470
+ * The backend guard checks already passed during this request, or null
471
+ * outside one. A stored page is guarded before the store is looked in, and
472
+ * when nothing is stored the render guards it again: two calls for one page,
473
+ * and a `throttle:6,1` that let three visits through. One visitor, one page,
474
+ * one answer - asked once.
475
+ */
476
+ export function guardsPassed() {
477
+ const store = scope()?.getStore();
478
+ return store?.request ? store.guards : null;
479
+ }
480
+ /**
481
+ * The url the visitor asked for, as they typed it: the request's own, under the
482
+ * host and scheme a proxy in front forwarded when one did. Null outside a
483
+ * request. For the backend's route middleware, which judges the page rather
484
+ * than the call asking about it - Laravel's `signed` checks a signature over
485
+ * exactly this. Not a read of the request: a route with backend middleware is
486
+ * never stored for everyone anyway.
487
+ */
488
+ export function requestPageUrl() {
489
+ const store = scope()?.getStore();
490
+ if (!store?.request || !store.url)
491
+ return null;
492
+ try {
493
+ const url = new URL(store.url);
494
+ const host = store.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
495
+ const forwarded = store.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
496
+ const proto = forwarded === "http" || forwarded === "https" ? forwarded : url.protocol.slice(0, -1);
497
+ // Rebuilt rather than assigned to url.host, which keeps this server's
498
+ // port when the forwarded name has none.
499
+ return host ? new URL(url.pathname + url.search, `${proto}://${host}`).href : url.href;
500
+ }
501
+ catch {
502
+ return null;
503
+ }
504
+ }
505
+ /**
506
+ * Who sent the request: the peer address the server put on it - srvx's
507
+ * `request.ip`, which Nitro hands every route - or, when RSC_TRUST_FORWARDED=1
508
+ * says a proxy of this deployment's own wrote the chain, the first hop of
509
+ * X-Forwarded-For. Never the chain otherwise: a browser can write that header,
510
+ * and would choose its own address for every throttle on the backend.
511
+ */
512
+ function clientAddress(from) {
513
+ const read = (name) => from instanceof Request ? from.headers.get(name) : (from.headers[name] ?? null);
514
+ if (typeof process !== "undefined" && process.env.RSC_TRUST_FORWARDED === "1") {
515
+ const first = read("x-forwarded-for")?.split(",")[0]?.trim() || read("x-real-ip")?.trim();
516
+ if (first)
517
+ return first;
518
+ }
519
+ let ip;
520
+ try {
521
+ ip = from.ip;
522
+ }
523
+ catch {
524
+ // A getter that throws for a request with no socket behind it.
525
+ ip = undefined;
526
+ }
527
+ return typeof ip === "string" && ip !== "" ? ip : null;
528
+ }
459
529
  /**
460
530
  * Whether the request being rendered is a crawler's, which is answered with
461
531
  * the finished document - see crawler.ts. False when there is no request.
@@ -514,6 +584,8 @@ export async function withRequest(from, run) {
514
584
  : new Headers(from.headers)
515
585
  : new Headers(),
516
586
  original: isRequest ? from : null,
587
+ ip: from ? clientAddress(from) : null,
588
+ guards: new Set(),
517
589
  read: false,
518
590
  readBy: [],
519
591
  readWhere: [],