@rsc-kit/core 0.18.0 → 0.19.0
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/action.d.ts +16 -2
- package/dist/action.js +21 -12
- package/dist/action.js.map +1 -1
- package/dist/apiPrerender.js +24 -6
- package/dist/apiPrerender.js.map +1 -1
- package/dist/clientPackages.d.ts +30 -0
- package/dist/clientPackages.js +234 -0
- package/dist/clientPackages.js.map +1 -0
- package/dist/compress.d.ts +12 -0
- package/dist/compress.js +134 -0
- package/dist/compress.js.map +1 -0
- package/dist/compressRuntime.d.ts +8 -0
- package/dist/compressRuntime.js +62 -0
- package/dist/compressRuntime.js.map +1 -0
- package/dist/files.d.ts +20 -0
- package/dist/files.js +38 -0
- package/dist/files.js.map +1 -1
- package/dist/formSubmit.d.ts +1 -0
- package/dist/formSubmit.js +14 -0
- package/dist/formSubmit.js.map +1 -0
- package/dist/host.d.ts +23 -0
- package/dist/host.js +175 -31
- package/dist/host.js.map +1 -1
- package/dist/hostCalls.d.ts +15 -0
- package/dist/hostCalls.js +75 -8
- package/dist/hostCalls.js.map +1 -1
- package/dist/js/Form.d.ts +19 -2
- package/dist/js/Form.js +110 -83
- package/dist/js/Form.js.map +1 -1
- package/dist/js/LoadingBoundary.d.ts +5 -0
- package/dist/js/LoadingBoundary.js +19 -0
- package/dist/js/LoadingBoundary.js.map +1 -0
- package/dist/js/createViteRscApp.d.ts +1 -0
- package/dist/js/createViteRscApp.js +34 -5
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/errors.d.ts +3 -1
- package/dist/js/errors.js +26 -3
- package/dist/js/errors.js.map +1 -1
- package/dist/js/fallbackReport.js +10 -8
- package/dist/js/fallbackReport.js.map +1 -1
- package/dist/js/formEncoding.d.ts +72 -3
- package/dist/js/formEncoding.js +284 -20
- package/dist/js/formEncoding.js.map +1 -1
- package/dist/js/navigate.d.ts +3 -0
- package/dist/js/navigate.js +56 -6
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/updateStore.js +8 -2
- package/dist/js/updateStore.js.map +1 -1
- package/dist/js/useEvents.js +9 -7
- package/dist/js/useEvents.js.map +1 -1
- package/dist/js/usePolling.js +38 -37
- package/dist/js/usePolling.js.map +1 -1
- package/dist/openapi.d.ts +74 -0
- package/dist/openapi.js +172 -0
- package/dist/openapi.js.map +1 -0
- package/dist/prerender.js +1 -0
- package/dist/prerender.js.map +1 -1
- package/dist/redirect.d.ts +2 -2
- package/dist/redirect.js.map +1 -1
- package/dist/request.d.ts +56 -2
- package/dist/request.js +68 -4
- package/dist/request.js.map +1 -1
- package/dist/routeSchema.d.ts +49 -0
- package/dist/routeSchema.js.map +1 -1
- package/dist/routes.d.ts +15 -1
- package/dist/routes.js.map +1 -1
- package/dist/testing.d.ts +22 -0
- package/dist/testing.js +83 -5
- package/dist/testing.js.map +1 -1
- package/dist/vite.d.ts +80 -1
- package/dist/vite.js +625 -82
- package/dist/vite.js.map +1 -1
- package/package.json +7 -3
package/dist/request.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading the request from inside a render.\n//\n// import { headers, cookies } from '@rsc-kit/core/request'\n//\n// export default async function middleware() {\n// const locale = cookies().get('locale') ?? negotiate(headers().get('accept-language'))\n// if (!locale) redirect('/en')\n// }\n//\n// Server components have no arguments beyond their props, and middleware has\n// none at all, so without this an app on a JavaScript host cannot see a header\n// — no locale negotiation, no subdomain, no cookie. The Laravel host escaped\n// that through rpc(), because PHP has the request; this is the same thing for\n// everyone else.\n//\n// Read-only on purpose. Setting a cookie means writing a response header, and\n// by the time a component renders the shell may already have been flushed — an\n// API that appears to set one and silently does not is worse than not having\n// it. Set them in the host, which owns the response.\n//\n// Asynchronous on purpose too, and not for ergonomics. A build has no request,\n// so a read there suspends and never settles — which is the same thing the\n// prerender probe does to a host call. Anything still waiting when the probe's\n// budget expires becomes a fallback in the frozen shell, so a read inside a\n// Suspense boundary leaves the shell frozen and makes only that boundary\n// dynamic. Reading synchronously could only mark the whole route dynamic,\n// because there would be nothing to suspend on.\n//\n// Scoped to the request, on the same store cache() uses. Two requests in\n// flight cannot see each other's headers.\n\nimport { resolveScope } from \"./revalidate.js\";\n\n/** What a host can supply: a real Request, or the parts of one. */\nexport type RequestLike =\n Request | { url: string; headers: Record<string, string> } | null;\n\ninterface Slot {\n /** False during a build: there is nothing to answer with. */\n request: boolean;\n url: string | null;\n /**\n * Held as Headers rather than as a Request.\n *\n * `new Request(url, { headers })` drops Cookie: a spec-compliant Headers\n * built with the \"request\" guard refuses the forbidden header names, and\n * Cookie is one of them. `new Headers()` has no guard and keeps it. Bun\n * happens to allow both, which is why rebuilding a Request looked fine until\n * it ran anywhere stricter — and what it loses is every cookie, silently.\n */\n headers: Headers;\n original: Request | null;\n /**\n * Whether anything asked. Only for the build's report — the classification\n * comes from the suspension, not from this.\n */\n read: boolean;\n /**\n * What read it, in the order they did.\n *\n * Only the name of the accessor — `cookies()`, `headers()` — which is enough\n * for the build to say why a page could not be frozen. Without it the answer\n * is \"something in this page reached for the request\", and finding which is\n * the part that takes an afternoon.\n */\n readBy: string[];\n /**\n * The same reads with the component that made each one - `cookies() in\n * RootLayout` - taken from the stack at the moment of the read, which is\n * still the component's own call: an accessor runs synchronously up to\n * the point it suspends. Only the build reads this. \"Reads the request\" is\n * a category; this is the line to open.\n */\n readWhere: string[];\n /**\n * Reads the server could not answer and React caught at a boundary - the\n * fallback is what got stored - with the component that made each:\n * `useSearchParams() in Query`. Noted by the SSR entry, read by the build,\n * which attaches them to the route so a page stored as its own loading\n * screen says why.\n */\n fallbacks: string[];\n /**\n * For a read made inside a cache()-wrapped helper: the components that\n * awaited that helper, first caller included. The helper runs once, so\n * the stack at the read names only whoever got there first; every later\n * awaiter is a cache hit that never reaches the accessor. The one above\n * every boundary is usually one of the later ones.\n */\n awaiters: Map<unknown, string[]>;\n /** The cache()-wrapped helper whose body is running, while it runs. */\n inHelper: unknown;\n /** readWhere entry -> the helper it was read inside. */\n readVia: Map<string, unknown>;\n}\n\nconst SCOPE = Symbol.for(\"@rsc-kit/core.request-scope\");\n\n/**\n * The accessors themselves and the plumbing under them: frames to walk past\n * on the way to the component that called.\n */\nconst NOT_A_CALLER = new Set([\n \"never\",\n \"cache\",\n \"noteAwaiter\",\n \"withHelper\",\n \"noteRequestRead\",\n \"recordRead\",\n \"caller\",\n \"slot\",\n \"cookies\",\n \"headers\",\n \"connection\",\n \"searchParams\",\n \"request\",\n \"url\",\n \"parseParams\",\n \"parseSearchParams\",\n \"parseBody\",\n \"get\",\n \"set\",\n \"has\",\n]);\n\n/** The accessors a component calls by name. One calling another is plumbing, not a second read. */\nconst ACCESSORS = new Set([\n \"cookies\",\n \"headers\",\n \"connection\",\n \"searchParams\",\n \"request\",\n \"url\",\n]);\n\n/**\n * The nearest named function above the accessor on the stack, and whether\n * another accessor sits between: `cookies()` reads through `headers()`, and\n * that inner read is not a second thing the component did.\n *\n * Read from a thrown-away Error: the one thing a build cannot otherwise learn\n * is which component reached for the request, and the stack knows. Function\n * names survive a server bundle, which is not minified, so this is\n * `RootLayout` and not a line number in a chunk. Internal names are walked\n * past; an anonymous frame is skipped rather than reported as `<anonymous>`.\n */\nfunction caller(own: string): { name: string | null; nested: boolean } {\n const stack = new Error().stack ?? \"\";\n let accessors = 0;\n let helper: string | null = null;\n\n for (const line of stack.split(\"\\n\")) {\n const match = /^\\s*at (?:async )?([^\\s(]+)/.exec(line);\n\n if (!match) continue;\n\n const name = match[1]!.split(\".\").pop()!;\n\n // A frame with no function name prints its location instead - `js:6054:8`\n // - which is not a name to report.\n if (!/^[\\w$]+$/.test(name)) continue;\n\n if (ACCESSORS.has(name)) accessors++;\n if (\n NOT_A_CALLER.has(name) ||\n name === \"<anonymous>\" ||\n name === \"Object\" ||\n name === \"Promise\"\n )\n continue;\n\n const nested = accessors > (ACCESSORS.has(own) ? 1 : 0);\n\n // A function that is not a component - getCurrentUser, wrapped in\n // cache() - is the shared helper the read lives in. Named, because with\n // cache() the read runs once, on whichever component called first, and\n // every other caller of the helper is invisible to the stack. The helper\n // is what they have in common.\n if (/^[a-z_$]/.test(name) && !/^use[A-Z]/.test(name)) {\n helper ??= name;\n\n continue;\n }\n\n return {\n name: helper && helper !== name ? `${helper} (from ${name})` : name,\n nested,\n };\n }\n\n return { name: helper, nested: accessors > (ACCESSORS.has(own) ? 1 : 0) };\n}\n\nfunction recordRead(store: Slot, by: string): void {\n if (!store.readBy.includes(by)) store.readBy.push(by);\n\n // Older stores, written by a bundle built before this field existed.\n store.readWhere ??= [];\n\n const { name, nested } = caller(by.replace(/\\(.*$/, \"\"));\n\n if (nested) return;\n\n const entry = name ? `${by} in ${name}` : by;\n\n if (!store.readWhere.includes(entry)) store.readWhere.push(entry);\n if (store.inHelper) (store.readVia ??= new Map()).set(entry, store.inHelper);\n}\n\nconst globals = globalThis as Record<symbol | string, unknown>;\n\nlet ready: Promise<void> | null = null;\n\ninterface Scope {\n getStore(): Slot | undefined;\n run<T>(store: Slot, fn: () => T): T;\n}\n\nfunction scope(): Scope | null {\n return (globals[SCOPE] as Scope | undefined) ?? null;\n}\n\nfunction slot(): Slot {\n const store = scope()?.getStore();\n\n if (!store) {\n throw new Error(\n \"No request in scope. headers() and cookies() are for a render — middleware, a layout, \" +\n \"a page — and there is nothing to read outside one.\",\n );\n }\n\n store.read = true;\n\n return store;\n}\n\n/**\n * The request's headers.\n *\n * Empty during a build, where there is no request: the page is marked as\n * needing one instead, and rendered on demand rather than frozen.\n */\nexport async function headers(): Promise<Headers> {\n const store = slot();\n\n return store.request ? store.headers : never(\"headers()\");\n}\n\n/** How a cookie should be written. The names browsers use. */\nexport interface CookieOptions {\n path?: string;\n domain?: string;\n maxAge?: number;\n expires?: Date;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n partitioned?: boolean;\n}\n\nexport interface Cookies {\n get(name: string): string | undefined;\n has(name: string): boolean;\n getAll(): Record<string, string>;\n /**\n * Write one on the response.\n *\n * Only from a server action, which has a response of its own that has not\n * been sent. A render has already flushed its headers by the time a\n * component runs — that is what makes the first paint fast — so this throws\n * there rather than appearing to work.\n */\n set(name: string, value: string, options?: CookieOptions): void;\n /** Write one that expires immediately. Same rule about where. */\n delete(name: string, options?: CookieOptions): void;\n}\n\n/** Kept for the name it had when it could only read. */\nexport type ReadonlyCookies = Cookies;\n\n/**\n * The response being built, while it can still be changed.\n *\n * Open from the moment a request is picked up until the host constructs its\n * Response, and sealed the instant it does. Middleware runs inside that window\n * — before any rendering starts — so a header set there is on the answer. A\n * component runs after it, during streaming, where the status line and headers\n * are already on the wire; setting one there is refused rather than dropped.\n */\ninterface Draft {\n headers: Headers;\n sealed: boolean;\n}\n\nconst DRAFT = Symbol.for(\"@rsc-kit/core.response-draft\");\n\nlet draftReady: Promise<void> | null = null;\n\ninterface DraftScope {\n getStore(): Draft | undefined;\n run<T>(store: Draft, fn: () => T): T;\n}\n\nfunction draft(): DraftScope | null {\n return (globals[DRAFT] as DraftScope | undefined) ?? null;\n}\n\n/** The open draft, or an explanation of why there is not one. */\nfunction writable(what: string): Draft {\n const open = draft()?.getStore();\n\n if (!open) {\n throw new Error(\n `${what} needs a response that has not been sent. Middleware and server actions have one; ` +\n \"a script outside a request does not.\",\n );\n }\n\n if (open.sealed) {\n throw new Error(\n `${what} was called after the response had been sent. Headers go out before the render ` +\n \"starts — that is what makes the first paint fast — so set them in middleware, which \" +\n \"runs before it.\",\n );\n }\n\n return open;\n}\n\n/**\n * Headers to put on the answer.\n *\n * Mutate it as you would any Headers. `append` is the one to reach for when a\n * header may legitimately appear twice; `set` replaces.\n */\n/**\n * The draft's headers, refusing writes once the response has gone.\n *\n * A proxy rather than the Headers itself: returning the live object means code\n * that captured it before the response was built can go on mutating a thing\n * nobody will read. Nothing in this package does that — the host copies the\n * headers onto the response the instant it seals — but \"safe because of the\n * order two other functions happen to run in\" is not a property, and an app\n * holding the object across an await would find writes silently doing nothing.\n */\nfunction sealable(draft: Draft): Headers {\n const guard =\n (method: \"set\" | \"append\" | \"delete\") =>\n (...args: [string, string]) => {\n writable(\"responseHeaders()\");\n\n return (draft.headers[method] as (...a: string[]) => void)(...args);\n };\n\n const proxy: Headers = new Proxy(draft.headers, {\n get(target, property, receiver) {\n if (\n property === \"set\" ||\n property === \"append\" ||\n property === \"delete\"\n ) {\n return guard(property);\n }\n\n // A maplike forEach passes the object it was called on as the third\n // callback argument, and binding to the target makes that the raw\n // Headers — a live, unguarded reference handed out by the very method\n // meant to be read-only. Substituted for the proxy so there is no way\n // through.\n if (property === \"forEach\") {\n return (\n fn: (value: string, key: string, parent: Headers) => void,\n thisArg?: unknown,\n ) =>\n target.forEach((value, key) => fn.call(thisArg, value, key, proxy));\n }\n\n const value = Reflect.get(target, property, receiver);\n\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n return proxy;\n}\n\nexport function responseHeaders(): Headers {\n return sealable(writable(\"responseHeaders()\"));\n}\n\n/**\n * Run a request with a response that can still be changed, and take what was\n * put on it.\n *\n * `seal()` is called by the host the moment it builds the Response, so\n * anything set afterwards is refused instead of silently going nowhere.\n */\nexport async function withResponseDraft<T>(\n run: (draft: { taken: () => Headers; seal: () => void }) => Promise<T>,\n): Promise<T> {\n if (!globals[DRAFT]) {\n draftReady ??= resolveScope().then((resolved) => {\n globals[DRAFT] ??= resolved as unknown as DraftScope;\n });\n\n await draftReady;\n }\n\n const open: Draft = { headers: new Headers(), sealed: false };\n\n return await draft()!.run(open, () =>\n run({\n taken: () => open.headers,\n seal: () => {\n open.sealed = true;\n },\n }),\n );\n}\n\n/**\n * A cookie name, as RFC 6265 defines one: a token, so no separators at all.\n *\n * Checked rather than escaped because there is no escaping — a name is not a\n * quoted string. An app that derives one from user input (`pref_${key}`) would\n * otherwise let that input close the pair and open another: a name of\n * `session=attacker; Path=/; HttpOnly; x` serializes to a header whose *first*\n * pair is a session cookie the caller chose, and the browser reads the first.\n */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\n/** No separator may appear in an attribute either, for the same reason. */\nconst COOKIE_ATTRIBUTE = /[;,\\r\\n]/;\n\n/** `name=value; Path=/; HttpOnly` — the header a browser expects. */\nexport function serializeCookie(cookie: {\n name: string;\n value: string;\n options: CookieOptions;\n}): string {\n if (!COOKIE_NAME.test(cookie.name)) {\n throw new Error(\n `Not a usable cookie name: ${JSON.stringify(cookie.name)}. ` +\n \"A name is a token — letters, digits and !#$%&'*+-.^_`|~ — with no spaces, \" +\n \"semicolons or equals signs. Nothing escapes them, so one cannot be encoded around.\",\n );\n }\n\n if (cookie.options.sameSite !== undefined) {\n const value = String(cookie.options.sameSite).toLowerCase();\n\n if (value !== \"strict\" && value !== \"lax\" && value !== \"none\") {\n throw new Error(\n `Not a SameSite value: ${JSON.stringify(cookie.options.sameSite)}. ` +\n \"It is written into the header as given, so anything else becomes further attributes.\",\n );\n }\n }\n\n for (const [attribute, value] of [\n [\"path\", cookie.options.path],\n [\"domain\", cookie.options.domain],\n // Trusted because it is typed as a Date — but a cast reaches this, and the\n // result lands in the header verbatim like the others.\n [\"expires\", cookie.options.expires?.toUTCString()],\n ] as const) {\n if (typeof value === \"string\" && COOKIE_ATTRIBUTE.test(value)) {\n throw new Error(\n `The cookie ${attribute} ${JSON.stringify(value)} contains a separator. ` +\n \"It would be read as further attributes rather than as part of this one.\",\n );\n }\n }\n\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n const o = cookie.options;\n\n if (o.maxAge !== undefined) parts.push(`Max-Age=${Math.floor(o.maxAge)}`);\n if (o.expires) parts.push(`Expires=${o.expires.toUTCString()}`);\n // Defaulted, because a cookie without one is scoped to the path that set it\n // — a session written by POST /_rsc/action would not be sent for any page.\n parts.push(`Path=${o.path ?? \"/\"}`);\n if (o.domain) parts.push(`Domain=${o.domain}`);\n if (o.sameSite)\n parts.push(`SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`);\n if (o.secure) parts.push(\"Secure\");\n if (o.httpOnly) parts.push(\"HttpOnly\");\n if (o.partitioned) parts.push(\"Partitioned\");\n\n return parts.join(\"; \");\n}\n\nexport async function cookies(): Promise<Cookies> {\n // Named before the await, because `headers()` never settles during a build —\n // nothing after this line runs there, and the reason would be recorded as\n // headers() for a call nobody wrote.\n const store = slot();\n\n if (!store.request) recordRead(store, \"cookies()\");\n\n const parsed = parseCookies((await headers()).get(\"cookie\") ?? \"\");\n\n const write = (\n name: string,\n value: string,\n options: CookieOptions = {},\n ): void => {\n // Appended, never set: several cookies on one response are several\n // Set-Cookie headers, and replacing would leave only the last.\n writable(\"cookies().set()\").headers.append(\n \"Set-Cookie\",\n serializeCookie({ name, value, options }),\n );\n };\n\n return {\n get: (name) => parsed[name],\n has: (name) => name in parsed,\n getAll: () => ({ ...parsed }),\n set: write,\n // Expired rather than removed: a browser drops a cookie when it is told\n // one has already passed, and there is no other way to say it.\n delete: (name, options = {}) => write(name, \"\", { ...options, maxAge: 0 }),\n };\n}\n\n/**\n * The whole request, for a host-specific need the accessors do not cover.\n *\n * Null on a host that forwards only the parts — the worker behind Laravel has\n * a socket, not a request. Use headers() and cookies(), which work everywhere.\n */\nexport async function request(): Promise<Request | null> {\n const store = slot();\n\n return store.request ? store.original : never(\"request()\");\n}\n\n/**\n * Mark everything below as belonging to the request, not to the build.\n *\n * export default async function Orders() {\n * await connection()\n *\n * const rows = await db.query('select * from orders')\n *\n * return <ul>{rows.map(...)}</ul>\n * }\n *\n * At build time this never resolves, so nothing after it runs: the query is not\n * made, the boundary above becomes a hole, and the rest of the page still\n * freezes. At request time it resolves immediately and the component runs\n * normally.\n *\n * **Once is enough.** It is a barrier, not a wrapper — everything after it in\n * this component belongs to the request, however many calls that is. Repeating\n * it before each query does nothing.\n *\n * It needs a boundary above it. With a <Suspense> or a loading.tsx there is a\n * fallback to store and the page becomes a shell; with neither, nothing can\n * paint and the build refuses the route rather than storing a blank.\n *\n * Reach for it when the build should not run something — a database the build\n * machine cannot see, or a value that must differ per visitor. A query the\n * build CAN run, whose answer is the same for everyone, wants none of this: it\n * should be frozen.\n *\n * The same name and behaviour as Next's `connection()`.\n */\nexport async function connection(): Promise<void> {\n const store = slot();\n\n // No request means a build. Suspend rather than continue, the same way\n // headers() and cookies() do.\n if (!store.request) return never(\"connection()\");\n}\n\n/** The url this request was made to, whichever way the host supplied it. */\nexport async function url(): Promise<string | null> {\n const store = slot();\n\n return store.request ? store.url : never(\"url()\");\n}\n\nfunction parseCookies(header: string): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const part of header.split(\";\")) {\n const eq = part.indexOf(\"=\");\n\n if (eq === -1) continue;\n\n const name = part.slice(0, eq).trim();\n\n if (!name) continue;\n\n // Decoded because that is how they were written. A malformed escape is\n // left as it arrived rather than throwing the render away.\n const raw = part.slice(eq + 1).trim();\n\n try {\n out[name] = decodeURIComponent(raw);\n } catch {\n out[name] = raw;\n }\n }\n\n return out;\n}\n\n/**\n * Run one request with its headers readable.\n *\n * `null` is the build: a scope with nothing in it, so a page that reads is\n * caught rather than silently frozen holding whatever the machine that built\n * it happened to send.\n */\nexport async function withRequest<T>(\n from: RequestLike,\n run: () => Promise<T>,\n): Promise<T> {\n if (!globals[SCOPE]) {\n ready ??= resolveScope().then((resolved) => {\n globals[SCOPE] ??= resolved as unknown as Scope;\n });\n\n await ready;\n }\n\n const isRequest = from instanceof Request;\n const store: Slot = {\n request: from !== null,\n url: from ? (isRequest ? from.url : from.url) : null,\n headers: from\n ? isRequest\n ? from.headers\n : new Headers(from.headers)\n : new Headers(),\n original: isRequest ? from : null,\n read: false,\n readBy: [],\n readWhere: [],\n fallbacks: [],\n awaiters: new Map(),\n inHelper: null,\n readVia: new Map(),\n };\n\n return await scope()!.run(store, run);\n}\n\n/**\n * A promise that never settles, so the caller suspends.\n *\n * What a read does during a build. React treats it exactly as it treats a host\n * call that never answers: the component suspends, its Suspense fallback goes\n * into the shell, and the probe's budget decides the rest.\n */\n/**\n * Record a read without suspending on it.\n *\n * For a reader that CAN answer during a build but whose answer would be wrong\n * to store — an api route's query string, which exists and is empty, and which\n * the build has no business freezing an answer to. `never()` is for a reader\n * with nothing to return; this is for one that has something and should still\n * be noticed.\n *\n * A no-op outside a render scope, so a handler called at request time pays a\n * property read and nothing else.\n */\nexport function noteRequestRead(by: string): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n recordRead(store, by);\n}\n\nfunction never(by: string): Promise<never> {\n const store = slot();\n\n store.read = true;\n recordRead(store, by);\n\n return new Promise(() => {});\n}\n\n/**\n * Whether anything read the request during the scope that is open.\n *\n * For the prerenderer, which opens one with no request and asks afterwards.\n */\nexport function requestWasRead(): boolean {\n return scope()?.getStore()?.read ?? false;\n}\n\n/**\n * What reached for the request during the scope that is open.\n *\n * For the build, which asks afterwards so it can say why a page is rendered per\n * visitor rather than only that it is.\n */\nexport function requestReadBy(): string[] {\n return scope()?.getStore()?.readBy ?? [];\n}\n\n/** A read React caught at a boundary during SSR: `useSearchParams() in Query`. */\nexport function noteFallback(text: string): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n store.fallbacks ??= [];\n\n if (!store.fallbacks.includes(text)) store.fallbacks.push(text);\n}\n\n/** The reads caught at a boundary during this render, with their components. */\nexport function requestFallbacks(): string[] {\n return scope()?.getStore()?.fallbacks ?? [];\n}\n\n/**\n * A component called a cache()-wrapped helper. Recorded on every call, hit\n * or miss, so a read the helper makes can name everyone who waited for it.\n */\nexport function noteAwaiter(helper: unknown): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n const { name } = caller(\"cache\");\n\n if (!name) return;\n\n store.awaiters ??= new Map();\n\n const list = store.awaiters.get(helper) ?? [];\n\n if (!list.includes(name)) list.push(name);\n\n store.awaiters.set(helper, list);\n}\n\n/** Run a cache()-wrapped helper's body, so a read inside it is filed under the helper. */\nexport function withHelper<T>(helper: unknown, run: () => T): T {\n const store = scope()?.getStore();\n\n if (!store) return run();\n\n const before = store.inHelper;\n\n store.inHelper = helper;\n\n try {\n return run();\n } finally {\n store.inHelper = before;\n }\n}\n\n/**\n * The same reads, each with the component that made it: `cookies() in\n * RootLayout`. A read inside a cached helper with more than one awaiter\n * names them all - `connection() awaited by AuthLinks, AuthDialogSlot` -\n * because the stack saw only the first, and the first is rarely the one\n * that blocks.\n */\nexport function requestReadWhere(): string[] {\n const store = scope()?.getStore();\n\n if (!store?.readWhere?.length) return store?.readBy ?? [];\n\n return store.readWhere.map((entry) => {\n const helper = store.readVia?.get(entry);\n const awaiters = helper ? store.awaiters?.get(helper) : undefined;\n\n if (!awaiters || awaiters.length < 2) return entry;\n\n const names =\n awaiters.length === 2\n ? awaiters.join(\" and \")\n : awaiters.slice(0, -1).join(\", \") + \" and \" + awaiters.at(-1);\n\n return `${entry.split(\" in \")[0]} awaited by ${names}`;\n });\n}\n\n/**\n * The query string, as a page receives it.\n *\n * Derived from the request rather than passed down the render, so no host has\n * to forward it separately — and so it suspends during a build for the same\n * reason every other read does. A frozen page cannot know the query it will be\n * asked for.\n */\nexport async function searchParams(): Promise<URLSearchParams> {\n // Named before the await, so the build reports the call someone wrote rather\n // than url(), which this happens to be built on.\n const store = slot();\n\n if (!store.request && !store.readBy.includes(\"searchParams()\")) {\n store.readBy.push(\"searchParams()\");\n }\n\n const from = await url();\n\n return from ? new URL(from).searchParams : new URLSearchParams();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading the request from inside a render.\n//\n// import { headers, cookies } from '@rsc-kit/core/request'\n//\n// export default async function middleware() {\n// const locale = cookies().get('locale')?.value ?? negotiate(headers().get('accept-language'))\n// if (!locale) redirect('/en')\n// }\n//\n// Server components have no arguments beyond their props, and middleware has\n// none at all, so without this an app on a JavaScript host cannot see a header\n// — no locale negotiation, no subdomain, no cookie. The Laravel host escaped\n// that through rpc(), because PHP has the request; this is the same thing for\n// everyone else.\n//\n// Read-only on purpose. Setting a cookie means writing a response header, and\n// by the time a component renders the shell may already have been flushed — an\n// API that appears to set one and silently does not is worse than not having\n// it. Set them in the host, which owns the response.\n//\n// Asynchronous on purpose too, and not for ergonomics. A build has no request,\n// so a read there suspends and never settles — which is the same thing the\n// prerender probe does to a host call. Anything still waiting when the probe's\n// budget expires becomes a fallback in the frozen shell, so a read inside a\n// Suspense boundary leaves the shell frozen and makes only that boundary\n// dynamic. Reading synchronously could only mark the whole route dynamic,\n// because there would be nothing to suspend on.\n//\n// Scoped to the request, on the same store cache() uses. Two requests in\n// flight cannot see each other's headers.\n\nimport { resolveScope } from \"./revalidate.js\";\n\n/** What a host can supply: a real Request, or the parts of one. */\nexport type RequestLike =\n Request | { url: string; headers: Record<string, string> } | null;\n\ninterface Slot {\n /** False during a build: there is nothing to answer with. */\n request: boolean;\n url: string | null;\n /**\n * Held as Headers rather than as a Request.\n *\n * `new Request(url, { headers })` drops Cookie: a spec-compliant Headers\n * built with the \"request\" guard refuses the forbidden header names, and\n * Cookie is one of them. `new Headers()` has no guard and keeps it. Bun\n * happens to allow both, which is why rebuilding a Request looked fine until\n * it ran anywhere stricter — and what it loses is every cookie, silently.\n */\n headers: Headers;\n original: Request | null;\n /**\n * Whether anything asked. Only for the build's report — the classification\n * comes from the suspension, not from this.\n */\n read: boolean;\n /**\n * What read it, in the order they did.\n *\n * Only the name of the accessor — `cookies()`, `headers()` — which is enough\n * for the build to say why a page could not be frozen. Without it the answer\n * is \"something in this page reached for the request\", and finding which is\n * the part that takes an afternoon.\n */\n readBy: string[];\n /**\n * The same reads with the component that made each one - `cookies() in\n * RootLayout` - taken from the stack at the moment of the read, which is\n * still the component's own call: an accessor runs synchronously up to\n * the point it suspends. Only the build reads this. \"Reads the request\" is\n * a category; this is the line to open.\n */\n readWhere: string[];\n /**\n * Reads the server could not answer and React caught at a boundary - the\n * fallback is what got stored - with the component that made each:\n * `useSearchParams() in Query`. Noted by the SSR entry, read by the build,\n * which attaches them to the route so a page stored as its own loading\n * screen says why.\n */\n fallbacks: string[];\n /**\n * For a read made inside a cache()-wrapped helper: the components that\n * awaited that helper, first caller included. The helper runs once, so\n * the stack at the read names only whoever got there first; every later\n * awaiter is a cache hit that never reaches the accessor. The one above\n * every boundary is usually one of the later ones.\n */\n awaiters: Map<unknown, string[]>;\n /** The cache()-wrapped helper whose body is running, while it runs. */\n inHelper: unknown;\n /** readWhere entry -> the helper it was read inside. */\n readVia: Map<string, unknown>;\n /** Work to run once the answer is on its way - see after(). */\n after: (() => unknown)[];\n}\n\nconst SCOPE = Symbol.for(\"@rsc-kit/core.request-scope\");\n\n/**\n * The accessors themselves and the plumbing under them: frames to walk past\n * on the way to the component that called.\n */\nconst NOT_A_CALLER = new Set([\n \"never\",\n \"cache\",\n \"noteAwaiter\",\n \"withHelper\",\n \"noteRequestRead\",\n \"recordRead\",\n \"caller\",\n \"slot\",\n \"cookies\",\n \"headers\",\n \"connection\",\n \"searchParams\",\n \"request\",\n \"url\",\n \"parseParams\",\n \"parseSearchParams\",\n \"parseBody\",\n \"get\",\n \"set\",\n \"has\",\n]);\n\n/** The accessors a component calls by name. One calling another is plumbing, not a second read. */\nconst ACCESSORS = new Set([\n \"cookies\",\n \"headers\",\n \"connection\",\n \"searchParams\",\n \"request\",\n \"url\",\n]);\n\n/**\n * The nearest named function above the accessor on the stack, and whether\n * another accessor sits between: `cookies()` reads through `headers()`, and\n * that inner read is not a second thing the component did.\n *\n * Read from a thrown-away Error: the one thing a build cannot otherwise learn\n * is which component reached for the request, and the stack knows. Function\n * names survive a server bundle, which is not minified, so this is\n * `RootLayout` and not a line number in a chunk. Internal names are walked\n * past; an anonymous frame is skipped rather than reported as `<anonymous>`.\n */\nfunction caller(own: string): { name: string | null; nested: boolean } {\n const stack = new Error().stack ?? \"\";\n let accessors = 0;\n let helper: string | null = null;\n\n for (const line of stack.split(\"\\n\")) {\n const match = /^\\s*at (?:async )?([^\\s(]+)/.exec(line);\n\n if (!match) continue;\n\n const name = match[1]!.split(\".\").pop()!;\n\n // A frame with no function name prints its location instead - `js:6054:8`\n // - which is not a name to report.\n if (!/^[\\w$]+$/.test(name)) continue;\n\n if (ACCESSORS.has(name)) accessors++;\n if (\n NOT_A_CALLER.has(name) ||\n name === \"<anonymous>\" ||\n name === \"Object\" ||\n name === \"Promise\"\n )\n continue;\n\n const nested = accessors > (ACCESSORS.has(own) ? 1 : 0);\n\n // A function that is not a component - getCurrentUser, wrapped in\n // cache() - is the shared helper the read lives in. Named, because with\n // cache() the read runs once, on whichever component called first, and\n // every other caller of the helper is invisible to the stack. The helper\n // is what they have in common.\n if (/^[a-z_$]/.test(name) && !/^use[A-Z]/.test(name)) {\n helper ??= name;\n\n continue;\n }\n\n return {\n name: helper && helper !== name ? `${helper} (from ${name})` : name,\n nested,\n };\n }\n\n return { name: helper, nested: accessors > (ACCESSORS.has(own) ? 1 : 0) };\n}\n\nfunction recordRead(store: Slot, by: string): void {\n if (!store.readBy.includes(by)) store.readBy.push(by);\n\n // Older stores, written by a bundle built before this field existed.\n store.readWhere ??= [];\n\n const { name, nested } = caller(by.replace(/\\(.*$/, \"\"));\n\n if (nested) return;\n\n const entry = name ? `${by} in ${name}` : by;\n\n if (!store.readWhere.includes(entry)) store.readWhere.push(entry);\n if (store.inHelper) (store.readVia ??= new Map()).set(entry, store.inHelper);\n}\n\nconst globals = globalThis as Record<symbol | string, unknown>;\n\nlet ready: Promise<void> | null = null;\n\ninterface Scope {\n getStore(): Slot | undefined;\n run<T>(store: Slot, fn: () => T): T;\n}\n\nfunction scope(): Scope | null {\n return (globals[SCOPE] as Scope | undefined) ?? null;\n}\n\nfunction slot(): Slot {\n const store = scope()?.getStore();\n\n if (!store) {\n throw new Error(\n \"No request in scope. headers() and cookies() are for a render — middleware, a layout, \" +\n \"a page — and there is nothing to read outside one.\",\n );\n }\n\n store.read = true;\n\n return store;\n}\n\n/**\n * The request's headers.\n *\n * Empty during a build, where there is no request: the page is marked as\n * needing one instead, and rendered on demand rather than frozen.\n */\nexport async function headers(): Promise<Headers> {\n const store = slot();\n\n return store.request ? store.headers : never(\"headers()\");\n}\n\n/** How a cookie should be written. The names browsers use. */\nexport interface CookieOptions {\n path?: string;\n domain?: string;\n maxAge?: number;\n expires?: Date;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n partitioned?: boolean;\n}\n\n/** One cookie as the request carried it: the shape Next's `cookies().get()` returns. */\nexport interface RequestCookie {\n name: string;\n value: string;\n}\n\n/**\n * The cookie jar, in the shape Next's `cookies()` has.\n *\n * `get()` returns `{ name, value }` rather than the string, because the guide\n * says \"the same names from `@rsc-kit/core/request`\" and a name that is the\n * same with a different return shape is the worst of both: ported code\n * reading `?.value` off a string got `undefined`, silently. `getAll()` is a\n * list for the same reason, and because a list composes where a record does\n * not.\n */\nexport interface Cookies {\n get(name: string): RequestCookie | undefined;\n has(name: string): boolean;\n getAll(): RequestCookie[];\n /**\n * Write one on the response.\n *\n * Only from a server action, which has a response of its own that has not\n * been sent. A render has already flushed its headers by the time a\n * component runs — that is what makes the first paint fast — so this throws\n * there rather than appearing to work.\n *\n * Either call shape Next takes: `set(name, value, options)` or\n * `set({ name, value, ...options })`.\n */\n set(name: string, value: string, options?: CookieOptions): void;\n set(cookie: RequestCookie & CookieOptions): void;\n /** Write one that expires immediately. Same rule about where. */\n delete(name: string, options?: CookieOptions): void;\n}\n\n/** Kept for the name it had when it could only read. */\nexport type ReadonlyCookies = Cookies;\n\n/**\n * The response being built, while it can still be changed.\n *\n * Open from the moment a request is picked up until the host constructs its\n * Response, and sealed the instant it does. Middleware runs inside that window\n * — before any rendering starts — so a header set there is on the answer. A\n * component runs after it, during streaming, where the status line and headers\n * are already on the wire; setting one there is refused rather than dropped.\n */\ninterface Draft {\n headers: Headers;\n sealed: boolean;\n}\n\nconst DRAFT = Symbol.for(\"@rsc-kit/core.response-draft\");\n\nlet draftReady: Promise<void> | null = null;\n\ninterface DraftScope {\n getStore(): Draft | undefined;\n run<T>(store: Draft, fn: () => T): T;\n}\n\nfunction draft(): DraftScope | null {\n return (globals[DRAFT] as DraftScope | undefined) ?? null;\n}\n\n/** The open draft, or an explanation of why there is not one. */\nfunction writable(what: string): Draft {\n const open = draft()?.getStore();\n\n if (!open) {\n throw new Error(\n `${what} needs a response that has not been sent. Middleware and server actions have one; ` +\n \"a script outside a request does not.\",\n );\n }\n\n if (open.sealed) {\n throw new Error(\n `${what} was called after the response had been sent. Headers go out before the render ` +\n \"starts — that is what makes the first paint fast — so set them in middleware, which \" +\n \"runs before it.\",\n );\n }\n\n return open;\n}\n\n/**\n * Headers to put on the answer.\n *\n * Mutate it as you would any Headers. `append` is the one to reach for when a\n * header may legitimately appear twice; `set` replaces.\n */\n/**\n * The draft's headers, refusing writes once the response has gone.\n *\n * A proxy rather than the Headers itself: returning the live object means code\n * that captured it before the response was built can go on mutating a thing\n * nobody will read. Nothing in this package does that — the host copies the\n * headers onto the response the instant it seals — but \"safe because of the\n * order two other functions happen to run in\" is not a property, and an app\n * holding the object across an await would find writes silently doing nothing.\n */\nfunction sealable(draft: Draft): Headers {\n const guard =\n (method: \"set\" | \"append\" | \"delete\") =>\n (...args: [string, string]) => {\n writable(\"responseHeaders()\");\n\n return (draft.headers[method] as (...a: string[]) => void)(...args);\n };\n\n const proxy: Headers = new Proxy(draft.headers, {\n get(target, property, receiver) {\n if (\n property === \"set\" ||\n property === \"append\" ||\n property === \"delete\"\n ) {\n return guard(property);\n }\n\n // A maplike forEach passes the object it was called on as the third\n // callback argument, and binding to the target makes that the raw\n // Headers — a live, unguarded reference handed out by the very method\n // meant to be read-only. Substituted for the proxy so there is no way\n // through.\n if (property === \"forEach\") {\n return (\n fn: (value: string, key: string, parent: Headers) => void,\n thisArg?: unknown,\n ) =>\n target.forEach((value, key) => fn.call(thisArg, value, key, proxy));\n }\n\n const value = Reflect.get(target, property, receiver);\n\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n return proxy;\n}\n\nexport function responseHeaders(): Headers {\n return sealable(writable(\"responseHeaders()\"));\n}\n\n/**\n * Run a request with a response that can still be changed, and take what was\n * put on it.\n *\n * `seal()` is called by the host the moment it builds the Response, so\n * anything set afterwards is refused instead of silently going nowhere.\n */\nexport async function withResponseDraft<T>(\n run: (draft: { taken: () => Headers; seal: () => void }) => Promise<T>,\n): Promise<T> {\n if (!globals[DRAFT]) {\n draftReady ??= resolveScope().then((resolved) => {\n globals[DRAFT] ??= resolved as unknown as DraftScope;\n });\n\n await draftReady;\n }\n\n const open: Draft = { headers: new Headers(), sealed: false };\n\n return await draft()!.run(open, () =>\n run({\n taken: () => open.headers,\n seal: () => {\n open.sealed = true;\n },\n }),\n );\n}\n\n/**\n * A cookie name, as RFC 6265 defines one: a token, so no separators at all.\n *\n * Checked rather than escaped because there is no escaping — a name is not a\n * quoted string. An app that derives one from user input (`pref_${key}`) would\n * otherwise let that input close the pair and open another: a name of\n * `session=attacker; Path=/; HttpOnly; x` serializes to a header whose *first*\n * pair is a session cookie the caller chose, and the browser reads the first.\n */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\n/** No separator may appear in an attribute either, for the same reason. */\nconst COOKIE_ATTRIBUTE = /[;,\\r\\n]/;\n\n/** `name=value; Path=/; HttpOnly` — the header a browser expects. */\nexport function serializeCookie(cookie: {\n name: string;\n value: string;\n options: CookieOptions;\n}): string {\n if (!COOKIE_NAME.test(cookie.name)) {\n throw new Error(\n `Not a usable cookie name: ${JSON.stringify(cookie.name)}. ` +\n \"A name is a token — letters, digits and !#$%&'*+-.^_`|~ — with no spaces, \" +\n \"semicolons or equals signs. Nothing escapes them, so one cannot be encoded around.\",\n );\n }\n\n if (cookie.options.sameSite !== undefined) {\n const value = String(cookie.options.sameSite).toLowerCase();\n\n if (value !== \"strict\" && value !== \"lax\" && value !== \"none\") {\n throw new Error(\n `Not a SameSite value: ${JSON.stringify(cookie.options.sameSite)}. ` +\n \"It is written into the header as given, so anything else becomes further attributes.\",\n );\n }\n }\n\n for (const [attribute, value] of [\n [\"path\", cookie.options.path],\n [\"domain\", cookie.options.domain],\n // Trusted because it is typed as a Date — but a cast reaches this, and the\n // result lands in the header verbatim like the others.\n [\"expires\", cookie.options.expires?.toUTCString()],\n ] as const) {\n if (typeof value === \"string\" && COOKIE_ATTRIBUTE.test(value)) {\n throw new Error(\n `The cookie ${attribute} ${JSON.stringify(value)} contains a separator. ` +\n \"It would be read as further attributes rather than as part of this one.\",\n );\n }\n }\n\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n const o = cookie.options;\n\n if (o.maxAge !== undefined) parts.push(`Max-Age=${Math.floor(o.maxAge)}`);\n if (o.expires) parts.push(`Expires=${o.expires.toUTCString()}`);\n // Defaulted, because a cookie without one is scoped to the path that set it\n // — a session written by POST /_rsc/action would not be sent for any page.\n parts.push(`Path=${o.path ?? \"/\"}`);\n if (o.domain) parts.push(`Domain=${o.domain}`);\n if (o.sameSite)\n parts.push(`SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`);\n if (o.secure) parts.push(\"Secure\");\n if (o.httpOnly) parts.push(\"HttpOnly\");\n if (o.partitioned) parts.push(\"Partitioned\");\n\n return parts.join(\"; \");\n}\n\nexport async function cookies(): Promise<Cookies> {\n // Named before the await, because `headers()` never settles during a build —\n // nothing after this line runs there, and the reason would be recorded as\n // headers() for a call nobody wrote.\n const store = slot();\n\n if (!store.request) recordRead(store, \"cookies()\");\n\n const parsed = parseCookies((await headers()).get(\"cookie\") ?? \"\");\n\n const write = (\n name: string,\n value: string,\n options: CookieOptions = {},\n ): void => {\n // Appended, never set: several cookies on one response are several\n // Set-Cookie headers, and replacing would leave only the last.\n writable(\"cookies().set()\").headers.append(\n \"Set-Cookie\",\n serializeCookie({ name, value, options }),\n );\n };\n\n return {\n get: (name) => (name in parsed ? { name, value: parsed[name] } : undefined),\n has: (name) => name in parsed,\n getAll: () => Object.entries(parsed).map(([name, value]) => ({ name, value })),\n set: (nameOrCookie: string | (RequestCookie & CookieOptions), value?: string, options?: CookieOptions) => {\n if (typeof nameOrCookie === \"string\") return write(nameOrCookie, value ?? \"\", options);\n\n const { name, value: v, ...rest } = nameOrCookie;\n\n return write(name, v, rest);\n },\n // Expired rather than removed: a browser drops a cookie when it is told\n // one has already passed, and there is no other way to say it.\n delete: (name, options = {}) => write(name, \"\", { ...options, maxAge: 0 }),\n };\n}\n\n/**\n * The whole request, for a host-specific need the accessors do not cover.\n *\n * Null on a host that forwards only the parts — the worker behind Laravel has\n * a socket, not a request. Use headers() and cookies(), which work everywhere.\n */\nexport async function request(): Promise<Request | null> {\n const store = slot();\n\n return store.request ? store.original : never(\"request()\");\n}\n\n/**\n * Mark everything below as belonging to the request, not to the build.\n *\n * export default async function Orders() {\n * await connection()\n *\n * const rows = await db.query('select * from orders')\n *\n * return <ul>{rows.map(...)}</ul>\n * }\n *\n * At build time this never resolves, so nothing after it runs: the query is not\n * made, the boundary above becomes a hole, and the rest of the page still\n * freezes. At request time it resolves immediately and the component runs\n * normally.\n *\n * **Once is enough.** It is a barrier, not a wrapper — everything after it in\n * this component belongs to the request, however many calls that is. Repeating\n * it before each query does nothing.\n *\n * It needs a boundary above it. With a <Suspense> or a loading.tsx there is a\n * fallback to store and the page becomes a shell; with neither, nothing can\n * paint and the build refuses the route rather than storing a blank.\n *\n * Reach for it when the build should not run something — a database the build\n * machine cannot see, or a value that must differ per visitor. A query the\n * build CAN run, whose answer is the same for everyone, wants none of this: it\n * should be frozen.\n *\n * The same name and behaviour as Next's `connection()`.\n */\nexport async function connection(): Promise<void> {\n const store = slot();\n\n // No request means a build. Suspend rather than continue, the same way\n // headers() and cookies() do.\n if (!store.request) return never(\"connection()\");\n}\n\n/** The url this request was made to, whichever way the host supplied it. */\nexport async function url(): Promise<string | null> {\n const store = slot();\n\n return store.request ? store.url : never(\"url()\");\n}\n\nfunction parseCookies(header: string): Record<string, string> {\n const out: Record<string, string> = {};\n\n for (const part of header.split(\";\")) {\n const eq = part.indexOf(\"=\");\n\n if (eq === -1) continue;\n\n const name = part.slice(0, eq).trim();\n\n if (!name) continue;\n\n // Decoded because that is how they were written. A malformed escape is\n // left as it arrived rather than throwing the render away.\n const raw = part.slice(eq + 1).trim();\n\n try {\n out[name] = decodeURIComponent(raw);\n } catch {\n out[name] = raw;\n }\n }\n\n return out;\n}\n\n/**\n * Run one request with its headers readable.\n *\n * `null` is the build: a scope with nothing in it, so a page that reads is\n * caught rather than silently frozen holding whatever the machine that built\n * it happened to send.\n */\nexport async function withRequest<T>(\n from: RequestLike,\n run: () => Promise<T>,\n): Promise<T> {\n if (!globals[SCOPE]) {\n ready ??= resolveScope().then((resolved) => {\n globals[SCOPE] ??= resolved as unknown as Scope;\n });\n\n await ready;\n }\n\n const isRequest = from instanceof Request;\n const store: Slot = {\n request: from !== null,\n url: from ? (isRequest ? from.url : from.url) : null,\n headers: from\n ? isRequest\n ? from.headers\n : new Headers(from.headers)\n : new Headers(),\n original: isRequest ? from : null,\n read: false,\n readBy: [],\n readWhere: [],\n fallbacks: [],\n awaiters: new Map(),\n inHelper: null,\n readVia: new Map(),\n after: [],\n };\n\n return await scope()!.run(store, run);\n}\n\n/**\n * A promise that never settles, so the caller suspends.\n *\n * What a read does during a build. React treats it exactly as it treats a host\n * call that never answers: the component suspends, its Suspense fallback goes\n * into the shell, and the probe's budget decides the rest.\n */\n/**\n * The build's probe hands a route a Request that records what is read out of\n * it, and a read of `url` marks the route as depending on the caller — the\n * Next way to read a query is `new URL(request.url).searchParams`, which the\n * probe cannot otherwise see. The engine itself reads the url to resolve the\n * awaited `searchParams`, and that read is accounted for by `searchParams`,\n * not by `url`; this is the door it goes through. A probe answers the real\n * Request to this key; anything else answers nothing, and the request is its\n * own.\n */\nexport const UNPROBED = Symbol.for(\"rsc-kit.unprobed\");\n\n/** The request's url, read by the engine rather than the route. */\nexport function urlOf(request: Request): string {\n return ((request as unknown as Record<symbol, Request | undefined>)[UNPROBED] ?? request).url;\n}\n\n/**\n * Record a read without suspending on it.\n *\n * For a reader that CAN answer during a build but whose answer would be wrong\n * to store — an api route's query string, which exists and is empty, and which\n * the build has no business freezing an answer to. `never()` is for a reader\n * with nothing to return; this is for one that has something and should still\n * be noticed.\n *\n * A no-op outside a render scope, so a handler called at request time pays a\n * property read and nothing else.\n */\nexport function noteRequestRead(by: string): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n recordRead(store, by);\n}\n\nfunction never(by: string): Promise<never> {\n const store = slot();\n\n store.read = true;\n recordRead(store, by);\n\n return new Promise(() => {});\n}\n\n/**\n * Whether anything read the request during the scope that is open.\n *\n * For the prerenderer, which opens one with no request and asks afterwards.\n */\nexport function requestWasRead(): boolean {\n return scope()?.getStore()?.read ?? false;\n}\n\n/**\n * What reached for the request during the scope that is open.\n *\n * For the build, which asks afterwards so it can say why a page is rendered per\n * visitor rather than only that it is.\n */\nexport function requestReadBy(): string[] {\n return scope()?.getStore()?.readBy ?? [];\n}\n\n/** A read React caught at a boundary during SSR: `useSearchParams() in Query`. */\nexport function noteFallback(text: string): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n store.fallbacks ??= [];\n\n if (!store.fallbacks.includes(text)) store.fallbacks.push(text);\n}\n\n/**\n * Run something once the answer is on its way, without making it wait.\n *\n * Logging, an audit row, an email, a cache warm: work the visitor should not\n * pay for, and that must still finish. Next's `after()`, and needed for the\n * same reason on every host: on a long-lived process a detached promise\n * happens to run to completion, but a Worker tears the isolate down when the\n * response ends unless the work is registered with the platform's\n * `waitUntil` - so a fire-and-forget promise there dies silently, some of\n * the time. The host hands these to `waitUntil` where one exists and runs\n * them detached where a process will keep them.\n *\n * From a component, a middleware, a server action or an api route. A\n * rejection is reported and never reaches the response, which has already\n * gone. Outside a request - a build - the work runs at once.\n */\nexport function after(work: () => unknown): void {\n const store = scope()?.getStore();\n\n if (!store || !store.request) {\n void Promise.resolve().then(work).catch(reportAfter);\n\n return;\n }\n\n store.after.push(work);\n}\n\nfunction reportAfter(error: unknown): void {\n console.error(\"[rsc-kit] after() work failed:\", error);\n}\n\n/**\n * @internal For the host: the work `after()` collected for this request, as\n * one promise that never rejects. Taken once; a second call has nothing.\n */\nexport function takeAfterWork(): Promise<void> | null {\n const store = scope()?.getStore();\n\n if (!store || store.after.length === 0) return null;\n\n const work = store.after.splice(0);\n\n return Promise.allSettled(\n work.map((run) => Promise.resolve().then(run)),\n ).then((results) => {\n for (const result of results) {\n if (result.status === \"rejected\") reportAfter(result.reason);\n }\n });\n}\n\n/** The reads caught at a boundary during this render, with their components. */\nexport function requestFallbacks(): string[] {\n return scope()?.getStore()?.fallbacks ?? [];\n}\n\n/**\n * A component called a cache()-wrapped helper. Recorded on every call, hit\n * or miss, so a read the helper makes can name everyone who waited for it.\n */\nexport function noteAwaiter(helper: unknown): void {\n const store = scope()?.getStore();\n\n if (!store) return;\n\n const { name } = caller(\"cache\");\n\n if (!name) return;\n\n store.awaiters ??= new Map();\n\n const list = store.awaiters.get(helper) ?? [];\n\n if (!list.includes(name)) list.push(name);\n\n store.awaiters.set(helper, list);\n}\n\n/** Run a cache()-wrapped helper's body, so a read inside it is filed under the helper. */\nexport function withHelper<T>(helper: unknown, run: () => T): T {\n const store = scope()?.getStore();\n\n if (!store) return run();\n\n const before = store.inHelper;\n\n store.inHelper = helper;\n\n try {\n return run();\n } finally {\n store.inHelper = before;\n }\n}\n\n/**\n * The same reads, each with the component that made it: `cookies() in\n * RootLayout`. A read inside a cached helper with more than one awaiter\n * names them all - `connection() awaited by AuthLinks, AuthDialogSlot` -\n * because the stack saw only the first, and the first is rarely the one\n * that blocks.\n */\nexport function requestReadWhere(): string[] {\n const store = scope()?.getStore();\n\n if (!store?.readWhere?.length) return store?.readBy ?? [];\n\n return store.readWhere.map((entry) => {\n const helper = store.readVia?.get(entry);\n const awaiters = helper ? store.awaiters?.get(helper) : undefined;\n\n if (!awaiters || awaiters.length < 2) return entry;\n\n const names =\n awaiters.length === 2\n ? awaiters.join(\" and \")\n : awaiters.slice(0, -1).join(\", \") + \" and \" + awaiters.at(-1);\n\n return `${entry.split(\" in \")[0]} awaited by ${names}`;\n });\n}\n\n/**\n * The query string, as a page receives it.\n *\n * Derived from the request rather than passed down the render, so no host has\n * to forward it separately — and so it suspends during a build for the same\n * reason every other read does. A frozen page cannot know the query it will be\n * asked for.\n */\nexport async function searchParams(): Promise<URLSearchParams> {\n // Named before the await, so the build reports the call someone wrote rather\n // than url(), which this happens to be built on.\n const store = slot();\n\n if (!store.request && !store.readBy.includes(\"searchParams()\")) {\n store.readBy.push(\"searchParams()\");\n }\n\n const from = await url();\n\n return from ? new URL(from).searchParams : new URLSearchParams();\n}\n"]}
|
package/dist/routeSchema.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ApiPattern } from './routes.js';
|
|
1
2
|
/**
|
|
2
3
|
* Standard Schema, restated rather than depended on.
|
|
3
4
|
*
|
|
@@ -43,6 +44,54 @@ export interface PageProps<P = never, S = never> {
|
|
|
43
44
|
params: Promise<[P] extends [never] ? Record<string, string> : ParamsOf<P>>;
|
|
44
45
|
searchParams: Promise<[S] extends [never] ? URLSearchParams : ParamsOf<S>>;
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The names a pattern binds: `/api/greet/[name]` binds `name`, and
|
|
49
|
+
* `/docs/[...path]` binds `path` — one string, the slashes kept.
|
|
50
|
+
*/
|
|
51
|
+
type SegmentNames<P extends string> = P extends `${string}[...${infer N}]${infer Rest}` ? N | SegmentNames<Rest> : P extends `${string}[${infer N}]${infer Rest}` ? N | SegmentNames<Rest> : never;
|
|
52
|
+
/** What a route's params resolve to, from its pattern: `{ name: string }`. */
|
|
53
|
+
export type ApiParams<P extends string> = {
|
|
54
|
+
[K in SegmentNames<P>]: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The second argument of an api route's handler.
|
|
58
|
+
*
|
|
59
|
+
* Every field is a promise, because the engine hands them lazily - a route
|
|
60
|
+
* that never awaits its query string provably does not vary by it, so the
|
|
61
|
+
* build can store one answer - and a sync read of `params.name` has to be a
|
|
62
|
+
* compile error rather than a route that quietly answers 404 to everything.
|
|
63
|
+
*
|
|
64
|
+
* Name the route and the params are typed from its segments; the pattern is
|
|
65
|
+
* checked against the routes the build found, so a typo fails `tsc`:
|
|
66
|
+
*
|
|
67
|
+
* export async function GET(request: Request, { params }: RouteContext<'/api/greet/[name]'>) {
|
|
68
|
+
* const { name } = await params // string
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* Or hand it the route's own schemas, the way PageProps takes a page's:
|
|
72
|
+
*
|
|
73
|
+
* export const params = z.object({ id: z.coerce.number() })
|
|
74
|
+
* export async function GET(request: Request, { params }: RouteContext<typeof params>) {
|
|
75
|
+
* const { id } = await params // number
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* With neither, params is the raw segments and searchParams the raw
|
|
79
|
+
* URLSearchParams, still behind a promise.
|
|
80
|
+
*/
|
|
81
|
+
export interface RouteContext<P extends ApiPattern | StandardSchemaV1 = never, S extends StandardSchemaV1 = never, B extends StandardSchemaV1 = never> {
|
|
82
|
+
params: Promise<[
|
|
83
|
+
P
|
|
84
|
+
] extends [never] ? Record<string, string> : P extends ApiPattern ? ApiParams<P> : ParamsOf<P>>;
|
|
85
|
+
searchParams: Promise<[S] extends [never] ? URLSearchParams : ParamsOf<S>>;
|
|
86
|
+
/** Undefined for a method with no body; a promise of the parsed body otherwise. */
|
|
87
|
+
body: [B] extends [never] ? Promise<unknown> | undefined : Promise<ParamsOf<B>>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A whole handler, typed from the route it answers:
|
|
91
|
+
*
|
|
92
|
+
* export const GET: RouteHandler<'/api/greet/[name]'> = async (request, { params }) => …
|
|
93
|
+
*/
|
|
94
|
+
export type RouteHandler<P extends ApiPattern | StandardSchemaV1 = never, S extends StandardSchemaV1 = never, B extends StandardSchemaV1 = never> = (request: Request, context: RouteContext<P, S, B>) => Response | Promise<Response>;
|
|
46
95
|
/** A query string the page's own schema refused. */
|
|
47
96
|
export declare class SearchParamsError extends Error {
|
|
48
97
|
readonly errors: Record<string, string[]>;
|
package/dist/routeSchema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routeSchema.js","sourceRoot":"","sources":["../src/routeSchema.ts"],"names":[],"mappings":"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","sourcesContent":["// Typing and checking what a url carries.\n//\n// // src/app/posts/[slug]/page.tsx\n// export const params = z.object({ slug: z.string().min(1) })\n// export const searchParams = z.object({\n// page: z.coerce.number().int().min(1).default(1),\n// })\n//\n// Both are ordinary exports beside the page, read by the build the same way\n// `metadata` and `generateStaticParams` are. Any Standard Schema works — Zod,\n// Valibot, ArkType — because the schema is asked to validate itself and this\n// module never imports one.\n//\n// What it buys beyond types: `?page=3` arrives as the number 3 rather than the\n// string \"3\", a missing one arrives as the default rather than undefined, and\n// `?page=banana` is refused in one place instead of surviving as NaN into\n// whatever the page does with it.\n//\n// The two fail differently, and that is the point of having both:\n//\n// params a url that does not describe a page. /posts/ tells a\n// crawler and a cache 404, not 500, so this is notFound().\n// searchParams the page exists and the query was wrong, which is the\n// nearest error.tsx — the same place any other bad input goes.\n\nimport { NotFoundSignal } from './notFound.js'\n\n/**\n * Standard Schema, restated rather than depended on.\n *\n * It is an interface, and a package for it would be a dependency that ships\n * nothing. Kept to the shape this module reads.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>\n readonly types?: { readonly input: Input; readonly output: Output }\n }\n}\n\ninterface StandardSchemaResult<Output> {\n readonly value?: Output\n readonly issues?: ReadonlyArray<{\n readonly message: string\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>\n }>\n}\n\n/** What a page gets from its own `params` export, already parsed. */\nexport type ParamsOf<S> = S extends StandardSchemaV1<unknown, infer Out> ? Out : never\n\n/**\n * The props a page with schemas receives.\n *\n * Written as a type rather than generated into a .d.ts because it needs no\n * build step to be correct: the schemas are exports of the same file, so a\n * page can refer to its own.\n *\n * export const params = z.object({ slug: z.string() })\n *\n * export default async function Page({ params: p }: PageProps<typeof params>) {\n * const { slug } = await p // string, not unknown\n * }\n */\nexport interface PageProps<P = never, S = never> {\n params: Promise<[P] extends [never] ? Record<string, string> : ParamsOf<P>>\n searchParams: Promise<[S] extends [never] ? URLSearchParams : ParamsOf<S>>\n}\n\n/**\n * The mark that says a refusal came from a route schema.\n *\n * Symbol.for for the reason every mark in this package is: the app's pages are\n * bundled apart from the engine, so each side gets its own copy of this class\n * and `instanceof` is false between them.\n */\nconst MARK = Symbol.for('@rsc-kit/core.route-input-error')\n\n/** A query string the page's own schema refused. */\nexport class SearchParamsError extends Error {\n public readonly errors: Record<string, string[]>\n\n constructor(errors: Record<string, string[]>) {\n super('The query string is not valid for this page.')\n this.name = 'SearchParamsError'\n this.errors = errors\n ;(this as unknown as Record<symbol, boolean>)[MARK] = true\n }\n}\n\n/** Whether this is a refused query string, whichever copy of the class built it. */\nexport function isSearchParamsError(error: unknown): error is SearchParamsError {\n return (\n typeof error === 'object' && error !== null && (error as Record<symbol, unknown>)[MARK] === true\n )\n}\n\n/** The field an issue belongs to, joined so a nested one reads as it was named. */\nfunction fieldOf(path: NonNullable<StandardSchemaResult<unknown>['issues']>[number]['path']): string {\n if (!path || path.length === 0) return ''\n\n return path\n .map((segment) => (typeof segment === 'object' && segment !== null ? segment.key : segment))\n .join('.')\n}\n\nfunction issuesToErrors(\n issues: NonNullable<StandardSchemaResult<unknown>['issues']>,\n): Record<string, string[]> {\n const errors: Record<string, string[]> = {}\n\n for (const issue of issues) {\n ;(errors[fieldOf(issue.path)] ??= []).push(issue.message)\n }\n\n return errors\n}\n\n/**\n * A URLSearchParams as the object a schema expects.\n *\n * A key that appears more than once becomes an array, and one that appears\n * once stays a scalar — so `?tag=a&tag=b` reaches `z.array(z.string())` and\n * `?q=shoes` reaches `z.string()` without the schema having to know which\n * shape the url happened to take.\n */\nexport function searchParamsToObject(search: URLSearchParams): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n\n for (const key of new Set(search.keys())) {\n const all = search.getAll(key)\n\n out[key] = all.length > 1 ? all : all[0]\n }\n\n return out\n}\n\n/**\n * Run a page's `params` schema, or hand back what the url gave.\n *\n * A refusal is notFound() rather than an error: the route matched a pattern,\n * but the values in it do not describe a page, and 404 is the answer a crawler\n * and a cache both need.\n */\nexport async function parseParams(\n schema: StandardSchemaV1 | undefined,\n value: Record<string, unknown>,\n): Promise<unknown> {\n if (!schema) return value\n\n const result = await schema['~standard'].validate(value)\n\n if (result.issues && result.issues.length > 0) {\n // The message is for a log, never a response: it repeats back a piece of\n // the url, and the not-found page is what the visitor sees either way.\n throw new NotFoundSignal(\n 'No page for these params: ' +\n Object.entries(issuesToErrors(result.issues))\n .map(([field, messages]) => `${field || 'params'} ${messages[0]}`)\n .join(', '),\n )\n }\n\n return result.value\n}\n\n/**\n * Run a page's `searchParams` schema, or hand back the URLSearchParams.\n *\n * A refusal throws into the nearest error.tsx, which can read `errors` for the\n * fields. The page exists — only the query was wrong — so this is deliberately\n * not a 404.\n */\nexport async function parseSearchParams(\n schema: StandardSchemaV1 | undefined,\n search: URLSearchParams,\n): Promise<unknown> {\n if (!schema) return search\n\n const result = await schema['~standard'].validate(searchParamsToObject(search))\n\n if (result.issues && result.issues.length > 0) {\n throw new SearchParamsError(issuesToErrors(result.issues))\n }\n\n return result.value\n}\n\n/**\n * What an api route's schemas turn a request into.\n *\n * Every field is optional and every one is opt-in: a route that exports no\n * schema gets the raw values it always got, and a handler written before any\n * of this existed keeps working untouched.\n */\nexport interface ApiInput {\n /** Parsed by the route's `params` export, or the raw url segments. */\n params: unknown\n /** Parsed by the route's `searchParams` export, or the URLSearchParams. */\n searchParams: unknown\n /** Parsed by the route's `body` export. Undefined when it declared none. */\n body?: unknown\n}\n\n/**\n * Its own mark, not the query string's.\n *\n * Sharing one was a real bug for as long as it took a test to run: both\n * refusals answered the check for a bad query string, whichever was asked\n * first, so every refused body came back 400 instead of 422.\n */\nconst BODY_MARK = Symbol.for('@rsc-kit/core.body-error')\n\n/** A request body the route's own schema refused. */\nexport class BodyError extends Error {\n public readonly errors: Record<string, string[]>\n\n constructor(errors: Record<string, string[]>) {\n super('The request body is not valid.')\n this.name = 'BodyError'\n this.errors = errors\n ;(this as unknown as Record<symbol, boolean>)[BODY_MARK] = true\n }\n}\n\n/**\n * Whether this is a refused body, whichever copy of the class built it.\n *\n * The mark rather than instanceof, for the reason every check in this package\n * uses one: an api route is bundled apart from the engine that calls it, so\n * each side has its own copy of this class and instanceof is false between\n * them — the refusal would surface as an unhandled 500.\n */\nexport function isBodyError(error: unknown): error is BodyError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as Record<symbol, unknown>)[BODY_MARK] === true\n )\n}\n\n/**\n * Run a route's `body` schema against whatever the request carries.\n *\n * JSON and form encodings both, because an api route answers a url and a url\n * is posted to by both. Anything else is handed to the schema as the raw text,\n * which is the only honest thing to do with a body this does not understand.\n */\nexport async function parseBody(\n schema: StandardSchemaV1 | undefined,\n request: Request,\n): Promise<unknown> {\n if (!schema) return undefined\n\n const type = request.headers.get('Content-Type') ?? ''\n let raw: unknown\n\n try {\n if (type.includes('json')) {\n raw = await request.json()\n } else if (type.includes('form')) {\n raw = searchParamsToObject(new URLSearchParams(await request.text()))\n } else {\n raw = await request.text()\n }\n } catch {\n // Malformed json is a refusal about the body as a whole rather than any\n // field, which is the empty-string key the form errors already use.\n throw new BodyError({ '': ['The request body could not be read.'] })\n }\n\n const result = await schema['~standard'].validate(raw)\n\n if (result.issues && result.issues.length > 0) {\n throw new BodyError(issuesToErrors(result.issues))\n }\n\n return result.value\n}\n"]}
|
|
1
|
+
{"version":3,"file":"routeSchema.js","sourceRoot":"","sources":["../src/routeSchema.ts"],"names":[],"mappings":"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","sourcesContent":["// Typing and checking what a url carries.\n//\n// // src/app/posts/[slug]/page.tsx\n// export const params = z.object({ slug: z.string().min(1) })\n// export const searchParams = z.object({\n// page: z.coerce.number().int().min(1).default(1),\n// })\n//\n// Both are ordinary exports beside the page, read by the build the same way\n// `metadata` and `generateStaticParams` are. Any Standard Schema works — Zod,\n// Valibot, ArkType — because the schema is asked to validate itself and this\n// module never imports one.\n//\n// What it buys beyond types: `?page=3` arrives as the number 3 rather than the\n// string \"3\", a missing one arrives as the default rather than undefined, and\n// `?page=banana` is refused in one place instead of surviving as NaN into\n// whatever the page does with it.\n//\n// The two fail differently, and that is the point of having both:\n//\n// params a url that does not describe a page. /posts/ tells a\n// crawler and a cache 404, not 500, so this is notFound().\n// searchParams the page exists and the query was wrong, which is the\n// nearest error.tsx — the same place any other bad input goes.\n\nimport { NotFoundSignal } from './notFound.js'\nimport type { ApiPattern } from './routes.js'\n\n/**\n * Standard Schema, restated rather than depended on.\n *\n * It is an interface, and a package for it would be a dependency that ships\n * nothing. Kept to the shape this module reads.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>\n readonly types?: { readonly input: Input; readonly output: Output }\n }\n}\n\ninterface StandardSchemaResult<Output> {\n readonly value?: Output\n readonly issues?: ReadonlyArray<{\n readonly message: string\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>\n }>\n}\n\n/** What a page gets from its own `params` export, already parsed. */\nexport type ParamsOf<S> = S extends StandardSchemaV1<unknown, infer Out> ? Out : never\n\n/**\n * The props a page with schemas receives.\n *\n * Written as a type rather than generated into a .d.ts because it needs no\n * build step to be correct: the schemas are exports of the same file, so a\n * page can refer to its own.\n *\n * export const params = z.object({ slug: z.string() })\n *\n * export default async function Page({ params: p }: PageProps<typeof params>) {\n * const { slug } = await p // string, not unknown\n * }\n */\nexport interface PageProps<P = never, S = never> {\n params: Promise<[P] extends [never] ? Record<string, string> : ParamsOf<P>>\n searchParams: Promise<[S] extends [never] ? URLSearchParams : ParamsOf<S>>\n}\n\n/**\n * The names a pattern binds: `/api/greet/[name]` binds `name`, and\n * `/docs/[...path]` binds `path` — one string, the slashes kept.\n */\ntype SegmentNames<P extends string> = P extends `${string}[...${infer N}]${infer Rest}`\n ? N | SegmentNames<Rest>\n : P extends `${string}[${infer N}]${infer Rest}`\n ? N | SegmentNames<Rest>\n : never\n\n/** What a route's params resolve to, from its pattern: `{ name: string }`. */\nexport type ApiParams<P extends string> = { [K in SegmentNames<P>]: string }\n\n/**\n * The second argument of an api route's handler.\n *\n * Every field is a promise, because the engine hands them lazily - a route\n * that never awaits its query string provably does not vary by it, so the\n * build can store one answer - and a sync read of `params.name` has to be a\n * compile error rather than a route that quietly answers 404 to everything.\n *\n * Name the route and the params are typed from its segments; the pattern is\n * checked against the routes the build found, so a typo fails `tsc`:\n *\n * export async function GET(request: Request, { params }: RouteContext<'/api/greet/[name]'>) {\n * const { name } = await params // string\n * }\n *\n * Or hand it the route's own schemas, the way PageProps takes a page's:\n *\n * export const params = z.object({ id: z.coerce.number() })\n * export async function GET(request: Request, { params }: RouteContext<typeof params>) {\n * const { id } = await params // number\n * }\n *\n * With neither, params is the raw segments and searchParams the raw\n * URLSearchParams, still behind a promise.\n */\nexport interface RouteContext<\n P extends ApiPattern | StandardSchemaV1 = never,\n S extends StandardSchemaV1 = never,\n B extends StandardSchemaV1 = never,\n> {\n params: Promise<\n [P] extends [never]\n ? Record<string, string>\n : P extends ApiPattern\n ? ApiParams<P>\n : ParamsOf<P>\n >\n searchParams: Promise<[S] extends [never] ? URLSearchParams : ParamsOf<S>>\n /** Undefined for a method with no body; a promise of the parsed body otherwise. */\n body: [B] extends [never] ? Promise<unknown> | undefined : Promise<ParamsOf<B>>\n}\n\n/**\n * A whole handler, typed from the route it answers:\n *\n * export const GET: RouteHandler<'/api/greet/[name]'> = async (request, { params }) => …\n */\nexport type RouteHandler<\n P extends ApiPattern | StandardSchemaV1 = never,\n S extends StandardSchemaV1 = never,\n B extends StandardSchemaV1 = never,\n> = (\n request: Request,\n context: RouteContext<P, S, B>,\n) => Response | Promise<Response>\n\n/**\n * The mark that says a refusal came from a route schema.\n *\n * Symbol.for for the reason every mark in this package is: the app's pages are\n * bundled apart from the engine, so each side gets its own copy of this class\n * and `instanceof` is false between them.\n */\nconst MARK = Symbol.for('@rsc-kit/core.route-input-error')\n\n/** A query string the page's own schema refused. */\nexport class SearchParamsError extends Error {\n public readonly errors: Record<string, string[]>\n\n constructor(errors: Record<string, string[]>) {\n super('The query string is not valid for this page.')\n this.name = 'SearchParamsError'\n this.errors = errors\n ;(this as unknown as Record<symbol, boolean>)[MARK] = true\n }\n}\n\n/** Whether this is a refused query string, whichever copy of the class built it. */\nexport function isSearchParamsError(error: unknown): error is SearchParamsError {\n return (\n typeof error === 'object' && error !== null && (error as Record<symbol, unknown>)[MARK] === true\n )\n}\n\n/** The field an issue belongs to, joined so a nested one reads as it was named. */\nfunction fieldOf(path: NonNullable<StandardSchemaResult<unknown>['issues']>[number]['path']): string {\n if (!path || path.length === 0) return ''\n\n return path\n .map((segment) => (typeof segment === 'object' && segment !== null ? segment.key : segment))\n .join('.')\n}\n\nfunction issuesToErrors(\n issues: NonNullable<StandardSchemaResult<unknown>['issues']>,\n): Record<string, string[]> {\n const errors: Record<string, string[]> = {}\n\n for (const issue of issues) {\n ;(errors[fieldOf(issue.path)] ??= []).push(issue.message)\n }\n\n return errors\n}\n\n/**\n * A URLSearchParams as the object a schema expects.\n *\n * A key that appears more than once becomes an array, and one that appears\n * once stays a scalar — so `?tag=a&tag=b` reaches `z.array(z.string())` and\n * `?q=shoes` reaches `z.string()` without the schema having to know which\n * shape the url happened to take.\n */\nexport function searchParamsToObject(search: URLSearchParams): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n\n for (const key of new Set(search.keys())) {\n const all = search.getAll(key)\n\n out[key] = all.length > 1 ? all : all[0]\n }\n\n return out\n}\n\n/**\n * Run a page's `params` schema, or hand back what the url gave.\n *\n * A refusal is notFound() rather than an error: the route matched a pattern,\n * but the values in it do not describe a page, and 404 is the answer a crawler\n * and a cache both need.\n */\nexport async function parseParams(\n schema: StandardSchemaV1 | undefined,\n value: Record<string, unknown>,\n): Promise<unknown> {\n if (!schema) return value\n\n const result = await schema['~standard'].validate(value)\n\n if (result.issues && result.issues.length > 0) {\n // The message is for a log, never a response: it repeats back a piece of\n // the url, and the not-found page is what the visitor sees either way.\n throw new NotFoundSignal(\n 'No page for these params: ' +\n Object.entries(issuesToErrors(result.issues))\n .map(([field, messages]) => `${field || 'params'} ${messages[0]}`)\n .join(', '),\n )\n }\n\n return result.value\n}\n\n/**\n * Run a page's `searchParams` schema, or hand back the URLSearchParams.\n *\n * A refusal throws into the nearest error.tsx, which can read `errors` for the\n * fields. The page exists — only the query was wrong — so this is deliberately\n * not a 404.\n */\nexport async function parseSearchParams(\n schema: StandardSchemaV1 | undefined,\n search: URLSearchParams,\n): Promise<unknown> {\n if (!schema) return search\n\n const result = await schema['~standard'].validate(searchParamsToObject(search))\n\n if (result.issues && result.issues.length > 0) {\n throw new SearchParamsError(issuesToErrors(result.issues))\n }\n\n return result.value\n}\n\n/**\n * What an api route's schemas turn a request into.\n *\n * Every field is optional and every one is opt-in: a route that exports no\n * schema gets the raw values it always got, and a handler written before any\n * of this existed keeps working untouched.\n */\nexport interface ApiInput {\n /** Parsed by the route's `params` export, or the raw url segments. */\n params: unknown\n /** Parsed by the route's `searchParams` export, or the URLSearchParams. */\n searchParams: unknown\n /** Parsed by the route's `body` export. Undefined when it declared none. */\n body?: unknown\n}\n\n/**\n * Its own mark, not the query string's.\n *\n * Sharing one was a real bug for as long as it took a test to run: both\n * refusals answered the check for a bad query string, whichever was asked\n * first, so every refused body came back 400 instead of 422.\n */\nconst BODY_MARK = Symbol.for('@rsc-kit/core.body-error')\n\n/** A request body the route's own schema refused. */\nexport class BodyError extends Error {\n public readonly errors: Record<string, string[]>\n\n constructor(errors: Record<string, string[]>) {\n super('The request body is not valid.')\n this.name = 'BodyError'\n this.errors = errors\n ;(this as unknown as Record<symbol, boolean>)[BODY_MARK] = true\n }\n}\n\n/**\n * Whether this is a refused body, whichever copy of the class built it.\n *\n * The mark rather than instanceof, for the reason every check in this package\n * uses one: an api route is bundled apart from the engine that calls it, so\n * each side has its own copy of this class and instanceof is false between\n * them — the refusal would surface as an unhandled 500.\n */\nexport function isBodyError(error: unknown): error is BodyError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as Record<symbol, unknown>)[BODY_MARK] === true\n )\n}\n\n/**\n * Run a route's `body` schema against whatever the request carries.\n *\n * JSON and form encodings both, because an api route answers a url and a url\n * is posted to by both. Anything else is handed to the schema as the raw text,\n * which is the only honest thing to do with a body this does not understand.\n */\nexport async function parseBody(\n schema: StandardSchemaV1 | undefined,\n request: Request,\n): Promise<unknown> {\n if (!schema) return undefined\n\n const type = request.headers.get('Content-Type') ?? ''\n let raw: unknown\n\n try {\n if (type.includes('json')) {\n raw = await request.json()\n } else if (type.includes('form')) {\n raw = searchParamsToObject(new URLSearchParams(await request.text()))\n } else {\n raw = await request.text()\n }\n } catch {\n // Malformed json is a refusal about the body as a whole rather than any\n // field, which is the empty-string key the form errors already use.\n throw new BodyError({ '': ['The request body could not be read.'] })\n }\n\n const result = await schema['~standard'].validate(raw)\n\n if (result.issues && result.issues.length > 0) {\n throw new BodyError(issuesToErrors(result.issues))\n }\n\n return result.value\n}\n"]}
|
package/dist/routes.d.ts
CHANGED
|
@@ -32,7 +32,19 @@ type OffRoute = `${string}://${string}` | `mailto:${string}` | `tel:${string}` |
|
|
|
32
32
|
* Cast when the destination is computed rather than written:
|
|
33
33
|
* `href={path as Href}`.
|
|
34
34
|
*/
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A url this app answers - a page or a route.ts - or one that deliberately
|
|
37
|
+
* leaves it. One type, as Next's `Route` is one type: what `<Link href>`,
|
|
38
|
+
* `visit()` and `redirect()` take. A route.ts is a full navigation rather
|
|
39
|
+
* than a payload fetch, and never prefetched - the client knows which urls
|
|
40
|
+
* are routes and treats a link to one as the anchor it is - so the type
|
|
41
|
+
* does not have to keep them apart to keep a hover from running one.
|
|
42
|
+
*
|
|
43
|
+
* `Route` is the same type under the name Next uses, so a port keeps the
|
|
44
|
+
* word it already has.
|
|
45
|
+
*/
|
|
46
|
+
export type Href = Unregistered extends true ? string : Filled<RoutePattern> | `${Filled<RoutePattern>}?${string}` | `${Filled<RoutePattern>}#${string}` | Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}` | OffRoute;
|
|
47
|
+
export type Route = Href;
|
|
36
48
|
/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */
|
|
37
49
|
export interface RegisterApi {
|
|
38
50
|
}
|
|
@@ -61,6 +73,8 @@ type NoApis = string extends ApiPattern ? true : false;
|
|
|
61
73
|
* because that is how a GET is parameterised.
|
|
62
74
|
*/
|
|
63
75
|
export type ApiHref = NoApis extends true ? string : Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}`;
|
|
76
|
+
/** A url a route.ts answers, alone: what `apiUrl()` narrows a fetch to. */
|
|
77
|
+
export type ApiRoute = ApiHref;
|
|
64
78
|
/**
|
|
65
79
|
* An api url, checked against the routes the build found.
|
|
66
80
|
*
|
package/dist/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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","sourcesContent":["// Typed routes: the urls this app can actually answer, as a type.\n//\n// Laravel needs route() because the url lives in PHP and can move\n// independently of the name it is called by. Here the url *is* the file path,\n// so a name would be indirection that buys nothing. What is worth having is\n// the other half — a link to a page that does not exist should fail at build\n// time rather than in the browser.\n//\n// There is no route() builder to go with this, deliberately. A template\n// literal is checked the same way — `/posts/${slug}` compiles and\n// `/postz/${slug}` does not — so a builder would only wrap what the language\n// already does. Encoding a value that is not url-safe is `encodeURIComponent`\n// in the template, the same as anywhere else.\n//\n// The build already walks app/ and knows every route's segments, so it writes\n// one line into the app's source dir:\n//\n// declare module '@rsc-kit/core/routes' {\n// interface Register { routes: '/' | '/posts/[slug]' }\n// }\n//\n// Everything below is derived from that union. An app that never runs the\n// generator — a generic host, a Laravel app that has not rebuilt — registers\n// nothing, `RoutePattern` stays `string`, and every url-taking API is exactly\n// as permissive as it was before. That fallback is the reason this can ship\n// without a flag.\n\n/**\n * Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.\n *\n * Declaration merging rather than a generic parameter, because the routes are\n * a property of the project, not of each call site — threading them through\n * every component that renders a Link is not a thing anyone would do twice.\n */\nexport interface Register {}\n\n/** The route patterns this app declared: `'/posts/[slug]'`. */\nexport type RoutePattern = Register extends { routes: infer R extends string }\n ? R\n : string;\n\n/** Whether anything was registered. `string` means the generator never ran. */\ntype Unregistered = string extends RoutePattern ? true : false;\n\n/**\n * A pattern with its dynamic segments opened up: `/posts/[slug]` accepts\n * `/posts/anything`.\n *\n * Catch-all and single params both become `${string}`, which for a catch-all\n * also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.\n */\ntype Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P extends `${infer A}[${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P;\n\n/**\n * Not a route, but a legitimate href: another site, a mail client, a phone\n * number, an anchor on this page, a bare query string.\n */\ntype OffRoute =\n | `${string}://${string}`\n | `mailto:${string}`\n | `tel:${string}`\n | `#${string}`\n | `?${string}`;\n\n/**\n * A url this app can answer, or one that deliberately leaves it.\n *\n * Cast when the destination is computed rather than written:\n * `href={path as Href}`.\n */\nexport type Href = Unregistered extends true\n ? string\n : | Filled<RoutePattern>\n | `${Filled<RoutePattern>}?${string}`\n | `${Filled<RoutePattern>}#${string}`\n | OffRoute;\n\n// ── Api routes ───────────────────────────────────────────────────────────────\n//\n// Their own union rather than part of Href, because they are not pages and a\n// link to one is almost always a mistake — an <a href=\"/api/orders\"> navigates\n// the browser away to a json document. Keeping them apart means `Link` refuses\n// an api url and `apiUrl()` refuses a page, which is the pair of mistakes worth\n// catching.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterApi {}\n\n// ── Regions a page can re-render on its own ──────────────────────────────────\n//\n// Every `section('name', …)` the build found, and every `@slot` directory.\n// `revalidate('orders')` is checked against them, so a name that matches no\n// section - a typo, or a section renamed since - stops compiling instead of\n// being refused by the renderer at runtime.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterRegions {}\n\n/** The region names the build found: section names and slot names. */\nexport type RegionName = RegisterRegions extends {\n regions: infer R extends string;\n}\n ? R\n : string;\n\ntype NoRegions = string extends RegionName ? true : false;\n\n/**\n * What `revalidate()` takes: the page, the whole document, or a region by\n * name. `string` until the build has written the types.\n */\nexport type RevalidateTarget = NoRegions extends true\n ? string\n : \"page\" | \"all\" | RegionName;\n\n/** The api route patterns this app declared: `'/api/orders/[id]'`. */\nexport type ApiPattern = RegisterApi extends { apis: infer R extends string }\n ? R\n : string;\n\ntype NoApis = string extends ApiPattern ? true : false;\n\n/**\n * A url an api route in this app answers.\n *\n * `/api/orders/[id]` accepts `/api/orders/42`, and a query string is allowed\n * because that is how a GET is parameterised.\n */\nexport type ApiHref = NoApis extends true\n ? string\n : Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}`;\n\n/**\n * An api url, checked against the routes the build found.\n *\n * await fetch(apiUrl(`/api/orders/${id}`))\n *\n * A function rather than a bare type so it can be used inline at a call site\n * that is typed `string` — `fetch` takes any string, so nothing would check the\n * argument without somewhere to put the type. It returns what it was given.\n *\n * Wrong path, and it stops compiling. Renamed the directory, and every call\n * site says so rather than one of them 404ing in production.\n */\nexport function apiUrl(href: ApiHref): string {\n return href;\n}\n\n// ── Search params, typed per route ───────────────────────────────────────────\n//\n// A page that exports a `searchParams` schema has said what its query string\n// means. The generated file records that schema per pattern:\n//\n// interface Register {\n// search: { '/search': SearchExportOf<typeof import('../src/app/search/page')> }\n// }\n//\n// and from there a link to `/search` is checked against the same schema the\n// page parses with — a `page` that must be a number is a number on the link,\n// a `q` the page requires is required to write the link, and a key the page\n// never reads does not compile. One schema, both ends. A route that exports\n// none takes anything; an href that is not a single route (computed, cast, or\n// off-site) takes anything too, because there is nothing to check it against.\n\n/** What a page module contributes: its `searchParams` export, or nothing. For the generated file. */\nexport type SearchExportOf<M> = M extends { searchParams: infer S }\n ? S\n : undefined;\n\ntype SearchMap = Register extends { search: infer M } ? M : {};\n\ntype StripQuery<H extends string> = H extends `${infer P}?${string}`\n ? P\n : H extends `${infer P}#${string}`\n ? P\n : H;\n\n/** The pattern a written href belongs to: `/posts/hello` is `/posts/[slug]`. */\ntype PatternOf<H extends string> = RoutePattern extends infer P\n ? P extends string\n ? StripQuery<H> extends Filled<P>\n ? P\n : never\n : never\n : never;\n\ntype IsUnion<T, U = T> = T extends unknown\n ? [U] extends [T]\n ? false\n : true\n : never;\n\ntype SchemaFor<P> = P extends keyof SearchMap ? SearchMap[P] : undefined;\n\ntype InputOf<S> = S extends { \"~standard\": { types?: { input: infer I } } }\n ? I\n : never;\ntype OutputOf<S> = S extends { \"~standard\": { types?: { output: infer O } } }\n ? O\n : never;\n\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\ntype RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * What a link may write for a schema: the keys the schema's input requires\n * are required, the rest optional, and every value is the schema's OUTPUT\n * type. Output rather than input because `z.coerce.number()` takes `unknown`\n * in - that is what coercion means - and a link typed by it would accept\n * `page: 'two'`. The output is the number the page will actually see.\n */\ntype LinkInputOf<S> = Simplify<\n { [K in RequiredKeys<InputOf<S>> & keyof OutputOf<S>]: OutputOf<S>[K] } & {\n [K in OptionalKeys<InputOf<S>> & keyof OutputOf<S>]?: OutputOf<S>[K];\n }\n>;\n\ntype Scalar = string | number | boolean | null | undefined;\n\n/** What a link may carry when nothing declares otherwise. */\nexport type LooseSearch = Record<string, Scalar | readonly (string | number)[]>;\n\n/**\n * The search params a link to `H` may carry.\n *\n * The page's schema input when `H` is one declared route with a schema;\n * otherwise anything. \"One route\" matters: `path as Href` is every route at\n * once, and a link that could go anywhere cannot be held to one page's schema.\n */\nexport type SearchFor<H extends string> = Unregistered extends true\n ? LooseSearch\n : IsUnion<H> extends true\n ? LooseSearch\n : [PatternOf<H>] extends [never]\n ? LooseSearch\n : SchemaFor<PatternOf<H>> extends undefined\n ? LooseSearch\n : LinkInputOf<SchemaFor<PatternOf<H>>>;\n\n/**\n * The `search` prop, required exactly when the page's schema has a required\n * key. A page that needs `q` is not reachable without one, so the link that\n * omits it is the bug — caught here rather than on the page's error boundary.\n */\nexport type SearchProp<H extends string> =\n {} extends SearchFor<H>\n ? { search?: SearchFor<H> }\n : { search: SearchFor<H> };\n\n/** A query string from an object: scalars stringified, arrays repeated, null and undefined dropped. */\nexport function searchString(search: object): string {\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(search)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n } else {\n params.set(key, String(value));\n }\n }\n\n return params.toString();\n}\n\n/** `path` with `search` appended, keeping any query and hash already on it. */\nexport function withSearch(path: string, search: object | undefined): string {\n if (!search) return path;\n\n const hashAt = path.indexOf(\"#\");\n const hash = hashAt === -1 ? \"\" : path.slice(hashAt);\n const before = hashAt === -1 ? path : path.slice(0, hashAt);\n const queryAt = before.indexOf(\"?\");\n const base = queryAt === -1 ? before : before.slice(0, queryAt);\n const existing = queryAt === -1 ? \"\" : before.slice(queryAt + 1);\n const added = searchString(search);\n const query = [existing, added].filter(Boolean).join(\"&\");\n\n return query ? `${base}?${query}${hash}` : `${base}${hash}`;\n}\n\n/**\n * A typed url with its search params, for the places that take a string.\n *\n * visit(href('/search', { q: 'shoes', page: 2 }))\n *\n * `Link` has the same check on its own `search` prop. This is for `visit`,\n * `prefetch`, `redirect` and anything else that wants the finished string.\n */\nexport function href<H extends Href>(\n path: H,\n ...rest: {} extends SearchFor<H>\n ? [search?: SearchFor<H>]\n : [search: SearchFor<H>]\n): Href {\n return withSearch(path, rest[0] as object | undefined) as Href;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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","sourcesContent":["// Typed routes: the urls this app can actually answer, as a type.\n//\n// Laravel needs route() because the url lives in PHP and can move\n// independently of the name it is called by. Here the url *is* the file path,\n// so a name would be indirection that buys nothing. What is worth having is\n// the other half — a link to a page that does not exist should fail at build\n// time rather than in the browser.\n//\n// There is no route() builder to go with this, deliberately. A template\n// literal is checked the same way — `/posts/${slug}` compiles and\n// `/postz/${slug}` does not — so a builder would only wrap what the language\n// already does. Encoding a value that is not url-safe is `encodeURIComponent`\n// in the template, the same as anywhere else.\n//\n// The build already walks app/ and knows every route's segments, so it writes\n// one line into the app's source dir:\n//\n// declare module '@rsc-kit/core/routes' {\n// interface Register { routes: '/' | '/posts/[slug]' }\n// }\n//\n// Everything below is derived from that union. An app that never runs the\n// generator — a generic host, a Laravel app that has not rebuilt — registers\n// nothing, `RoutePattern` stays `string`, and every url-taking API is exactly\n// as permissive as it was before. That fallback is the reason this can ship\n// without a flag.\n\n/**\n * Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.\n *\n * Declaration merging rather than a generic parameter, because the routes are\n * a property of the project, not of each call site — threading them through\n * every component that renders a Link is not a thing anyone would do twice.\n */\nexport interface Register {}\n\n/** The route patterns this app declared: `'/posts/[slug]'`. */\nexport type RoutePattern = Register extends { routes: infer R extends string }\n ? R\n : string;\n\n/** Whether anything was registered. `string` means the generator never ran. */\ntype Unregistered = string extends RoutePattern ? true : false;\n\n/**\n * A pattern with its dynamic segments opened up: `/posts/[slug]` accepts\n * `/posts/anything`.\n *\n * Catch-all and single params both become `${string}`, which for a catch-all\n * also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.\n */\ntype Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P extends `${infer A}[${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P;\n\n/**\n * Not a route, but a legitimate href: another site, a mail client, a phone\n * number, an anchor on this page, a bare query string.\n */\ntype OffRoute =\n | `${string}://${string}`\n | `mailto:${string}`\n | `tel:${string}`\n | `#${string}`\n | `?${string}`;\n\n/**\n * A url this app can answer, or one that deliberately leaves it.\n *\n * Cast when the destination is computed rather than written:\n * `href={path as Href}`.\n */\n/**\n * A url this app answers - a page or a route.ts - or one that deliberately\n * leaves it. One type, as Next's `Route` is one type: what `<Link href>`,\n * `visit()` and `redirect()` take. A route.ts is a full navigation rather\n * than a payload fetch, and never prefetched - the client knows which urls\n * are routes and treats a link to one as the anchor it is - so the type\n * does not have to keep them apart to keep a hover from running one.\n *\n * `Route` is the same type under the name Next uses, so a port keeps the\n * word it already has.\n */\nexport type Href = Unregistered extends true\n ? string\n : | Filled<RoutePattern>\n | `${Filled<RoutePattern>}?${string}`\n | `${Filled<RoutePattern>}#${string}`\n | Filled<ApiPattern>\n | `${Filled<ApiPattern>}?${string}`\n | OffRoute;\n\nexport type Route = Href;\n\n// ── Api routes ───────────────────────────────────────────────────────────────\n//\n// Their own union rather than part of Href, because they are not pages and a\n// link to one is almost always a mistake — an <a href=\"/api/orders\"> navigates\n// the browser away to a json document. Keeping them apart means `Link` refuses\n// an api url and `apiUrl()` refuses a page, which is the pair of mistakes worth\n// catching.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterApi {}\n\n// ── Regions a page can re-render on its own ──────────────────────────────────\n//\n// Every `section('name', …)` the build found, and every `@slot` directory.\n// `revalidate('orders')` is checked against them, so a name that matches no\n// section - a typo, or a section renamed since - stops compiling instead of\n// being refused by the renderer at runtime.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterRegions {}\n\n/** The region names the build found: section names and slot names. */\nexport type RegionName = RegisterRegions extends {\n regions: infer R extends string;\n}\n ? R\n : string;\n\ntype NoRegions = string extends RegionName ? true : false;\n\n/**\n * What `revalidate()` takes: the page, the whole document, or a region by\n * name. `string` until the build has written the types.\n */\nexport type RevalidateTarget = NoRegions extends true\n ? string\n : \"page\" | \"all\" | RegionName;\n\n/** The api route patterns this app declared: `'/api/orders/[id]'`. */\nexport type ApiPattern = RegisterApi extends { apis: infer R extends string }\n ? R\n : string;\n\ntype NoApis = string extends ApiPattern ? true : false;\n\n/**\n * A url an api route in this app answers.\n *\n * `/api/orders/[id]` accepts `/api/orders/42`, and a query string is allowed\n * because that is how a GET is parameterised.\n */\nexport type ApiHref = NoApis extends true\n ? string\n : Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}`;\n\n/** A url a route.ts answers, alone: what `apiUrl()` narrows a fetch to. */\nexport type ApiRoute = ApiHref;\n\n/**\n * An api url, checked against the routes the build found.\n *\n * await fetch(apiUrl(`/api/orders/${id}`))\n *\n * A function rather than a bare type so it can be used inline at a call site\n * that is typed `string` — `fetch` takes any string, so nothing would check the\n * argument without somewhere to put the type. It returns what it was given.\n *\n * Wrong path, and it stops compiling. Renamed the directory, and every call\n * site says so rather than one of them 404ing in production.\n */\nexport function apiUrl(href: ApiHref): string {\n return href;\n}\n\n// ── Search params, typed per route ───────────────────────────────────────────\n//\n// A page that exports a `searchParams` schema has said what its query string\n// means. The generated file records that schema per pattern:\n//\n// interface Register {\n// search: { '/search': SearchExportOf<typeof import('../src/app/search/page')> }\n// }\n//\n// and from there a link to `/search` is checked against the same schema the\n// page parses with — a `page` that must be a number is a number on the link,\n// a `q` the page requires is required to write the link, and a key the page\n// never reads does not compile. One schema, both ends. A route that exports\n// none takes anything; an href that is not a single route (computed, cast, or\n// off-site) takes anything too, because there is nothing to check it against.\n\n/** What a page module contributes: its `searchParams` export, or nothing. For the generated file. */\nexport type SearchExportOf<M> = M extends { searchParams: infer S }\n ? S\n : undefined;\n\ntype SearchMap = Register extends { search: infer M } ? M : {};\n\ntype StripQuery<H extends string> = H extends `${infer P}?${string}`\n ? P\n : H extends `${infer P}#${string}`\n ? P\n : H;\n\n/** The pattern a written href belongs to: `/posts/hello` is `/posts/[slug]`. */\ntype PatternOf<H extends string> = RoutePattern extends infer P\n ? P extends string\n ? StripQuery<H> extends Filled<P>\n ? P\n : never\n : never\n : never;\n\ntype IsUnion<T, U = T> = T extends unknown\n ? [U] extends [T]\n ? false\n : true\n : never;\n\ntype SchemaFor<P> = P extends keyof SearchMap ? SearchMap[P] : undefined;\n\ntype InputOf<S> = S extends { \"~standard\": { types?: { input: infer I } } }\n ? I\n : never;\ntype OutputOf<S> = S extends { \"~standard\": { types?: { output: infer O } } }\n ? O\n : never;\n\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\ntype RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * What a link may write for a schema: the keys the schema's input requires\n * are required, the rest optional, and every value is the schema's OUTPUT\n * type. Output rather than input because `z.coerce.number()` takes `unknown`\n * in - that is what coercion means - and a link typed by it would accept\n * `page: 'two'`. The output is the number the page will actually see.\n */\ntype LinkInputOf<S> = Simplify<\n { [K in RequiredKeys<InputOf<S>> & keyof OutputOf<S>]: OutputOf<S>[K] } & {\n [K in OptionalKeys<InputOf<S>> & keyof OutputOf<S>]?: OutputOf<S>[K];\n }\n>;\n\ntype Scalar = string | number | boolean | null | undefined;\n\n/** What a link may carry when nothing declares otherwise. */\nexport type LooseSearch = Record<string, Scalar | readonly (string | number)[]>;\n\n/**\n * The search params a link to `H` may carry.\n *\n * The page's schema input when `H` is one declared route with a schema;\n * otherwise anything. \"One route\" matters: `path as Href` is every route at\n * once, and a link that could go anywhere cannot be held to one page's schema.\n */\nexport type SearchFor<H extends string> = Unregistered extends true\n ? LooseSearch\n : IsUnion<H> extends true\n ? LooseSearch\n : [PatternOf<H>] extends [never]\n ? LooseSearch\n : SchemaFor<PatternOf<H>> extends undefined\n ? LooseSearch\n : LinkInputOf<SchemaFor<PatternOf<H>>>;\n\n/**\n * The `search` prop, required exactly when the page's schema has a required\n * key. A page that needs `q` is not reachable without one, so the link that\n * omits it is the bug — caught here rather than on the page's error boundary.\n */\nexport type SearchProp<H extends string> =\n {} extends SearchFor<H>\n ? { search?: SearchFor<H> }\n : { search: SearchFor<H> };\n\n/** A query string from an object: scalars stringified, arrays repeated, null and undefined dropped. */\nexport function searchString(search: object): string {\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(search)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n } else {\n params.set(key, String(value));\n }\n }\n\n return params.toString();\n}\n\n/** `path` with `search` appended, keeping any query and hash already on it. */\nexport function withSearch(path: string, search: object | undefined): string {\n if (!search) return path;\n\n const hashAt = path.indexOf(\"#\");\n const hash = hashAt === -1 ? \"\" : path.slice(hashAt);\n const before = hashAt === -1 ? path : path.slice(0, hashAt);\n const queryAt = before.indexOf(\"?\");\n const base = queryAt === -1 ? before : before.slice(0, queryAt);\n const existing = queryAt === -1 ? \"\" : before.slice(queryAt + 1);\n const added = searchString(search);\n const query = [existing, added].filter(Boolean).join(\"&\");\n\n return query ? `${base}?${query}${hash}` : `${base}${hash}`;\n}\n\n/**\n * A typed url with its search params, for the places that take a string.\n *\n * visit(href('/search', { q: 'shoes', page: 2 }))\n *\n * `Link` has the same check on its own `search` prop. This is for `visit`,\n * `prefetch`, `redirect` and anything else that wants the finished string.\n */\nexport function href<H extends Href>(\n path: H,\n ...rest: {} extends SearchFor<H>\n ? [search?: SearchFor<H>]\n : [search: SearchFor<H>]\n): Href {\n return withSearch(path, rest[0] as object | undefined) as Href;\n}\n"]}
|
package/dist/testing.d.ts
CHANGED
|
@@ -19,6 +19,28 @@ export interface TestAppOptions {
|
|
|
19
19
|
/** The origin requests are made against. Nothing reads it; it is a url. */
|
|
20
20
|
origin?: string;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A file the build put in .output/public, answered the way Nitro's static
|
|
24
|
+
* layer would - which the handler this harness loads does not include. The
|
|
25
|
+
* handler is the rsc service: routing, pages, actions, api routes. Assets,
|
|
26
|
+
* the service worker, the manifest and the icons are files Nitro serves in
|
|
27
|
+
* production from .output/public, and a test that asked for /sw.js used to
|
|
28
|
+
* get the router's 404 for a file the deployment serves fine - the one
|
|
29
|
+
* shape this harness exists to catch, in the other direction.
|
|
30
|
+
*
|
|
31
|
+
* Files only, for GET and HEAD, within the directory: a path that escapes it
|
|
32
|
+
* is a request for the handler, not a file.
|
|
33
|
+
*/
|
|
34
|
+
/** @internal Exported for its test. */
|
|
35
|
+
export declare function staticFile(root: string, request: Request): Response | null;
|
|
36
|
+
/**
|
|
37
|
+
* How this project builds: its own `build` script, run by the package manager
|
|
38
|
+
* this test is running under. Without a script, Vite directly - on Bun's
|
|
39
|
+
* runtime when the tests are, since a bin's node shebang would otherwise
|
|
40
|
+
* start it under Node.
|
|
41
|
+
*/
|
|
42
|
+
/** @internal Exported for its test. */
|
|
43
|
+
export declare function buildCommand(root: string): [string, string[]];
|
|
22
44
|
/**
|
|
23
45
|
* Build the app if it needs it, load it, and hand back something to fetch from.
|
|
24
46
|
*
|
package/dist/testing.js
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
// works; this proves the app answers a url the way it is deployed, which is
|
|
17
17
|
// where a route that renders fine and is served wrong shows up.
|
|
18
18
|
import { spawnSync } from 'node:child_process';
|
|
19
|
-
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
20
|
-
import { join, resolve } from 'node:path';
|
|
19
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
20
|
+
import { extname, join, resolve, sep } from 'node:path';
|
|
21
21
|
import { pathToFileURL } from 'node:url';
|
|
22
22
|
/**
|
|
23
23
|
* Where the build put the server bundle.
|
|
@@ -51,6 +51,78 @@ function newest(dir) {
|
|
|
51
51
|
}
|
|
52
52
|
/** One build per process per root, however many test files ask. */
|
|
53
53
|
const built = new Map();
|
|
54
|
+
/** The content types a test is likely to assert on; anything else is octet-stream. */
|
|
55
|
+
const CONTENT_TYPES = {
|
|
56
|
+
'.html': 'text/html; charset=utf-8',
|
|
57
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
58
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
59
|
+
'.css': 'text/css; charset=utf-8',
|
|
60
|
+
'.json': 'application/json; charset=utf-8',
|
|
61
|
+
'.webmanifest': 'application/manifest+json',
|
|
62
|
+
'.xml': 'application/xml',
|
|
63
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
64
|
+
'.svg': 'image/svg+xml',
|
|
65
|
+
'.png': 'image/png',
|
|
66
|
+
'.jpg': 'image/jpeg',
|
|
67
|
+
'.jpeg': 'image/jpeg',
|
|
68
|
+
'.webp': 'image/webp',
|
|
69
|
+
'.avif': 'image/avif',
|
|
70
|
+
'.gif': 'image/gif',
|
|
71
|
+
'.ico': 'image/x-icon',
|
|
72
|
+
'.woff': 'font/woff',
|
|
73
|
+
'.woff2': 'font/woff2',
|
|
74
|
+
'.wasm': 'application/wasm',
|
|
75
|
+
'.map': 'application/json',
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* A file the build put in .output/public, answered the way Nitro's static
|
|
79
|
+
* layer would - which the handler this harness loads does not include. The
|
|
80
|
+
* handler is the rsc service: routing, pages, actions, api routes. Assets,
|
|
81
|
+
* the service worker, the manifest and the icons are files Nitro serves in
|
|
82
|
+
* production from .output/public, and a test that asked for /sw.js used to
|
|
83
|
+
* get the router's 404 for a file the deployment serves fine - the one
|
|
84
|
+
* shape this harness exists to catch, in the other direction.
|
|
85
|
+
*
|
|
86
|
+
* Files only, for GET and HEAD, within the directory: a path that escapes it
|
|
87
|
+
* is a request for the handler, not a file.
|
|
88
|
+
*/
|
|
89
|
+
/** @internal Exported for its test. */
|
|
90
|
+
export function staticFile(root, request) {
|
|
91
|
+
if (request.method !== 'GET' && request.method !== 'HEAD')
|
|
92
|
+
return null;
|
|
93
|
+
const publicDir = join(root, '.output/public');
|
|
94
|
+
const pathname = decodeURIComponent(new URL(request.url).pathname);
|
|
95
|
+
const file = resolve(publicDir, '.' + pathname);
|
|
96
|
+
if (!file.startsWith(publicDir + sep) || !existsSync(file) || !statSync(file).isFile())
|
|
97
|
+
return null;
|
|
98
|
+
const type = CONTENT_TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream';
|
|
99
|
+
const body = request.method === 'HEAD' ? null : readFileSync(file);
|
|
100
|
+
return new Response(body, {
|
|
101
|
+
status: 200,
|
|
102
|
+
headers: { 'Content-Type': type, 'Cache-Control': 'no-store', 'X-RSC-Kit-Test': 'static' },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* How this project builds: its own `build` script, run by the package manager
|
|
107
|
+
* this test is running under. Without a script, Vite directly - on Bun's
|
|
108
|
+
* runtime when the tests are, since a bin's node shebang would otherwise
|
|
109
|
+
* start it under Node.
|
|
110
|
+
*/
|
|
111
|
+
/** @internal Exported for its test. */
|
|
112
|
+
export function buildCommand(root) {
|
|
113
|
+
const onBun = typeof process.versions.bun === 'string';
|
|
114
|
+
let scripts = {};
|
|
115
|
+
try {
|
|
116
|
+
scripts = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')).scripts ?? {};
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// No package.json, or not JSON: there is no script to run.
|
|
120
|
+
}
|
|
121
|
+
if (typeof scripts.build === 'string') {
|
|
122
|
+
return onBun ? ['bun', ['run', 'build']] : ['npm', ['run', 'build']];
|
|
123
|
+
}
|
|
124
|
+
return onBun ? ['bun', ['--bun', 'vite', 'build']] : ['npx', ['vite', 'build']];
|
|
125
|
+
}
|
|
54
126
|
async function ensureBuilt(root, build) {
|
|
55
127
|
const existing = findBundle(root);
|
|
56
128
|
if (!build) {
|
|
@@ -63,8 +135,11 @@ async function ensureBuilt(root, build) {
|
|
|
63
135
|
if (fresh)
|
|
64
136
|
return existing;
|
|
65
137
|
// The user's own build command, so what is tested is what ships. A test
|
|
66
|
-
// that built some other way would pass against a bundle nobody deploys
|
|
67
|
-
|
|
138
|
+
// that built some other way would pass against a bundle nobody deploys -
|
|
139
|
+
// and used to: this ran `npx vite build` whatever package.json said, which
|
|
140
|
+
// on a Bun project built under Node and failed on the first `import 'bun'`.
|
|
141
|
+
const [command, args] = buildCommand(root);
|
|
142
|
+
const run = spawnSync(command, args, {
|
|
68
143
|
cwd: root,
|
|
69
144
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
70
145
|
env: { ...process.env, NODE_ENV: 'production' },
|
|
@@ -96,7 +171,10 @@ export async function createTestApp(options = {}) {
|
|
|
96
171
|
}
|
|
97
172
|
return {
|
|
98
173
|
bundle,
|
|
99
|
-
fetch: (path, init) =>
|
|
174
|
+
fetch: async (path, init) => {
|
|
175
|
+
const request = new Request(new URL(path, origin), init);
|
|
176
|
+
return staticFile(root, request) ?? entry.default(request);
|
|
177
|
+
},
|
|
100
178
|
};
|
|
101
179
|
}
|
|
102
180
|
//# sourceMappingURL=testing.js.map
|