@rshono/core 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +273 -0
  2. package/bin/rshono.mjs +15 -0
  3. package/dist/builder/env-shadow-loader.cjs +26 -0
  4. package/dist/builder/page-entry-loader.cjs +7 -0
  5. package/dist/builder/page-files.d.ts +2 -0
  6. package/dist/builder/page-files.d.ts.map +1 -0
  7. package/dist/builder/page-files.js +45 -0
  8. package/dist/builder/page-files.js.map +1 -0
  9. package/dist/builder/public-env.d.ts +2 -0
  10. package/dist/builder/public-env.d.ts.map +1 -0
  11. package/dist/builder/public-env.js +5 -0
  12. package/dist/builder/public-env.js.map +1 -0
  13. package/dist/builder/rspack-config.d.ts +14 -0
  14. package/dist/builder/rspack-config.d.ts.map +1 -0
  15. package/dist/builder/rspack-config.js +235 -0
  16. package/dist/builder/rspack-config.js.map +1 -0
  17. package/dist/cli/build.d.ts +11 -0
  18. package/dist/cli/build.d.ts.map +1 -0
  19. package/dist/cli/build.js +64 -0
  20. package/dist/cli/build.js.map +1 -0
  21. package/dist/cli/dev.d.ts +9 -0
  22. package/dist/cli/dev.d.ts.map +1 -0
  23. package/dist/cli/dev.js +210 -0
  24. package/dist/cli/dev.js.map +1 -0
  25. package/dist/cli/index.d.ts +2 -0
  26. package/dist/cli/index.d.ts.map +1 -0
  27. package/dist/cli/index.js +77 -0
  28. package/dist/cli/index.js.map +1 -0
  29. package/dist/cli/start.d.ts +8 -0
  30. package/dist/cli/start.d.ts.map +1 -0
  31. package/dist/cli/start.js +39 -0
  32. package/dist/cli/start.js.map +1 -0
  33. package/dist/config.d.ts +121 -0
  34. package/dist/config.d.ts.map +1 -0
  35. package/dist/config.js +5 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/deploy/aws-lambda/runtime.d.ts +14 -0
  38. package/dist/deploy/aws-lambda/runtime.d.ts.map +1 -0
  39. package/dist/deploy/aws-lambda/runtime.js +24 -0
  40. package/dist/deploy/aws-lambda/runtime.js.map +1 -0
  41. package/dist/deploy/build-marker.d.ts +15 -0
  42. package/dist/deploy/build-marker.d.ts.map +1 -0
  43. package/dist/deploy/build-marker.js +26 -0
  44. package/dist/deploy/build-marker.js.map +1 -0
  45. package/dist/deploy/bun/runtime.d.ts +11 -0
  46. package/dist/deploy/bun/runtime.d.ts.map +1 -0
  47. package/dist/deploy/bun/runtime.js +22 -0
  48. package/dist/deploy/bun/runtime.js.map +1 -0
  49. package/dist/deploy/cloudflare/build.d.ts +7 -0
  50. package/dist/deploy/cloudflare/build.d.ts.map +1 -0
  51. package/dist/deploy/cloudflare/build.js +74 -0
  52. package/dist/deploy/cloudflare/build.js.map +1 -0
  53. package/dist/deploy/cloudflare/runtime.d.ts +10 -0
  54. package/dist/deploy/cloudflare/runtime.d.ts.map +1 -0
  55. package/dist/deploy/cloudflare/runtime.js +129 -0
  56. package/dist/deploy/cloudflare/runtime.js.map +1 -0
  57. package/dist/deploy/contract.d.ts +65 -0
  58. package/dist/deploy/contract.d.ts.map +1 -0
  59. package/dist/deploy/contract.js +2 -0
  60. package/dist/deploy/contract.js.map +1 -0
  61. package/dist/deploy/deno/runtime.d.ts +11 -0
  62. package/dist/deploy/deno/runtime.d.ts.map +1 -0
  63. package/dist/deploy/deno/runtime.js +16 -0
  64. package/dist/deploy/deno/runtime.js.map +1 -0
  65. package/dist/deploy/filesystem.d.ts +10 -0
  66. package/dist/deploy/filesystem.d.ts.map +1 -0
  67. package/dist/deploy/filesystem.js +47 -0
  68. package/dist/deploy/filesystem.js.map +1 -0
  69. package/dist/deploy/listen.d.ts +20 -0
  70. package/dist/deploy/listen.d.ts.map +1 -0
  71. package/dist/deploy/listen.js +24 -0
  72. package/dist/deploy/listen.js.map +1 -0
  73. package/dist/deploy/netlify/build.d.ts +8 -0
  74. package/dist/deploy/netlify/build.d.ts.map +1 -0
  75. package/dist/deploy/netlify/build.js +52 -0
  76. package/dist/deploy/netlify/build.js.map +1 -0
  77. package/dist/deploy/netlify/runtime.d.ts +13 -0
  78. package/dist/deploy/netlify/runtime.d.ts.map +1 -0
  79. package/dist/deploy/netlify/runtime.js +24 -0
  80. package/dist/deploy/netlify/runtime.js.map +1 -0
  81. package/dist/deploy/node/runtime.d.ts +7 -0
  82. package/dist/deploy/node/runtime.d.ts.map +1 -0
  83. package/dist/deploy/node/runtime.js +36 -0
  84. package/dist/deploy/node/runtime.js.map +1 -0
  85. package/dist/deploy/presets.d.ts +88 -0
  86. package/dist/deploy/presets.d.ts.map +1 -0
  87. package/dist/deploy/presets.js +110 -0
  88. package/dist/deploy/presets.js.map +1 -0
  89. package/dist/deploy/vercel/build.d.ts +3 -0
  90. package/dist/deploy/vercel/build.d.ts.map +1 -0
  91. package/dist/deploy/vercel/build.js +56 -0
  92. package/dist/deploy/vercel/build.js.map +1 -0
  93. package/dist/deploy/vercel/runtime.d.ts +14 -0
  94. package/dist/deploy/vercel/runtime.d.ts.map +1 -0
  95. package/dist/deploy/vercel/runtime.js +26 -0
  96. package/dist/deploy/vercel/runtime.js.map +1 -0
  97. package/dist/index.d.ts +23 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +17 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/router.d.ts +337 -0
  102. package/dist/router.d.ts.map +1 -0
  103. package/dist/router.js +18 -0
  104. package/dist/router.js.map +1 -0
  105. package/dist/runtime/boundaries.d.ts +102 -0
  106. package/dist/runtime/boundaries.d.ts.map +1 -0
  107. package/dist/runtime/boundaries.js +101 -0
  108. package/dist/runtime/boundaries.js.map +1 -0
  109. package/dist/runtime/client.d.ts +16 -0
  110. package/dist/runtime/client.d.ts.map +1 -0
  111. package/dist/runtime/client.js +16 -0
  112. package/dist/runtime/client.js.map +1 -0
  113. package/dist/runtime/context.d.ts +271 -0
  114. package/dist/runtime/context.d.ts.map +1 -0
  115. package/dist/runtime/context.js +360 -0
  116. package/dist/runtime/context.js.map +1 -0
  117. package/dist/runtime/control.d.ts +18 -0
  118. package/dist/runtime/control.d.ts.map +1 -0
  119. package/dist/runtime/control.js +39 -0
  120. package/dist/runtime/control.js.map +1 -0
  121. package/dist/runtime/dev-protocol.d.ts +22 -0
  122. package/dist/runtime/dev-protocol.d.ts.map +1 -0
  123. package/dist/runtime/dev-protocol.js +2 -0
  124. package/dist/runtime/dev-protocol.js.map +1 -0
  125. package/dist/runtime/empty-server-app.d.ts +3 -0
  126. package/dist/runtime/empty-server-app.d.ts.map +1 -0
  127. package/dist/runtime/empty-server-app.js +2 -0
  128. package/dist/runtime/empty-server-app.js.map +1 -0
  129. package/dist/runtime/entry.client.d.ts +2 -0
  130. package/dist/runtime/entry.client.d.ts.map +1 -0
  131. package/dist/runtime/entry.client.js +453 -0
  132. package/dist/runtime/entry.client.js.map +1 -0
  133. package/dist/runtime/entry.rsc.d.ts +32 -0
  134. package/dist/runtime/entry.rsc.d.ts.map +1 -0
  135. package/dist/runtime/entry.rsc.js +466 -0
  136. package/dist/runtime/entry.rsc.js.map +1 -0
  137. package/dist/runtime/entry.ssr.d.ts +25 -0
  138. package/dist/runtime/entry.ssr.d.ts.map +1 -0
  139. package/dist/runtime/entry.ssr.js +86 -0
  140. package/dist/runtime/entry.ssr.js.map +1 -0
  141. package/dist/runtime/navigation.d.ts +114 -0
  142. package/dist/runtime/navigation.d.ts.map +1 -0
  143. package/dist/runtime/navigation.js +112 -0
  144. package/dist/runtime/navigation.js.map +1 -0
  145. package/dist/runtime/request.d.ts +35 -0
  146. package/dist/runtime/request.d.ts.map +1 -0
  147. package/dist/runtime/request.js +39 -0
  148. package/dist/runtime/request.js.map +1 -0
  149. package/dist/runtime/server.d.ts +24 -0
  150. package/dist/runtime/server.d.ts.map +1 -0
  151. package/dist/runtime/server.js +24 -0
  152. package/dist/runtime/server.js.map +1 -0
  153. package/dist/server/compress.d.ts +15 -0
  154. package/dist/server/compress.d.ts.map +1 -0
  155. package/dist/server/compress.js +76 -0
  156. package/dist/server/compress.js.map +1 -0
  157. package/dist/server/headers.d.ts +24 -0
  158. package/dist/server/headers.d.ts.map +1 -0
  159. package/dist/server/headers.js +44 -0
  160. package/dist/server/headers.js.map +1 -0
  161. package/dist/server/load-config.d.ts +7 -0
  162. package/dist/server/load-config.d.ts.map +1 -0
  163. package/dist/server/load-config.js +41 -0
  164. package/dist/server/load-config.js.map +1 -0
  165. package/dist/server/load-env.d.ts +2 -0
  166. package/dist/server/load-env.d.ts.map +1 -0
  167. package/dist/server/load-env.js +10 -0
  168. package/dist/server/load-env.js.map +1 -0
  169. package/dist/server/prerendered.d.ts +67 -0
  170. package/dist/server/prerendered.d.ts.map +1 -0
  171. package/dist/server/prerendered.js +39 -0
  172. package/dist/server/prerendered.js.map +1 -0
  173. package/dist/server/server-config.d.ts +72 -0
  174. package/dist/server/server-config.d.ts.map +1 -0
  175. package/dist/server/server-config.js +94 -0
  176. package/dist/server/server-config.js.map +1 -0
  177. package/dist/server/shutdown.d.ts +7 -0
  178. package/dist/server/shutdown.d.ts.map +1 -0
  179. package/dist/server/shutdown.js +11 -0
  180. package/dist/server/shutdown.js.map +1 -0
  181. package/dist/server/ssg.d.ts +25 -0
  182. package/dist/server/ssg.d.ts.map +1 -0
  183. package/dist/server/ssg.js +160 -0
  184. package/dist/server/ssg.js.map +1 -0
  185. package/dist/server/static.d.ts +14 -0
  186. package/dist/server/static.d.ts.map +1 -0
  187. package/dist/server/static.js +30 -0
  188. package/dist/server/static.js.map +1 -0
  189. package/package.json +86 -0
