@rshono/core 1.0.0-rc.2 → 1.0.0-rc.21

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 (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -158
  3. package/bin/rshono.mjs +3 -4
  4. package/dist/builder/env-shadow-loader.cjs +123 -8
  5. package/dist/builder/page-entry-loader.cjs +28 -1
  6. package/dist/builder/page-files.d.ts +0 -1
  7. package/dist/builder/page-files.js +7 -3
  8. package/dist/builder/page-files.js.map +1 -1
  9. package/dist/builder/public-env.d.ts +7 -1
  10. package/dist/builder/public-env.js +7 -0
  11. package/dist/builder/public-env.js.map +1 -1
  12. package/dist/builder/react-versions.d.ts +12 -0
  13. package/dist/builder/react-versions.js +74 -0
  14. package/dist/builder/react-versions.js.map +1 -0
  15. package/dist/builder/rspack-config.d.ts +13 -4
  16. package/dist/builder/rspack-config.js +136 -53
  17. package/dist/builder/rspack-config.js.map +1 -1
  18. package/dist/builder/server-only-imports.d.ts +32 -0
  19. package/dist/builder/server-only-imports.js +51 -0
  20. package/dist/builder/server-only-imports.js.map +1 -0
  21. package/dist/cli/build.d.ts +2 -3
  22. package/dist/cli/build.js +51 -13
  23. package/dist/cli/build.js.map +1 -1
  24. package/dist/cli/dev.d.ts +2 -3
  25. package/dist/cli/dev.js +149 -45
  26. package/dist/cli/dev.js.map +1 -1
  27. package/dist/cli/exit.d.ts +12 -0
  28. package/dist/cli/exit.js +16 -0
  29. package/dist/cli/exit.js.map +1 -0
  30. package/dist/cli/index.d.ts +0 -1
  31. package/dist/cli/index.js +91 -30
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/cli/start.d.ts +0 -1
  34. package/dist/cli/start.js +15 -18
  35. package/dist/cli/start.js.map +1 -1
  36. package/dist/config.d.ts +72 -78
  37. package/dist/config.js +17 -1
  38. package/dist/config.js.map +1 -1
  39. package/dist/deploy/aws-lambda/runtime.d.ts +4 -7
  40. package/dist/deploy/aws-lambda/runtime.js +5 -8
  41. package/dist/deploy/aws-lambda/runtime.js.map +1 -1
  42. package/dist/deploy/build-marker.d.ts +9 -6
  43. package/dist/deploy/build-marker.js +8 -4
  44. package/dist/deploy/build-marker.js.map +1 -1
  45. package/dist/deploy/cloudflare/build.d.ts +0 -1
  46. package/dist/deploy/cloudflare/build.js +17 -13
  47. package/dist/deploy/cloudflare/build.js.map +1 -1
  48. package/dist/deploy/cloudflare/runtime.d.ts +2 -6
  49. package/dist/deploy/cloudflare/runtime.js +73 -68
  50. package/dist/deploy/cloudflare/runtime.js.map +1 -1
  51. package/dist/deploy/contract.d.ts +57 -41
  52. package/dist/deploy/contract.js.map +1 -1
  53. package/dist/deploy/filesystem.d.ts +3 -6
  54. package/dist/deploy/filesystem.js +23 -20
  55. package/dist/deploy/filesystem.js.map +1 -1
  56. package/dist/deploy/node/runtime.d.ts +5 -3
  57. package/dist/deploy/node/runtime.js +23 -9
  58. package/dist/deploy/node/runtime.js.map +1 -1
  59. package/dist/deploy/presets.d.ts +34 -30
  60. package/dist/deploy/presets.js +56 -44
  61. package/dist/deploy/presets.js.map +1 -1
  62. package/dist/deploy/public-paths.d.ts +42 -0
  63. package/dist/deploy/public-paths.js +66 -0
  64. package/dist/deploy/public-paths.js.map +1 -0
  65. package/dist/deploy/vercel/build.d.ts +0 -1
  66. package/dist/deploy/vercel/build.js +25 -16
  67. package/dist/deploy/vercel/build.js.map +1 -1
  68. package/dist/deploy/vercel/runtime.d.ts +10 -7
  69. package/dist/deploy/vercel/runtime.js +48 -11
  70. package/dist/deploy/vercel/runtime.js.map +1 -1
  71. package/dist/index.d.ts +19 -17
  72. package/dist/index.js +20 -10
  73. package/dist/index.js.map +1 -1
  74. package/dist/router.d.ts +212 -120
  75. package/dist/router.js +3 -8
  76. package/dist/router.js.map +1 -1
  77. package/dist/runtime/boundaries.d.ts +62 -48
  78. package/dist/runtime/boundaries.js +32 -34
  79. package/dist/runtime/boundaries.js.map +1 -1
  80. package/dist/runtime/client.d.ts +20 -11
  81. package/dist/runtime/client.js +20 -10
  82. package/dist/runtime/client.js.map +1 -1
  83. package/dist/runtime/context.d.ts +304 -123
  84. package/dist/runtime/context.js +377 -149
  85. package/dist/runtime/context.js.map +1 -1
  86. package/dist/runtime/control.d.ts +15 -1
  87. package/dist/runtime/control.js +24 -0
  88. package/dist/runtime/control.js.map +1 -1
  89. package/dist/runtime/dev-protocol.d.ts +4 -9
  90. package/dist/runtime/dev-protocol.js.map +1 -1
  91. package/dist/runtime/empty-server-app.d.ts +0 -1
  92. package/dist/runtime/entry.client.d.ts +4 -1
  93. package/dist/runtime/entry.client.js +455 -313
  94. package/dist/runtime/entry.client.js.map +1 -1
  95. package/dist/runtime/entry.rsc.d.ts +15 -7
  96. package/dist/runtime/entry.rsc.js +811 -252
  97. package/dist/runtime/entry.rsc.js.map +1 -1
  98. package/dist/runtime/entry.ssr.d.ts +27 -10
  99. package/dist/runtime/entry.ssr.js +30 -48
  100. package/dist/runtime/entry.ssr.js.map +1 -1
  101. package/dist/runtime/failure-document.d.ts +11 -0
  102. package/dist/runtime/failure-document.js +35 -0
  103. package/dist/runtime/failure-document.js.map +1 -0
  104. package/dist/runtime/flight-inject.d.ts +23 -0
  105. package/dist/runtime/flight-inject.js +403 -0
  106. package/dist/runtime/flight-inject.js.map +1 -0
  107. package/dist/runtime/hot-update.d.ts +44 -0
  108. package/dist/runtime/hot-update.js +44 -0
  109. package/dist/runtime/hot-update.js.map +1 -0
  110. package/dist/runtime/navigation.d.ts +46 -54
  111. package/dist/runtime/navigation.js +22 -63
  112. package/dist/runtime/navigation.js.map +1 -1
  113. package/dist/runtime/request.d.ts +42 -8
  114. package/dist/runtime/request.js +84 -7
  115. package/dist/runtime/request.js.map +1 -1
  116. package/dist/runtime/server.d.ts +20 -18
  117. package/dist/runtime/server.js +22 -17
  118. package/dist/runtime/server.js.map +1 -1
  119. package/dist/runtime/validate-entries.d.ts +77 -0
  120. package/dist/runtime/validate-entries.js +405 -0
  121. package/dist/runtime/validate-entries.js.map +1 -0
  122. package/dist/server/headers.d.ts +41 -15
  123. package/dist/server/headers.js +53 -24
  124. package/dist/server/headers.js.map +1 -1
  125. package/dist/server/load-config.d.ts +4 -5
  126. package/dist/server/load-config.js +22 -14
  127. package/dist/server/load-config.js.map +1 -1
  128. package/dist/server/load-env.d.ts +0 -1
  129. package/dist/server/prerendered.d.ts +88 -34
  130. package/dist/server/prerendered.js +184 -24
  131. package/dist/server/prerendered.js.map +1 -1
  132. package/dist/server/server-config.d.ts +35 -50
  133. package/dist/server/server-config.js +27 -74
  134. package/dist/server/server-config.js.map +1 -1
  135. package/dist/server/shutdown.d.ts +5 -4
  136. package/dist/server/shutdown.js +5 -3
  137. package/dist/server/shutdown.js.map +1 -1
  138. package/dist/server/ssg.d.ts +9 -7
  139. package/dist/server/ssg.js +209 -65
  140. package/dist/server/ssg.js.map +1 -1
  141. package/dist/server/static.d.ts +0 -1
  142. package/dist/server/static.js +7 -1
  143. package/dist/server/static.js.map +1 -1
  144. package/package.json +21 -23
  145. package/dist/builder/page-files.d.ts.map +0 -1
  146. package/dist/builder/public-env.d.ts.map +0 -1
  147. package/dist/builder/rspack-config.d.ts.map +0 -1
  148. package/dist/cli/build.d.ts.map +0 -1
  149. package/dist/cli/dev.d.ts.map +0 -1
  150. package/dist/cli/index.d.ts.map +0 -1
  151. package/dist/cli/start.d.ts.map +0 -1
  152. package/dist/config.d.ts.map +0 -1
  153. package/dist/deploy/aws-lambda/runtime.d.ts.map +0 -1
  154. package/dist/deploy/build-marker.d.ts.map +0 -1
  155. package/dist/deploy/bun/runtime.d.ts +0 -11
  156. package/dist/deploy/bun/runtime.d.ts.map +0 -1
  157. package/dist/deploy/bun/runtime.js +0 -22
  158. package/dist/deploy/bun/runtime.js.map +0 -1
  159. package/dist/deploy/cloudflare/build.d.ts.map +0 -1
  160. package/dist/deploy/cloudflare/runtime.d.ts.map +0 -1
  161. package/dist/deploy/contract.d.ts.map +0 -1
  162. package/dist/deploy/deno/runtime.d.ts +0 -11
  163. package/dist/deploy/deno/runtime.d.ts.map +0 -1
  164. package/dist/deploy/deno/runtime.js +0 -16
  165. package/dist/deploy/deno/runtime.js.map +0 -1
  166. package/dist/deploy/filesystem.d.ts.map +0 -1
  167. package/dist/deploy/listen.d.ts +0 -20
  168. package/dist/deploy/listen.d.ts.map +0 -1
  169. package/dist/deploy/listen.js +0 -24
  170. package/dist/deploy/listen.js.map +0 -1
  171. package/dist/deploy/netlify/build.d.ts +0 -8
  172. package/dist/deploy/netlify/build.d.ts.map +0 -1
  173. package/dist/deploy/netlify/build.js +0 -52
  174. package/dist/deploy/netlify/build.js.map +0 -1
  175. package/dist/deploy/netlify/runtime.d.ts +0 -13
  176. package/dist/deploy/netlify/runtime.d.ts.map +0 -1
  177. package/dist/deploy/netlify/runtime.js +0 -24
  178. package/dist/deploy/netlify/runtime.js.map +0 -1
  179. package/dist/deploy/node/runtime.d.ts.map +0 -1
  180. package/dist/deploy/presets.d.ts.map +0 -1
  181. package/dist/deploy/vercel/build.d.ts.map +0 -1
  182. package/dist/deploy/vercel/runtime.d.ts.map +0 -1
  183. package/dist/index.d.ts.map +0 -1
  184. package/dist/router.d.ts.map +0 -1
  185. package/dist/runtime/boundaries.d.ts.map +0 -1
  186. package/dist/runtime/client.d.ts.map +0 -1
  187. package/dist/runtime/context.d.ts.map +0 -1
  188. package/dist/runtime/control.d.ts.map +0 -1
  189. package/dist/runtime/dev-protocol.d.ts.map +0 -1
  190. package/dist/runtime/empty-server-app.d.ts.map +0 -1
  191. package/dist/runtime/entry.client.d.ts.map +0 -1
  192. package/dist/runtime/entry.rsc.d.ts.map +0 -1
  193. package/dist/runtime/entry.ssr.d.ts.map +0 -1
  194. package/dist/runtime/navigation.d.ts.map +0 -1
  195. package/dist/runtime/request.d.ts.map +0 -1
  196. package/dist/runtime/server.d.ts.map +0 -1
  197. package/dist/server/compress.d.ts +0 -15
  198. package/dist/server/compress.d.ts.map +0 -1
  199. package/dist/server/compress.js +0 -76
  200. package/dist/server/compress.js.map +0 -1
  201. package/dist/server/headers.d.ts.map +0 -1
  202. package/dist/server/load-config.d.ts.map +0 -1
  203. package/dist/server/load-env.d.ts.map +0 -1
  204. package/dist/server/prerendered.d.ts.map +0 -1
  205. package/dist/server/server-config.d.ts.map +0 -1
  206. package/dist/server/shutdown.d.ts.map +0 -1
  207. package/dist/server/ssg.d.ts.map +0 -1
  208. package/dist/server/static.d.ts.map +0 -1
package/dist/cli/index.js CHANGED
@@ -3,9 +3,11 @@ import { parseArgs } from 'node:util';
3
3
  import { DEPLOY_TARGETS, resolveDeployPreset } from '../deploy/presets.js';
4
4
  import { loadConfig } from '../server/load-config.js';
5
5
  import { loadEnvFiles } from '../server/load-env.js';
6
- import { buildCommand } from './build.js';
7
- import { devCommand } from './dev.js';
8
- import { startCommand } from './start.js';
6
+ import { parsePort } from '../server/server-config.js';
7
+ import { exit } from './exit.js';
8
+ // The commands are imported where they are dispatched: `build` and `dev` pull in Rspack, and a static import
9
+ // would load it for `start` too — ~30 MB of RSS and ~70ms of startup that would then sit in the server's own
10
+ // process for its whole life, since `start` imports the bundle rather than spawning it.
9
11
  const HELP = `rshono — Hono + Rspack + React Server Components
10
12
 
11
13
  Usage:
@@ -14,26 +16,53 @@ Usage:
14
16
  rshono start [--port 3000] run the production build
15
17
 
16
18
  Options:
17
- -p, --port <n> port to listen on (default: PORT env or rshono.config.ts or 3000)
19
+ -p, --port <n> port to listen on (default: PORT env or 3000)
18
20
  -c, --config <path> path to a config file (default: rshono.config.{ts,js,mjs})
19
21
  -d, --deploy <name> platform to build for: ${DEPLOY_TARGETS.join(' | ')} (default: node)
20
22
  -h, --help show this help
21
23
  -v, --version print the version
24
+
25
+ Environment:
26
+ PORT port to listen on, unless --port is given
27
+ HOST address \`start\` binds to (default 0.0.0.0); \`dev\` always binds 127.0.0.1
28
+ RSHONO_DEPLOY platform to build for, unless --deploy is given
22
29
  `;
30
+ /** `as const` so `parseArgs` can type `values` off the literal `type` fields rather than widening them. */
31
+ const CLI_OPTIONS = {
32
+ port: { type: 'string', short: 'p' },
33
+ config: { type: 'string', short: 'c' },
34
+ deploy: { type: 'string', short: 'd' },
35
+ help: { type: 'boolean', short: 'h' },
36
+ version: { type: 'boolean', short: 'v' },
37
+ };
38
+ /**
39
+ * {@link parseArgs}, reported the way the CLI reports every other bad input: one line, then the help.
40
+ *
41
+ * A typo'd flag is the likeliest of the three bad inputs and used to get by far the worst output. `parseArgs`
42
+ * was the first statement of `main`, with nothing between it and the `main().catch` that prints the raw error
43
+ * object — so `rshono build --porf 3000` answered with nine frames of Node internals, where an unknown
44
+ * command and an unparseable `--port` each answered with a sentence.
45
+ *
46
+ * Node's own message is kept: it names the offending flag, which is the whole content of the error, and
47
+ * `HELP` beneath it lists the ones that exist. Through {@link exit} rather than `process.exit`, because that
48
+ * help goes to stdout and a piped stdout drops whatever has not left the buffer.
49
+ */
50
+ async function readArgs() {
51
+ try {
52
+ return parseArgs({ options: CLI_OPTIONS, allowPositionals: true });
53
+ }
54
+ catch (error) {
55
+ console.error(`rshono: ${error instanceof Error ? error.message : String(error)}\n`);
56
+ console.log(HELP);
57
+ return exit(1);
58
+ }
59
+ }
23
60
  async function main() {
24
- const { values, positionals } = parseArgs({
25
- options: {
26
- port: { type: 'string', short: 'p' },
27
- config: { type: 'string', short: 'c' },
28
- deploy: { type: 'string', short: 'd' },
29
- help: { type: 'boolean', short: 'h' },
30
- version: { type: 'boolean', short: 'v' },
31
- },
32
- allowPositionals: true,
33
- });
61
+ const { values, positionals } = await readArgs();
34
62
  if (values.version) {
35
63
  const require = createRequire(import.meta.url);
36
- console.log(require('@rshono/core/package.json').version);
64
+ const { version } = require('@rshono/core/package.json');
65
+ console.log(version);
37
66
  return;
38
67
  }
39
68
  const command = positionals[0];
@@ -44,34 +73,66 @@ async function main() {
44
73
  const rootDir = process.cwd();
45
74
  loadEnvFiles(rootDir);
46
75
  const config = await loadConfig(rootDir, values.config);
47
- const flagPort = values.port ? Number(values.port) : undefined;
48
- if (values.port && Number.isNaN(flagPort)) {
49
- console.error(`rshono: invalid --port "${values.port}"`);
50
- process.exit(1);
76
+ // Precedence: --port flag > PORT env > the command's built-in default. Both sources go through the same
77
+ // parse as the bundle's own, so `PORT=""` means "unset" in the CLI and in the server it starts alike.
78
+ //
79
+ // The parse stays synchronous and the exit is here, where the other flag errors are answered, rather than
80
+ // inside a `readPort` helper of its own. That helper had to reach `process.exit` directly — it is not
81
+ // `async`, and `exit()` returns a promise — so it was the one bad-input path in the CLI that did not
82
+ // drain a piped stderr. Making it `async` instead is the trap: `??` on a `Promise` is never nullish, so
83
+ // `--port` would resolve to a pending promise and `PORT` would stop being read at all.
84
+ let port;
85
+ try {
86
+ port = parsePort(values.port, '--port') ?? parsePort(process.env.PORT, 'PORT');
87
+ }
88
+ catch (error) {
89
+ console.error(`rshono: ${error instanceof Error ? error.message : String(error)}`);
90
+ return exit(1);
51
91
  }
52
- // Precedence: --port flag > PORT env > rshono.config.ts > the command's built-in default.
53
- const envPort = process.env.PORT ? Number(process.env.PORT) : undefined;
54
- const port = flagPort ?? envPort ?? config.port;
55
- const host = process.env.HOST ?? config.host;
92
+ const host = process.env.HOST;
56
93
  switch (command) {
57
94
  case 'dev':
58
- return devCommand({ rootDir, port, config });
95
+ // `HOST` belongs to `start`. The dev server binds loopback unconditionally — its source maps embed the
96
+ // original source of `'use server'` modules — and `DevOptions` has no `host` field to pass one to, so
97
+ // the drop is structural rather than conditional. Said out loud because the variable is read a few
98
+ // lines up and the README lists it under all three commands: `HOST=0.0.0.0 rshono dev` used to do
99
+ // nothing, with nothing to notice.
100
+ if (host !== undefined) {
101
+ console.warn('rshono: HOST is ignored by `rshono dev`, which always binds 127.0.0.1 — it applies to `rshono start`.');
102
+ }
103
+ return (await import('./dev.js')).devCommand({ rootDir, port, config });
59
104
  case 'build':
60
- return buildCommand({
105
+ return (await import('./build.js')).buildCommand({
61
106
  rootDir,
62
107
  config,
63
108
  preset: resolveDeployPreset({ flag: values.deploy, env: process.env.RSHONO_DEPLOY, config: config.deploy }),
64
109
  });
65
110
  case 'start':
66
- return startCommand({ rootDir, port, host });
111
+ return (await import('./start.js')).startCommand({ rootDir, port, host });
67
112
  default:
68
113
  console.error(`rshono: unknown command "${command}"\n`);
69
114
  console.log(HELP);
70
- process.exit(1);
115
+ return exit(1);
71
116
  }
72
117
  }
73
- main().catch((error) => {
74
- console.error(error);
75
- process.exit(1);
118
+ /**
119
+ * The one place a failure becomes output, for every command.
120
+ *
121
+ * A `[rshono]` message was written for whoever is running the command, so it is printed as the one line it
122
+ * is; anything else is a bug in the framework and keeps its stack, because that stack is the report.
123
+ *
124
+ * This rule used to live in `build.ts`, as a `phase()` wrapper around three stages of one command — which
125
+ * left everything outside those three going out through a bare `console.error(error)`: the whole of
126
+ * `rshono dev`, and `build`'s own `createConfigs`, which is where a missing `src/routes.ts` is found. So the
127
+ * likeliest first-run mistake there is answered with a raw `Error` object and two frames of framework
128
+ * internals, which is the same shape of problem an unknown CLI flag had.
129
+ */
130
+ main().catch(async (error) => {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ if (message.startsWith('[rshono]'))
133
+ console.error(`\n ✗ ${message}\n`);
134
+ else
135
+ console.error(error);
136
+ await exit(1);
76
137
  });
77
138
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { DEPLOY_TARGETS, resolveDeployPreset } from '../deploy/presets.js';\nimport { loadConfig } from '../server/load-config.js';\nimport { loadEnvFiles } from '../server/load-env.js';\nimport { buildCommand } from './build.js';\nimport { devCommand } from './dev.js';\nimport { startCommand } from './start.js';\n\nconst HELP = `rshono — Hono + Rspack + React Server Components\n\nUsage:\n rshono dev [--port 3000] start the dev server\n rshono build build for production (client + server + SSG)\n rshono start [--port 3000] run the production build\n\nOptions:\n -p, --port <n> port to listen on (default: PORT env or rshono.config.ts or 3000)\n -c, --config <path> path to a config file (default: rshono.config.{ts,js,mjs})\n -d, --deploy <name> platform to build for: ${DEPLOY_TARGETS.join(' | ')} (default: node)\n -h, --help show this help\n -v, --version print the version\n`;\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n options: {\n port: { type: 'string', short: 'p' },\n config: { type: 'string', short: 'c' },\n deploy: { type: 'string', short: 'd' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n },\n allowPositionals: true,\n });\n\n if (values.version) {\n const require = createRequire(import.meta.url);\n console.log(require('@rshono/core/package.json').version);\n return;\n }\n\n const command = positionals[0];\n if (values.help || !command) {\n console.log(HELP);\n return;\n }\n\n const rootDir = process.cwd();\n loadEnvFiles(rootDir);\n const config = await loadConfig(rootDir, values.config);\n\n const flagPort = values.port ? Number(values.port) : undefined;\n if (values.port && Number.isNaN(flagPort)) {\n console.error(`rshono: invalid --port \"${values.port}\"`);\n process.exit(1);\n }\n // Precedence: --port flag > PORT env > rshono.config.ts > the command's built-in default.\n const envPort = process.env.PORT ? Number(process.env.PORT) : undefined;\n const port = flagPort ?? envPort ?? config.port;\n const host = process.env.HOST ?? config.host;\n\n switch (command) {\n case 'dev':\n return devCommand({ rootDir, port, config });\n case 'build':\n return buildCommand({\n rootDir,\n config,\n preset: resolveDeployPreset({ flag: values.deploy, env: process.env.RSHONO_DEPLOY, config: config.deploy }),\n });\n case 'start':\n return startCommand({ rootDir, port, host });\n default:\n console.error(`rshono: unknown command \"${command}\"\\n`);\n console.log(HELP);\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { DEPLOY_TARGETS, resolveDeployPreset } from '../deploy/presets.js';\nimport { loadConfig } from '../server/load-config.js';\nimport { loadEnvFiles } from '../server/load-env.js';\nimport { parsePort } from '../server/server-config.js';\nimport { exit } from './exit.js';\n\n// The commands are imported where they are dispatched: `build` and `dev` pull in Rspack, and a static import\n// would load it for `start` too — ~30 MB of RSS and ~70ms of startup that would then sit in the server's own\n// process for its whole life, since `start` imports the bundle rather than spawning it.\n\nconst HELP = `rshono — Hono + Rspack + React Server Components\n\nUsage:\n rshono dev [--port 3000] start the dev server\n rshono build build for production (client + server + SSG)\n rshono start [--port 3000] run the production build\n\nOptions:\n -p, --port <n> port to listen on (default: PORT env or 3000)\n -c, --config <path> path to a config file (default: rshono.config.{ts,js,mjs})\n -d, --deploy <name> platform to build for: ${DEPLOY_TARGETS.join(' | ')} (default: node)\n -h, --help show this help\n -v, --version print the version\n\nEnvironment:\n PORT port to listen on, unless --port is given\n HOST address \\`start\\` binds to (default 0.0.0.0); \\`dev\\` always binds 127.0.0.1\n RSHONO_DEPLOY platform to build for, unless --deploy is given\n`;\n\n/** `as const` so `parseArgs` can type `values` off the literal `type` fields rather than widening them. */\nconst CLI_OPTIONS = {\n port: { type: 'string', short: 'p' },\n config: { type: 'string', short: 'c' },\n deploy: { type: 'string', short: 'd' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n} as const;\n\n/**\n * {@link parseArgs}, reported the way the CLI reports every other bad input: one line, then the help.\n *\n * A typo'd flag is the likeliest of the three bad inputs and used to get by far the worst output. `parseArgs`\n * was the first statement of `main`, with nothing between it and the `main().catch` that prints the raw error\n * object — so `rshono build --porf 3000` answered with nine frames of Node internals, where an unknown\n * command and an unparseable `--port` each answered with a sentence.\n *\n * Node's own message is kept: it names the offending flag, which is the whole content of the error, and\n * `HELP` beneath it lists the ones that exist. Through {@link exit} rather than `process.exit`, because that\n * help goes to stdout and a piped stdout drops whatever has not left the buffer.\n */\nasync function readArgs(): Promise<ReturnType<typeof parseArgs<{ options: typeof CLI_OPTIONS; allowPositionals: true }>>> {\n try {\n return parseArgs({ options: CLI_OPTIONS, allowPositionals: true });\n } catch (error) {\n console.error(`rshono: ${error instanceof Error ? error.message : String(error)}\\n`);\n console.log(HELP);\n return exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const { values, positionals } = await readArgs();\n\n if (values.version) {\n const require = createRequire(import.meta.url);\n const { version } = require('@rshono/core/package.json') as { version: string };\n console.log(version);\n return;\n }\n\n const command = positionals[0];\n if (values.help || !command) {\n console.log(HELP);\n return;\n }\n\n const rootDir = process.cwd();\n loadEnvFiles(rootDir);\n const config = await loadConfig(rootDir, values.config);\n\n // Precedence: --port flag > PORT env > the command's built-in default. Both sources go through the same\n // parse as the bundle's own, so `PORT=\"\"` means \"unset\" in the CLI and in the server it starts alike.\n //\n // The parse stays synchronous and the exit is here, where the other flag errors are answered, rather than\n // inside a `readPort` helper of its own. That helper had to reach `process.exit` directly — it is not\n // `async`, and `exit()` returns a promise — so it was the one bad-input path in the CLI that did not\n // drain a piped stderr. Making it `async` instead is the trap: `??` on a `Promise` is never nullish, so\n // `--port` would resolve to a pending promise and `PORT` would stop being read at all.\n let port: number | undefined;\n try {\n port = parsePort(values.port, '--port') ?? parsePort(process.env.PORT, 'PORT');\n } catch (error) {\n console.error(`rshono: ${error instanceof Error ? error.message : String(error)}`);\n return exit(1);\n }\n const host = process.env.HOST;\n\n switch (command) {\n case 'dev':\n // `HOST` belongs to `start`. The dev server binds loopback unconditionally — its source maps embed the\n // original source of `'use server'` modules — and `DevOptions` has no `host` field to pass one to, so\n // the drop is structural rather than conditional. Said out loud because the variable is read a few\n // lines up and the README lists it under all three commands: `HOST=0.0.0.0 rshono dev` used to do\n // nothing, with nothing to notice.\n if (host !== undefined) {\n console.warn('rshono: HOST is ignored by `rshono dev`, which always binds 127.0.0.1 — it applies to `rshono start`.');\n }\n return (await import('./dev.js')).devCommand({ rootDir, port, config });\n case 'build':\n return (await import('./build.js')).buildCommand({\n rootDir,\n config,\n preset: resolveDeployPreset({ flag: values.deploy, env: process.env.RSHONO_DEPLOY, config: config.deploy }),\n });\n case 'start':\n return (await import('./start.js')).startCommand({ rootDir, port, host });\n default:\n console.error(`rshono: unknown command \"${command}\"\\n`);\n console.log(HELP);\n return exit(1);\n }\n}\n\n/**\n * The one place a failure becomes output, for every command.\n *\n * A `[rshono]` message was written for whoever is running the command, so it is printed as the one line it\n * is; anything else is a bug in the framework and keeps its stack, because that stack is the report.\n *\n * This rule used to live in `build.ts`, as a `phase()` wrapper around three stages of one command — which\n * left everything outside those three going out through a bare `console.error(error)`: the whole of\n * `rshono dev`, and `build`'s own `createConfigs`, which is where a missing `src/routes.ts` is found. So the\n * likeliest first-run mistake there is answered with a raw `Error` object and two frames of framework\n * internals, which is the same shape of problem an unknown CLI flag had.\n */\nmain().catch(async (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith('[rshono]')) console.error(`\\n ✗ ${message}\\n`);\n else console.error(error);\n await exit(1);\n});\n"]}
@@ -5,4 +5,3 @@ interface StartOptions {
5
5
  }
6
6
  export declare function startCommand(options: StartOptions): Promise<void>;
7
7
  export {};
8
- //# sourceMappingURL=start.d.ts.map
package/dist/cli/start.js CHANGED
@@ -1,39 +1,36 @@
1
- import { spawn } from 'node:child_process';
2
1
  import { existsSync } from 'node:fs';
3
2
  import { join } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
4
  import { readBuildMarker } from '../deploy/build-marker.js';
5
5
  import { deployHintFor } from '../deploy/presets.js';
6
- import { onShutdown } from '../server/shutdown.js';
6
+ import { exit } from './exit.js';
7
7
  export async function startCommand(options) {
8
8
  const { rootDir, port, host } = options;
9
9
  const distDir = join(rootDir, 'dist');
10
10
  const mainPath = join(distDir, 'server', 'main.mjs');
11
11
  if (!existsSync(mainPath)) {
12
12
  console.error('rshono: no production build found — run `rshono build` first.');
13
- process.exit(1);
13
+ return exit(1);
14
14
  }
15
- // A bundle built for a hosting platform has no listener in it its entry hands a `fetch` handler to
16
- // whatever is running it. Starting one here would exit silently the moment the module finished
17
- // evaluating, so say what happened and point at the command that does deploy it.
15
+ // A bundle built for a hosting platform has no listener in it, so starting one here would exit silently the
16
+ // moment the module finished evaluating.
18
17
  const target = readBuildMarker(distDir);
19
18
  if (target !== null && target !== 'node') {
20
19
  const hint = deployHintFor(target);
21
20
  console.error(`rshono: this build targets ${target}, which \`rshono start\` cannot run${hint ? ` — ${hint}` : '.'}`);
22
21
  console.error(' Rebuild for a server with `rshono build --deploy node` to run it here.');
23
- process.exit(1);
22
+ return exit(1);
24
23
  }
25
- const env = { ...process.env };
24
+ // Read by the bundle as it evaluates, which is what binds the port — so they have to be set first.
26
25
  if (port !== undefined)
27
- env.PORT = String(port);
26
+ process.env.PORT = String(port);
28
27
  if (host !== undefined)
29
- env.HOST = host;
30
- const child = spawn(process.execPath, ['--enable-source-maps', mainPath], {
31
- stdio: 'inherit',
32
- env,
33
- });
34
- onShutdown((signal) => child.kill(signal));
35
- child.on('exit', (code, signal) => {
36
- process.exit(signal ? 1 : (code ?? 1));
37
- });
28
+ process.env.HOST = host;
29
+ // Imported into this process rather than spawned into a child. The child only ever existed to pass
30
+ // `--enable-source-maps`, and `bin/rshono.mjs` already enables the same thing in-process before the CLI
31
+ // loads — so a supervisor bought a second PID and its own heap in every container, an extra frame in every
32
+ // stack, and signal forwarding for signals the bundle already handles itself through `onShutdown`. `start`
33
+ // deliberately imports no Rspack, so the process this leaves behind is the server and little else.
34
+ await import(pathToFileURL(mainPath).href);
38
35
  }
39
36
  //# sourceMappingURL=start.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/cli/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAQnD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAC/E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,qGAAqG;IACrG,+FAA+F;IAC/F,iFAAiF;IACjF,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,8BAA8B,MAAM,sCAAsC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrH,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/B,IAAI,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAExC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,sBAAsB,EAAE,QAAQ,CAAC,EAAE;QACxE,KAAK,EAAE,SAAS;QAChB,GAAG;KACJ,CAAC,CAAC;IAEH,UAAU,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAChC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { readBuildMarker } from '../deploy/build-marker.js';\nimport { deployHintFor } from '../deploy/presets.js';\nimport { onShutdown } from '../server/shutdown.js';\n\ninterface StartOptions {\n rootDir: string;\n port?: number;\n host?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const { rootDir, port, host } = options;\n const distDir = join(rootDir, 'dist');\n const mainPath = join(distDir, 'server', 'main.mjs');\n if (!existsSync(mainPath)) {\n console.error('rshono: no production build found — run `rshono build` first.');\n process.exit(1);\n }\n\n // A bundle built for a hosting platform has no listener in it its entry hands a `fetch` handler to\n // whatever is running it. Starting one here would exit silently the moment the module finished\n // evaluating, so say what happened and point at the command that does deploy it.\n const target = readBuildMarker(distDir);\n if (target !== null && target !== 'node') {\n const hint = deployHintFor(target);\n console.error(`rshono: this build targets ${target}, which \\`rshono start\\` cannot run${hint ? ` — ${hint}` : '.'}`);\n console.error(' Rebuild for a server with `rshono build --deploy node` to run it here.');\n process.exit(1);\n }\n\n const env = { ...process.env };\n if (port !== undefined) env.PORT = String(port);\n if (host !== undefined) env.HOST = host;\n\n const child = spawn(process.execPath, ['--enable-source-maps', mainPath], {\n stdio: 'inherit',\n env,\n });\n\n onShutdown((signal) => child.kill(signal));\n child.on('exit', (code, signal) => {\n process.exit(signal ? 1 : (code ?? 1));\n });\n}\n"]}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/cli/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,4GAA4G;IAC5G,yCAAyC;IACzC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,8BAA8B,MAAM,sCAAsC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrH,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC1F,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,mGAAmG;IACnG,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAEhD,mGAAmG;IACnG,wGAAwG;IACxG,2GAA2G;IAC3G,2GAA2G;IAC3G,mGAAmG;IACnG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { readBuildMarker } from '../deploy/build-marker.js';\nimport { deployHintFor } from '../deploy/presets.js';\nimport { exit } from './exit.js';\n\ninterface StartOptions {\n rootDir: string;\n port?: number;\n host?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const { rootDir, port, host } = options;\n const distDir = join(rootDir, 'dist');\n const mainPath = join(distDir, 'server', 'main.mjs');\n if (!existsSync(mainPath)) {\n console.error('rshono: no production build found — run `rshono build` first.');\n return exit(1);\n }\n\n // A bundle built for a hosting platform has no listener in it, so starting one here would exit silently the\n // moment the module finished evaluating.\n const target = readBuildMarker(distDir);\n if (target !== null && target !== 'node') {\n const hint = deployHintFor(target);\n console.error(`rshono: this build targets ${target}, which \\`rshono start\\` cannot run${hint ? ` — ${hint}` : '.'}`);\n console.error(' Rebuild for a server with `rshono build --deploy node` to run it here.');\n return exit(1);\n }\n\n // Read by the bundle as it evaluates, which is what binds the port — so they have to be set first.\n if (port !== undefined) process.env.PORT = String(port);\n if (host !== undefined) process.env.HOST = host;\n\n // Imported into this process rather than spawned into a child. The child only ever existed to pass\n // `--enable-source-maps`, and `bin/rshono.mjs` already enables the same thing in-process before the CLI\n // loads — so a supervisor bought a second PID and its own heap in every container, an extra frame in every\n // stack, and signal forwarding for signals the bundle already handles itself through `onShutdown`. `start`\n // deliberately imports no Rspack, so the process this leaves behind is the server and little else.\n await import(pathToFileURL(mainPath).href);\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { RspackOptions } from '@rspack/core';
2
2
  import type { DeployTarget } from './deploy/contract.js';
3
- /** Which of the two Rspack compilers the {@link RSHonoConfig.rspack} hook is being called for. */
3
+ /** Which of the two Rspack compilers the {@link RshonoConfig.rspack} hook is being called for. */
4
4
  export interface RspackHookContext {
5
5
  /** `true` for the server (`target: node`) bundle, `false` for the client (`target: web`) bundle. */
6
6
  isServer: boolean;
@@ -8,114 +8,108 @@ export interface RspackHookContext {
8
8
  isDev: boolean;
9
9
  }
10
10
  /**
11
- * Project configuration for rshono, default-exported from `rshono.config.ts` at the project root
12
- * (`.js` / `.mjs` also work). Every field is optional; omit the file entirely to accept all defaults.
11
+ * Project configuration, default-exported from `rshono.config.ts` at the project root (`.js` / `.mjs`
12
+ * also work). Every field is optional; omit the file entirely to accept all defaults.
13
+ *
14
+ * It holds only what the *build* decides. Per-request concerns — CSRF, CSP, the body cap — are Hono
15
+ * middleware in `src/server.ts`, which is where Hono already ships all of them.
13
16
  *
14
17
  * @example
15
18
  * ```ts
16
19
  * import { defineConfig } from '@rshono/core';
17
20
  *
18
21
  * export default defineConfig({
19
- * csp: true,
20
- * bodySizeLimit: '4mb',
21
- * allowedOrigins: ['https://admin.example.com'],
22
+ * deploy: 'cloudflare',
23
+ * siteUrl: 'https://example.com',
22
24
  * });
23
25
  * ```
26
+ *
27
+ * @see {@link https://www.rshono.com/docs/configuration | Docs — configuration}
24
28
  */
25
- export interface RSHonoConfig {
29
+ export interface RshonoConfig {
26
30
  /**
27
- * The hosting platform `rshono build` targets. Default `'node'` — a long-lived server process, for
28
- * a VPS, a container or anywhere else you run `rshono start`.
31
+ * The hosting platform `rshono build` targets. Default `'node'` — a long-lived server process run
32
+ * with `rshono start`.
33
+ *
34
+ * Overridden by the `--deploy` flag or the `RSHONO_DEPLOY` env var, so one config can build for more
35
+ * than one place. `rshono dev` ignores it and always runs the Node dev server.
29
36
  *
30
- * Overridden by the `--deploy` flag or the `RSHONO_DEPLOY` env var, so one config can still be
31
- * built for more than one place. `rshono dev` ignores it entirely and always runs the Node dev
32
- * server.
37
+ * @see {@link https://www.rshono.com/docs/deployment | Docs deployment}
33
38
  */
34
39
  deploy?: DeployTarget;
35
40
  /**
36
- * The public origin the site is served from, e.g. `'https://example.com'`.
41
+ * The public origin the site is served from, e.g. `'https://example.com'`. Must be a bare origin —
42
+ * a path is rejected rather than silently dropped.
37
43
  *
38
- * Only used when prerendering `render: 'static'` routes. A prerendered page is one fixed file
39
- * handed to everyone, so any absolute URL inside it has to be decided at build time — there is no
40
- * request to read a `Host` from. That is what a page's `url` prop is, so without this a static
41
- * page bakes in `http://localhost` wherever it builds a canonical tag, an absolute link or an
42
- * `og:url`. Dynamic routes are unaffected: they resolve the URL per request.
44
+ * Only used when prerendering `render: 'static'` routes, which have no request to read a `Host`
45
+ * from: without it their `url` prop falls back to `http://localhost`, and that is what canonical
46
+ * tags, absolute links and `og:url` get baked with.
43
47
  *
44
- * The origin is what's used; a path is rejected rather than silently dropped.
48
+ * @see {@link https://www.rshono.com/docs/configuration#siteurl | Docs siteUrl}
45
49
  */
46
50
  siteUrl?: string;
47
- /** Default port for `dev` / `start`. Overridden by the `--port` flag or the `PORT` env var. Default `3000`. */
48
- port?: number;
49
- /** Bind address for `start`. Overridden by the `HOST` env var. Default `'0.0.0.0'`. */
50
- host?: string;
51
51
  /**
52
- * Honour `X-Forwarded-Host` / `X-Forwarded-Proto` when resolving the browser-facing request
53
- * URL (`getContext().url`, a page's `url` prop, and the origin the CSRF check compares against).
52
+ * Honour `X-Forwarded-Host` / `X-Forwarded-Proto` when resolving the browser-facing request URL —
53
+ * `getRequestContext().url` and a page's `url` prop. Default `false`; always `true` under
54
+ * `rshono dev`.
54
55
  *
55
- * **Off by default, and leave it off unless a proxy you control sets those headers**, because
56
- * any client can send them: with it on and nothing stripping them at the edge, one request can
57
- * point every absolute URL your app builds at an attacker's host (and poison a shared cache).
58
- * Turn it on when you terminate TLS or rewrite `Host` at a reverse proxy / load balancer.
59
- * Always `true` under `rshono dev`, where the framework's own proxy sets them and binds to
60
- * localhost. Default `false`.
61
- */
62
- trustProxy?: boolean;
63
- /**
64
- * CSRF origin check on server-action POSTs — rejects a cross-origin request with 403.
65
- * Turn off only behind a gateway that already enforces it. Default `true`.
66
- */
67
- checkOrigin?: boolean;
68
- /**
69
- * Extra origins allowed to post server actions, in addition to the app's own origin.
70
- * Accepts full origins or bare hosts, e.g. `['https://admin.example.com', 'localhost:4000']`.
71
- */
72
- allowedOrigins?: string[];
73
- /**
74
- * Send a strict per-request-nonce `Content-Security-Policy` with every HTML document.
75
- * While enabled, `render: 'static'` routes render per request (a prerendered file can't carry a
76
- * per-request nonce). Default `false`.
77
- */
78
- csp?: boolean;
79
- /**
80
- * Directives merged over the built-in {@link csp} policy, which is deliberately strict
81
- * (`default-src 'self'`, no framing, no plugins) and so blocks third-party images, fonts and
82
- * API hosts until you widen it here. Set a directive to `''` to drop it entirely.
56
+ * **Enable it only behind a proxy you control**: any client can send those headers, so with nothing
57
+ * stripping them at the edge one request can point every absolute URL the app builds at an
58
+ * attacker's host. Turn it on when you terminate TLS or rewrite `Host` at a reverse proxy.
59
+ *
60
+ * Middleware in `src/server.ts` reads Hono's `c.req.url` the *internal* address whatever this
61
+ * says, so give `csrf()` the public origin explicitly.
62
+ *
63
+ * **Compiled into the server bundle**, not read at runtime, so it takes a rebuild to change and there is
64
+ * no environment variable for it: one artifact cannot be promoted from a direct-exposure staging box to a
65
+ * proxied production one.
83
66
  *
84
- * The per-request nonce is always appended to `script-src`, whatever you put there.
85
- * Ignored unless `csp` is `true`.
67
+ * **The `vercel` target is the exception, for the scheme only.** TLS terminates at that platform's edge
68
+ * and the function is reached over plain HTTP, so the request is rebuilt from `X-Forwarded-Proto`
69
+ * whatever this says — the function is reachable only through the edge, which sets the header on every
70
+ * request, so it is not client-supplied there. The `Host` header is not part of the exception, and is no
71
+ * more trusted on that target than on any other.
86
72
  *
87
73
  * @example
88
74
  * ```ts
89
- * cspDirectives: {
90
- * 'img-src': "'self' data: https://images.example.com",
91
- * 'font-src': "'self' https://fonts.gstatic.com",
92
- * 'frame-ancestors': "'self'",
93
- * }
75
+ * export default defineConfig({ trustProxy: true });
94
76
  * ```
95
- */
96
- cspDirectives?: Record<string, string>;
97
- /**
98
- * Max server-action request body before it's rejected with 413 — a memory-exhaustion guard.
99
- * A number is bytes; a string carries a unit (`'512kb'`, `'4mb'`); `false` (or `0`) disables the cap.
100
- * Default `'1mb'`.
101
- */
102
- bodySizeLimit?: string | number | false;
103
- /** Deadline in milliseconds for a single page render (flight + SSR). Default `10000`. */
104
- renderTimeout?: number;
105
- /**
106
- * Gzip compressible responses (HTML, flight payloads, JSON, CSS, JS). Streaming-safe — each
107
- * chunk the renderer flushes is flushed on the wire too. Default `true`.
108
77
  *
109
- * Set `false` behind a proxy or CDN that already compresses, to avoid doing the work twice.
78
+ * @see {@link https://www.rshono.com/docs/configuration#proxy-headers | Docs proxy headers}
110
79
  */
111
- compress?: boolean;
80
+ trustProxy?: boolean;
112
81
  /**
113
82
  * Escape hatch: mutate the generated Rspack config just before it's compiled. Called once per
114
83
  * compiler — inspect {@link RspackHookContext.isServer} to tell them apart. Mutate `config` in
115
84
  * place and return nothing, or return a replacement.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * rspack(config, { isServer }) {
89
+ * config.module?.rules?.push({ test: /\.svg$/, type: 'asset/source' });
90
+ * }
91
+ * ```
92
+ *
93
+ * @see {@link https://rspack.rs/config/ | Rspack — configuration reference}
94
+ * @see {@link https://www.rshono.com/docs/configuration#the-rspack-hook | Docs — the rspack hook}
116
95
  */
117
96
  rspack?: (config: RspackOptions, ctx: RspackHookContext) => RspackOptions | void;
118
97
  }
119
- /** Identity helper that types a config object — gives editor autocomplete without an explicit annotation. */
120
- export declare function defineConfig(config: RSHonoConfig): RSHonoConfig;
121
- //# sourceMappingURL=config.d.ts.map
98
+ /**
99
+ * Types a config object for editor autocomplete, without an explicit annotation. Default-export the
100
+ * result from `rshono.config.ts`.
101
+ *
102
+ * @param config - The project's {@link RshonoConfig}; every field is optional.
103
+ * @returns The config, unchanged and fully typed.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * // rshono.config.ts
108
+ * import { defineConfig } from '@rshono/core';
109
+ *
110
+ * export default defineConfig({ deploy: 'cloudflare', siteUrl: 'https://example.com' });
111
+ * ```
112
+ *
113
+ * @see {@link https://www.rshono.com/docs/configuration | Docs — configuration}
114
+ */
115
+ export declare function defineConfig(config: RshonoConfig): RshonoConfig;
package/dist/config.js CHANGED
@@ -1,4 +1,20 @@
1
- /** Identity helper that types a config object — gives editor autocomplete without an explicit annotation. */
1
+ /**
2
+ * Types a config object for editor autocomplete, without an explicit annotation. Default-export the
3
+ * result from `rshono.config.ts`.
4
+ *
5
+ * @param config - The project's {@link RshonoConfig}; every field is optional.
6
+ * @returns The config, unchanged and fully typed.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // rshono.config.ts
11
+ * import { defineConfig } from '@rshono/core';
12
+ *
13
+ * export default defineConfig({ deploy: 'cloudflare', siteUrl: 'https://example.com' });
14
+ * ```
15
+ *
16
+ * @see {@link https://www.rshono.com/docs/configuration | Docs — configuration}
17
+ */
2
18
  export function defineConfig(config) {
3
19
  return config;
4
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAyHA,6GAA6G;AAC7G,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { RspackOptions } from '@rspack/core';\nimport type { DeployTarget } from './deploy/contract.js';\n\n/** Which of the two Rspack compilers the {@link RSHonoConfig.rspack} hook is being called for. */\nexport interface RspackHookContext {\n /** `true` for the server (`target: node`) bundle, `false` for the client (`target: web`) bundle. */\n isServer: boolean;\n /** `true` under `rshono dev`, `false` under `rshono build`. */\n isDev: boolean;\n}\n\n/**\n * Project configuration for rshono, default-exported from `rshono.config.ts` at the project root\n * (`.js` / `.mjs` also work). Every field is optional; omit the file entirely to accept all defaults.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@rshono/core';\n *\n * export default defineConfig({\n * csp: true,\n * bodySizeLimit: '4mb',\n * allowedOrigins: ['https://admin.example.com'],\n * });\n * ```\n */\nexport interface RSHonoConfig {\n /**\n * The hosting platform `rshono build` targets. Default `'node'` — a long-lived server process, for\n * a VPS, a container or anywhere else you run `rshono start`.\n *\n * Overridden by the `--deploy` flag or the `RSHONO_DEPLOY` env var, so one config can still be\n * built for more than one place. `rshono dev` ignores it entirely and always runs the Node dev\n * server.\n */\n deploy?: DeployTarget;\n /**\n * The public origin the site is served from, e.g. `'https://example.com'`.\n *\n * Only used when prerendering `render: 'static'` routes. A prerendered page is one fixed file\n * handed to everyone, so any absolute URL inside it has to be decided at build time — there is no\n * request to read a `Host` from. That is what a page's `url` prop is, so without this a static\n * page bakes in `http://localhost` wherever it builds a canonical tag, an absolute link or an\n * `og:url`. Dynamic routes are unaffected: they resolve the URL per request.\n *\n * The origin is what's used; a path is rejected rather than silently dropped.\n */\n siteUrl?: string;\n /** Default port for `dev` / `start`. Overridden by the `--port` flag or the `PORT` env var. Default `3000`. */\n port?: number;\n /** Bind address for `start`. Overridden by the `HOST` env var. Default `'0.0.0.0'`. */\n host?: string;\n /**\n * Honour `X-Forwarded-Host` / `X-Forwarded-Proto` when resolving the browser-facing request\n * URL (`getContext().url`, a page's `url` prop, and the origin the CSRF check compares against).\n *\n * **Off by default, and leave it off unless a proxy you control sets those headers**, because\n * any client can send them: with it on and nothing stripping them at the edge, one request can\n * point every absolute URL your app builds at an attacker's host (and poison a shared cache).\n * Turn it on when you terminate TLS or rewrite `Host` at a reverse proxy / load balancer.\n * Always `true` under `rshono dev`, where the framework's own proxy sets them and binds to\n * localhost. Default `false`.\n */\n trustProxy?: boolean;\n /**\n * CSRF origin check on server-action POSTs rejects a cross-origin request with 403.\n * Turn off only behind a gateway that already enforces it. Default `true`.\n */\n checkOrigin?: boolean;\n /**\n * Extra origins allowed to post server actions, in addition to the app's own origin.\n * Accepts full origins or bare hosts, e.g. `['https://admin.example.com', 'localhost:4000']`.\n */\n allowedOrigins?: string[];\n /**\n * Send a strict per-request-nonce `Content-Security-Policy` with every HTML document.\n * While enabled, `render: 'static'` routes render per request (a prerendered file can't carry a\n * per-request nonce). Default `false`.\n */\n csp?: boolean;\n /**\n * Directives merged over the built-in {@link csp} policy, which is deliberately strict\n * (`default-src 'self'`, no framing, no plugins) and so blocks third-party images, fonts and\n * API hosts until you widen it here. Set a directive to `''` to drop it entirely.\n *\n * The per-request nonce is always appended to `script-src`, whatever you put there.\n * Ignored unless `csp` is `true`.\n *\n * @example\n * ```ts\n * cspDirectives: {\n * 'img-src': \"'self' data: https://images.example.com\",\n * 'font-src': \"'self' https://fonts.gstatic.com\",\n * 'frame-ancestors': \"'self'\",\n * }\n * ```\n */\n cspDirectives?: Record<string, string>;\n /**\n * Max server-action request body before it's rejected with 413 — a memory-exhaustion guard.\n * A number is bytes; a string carries a unit (`'512kb'`, `'4mb'`); `false` (or `0`) disables the cap.\n * Default `'1mb'`.\n */\n bodySizeLimit?: string | number | false;\n /** Deadline in milliseconds for a single page render (flight + SSR). Default `10000`. */\n renderTimeout?: number;\n /**\n * Gzip compressible responses (HTML, flight payloads, JSON, CSS, JS). Streaming-safe each\n * chunk the renderer flushes is flushed on the wire too. Default `true`.\n *\n * Set `false` behind a proxy or CDN that already compresses, to avoid doing the work twice.\n */\n compress?: boolean;\n /**\n * Escape hatch: mutate the generated Rspack config just before it's compiled. Called once per\n * compiler inspect {@link RspackHookContext.isServer} to tell them apart. Mutate `config` in\n * place and return nothing, or return a replacement.\n */\n rspack?: (config: RspackOptions, ctx: RspackHookContext) => RspackOptions | void;\n}\n\n/** Identity helper that types a config object gives editor autocomplete without an explicit annotation. */\nexport function defineConfig(config: RSHonoConfig): RSHonoConfig {\n return config;\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAoGA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { RspackOptions } from '@rspack/core';\nimport type { DeployTarget } from './deploy/contract.js';\n\n/** Which of the two Rspack compilers the {@link RshonoConfig.rspack} hook is being called for. */\nexport interface RspackHookContext {\n /** `true` for the server (`target: node`) bundle, `false` for the client (`target: web`) bundle. */\n isServer: boolean;\n /** `true` under `rshono dev`, `false` under `rshono build`. */\n isDev: boolean;\n}\n\n/**\n * Project configuration, default-exported from `rshono.config.ts` at the project root (`.js` / `.mjs`\n * also work). Every field is optional; omit the file entirely to accept all defaults.\n *\n * It holds only what the *build* decides. Per-request concerns — CSRF, CSP, the body cap — are Hono\n * middleware in `src/server.ts`, which is where Hono already ships all of them.\n *\n * @example\n * ```ts\n * import { defineConfig } from '@rshono/core';\n *\n * export default defineConfig({\n * deploy: 'cloudflare',\n * siteUrl: 'https://example.com',\n * });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/configuration | Docs — configuration}\n */\nexport interface RshonoConfig {\n /**\n * The hosting platform `rshono build` targets. Default `'node'` — a long-lived server process run\n * with `rshono start`.\n *\n * Overridden by the `--deploy` flag or the `RSHONO_DEPLOY` env var, so one config can build for more\n * than one place. `rshono dev` ignores it and always runs the Node dev server.\n *\n * @see {@link https://www.rshono.com/docs/deployment | Docs — deployment}\n */\n deploy?: DeployTarget;\n /**\n * The public origin the site is served from, e.g. `'https://example.com'`. Must be a bare origin —\n * a path is rejected rather than silently dropped.\n *\n * Only used when prerendering `render: 'static'` routes, which have no request to read a `Host`\n * from: without it their `url` prop falls back to `http://localhost`, and that is what canonical\n * tags, absolute links and `og:url` get baked with.\n *\n * @see {@link https://www.rshono.com/docs/configuration#siteurl | Docs siteUrl}\n */\n siteUrl?: string;\n /**\n * Honour `X-Forwarded-Host` / `X-Forwarded-Proto` when resolving the browser-facing request URL —\n * `getRequestContext().url` and a page's `url` prop. Default `false`; always `true` under\n * `rshono dev`.\n *\n * **Enable it only behind a proxy you control**: any client can send those headers, so with nothing\n * stripping them at the edge one request can point every absolute URL the app builds at an\n * attacker's host. Turn it on when you terminate TLS or rewrite `Host` at a reverse proxy.\n *\n * Middleware in `src/server.ts` reads Hono's `c.req.url` the *internal* address whatever this\n * says, so give `csrf()` the public origin explicitly.\n *\n * **Compiled into the server bundle**, not read at runtime, so it takes a rebuild to change and there is\n * no environment variable for it: one artifact cannot be promoted from a direct-exposure staging box to a\n * proxied production one.\n *\n * **The `vercel` target is the exception, for the scheme only.** TLS terminates at that platform's edge\n * and the function is reached over plain HTTP, so the request is rebuilt from `X-Forwarded-Proto`\n * whatever this says the function is reachable only through the edge, which sets the header on every\n * request, so it is not client-supplied there. The `Host` header is not part of the exception, and is no\n * more trusted on that target than on any other.\n *\n * @example\n * ```ts\n * export default defineConfig({ trustProxy: true });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/configuration#proxy-headers | Docs — proxy headers}\n */\n trustProxy?: boolean;\n /**\n * Escape hatch: mutate the generated Rspack config just before it's compiled. Called once per\n * compiler inspect {@link RspackHookContext.isServer} to tell them apart. Mutate `config` in\n * place and return nothing, or return a replacement.\n *\n * @example\n * ```ts\n * rspack(config, { isServer }) {\n * config.module?.rules?.push({ test: /\\.svg$/, type: 'asset/source' });\n * }\n * ```\n *\n * @see {@link https://rspack.rs/config/ | Rspack configuration reference}\n * @see {@link https://www.rshono.com/docs/configuration#the-rspack-hook | Docs the rspack hook}\n */\n rspack?: (config: RspackOptions, ctx: RspackHookContext) => RspackOptions | void;\n}\n\n/**\n * Types a config object for editor autocomplete, without an explicit annotation. Default-export the\n * result from `rshono.config.ts`.\n *\n * @param config - The project's {@link RshonoConfig}; every field is optional.\n * @returns The config, unchanged and fully typed.\n *\n * @example\n * ```ts\n * // rshono.config.ts\n * import { defineConfig } from '@rshono/core';\n *\n * export default defineConfig({ deploy: 'cloudflare', siteUrl: 'https://example.com' });\n * ```\n *\n * @see {@link https://www.rshono.com/docs/configuration | Docsconfiguration}\n */\nexport function defineConfig(config: RshonoConfig): RshonoConfig {\n return config;\n}\n"]}
@@ -2,13 +2,10 @@ import type { DeployRuntime } from '../contract.js';
2
2
  /**
3
3
  * AWS Lambda behind a Function URL, in streaming mode.
4
4
  *
5
- * `streamHandle` wraps the app with `awslambda.streamifyResponse`, which is the only way a Lambda can
6
- * write a response progressively — and progressive is the whole point of a streamed SSR shell. It
7
- * requires the Function URL's invoke mode to be `RESPONSE_STREAM`; the buffered `handle` would work
8
- * anywhere but would hold every page until the last byte rendered.
5
+ * `streamHandle` wraps the app with `awslambda.streamifyResponse`, the only way a Lambda writes a response
6
+ * progressively — which is the whole point of a streamed SSR shell. It requires the Function URL's invoke mode
7
+ * to be `RESPONSE_STREAM`; the buffered `handle` would work anywhere but hold every page until its last byte.
9
8
  *
10
- * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only
11
- * disk, so `dist/static`, `dist/public` and `dist/ssg` are read exactly as they are on a server.
9
+ * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only disk.
12
10
  */
13
11
  export declare const runtime: DeployRuntime;
14
- //# sourceMappingURL=runtime.d.ts.map
@@ -3,19 +3,16 @@ import { fileSystemRuntime } from '../filesystem.js';
3
3
  /**
4
4
  * AWS Lambda behind a Function URL, in streaming mode.
5
5
  *
6
- * `streamHandle` wraps the app with `awslambda.streamifyResponse`, which is the only way a Lambda can
7
- * write a response progressively — and progressive is the whole point of a streamed SSR shell. It
8
- * requires the Function URL's invoke mode to be `RESPONSE_STREAM`; the buffered `handle` would work
9
- * anywhere but would hold every page until the last byte rendered.
6
+ * `streamHandle` wraps the app with `awslambda.streamifyResponse`, the only way a Lambda writes a response
7
+ * progressively — which is the whole point of a streamed SSR shell. It requires the Function URL's invoke mode
8
+ * to be `RESPONSE_STREAM`; the buffered `handle` would work anywhere but hold every page until its last byte.
10
9
  *
11
- * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only
12
- * disk, so `dist/static`, `dist/public` and `dist/ssg` are read exactly as they are on a server.
10
+ * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only disk.
13
11
  */
14
12
  export const runtime = {
15
13
  ...fileSystemRuntime,
16
14
  serveApp(app) {
17
- // `awslambda` is a global the Lambda runtime injects, so it is absent when the build imports this
18
- // bundle to prerender. Returning nothing then keeps that pass working; a real invocation has it.
15
+ // A global the Lambda runtime injects, so it is absent when the build imports this bundle to prerender.
19
16
  if (typeof globalThis.awslambda === 'undefined')
20
17
  return undefined;
21
18
  return streamHandle(app);
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/aws-lambda/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,GAAG,iBAAiB;IAEpB,QAAQ,CAAC,GAAS;QAChB,kGAAkG;QAClG,iGAAiG;QACjG,IAAI,OAAQ,UAAsC,CAAC,SAAS,KAAK,WAAW;YAAE,OAAO,SAAS,CAAC;QAC/F,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF,CAAC","sourcesContent":["import type { Hono } from 'hono';\nimport { streamHandle } from 'hono/aws-lambda';\nimport type { DeployRuntime } from '../contract.js';\nimport { fileSystemRuntime } from '../filesystem.js';\n\n/**\n * AWS Lambda behind a Function URL, in streaming mode.\n *\n * `streamHandle` wraps the app with `awslambda.streamifyResponse`, which is the only way a Lambda can\n * write a response progressively — and progressive is the whole point of a streamed SSR shell. It\n * requires the Function URL's invoke mode to be `RESPONSE_STREAM`; the buffered `handle` would work\n * anywhere but would hold every page until the last byte rendered.\n *\n * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only\n * disk, so `dist/static`, `dist/public` and `dist/ssg` are read exactly as they are on a server.\n */\nexport const runtime: DeployRuntime = {\n ...fileSystemRuntime,\n\n serveApp(app: Hono): unknown {\n // `awslambda` is a global the Lambda runtime injects, so it is absent when the build imports this\n // bundle to prerender. Returning nothing then keeps that pass working; a real invocation has it.\n if (typeof (globalThis as { awslambda?: unknown }).awslambda === 'undefined') return undefined;\n return streamHandle(app);\n },\n};\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/aws-lambda/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,GAAG,iBAAiB;IAEpB,QAAQ,CAAC,GAAS;QAChB,wGAAwG;QACxG,IAAI,OAAQ,UAAsC,CAAC,SAAS,KAAK,WAAW;YAAE,OAAO,SAAS,CAAC;QAC/F,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF,CAAC","sourcesContent":["import type { Hono } from 'hono';\nimport { streamHandle } from 'hono/aws-lambda';\nimport type { DeployRuntime } from '../contract.js';\nimport { fileSystemRuntime } from '../filesystem.js';\n\n/**\n * AWS Lambda behind a Function URL, in streaming mode.\n *\n * `streamHandle` wraps the app with `awslambda.streamifyResponse`, the only way a Lambda writes a response\n * progressively — which is the whole point of a streamed SSR shell. It requires the Function URL's invoke mode\n * to be `RESPONSE_STREAM`; the buffered `handle` would work anywhere but hold every page until its last byte.\n *\n * The filesystem capabilities are Node's: a Lambda unpacks the deployment package onto a read-only disk.\n */\nexport const runtime: DeployRuntime = {\n ...fileSystemRuntime,\n\n serveApp(app: Hono): unknown {\n // A global the Lambda runtime injects, so it is absent when the build imports this bundle to prerender.\n if (typeof (globalThis as { awslambda?: unknown }).awslambda === 'undefined') return undefined;\n return streamHandle(app);\n },\n};\n"]}