@rshono/core 1.0.0-rc.18 → 1.0.0-rc.19

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.
Files changed (53) hide show
  1. package/README.md +13 -3
  2. package/dist/builder/rspack-config.d.ts.map +1 -1
  3. package/dist/builder/rspack-config.js +5 -1
  4. package/dist/builder/rspack-config.js.map +1 -1
  5. package/dist/builder/server-only-imports.d.ts +33 -0
  6. package/dist/builder/server-only-imports.d.ts.map +1 -0
  7. package/dist/builder/server-only-imports.js +51 -0
  8. package/dist/builder/server-only-imports.js.map +1 -0
  9. package/dist/cli/build.d.ts.map +1 -1
  10. package/dist/cli/build.js +18 -9
  11. package/dist/cli/build.js.map +1 -1
  12. package/dist/cli/dev.d.ts.map +1 -1
  13. package/dist/cli/dev.js +45 -0
  14. package/dist/cli/dev.js.map +1 -1
  15. package/dist/deploy/presets.d.ts +8 -0
  16. package/dist/deploy/presets.d.ts.map +1 -1
  17. package/dist/deploy/presets.js +1 -0
  18. package/dist/deploy/presets.js.map +1 -1
  19. package/dist/router.d.ts +33 -18
  20. package/dist/router.d.ts.map +1 -1
  21. package/dist/router.js.map +1 -1
  22. package/dist/runtime/boundaries.d.ts +11 -4
  23. package/dist/runtime/boundaries.d.ts.map +1 -1
  24. package/dist/runtime/boundaries.js.map +1 -1
  25. package/dist/runtime/context.d.ts +9 -2
  26. package/dist/runtime/context.d.ts.map +1 -1
  27. package/dist/runtime/context.js +12 -3
  28. package/dist/runtime/context.js.map +1 -1
  29. package/dist/runtime/entry.client.js +124 -19
  30. package/dist/runtime/entry.client.js.map +1 -1
  31. package/dist/runtime/entry.rsc.d.ts +7 -0
  32. package/dist/runtime/entry.rsc.d.ts.map +1 -1
  33. package/dist/runtime/entry.rsc.js +17 -20
  34. package/dist/runtime/entry.rsc.js.map +1 -1
  35. package/dist/runtime/validate-entries.d.ts +49 -4
  36. package/dist/runtime/validate-entries.d.ts.map +1 -1
  37. package/dist/runtime/validate-entries.js +118 -2
  38. package/dist/runtime/validate-entries.js.map +1 -1
  39. package/dist/server/prerendered.d.ts +0 -5
  40. package/dist/server/prerendered.d.ts.map +1 -1
  41. package/dist/server/prerendered.js +1 -1
  42. package/dist/server/prerendered.js.map +1 -1
  43. package/dist/server/server-config.d.ts +8 -1
  44. package/dist/server/server-config.d.ts.map +1 -1
  45. package/dist/server/server-config.js +2 -1
  46. package/dist/server/server-config.js.map +1 -1
  47. package/dist/server/ssg.d.ts.map +1 -1
  48. package/dist/server/ssg.js +24 -7
  49. package/dist/server/ssg.js.map +1 -1
  50. package/dist/server/static.d.ts.map +1 -1
  51. package/dist/server/static.js +5 -1
  52. package/dist/server/static.js.map +1 -1
  53. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { serve } from '@hono/node-server';\nimport { rspack, type Stats } from '@rspack/core';\nimport { Hono } from 'hono';\nimport { proxy } from 'hono/proxy';\nimport { mkdir, rm } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport { join } from 'node:path';\nimport { Worker } from 'node:worker_threads';\nimport { DEV_OUT_DIR, createConfigs } from '../builder/rspack-config.js';\nimport type { RshonoConfig } from '../config.js';\nimport { NODE_PRESET } from '../deploy/presets.js';\nimport type { DevMessage } from '../runtime/dev-protocol.js';\nimport { SERVER_DEFAULTS } from '../server/server-config.js';\nimport { createStaticAssetsApp } from '../server/static.js';\n\nconst WORKER_READY_TIMEOUT_MS = 15_000;\n\n/** An error as the dev server shows it in the browser: the stack where there is one, since this is dev. */\nfunction describe(error: unknown): string {\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\n/**\n * Whether nothing is listening on the port yet — checked before the output directory is emptied, because a\n * second `rshono dev` would otherwise wipe the chunks the running one is still importing and only then exit\n * on EADDRINUSE.\n */\nfunction portAvailable(port: number, hostname: string): Promise<boolean> {\n const { promise, resolve } = Promise.withResolvers<boolean>();\n const probe = createServer();\n probe.once('error', () => resolve(false));\n probe.listen(port, hostname, () => probe.close(() => resolve(true)));\n return promise;\n}\n\ninterface DevOptions {\n rootDir: string;\n port?: number;\n config: RshonoConfig;\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n const { rootDir, config } = options;\n const port = options.port ?? SERVER_DEFAULTS.port;\n // Its own directory, never `dist/`: a `rshono build` in another terminal must not be able to delete the\n // chunks this server is still importing.\n const outDir = join(rootDir, DEV_OUT_DIR);\n\n // Before the `rm` below, not at `serve` where the bind happens — see {@link portAvailable}.\n if (!(await portAvailable(port, '127.0.0.1'))) {\n console.error(` ✗ port ${port} is already in use`);\n process.exit(1);\n }\n\n await rm(outDir, { recursive: true, force: true });\n await mkdir(join(outDir, 'static'), { recursive: true });\n\n const encoder = new TextEncoder();\n const sseClients = new Set<ReadableStreamDefaultController<Uint8Array>>();\n let clientHash: string | undefined;\n\n const sseChunk = (data: unknown) => encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`);\n\n /**\n * Writes one already-encoded SSE frame to every open client. A failed write is what retires a client:\n * `cancel` is not always reached first when a browser navigates away.\n */\n function sendToAll(chunk: Uint8Array): void {\n for (const controller of sseClients) {\n try {\n controller.enqueue(chunk);\n } catch {\n sseClients.delete(controller);\n }\n }\n }\n\n function broadcast(message: DevMessage): void {\n sendToAll(sseChunk(message));\n }\n\n let serverComponentsChanged = false;\n const [clientConfig, serverConfig] = createConfigs({\n rootDir,\n isDev: true,\n config,\n // Always Node, whatever `deploy` says: that is a property of `build` output, and the dev server runs the\n // bundle in a worker thread of this process.\n preset: NODE_PRESET,\n onServerComponentChanges: () => {\n serverComponentsChanged = true;\n },\n });\n const compiler = rspack([clientConfig, serverConfig]);\n const [clientCompiler, serverCompiler] = compiler.compilers;\n\n let currentWorker: Worker | null = null;\n let workerPort: number | null = null;\n let workerGate = createGate();\n let restartChain: Promise<void> = Promise.resolve();\n\n function createGate() {\n const { promise, resolve } = Promise.withResolvers<{ error?: string }>();\n return { promise, open: resolve };\n }\n\n function spawnWorker(): Promise<{ worker: Worker; port: number }> {\n return new Promise((resolve, reject) => {\n const worker = new Worker(join(outDir, 'server', 'main.mjs'), {\n workerData: { port: 0, hostname: '127.0.0.1' },\n execArgv: ['--enable-source-maps'],\n env: process.env as Record<string, string>,\n });\n const timeout = setTimeout(() => {\n // Nothing waits on the termination: the promise below is already being rejected, and a worker that\n // never reported ready has no state left worth draining.\n void worker.terminate();\n reject(new Error(`server worker did not become ready within ${WORKER_READY_TIMEOUT_MS / 1000}s`));\n }, WORKER_READY_TIMEOUT_MS);\n\n let ready = false;\n worker.once('message', (message: { type?: string; port?: number }) => {\n if (message?.type === 'ready' && typeof message.port === 'number') {\n ready = true;\n clearTimeout(timeout);\n resolve({ worker, port: message.port });\n }\n });\n // `on`, not `once`: an error after the worker is ready has no pending promise left to reject, and a\n // consumed `once` would leave it unlistened — which Node turns into an uncaught exception here.\n worker.on('error', (error: Error) => {\n if (ready) {\n console.error(' ✗ server worker crashed:', error);\n return;\n }\n clearTimeout(timeout);\n reject(error);\n });\n worker.on('exit', (code) => {\n if (worker === currentWorker && code !== 0) {\n console.error(` ✗ server worker exited with code ${code} — waiting for the next rebuild`);\n currentWorker = null;\n // Opened with the reason rather than left closed, so a request arriving before the next rebuild sees\n // it instead of hanging. `hooks.invalid` installs a fresh closed gate as soon as a file changes.\n workerGate = createGate();\n workerGate.open({ error: `The server worker exited with code ${code}. See the terminal for the error it crashed with.` });\n }\n });\n });\n }\n\n serverCompiler.hooks.invalid.tap('rshono/gate', () => {\n workerGate = createGate();\n });\n\n serverCompiler.hooks.done.tapPromise('rshono/worker', async (stats: Stats) => {\n const gate = workerGate;\n restartChain = restartChain.then(async () => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n gate.open({ error: stats.toString({ preset: 'errors-only', colors: false }) });\n return;\n }\n try {\n if (currentWorker) {\n const old = currentWorker;\n currentWorker = null;\n await old.terminate();\n }\n const { worker, port: newPort } = await spawnWorker();\n currentWorker = worker;\n workerPort = newPort;\n gate.open({});\n if (serverComponentsChanged) {\n serverComponentsChanged = false;\n broadcast({ type: 'rsc-update' });\n }\n } catch (error) {\n console.error(' ✗ failed to start server worker:', error);\n gate.open({ error: describe(error) });\n }\n });\n // No link in this queue may be left rejected: `.then` on a rejected promise short-circuits, and from there\n // no worker is spawned and no gate is opened again.\n restartChain = restartChain.catch((error) => {\n console.error(' ✗ dev server restart failed:', error);\n gate.open({ error: describe(error) });\n });\n await restartChain;\n });\n\n clientCompiler.hooks.done.tap('rshono/hmr', (stats: Stats) => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n return;\n }\n clientHash = stats.hash ?? undefined;\n if (clientHash) broadcast({ type: 'client-built', hash: clientHash });\n });\n\n let firstBuild = true;\n compiler.watch([{}, {}] as never, (err, multiStats) => {\n if (err) {\n console.error(' ✗ build failed:', err);\n return;\n }\n if (multiStats && !multiStats.hasErrors()) {\n // Errors print themselves through the per-compiler `done` hooks; a warning has nowhere else to go, and\n // the env shadow reports what it cannot cover that way. See `env-shadow-loader.cjs`.\n if (multiStats.hasWarnings()) console.warn(multiStats.toString({ preset: 'errors-warnings', colors: true }));\n const seconds = Math.max(...multiStats.stats.map((s) => (s.endTime ?? 0) - (s.startTime ?? 0))) / 1000;\n console.log(` ${firstBuild ? '✓ built' : '✓ rebuilt'} in ${seconds.toFixed(1)}s`);\n firstBuild = false;\n }\n });\n\n const front = new Hono();\n\n front.route('/_static', createStaticAssetsApp({ root: join(outDir, 'static'), isDev: true }));\n\n front.get('/_rshono/hmr', () => {\n let ctrl: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n ctrl = controller;\n sseClients.add(controller);\n controller.enqueue(encoder.encode('retry: 500\\n\\n'));\n controller.enqueue(sseChunk({ type: 'hello', hash: clientHash } satisfies DevMessage));\n },\n cancel() {\n sseClients.delete(ctrl);\n },\n });\n return new Response(stream, {\n headers: {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n },\n });\n });\n\n // An SSE comment, not a message: it keeps an idle connection off a proxy's timeout without the client\n // having to know a frame type that means nothing.\n const ping = encoder.encode(': ping\\n\\n');\n setInterval(() => sendToAll(ping), 15_000).unref();\n\n front.all('*', async (c) => {\n const { error } = await workerGate.promise;\n if (error || workerPort === null) {\n return c.text(`Build failed:\\n\\n${error ?? 'server not running'}`, 500);\n }\n\n const incoming = new URL(c.req.url);\n const target = `http://127.0.0.1:${workerPort}${incoming.pathname}${incoming.search}`;\n\n // Hono's proxy helper rather than a hand-rolled `fetch`: it carries the method, the streamed body (with\n // the `duplex` Node requires) and the client's abort signal, and strips the hop-by-hop headers on the way\n // back. `headers` replaces the set wholesale, so the request's own are spread back in first.\n const response = await proxy(target, {\n raw: c.req.raw,\n // The worker's redirects are the app's answer to this request, to be rewritten below and passed on.\n redirect: 'manual',\n headers: {\n ...c.req.header(),\n // This front-end *is* the proxy the app sits behind, which is why `trustProxy` is forced on in dev:\n // without these the app resolves every URL against the worker's random 127.0.0.1 port.\n 'x-forwarded-host': incoming.host,\n 'x-forwarded-proto': incoming.protocol.replace(':', ''),\n },\n });\n\n // A redirect to the worker's own address has to become a relative one, or the browser leaves the dev\n // server for a random port only this process knows about.\n const location = response.headers.get('location');\n const loc = location ? URL.parse(location, target) : null;\n if (loc && loc.host === `127.0.0.1:${workerPort}`) {\n response.headers.set('location', `${loc.pathname}${loc.search}${loc.hash}`);\n }\n return response;\n });\n\n serve({ fetch: front.fetch, port, hostname: '127.0.0.1' }, (info) => {\n console.log(` ➜ rshono dev server: http://localhost:${info.port}`);\n });\n}\n"]}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { serve } from '@hono/node-server';\nimport { rspack, type Stats } from '@rspack/core';\nimport { Hono } from 'hono';\nimport { proxy } from 'hono/proxy';\nimport { watch } from 'node:fs';\nimport { mkdir, rm } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport { join } from 'node:path';\nimport { Worker } from 'node:worker_threads';\nimport { DEV_OUT_DIR, createConfigs } from '../builder/rspack-config.js';\nimport type { RshonoConfig } from '../config.js';\nimport { NODE_PRESET } from '../deploy/presets.js';\nimport type { DevMessage } from '../runtime/dev-protocol.js';\nimport { SERVER_DEFAULTS } from '../server/server-config.js';\nimport { createStaticAssetsApp } from '../server/static.js';\n\nconst WORKER_READY_TIMEOUT_MS = 15_000;\n\n/**\n * Files whose contents are *compiled into* a build rather than read per request, so no rebuild picks a change\n * to them up: `.env` because the `PUBLIC_` view of it is baked into the client bundle by DefinePlugin and into\n * the SSR layer by the env-shadow prelude, and `rshono.config.ts` because `ServerConfig` is a DefinePlugin\n * literal too. `.env` is worse than the config, which the docs at least describe as build-time: a page that\n * reads `process.env` rebuilds, serves, and shows the old value.\n */\nconst BAKED_IN_FILES = ['.env.local', '.env', 'rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];\n\n/**\n * Watches those files, only to say that a restart is needed.\n *\n * Reloading them for real means re-exec'ing this process: `process.loadEnvFile` does not override a variable\n * that is already set, so the copy this process took at startup would survive an in-place reload — and the\n * worker is spawned with `env: process.env`, so it would inherit the stale value too. Choreographing that\n * against a bound port and a live SSE connection is a great deal of machinery to save one Ctrl-C; a line\n * saying which key to press costs nothing and loses nobody an afternoon.\n *\n * The directory is watched rather than each file, so a file that does not exist yet is still covered — and\n * because an editor's atomic save replaces the file, which a watch on the file itself does not survive. An\n * explicit `--config` outside the project root is not covered: this is a convenience, not a contract.\n */\nfunction warnOnBakedInFileChanges(rootDir: string): void {\n const warnedAt = new Map<string, number>();\n try {\n // `persistent: false`: the dev server is kept alive by its listener, and a watcher that outlived it\n // would hold the process open with nothing left to report to.\n watch(rootDir, { persistent: false }, (_event, filename) => {\n if (!filename || !BAKED_IN_FILES.includes(filename)) return;\n // One save fires several events — a write, a rename, an attribute change — and they are all the same\n // save. The window is short enough that a second real edit still gets its own line.\n const now = Date.now();\n if (now - (warnedAt.get(filename) ?? 0) < 250) return;\n warnedAt.set(filename, now);\n console.warn(` ⚠ ${filename} changed — restart \\`rshono dev\\` to pick it up. It is compiled into the build, not read per request.`);\n });\n } catch {\n // No watcher available: a filesystem that cannot, or a platform limit reached. Rebuilding still works,\n // so the dev server goes on without the reminder rather than refusing to start over one.\n }\n}\n\n/** An error as the dev server shows it in the browser: the stack where there is one, since this is dev. */\nfunction describe(error: unknown): string {\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\n/**\n * Whether nothing is listening on the port yet — checked before the output directory is emptied, because a\n * second `rshono dev` would otherwise wipe the chunks the running one is still importing and only then exit\n * on EADDRINUSE.\n */\nfunction portAvailable(port: number, hostname: string): Promise<boolean> {\n const { promise, resolve } = Promise.withResolvers<boolean>();\n const probe = createServer();\n probe.once('error', () => resolve(false));\n probe.listen(port, hostname, () => probe.close(() => resolve(true)));\n return promise;\n}\n\ninterface DevOptions {\n rootDir: string;\n port?: number;\n config: RshonoConfig;\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n const { rootDir, config } = options;\n const port = options.port ?? SERVER_DEFAULTS.port;\n // Its own directory, never `dist/`: a `rshono build` in another terminal must not be able to delete the\n // chunks this server is still importing.\n const outDir = join(rootDir, DEV_OUT_DIR);\n\n // Before the `rm` below, not at `serve` where the bind happens — see {@link portAvailable}.\n if (!(await portAvailable(port, '127.0.0.1'))) {\n console.error(` ✗ port ${port} is already in use`);\n process.exit(1);\n }\n\n await rm(outDir, { recursive: true, force: true });\n await mkdir(join(outDir, 'static'), { recursive: true });\n\n const encoder = new TextEncoder();\n const sseClients = new Set<ReadableStreamDefaultController<Uint8Array>>();\n let clientHash: string | undefined;\n\n const sseChunk = (data: unknown) => encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`);\n\n /**\n * Writes one already-encoded SSE frame to every open client. A failed write is what retires a client:\n * `cancel` is not always reached first when a browser navigates away.\n */\n function sendToAll(chunk: Uint8Array): void {\n for (const controller of sseClients) {\n try {\n controller.enqueue(chunk);\n } catch {\n sseClients.delete(controller);\n }\n }\n }\n\n function broadcast(message: DevMessage): void {\n sendToAll(sseChunk(message));\n }\n\n let serverComponentsChanged = false;\n const [clientConfig, serverConfig] = createConfigs({\n rootDir,\n isDev: true,\n config,\n // Always Node, whatever `deploy` says: that is a property of `build` output, and the dev server runs the\n // bundle in a worker thread of this process.\n preset: NODE_PRESET,\n onServerComponentChanges: () => {\n serverComponentsChanged = true;\n },\n });\n const compiler = rspack([clientConfig, serverConfig]);\n const [clientCompiler, serverCompiler] = compiler.compilers;\n\n let currentWorker: Worker | null = null;\n let workerPort: number | null = null;\n let workerGate = createGate();\n let restartChain: Promise<void> = Promise.resolve();\n\n function createGate() {\n const { promise, resolve } = Promise.withResolvers<{ error?: string }>();\n return { promise, open: resolve };\n }\n\n function spawnWorker(): Promise<{ worker: Worker; port: number }> {\n return new Promise((resolve, reject) => {\n const worker = new Worker(join(outDir, 'server', 'main.mjs'), {\n workerData: { port: 0, hostname: '127.0.0.1' },\n execArgv: ['--enable-source-maps'],\n env: process.env as Record<string, string>,\n });\n const timeout = setTimeout(() => {\n // Nothing waits on the termination: the promise below is already being rejected, and a worker that\n // never reported ready has no state left worth draining.\n void worker.terminate();\n reject(new Error(`server worker did not become ready within ${WORKER_READY_TIMEOUT_MS / 1000}s`));\n }, WORKER_READY_TIMEOUT_MS);\n\n let ready = false;\n worker.once('message', (message: { type?: string; port?: number }) => {\n if (message?.type === 'ready' && typeof message.port === 'number') {\n ready = true;\n clearTimeout(timeout);\n resolve({ worker, port: message.port });\n }\n });\n // `on`, not `once`: an error after the worker is ready has no pending promise left to reject, and a\n // consumed `once` would leave it unlistened — which Node turns into an uncaught exception here.\n worker.on('error', (error: Error) => {\n if (ready) {\n console.error(' ✗ server worker crashed:', error);\n return;\n }\n clearTimeout(timeout);\n reject(error);\n });\n worker.on('exit', (code) => {\n if (worker === currentWorker && code !== 0) {\n console.error(` ✗ server worker exited with code ${code} — waiting for the next rebuild`);\n currentWorker = null;\n // Opened with the reason rather than left closed, so a request arriving before the next rebuild sees\n // it instead of hanging. `hooks.invalid` installs a fresh closed gate as soon as a file changes.\n workerGate = createGate();\n workerGate.open({ error: `The server worker exited with code ${code}. See the terminal for the error it crashed with.` });\n }\n });\n });\n }\n\n serverCompiler.hooks.invalid.tap('rshono/gate', () => {\n workerGate = createGate();\n });\n\n serverCompiler.hooks.done.tapPromise('rshono/worker', async (stats: Stats) => {\n const gate = workerGate;\n restartChain = restartChain.then(async () => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n gate.open({ error: stats.toString({ preset: 'errors-only', colors: false }) });\n return;\n }\n try {\n if (currentWorker) {\n const old = currentWorker;\n currentWorker = null;\n await old.terminate();\n }\n const { worker, port: newPort } = await spawnWorker();\n currentWorker = worker;\n workerPort = newPort;\n gate.open({});\n if (serverComponentsChanged) {\n serverComponentsChanged = false;\n broadcast({ type: 'rsc-update' });\n }\n } catch (error) {\n console.error(' ✗ failed to start server worker:', error);\n gate.open({ error: describe(error) });\n }\n });\n // No link in this queue may be left rejected: `.then` on a rejected promise short-circuits, and from there\n // no worker is spawned and no gate is opened again.\n restartChain = restartChain.catch((error) => {\n console.error(' ✗ dev server restart failed:', error);\n gate.open({ error: describe(error) });\n });\n await restartChain;\n });\n\n clientCompiler.hooks.done.tap('rshono/hmr', (stats: Stats) => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n return;\n }\n clientHash = stats.hash ?? undefined;\n if (clientHash) broadcast({ type: 'client-built', hash: clientHash });\n });\n\n let firstBuild = true;\n compiler.watch([{}, {}] as never, (err, multiStats) => {\n if (err) {\n console.error(' ✗ build failed:', err);\n return;\n }\n if (multiStats && !multiStats.hasErrors()) {\n // Errors print themselves through the per-compiler `done` hooks; a warning has nowhere else to go, and\n // the env shadow reports what it cannot cover that way. See `env-shadow-loader.cjs`.\n if (multiStats.hasWarnings()) console.warn(multiStats.toString({ preset: 'errors-warnings', colors: true }));\n const seconds = Math.max(...multiStats.stats.map((s) => (s.endTime ?? 0) - (s.startTime ?? 0))) / 1000;\n console.log(` ${firstBuild ? '✓ built' : '✓ rebuilt'} in ${seconds.toFixed(1)}s`);\n firstBuild = false;\n }\n });\n\n const front = new Hono();\n\n front.route('/_static', createStaticAssetsApp({ root: join(outDir, 'static'), isDev: true }));\n\n front.get('/_rshono/hmr', () => {\n let ctrl: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n ctrl = controller;\n sseClients.add(controller);\n controller.enqueue(encoder.encode('retry: 500\\n\\n'));\n controller.enqueue(sseChunk({ type: 'hello', hash: clientHash } satisfies DevMessage));\n },\n cancel() {\n sseClients.delete(ctrl);\n },\n });\n return new Response(stream, {\n headers: {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n },\n });\n });\n\n // An SSE comment, not a message: it keeps an idle connection off a proxy's timeout without the client\n // having to know a frame type that means nothing.\n const ping = encoder.encode(': ping\\n\\n');\n setInterval(() => sendToAll(ping), 15_000).unref();\n\n front.all('*', async (c) => {\n const { error } = await workerGate.promise;\n if (error || workerPort === null) {\n return c.text(`Build failed:\\n\\n${error ?? 'server not running'}`, 500);\n }\n\n const incoming = new URL(c.req.url);\n const target = `http://127.0.0.1:${workerPort}${incoming.pathname}${incoming.search}`;\n\n // Hono's proxy helper rather than a hand-rolled `fetch`: it carries the method, the streamed body (with\n // the `duplex` Node requires) and the client's abort signal, and strips the hop-by-hop headers on the way\n // back. `headers` replaces the set wholesale, so the request's own are spread back in first.\n const response = await proxy(target, {\n raw: c.req.raw,\n // The worker's redirects are the app's answer to this request, to be rewritten below and passed on.\n redirect: 'manual',\n headers: {\n ...c.req.header(),\n // This front-end *is* the proxy the app sits behind, which is why `trustProxy` is forced on in dev:\n // without these the app resolves every URL against the worker's random 127.0.0.1 port.\n 'x-forwarded-host': incoming.host,\n 'x-forwarded-proto': incoming.protocol.replace(':', ''),\n },\n });\n\n // A redirect to the worker's own address has to become a relative one, or the browser leaves the dev\n // server for a random port only this process knows about.\n const location = response.headers.get('location');\n const loc = location ? URL.parse(location, target) : null;\n if (loc && loc.host === `127.0.0.1:${workerPort}`) {\n response.headers.set('location', `${loc.pathname}${loc.search}${loc.hash}`);\n }\n return response;\n });\n\n warnOnBakedInFileChanges(rootDir);\n\n serve({ fetch: front.fetch, port, hostname: '127.0.0.1' }, (info) => {\n console.log(` ➜ rshono dev server: http://localhost:${info.port}`);\n });\n}\n"]}
@@ -29,6 +29,14 @@ export interface DeployPreset {
29
29
  readonly runtimeModule: string;
30
30
  /** How to run what was just built, completing the "build complete —" line. */
31
31
  readonly deployHint: string;
32
+ /**
33
+ * `true` where the platform hands per-request bindings to `app.fetch(request, env)` — Workers, and
34
+ * nowhere else. It gates the `getRequestContext().env` merge, because every other target passes its own
35
+ * private handles in that argument (`{ incoming, outgoing }` on Node, the whole invocation on Lambda) and
36
+ * merging those would put a live socket, or a request's headers and cookies, behind a name typed
37
+ * `string | undefined`.
38
+ */
39
+ readonly envBindings?: boolean;
32
40
  /**
33
41
  * Extra resolve conditions for the server bundle, most specific first — what picks the right build of React
34
42
  * and the RSC runtime, both of which ship one per runtime. Omit to accept whatever the Rspack target
@@ -1 +1 @@
1
- {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/deploy/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,uGAAuG;IACvG,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kGAAkG;IAClG,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,qFAAqF;IACrF,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;OAGG;IACH,eAAe,CAAC,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,YAIzB,CAAC;AAiFF,kEAAkE;AAClE,eAAO,MAAM,cAAc,EAA2B,YAAY,EAAE,CAAC;AAErE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mGAAmG;IACnG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,aAAkB,GAAG,YAAY,CAS7E"}
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/deploy/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,uGAAuG;IACvG,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kGAAkG;IAClG,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,qFAAqF;IACrF,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;OAGG;IACH,eAAe,CAAC,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,YAIzB,CAAC;AAkFF,kEAAkE;AAClE,eAAO,MAAM,cAAc,EAA2B,YAAY,EAAE,CAAC;AAErE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mGAAmG;IACnG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,aAAkB,GAAG,YAAY,CAS7E"}
@@ -21,6 +21,7 @@ const CLOUDFLARE_PRESET = {
21
21
  name: 'cloudflare',
22
22
  runtimeModule: 'deploy/cloudflare/runtime.js',
23
23
  deployHint: 'deploy with `wrangler deploy`',
24
+ envBindings: true,
24
25
  resolveConditions: ['workerd'],
25
26
  syntaxTargets: ['chrome 120'],
26
27
  configureServer(config) {
@@ -1 +1 @@
1
- {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/deploy/presets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAoDxD;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiB;IACvC,IAAI,EAAE,MAAM;IACZ,aAAa,EAAE,wBAAwB;IACvC,UAAU,EAAE,oBAAoB;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,8BAA8B;IAC7C,UAAU,EAAE,+BAA+B;IAC3C,iBAAiB,EAAE,CAAC,SAAS,CAAC;IAC9B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,eAAe,CAAC,MAAM;QACpB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;QAC5B,MAAM,CAAC,aAAa,GAAG,eAAe,CAAC;QACvC,MAAM,CAAC,SAAS,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,QAAQ,EAAE,uBAAuB;CAClC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,kBAAkB,CAAC,MAAqB;IAC/C,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,aAAa,GAAiB;IAClC,IAAI,EAAE,QAAQ;IACd,aAAa,EAAE,0BAA0B;IACzC,UAAU,EAAE,wCAAwC;IACpD,eAAe,EAAE,kBAAkB;IACnC,QAAQ,EAAE,mBAAmB;CAC9B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,8BAA8B;IAC7C,UAAU,EAAE,oDAAoD;IAChE,eAAe,EAAE,kBAAkB;CACpC,CAAC;AAEF,MAAM,OAAO,GAAuC;IAClD,IAAI,EAAE,WAAW;IACjB,UAAU,EAAE,iBAAiB;IAC7B,MAAM,EAAE,aAAa;IACrB,YAAY,EAAE,iBAAiB;CAChC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAmB,CAAC;AAErE;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAQ,OAAoD,CAAC,MAAM,CAAC,EAAE,UAAU,IAAI,IAAI,CAAC;AAC3F,CAAC;AAYD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAkB,EAAE;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACrF,IAAI,CAAC,MAAM;QAAE,OAAO,WAAW,CAAC;IAEhC,MAAM,MAAM,GAAI,OAAoD,CAAC,MAAM,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/H,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { RspackOptions } from '@rspack/core';\nimport { finalizeCloudflareBuild } from './cloudflare/build.js';\nimport type { DeployTarget } from './contract.js';\nimport { finalizeVercelBuild } from './vercel/build.js';\n\n/** What a preset's {@link DeployPreset.finalize} is told about the build it is arranging. */\nexport interface DeployBuildContext {\n /** The project root — where `rshono build` ran, and what a platform config file is written next to. */\n rootDir: string;\n /** `<root>/dist`. */\n distDir: string;\n /** The hashed client bundle, `<root>/dist/static` — served at `/_static`. */\n staticDir: string;\n /** The copy of the app's `public/`, or `null` when it has none. Served at the web root. */\n publicDir: string | null;\n /** Prerendered pages, `<root>/dist/ssg` — empty when the app has no `render: 'static'` routes. */\n ssgDir: string;\n}\n\n/**\n * The build-time half of a deploy target: which runtime module the bundle gets, how the server compiler has\n * to change to produce something the platform can run, and how the output is arranged once it exists.\n *\n * The runtime half is `DeployRuntime`, in its own file because it is compiled *into* the app bundle — this\n * side only ever runs in the CLI.\n */\nexport interface DeployPreset {\n readonly name: DeployTarget;\n /**\n * The module `@rshono/deploy` resolves to, as a path relative to the framework's own `dist/`.\n * Slash-separated and split on use, so it stays a valid path on Windows too.\n */\n readonly runtimeModule: string;\n /** How to run what was just built, completing the \"build complete —\" line. */\n readonly deployHint: string;\n /**\n * Extra resolve conditions for the server bundle, most specific first — what picks the right build of React\n * and the RSC runtime, both of which ship one per runtime. Omit to accept whatever the Rspack target\n * implies, which is correct for Node.\n */\n readonly resolveConditions?: readonly string[];\n /** browserslist-style targets for the server bundle's swc pass. Defaults to Node. */\n readonly syntaxTargets?: readonly string[];\n /**\n * Adjusts the generated server Rspack config for this platform — target, externals policy, output shape.\n * Mutates in place, and runs before the user's `rspack` hook so that hook keeps the last word.\n */\n configureServer?(config: RspackOptions): void;\n /**\n * Arranges the finished build for the platform: assembles the directory layout it expects, emits its config\n * file, and prints how to deploy. Runs last, so everything it needs to move is already on disk.\n */\n finalize?(ctx: DeployBuildContext): Promise<void> | void;\n}\n\n/**\n * Node: a long-lived server process. The generated config is already this shape — `target: 'node'`, the\n * externals policy and ESM chunk output are all defaults — so the preset has nothing to contribute.\n */\nexport const NODE_PRESET: DeployPreset = {\n name: 'node',\n runtimeModule: 'deploy/node/runtime.js',\n deployHint: 'run `rshono start`',\n};\n\n/**\n * Cloudflare Workers: the host owns the process, the CDN owns the assets, and there is no filesystem.\n *\n * Every setting here follows from `workerd` not being Node. Dependencies are bundled because nothing resolves\n * `node_modules` at runtime; `node:` and `cloudflare:` imports stay external because the runtime provides\n * them under `nodejs_compat`; and async chunks are inlined because Wrangler's bundler cannot follow the\n * computed specifier Rspack's ESM chunk loader emits — a split bundle deploys and then fails on first render.\n */\nconst CLOUDFLARE_PRESET: DeployPreset = {\n name: 'cloudflare',\n runtimeModule: 'deploy/cloudflare/runtime.js',\n deployHint: 'deploy with `wrangler deploy`',\n resolveConditions: ['workerd'],\n syntaxTargets: ['chrome 120'],\n configureServer(config) {\n config.target = 'webworker';\n config.externalsType = 'module-import';\n config.externals = [/^(?:node|cloudflare):/];\n config.output = { ...config.output, asyncChunks: false };\n },\n finalize: finalizeCloudflareBuild,\n};\n\n/**\n * Bundles the app's dependencies into the server output instead of importing them from `node_modules`.\n *\n * The generated config externalizes them, which is right where the bundle runs *inside* the project — but a\n * serverless function is uploaded as a directory rather than installed into one, so nothing resolves\n * `node_modules` at request time and an externalized `import 'some-package'` is a cold start that dies on\n * `ERR_MODULE_NOT_FOUND`. Node's own builtins stay external either way, through the `node` externals preset\n * that `target: 'node'` already enables.\n *\n * The cost is a dependency that cannot be bundled — a native addon, or one that reads its own files off disk\n * relative to `__dirname`. Those now fail the build rather than the deploy, which is the same constraint\n * `cloudflare` has always had; the `rspack` hook in `rshono.config.ts` is the way out.\n */\nfunction bundleDependencies(config: RspackOptions): void {\n config.externals = [];\n}\n\n/**\n * Vercel: one Node function behind the platform's CDN, which serves the assets and reaches the function only\n * for a page. `finalize` assembles the Build Output API layout the platform uploads — including the\n * `supportsResponseStreaming` flag, without which Vercel buffers the whole response and silently undoes\n * streamed SSR.\n *\n * Only `dist/server` is uploaded with the function, which is why dependencies are bundled: see\n * {@link bundleDependencies}.\n */\nconst VERCEL_PRESET: DeployPreset = {\n name: 'vercel',\n runtimeModule: 'deploy/vercel/runtime.js',\n deployHint: 'deploy with `vercel deploy --prebuilt`',\n configureServer: bundleDependencies,\n finalize: finalizeVercelBuild,\n};\n\n/**\n * AWS Lambda behind a Function URL in `RESPONSE_STREAM` mode — the AWS shape that keeps streaming. The\n * runtime wraps the app in `awslambda.streamifyResponse`; the buffered alternative would deploy fine and then\n * hold every page until its last byte rendered.\n *\n * The deployment package is `dist/` and nothing else, which is why dependencies are bundled: see\n * {@link bundleDependencies}.\n */\nconst AWS_LAMBDA_PRESET: DeployPreset = {\n name: 'aws-lambda',\n runtimeModule: 'deploy/aws-lambda/runtime.js',\n deployHint: 'zip dist/ with the handler at dist/server/main.mjs',\n configureServer: bundleDependencies,\n};\n\nconst PRESETS: Record<DeployTarget, DeployPreset> = {\n node: NODE_PRESET,\n cloudflare: CLOUDFLARE_PRESET,\n vercel: VERCEL_PRESET,\n 'aws-lambda': AWS_LAMBDA_PRESET,\n};\n\n/** Every target `deploy` accepts, for error messages and docs. */\nexport const DEPLOY_TARGETS = Object.keys(PRESETS) as DeployTarget[];\n\n/**\n * How to deploy what a given target built, or `null` for a name this rshono does not know — which a `dist/`\n * from a newer version can legitimately carry.\n */\nexport function deployHintFor(target: string): string | null {\n return (PRESETS as Record<string, DeployPreset | undefined>)[target]?.deployHint ?? null;\n}\n\n/** Where a deploy target can be named, in precedence order. */\nexport interface DeploySources {\n /** The `--deploy` flag. */\n flag?: string;\n /** The `RSHONO_DEPLOY` env var — for a CI job that deploys the same app to more than one place. */\n env?: string;\n /** The `deploy` field in `rshono.config.ts`. */\n config?: string;\n}\n\n/**\n * Resolves the preset to build with: the flag wins over the environment, which wins over the config file,\n * which wins over the `node` default. Blank values are ignored at every level, so an unset-but-present\n * `RSHONO_DEPLOY=` in CI falls through rather than failing the build.\n */\nexport function resolveDeployPreset(sources: DeploySources = {}): DeployPreset {\n const target = sources.flag?.trim() || sources.env?.trim() || sources.config?.trim();\n if (!target) return NODE_PRESET;\n\n const preset = (PRESETS as Record<string, DeployPreset | undefined>)[target];\n if (!preset) {\n throw new Error(`[rshono] unknown deploy target ${JSON.stringify(target)} — expected one of: ${DEPLOY_TARGETS.join(', ')}.`);\n }\n return preset;\n}\n"]}
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/deploy/presets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA4DxD;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiB;IACvC,IAAI,EAAE,MAAM;IACZ,aAAa,EAAE,wBAAwB;IACvC,UAAU,EAAE,oBAAoB;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,8BAA8B;IAC7C,UAAU,EAAE,+BAA+B;IAC3C,WAAW,EAAE,IAAI;IACjB,iBAAiB,EAAE,CAAC,SAAS,CAAC;IAC9B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,eAAe,CAAC,MAAM;QACpB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;QAC5B,MAAM,CAAC,aAAa,GAAG,eAAe,CAAC;QACvC,MAAM,CAAC,SAAS,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,QAAQ,EAAE,uBAAuB;CAClC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,kBAAkB,CAAC,MAAqB;IAC/C,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,aAAa,GAAiB;IAClC,IAAI,EAAE,QAAQ;IACd,aAAa,EAAE,0BAA0B;IACzC,UAAU,EAAE,wCAAwC;IACpD,eAAe,EAAE,kBAAkB;IACnC,QAAQ,EAAE,mBAAmB;CAC9B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAiB;IACtC,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,8BAA8B;IAC7C,UAAU,EAAE,oDAAoD;IAChE,eAAe,EAAE,kBAAkB;CACpC,CAAC;AAEF,MAAM,OAAO,GAAuC;IAClD,IAAI,EAAE,WAAW;IACjB,UAAU,EAAE,iBAAiB;IAC7B,MAAM,EAAE,aAAa;IACrB,YAAY,EAAE,iBAAiB;CAChC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAmB,CAAC;AAErE;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAQ,OAAoD,CAAC,MAAM,CAAC,EAAE,UAAU,IAAI,IAAI,CAAC;AAC3F,CAAC;AAYD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAkB,EAAE;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACrF,IAAI,CAAC,MAAM;QAAE,OAAO,WAAW,CAAC;IAEhC,MAAM,MAAM,GAAI,OAAoD,CAAC,MAAM,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/H,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { RspackOptions } from '@rspack/core';\nimport { finalizeCloudflareBuild } from './cloudflare/build.js';\nimport type { DeployTarget } from './contract.js';\nimport { finalizeVercelBuild } from './vercel/build.js';\n\n/** What a preset's {@link DeployPreset.finalize} is told about the build it is arranging. */\nexport interface DeployBuildContext {\n /** The project root — where `rshono build` ran, and what a platform config file is written next to. */\n rootDir: string;\n /** `<root>/dist`. */\n distDir: string;\n /** The hashed client bundle, `<root>/dist/static` — served at `/_static`. */\n staticDir: string;\n /** The copy of the app's `public/`, or `null` when it has none. Served at the web root. */\n publicDir: string | null;\n /** Prerendered pages, `<root>/dist/ssg` — empty when the app has no `render: 'static'` routes. */\n ssgDir: string;\n}\n\n/**\n * The build-time half of a deploy target: which runtime module the bundle gets, how the server compiler has\n * to change to produce something the platform can run, and how the output is arranged once it exists.\n *\n * The runtime half is `DeployRuntime`, in its own file because it is compiled *into* the app bundle — this\n * side only ever runs in the CLI.\n */\nexport interface DeployPreset {\n readonly name: DeployTarget;\n /**\n * The module `@rshono/deploy` resolves to, as a path relative to the framework's own `dist/`.\n * Slash-separated and split on use, so it stays a valid path on Windows too.\n */\n readonly runtimeModule: string;\n /** How to run what was just built, completing the \"build complete —\" line. */\n readonly deployHint: string;\n /**\n * `true` where the platform hands per-request bindings to `app.fetch(request, env)` — Workers, and\n * nowhere else. It gates the `getRequestContext().env` merge, because every other target passes its own\n * private handles in that argument (`{ incoming, outgoing }` on Node, the whole invocation on Lambda) and\n * merging those would put a live socket, or a request's headers and cookies, behind a name typed\n * `string | undefined`.\n */\n readonly envBindings?: boolean;\n /**\n * Extra resolve conditions for the server bundle, most specific first — what picks the right build of React\n * and the RSC runtime, both of which ship one per runtime. Omit to accept whatever the Rspack target\n * implies, which is correct for Node.\n */\n readonly resolveConditions?: readonly string[];\n /** browserslist-style targets for the server bundle's swc pass. Defaults to Node. */\n readonly syntaxTargets?: readonly string[];\n /**\n * Adjusts the generated server Rspack config for this platform — target, externals policy, output shape.\n * Mutates in place, and runs before the user's `rspack` hook so that hook keeps the last word.\n */\n configureServer?(config: RspackOptions): void;\n /**\n * Arranges the finished build for the platform: assembles the directory layout it expects, emits its config\n * file, and prints how to deploy. Runs last, so everything it needs to move is already on disk.\n */\n finalize?(ctx: DeployBuildContext): Promise<void> | void;\n}\n\n/**\n * Node: a long-lived server process. The generated config is already this shape — `target: 'node'`, the\n * externals policy and ESM chunk output are all defaults — so the preset has nothing to contribute.\n */\nexport const NODE_PRESET: DeployPreset = {\n name: 'node',\n runtimeModule: 'deploy/node/runtime.js',\n deployHint: 'run `rshono start`',\n};\n\n/**\n * Cloudflare Workers: the host owns the process, the CDN owns the assets, and there is no filesystem.\n *\n * Every setting here follows from `workerd` not being Node. Dependencies are bundled because nothing resolves\n * `node_modules` at runtime; `node:` and `cloudflare:` imports stay external because the runtime provides\n * them under `nodejs_compat`; and async chunks are inlined because Wrangler's bundler cannot follow the\n * computed specifier Rspack's ESM chunk loader emits — a split bundle deploys and then fails on first render.\n */\nconst CLOUDFLARE_PRESET: DeployPreset = {\n name: 'cloudflare',\n runtimeModule: 'deploy/cloudflare/runtime.js',\n deployHint: 'deploy with `wrangler deploy`',\n envBindings: true,\n resolveConditions: ['workerd'],\n syntaxTargets: ['chrome 120'],\n configureServer(config) {\n config.target = 'webworker';\n config.externalsType = 'module-import';\n config.externals = [/^(?:node|cloudflare):/];\n config.output = { ...config.output, asyncChunks: false };\n },\n finalize: finalizeCloudflareBuild,\n};\n\n/**\n * Bundles the app's dependencies into the server output instead of importing them from `node_modules`.\n *\n * The generated config externalizes them, which is right where the bundle runs *inside* the project — but a\n * serverless function is uploaded as a directory rather than installed into one, so nothing resolves\n * `node_modules` at request time and an externalized `import 'some-package'` is a cold start that dies on\n * `ERR_MODULE_NOT_FOUND`. Node's own builtins stay external either way, through the `node` externals preset\n * that `target: 'node'` already enables.\n *\n * The cost is a dependency that cannot be bundled — a native addon, or one that reads its own files off disk\n * relative to `__dirname`. Those now fail the build rather than the deploy, which is the same constraint\n * `cloudflare` has always had; the `rspack` hook in `rshono.config.ts` is the way out.\n */\nfunction bundleDependencies(config: RspackOptions): void {\n config.externals = [];\n}\n\n/**\n * Vercel: one Node function behind the platform's CDN, which serves the assets and reaches the function only\n * for a page. `finalize` assembles the Build Output API layout the platform uploads — including the\n * `supportsResponseStreaming` flag, without which Vercel buffers the whole response and silently undoes\n * streamed SSR.\n *\n * Only `dist/server` is uploaded with the function, which is why dependencies are bundled: see\n * {@link bundleDependencies}.\n */\nconst VERCEL_PRESET: DeployPreset = {\n name: 'vercel',\n runtimeModule: 'deploy/vercel/runtime.js',\n deployHint: 'deploy with `vercel deploy --prebuilt`',\n configureServer: bundleDependencies,\n finalize: finalizeVercelBuild,\n};\n\n/**\n * AWS Lambda behind a Function URL in `RESPONSE_STREAM` mode — the AWS shape that keeps streaming. The\n * runtime wraps the app in `awslambda.streamifyResponse`; the buffered alternative would deploy fine and then\n * hold every page until its last byte rendered.\n *\n * The deployment package is `dist/` and nothing else, which is why dependencies are bundled: see\n * {@link bundleDependencies}.\n */\nconst AWS_LAMBDA_PRESET: DeployPreset = {\n name: 'aws-lambda',\n runtimeModule: 'deploy/aws-lambda/runtime.js',\n deployHint: 'zip dist/ with the handler at dist/server/main.mjs',\n configureServer: bundleDependencies,\n};\n\nconst PRESETS: Record<DeployTarget, DeployPreset> = {\n node: NODE_PRESET,\n cloudflare: CLOUDFLARE_PRESET,\n vercel: VERCEL_PRESET,\n 'aws-lambda': AWS_LAMBDA_PRESET,\n};\n\n/** Every target `deploy` accepts, for error messages and docs. */\nexport const DEPLOY_TARGETS = Object.keys(PRESETS) as DeployTarget[];\n\n/**\n * How to deploy what a given target built, or `null` for a name this rshono does not know — which a `dist/`\n * from a newer version can legitimately carry.\n */\nexport function deployHintFor(target: string): string | null {\n return (PRESETS as Record<string, DeployPreset | undefined>)[target]?.deployHint ?? null;\n}\n\n/** Where a deploy target can be named, in precedence order. */\nexport interface DeploySources {\n /** The `--deploy` flag. */\n flag?: string;\n /** The `RSHONO_DEPLOY` env var — for a CI job that deploys the same app to more than one place. */\n env?: string;\n /** The `deploy` field in `rshono.config.ts`. */\n config?: string;\n}\n\n/**\n * Resolves the preset to build with: the flag wins over the environment, which wins over the config file,\n * which wins over the `node` default. Blank values are ignored at every level, so an unset-but-present\n * `RSHONO_DEPLOY=` in CI falls through rather than failing the build.\n */\nexport function resolveDeployPreset(sources: DeploySources = {}): DeployPreset {\n const target = sources.flag?.trim() || sources.env?.trim() || sources.config?.trim();\n if (!target) return NODE_PRESET;\n\n const preset = (PRESETS as Record<string, DeployPreset | undefined>)[target];\n if (!preset) {\n throw new Error(`[rshono] unknown deploy target ${JSON.stringify(target)} — expected one of: ${DEPLOY_TARGETS.join(', ')}.`);\n }\n return preset;\n}\n"]}
package/dist/router.d.ts CHANGED
@@ -340,18 +340,44 @@ type ValidateRoute<R> = R extends {
340
340
  type ValidateRoutes<TRoutes extends readonly Route[]> = {
341
341
  [K in keyof TRoutes]: ValidateRoute<TRoutes[K]>;
342
342
  };
343
+ /**
344
+ * Refuses a key that is not a {@link RouteConfig} field — a typo'd `notfound` is otherwise a fallback page
345
+ * that never renders, and nothing at runtime looks for one.
346
+ *
347
+ * Excess-property checking did this while the object form was an overload of its own with a concrete
348
+ * parameter type. It cannot once the parameter is generic and inferred from the argument, because the
349
+ * inferred type *has* the extra key. The message here is the better one anyway: it lands on the field.
350
+ */
351
+ type ValidateConfigKeys<T> = Exclude<keyof T, keyof RouteConfig> extends never ? T : T & Record<Exclude<keyof T, keyof RouteConfig>, 'not a defineRoutes field — the fields are routes, notFound and error'>;
352
+ /** The check for whichever of the two accepted shapes was passed. */
353
+ type ValidateInput<T> = T extends readonly Route[] ? ValidateRoutes<T> : T extends {
354
+ routes: infer R extends readonly Route[];
355
+ } ? ValidateConfigKeys<T> & {
356
+ routes: ValidateRoutes<R>;
357
+ } : T;
358
+ /** The route tuple inside either shape, so the return type carries the literals through both. */
359
+ type RoutesOf<T> = T extends readonly Route[] ? T : T extends {
360
+ routes: infer R extends readonly Route[];
361
+ } ? R : readonly Route[];
343
362
  /**
344
363
  * Declares the app's route table. Export the result as `routes` from `src/routes.ts` — the one file
345
364
  * rshono requires. It only ever runs on the server, so importing server-only modules from it (inside
346
365
  * `staticPaths`, say) is safe.
347
366
  *
367
+ * Takes a {@link RouteConfig} — the `routes` array plus the optional `notFound` and `error` pages — or a
368
+ * bare {@link Route} array as shorthand for an app with neither.
369
+ *
348
370
  * Beyond typing the config, every page is cross-checked against its own path: props not satisfied by
349
- * `PageProps<'<its path>'>` make the `component` field a type error. A bare {@link Route} array is
350
- * accepted as shorthand — see the second overload.
371
+ * `PageProps<'<its path>'>` make the `component` field a type error, and a `staticPaths` whose param sets do
372
+ * not fill the path makes that field one.
373
+ *
374
+ * **One signature over both shapes, deliberately.** The array form used to be a second overload, which meant
375
+ * a mistake inside a bare array was reported as an overload-resolution failure whose *first* line was the
376
+ * object form's complaint — "Property 'routes' is missing" — pointing at a change the author should not make.
377
+ * The real message was on line 8. A single signature reports the argument once, at the field that is wrong.
351
378
  *
352
- * @param config - A {@link RouteConfig}: the `routes` array plus the optional `notFound` and `error`
353
- * pages.
354
- * @returns The config, unchanged and fully typed.
379
+ * @param input - A {@link RouteConfig}, or the bare `routes` array.
380
+ * @returns The config, unchanged and fully typed; an array is wrapped as `{ routes }`.
355
381
  *
356
382
  * @example
357
383
  * ```ts
@@ -375,25 +401,14 @@ type ValidateRoutes<TRoutes extends readonly Route[]> = {
375
401
  * });
376
402
  * ```
377
403
  *
378
- * @see {@link https://www.rshono.com/docs/routing | Docs — routing}
379
- */
380
- export declare function defineRoutes<const TRoutes extends readonly Route[]>(config: RouteConfig<TRoutes> & {
381
- routes: ValidateRoutes<TRoutes>;
382
- }): RouteConfig<TRoutes>;
383
- /**
384
- * Array shorthand for {@link defineRoutes} — equivalent to `defineRoutes({ routes })`, for an app
385
- * with no `notFound` or `error` page.
386
- *
387
- * @param routes - The {@link Route} array; each page is checked against its own `path`.
388
- * @returns A {@link RouteConfig} wrapping them.
389
- *
390
404
  * @example
391
405
  * ```ts
406
+ * // The shorthand, for an app with no notFound or error page.
392
407
  * export const routes = defineRoutes([{ path: '/', component: () => import('./components/home') }]);
393
408
  * ```
394
409
  *
395
410
  * @see {@link https://www.rshono.com/docs/routing | Docs — routing}
396
411
  */
397
- export declare function defineRoutes<const TRoutes extends readonly Route[]>(routes: TRoutes & ValidateRoutes<TRoutes>): RouteConfig<TRoutes>;
412
+ export declare function defineRoutes<const T extends readonly Route[] | RouteConfig<readonly Route[]>>(input: T & ValidateInput<T>): RouteConfig<RoutesOf<T>>;
398
413
  export {};
399
414
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AACjD,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC;AAEpH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IACrC,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAErH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,SAAS,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,GAAG,GAAG,GAAG;IAC1E;;;;;;;;OAQG;IACH,GAAG,EAAE,GAAG,CAAC;IACT,qFAAqF;IACrF,MAAM,EAAE,MAAM,SAAS,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACxE;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AAIH,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAElF;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IACrD,oGAAoG;IACpG,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CAC5F;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC;IAC5C,8EAA8E;IAC9E,MAAM,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAEzF,wFAAwF;AACxF,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,aAAa,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,SAAS,CAE5D;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,0FAA0F;IAC1F,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;CACtD;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG;IACvE,6FAA6F;IAC7F,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW,CAAC,OAAO,SAAS,SAAS,KAAK,EAAE,GAAG,SAAS,KAAK,EAAE;IAC9E,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,KAAK,mBAAmB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAC1C,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,GACtB,CAAC,GACD,CAAC,SAAS;IAAE,WAAW,EAAE,MAAM,MAAM,IAAI,CAAA;CAAE,GACzC,OAAO,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,MAAM,GAAG,CAAC,GAC5C,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GACvC,CAAC,GACD,CAAC,GAAG;IAAE,WAAW,EAAE,4DAA4D,CAAC,GAAG,CAAA;CAAE,GACvF,CAAC,GACH,CAAC,CAAC;AAIV,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS;IAChC,IAAI,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAChE,GAEG;IAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;CAAC,SAAS,CAAC,EAAE,CAAC,GAC9B,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GACzB,CAAC,GAAG;IAAE,SAAS,EAAE,mDAAmD,CAAC,IAAI,CAAA;CAAE,GAC7E,CAAC,CAAC;AAEN,KAAK,cAAc,CAAC,OAAO,SAAS,SAAS,KAAK,EAAE,IAAI;KAAG,CAAC,IAAI,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAE5G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,OAAO,SAAS,SAAS,KAAK,EAAE,EACjE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;CAAE,GACjE,WAAW,CAAC,OAAO,CAAC,CAAC;AACxB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,OAAO,SAAS,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAyPA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC;AACnC,CAAC;AAmKD,MAAM,UAAU,YAAY,CAAC,KAAqC;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAE,KAAqB,CAAC;AAC3E,CAAC","sourcesContent":["import type { Env, Handler } from 'hono';\nimport type { ParamKeys, ParamKeyToRecord } from 'hono/types';\nimport type { ReactNode } from 'react';\n// Type-only, so importing `@rshono/core` pulls in none of `context.ts`'s runtime machinery.\nimport type { RequestContext } from './runtime/context.js';\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\ntype UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;\n\n/**\n * The `params` record implied by a route path pattern — one required `string` key per `:param`\n * segment, `Record<string, never>` for a path with none. Paths use Hono's syntax, so `:id`,\n * `:id{[0-9]+}` and `*` all work.\n *\n * You rarely name this directly; {@link PageProps} applies it for you.\n *\n * @typeParam P - The literal route path, e.g. `'/users/:id/posts/:postId'`.\n *\n * @example\n * ```ts\n * type P = PathParams<'/users/:id/posts/:postId'>; // { id: string; postId: string }\n * ```\n *\n * @see {@link https://hono.dev/docs/api/routing#path-parameter | Hono — path parameters}\n */\nexport type PathParams<P extends string> =\n ParamKeys<P> extends never ? Record<string, never> : Simplify<UnionToIntersection<ParamKeyToRecord<ParamKeys<P>>>>;\n\n/**\n * Props every page component receives. Pass the route's path as the type argument to get `params`\n * typed key-by-key; without it `params` falls back to an open `Record<string, string>`.\n *\n * `defineRoutes` checks each page's props against `PageProps<path>`, so a mismatched path literal is\n * a type error at the route definition. `url` and `params` mirror what `useNavigation()` gives a\n * `'use client'` component, so a read moves across the server/client line unchanged.\n *\n * @typeParam Path - The literal path this page is mounted at, e.g. `'/profile/:id'`.\n * @typeParam E - The app's Hono {@link Env}, to type {@link RequestContext.var} and\n * {@link RequestContext.env} on {@link PageProps.ctx}.\n *\n * @example\n * ```tsx\n * import type { PageProps } from '@rshono/core';\n *\n * export default async function Profile({ params, url }: PageProps<'/profile/:id'>) {\n * const user = await db.getUser(params.id); // params.id is string\n * const tab = url.searchParams.get('tab') ?? 'overview';\n * return <Layout>{user.name} — {tab}</Layout>;\n * }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/pages#page-props | Docs — page props}\n */\nexport interface PageProps<Path extends string = string, E extends Env = Env> {\n /**\n * The absolute browser-facing request {@link URL}, proxy-header aware (`X-Forwarded-Host` /\n * `-Proto`). A fresh instance per request, so mutating it is local to the page; it is not\n * serializable, so hand a `'use client'` component `url.href` rather than `url`.\n *\n * On a `render: 'static'` route this is the build-time URL — rendered once against `siteUrl`, so\n * `url.searchParams` is always empty. Read the query from `useNavigation().url` in a `'use client'`\n * component instead, or mark the route `render: 'dynamic'`.\n */\n url: URL;\n /** Matched route params for this request, e.g. `{ id: '42' }` for `/profile/:id`. */\n params: string extends Path ? Record<string, string> : PathParams<Path>;\n /**\n * The request context — the object `getRequestContext()` returns, handed to the page so cookies,\n * headers, env and middleware variables are reachable without an import.\n *\n * Server-only, non-enumerable (a `{...props}` spread and `JSON.stringify` both skip it) and never\n * serialized. Passing it to a `'use client'` component fails the render, because it wraps the live\n * request — read what you need here and pass plain values down.\n *\n * Non-enumerable is the one place this API breaks a JavaScript expectation, and it is unavoidable: an\n * enumerable `ctx` would put `ctx.hono.env` — every binding and secret — into React's dev-only\n * serialization of a server component's props, which walks own enumerable properties. So `<Child\n * {...props} />` hands a **server** child `ctx: undefined` with no error, while the type says otherwise.\n * Nested server components are meant to call `getRequestContext()` for the same object rather than\n * receive it, which is also the fix if a spread has already cost you an afternoon.\n *\n * Reading it on a `render: 'static'` route throws: a prerendered page has no per-request context.\n * Mark the route `render: 'dynamic'`, or use the `url` / `params` props.\n *\n * @example\n * ```tsx\n * export default function Dashboard({ ctx }: PageProps) {\n * const session = ctx.cookies.get('session');\n * if (!session) redirect('/login');\n * return <Layout>Signed in as {session}</Layout>;\n * }\n * ```\n */\n ctx: RequestContext<E>;\n}\n\n/**\n * A page: a React **server component** rendering the entire document (`<html>…</html>`), usually via\n * a shared layout. It may be `async` and await data directly.\n *\n * Each page module default-exports exactly one. Interactive parts belong in `'use client'` components\n * the page imports — only those ship JS.\n *\n * @typeParam P - The component's props; for a page these are {@link PageProps}.\n *\n * @see {@link https://react.dev/reference/rsc/server-components | React — Server Components}\n * @see {@link https://www.rshono.com/docs/pages | Docs — pages}\n */\n// `any`, not `unknown`: this default is what an unparameterised `PageComponent` means in a user's own\n// annotation, and `unknown` props would reject every component that declares the props it actually takes.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PageComponent<P = any> = (props: P) => ReactNode | Promise<ReactNode>;\n\n/**\n * The shape an `{ type: 'endpoint' }` route's server module must have: a single named `handler`\n * export. It only ever loads on the server, so importing a database client or reading secrets from it\n * is safe.\n *\n * @example\n * ```ts\n * // src/health.ts\n * import type { Handler } from 'hono';\n *\n * export const handler: Handler = (c) => c.json({ ok: true });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#endpoint-routes | Docs — endpoint routes}\n */\nexport interface EndpointServerModule {\n /**\n * A Hono {@link Handler} for every request the route matches. It is passed Hono's `Context`, so the\n * request, the response builders (`c.json`, `c.text`, `c.body`) and middleware variables are all\n * reached through it.\n *\n * @see {@link https://hono.dev/docs/api/context | Hono — Context}\n */\n handler: Handler;\n}\n\n/**\n * A page route — a path rendered by a server component. This is the default\n * route kind, so `type` can be omitted.\n *\n * @example\n * ```ts\n * { path: '/profile/:id', component: () => import('./components/profile') }\n * ```\n */\nexport interface PageRoute {\n /** Discriminates a page from an endpoint; optional because `'page'` is the default. */\n type?: 'page';\n /**\n * Hono-style path pattern, e.g. `/`, `/profile/:id`, `/files/*`. Routes are matched in\n * declaration order.\n *\n * @see {@link https://hono.dev/docs/api/routing | Hono — routing}\n */\n path: string;\n /**\n * Dynamic import of the page module, whose default export is the {@link PageComponent}.\n *\n * Write it inline as `() => import('…')`: the framework detects that exact form and injects the\n * `'use server-entry'` directive that attaches the page's client JS and CSS. Wire the component up\n * any other way — a variable, a barrel re-export, a computed specifier — and you have to put\n * `'use server-entry'` on the page module's first line yourself.\n *\n * @example\n * ```ts\n * component: () => import('./components/profile')\n * ```\n *\n * @see {@link https://www.rshono.com/docs/pages#the-use-server-entry-directive | Docs — the `'use server-entry'` directive}\n */\n component: () => Promise<{ default: PageComponent }>;\n /** `'static'` prerenders the route at build time; `'dynamic'` (the default) renders per request. */\n render?: 'static' | 'dynamic';\n /**\n * For a `render: 'static'` route with params: the param sets to prerender, one page each. Runs at\n * build time on the server, so it may hit a database or read the filesystem.\n *\n * A parameterised static route without this falls back to rendering per request, with a build\n * warning. Wildcard (`*`), optional and regex params cannot be prerendered.\n *\n * @example\n * ```ts\n * {\n * path: '/docs/:slug',\n * render: 'static',\n * component: () => import('./components/documentation'),\n * staticPaths: async () => (await db.docs.all()).map((d) => ({ slug: d.slug })),\n * }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#static-rendering | Docs — static rendering}\n */\n staticPaths?: () => Array<Record<string, string>> | Promise<Array<Record<string, string>>>;\n}\n\n/**\n * An endpoint route — a path served by a raw Hono handler instead of a React\n * component. Use it for JSON APIs, webhooks, redirects, feeds, or anything that\n * isn't an HTML page.\n *\n * @example\n * ```ts\n * { type: 'endpoint', path: '/api/health', server: () => import('./health') }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#endpoint-routes | Docs — endpoint routes}\n */\nexport interface EndpointRoute {\n /** Marks this route as an endpoint rather than a page. Required. */\n type: 'endpoint';\n /**\n * Hono-style path pattern, e.g. `/api/health`, `/api/users/:id`.\n *\n * @see {@link https://hono.dev/docs/api/routing | Hono — routing}\n */\n path: string;\n /**\n * HTTP method to match, or a list of them. Defaults to `'all'` — every method.\n *\n * There is no `'head'`: Hono dispatches a `HEAD` as a `GET` and strips the body off the response, so a\n * `HEAD` is already answered by the `'get'` handler (and by `'all'`), and a route registered for `HEAD`\n * alone would never be reached.\n *\n * A list is how a two-method endpoint says so; `'all'` inside one is refused, since it is either the\n * whole thing or a mistake. A method the route does not name gets Hono's 404 rather than the handler.\n *\n * @example\n * ```ts\n * { type: 'endpoint', path: '/api/session', method: ['get', 'delete'], server: () => import('./session') }\n * ```\n */\n method?: HTTPMethod | readonly HTTPMethod[];\n /** Dynamic import of the {@link EndpointServerModule} exporting `handler`. */\n server: () => Promise<EndpointServerModule>;\n}\n\n/**\n * HTTP methods an {@link EndpointRoute} can match. `'all'` matches every method.\n *\n * No `'head'`, deliberately — see {@link EndpointRoute.method}. A `HEAD` reaches the `'get'` handler.\n */\nexport type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'all';\n\n/** Any entry in the `routes` array: a {@link PageRoute} or an {@link EndpointRoute}. */\nexport type Route = PageRoute | EndpointRoute;\n\n/**\n * Narrows a {@link Route} to a {@link PageRoute} — `type` is optional on page routes, so anything not\n * explicitly `'endpoint'` is one.\n *\n * @internal\n */\nexport function isPageRoute(route: Route): route is PageRoute {\n return route.type !== 'endpoint';\n}\n\n/**\n * A page the framework falls back to rather than routes to — `notFound` and `error` in\n * {@link RouteConfig}. Same contract as a {@link PageRoute} `component`, without a path of its own.\n */\nexport interface FallbackPage {\n /** Dynamic import of the page module; its default export is the {@link PageComponent}. */\n component: () => Promise<{ default: PageComponent }>;\n}\n\n/**\n * The error detail handed to the `error` page. Redacted in production — a generic\n * `'Internal Server Error'` and no `stack`; in dev, the real message and stack.\n */\nexport interface ErrorPageInfo {\n /** The thrown error's message in dev; `'Internal Server Error'` in production. */\n message: string;\n /**\n * The stack trace — **dev only**, and `undefined` in every build. Optional for that reason rather than\n * because some errors lack one, so a page that renders it should guard on it, not on a mode flag.\n */\n stack?: string;\n}\n\n/**\n * Props for the `error` page declared in {@link RouteConfig.error} — the usual {@link PageProps} plus\n * the redaction-aware {@link ErrorPageInfo}.\n *\n * In a build `error.message` is the generic `'Internal Server Error'` and `error.stack` is `undefined`, so\n * the page below guards on the stack rather than on a mode flag — there is no mode flag to guard on.\n *\n * @typeParam E - The app's Hono {@link Env}, forwarded to {@link PageProps.ctx}.\n *\n * @example\n * ```tsx\n * import type { ErrorPageProps } from '@rshono/core';\n *\n * export default function ServerError({ error }: ErrorPageProps) {\n * return (\n * <html>\n * <body>\n * <h1>Something went wrong</h1>\n * <p>{error.message}</p>\n * {error.stack && <pre>{error.stack}</pre>}\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport type ErrorPageProps<E extends Env = Env> = PageProps<string, E> & {\n /** The error that failed the request, redacted in production — see {@link ErrorPageInfo}. */\n error: ErrorPageInfo;\n};\n\n/**\n * The object form accepted by {@link defineRoutes}: the route table plus the two\n * optional framework-owned pages.\n *\n * @typeParam TRoutes - Inferred tuple of route literals, which is what makes the\n * per-route `path` → props check possible.\n *\n * @see {@link https://www.rshono.com/docs/routing#notfound-and-error | Docs — notFound and error pages}\n */\nexport interface RouteConfig<TRoutes extends readonly Route[] = readonly Route[]> {\n /** Every page and endpoint in the app, matched in order. */\n routes: TRoutes;\n /** Page rendered with a 404 status for unmatched paths and for `notFound()` calls. */\n notFound?: FallbackPage;\n /** Page rendered with a 500 status when a request throws. Receives {@link ErrorPageProps}. */\n error?: FallbackPage;\n}\n\n/**\n * The same check for `staticPaths`, whose param sets have to fill the route's own path: a key that does not\n * is otherwise a build-time throw from `interpolatePath` rather than a type error.\n *\n * Keys only, not full assignability, because the declared field type is `Record<string, string>` and a\n * `staticPaths` annotated as returning exactly that has to stay accepted — an index signature carries no\n * key to check, so it passes. Skipped where the path has no params, because `staticPaths` is not called for\n * such a route at all and an error there would be about the wrong thing.\n */\ntype ValidateStaticPaths<R, P extends string> =\n ParamKeys<P> extends never\n ? R\n : R extends { staticPaths: () => infer Sets }\n ? Awaited<Sets> extends ReadonlyArray<infer Set>\n ? [keyof PathParams<P>] extends [keyof Set]\n ? R\n : R & { staticPaths: `every param set staticPaths returns needs the params of '${P}'` }\n : R\n : R;\n\n// `PageProps<P, any>`, not `PageProps<P>`: only the *path* is being checked, and pinning the Env would\n// fail every page that declares its own (`PageProps<'/x', MyEnv>`, to type `ctx.var`).\ntype ValidateRoute<R> = R extends {\n path: infer P extends string;\n component: () => Promise<{ default: PageComponent<infer CP> }>;\n}\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- the Env is deliberately unpinned; see above.\n [PageProps<P, any>] extends [CP]\n ? ValidateStaticPaths<R, P>\n : R & { component: `component props are not satisfied by PageProps<'${P}'>` }\n : R;\n\ntype ValidateRoutes<TRoutes extends readonly Route[]> = { [K in keyof TRoutes]: ValidateRoute<TRoutes[K]> };\n\n/**\n * Declares the app's route table. Export the result as `routes` from `src/routes.ts` — the one file\n * rshono requires. It only ever runs on the server, so importing server-only modules from it (inside\n * `staticPaths`, say) is safe.\n *\n * Beyond typing the config, every page is cross-checked against its own path: props not satisfied by\n * `PageProps<'<its path>'>` make the `component` field a type error. A bare {@link Route} array is\n * accepted as shorthand — see the second overload.\n *\n * @param config - A {@link RouteConfig}: the `routes` array plus the optional `notFound` and `error`\n * pages.\n * @returns The config, unchanged and fully typed.\n *\n * @example\n * ```ts\n * // src/routes.ts\n * import { defineRoutes } from '@rshono/core';\n *\n * export const routes = defineRoutes({\n * routes: [\n * { path: '/', component: () => import('./components/home') },\n * { path: '/profile/:id', component: () => import('./components/profile') },\n * {\n * path: '/docs/:slug',\n * render: 'static',\n * component: () => import('./components/documentation'),\n * staticPaths: async () => [{ slug: 'getting-started' }, { slug: 'deployment' }],\n * },\n * { type: 'endpoint', path: '/api/health', server: () => import('./health') },\n * ],\n * notFound: { component: () => import('./components/404') },\n * error: { component: () => import('./components/500') },\n * });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing | Docs — routing}\n */\nexport function defineRoutes<const TRoutes extends readonly Route[]>(\n config: RouteConfig<TRoutes> & { routes: ValidateRoutes<TRoutes> },\n): RouteConfig<TRoutes>;\n/**\n * Array shorthand for {@link defineRoutes} — equivalent to `defineRoutes({ routes })`, for an app\n * with no `notFound` or `error` page.\n *\n * @param routes - The {@link Route} array; each page is checked against its own `path`.\n * @returns A {@link RouteConfig} wrapping them.\n *\n * @example\n * ```ts\n * export const routes = defineRoutes([{ path: '/', component: () => import('./components/home') }]);\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing | Docs — routing}\n */\nexport function defineRoutes<const TRoutes extends readonly Route[]>(routes: TRoutes & ValidateRoutes<TRoutes>): RouteConfig<TRoutes>;\nexport function defineRoutes(input: readonly Route[] | RouteConfig): RouteConfig {\n return Array.isArray(input) ? { routes: input } : (input as RouteConfig);\n}\n"]}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAyPA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC;AACnC,CAAC;AAsLD,MAAM,UAAU,YAAY,CAAC,KAAqC;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAE,KAAqB,CAAC;AAC3E,CAAC","sourcesContent":["import type { Env, Handler } from 'hono';\nimport type { ParamKeys, ParamKeyToRecord } from 'hono/types';\nimport type { ReactNode } from 'react';\n// Type-only, so importing `@rshono/core` pulls in none of `context.ts`'s runtime machinery.\nimport type { RequestContext } from './runtime/context.js';\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\ntype UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;\n\n/**\n * The `params` record implied by a route path pattern — one required `string` key per `:param`\n * segment, `Record<string, never>` for a path with none. Paths use Hono's syntax, so `:id`,\n * `:id{[0-9]+}` and `*` all work.\n *\n * You rarely name this directly; {@link PageProps} applies it for you.\n *\n * @typeParam P - The literal route path, e.g. `'/users/:id/posts/:postId'`.\n *\n * @example\n * ```ts\n * type P = PathParams<'/users/:id/posts/:postId'>; // { id: string; postId: string }\n * ```\n *\n * @see {@link https://hono.dev/docs/api/routing#path-parameter | Hono — path parameters}\n */\nexport type PathParams<P extends string> =\n ParamKeys<P> extends never ? Record<string, never> : Simplify<UnionToIntersection<ParamKeyToRecord<ParamKeys<P>>>>;\n\n/**\n * Props every page component receives. Pass the route's path as the type argument to get `params`\n * typed key-by-key; without it `params` falls back to an open `Record<string, string>`.\n *\n * `defineRoutes` checks each page's props against `PageProps<path>`, so a mismatched path literal is\n * a type error at the route definition. `url` and `params` mirror what `useNavigation()` gives a\n * `'use client'` component, so a read moves across the server/client line unchanged.\n *\n * @typeParam Path - The literal path this page is mounted at, e.g. `'/profile/:id'`.\n * @typeParam E - The app's Hono {@link Env}, to type {@link RequestContext.var} and\n * {@link RequestContext.env} on {@link PageProps.ctx}.\n *\n * @example\n * ```tsx\n * import type { PageProps } from '@rshono/core';\n *\n * export default async function Profile({ params, url }: PageProps<'/profile/:id'>) {\n * const user = await db.getUser(params.id); // params.id is string\n * const tab = url.searchParams.get('tab') ?? 'overview';\n * return <Layout>{user.name} — {tab}</Layout>;\n * }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/pages#page-props | Docs — page props}\n */\nexport interface PageProps<Path extends string = string, E extends Env = Env> {\n /**\n * The absolute browser-facing request {@link URL}, proxy-header aware (`X-Forwarded-Host` /\n * `-Proto`). A fresh instance per request, so mutating it is local to the page; it is not\n * serializable, so hand a `'use client'` component `url.href` rather than `url`.\n *\n * On a `render: 'static'` route this is the build-time URL — rendered once against `siteUrl`, so\n * `url.searchParams` is always empty. Read the query from `useNavigation().url` in a `'use client'`\n * component instead, or mark the route `render: 'dynamic'`.\n */\n url: URL;\n /** Matched route params for this request, e.g. `{ id: '42' }` for `/profile/:id`. */\n params: string extends Path ? Record<string, string> : PathParams<Path>;\n /**\n * The request context — the object `getRequestContext()` returns, handed to the page so cookies,\n * headers, env and middleware variables are reachable without an import.\n *\n * Server-only, non-enumerable (a `{...props}` spread and `JSON.stringify` both skip it) and never\n * serialized. Passing it to a `'use client'` component fails the render, because it wraps the live\n * request — read what you need here and pass plain values down.\n *\n * Non-enumerable is the one place this API breaks a JavaScript expectation, and it is unavoidable: an\n * enumerable `ctx` would put `ctx.hono.env` — every binding and secret — into React's dev-only\n * serialization of a server component's props, which walks own enumerable properties. So `<Child\n * {...props} />` hands a **server** child `ctx: undefined` with no error, while the type says otherwise.\n * Nested server components are meant to call `getRequestContext()` for the same object rather than\n * receive it, which is also the fix if a spread has already cost you an afternoon.\n *\n * Reading it on a `render: 'static'` route throws: a prerendered page has no per-request context.\n * Mark the route `render: 'dynamic'`, or use the `url` / `params` props.\n *\n * @example\n * ```tsx\n * export default function Dashboard({ ctx }: PageProps) {\n * const session = ctx.cookies.get('session');\n * if (!session) redirect('/login');\n * return <Layout>Signed in as {session}</Layout>;\n * }\n * ```\n */\n ctx: RequestContext<E>;\n}\n\n/**\n * A page: a React **server component** rendering the entire document (`<html>…</html>`), usually via\n * a shared layout. It may be `async` and await data directly.\n *\n * Each page module default-exports exactly one. Interactive parts belong in `'use client'` components\n * the page imports — only those ship JS.\n *\n * @typeParam P - The component's props; for a page these are {@link PageProps}.\n *\n * @see {@link https://react.dev/reference/rsc/server-components | React — Server Components}\n * @see {@link https://www.rshono.com/docs/pages | Docs — pages}\n */\n// `any`, not `unknown`: this default is what an unparameterised `PageComponent` means in a user's own\n// annotation, and `unknown` props would reject every component that declares the props it actually takes.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PageComponent<P = any> = (props: P) => ReactNode | Promise<ReactNode>;\n\n/**\n * The shape an `{ type: 'endpoint' }` route's server module must have: a single named `handler`\n * export. It only ever loads on the server, so importing a database client or reading secrets from it\n * is safe.\n *\n * @example\n * ```ts\n * // src/health.ts\n * import type { Handler } from 'hono';\n *\n * export const handler: Handler = (c) => c.json({ ok: true });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#endpoint-routes | Docs — endpoint routes}\n */\nexport interface EndpointServerModule {\n /**\n * A Hono {@link Handler} for every request the route matches. It is passed Hono's `Context`, so the\n * request, the response builders (`c.json`, `c.text`, `c.body`) and middleware variables are all\n * reached through it.\n *\n * @see {@link https://hono.dev/docs/api/context | Hono — Context}\n */\n handler: Handler;\n}\n\n/**\n * A page route — a path rendered by a server component. This is the default\n * route kind, so `type` can be omitted.\n *\n * @example\n * ```ts\n * { path: '/profile/:id', component: () => import('./components/profile') }\n * ```\n */\nexport interface PageRoute {\n /** Discriminates a page from an endpoint; optional because `'page'` is the default. */\n type?: 'page';\n /**\n * Hono-style path pattern, e.g. `/`, `/profile/:id`, `/files/*`. Routes are matched in\n * declaration order.\n *\n * @see {@link https://hono.dev/docs/api/routing | Hono — routing}\n */\n path: string;\n /**\n * Dynamic import of the page module, whose default export is the {@link PageComponent}.\n *\n * Write it inline as `() => import('…')`: the framework detects that exact form and injects the\n * `'use server-entry'` directive that attaches the page's client JS and CSS. Wire the component up\n * any other way — a variable, a barrel re-export, a computed specifier — and you have to put\n * `'use server-entry'` on the page module's first line yourself.\n *\n * @example\n * ```ts\n * component: () => import('./components/profile')\n * ```\n *\n * @see {@link https://www.rshono.com/docs/pages#the-use-server-entry-directive | Docs — the `'use server-entry'` directive}\n */\n component: () => Promise<{ default: PageComponent }>;\n /** `'static'` prerenders the route at build time; `'dynamic'` (the default) renders per request. */\n render?: 'static' | 'dynamic';\n /**\n * For a `render: 'static'` route with params: the param sets to prerender, one page each. Runs at\n * build time on the server, so it may hit a database or read the filesystem.\n *\n * A parameterised static route without this falls back to rendering per request, with a build\n * warning. Wildcard (`*`), optional and regex params cannot be prerendered.\n *\n * @example\n * ```ts\n * {\n * path: '/docs/:slug',\n * render: 'static',\n * component: () => import('./components/documentation'),\n * staticPaths: async () => (await db.docs.all()).map((d) => ({ slug: d.slug })),\n * }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#static-rendering | Docs — static rendering}\n */\n staticPaths?: () => Array<Record<string, string>> | Promise<Array<Record<string, string>>>;\n}\n\n/**\n * An endpoint route — a path served by a raw Hono handler instead of a React\n * component. Use it for JSON APIs, webhooks, redirects, feeds, or anything that\n * isn't an HTML page.\n *\n * @example\n * ```ts\n * { type: 'endpoint', path: '/api/health', server: () => import('./health') }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing#endpoint-routes | Docs — endpoint routes}\n */\nexport interface EndpointRoute {\n /** Marks this route as an endpoint rather than a page. Required. */\n type: 'endpoint';\n /**\n * Hono-style path pattern, e.g. `/api/health`, `/api/users/:id`.\n *\n * @see {@link https://hono.dev/docs/api/routing | Hono — routing}\n */\n path: string;\n /**\n * HTTP method to match, or a list of them. Defaults to `'all'` — every method.\n *\n * There is no `'head'`: Hono dispatches a `HEAD` as a `GET` and strips the body off the response, so a\n * `HEAD` is already answered by the `'get'` handler (and by `'all'`), and a route registered for `HEAD`\n * alone would never be reached.\n *\n * A list is how a two-method endpoint says so; `'all'` inside one is refused, since it is either the\n * whole thing or a mistake. A method the route does not name gets Hono's 404 rather than the handler.\n *\n * @example\n * ```ts\n * { type: 'endpoint', path: '/api/session', method: ['get', 'delete'], server: () => import('./session') }\n * ```\n */\n method?: HTTPMethod | readonly HTTPMethod[];\n /** Dynamic import of the {@link EndpointServerModule} exporting `handler`. */\n server: () => Promise<EndpointServerModule>;\n}\n\n/**\n * HTTP methods an {@link EndpointRoute} can match. `'all'` matches every method.\n *\n * No `'head'`, deliberately — see {@link EndpointRoute.method}. A `HEAD` reaches the `'get'` handler.\n */\nexport type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'all';\n\n/** Any entry in the `routes` array: a {@link PageRoute} or an {@link EndpointRoute}. */\nexport type Route = PageRoute | EndpointRoute;\n\n/**\n * Narrows a {@link Route} to a {@link PageRoute} — `type` is optional on page routes, so anything not\n * explicitly `'endpoint'` is one.\n *\n * @internal\n */\nexport function isPageRoute(route: Route): route is PageRoute {\n return route.type !== 'endpoint';\n}\n\n/**\n * A page the framework falls back to rather than routes to — `notFound` and `error` in\n * {@link RouteConfig}. Same contract as a {@link PageRoute} `component`, without a path of its own.\n */\nexport interface FallbackPage {\n /** Dynamic import of the page module; its default export is the {@link PageComponent}. */\n component: () => Promise<{ default: PageComponent }>;\n}\n\n/**\n * The error detail handed to the `error` page. Redacted in production — a generic\n * `'Internal Server Error'` and no `stack`; in dev, the real message and stack.\n */\nexport interface ErrorPageInfo {\n /** The thrown error's message in dev; `'Internal Server Error'` in production. */\n message: string;\n /**\n * The stack trace — **dev only**, and `undefined` in every build. Optional for that reason rather than\n * because some errors lack one, so a page that renders it should guard on it, not on a mode flag.\n */\n stack?: string;\n}\n\n/**\n * Props for the `error` page declared in {@link RouteConfig.error} — the usual {@link PageProps} plus\n * the redaction-aware {@link ErrorPageInfo}.\n *\n * In a build `error.message` is the generic `'Internal Server Error'` and `error.stack` is `undefined`, so\n * the page below guards on the stack rather than on a mode flag — there is no mode flag to guard on.\n *\n * @typeParam E - The app's Hono {@link Env}, forwarded to {@link PageProps.ctx}.\n *\n * @example\n * ```tsx\n * import type { ErrorPageProps } from '@rshono/core';\n *\n * export default function ServerError({ error }: ErrorPageProps) {\n * return (\n * <html>\n * <body>\n * <h1>Something went wrong</h1>\n * <p>{error.message}</p>\n * {error.stack && <pre>{error.stack}</pre>}\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport type ErrorPageProps<E extends Env = Env> = PageProps<string, E> & {\n /** The error that failed the request, redacted in production — see {@link ErrorPageInfo}. */\n error: ErrorPageInfo;\n};\n\n/**\n * The object form accepted by {@link defineRoutes}: the route table plus the two\n * optional framework-owned pages.\n *\n * @typeParam TRoutes - Inferred tuple of route literals, which is what makes the\n * per-route `path` → props check possible.\n *\n * @see {@link https://www.rshono.com/docs/routing#notfound-and-error | Docs — notFound and error pages}\n */\nexport interface RouteConfig<TRoutes extends readonly Route[] = readonly Route[]> {\n /** Every page and endpoint in the app, matched in order. */\n routes: TRoutes;\n /** Page rendered with a 404 status for unmatched paths and for `notFound()` calls. */\n notFound?: FallbackPage;\n /** Page rendered with a 500 status when a request throws. Receives {@link ErrorPageProps}. */\n error?: FallbackPage;\n}\n\n/**\n * The same check for `staticPaths`, whose param sets have to fill the route's own path: a key that does not\n * is otherwise a build-time throw from `interpolatePath` rather than a type error.\n *\n * Keys only, not full assignability, because the declared field type is `Record<string, string>` and a\n * `staticPaths` annotated as returning exactly that has to stay accepted — an index signature carries no\n * key to check, so it passes. Skipped where the path has no params, because `staticPaths` is not called for\n * such a route at all and an error there would be about the wrong thing.\n */\ntype ValidateStaticPaths<R, P extends string> =\n ParamKeys<P> extends never\n ? R\n : R extends { staticPaths: () => infer Sets }\n ? Awaited<Sets> extends ReadonlyArray<infer Set>\n ? [keyof PathParams<P>] extends [keyof Set]\n ? R\n : R & { staticPaths: `every param set staticPaths returns needs the params of '${P}'` }\n : R\n : R;\n\n// `PageProps<P, any>`, not `PageProps<P>`: only the *path* is being checked, and pinning the Env would\n// fail every page that declares its own (`PageProps<'/x', MyEnv>`, to type `ctx.var`).\ntype ValidateRoute<R> = R extends {\n path: infer P extends string;\n component: () => Promise<{ default: PageComponent<infer CP> }>;\n}\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- the Env is deliberately unpinned; see above.\n [PageProps<P, any>] extends [CP]\n ? ValidateStaticPaths<R, P>\n : R & { component: `component props are not satisfied by PageProps<'${P}'>` }\n : R;\n\ntype ValidateRoutes<TRoutes extends readonly Route[]> = { [K in keyof TRoutes]: ValidateRoute<TRoutes[K]> };\n\n/**\n * Refuses a key that is not a {@link RouteConfig} field — a typo'd `notfound` is otherwise a fallback page\n * that never renders, and nothing at runtime looks for one.\n *\n * Excess-property checking did this while the object form was an overload of its own with a concrete\n * parameter type. It cannot once the parameter is generic and inferred from the argument, because the\n * inferred type *has* the extra key. The message here is the better one anyway: it lands on the field.\n */\ntype ValidateConfigKeys<T> =\n Exclude<keyof T, keyof RouteConfig> extends never\n ? T\n : T & Record<Exclude<keyof T, keyof RouteConfig>, 'not a defineRoutes field — the fields are routes, notFound and error'>;\n\n/** The check for whichever of the two accepted shapes was passed. */\ntype ValidateInput<T> = T extends readonly Route[]\n ? ValidateRoutes<T>\n : T extends { routes: infer R extends readonly Route[] }\n ? ValidateConfigKeys<T> & { routes: ValidateRoutes<R> }\n : T;\n\n/** The route tuple inside either shape, so the return type carries the literals through both. */\ntype RoutesOf<T> = T extends readonly Route[] ? T : T extends { routes: infer R extends readonly Route[] } ? R : readonly Route[];\n\n/**\n * Declares the app's route table. Export the result as `routes` from `src/routes.ts` — the one file\n * rshono requires. It only ever runs on the server, so importing server-only modules from it (inside\n * `staticPaths`, say) is safe.\n *\n * Takes a {@link RouteConfig} — the `routes` array plus the optional `notFound` and `error` pages — or a\n * bare {@link Route} array as shorthand for an app with neither.\n *\n * Beyond typing the config, every page is cross-checked against its own path: props not satisfied by\n * `PageProps<'<its path>'>` make the `component` field a type error, and a `staticPaths` whose param sets do\n * not fill the path makes that field one.\n *\n * **One signature over both shapes, deliberately.** The array form used to be a second overload, which meant\n * a mistake inside a bare array was reported as an overload-resolution failure whose *first* line was the\n * object form's complaint — \"Property 'routes' is missing\" — pointing at a change the author should not make.\n * The real message was on line 8. A single signature reports the argument once, at the field that is wrong.\n *\n * @param input - A {@link RouteConfig}, or the bare `routes` array.\n * @returns The config, unchanged and fully typed; an array is wrapped as `{ routes }`.\n *\n * @example\n * ```ts\n * // src/routes.ts\n * import { defineRoutes } from '@rshono/core';\n *\n * export const routes = defineRoutes({\n * routes: [\n * { path: '/', component: () => import('./components/home') },\n * { path: '/profile/:id', component: () => import('./components/profile') },\n * {\n * path: '/docs/:slug',\n * render: 'static',\n * component: () => import('./components/documentation'),\n * staticPaths: async () => [{ slug: 'getting-started' }, { slug: 'deployment' }],\n * },\n * { type: 'endpoint', path: '/api/health', server: () => import('./health') },\n * ],\n * notFound: { component: () => import('./components/404') },\n * error: { component: () => import('./components/500') },\n * });\n * ```\n *\n * @example\n * ```ts\n * // The shorthand, for an app with no notFound or error page.\n * export const routes = defineRoutes([{ path: '/', component: () => import('./components/home') }]);\n * ```\n *\n * @see {@link https://www.rshono.com/docs/routing | Docs — routing}\n */\nexport function defineRoutes<const T extends readonly Route[] | RouteConfig<readonly Route[]>>(input: T & ValidateInput<T>): RouteConfig<RoutesOf<T>>;\nexport function defineRoutes(input: readonly Route[] | RouteConfig): RouteConfig {\n return Array.isArray(input) ? { routes: input } : (input as RouteConfig);\n}\n"]}
@@ -16,12 +16,19 @@ export interface CatchBoundaryProps {
16
16
  * here.
17
17
  */
18
18
  fallback?: ErrorFallback;
19
- /** Called with the caught error, for logging or reporting. */
19
+ /**
20
+ * Called with the caught error, for logging or reporting.
21
+ *
22
+ * A function prop, so — like {@link ErrorFallback}'s function form — it can only be passed from a
23
+ * `'use client'` component. React refuses one from a server component by name: "Event handlers cannot be
24
+ * passed to Client Component props".
25
+ */
20
26
  onError?: (error: Error) => void;
21
27
  /**
22
28
  * Clears the error automatically when any value in this array changes while the fallback is showing.
23
- * Pass the current pathname to recover when the user navigates away:
24
- * `resetKeys={[useNavigation().url.pathname]}`.
29
+ * Pass the current pathname to recover when the user navigates away — `resetKeys={[url.pathname]}` from a
30
+ * page's `url` prop, which is the form that works from the server component rendering this boundary, or
31
+ * `resetKeys={[useNavigation().url.pathname]}` inside a `'use client'` component.
25
32
  */
26
33
  resetKeys?: readonly unknown[];
27
34
  /** The subtree this boundary protects. */
@@ -73,7 +80,7 @@ export interface AsyncBoundaryProps {
73
80
  loading: ReactNode;
74
81
  /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */
75
82
  error?: ErrorFallback;
76
- /** Called with the caught error. */
83
+ /** Called with the caught error. From a `'use client'` component only — see {@link CatchBoundaryProps.onError}. */
77
84
  onError?: (error: Error) => void;
78
85
  /** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */
79
86
  resetKeys?: readonly unknown[];
@@ -1 +1 @@
1
- {"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAU5D;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;AAEzF,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,CAEhE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAGpC;IAED,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAKjD;IAED,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS,CASlB;CACF;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,mGAAmG;IACnG,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAM7G"}
1
+ {"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAU5D;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;AAEzF,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,CAEhE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAGpC;IAED,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAKjD;IAED,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS,CASlB;CACF;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,mHAAmH;IACnH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,mGAAmG;IACnG,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAM7G"}
@@ -1 +1 @@
1
- {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,wGAAwG;AACxG,kGAAkG;AAClG,mDAAmD;AACnD,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,eAAe,CAAE,KAAqC,EAAE,MAAM,CAAC,CAAC;AACzE,CAAC;AAoCD,SAAS,WAAW,CAAC,CAAqB,EAAE,CAAqB;IAC/D,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAClF,KAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY;QAC5B,IAAI,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,kBAAkB,CAAC,IAAwB;QACzC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,GAAG,GAAS,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACvC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,QAAQ,KAAK,SAAS;gBAAE,MAAM,KAAK,CAAC,CAAC,iCAAiC;YAC1E,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAsB;IAChG,OAAO,CACL,KAAC,aAAa,IAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,YACpE,KAAC,QAAQ,IAAC,QAAQ,EAAE,OAAO,YAAG,QAAQ,GAAY,GACpC,CACjB,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { Component, Suspense, type ReactNode } from 'react';\nimport { isControlDigest } from './control.js';\n\n// `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest. They are\n// navigation, not failure, so no boundary absorbs one — they are re-thrown to the root, where the\n// runtime turns the digest into a real navigation.\nfunction isControlError(error: unknown): boolean {\n return isControlDigest((error as { digest?: unknown } | null)?.digest);\n}\n\n/**\n * What a {@link CatchBoundary} / {@link AsyncBoundary} renders once a child throws: either a static\n * node, or a render function given the error and a `reset` callback that clears it and re-renders the\n * children (a \"Try again\" button, say).\n *\n * The function form only works from a `'use client'` component — functions can't cross the\n * server→client boundary. From a server component, pass a `ReactNode`.\n */\nexport type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\n/** Props for {@link CatchBoundary}. */\nexport interface CatchBoundaryProps {\n /**\n * Rendered in place of the children after one of them throws. Omit it to report the error via\n * `onError` and re-throw to the next boundary out — or the app's `error` page — instead of handling it\n * here.\n */\n fallback?: ErrorFallback;\n /** Called with the caught error, for logging or reporting. */\n onError?: (error: Error) => void;\n /**\n * Clears the error automatically when any value in this array changes while the fallback is showing.\n * Pass the current pathname to recover when the user navigates away:\n * `resetKeys={[useNavigation().url.pathname]}`.\n */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary protects. */\n children: ReactNode;\n}\n\ninterface CatchBoundaryState {\n error: Error | null;\n}\n\nfunction keysChanged(a: readonly unknown[], b: readonly unknown[]): boolean {\n return a.length !== b.length || a.some((value, i) => !Object.is(value, b[i]));\n}\n\n/**\n * A general-purpose error boundary: catches what its children throw — a client island that blew up, a\n * server component that rejected on a soft navigation — and renders `fallback` in their place rather\n * than tearing down the page.\n *\n * It is a `'use client'` component (React error boundaries must be), so a server component can render\n * it too. Reach for {@link AsyncBoundary} when you also want a Suspense loading fallback.\n *\n * @example\n * ```tsx\n * import { CatchBoundary } from '@rshono/core/client';\n *\n * <CatchBoundary fallback={(error, reset) => (\n * <div role=\"alert\">\n * <p>{error.message}</p>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}>\n * <RiskyWidget />\n * </CatchBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary | React — error boundaries}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport class CatchBoundary extends Component<CatchBoundaryProps, CatchBoundaryState> {\n state: CatchBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): CatchBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error): void {\n if (isControlError(error)) return;\n this.props.onError?.(error);\n }\n\n componentDidUpdate(prev: CatchBoundaryProps): void {\n const { resetKeys } = this.props;\n if (this.state.error && prev.resetKeys && resetKeys && keysChanged(prev.resetKeys, resetKeys)) {\n this.reset();\n }\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n if (error !== null) {\n if (isControlError(error)) throw error;\n const { fallback } = this.props;\n if (fallback === undefined) throw error; // propagate to an outer boundary\n return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;\n }\n return this.props.children;\n }\n}\n\n/** Props for {@link AsyncBoundary}. */\nexport interface AsyncBoundaryProps {\n /**\n * Suspense fallback, shown while the children or their data are still loading. Required — a loading\n * state is the reason to reach for this over {@link CatchBoundary}, so showing nothing is an explicit\n * `loading={null}`.\n */\n loading: ReactNode;\n /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */\n error?: ErrorFallback;\n /** Called with the caught error. */\n onError?: (error: Error) => void;\n /** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary suspends on and protects. */\n children: ReactNode;\n}\n\n/**\n * A loading and error boundary in one wrapper — the common case for an async section of a page. It\n * always renders the same shape:\n *\n * ```tsx\n * <CatchBoundary fallback={error}>\n * <Suspense fallback={loading}>{children}</Suspense>\n * </CatchBoundary>\n * ```\n *\n * so `loading` shows until the children resolve and `error` catches whatever they throw, suspended or\n * not. `error` is optional: omit it and errors propagate to the next boundary out.\n *\n * @example\n * ```tsx\n * import { AsyncBoundary } from '@rshono/core/client';\n *\n * <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>\n * <SlowServerComponent />\n * </AsyncBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Suspense | React — `<Suspense>`}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport function AsyncBoundary({ loading, error, onError, resetKeys, children }: AsyncBoundaryProps): ReactNode {\n return (\n <CatchBoundary fallback={error} onError={onError} resetKeys={resetKeys}>\n <Suspense fallback={loading}>{children}</Suspense>\n </CatchBoundary>\n );\n}\n"]}
1
+ {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,wGAAwG;AACxG,kGAAkG;AAClG,mDAAmD;AACnD,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,eAAe,CAAE,KAAqC,EAAE,MAAM,CAAC,CAAC;AACzE,CAAC;AA2CD,SAAS,WAAW,CAAC,CAAqB,EAAE,CAAqB;IAC/D,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAClF,KAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY;QAC5B,IAAI,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,kBAAkB,CAAC,IAAwB;QACzC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,GAAG,GAAS,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACvC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,QAAQ,KAAK,SAAS;gBAAE,MAAM,KAAK,CAAC,CAAC,iCAAiC;YAC1E,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAsB;IAChG,OAAO,CACL,KAAC,aAAa,IAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,YACpE,KAAC,QAAQ,IAAC,QAAQ,EAAE,OAAO,YAAG,QAAQ,GAAY,GACpC,CACjB,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { Component, Suspense, type ReactNode } from 'react';\nimport { isControlDigest } from './control.js';\n\n// `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest. They are\n// navigation, not failure, so no boundary absorbs one — they are re-thrown to the root, where the\n// runtime turns the digest into a real navigation.\nfunction isControlError(error: unknown): boolean {\n return isControlDigest((error as { digest?: unknown } | null)?.digest);\n}\n\n/**\n * What a {@link CatchBoundary} / {@link AsyncBoundary} renders once a child throws: either a static\n * node, or a render function given the error and a `reset` callback that clears it and re-renders the\n * children (a \"Try again\" button, say).\n *\n * The function form only works from a `'use client'` component — functions can't cross the\n * server→client boundary. From a server component, pass a `ReactNode`.\n */\nexport type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\n/** Props for {@link CatchBoundary}. */\nexport interface CatchBoundaryProps {\n /**\n * Rendered in place of the children after one of them throws. Omit it to report the error via\n * `onError` and re-throw to the next boundary out — or the app's `error` page — instead of handling it\n * here.\n */\n fallback?: ErrorFallback;\n /**\n * Called with the caught error, for logging or reporting.\n *\n * A function prop, so — like {@link ErrorFallback}'s function form — it can only be passed from a\n * `'use client'` component. React refuses one from a server component by name: \"Event handlers cannot be\n * passed to Client Component props\".\n */\n onError?: (error: Error) => void;\n /**\n * Clears the error automatically when any value in this array changes while the fallback is showing.\n * Pass the current pathname to recover when the user navigates away — `resetKeys={[url.pathname]}` from a\n * page's `url` prop, which is the form that works from the server component rendering this boundary, or\n * `resetKeys={[useNavigation().url.pathname]}` inside a `'use client'` component.\n */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary protects. */\n children: ReactNode;\n}\n\ninterface CatchBoundaryState {\n error: Error | null;\n}\n\nfunction keysChanged(a: readonly unknown[], b: readonly unknown[]): boolean {\n return a.length !== b.length || a.some((value, i) => !Object.is(value, b[i]));\n}\n\n/**\n * A general-purpose error boundary: catches what its children throw — a client island that blew up, a\n * server component that rejected on a soft navigation — and renders `fallback` in their place rather\n * than tearing down the page.\n *\n * It is a `'use client'` component (React error boundaries must be), so a server component can render\n * it too. Reach for {@link AsyncBoundary} when you also want a Suspense loading fallback.\n *\n * @example\n * ```tsx\n * import { CatchBoundary } from '@rshono/core/client';\n *\n * <CatchBoundary fallback={(error, reset) => (\n * <div role=\"alert\">\n * <p>{error.message}</p>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}>\n * <RiskyWidget />\n * </CatchBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary | React — error boundaries}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport class CatchBoundary extends Component<CatchBoundaryProps, CatchBoundaryState> {\n state: CatchBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): CatchBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error): void {\n if (isControlError(error)) return;\n this.props.onError?.(error);\n }\n\n componentDidUpdate(prev: CatchBoundaryProps): void {\n const { resetKeys } = this.props;\n if (this.state.error && prev.resetKeys && resetKeys && keysChanged(prev.resetKeys, resetKeys)) {\n this.reset();\n }\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n if (error !== null) {\n if (isControlError(error)) throw error;\n const { fallback } = this.props;\n if (fallback === undefined) throw error; // propagate to an outer boundary\n return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;\n }\n return this.props.children;\n }\n}\n\n/** Props for {@link AsyncBoundary}. */\nexport interface AsyncBoundaryProps {\n /**\n * Suspense fallback, shown while the children or their data are still loading. Required — a loading\n * state is the reason to reach for this over {@link CatchBoundary}, so showing nothing is an explicit\n * `loading={null}`.\n */\n loading: ReactNode;\n /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */\n error?: ErrorFallback;\n /** Called with the caught error. From a `'use client'` component only — see {@link CatchBoundaryProps.onError}. */\n onError?: (error: Error) => void;\n /** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary suspends on and protects. */\n children: ReactNode;\n}\n\n/**\n * A loading and error boundary in one wrapper — the common case for an async section of a page. It\n * always renders the same shape:\n *\n * ```tsx\n * <CatchBoundary fallback={error}>\n * <Suspense fallback={loading}>{children}</Suspense>\n * </CatchBoundary>\n * ```\n *\n * so `loading` shows until the children resolve and `error` catches whatever they throw, suspended or\n * not. `error` is optional: omit it and errors propagate to the next boundary out.\n *\n * @example\n * ```tsx\n * import { AsyncBoundary } from '@rshono/core/client';\n *\n * <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>\n * <SlowServerComponent />\n * </AsyncBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Suspense | React — `<Suspense>`}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport function AsyncBoundary({ loading, error, onError, resetKeys, children }: AsyncBoundaryProps): ReactNode {\n return (\n <CatchBoundary fallback={error} onError={onError} resetKeys={resetKeys}>\n <Suspense fallback={loading}>{children}</Suspense>\n </CatchBoundary>\n );\n}\n"]}
@@ -186,8 +186,15 @@ export declare class RequestContext<E extends Env = Env> {
186
186
  */
187
187
  get var(): Readonly<E['Variables']>;
188
188
  /**
189
- * Environment for the request: process env vars merged with runtime bindings, which win on conflict.
190
- * Computed once and cached.
189
+ * Environment for the request: process env vars, merged on a bindings platform with the bindings, which
190
+ * win on conflict. Computed once and cached.
191
+ *
192
+ * Bindings are merged **only where the platform supplies them** — `deploy: 'cloudflare'`, today. Hono's
193
+ * `c.env` is whatever the host passed as the second argument to `app.fetch`, and off Workers that is the
194
+ * adapter's own private state: `{ incoming, outgoing }` on Node and Vercel, the entire invocation —
195
+ * headers, cookies, `authorization` — on Lambda. Merging it would make `ctx.env` uncloneable on one and
196
+ * a disclosure vector on the other, both behind names this type declares `string | undefined`. Reach for
197
+ * {@link RequestContext.hono}`.env` if you really do want the adapter's argument.
191
198
  *
192
199
  * @example `const key = getRequestContext().env.STRIPE_SECRET_KEY;`
193
200
  *
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"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"}
@@ -81,6 +81,8 @@ function firstForwardedValue(header) {
81
81
  // Read through `typeof`: DefinePlugin inlines this into the server bundle, but the module is also the
82
82
  // public `@rshono/core/server` entry, which tooling can load without one. Absent means don't trust.
83
83
  const trustProxy = typeof __RSHONO_CONFIG__ !== 'undefined' && __RSHONO_CONFIG__.trustProxy;
84
+ // Read the same way, for the same reason. Absent means the platform passes no bindings — see the getter.
85
+ const envBindings = typeof __RSHONO_CONFIG__ !== 'undefined' && __RSHONO_CONFIG__.envBindings;
84
86
  /**
85
87
  * The browser-facing {@link URL} for a request, resolved from Hono's {@link Context} — a fresh
86
88
  * instance per call.
@@ -256,8 +258,15 @@ export class RequestContext {
256
258
  return this.#raw.var;
257
259
  }
258
260
  /**
259
- * Environment for the request: process env vars merged with runtime bindings, which win on conflict.
260
- * Computed once and cached.
261
+ * Environment for the request: process env vars, merged on a bindings platform with the bindings, which
262
+ * win on conflict. Computed once and cached.
263
+ *
264
+ * Bindings are merged **only where the platform supplies them** — `deploy: 'cloudflare'`, today. Hono's
265
+ * `c.env` is whatever the host passed as the second argument to `app.fetch`, and off Workers that is the
266
+ * adapter's own private state: `{ incoming, outgoing }` on Node and Vercel, the entire invocation —
267
+ * headers, cookies, `authorization` — on Lambda. Merging it would make `ctx.env` uncloneable on one and
268
+ * a disclosure vector on the other, both behind names this type declares `string | undefined`. Reach for
269
+ * {@link RequestContext.hono}`.env` if you really do want the adapter's argument.
261
270
  *
262
271
  * @example `const key = getRequestContext().env.STRIPE_SECRET_KEY;`
263
272
  *
@@ -267,7 +276,7 @@ export class RequestContext {
267
276
  get env() {
268
277
  if (this.#env)
269
278
  return this.#env;
270
- const bindings = this.#raw.env;
279
+ const bindings = envBindings ? this.#raw.env : undefined;
271
280
  return (this.#env = (bindings ? { ...processEnv(), ...bindings } : processEnv()));
272
281
  }
273
282
  /**