@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
@@ -1,24 +1,50 @@
1
1
  /**
2
- * Small header utilities shared by the response-header defaults, the compressor and the
3
- * prerendered-page cache. Kept apart from any one of them because getting `Vary` and `ETag`
4
- * comparison subtly wrong is exactly the kind of thing that only shows up behind a CDN.
2
+ * Header utilities shared by the response-header defaults and the prerendered-page cache. Their own module
3
+ * because getting `Vary` and `ETag` comparison subtly wrong only shows up behind a CDN.
5
4
  */
6
5
  /**
7
- * Adds `value` to the `Vary` header without discarding what is already there.
6
+ * What a 404 under the `/_static` mount must carry, on every deploy target.
8
7
  *
9
- * `Vary` is a list, and two different concerns write to it here content negotiation on `Accept`
10
- * (HTML document vs flight payload) and `Accept-Encoding` from the compressor. A plain `set` from
11
- * whichever ran last would drop the other, and a cache would then happily serve one variant in
12
- * place of the other. `*` is left alone: it already means "never reuse this".
8
+ * A 404 is heuristically cacheable under RFC 9111, and the miss this answers is the one a **rolling deploy**
9
+ * produces: an old instance 404s a content-hashed chunk the new one has. Without this a shared cache may
10
+ * store that answer against a URL that is about to become valid, and then serve it to everyone.
11
+ *
12
+ * Shared by the two mounts — `createStaticAssetsApp` for the filesystem targets and `mountStaticAssets` in
13
+ * `deploy/cloudflare/runtime.ts` — because it was written down in one of them and inherited by accident in
14
+ * the other, which is how the Workers mount came to have no terminal 404 at all. Here rather than in either,
15
+ * since `server/static.ts` reaches for `@hono/node-server` and a Worker bundle cannot import it.
16
+ *
17
+ * The same string as a page response's default (`PAGE_CACHE_CONTROL` in `entry.rsc.tsx`) and a separate
18
+ * constant on purpose: that one is about a logged-in user's page, this one about a hash that is about to
19
+ * resolve, and a change to either must not move the other.
13
20
  */
14
- export declare function appendVary(headers: Headers, value: string): void;
21
+ export declare const ASSET_MISS_CACHE_CONTROL = "private, no-cache";
15
22
  /**
16
- * True when an `If-None-Match` request header matches `etag`, i.e. the client already holds this
17
- * exact body and should be answered with a 304.
23
+ * The `Vary` a response needs in order to carry `value`, or `null` when there is nothing to write — because it
24
+ * already lists it, or because it lists `*`, which means "never reuse this" and covers everything.
25
+ *
26
+ * What is already there is kept, spelling and all: a route, a middleware or a proxy may have added an entry of
27
+ * its own, and a plain `set` would drop it, leaving a cache free to serve one variant in place of another.
18
28
  *
19
- * The header carries a *list*, and each entry may be weak (`W/"…"`). The weak prefix is ignored on
20
- * both sides because the compressor rewrites a strong tag to a weak one when it changes the bytes
21
- * on the wire the representation is still the same one the client cached.
29
+ * The header is parsed into entries rather than tested as a string, because both of the questions it is asked
30
+ * are about the *list* and neither is about the text. `*` counts wherever it sits, not only alone — a cache
31
+ * told never to reuse a response is not told it harder by another field name. And an existing header with no
32
+ * entries in it (`vary: ''`, which an app writing one from a list that came out empty can produce, and which
33
+ * Hono keeps verbatim) is nothing to append to: `, RSC` is an empty list element, which RFC 9110 tells a
34
+ * sender not to generate, and a cache strict enough to reject the malformed header would drop the `Vary` that
35
+ * is the only thing keeping a page URL's two representations apart. That is the failure this header exists to
36
+ * prevent, reached through the header itself.
37
+ *
38
+ * Pure, and separate from {@link appendVary}, because the two callers cannot write the same way. One holds a
39
+ * `Response` it built itself; the response floor holds one the *app* may have returned, whose header bag can be
40
+ * immutable — see the floor in `entry.rsc.tsx`, which writes this answer through `c.header()`.
41
+ */
42
+ export declare function varyWith(existing: string | null, value: string): string | null;
43
+ /** {@link varyWith}, written straight back — for a `Headers` the caller knows it can write to. */
44
+ export declare function appendVary(headers: Headers, value: string): void;
45
+ /**
46
+ * True when an `If-None-Match` matches `etag` — the client already holds this body and can be answered with a
47
+ * 304. The header carries a list, and the weak prefix is ignored on both sides: a CDN that gzips on the way out
48
+ * changes the bytes without changing the representation, and may weaken the validator when it does.
22
49
  */