@@ -0,0 +1,129 @@
1
+ import { prerenderedRelPath } from '../../server/prerendered.js';
2
+ /**
3
+ * Where `finalize` puts the prerendered pages inside the assets directory.
4
+ *
5
+ * A prefix of its own, rather than the pages' real URLs, is what keeps every page URL reaching the
6
+ * worker: one URL answers with a document or a flight payload depending on `Accept`, and a CDN keyed
7
+ * on the path alone cannot make that choice. Assets miss, the worker runs, the worker negotiates.
8
+ *
9
+ * The bytes are public either way — they are a public page — but they are also reachable under this
10
+ * path, so `finalize` writes a `_headers` rule marking the tree `noindex` rather than leaving a
11
+ * crawler to find the same page twice.
12
+ */
13
+ const SSG_PREFIX = '/__ssg';
14
+ function assetsBinding(c) {
15
+ const binding = c.env?.ASSETS;
16
+ return binding && typeof binding.fetch === 'function' ? binding : null;
17
+ }
18
+ /**
19
+ * Asks the assets binding for one path, or `null` when there is no binding at all — so a caller can
20
+ * tell "this deployment serves its assets some other way" from "that file is not there".
21
+ *
22
+ * `headers` is passed on only where the *client's* conditional and range requests should be answered
23
+ * by the store: forwarding an `If-None-Match` while fetching a page to serve would come back 304 with
24
+ * no body, which is a useless answer to "give me this page".
25
+ */
26
+ async function assetResponse(c, path, headers) {
27
+ const binding = assetsBinding(c);
28
+ if (!binding)
29
+ return null;
30
+ // Resolved against the request so the asset request carries this deployment's own origin; only the
31
+ // path is ever used to look one up.
32
+ const url = new URL(path, c.req.url);
33
+ return binding.fetch(new Request(url, { method: 'GET', headers }));
34
+ }
35
+ /**
36
+ * Hands an asset response back as the app's own.
37
+ *
38
+ * Rebuilt rather than returned as-is because a `Response` that came from `fetch` has immutable
39
+ * headers, and the framework's outermost middleware sets its baseline security headers on the way out.
40
+ */
41
+ function serveAsset(asset) {
42
+ return new Response(asset.body, asset);
43
+ }
44
+ /**
45
+ * A weak `ETag` for a body the asset store gave us no validator for.
46
+ *
47
+ * Web Crypto rather than `node:crypto`: this module is compiled for `workerd`, and the digest is the
48
+ * one thing here that has a platform-neutral form.
49
+ */
50
+ async function weakEtag(body) {
51
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(body));
52
+ const bytes = String.fromCharCode(...new Uint8Array(digest));
53
+ const base64url = btoa(bytes).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
54
+ return `W/"${base64url.slice(0, 22)}"`;
55
+ }
56
+ /**
57
+ * Prerendered pages, keyed by the path that produced them.
58
+ *
59
+ * Per isolate rather than per process, and bounded for the same reason the Node cache is: a site with
60
+ * thousands of prerendered pages should keep a working set, not the whole build. Only *hits* are
61
+ * cached, so nobody can mint entries by requesting paths that do not exist.
62
+ */
63
+ const pageCache = new Map();
64
+ const MAX_CACHED_PAGES = 128;
65
+ /**
66
+ * Cloudflare Workers: the host owns the process, so there is nothing to listen on; the CDN owns the
67
+ * assets, so there is nothing to serve from disk; and there is no filesystem, so `.env` files and a
68
+ * streaming gzip are both out.
69
+ *
70
+ * What is left is the assets binding, which every read here goes through.
71
+ */
72
+ export const runtime = {
73
+ serveApp(app) {
74
+ // What Workers looks for on the default export. `app.fetch` already takes `(request, env, ctx)`,
75
+ // which is why bindings arrive as `c.env` — and so why `getContext().env` sees them.
76
+ return { fetch: app.fetch };
77
+ },
78
+ mountStaticAssets(app) {
79
+ // Normally dead: with the scaffolded config the CDN answers `/_static/*` before the worker is
80
+ // invoked at all. It exists so the deployment is still correct — if slower — under an assets
81
+ // configuration that routes everything to the worker first.
82
+ app.on(['GET', 'HEAD'], '/_static/*', async (c, next) => {
83
+ const asset = await assetResponse(c, c.req.path, c.req.raw.headers);
84
+ return asset && asset.status !== 404 ? serveAsset(asset) : next();
85
+ });
86
+ },
87
+ mountPublicFallback(app) {
88
+ app.on(['GET', 'HEAD'], '/*', async (c, next) => {
89
+ // The prerender tree lives in the same store; it is reachable directly from the CDN by design,
90
+ // but the app itself only ever serves it through `readPrerendered`, negotiated on `Accept`.
91
+ if (c.req.path.startsWith(`${SSG_PREFIX}/`))
92
+ return next();
93
+ const asset = await assetResponse(c, c.req.path, c.req.raw.headers);
94
+ return asset && asset.status !== 404 ? serveAsset(asset) : next();
95
+ });
96
+ },
97
+ async readPrerendered(c, variant) {
98
+ const relPath = prerenderedRelPath(c.req.path, variant);
99
+ if (relPath === null)
100
+ return null;
101
+ const key = `${variant}\0${relPath}`;
102
+ const cached = pageCache.get(key);
103
+ if (cached)
104
+ return cached;
105
+ const asset = await assetResponse(c, `${SSG_PREFIX}/${relPath}`);
106
+ if (!asset || asset.status !== 200)
107
+ return null;
108
+ const body = await asset.text();
109
+ const page = {
110
+ body,
111
+ contentLength: String(new TextEncoder().encode(body).byteLength),
112
+ // The store's own validator where it has one — it already describes these exact bytes. Weakened
113
+ // because compression downstream would make a strong one wrong for the compressed encoding.
114
+ etag: asset.headers.get('etag')?.replace(/^(?!W\/)/, 'W/') ?? (await weakEtag(body)),
115
+ };
116
+ pageCache.set(key, page);
117
+ if (pageCache.size > MAX_CACHED_PAGES)
118
+ pageCache.delete(pageCache.keys().next().value);
119
+ return page;
120
+ },
121
+ // Cloudflare compresses on the way out at the edge, and `workerd` has no `node:zlib` stream to do
122
+ // it with anyway. Doing it here would only spend CPU on bytes the edge is about to re-encode.
123
+ compress: null,
124
+ loadEnv() {
125
+ // Nothing to load: a Worker has no filesystem and no `.env`. Secrets and bindings arrive per
126
+ // request as `c.env`, which `getContext().env` already merges — see `runtime/context.ts`.
127
+ },
128
+ };
129
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/cloudflare/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Hono } from 'hono';\nimport { prerenderedRelPath, type PrerenderedPage } from '../../server/prerendered.js';\nimport type { DeployRuntime } from '../contract.js';\n\n/**\n * Where `finalize` puts the prerendered pages inside the assets directory.\n *\n * A prefix of its own, rather than the pages' real URLs, is what keeps every page URL reaching the\n * worker: one URL answers with a document or a flight payload depending on `Accept`, and a CDN keyed\n * on the path alone cannot make that choice. Assets miss, the worker runs, the worker negotiates.\n *\n * The bytes are public either way — they are a public page — but they are also reachable under this\n * path, so `finalize` writes a `_headers` rule marking the tree `noindex` rather than leaving a\n * crawler to find the same page twice.\n */\nconst SSG_PREFIX = '/__ssg';\n\n/** The Workers Assets binding, as much of it as this file uses. */\ninterface AssetsBinding {\n fetch(request: Request): Promise<Response>;\n}\n\nfunction assetsBinding(c: Context): AssetsBinding | null {\n const binding = (c.env as { ASSETS?: AssetsBinding } | undefined)?.ASSETS;\n return binding && typeof binding.fetch === 'function' ? binding : null;\n}\n\n/**\n * Asks the assets binding for one path, or `null` when there is no binding at all — so a caller can\n * tell \"this deployment serves its assets some other way\" from \"that file is not there\".\n *\n * `headers` is passed on only where the *client's* conditional and range requests should be answered\n * by the store: forwarding an `If-None-Match` while fetching a page to serve would come back 304 with\n * no body, which is a useless answer to \"give me this page\".\n */\nasync function assetResponse(c: Context, path: string, headers?: Headers): Promise<Response | null> {\n const binding = assetsBinding(c);\n if (!binding) return null;\n // Resolved against the request so the asset request carries this deployment's own origin; only the\n // path is ever used to look one up.\n const url = new URL(path, c.req.url);\n return binding.fetch(new Request(url, { method: 'GET', headers }));\n}\n\n/**\n * Hands an asset response back as the app's own.\n *\n * Rebuilt rather than returned as-is because a `Response` that came from `fetch` has immutable\n * headers, and the framework's outermost middleware sets its baseline security headers on the way out.\n */\nfunction serveAsset(asset: Response): Response {\n return new Response(asset.body, asset);\n}\n\n/**\n * A weak `ETag` for a body the asset store gave us no validator for.\n *\n * Web Crypto rather than `node:crypto`: this module is compiled for `workerd`, and the digest is the\n * one thing here that has a platform-neutral form.\n */\nasync function weakEtag(body: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(body));\n const bytes = String.fromCharCode(...new Uint8Array(digest));\n const base64url = btoa(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n return `W/\"${base64url.slice(0, 22)}\"`;\n}\n\n/**\n * Prerendered pages, keyed by the path that produced them.\n *\n * Per isolate rather than per process, and bounded for the same reason the Node cache is: a site with\n * thousands of prerendered pages should keep a working set, not the whole build. Only *hits* are\n * cached, so nobody can mint entries by requesting paths that do not exist.\n */\nconst pageCache = new Map<string, PrerenderedPage>();\nconst MAX_CACHED_PAGES = 128;\n\n/**\n * Cloudflare Workers: the host owns the process, so there is nothing to listen on; the CDN owns the\n * assets, so there is nothing to serve from disk; and there is no filesystem, so `.env` files and a\n * streaming gzip are both out.\n *\n * What is left is the assets binding, which every read here goes through.\n */\nexport const runtime: DeployRuntime = {\n serveApp(app: Hono): unknown {\n // What Workers looks for on the default export. `app.fetch` already takes `(request, env, ctx)`,\n // which is why bindings arrive as `c.env` — and so why `getContext().env` sees them.\n return { fetch: app.fetch };\n },\n\n mountStaticAssets(app: Hono): void {\n // Normally dead: with the scaffolded config the CDN answers `/_static/*` before the worker is\n // invoked at all. It exists so the deployment is still correct — if slower — under an assets\n // configuration that routes everything to the worker first.\n app.on(['GET', 'HEAD'], '/_static/*', async (c, next) => {\n const asset = await assetResponse(c, c.req.path, c.req.raw.headers);\n return asset && asset.status !== 404 ? serveAsset(asset) : next();\n });\n },\n\n mountPublicFallback(app: Hono): void {\n app.on(['GET', 'HEAD'], '/*', async (c, next) => {\n // The prerender tree lives in the same store; it is reachable directly from the CDN by design,\n // but the app itself only ever serves it through `readPrerendered`, negotiated on `Accept`.\n if (c.req.path.startsWith(`${SSG_PREFIX}/`)) return next();\n const asset = await assetResponse(c, c.req.path, c.req.raw.headers);\n return asset && asset.status !== 404 ? serveAsset(asset) : next();\n });\n },\n\n async readPrerendered(c: Context, variant): Promise<PrerenderedPage | null> {\n const relPath = prerenderedRelPath(c.req.path, variant);\n if (relPath === null) return null;\n\n const key = `${variant}\\0${relPath}`;\n const cached = pageCache.get(key);\n if (cached) return cached;\n\n const asset = await assetResponse(c, `${SSG_PREFIX}/${relPath}`);\n if (!asset || asset.status !== 200) return null;\n\n const body = await asset.text();\n const page: PrerenderedPage = {\n body,\n contentLength: String(new TextEncoder().encode(body).byteLength),\n // The store's own validator where it has one — it already describes these exact bytes. Weakened\n // because compression downstream would make a strong one wrong for the compressed encoding.\n etag: asset.headers.get('etag')?.replace(/^(?!W\\/)/, 'W/') ?? (await weakEtag(body)),\n };\n\n pageCache.set(key, page);\n if (pageCache.size > MAX_CACHED_PAGES) pageCache.delete(pageCache.keys().next().value!);\n return page;\n },\n\n // Cloudflare compresses on the way out at the edge, and `workerd` has no `node:zlib` stream to do\n // it with anyway. Doing it here would only spend CPU on bytes the edge is about to re-encode.\n compress: null,\n\n loadEnv(): void {\n // Nothing to load: a Worker has no filesystem and no `.env`. Secrets and bindings arrive per\n // request as `c.env`, which `getContext().env` already merges — see `runtime/context.ts`.\n },\n};\n"]}
@@ -0,0 +1,65 @@
1
+ import type { Context, Hono, MiddlewareHandler } from 'hono';
2
+ import type { PrerenderVariant, PrerenderedPage } from '../server/prerendered.js';
3
+ /**
4
+ * A hosting platform rshono can build for. Selected with {@link RSHonoConfig.deploy}, the
5
+ * `--deploy` flag or the `RSHONO_DEPLOY` env var, and resolved to a preset by `deploy/presets.ts`.
6
+ *
7
+ * `rshono dev` always runs the `node` server whatever this says — the dev server owns the process,
8
+ * watches both compilers and fronts them on one port, none of which a hosting platform provides.
9
+ */
10
+ export type DeployTarget = 'node' | 'cloudflare' | 'bun' | 'deno' | 'vercel' | 'netlify' | 'aws-lambda';
11
+ /**
12
+ * Everything the app server needs from the platform it is running on.
13
+ *
14
+ * One preset implements this per target, and the `@rshono/deploy` alias resolves to exactly that
15
+ * module at build time (see `builder/rspack-config.ts`), so only the selected platform's code is
16
+ * ever in the bundle. `runtime/entry.rsc.tsx` is written against this interface and nothing else —
17
+ * it is the whole of what "which platform is this" means at request time.
18
+ *
19
+ * The split is deliberately by *capability*, not by runtime: a platform with a filesystem reuses the
20
+ * implementations in `server/` (`static.ts`, `compress.ts`, `ssg.ts`) and only differs in how the
21
+ * finished app is handed over.
22
+ */
23
+ export interface DeployRuntime {
24
+ /**
25
+ * Hands the assembled app to the platform, and returns whatever the entry module should
26
+ * `export default` there.
27
+ *
28
+ * The two shapes hosting takes, in one call: where rshono owns the process (node, bun, deno) this
29
+ * binds a port and returns nothing; where the host owns it, it returns the export the platform
30
+ * looks for — `{ fetch }` on Workers, a handler function on Vercel/Netlify/Lambda.
31
+ */
32
+ serveApp(app: Hono): unknown;
33
+ /**
34
+ * Mounts the hashed client bundle at `/_static`. A no-op where the platform's own CDN serves it
35
+ * before a request ever reaches the app.
36
+ *
37
+ * Called *before* the app's routes, so the bundle is never shadowed by one.
38
+ */
39
+ mountStaticAssets(app: Hono): void;
40
+ /**
41
+ * Mounts the `public/` fallback at the web root — files served verbatim, and only for paths no
42
+ * route claimed. Called *after* every route for exactly that reason.
43
+ */
44
+ mountPublicFallback(app: Hono): void;
45
+ /**
46
+ * Reads the page prerendered for `c.req.path` by `rshono build`, or `null` when there is none (in
47
+ * which case the route renders per request, which is always a valid answer).
48
+ *
49
+ * Takes the whole {@link Context} rather than just the path because on a platform with no
50
+ * filesystem the store *is* a request-scoped binding — `c.env.ASSETS` on Workers. The path is
51
+ * untrusted either way, so an implementation has to treat traversal as a miss, not a lookup
52
+ * (see `prerenderedRelPath`).
53
+ */
54
+ readPrerendered(c: Context, variant: PrerenderVariant): Promise<PrerenderedPage | null>;
55
+ /**
56
+ * Response compression, or `null` where the platform already compresses on the way out (and
57
+ * doing it twice would only cost CPU) or cannot stream a compressor at all.
58
+ *
59
+ * Whether it is *used* is still the app's `compress` setting; this is only whether it is available.
60
+ */
61
+ compress: MiddlewareHandler | null;
62
+ /** Loads `.env` files, where the platform has a filesystem to read them from. Env is bindings elsewhere. */
63
+ loadEnv(): void;
64
+ }
65
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/deploy/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAExG;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC;;;OAGG;IACH,mBAAmB,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACrC;;;;;;;;OAQG;IACH,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACxF;;;;;OAKG;IACH,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,4GAA4G;IAC5G,OAAO,IAAI,IAAI,CAAC;CACjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/deploy/contract.ts"],"names":[],"mappings":"","sourcesContent":["import type { Context, Hono, MiddlewareHandler } from 'hono';\nimport type { PrerenderVariant, PrerenderedPage } from '../server/prerendered.js';\n\n/**\n * A hosting platform rshono can build for. Selected with {@link RSHonoConfig.deploy}, the\n * `--deploy` flag or the `RSHONO_DEPLOY` env var, and resolved to a preset by `deploy/presets.ts`.\n *\n * `rshono dev` always runs the `node` server whatever this says — the dev server owns the process,\n * watches both compilers and fronts them on one port, none of which a hosting platform provides.\n */\nexport type DeployTarget = 'node' | 'cloudflare' | 'bun' | 'deno' | 'vercel' | 'netlify' | 'aws-lambda';\n\n/**\n * Everything the app server needs from the platform it is running on.\n *\n * One preset implements this per target, and the `@rshono/deploy` alias resolves to exactly that\n * module at build time (see `builder/rspack-config.ts`), so only the selected platform's code is\n * ever in the bundle. `runtime/entry.rsc.tsx` is written against this interface and nothing else —\n * it is the whole of what \"which platform is this\" means at request time.\n *\n * The split is deliberately by *capability*, not by runtime: a platform with a filesystem reuses the\n * implementations in `server/` (`static.ts`, `compress.ts`, `ssg.ts`) and only differs in how the\n * finished app is handed over.\n */\nexport interface DeployRuntime {\n /**\n * Hands the assembled app to the platform, and returns whatever the entry module should\n * `export default` there.\n *\n * The two shapes hosting takes, in one call: where rshono owns the process (node, bun, deno) this\n * binds a port and returns nothing; where the host owns it, it returns the export the platform\n * looks for — `{ fetch }` on Workers, a handler function on Vercel/Netlify/Lambda.\n */\n serveApp(app: Hono): unknown;\n /**\n * Mounts the hashed client bundle at `/_static`. A no-op where the platform's own CDN serves it\n * before a request ever reaches the app.\n *\n * Called *before* the app's routes, so the bundle is never shadowed by one.\n */\n mountStaticAssets(app: Hono): void;\n /**\n * Mounts the `public/` fallback at the web root — files served verbatim, and only for paths no\n * route claimed. Called *after* every route for exactly that reason.\n */\n mountPublicFallback(app: Hono): void;\n /**\n * Reads the page prerendered for `c.req.path` by `rshono build`, or `null` when there is none (in\n * which case the route renders per request, which is always a valid answer).\n *\n * Takes the whole {@link Context} rather than just the path because on a platform with no\n * filesystem the store *is* a request-scoped binding — `c.env.ASSETS` on Workers. The path is\n * untrusted either way, so an implementation has to treat traversal as a miss, not a lookup\n * (see `prerenderedRelPath`).\n */\n readPrerendered(c: Context, variant: PrerenderVariant): Promise<PrerenderedPage | null>;\n /**\n * Response compression, or `null` where the platform already compresses on the way out (and\n * doing it twice would only cost CPU) or cannot stream a compressor at all.\n *\n * Whether it is *used* is still the app's `compress` setting; this is only whether it is available.\n */\n compress: MiddlewareHandler | null;\n /** Loads `.env` files, where the platform has a filesystem to read them from. Env is bindings elsewhere. */\n loadEnv(): void;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { DeployRuntime } from '../contract.js';
2
+ /**
3
+ * Deno: `deno serve` and Deno Deploy both look for a `fetch` on the default export, so the handoff is
4
+ * an export rather than a listener — which is also what makes one build work for both.
5
+ *
6
+ * The compiler settings stay Node's deliberately. Deno's `node:` compatibility covers everything
7
+ * `server/` uses, so the Node builds of React and the RSC runtime run as-is, and the alternative would
8
+ * be a second bundle shape to keep working for no behavioural gain.
9
+ */
10
+ export declare const runtime: DeployRuntime;
11
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/deploy/deno/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,aAMrB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { fileSystemRuntime } from '../filesystem.js';
2
+ /**
3
+ * Deno: `deno serve` and Deno Deploy both look for a `fetch` on the default export, so the handoff is
4
+ * an export rather than a listener — which is also what makes one build work for both.
5
+ *
6
+ * The compiler settings stay Node's deliberately. Deno's `node:` compatibility covers everything
7
+ * `server/` uses, so the Node builds of React and the RSC runtime run as-is, and the alternative would
8
+ * be a second bundle shape to keep working for no behavioural gain.
9
+ */
10
+ export const runtime = {
11
+ ...fileSystemRuntime,
12
+ serveApp(app) {
13
+ return { fetch: app.fetch };
14
+ },
15
+ };
16
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/deno/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,GAAG,iBAAiB;IAEpB,QAAQ,CAAC,GAAS;QAChB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACF,CAAC","sourcesContent":["import type { Hono } from 'hono';\nimport type { DeployRuntime } from '../contract.js';\nimport { fileSystemRuntime } from '../filesystem.js';\n\n/**\n * Deno: `deno serve` and Deno Deploy both look for a `fetch` on the default export, so the handoff is\n * an export rather than a listener — which is also what makes one build work for both.\n *\n * The compiler settings stay Node's deliberately. Deno's `node:` compatibility covers everything\n * `server/` uses, so the Node builds of React and the RSC runtime run as-is, and the alternative would\n * be a second bundle shape to keep working for no behavioural gain.\n */\nexport const runtime: DeployRuntime = {\n ...fileSystemRuntime,\n\n serveApp(app: Hono): unknown {\n return { fetch: app.fetch };\n },\n};\n"]}
@@ -0,0 +1,10 @@
1
+ import type { DeployRuntime } from './contract.js';
2
+ /**
3
+ * Everything a deploy target with a real filesystem does the same way.
4
+ *
5
+ * Node, Bun, Deno and the serverless runtimes that unpack a bundle onto disk (Vercel, Netlify, Lambda)
6
+ * differ only in how the finished app is handed over — so each of those presets is this object plus
7
+ * its own {@link DeployRuntime.serveApp}, and the implementations live once, in `server/`.
8
+ */
9
+ export declare const fileSystemRuntime: Omit<DeployRuntime, 'serveApp'>;
10
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/deploy/filesystem.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAqBnD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAmB7D,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { compress } from '../server/compress.js';
4
+ import { loadEnvFiles } from '../server/load-env.js';
5
+ import { readPrerendered } from '../server/ssg.js';
6
+ import { createPublicFallback, createStaticAssetsApp } from '../server/static.js';
7
+ const isDev = __RSHONO_CONFIG__.isDev;
8
+ /**
9
+ * The project root, derived from where the bundle itself ended up: `rshono build` writes the server
10
+ * bundle to `<root>/dist/server/main.mjs`, and this module is bundled into it, which makes
11
+ * `import.meta.dirname` `<root>/dist/server` at runtime.
12
+ *
13
+ * Derived rather than baked in at build time on purpose: an absolute build-time path would tie the
14
+ * output to the machine that produced it, and building in CI to run somewhere else is the normal case.
15
+ * A preset that relocates the bundle keeps `dist/server/main.mjs` intact inside its own layout for
16
+ * exactly this reason.
17
+ */
18
+ const rootDir = join(import.meta.dirname, '..', '..');
19
+ const staticDir = join(rootDir, 'dist', 'static');
20
+ const ssgDir = join(rootDir, 'dist', 'ssg');
21
+ /** `public/` is copied into `dist/` by the build, so a deployed build is self-contained; dev reads the source. */
22
+ const publicDir = isDev ? join(rootDir, 'public') : join(rootDir, 'dist', 'public');
23
+ /**
24
+ * Everything a deploy target with a real filesystem does the same way.
25
+ *
26
+ * Node, Bun, Deno and the serverless runtimes that unpack a bundle onto disk (Vercel, Netlify, Lambda)
27
+ * differ only in how the finished app is handed over — so each of those presets is this object plus
28
+ * its own {@link DeployRuntime.serveApp}, and the implementations live once, in `server/`.
29
+ */
30
+ export const fileSystemRuntime = {
31
+ mountStaticAssets(app) {
32
+ app.route('/_static', createStaticAssetsApp({ root: staticDir, isDev }));
33
+ },
34
+ mountPublicFallback(app) {
35
+ if (!existsSync(publicDir))
36
+ return;
37
+ app.on(['GET', 'HEAD'], '/*', createPublicFallback({ root: publicDir, isDev }));
38
+ },
39
+ readPrerendered(c, variant) {
40
+ return readPrerendered(ssgDir, c.req.path, variant);
41
+ },
42
+ compress: compress(),
43
+ loadEnv() {
44
+ loadEnvFiles(rootDir);
45
+ },
46
+ };
47
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/deploy/filesystem.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGlF,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAC5C,kHAAkH;AAClH,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEpF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAoC;IAChE,iBAAiB,CAAC,GAAS;QACzB,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,mBAAmB,CAAC,GAAS;QAC3B,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO;QACnC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,eAAe,CAAC,CAAC,EAAE,OAAO;QACxB,OAAO,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,QAAQ,EAAE,QAAQ,EAAE;IAEpB,OAAO;QACL,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;CACF,CAAC","sourcesContent":["import type { Hono } from 'hono';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { compress } from '../server/compress.js';\nimport { loadEnvFiles } from '../server/load-env.js';\nimport { readPrerendered } from '../server/ssg.js';\nimport { createPublicFallback, createStaticAssetsApp } from '../server/static.js';\nimport type { DeployRuntime } from './contract.js';\n\nconst isDev = __RSHONO_CONFIG__.isDev;\n\n/**\n * The project root, derived from where the bundle itself ended up: `rshono build` writes the server\n * bundle to `<root>/dist/server/main.mjs`, and this module is bundled into it, which makes\n * `import.meta.dirname` `<root>/dist/server` at runtime.\n *\n * Derived rather than baked in at build time on purpose: an absolute build-time path would tie the\n * output to the machine that produced it, and building in CI to run somewhere else is the normal case.\n * A preset that relocates the bundle keeps `dist/server/main.mjs` intact inside its own layout for\n * exactly this reason.\n */\nconst rootDir = join(import.meta.dirname, '..', '..');\n\nconst staticDir = join(rootDir, 'dist', 'static');\nconst ssgDir = join(rootDir, 'dist', 'ssg');\n/** `public/` is copied into `dist/` by the build, so a deployed build is self-contained; dev reads the source. */\nconst publicDir = isDev ? join(rootDir, 'public') : join(rootDir, 'dist', 'public');\n\n/**\n * Everything a deploy target with a real filesystem does the same way.\n *\n * Node, Bun, Deno and the serverless runtimes that unpack a bundle onto disk (Vercel, Netlify, Lambda)\n * differ only in how the finished app is handed over — so each of those presets is this object plus\n * its own {@link DeployRuntime.serveApp}, and the implementations live once, in `server/`.\n */\nexport const fileSystemRuntime: Omit<DeployRuntime, 'serveApp'> = {\n mountStaticAssets(app: Hono): void {\n app.route('/_static', createStaticAssetsApp({ root: staticDir, isDev }));\n },\n\n mountPublicFallback(app: Hono): void {\n if (!existsSync(publicDir)) return;\n app.on(['GET', 'HEAD'], '/*', createPublicFallback({ root: publicDir, isDev }));\n },\n\n readPrerendered(c, variant) {\n return readPrerendered(ssgDir, c.req.path, variant);\n },\n\n compress: compress(),\n\n loadEnv(): void {\n loadEnvFiles(rootDir);\n },\n};\n"]}
@@ -0,0 +1,20 @@
1
+ /** What to bind, for the targets where rshono owns the process rather than being handed a request. */
2
+ export interface ListenAddress {
3
+ port: number;
4
+ hostname: string;
5
+ }
6
+ /**
7
+ * Resolves the address to listen on: an explicit override (the dev server, which picks the port for
8
+ * its worker) beats the environment, which beats `rshono.config.ts`, which beats the built-in default.
9
+ *
10
+ * `PORT` and `HOST` stay env-overridable because that is the deployment convention everywhere this
11
+ * runs — a container, a process manager, a PaaS. `??` rather than `||` so an explicit `PORT=0`, which
12
+ * means "any free port", is honoured.
13
+ */
14
+ export declare function listenAddress(overrides?: {
15
+ port?: number;
16
+ hostname?: string;
17
+ }): ListenAddress;
18
+ /** What to print once listening — `localhost` rather than the wildcard the socket is actually bound to. */
19
+ export declare function readyMessage({ port, hostname }: ListenAddress): string;
20
+ //# sourceMappingURL=listen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.d.ts","sourceRoot":"","sources":["../../src/deploy/listen.ts"],"names":[],"mappings":"AAOA,sGAAsG;AACtG,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAM7F;AAED,2GAA2G;AAC3G,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,aAAa,GAAG,MAAM,CAEtE"}
@@ -0,0 +1,24 @@
1
+ import { SERVER_DEFAULTS } from '../server/server-config.js';
2
+ const CONFIG = __RSHONO_CONFIG__;
3
+ /** Bound to every interface — so the address printed on start is `localhost`, not this. */
4
+ const WILDCARD_HOST = '0.0.0.0';
5
+ /**
6
+ * Resolves the address to listen on: an explicit override (the dev server, which picks the port for
7
+ * its worker) beats the environment, which beats `rshono.config.ts`, which beats the built-in default.
8
+ *
9
+ * `PORT` and `HOST` stay env-overridable because that is the deployment convention everywhere this
10
+ * runs — a container, a process manager, a PaaS. `??` rather than `||` so an explicit `PORT=0`, which
11
+ * means "any free port", is honoured.
12
+ */
13
+ export function listenAddress(overrides) {
14
+ const envPort = process.env.PORT !== undefined ? Number(process.env.PORT) : undefined;
15
+ return {
16
+ port: overrides?.port ?? envPort ?? CONFIG.port ?? SERVER_DEFAULTS.port,
17
+ hostname: overrides?.hostname ?? process.env.HOST ?? CONFIG.host ?? SERVER_DEFAULTS.host,
18
+ };
19
+ }
20
+ /** What to print once listening — `localhost` rather than the wildcard the socket is actually bound to. */
21
+ export function readyMessage({ port, hostname }) {
22
+ return ` ➜ rshono serving on http://${hostname === WILDCARD_HOST ? 'localhost' : hostname}:${port}`;
23
+ }
24
+ //# sourceMappingURL=listen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.js","sourceRoot":"","sources":["../../src/deploy/listen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,MAAM,GAAG,iBAAiB,CAAC;AAEjC,2FAA2F;AAC3F,MAAM,aAAa,GAAG,SAAS,CAAC;AAQhC;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,SAAgD;IAC5E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,OAAO;QACL,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;QACvE,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI;KACzF,CAAC;AACJ,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,YAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAiB;IAC5D,OAAO,gCAAgC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;AACvG,CAAC","sourcesContent":["import { SERVER_DEFAULTS } from '../server/server-config.js';\n\nconst CONFIG = __RSHONO_CONFIG__;\n\n/** Bound to every interface — so the address printed on start is `localhost`, not this. */\nconst WILDCARD_HOST = '0.0.0.0';\n\n/** What to bind, for the targets where rshono owns the process rather than being handed a request. */\nexport interface ListenAddress {\n port: number;\n hostname: string;\n}\n\n/**\n * Resolves the address to listen on: an explicit override (the dev server, which picks the port for\n * its worker) beats the environment, which beats `rshono.config.ts`, which beats the built-in default.\n *\n * `PORT` and `HOST` stay env-overridable because that is the deployment convention everywhere this\n * runs — a container, a process manager, a PaaS. `??` rather than `||` so an explicit `PORT=0`, which\n * means \"any free port\", is honoured.\n */\nexport function listenAddress(overrides?: { port?: number; hostname?: string }): ListenAddress {\n const envPort = process.env.PORT !== undefined ? Number(process.env.PORT) : undefined;\n return {\n port: overrides?.port ?? envPort ?? CONFIG.port ?? SERVER_DEFAULTS.port,\n hostname: overrides?.hostname ?? process.env.HOST ?? CONFIG.host ?? SERVER_DEFAULTS.host,\n };\n}\n\n/** What to print once listening — `localhost` rather than the wildcard the socket is actually bound to. */\nexport function readyMessage({ port, hostname }: ListenAddress): string {\n return ` ➜ rshono serving on http://${hostname === WILDCARD_HOST ? 'localhost' : hostname}:${port}`;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { DeployBuildContext } from '../presets.js';
2
+ /**
3
+ * `dist/server/main.mjs` is kept at that exact path inside the function bundle because the runtime
4
+ * derives the project root from where it sits (see `deploy/filesystem.ts`), which is what puts
5
+ * `dist/ssg` within reach at request time.
6
+ */
7
+ export declare function finalizeNetlifyBuild(ctx: DeployBuildContext): Promise<void>;
8
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/deploy/netlify/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AA0BxD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBjF"}
@@ -0,0 +1,52 @@
1
+ import { cpSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * What Netlify uploads: a publish directory the CDN serves, and an internal function directory the
6
+ * platform picks handlers up from without the project needing a `netlify/functions` folder of its own.
7
+ */
8
+ const PUBLISH_DIR = join('.netlify', 'publish');
9
+ const FUNCTIONS_DIR = join('.netlify', 'functions-internal');
10
+ const FUNCTION_NAME = 'rshono-server';
11
+ /**
12
+ * The function entry Netlify loads. It re-exports the built bundle's default handler and, next to it,
13
+ * the v2 `config` that claims every path — so routing is declared in code rather than in a redirect
14
+ * rule the project would have to maintain.
15
+ *
16
+ * `preferStatic` is what keeps the CDN in front: a path that exists in the publish directory is served
17
+ * from there and the function is never invoked.
18
+ */
19
+ const FUNCTION_ENTRY = `export { default } from '../../dist/server/main.mjs';
20
+
21
+ export const config = {
22
+ path: '/*',
23
+ preferStatic: true,
24
+ };
25
+ `;
26
+ /**
27
+ * `dist/server/main.mjs` is kept at that exact path inside the function bundle because the runtime
28
+ * derives the project root from where it sits (see `deploy/filesystem.ts`), which is what puts
29
+ * `dist/ssg` within reach at request time.
30
+ */
31
+ export async function finalizeNetlifyBuild(ctx) {
32
+ const publishDir = join(ctx.rootDir, PUBLISH_DIR);
33
+ const functionsDir = join(ctx.rootDir, FUNCTIONS_DIR);
34
+ // Rebuilt from scratch, so a deleted page or asset cannot survive in the uploaded output.
35
+ await rm(publishDir, { recursive: true, force: true });
36
+ await rm(functionsDir, { recursive: true, force: true });
37
+ mkdirSync(publishDir, { recursive: true });
38
+ mkdirSync(functionsDir, { recursive: true });
39
+ cpSync(ctx.staticDir, join(publishDir, '_static'), { recursive: true });
40
+ if (ctx.publicDir)
41
+ cpSync(ctx.publicDir, publishDir, { recursive: true });
42
+ // The one header the CDN cannot infer; everything else keeps Netlify's defaults.
43
+ writeFileSync(join(publishDir, '_headers'), '/_static/*\n Cache-Control: public, max-age=31536000, immutable\n');
44
+ cpSync(join(ctx.distDir, 'server'), join(functionsDir, 'dist', 'server'), { recursive: true });
45
+ if (existsSync(ctx.ssgDir))
46
+ cpSync(ctx.ssgDir, join(functionsDir, 'dist', 'ssg'), { recursive: true });
47
+ if (ctx.publicDir)
48
+ cpSync(ctx.publicDir, join(functionsDir, 'dist', 'public'), { recursive: true });
49
+ writeFileSync(join(functionsDir, `${FUNCTION_NAME}.mjs`), FUNCTION_ENTRY);
50
+ console.log(` • assembled ${PUBLISH_DIR} and ${FUNCTIONS_DIR}`);
51
+ }
52
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/deploy/netlify/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;GAGG;AACH,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAChD,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;AAC7D,MAAM,aAAa,GAAG,eAAe,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG;;;;;;CAMtB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAuB;IAChE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAEtD,0FAA0F;IAC1F,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,EAAE,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,IAAI,GAAG,CAAC,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,iFAAiF;IACjF,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,oEAAoE,CAAC,CAAC;IAElH,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvG,IAAI,GAAG,CAAC,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpG,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,aAAa,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC;IAE1E,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,QAAQ,aAAa,EAAE,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { cpSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DeployBuildContext } from '../presets.js';\n\n/**\n * What Netlify uploads: a publish directory the CDN serves, and an internal function directory the\n * platform picks handlers up from without the project needing a `netlify/functions` folder of its own.\n */\nconst PUBLISH_DIR = join('.netlify', 'publish');\nconst FUNCTIONS_DIR = join('.netlify', 'functions-internal');\nconst FUNCTION_NAME = 'rshono-server';\n\n/**\n * The function entry Netlify loads. It re-exports the built bundle's default handler and, next to it,\n * the v2 `config` that claims every path — so routing is declared in code rather than in a redirect\n * rule the project would have to maintain.\n *\n * `preferStatic` is what keeps the CDN in front: a path that exists in the publish directory is served\n * from there and the function is never invoked.\n */\nconst FUNCTION_ENTRY = `export { default } from '../../dist/server/main.mjs';\n\nexport const config = {\n path: '/*',\n preferStatic: true,\n};\n`;\n\n/**\n * `dist/server/main.mjs` is kept at that exact path inside the function bundle because the runtime\n * derives the project root from where it sits (see `deploy/filesystem.ts`), which is what puts\n * `dist/ssg` within reach at request time.\n */\nexport async function finalizeNetlifyBuild(ctx: DeployBuildContext): Promise<void> {\n const publishDir = join(ctx.rootDir, PUBLISH_DIR);\n const functionsDir = join(ctx.rootDir, FUNCTIONS_DIR);\n\n // Rebuilt from scratch, so a deleted page or asset cannot survive in the uploaded output.\n await rm(publishDir, { recursive: true, force: true });\n await rm(functionsDir, { recursive: true, force: true });\n mkdirSync(publishDir, { recursive: true });\n mkdirSync(functionsDir, { recursive: true });\n\n cpSync(ctx.staticDir, join(publishDir, '_static'), { recursive: true });\n if (ctx.publicDir) cpSync(ctx.publicDir, publishDir, { recursive: true });\n // The one header the CDN cannot infer; everything else keeps Netlify's defaults.\n writeFileSync(join(publishDir, '_headers'), '/_static/*\\n Cache-Control: public, max-age=31536000, immutable\\n');\n\n cpSync(join(ctx.distDir, 'server'), join(functionsDir, 'dist', 'server'), { recursive: true });\n if (existsSync(ctx.ssgDir)) cpSync(ctx.ssgDir, join(functionsDir, 'dist', 'ssg'), { recursive: true });\n if (ctx.publicDir) cpSync(ctx.publicDir, join(functionsDir, 'dist', 'public'), { recursive: true });\n writeFileSync(join(functionsDir, `${FUNCTION_NAME}.mjs`), FUNCTION_ENTRY);\n\n console.log(` • assembled ${PUBLISH_DIR} and ${FUNCTIONS_DIR}`);\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { DeployRuntime } from '../contract.js';
2
+ /**
3
+ * Netlify, as one Functions v2 handler behind the CDN.
4
+ *
5
+ * Same division as Vercel: the publish directory holds `/_static` and `public/`, so the CDN answers
6
+ * those and the function is only reached for a page. Prerendered pages ship inside the function and are
7
+ * read from disk, because serving them from the CDN would lose the `Accept` negotiation that decides
8
+ * between a document and a flight payload.
9
+ *
10
+ * Functions v2 streams a `Response` body, so streamed SSR survives.
11
+ */
12
+ export declare const runtime: DeployRuntime;
13
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/deploy/netlify/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,EAAE,aAarB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { handle } from 'hono/netlify';
2
+ import { fileSystemRuntime } from '../filesystem.js';
3
+ /**
4
+ * Netlify, as one Functions v2 handler behind the CDN.
5
+ *
6
+ * Same division as Vercel: the publish directory holds `/_static` and `public/`, so the CDN answers
7
+ * those and the function is only reached for a page. Prerendered pages ship inside the function and are
8
+ * read from disk, because serving them from the CDN would lose the `Accept` negotiation that decides
9
+ * between a document and a flight payload.
10
+ *
11
+ * Functions v2 streams a `Response` body, so streamed SSR survives.
12
+ */
13
+ export const runtime = {
14
+ ...fileSystemRuntime,
15
+ mountStaticAssets() {
16
+ // Served from the publish directory before the function runs.
17
+ },
18
+ serveApp(app) {
19
+ return handle(app);
20
+ },
21
+ // Netlify compresses at the edge.
22
+ compress: null,
23
+ };
24
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/netlify/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,GAAG,iBAAiB;IAEpB,iBAAiB;QACf,8DAA8D;IAChE,CAAC;IAED,QAAQ,CAAC,GAAS;QAChB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,kCAAkC;IAClC,QAAQ,EAAE,IAAI;CACf,CAAC","sourcesContent":["import type { Hono } from 'hono';\nimport { handle } from 'hono/netlify';\nimport type { DeployRuntime } from '../contract.js';\nimport { fileSystemRuntime } from '../filesystem.js';\n\n/**\n * Netlify, as one Functions v2 handler behind the CDN.\n *\n * Same division as Vercel: the publish directory holds `/_static` and `public/`, so the CDN answers\n * those and the function is only reached for a page. Prerendered pages ship inside the function and are\n * read from disk, because serving them from the CDN would lose the `Accept` negotiation that decides\n * between a document and a flight payload.\n *\n * Functions v2 streams a `Response` body, so streamed SSR survives.\n */\nexport const runtime: DeployRuntime = {\n ...fileSystemRuntime,\n\n mountStaticAssets(): void {\n // Served from the publish directory before the function runs.\n },\n\n serveApp(app: Hono): unknown {\n return handle(app);\n },\n\n // Netlify compresses at the edge.\n compress: null,\n};\n"]}
@@ -0,0 +1,7 @@
1
+ import type { DeployRuntime } from '../contract.js';
2
+ /**
3
+ * Node: a long-lived process that owns its own port, with a filesystem behind every asset. The shape
4
+ * the framework was built against, and the only target `rshono dev` ever produces.
5
+ */
6
+ export declare const runtime: DeployRuntime;
7
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/deploy/node/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,aA2BrB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { serve } from '@hono/node-server';
2
+ import { parentPort, workerData } from 'node:worker_threads';
3
+ import { onShutdown } from '../../server/shutdown.js';
4
+ import { fileSystemRuntime } from '../filesystem.js';
5
+ import { listenAddress, readyMessage } from '../listen.js';
6
+ /**
7
+ * Node: a long-lived process that owns its own port, with a filesystem behind every asset. The shape
8
+ * the framework was built against, and the only target `rshono dev` ever produces.
9
+ */
10
+ export const runtime = {
11
+ ...fileSystemRuntime,
12
+ serveApp(app) {
13
+ // `rshono build` imports this bundle to prerender `render: 'static'` routes. That pass renders
14
+ // through `app.fetch` directly and must not bind a port — nothing is listening for it, and the
15
+ // build would never exit.
16
+ if (process.env.RSHONO_PRERENDER)
17
+ return;
18
+ // The dev server runs this bundle in a worker thread and picks the port itself, so its choice wins
19
+ // over both the environment and the config file.
20
+ const devWorker = workerData;
21
+ const address = listenAddress(devWorker ?? undefined);
22
+ const server = serve({ fetch: app.fetch, ...address }, (info) => {
23
+ if (parentPort) {
24
+ parentPort.postMessage({ type: 'ready', port: info.port });
25
+ }
26
+ else {
27
+ console.log(readyMessage({ ...address, port: info.port }));
28
+ }
29
+ });
30
+ onShutdown(() => {
31
+ server.close(() => process.exit(0));
32
+ setTimeout(() => process.exit(0), 3000).unref();
33
+ });
34
+ },
35
+ };
36
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/deploy/node/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3D;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,GAAG,iBAAiB;IAEpB,QAAQ,CAAC,GAAS;QAChB,+FAA+F;QAC/F,+FAA+F;QAC/F,0BAA0B;QAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;YAAE,OAAO;QAEzC,mGAAmG;QACnG,iDAAiD;QACjD,MAAM,SAAS,GAAG,UAAyD,CAAC;QAC5E,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;QAEtD,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9D,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import { serve } from '@hono/node-server';\nimport type { Hono } from 'hono';\nimport { parentPort, workerData } from 'node:worker_threads';\nimport { onShutdown } from '../../server/shutdown.js';\nimport type { DeployRuntime } from '../contract.js';\nimport { fileSystemRuntime } from '../filesystem.js';\nimport { listenAddress, readyMessage } from '../listen.js';\n\n/**\n * Node: a long-lived process that owns its own port, with a filesystem behind every asset. The shape\n * the framework was built against, and the only target `rshono dev` ever produces.\n */\nexport const runtime: DeployRuntime = {\n ...fileSystemRuntime,\n\n serveApp(app: Hono): undefined {\n // `rshono build` imports this bundle to prerender `render: 'static'` routes. That pass renders\n // through `app.fetch` directly and must not bind a port — nothing is listening for it, and the\n // build would never exit.\n if (process.env.RSHONO_PRERENDER) return;\n\n // The dev server runs this bundle in a worker thread and picks the port itself, so its choice wins\n // over both the environment and the config file.\n const devWorker = workerData as { port?: number; hostname?: string } | null;\n const address = listenAddress(devWorker ?? undefined);\n\n const server = serve({ fetch: app.fetch, ...address }, (info) => {\n if (parentPort) {\n parentPort.postMessage({ type: 'ready', port: info.port });\n } else {\n console.log(readyMessage({ ...address, port: info.port }));\n }\n });\n\n onShutdown(() => {\n server.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 3000).unref();\n });\n },\n};\n"]}