23
50
  export declare function etagMatches(ifNoneMatch: string | undefined, etag: string): boolean;
24
- //# sourceMappingURL=headers.d.ts.map
@@ -1,35 +1,64 @@
1
1
  /**
2
- * Small header utilities shared by the response-header defaults, the compressor and the
3
- * prerendered-page cache. Kept apart from any one of them because getting `Vary` and `ETag`
4
- * comparison subtly wrong is exactly the kind of thing that only shows up behind a CDN.
2
+ * Header utilities shared by the response-header defaults and the prerendered-page cache. Their own module
3
+ * because getting `Vary` and `ETag` comparison subtly wrong only shows up behind a CDN.
5
4
  */
6
5
  /**
7
- * Adds `value` to the `Vary` header without discarding what is already there.
6
+ * What a 404 under the `/_static` mount must carry, on every deploy target.
8
7
  *
9
- * `Vary` is a list, and two different concerns write to it here content negotiation on `Accept`
10
- * (HTML document vs flight payload) and `Accept-Encoding` from the compressor. A plain `set` from
11
- * whichever ran last would drop the other, and a cache would then happily serve one variant in
12
- * place of the other. `*` is left alone: it already means "never reuse this".
8
+ * A 404 is heuristically cacheable under RFC 9111, and the miss this answers is the one a **rolling deploy**
9
+ * produces: an old instance 404s a content-hashed chunk the new one has. Without this a shared cache may
10
+ * store that answer against a URL that is about to become valid, and then serve it to everyone.
11
+ *
12
+ * Shared by the two mounts — `createStaticAssetsApp` for the filesystem targets and `mountStaticAssets` in
13
+ * `deploy/cloudflare/runtime.ts` — because it was written down in one of them and inherited by accident in
14
+ * the other, which is how the Workers mount came to have no terminal 404 at all. Here rather than in either,
15
+ * since `server/static.ts` reaches for `@hono/node-server` and a Worker bundle cannot import it.
16
+ *
17
+ * The same string as a page response's default (`PAGE_CACHE_CONTROL` in `entry.rsc.tsx`) and a separate
18
+ * constant on purpose: that one is about a logged-in user's page, this one about a hash that is about to
19
+ * resolve, and a change to either must not move the other.
13
20
  */
21
+ export const ASSET_MISS_CACHE_CONTROL = 'private, no-cache';
22
+ /**
23
+ * The `Vary` a response needs in order to carry `value`, or `null` when there is nothing to write — because it
24
+ * already lists it, or because it lists `*`, which means "never reuse this" and covers everything.
25
+ *
26
+ * What is already there is kept, spelling and all: a route, a middleware or a proxy may have added an entry of
27
+ * its own, and a plain `set` would drop it, leaving a cache free to serve one variant in place of another.
28
+ *
29
+ * The header is parsed into entries rather than tested as a string, because both of the questions it is asked
30
+ * are about the *list* and neither is about the text. `*` counts wherever it sits, not only alone — a cache
31
+ * told never to reuse a response is not told it harder by another field name. And an existing header with no
32
+ * entries in it (`vary: ''`, which an app writing one from a list that came out empty can produce, and which
33
+ * Hono keeps verbatim) is nothing to append to: `, RSC` is an empty list element, which RFC 9110 tells a
34
+ * sender not to generate, and a cache strict enough to reject the malformed header would drop the `Vary` that
35
+ * is the only thing keeping a page URL's two representations apart. That is the failure this header exists to
36
+ * prevent, reached through the header itself.
37
+ *
38
+ * Pure, and separate from {@link appendVary}, because the two callers cannot write the same way. One holds a
39
+ * `Response` it built itself; the response floor holds one the *app* may have returned, whose header bag can be
40
+ * immutable — see the floor in `entry.rsc.tsx`, which writes this answer through `c.header()`.
41
+ */
42
+ export function varyWith(existing, value) {
43
+ if (existing === null)
44
+ return value;
45
+ const entries = existing.split(',').map((entry) => entry.trim());
46
+ if (entries.some((entry) => entry === '*' || entry.toLowerCase() === value.toLowerCase()))
47
+ return null;
48
+ // Appended to the original text, not to the parsed entries, so a header this has nothing to say about comes
49
+ // back exactly as its author wrote it.
50
+ return entries.some(Boolean) ? `${existing}, ${value}` : value;
51
+ }
52
+ /** {@link varyWith}, written straight back — for a `Headers` the caller knows it can write to. */
14
53
  export function appendVary(headers, value) {
15
- const existing = headers.get('vary');
16
- if (existing === null) {
17
- headers.set('vary', value);
18
- return;
19
- }
20
- if (existing.trim() === '*')
21
- return;
22
- const already = existing.split(',').some((entry) => entry.trim().toLowerCase() === value.toLowerCase());
23
- if (!already)
24
- headers.set('vary', `${existing}, ${value}`);
54
+ const vary = varyWith(headers.get('vary'), value);
55
+ if (vary !== null)
56
+ headers.set('vary', vary);
25
57
  }
26
58
  /**
27
- * True when an `If-None-Match` request header matches `etag`, i.e. the client already holds this
28
- * exact body and should be answered with a 304.
29
- *
30
- * The header carries a *list*, and each entry may be weak (`W/"…"`). The weak prefix is ignored on
31
- * both sides because the compressor rewrites a strong tag to a weak one when it changes the bytes
32
- * on the wire — the representation is still the same one the client cached.
59
+ * True when an `If-None-Match` matches `etag` the client already holds this body and can be answered with a
60
+ * 304. The header carries a list, and the weak prefix is ignored on both sides: a CDN that gzips on the way out
61
+ * changes the bytes without changing the representation, and may weaken the validator when it does.
33
62
  */
34
63
  export function etagMatches(ifNoneMatch, etag) {
35
64
  if (!ifNoneMatch)
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/server/headers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,KAAa;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG;QAAE,OAAO;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,WAA+B,EAAE,IAAY;IACvE,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,MAAM,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Small header utilities shared by the response-header defaults, the compressor and the\n * prerendered-page cache. Kept apart from any one of them because getting `Vary` and `ETag`\n * comparison subtly wrong is exactly the kind of thing that only shows up behind a CDN.\n */\n\n/**\n * Adds `value` to the `Vary` header without discarding what is already there.\n *\n * `Vary` is a list, and two different concerns write to it here content negotiation on `Accept`\n * (HTML document vs flight payload) and `Accept-Encoding` from the compressor. A plain `set` from\n * whichever ran last would drop the other, and a cache would then happily serve one variant in\n * place of the other. `*` is left alone: it already means \"never reuse this\".\n */\nexport function appendVary(headers: Headers, value: string): void {\n const existing = headers.get('vary');\n if (existing === null) {\n headers.set('vary', value);\n return;\n }\n if (existing.trim() === '*') return;\n const already = existing.split(',').some((entry) => entry.trim().toLowerCase() === value.toLowerCase());\n if (!already) headers.set('vary', `${existing}, ${value}`);\n}\n\n/**\n * True when an `If-None-Match` request header matches `etag`, i.e. the client already holds this\n * exact body and should be answered with a 304.\n *\n * The header carries a *list*, and each entry may be weak (`W/\"…\"`). The weak prefix is ignored on\n * both sides because the compressor rewrites a strong tag to a weak one when it changes the bytes\n * on the wire the representation is still the same one the client cached.\n */\nexport function etagMatches(ifNoneMatch: string | undefined, etag: string): boolean {\n if (!ifNoneMatch) return false;\n const normalize = (value: string) => value.trim().replace(/^W\\//, '');\n const wanted = normalize(etag);\n return ifNoneMatch.split(',').some((entry) => {\n const candidate = normalize(entry);\n return candidate === '*' || candidate === wanted;\n });\n}\n"]}
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/server/headers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAuB,EAAE,KAAa;IAC7D,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IACvG,4GAA4G;IAC5G,uCAAuC;IACvC,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACjE,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,KAAa;IACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,WAA+B,EAAE,IAAY;IACvE,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,MAAM,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Header utilities shared by the response-header defaults and the prerendered-page cache. Their own module\n * because getting `Vary` and `ETag` comparison subtly wrong only shows up behind a CDN.\n */\n\n/**\n * What a 404 under the `/_static` mount must carry, on every deploy target.\n *\n * A 404 is heuristically cacheable under RFC 9111, and the miss this answers is the one a **rolling deploy**\n * produces: an old instance 404s a content-hashed chunk the new one has. Without this a shared cache may\n * store that answer against a URL that is about to become valid, and then serve it to everyone.\n *\n * Shared by the two mounts — `createStaticAssetsApp` for the filesystem targets and `mountStaticAssets` in\n * `deploy/cloudflare/runtime.ts` because it was written down in one of them and inherited by accident in\n * the other, which is how the Workers mount came to have no terminal 404 at all. Here rather than in either,\n * since `server/static.ts` reaches for `@hono/node-server` and a Worker bundle cannot import it.\n *\n * The same string as a page response's default (`PAGE_CACHE_CONTROL` in `entry.rsc.tsx`) and a separate\n * constant on purpose: that one is about a logged-in user's page, this one about a hash that is about to\n * resolve, and a change to either must not move the other.\n */\nexport const ASSET_MISS_CACHE_CONTROL = 'private, no-cache';\n\n/**\n * The `Vary` a response needs in order to carry `value`, or `null` when there is nothing to write — because it\n * already lists it, or because it lists `*`, which means \"never reuse this\" and covers everything.\n *\n * What is already there is kept, spelling and all: a route, a middleware or a proxy may have added an entry of\n * its own, and a plain `set` would drop it, leaving a cache free to serve one variant in place of another.\n *\n * The header is parsed into entries rather than tested as a string, because both of the questions it is asked\n * are about the *list* and neither is about the text. `*` counts wherever it sits, not only alone — a cache\n * told never to reuse a response is not told it harder by another field name. And an existing header with no\n * entries in it (`vary: ''`, which an app writing one from a list that came out empty can produce, and which\n * Hono keeps verbatim) is nothing to append to: `, RSC` is an empty list element, which RFC 9110 tells a\n * sender not to generate, and a cache strict enough to reject the malformed header would drop the `Vary` that\n * is the only thing keeping a page URL's two representations apart. That is the failure this header exists to\n * prevent, reached through the header itself.\n *\n * Pure, and separate from {@link appendVary}, because the two callers cannot write the same way. One holds a\n * `Response` it built itself; the response floor holds one the *app* may have returned, whose header bag can be\n * immutable — see the floor in `entry.rsc.tsx`, which writes this answer through `c.header()`.\n */\nexport function varyWith(existing: string | null, value: string): string | null {\n if (existing === null) return value;\n const entries = existing.split(',').map((entry) => entry.trim());\n if (entries.some((entry) => entry === '*' || entry.toLowerCase() === value.toLowerCase())) return null;\n // Appended to the original text, not to the parsed entries, so a header this has nothing to say about comes\n // back exactly as its author wrote it.\n return entries.some(Boolean) ? `${existing}, ${value}` : value;\n}\n\n/** {@link varyWith}, written straight back — for a `Headers` the caller knows it can write to. */\nexport function appendVary(headers: Headers, value: string): void {\n const vary = varyWith(headers.get('vary'), value);\n if (vary !== null) headers.set('vary', vary);\n}\n\n/**\n * True when an `If-None-Match` matches `etag` the client already holds this body and can be answered with a\n * 304. The header carries a list, and the weak prefix is ignored on both sides: a CDN that gzips on the way out\n * changes the bytes without changing the representation, and may weaken the validator when it does.\n */\nexport function etagMatches(ifNoneMatch: string | undefined, etag: string): boolean {\n if (!ifNoneMatch) return false;\n const normalize = (value: string) => value.trim().replace(/^W\\//, '');\n const wanted = normalize(etag);\n return ifNoneMatch.split(',').some((entry) => {\n const candidate = normalize(entry);\n return candidate === '*' || candidate === wanted;\n });\n}\n"]}
@@ -1,7 +1,6 @@
1
- import type { RSHonoConfig } from '../config.js';
1
+ import type { RshonoConfig } from '../config.js';
2
2
  /**
3
- * Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at
4
- * {@link rootDir} unless an explicit {@link configPath} is given (resolved relative to `cwd`).
3
+ * Loads the project config, or `{}` if there is none: scans `rshono.config.{ts,js,mjs}` at `rootDir`, unless an
4
+ * explicit `configPath` is given (resolved relative to `cwd`).
5
5
  */
6
- export declare function loadConfig(rootDir: string, configPath?: string): Promise<RSHonoConfig>;
7
- //# sourceMappingURL=load-config.d.ts.map
6
+ export declare function loadConfig(rootDir: string, configPath?: string): Promise<RshonoConfig>;
@@ -3,23 +3,19 @@ import { isAbsolute, join, resolve } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  const CONFIG_FILES = ['rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];
5
5
  /**
6
- * Imports the config module.
6
+ * Imports the config module. A `.ts` config needs no loader: Node strips types natively from 22.18 on, which is
7
+ * what the package's `engines` floor is for.
7
8
  *
8
- * A `.js`/`.mjs` config is a plain dynamic import. A `.ts` one can't be the CLI itself runs as
9
- * compiled JavaScript, so nothing in the process understands TypeScript by default. `tsx`'s
10
- * programmatic API is loaded for that case only, which keeps it off the startup path of every
11
- * other command instead of paying for a TypeScript loader the whole CLI doesn't need.
9
+ * The one thing native stripping will not do is resolve a `.js` specifier to a `.ts` file, so a config
10
+ * importing a sibling has to name it with its real extension — {@link loadConfig} turns that resolution failure
11
+ * into the advice rather than letting a raw `ERR_MODULE_NOT_FOUND` out.
12
12
  */
13
- async function importConfig(file) {
14
- const href = pathToFileURL(file).href;
15
- if (!/\.[cm]?ts$/.test(file))
16
- return import(href);
17
- const { tsImport } = await import('tsx/esm/api');
18
- return tsImport(href, import.meta.url);
13
+ function importConfig(file) {
14
+ return import(pathToFileURL(file).href);
19
15
  }
20
16
  /**
21
- * Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at
22
- * {@link rootDir} unless an explicit {@link configPath} is given (resolved relative to `cwd`).
17
+ * Loads the project config, or `{}` if there is none: scans `rshono.config.{ts,js,mjs}` at `rootDir`, unless an
18
+ * explicit `configPath` is given (resolved relative to `cwd`).
23
19
  */
24
20
  export async function loadConfig(rootDir, configPath) {
25
21
  const file = configPath
@@ -32,7 +28,19 @@ export async function loadConfig(rootDir, configPath) {
32
28
  if (!existsSync(file)) {
33
29
  throw new Error(`[rshono] config file not found: ${file}`);
34
30
  }
35
- const mod = await importConfig(file);
31
+ let mod;
32
+ try {
33
+ mod = await importConfig(file);
34
+ }
35
+ catch (error) {
36
+ // The one failure mode native type stripping has that a TypeScript-aware loader does not.
37
+ if (/\.[cm]?ts$/.test(file) && error?.code === 'ERR_MODULE_NOT_FOUND') {
38
+ throw new Error(`[rshono] ${file} imports a module Node could not resolve: ${error.message}\n` +
39
+ ' A .ts config is loaded by Node itself, which does not rewrite a .js specifier to the .ts file ' +
40
+ 'beside it. Import it by its real extension, or move the config to rshono.config.mjs.', { cause: error });
41
+ }
42
+ throw error;
43
+ }
36
44
  if (!mod.default) {
37
45
  throw new Error(`[rshono] ${file} must \`export default\` a config object (use \`defineConfig({ … })\`).`);
38
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,YAAY,GAAG,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;AAEnF;;;;;;;GAOG;AACH,KAAK,UAAU,YAAY,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;IACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAwC,CAAC;IACzF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,GAAG,CAAwC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,UAAmB;IACnE,MAAM,IAAI,GAAG,UAAU;QACrB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC;QACtC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,yEAAyE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC","sourcesContent":["import { existsSync } from 'node:fs';\nimport { isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { RSHonoConfig } from '../config.js';\n\nconst CONFIG_FILES = ['rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];\n\n/**\n * Imports the config module.\n *\n * A `.js`/`.mjs` config is a plain dynamic import. A `.ts` one can't be the CLI itself runs as\n * compiled JavaScript, so nothing in the process understands TypeScript by default. `tsx`'s\n * programmatic API is loaded for that case only, which keeps it off the startup path of every\n * other command instead of paying for a TypeScript loader the whole CLI doesn't need.\n */\nasync function importConfig(file: string): Promise<{ default?: RSHonoConfig }> {\n const href = pathToFileURL(file).href;\n if (!/\\.[cm]?ts$/.test(file)) return import(href) as Promise<{ default?: RSHonoConfig }>;\n const { tsImport } = await import('tsx/esm/api');\n return tsImport(href, import.meta.url) as Promise<{ default?: RSHonoConfig }>;\n}\n\n/**\n * Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at\n * {@link rootDir} unless an explicit {@link configPath} is given (resolved relative to `cwd`).\n */\nexport async function loadConfig(rootDir: string, configPath?: string): Promise<RSHonoConfig> {\n const file = configPath\n ? isAbsolute(configPath)\n ? configPath\n : resolve(process.cwd(), configPath)\n : CONFIG_FILES.map((f) => join(rootDir, f)).find(existsSync);\n if (!file) return {};\n if (!existsSync(file)) {\n throw new Error(`[rshono] config file not found: ${file}`);\n }\n const mod = await importConfig(file);\n if (!mod.default) {\n throw new Error(`[rshono] ${file} must \\`export default\\` a config object (use \\`defineConfig({ … })\\`).`);\n }\n return mod.default;\n}\n"]}
1
+ {"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,YAAY,GAAG,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;AAEnF;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAwC,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,UAAmB;IACnE,MAAM,IAAI,GAAG,UAAU;QACrB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC;QACtC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,GAA+B,CAAC;IACpC,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,0FAA0F;QAC1F,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAK,KAAkC,EAAE,IAAI,KAAK,sBAAsB,EAAE,CAAC;YACpG,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,6CAA8C,KAAe,CAAC,OAAO,IAAI;gBACvF,kGAAkG;gBAClG,sFAAsF,EACxF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,yEAAyE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC","sourcesContent":["import { existsSync } from 'node:fs';\nimport { isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { RshonoConfig } from '../config.js';\n\nconst CONFIG_FILES = ['rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];\n\n/**\n * Imports the config module. A `.ts` config needs no loader: Node strips types natively from 22.18 on, which is\n * what the package's `engines` floor is for.\n *\n * The one thing native stripping will not do is resolve a `.js` specifier to a `.ts` file, so a config\n * importing a sibling has to name it with its real extension {@link loadConfig} turns that resolution failure\n * into the advice rather than letting a raw `ERR_MODULE_NOT_FOUND` out.\n */\nfunction importConfig(file: string): Promise<{ default?: RshonoConfig }> {\n return import(pathToFileURL(file).href) as Promise<{ default?: RshonoConfig }>;\n}\n\n/**\n * Loads the project config, or `{}` if there is none: scans `rshono.config.{ts,js,mjs}` at `rootDir`, unless an\n * explicit `configPath` is given (resolved relative to `cwd`).\n */\nexport async function loadConfig(rootDir: string, configPath?: string): Promise<RshonoConfig> {\n const file = configPath\n ? isAbsolute(configPath)\n ? configPath\n : resolve(process.cwd(), configPath)\n : CONFIG_FILES.map((f) => join(rootDir, f)).find(existsSync);\n if (!file) return {};\n if (!existsSync(file)) {\n throw new Error(`[rshono] config file not found: ${file}`);\n }\n let mod: { default?: RshonoConfig };\n try {\n mod = await importConfig(file);\n } catch (error) {\n // The one failure mode native type stripping has that a TypeScript-aware loader does not.\n if (/\\.[cm]?ts$/.test(file) && (error as { code?: string } | null)?.code === 'ERR_MODULE_NOT_FOUND') {\n throw new Error(\n `[rshono] ${file} imports a module Node could not resolve: ${(error as Error).message}\\n` +\n ' A .ts config is loaded by Node itself, which does not rewrite a .js specifier to the .ts file ' +\n 'beside it. Import it by its real extension, or move the config to rshono.config.mjs.',\n { cause: error },\n );\n }\n throw error;\n }\n if (!mod.default) {\n throw new Error(`[rshono] ${file} must \\`export default\\` a config object (use \\`defineConfig({ … })\\`).`);\n }\n return mod.default;\n}\n"]}
@@ -1,2 +1 @@
1
1
  export declare function loadEnvFiles(rootDir: string): void;
2
- //# sourceMappingURL=load-env.d.ts.map
@@ -1,67 +1,121 @@
1
1
  /**
2
- * Everything about a prerendered page that doesn't need a filesystem: where the build puts it, and
3
- * what it looks like once read back.
2
+ * Everything about a prerendered page that doesn't need a filesystem: where the build puts it, and what it
3
+ * looks like once read back.
4
4
  *
5
- * Split out of `ssg.ts` which owns the writing and the reading, both through `node:fs` because a
6
- * deploy target without a filesystem reads the very same layout out of an asset store, and importing
7
- * it from the fs module would drag `node:fs` and `node:crypto` into a bundle that has neither.
5
+ * Apart from `ssg.ts`, which does the reading and writing through `node:fs`, because a deploy target without a
6
+ * filesystem reads the same layout out of an asset store and importing it from there would drag `node:fs`
7
+ * into a bundle that has neither.
8
8
  */
9
9
  /**
10
- * The two representations of a page, prerendered side by side.
11
- *
12
- * A hard load wants the HTML document; a soft navigation asks the same URL for a flight payload.
13
- * Writing only the HTML meant every in-app click re-rendered a page that was already built, so the
14
- * prerender only ever paid off for cold loads and crawlers.
10
+ * The two representations of a page, prerendered side by side: a hard load wants the HTML document, a soft
11
+ * navigation asks the same URL for a flight payload. Without both, every in-app click would re-render a page
12
+ * that was already built.
15
13
  */
16
14
  export type PrerenderVariant = 'html' | 'flight';
17
15
  export declare const VARIANTS: {
18
16
  readonly html: {
19
17
  readonly file: 'index.html';
20
- readonly accept: 'text/html';
18
+ readonly headers: {};
21
19
  readonly contentType: 'text/html';
22
20
  };
23
21
  readonly flight: {
24
22
  readonly file: 'index.rsc';
25
- readonly accept: 'text/x-component';
23
+ readonly headers: {
24
+ readonly RSC: '1';
25
+ };
26
26
  readonly contentType: 'text/x-component';
27
27
  };
28
28
  };
29
29
  /**
30
- * Where a route's prerendered output lives, relative to the output root or `null` for a path that
31
- * cannot be prerendered at all (one with a param or a wildcard left in it).
30
+ * How a build-time document render tells the prerender pass that the app minted a CSP nonce for its path.
31
+ *
32
+ * The other half of the same contract as {@link VARIANTS}, and it exists because the pass cannot see the
33
+ * request it made: it renders through the app's own middleware inside the server bundle, in a module graph of
34
+ * its own, and keeps the body alone. What it needs to know is whether a *request* for this path will mint a
35
+ * nonce, because a document that will is one the framework re-renders per request — so the file the pass is
36
+ * about to write is one no deployment will read, and saying "prerendered" about it is a lie by omission.
37
+ *
38
+ * Set only while `RSHONO_PRERENDER` is in the environment, which is `rshono build`'s own process, so it never
39
+ * reaches a deployed response; and never stored, since only the body is.
40
+ */
41
+ export declare const PRERENDER_NONCE_HEADER = "x-rshono-prerender-nonce";
42
+ /**
43
+ * The index the build leaves beside the pages, naming every file it wrote — one `files` array of
44
+ * {@link ssgFilePath} names.
32
45
  *
33
- * Always `/`-separated, never the host's separator: the same string addresses a file on a filesystem
34
- * (`resolve()` and `join()` both accept forward slashes on Windows) and a key in an asset store,
35
- * where a backslash would simply be the wrong character.
46
+ * It exists so a reader can tell "this store has no page for that path" without asking the store, which is
47
+ * the difference between a `Map` lookup and a failed `readFile` (or, without a filesystem, a failed store
48
+ * fetch) on **every** request to a static route the build could not prerender — misses are deliberately not
49
+ * cached, so that one never warms up. Absent is not an error: a build from an older rshono has none, and a
50
+ * reader that finds none looks the way it always did.
36
51
  */
37
- export declare function ssgFilePath(routePath: string, variant?: PrerenderVariant): string | null;
52
+ export declare const SSG_MANIFEST_FILE = "manifest.json";
38
53
  /**
39
- * {@link ssgFilePath} for a path that came off a request, so traversal is a miss rather than a lookup.
54
+ * Where a path's prerendered output lives, relative to the output root or `null` for a path no single file
55
+ * can answer.
40
56
  *
41
- * The first line of defence for every deploy target: a store addressed by key has no `resolve()` to
42
- * fall back on, so `..` has to be refused here or not at all.
57
+ * **The one canonical form**, shared by the build that writes the file and by every deploy target that reads
58
+ * it back, because the two drifting apart is invisible: the build reports the page as prerendered and every
59
+ * request afterwards falls through to SSR, forever. Each segment is stored *decoded*, so `/docs/café` is
60
+ * `docs/café` whether the caller holds the path percent-encoded — the build, which interpolates `staticPaths`
61
+ * values into a URL — or already decoded, as Hono's `c.req.path` is: it runs `decodeURI` over any path
62
+ * containing a `%`. Decoded is also the form every other static file server addresses, so the tree stays
63
+ * servable by something that is not this framework.
64
+ *
65
+ * Always `/`-separated: the same string addresses a file on a filesystem, which accepts forward slashes on
66
+ * Windows too, and a key in an asset store, where a backslash would be the wrong character.
67
+ *
68
+ * `null` is everything that is not one file: a `.` or `..` segment — so traversal is a miss rather than a
69
+ * lookup, which a store addressed by key, with no `resolve()` to fall back on, depends on — an empty segment,
70
+ * and any segment {@link isStorableSegment} refuses as unportable.
43
71
  */
44
- export declare function prerenderedRelPath(requestPath: string, variant: PrerenderVariant): string | null;
72
+ export declare function ssgFilePath(path: string, variant?: PrerenderVariant): string | null;
73
+ /**
74
+ * {@link ssgFilePath} as a URL path, for a store addressed by one rather than by key. Every segment is escaped
75
+ * on the way out and the store decodes it back, so a page reached through a URL lands on the same file a
76
+ * filesystem target opens by name.
77
+ */
78
+ export declare function ssgAssetPath(relPath: string): string;
79
+ /**
80
+ * A bounded, insertion-ordered cache of prerendered pages, so a site with thousands of them keeps a working set
81
+ * rather than the whole build in memory. Only *hits* are stored — caching misses would let anyone mint entries
82
+ * by requesting paths that don't exist — and the files never change while the server is up.
83
+ *
84
+ * Bounded by **bytes**, not by entry count: a count says nothing about how much memory it stands for, and a
85
+ * page can be any size. At 128 entries — the previous limit — a documentation site with half-megabyte pages
86
+ * retained ~64 MB across the two variants, with nothing about the number to suggest it.
87
+ *
88
+ * A page larger than the whole budget is served and not stored, rather than evicting everything to hold one.
89
+ */
90
+ export declare function createPageCache(maxBytes?: number): {
91
+ get(key: string): PrerenderedPage | undefined;
92
+ set(key: string, page: PrerenderedPage): void;
93
+ };
94
+ /**
95
+ * Assembles a {@link PrerenderedPage} from a body just read out of the build. `storeEtag` is the validator the
96
+ * store supplied, where it has one: it already describes these exact bytes, so it is weakened rather than
97
+ * replaced by a fresh hash.
98
+ */
99
+ export declare function toPrerenderedPage(body: Uint8Array<ArrayBuffer>, storeEtag?: string | null): Promise<PrerenderedPage>;
45
100
  /** A prerendered page, ready to serve: its body and a validator derived from those exact bytes. */
46
101
  export interface PrerenderedPage {
47
- /** The document or the flight payload, depending on which {@link PrerenderVariant} was read. */
48
- body: string;
49
102
  /**
50
- * `Content-Length` for {@link body}, in bytes rather than characters.
51
- *
52
- * Served with the response because Hono sets no length for an in-memory body, and without one the
53
- * compressor cannot tell a 300-byte page from a 300 KB one — so it gzips both, including the ones
54
- * where the framing costs more than it saves.
103
+ * The document or the flight payload, depending on which {@link PrerenderVariant} was read. Bytes rather
104
+ * than a string, because this cache entry is served verbatim to every request that hits it — as a string,
105
+ * each would pay a fresh UTF-8 encode of the whole page.
106
+ */
107
+ body: Uint8Array<ArrayBuffer>;
108
+ /**
109
+ * `Content-Length` for {@link body}, in bytes. Served explicitly because Hono sets no length for an
110
+ * in-memory body, and a proxy in front is entitled to make decisions with it.
55
111
  */
56
112
  contentLength: string;
57
113
  /**
58
114
  * `ETag` for the page, so a revalidating client can be answered with a 304 instead of the body.
59
115
  *
60
- * Deliberately **weak**. The bytes on the wire depend on whether the client took gzip, and a
61
- * strong validator would have to differ between those two so the 200 and the 304 that
62
- * revalidates it would disagree, and a cache would treat them as different pages. A weak tag
63
- * says "the same representation", which is exactly what is true across content codings.
116
+ * Deliberately **weak**: the bytes on the wire depend on whether something in front re-encoded them, so a
117
+ * strong validator would differ per coding and a cache would treat the 200 and the 304 that revalidates it
118
+ * as different pages. A weak tag says "the same representation", which stays true across codings.
64
119
  */
65
120
  etag: string;
66
121
  }
67
- //# sourceMappingURL=prerendered.d.ts.map