@nifrajs/web 2.1.0 → 2.3.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 (194) hide show
  1. package/README.md +3 -3
  2. package/dist/build-vite.d.ts +68 -0
  3. package/dist/build-vite.d.ts.map +1 -0
  4. package/dist/build-vite.js +433 -0
  5. package/dist/build-vite.js.map +1 -0
  6. package/dist/build.d.ts +174 -65
  7. package/dist/build.d.ts.map +1 -1
  8. package/dist/build.js +376 -135
  9. package/dist/build.js.map +1 -1
  10. package/dist/bun-dev-entry.d.ts +83 -0
  11. package/dist/bun-dev-entry.d.ts.map +1 -0
  12. package/dist/bun-dev-entry.js +136 -0
  13. package/dist/bun-dev-entry.js.map +1 -0
  14. package/dist/chunk-names.js +1 -1
  15. package/dist/client.d.ts +3 -9
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +209 -27
  18. package/dist/client.js.map +1 -1
  19. package/dist/conformance.d.ts +2 -2
  20. package/dist/conformance.js +3 -3
  21. package/dist/deferred.d.ts +11 -11
  22. package/dist/deferred.d.ts.map +1 -1
  23. package/dist/deferred.js +16 -16
  24. package/dist/dev-error.d.ts +2 -2
  25. package/dist/dev-error.js +3 -3
  26. package/dist/dev-port.d.ts +36 -0
  27. package/dist/dev-port.d.ts.map +1 -0
  28. package/dist/dev-port.js +48 -0
  29. package/dist/dev-port.js.map +1 -0
  30. package/dist/dev.d.ts +62 -5
  31. package/dist/dev.d.ts.map +1 -1
  32. package/dist/dev.js +419 -60
  33. package/dist/dev.js.map +1 -1
  34. package/dist/draft.d.ts +44 -7
  35. package/dist/draft.d.ts.map +1 -1
  36. package/dist/draft.js +92 -9
  37. package/dist/draft.js.map +1 -1
  38. package/dist/fn-state.d.ts +56 -0
  39. package/dist/fn-state.d.ts.map +1 -0
  40. package/dist/fn-state.js +77 -0
  41. package/dist/fn-state.js.map +1 -0
  42. package/dist/fn.d.ts +111 -0
  43. package/dist/fn.d.ts.map +1 -0
  44. package/dist/fn.js +125 -0
  45. package/dist/fn.js.map +1 -0
  46. package/dist/fonts-google.d.ts +9 -9
  47. package/dist/fonts-google.js +10 -10
  48. package/dist/fonts.d.ts +7 -7
  49. package/dist/fonts.js +1 -1
  50. package/dist/forms.d.ts +8 -8
  51. package/dist/fs.d.ts +15 -2
  52. package/dist/fs.d.ts.map +1 -1
  53. package/dist/fs.js +8 -2
  54. package/dist/fs.js.map +1 -1
  55. package/dist/index.d.ts +266 -74
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +869 -179
  58. package/dist/index.js.map +1 -1
  59. package/dist/internal/head-attributes.d.ts +15 -0
  60. package/dist/internal/head-attributes.d.ts.map +1 -0
  61. package/dist/internal/head-attributes.js +99 -0
  62. package/dist/internal/head-attributes.js.map +1 -0
  63. package/dist/internal/script-types.d.ts +14 -0
  64. package/dist/internal/script-types.d.ts.map +1 -0
  65. package/dist/internal/script-types.js +17 -0
  66. package/dist/internal/script-types.js.map +1 -0
  67. package/dist/internal/server-boundary.d.ts +60 -0
  68. package/dist/internal/server-boundary.d.ts.map +1 -0
  69. package/dist/internal/server-boundary.js +174 -0
  70. package/dist/internal/server-boundary.js.map +1 -0
  71. package/dist/internal/timing-safe-equal.d.ts +16 -0
  72. package/dist/internal/timing-safe-equal.d.ts.map +1 -0
  73. package/dist/internal/timing-safe-equal.js +26 -0
  74. package/dist/internal/timing-safe-equal.js.map +1 -0
  75. package/dist/internal/vite-import.d.ts +63 -0
  76. package/dist/internal/vite-import.d.ts.map +1 -0
  77. package/dist/internal/vite-import.js +94 -0
  78. package/dist/internal/vite-import.js.map +1 -0
  79. package/dist/islands.d.ts +5 -5
  80. package/dist/islands.js +5 -5
  81. package/dist/isr.d.ts +15 -15
  82. package/dist/isr.d.ts.map +1 -1
  83. package/dist/isr.js +8 -21
  84. package/dist/isr.js.map +1 -1
  85. package/dist/llms-txt.d.ts.map +1 -1
  86. package/dist/llms-txt.js +12 -6
  87. package/dist/llms-txt.js.map +1 -1
  88. package/dist/manifest.d.ts +121 -40
  89. package/dist/manifest.d.ts.map +1 -1
  90. package/dist/manifest.js +136 -26
  91. package/dist/manifest.js.map +1 -1
  92. package/dist/module-graph.d.ts +91 -0
  93. package/dist/module-graph.d.ts.map +1 -0
  94. package/dist/module-graph.js +92 -0
  95. package/dist/module-graph.js.map +1 -0
  96. package/dist/navigation.d.ts +120 -5
  97. package/dist/navigation.d.ts.map +1 -1
  98. package/dist/navigation.js +37 -5
  99. package/dist/navigation.js.map +1 -1
  100. package/dist/plugins/css-modules.d.ts +3 -3
  101. package/dist/plugins/css-modules.d.ts.map +1 -1
  102. package/dist/plugins/css-modules.js +0 -0
  103. package/dist/plugins/css-modules.js.map +1 -1
  104. package/dist/plugins/kit.d.ts +8 -8
  105. package/dist/plugins/kit.js +9 -9
  106. package/dist/plugins/scss.js +3 -3
  107. package/dist/plugins/vite-leak-guard.d.ts +45 -0
  108. package/dist/plugins/vite-leak-guard.d.ts.map +1 -0
  109. package/dist/plugins/vite-leak-guard.js +67 -0
  110. package/dist/plugins/vite-leak-guard.js.map +1 -0
  111. package/dist/plugins/vite-server-fn.d.ts +23 -0
  112. package/dist/plugins/vite-server-fn.d.ts.map +1 -0
  113. package/dist/plugins/vite-server-fn.js +51 -0
  114. package/dist/plugins/vite-server-fn.js.map +1 -0
  115. package/dist/plugins/vite-server-only.d.ts +49 -0
  116. package/dist/plugins/vite-server-only.d.ts.map +1 -0
  117. package/dist/plugins/vite-server-only.js +57 -0
  118. package/dist/plugins/vite-server-only.js.map +1 -0
  119. package/dist/prerender.d.ts +5 -5
  120. package/dist/prerender.js +7 -7
  121. package/dist/public-dir.d.ts +36 -0
  122. package/dist/public-dir.d.ts.map +1 -0
  123. package/dist/public-dir.js +89 -0
  124. package/dist/public-dir.js.map +1 -0
  125. package/dist/query.d.ts +10 -10
  126. package/dist/query.js +15 -15
  127. package/dist/route-manifest.d.ts +91 -0
  128. package/dist/route-manifest.d.ts.map +1 -0
  129. package/dist/route-manifest.js +128 -0
  130. package/dist/route-manifest.js.map +1 -0
  131. package/dist/router.d.ts +94 -22
  132. package/dist/router.d.ts.map +1 -1
  133. package/dist/router.js +170 -58
  134. package/dist/router.js.map +1 -1
  135. package/dist/search.d.ts +94 -0
  136. package/dist/search.d.ts.map +1 -0
  137. package/dist/search.js +232 -0
  138. package/dist/search.js.map +1 -0
  139. package/dist/server-only.d.ts +6 -6
  140. package/dist/server-only.js +7 -7
  141. package/dist/service-worker.d.ts +56 -0
  142. package/dist/service-worker.d.ts.map +1 -0
  143. package/dist/service-worker.js +127 -0
  144. package/dist/service-worker.js.map +1 -0
  145. package/dist/vite.d.ts +25 -17
  146. package/dist/vite.d.ts.map +1 -1
  147. package/dist/vite.js +75 -33
  148. package/dist/vite.js.map +1 -1
  149. package/package.json +48 -4
  150. package/server-manifest.d.ts +31 -0
  151. package/src/build-vite.ts +553 -0
  152. package/src/build.ts +563 -172
  153. package/src/bun-dev-entry.ts +184 -0
  154. package/src/chunk-names.ts +1 -1
  155. package/src/client.ts +221 -30
  156. package/src/conformance.ts +3 -3
  157. package/src/deferred.ts +17 -17
  158. package/src/dev-error.ts +3 -3
  159. package/src/dev-port.ts +63 -0
  160. package/src/dev.ts +468 -66
  161. package/src/draft.ts +124 -12
  162. package/src/fn-state.ts +107 -0
  163. package/src/fn.ts +199 -0
  164. package/src/fonts-google.ts +13 -13
  165. package/src/fonts.ts +7 -7
  166. package/src/forms.ts +8 -8
  167. package/src/fs.ts +24 -6
  168. package/src/index.ts +1174 -225
  169. package/src/internal/head-attributes.ts +109 -0
  170. package/src/internal/script-types.ts +18 -0
  171. package/src/internal/server-boundary.ts +187 -0
  172. package/src/internal/timing-safe-equal.ts +24 -0
  173. package/src/internal/vite-import.ts +92 -0
  174. package/src/islands.ts +7 -7
  175. package/src/isr.ts +18 -30
  176. package/src/llms-txt.ts +13 -5
  177. package/src/manifest.ts +264 -48
  178. package/src/module-graph.ts +146 -0
  179. package/src/navigation.ts +151 -5
  180. package/src/plugins/css-modules.ts +0 -0
  181. package/src/plugins/kit.ts +11 -11
  182. package/src/plugins/scss.ts +3 -3
  183. package/src/plugins/vite-leak-guard.ts +110 -0
  184. package/src/plugins/vite-server-fn.ts +67 -0
  185. package/src/plugins/vite-server-only.ts +75 -0
  186. package/src/prerender.ts +10 -10
  187. package/src/public-dir.ts +108 -0
  188. package/src/query.ts +22 -22
  189. package/src/route-manifest.ts +240 -0
  190. package/src/router.ts +288 -74
  191. package/src/search.ts +297 -0
  192. package/src/server-only.ts +7 -7
  193. package/src/service-worker.ts +165 -0
  194. package/src/vite.ts +103 -49
@@ -7,7 +7,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  /**
10
- * `@nifrajs/web/plugins` shared kit — the small infrastructure every CSS-bearing asset plugin reuses, so
10
+ * `@nifrajs/web/plugins` shared kit - the small infrastructure every CSS-bearing asset plugin reuses, so
11
11
  * P1 (CSS Modules), P2 (SCSS), and any future P4 (LESS/Stylus/PostCSS) don't each re-implement the
12
12
  * CSS→client-bundle wiring, the deterministic hash, or the optional-peer guard. Internal to
13
13
  * `@nifrajs/web` (imported by the sibling plugin modules); not a public subpath.
@@ -27,7 +27,7 @@ function packageRootOf(startDir) {
27
27
  break;
28
28
  const parent = dirname(dir);
29
29
  if (parent === dir) {
30
- dir = startDir; // reached the filesystem root with no package.json — anchor on the file's own dir
30
+ dir = startDir; // reached the filesystem root with no package.json - anchor on the file's own dir
31
31
  break;
32
32
  }
33
33
  dir = parent;
@@ -36,11 +36,11 @@ function packageRootOf(startDir) {
36
36
  return dir;
37
37
  }
38
38
  /**
39
- * A **package-root-relative**, forward-slashed form of an absolute path — the input to {@link hash8} for
39
+ * A **package-root-relative**, forward-slashed form of an absolute path - the input to {@link hash8} for
40
40
  * any build-stable identifier (e.g. CSS-module scoped names). Anchoring on the file's nearest
41
41
  * `package.json` (not the absolute path, not `process.cwd()`) makes the result independent of BOTH the
42
- * machine's directory layout AND the working directory: the dom build and the ssr runtime — even on
43
- * different machines or from different cwds — derive the SAME relative path for a given file, so their
42
+ * machine's directory layout AND the working directory: the dom build and the ssr runtime - even on
43
+ * different machines or from different cwds - derive the SAME relative path for a given file, so their
44
44
  * scoped class maps always agree. (Hashing the absolute path would differ across CI/host; a
45
45
  * cwd-relative path would silently desync if the SSR server started from a non-project-root cwd.)
46
46
  */
@@ -49,7 +49,7 @@ export function reproduciblePath(absolutePath) {
49
49
  return relative(root, absolutePath).replaceAll("\\", "/");
50
50
  }
51
51
  /**
52
- * Deterministic 8-hex hash (djb2/xor). Stable across builds — no `Date.now`/`Math.random` — so build
52
+ * Deterministic 8-hex hash (djb2/xor). Stable across builds - no `Date.now`/`Math.random` - so build
53
53
  * output is reproducible. The single hash implementation behind CSS-module scoped names (and a drop-in
54
54
  * for any SFC scope id).
55
55
  */
@@ -62,7 +62,7 @@ export function hash8(input) {
62
62
  /**
63
63
  * Wire the virtual-CSS-module handlers onto `build` for `namespace`, returning an {@link StylesheetEmitter}.
64
64
  * The `namespace` must be a plain identifier (letters/`-`); it's used verbatim as the import suffix and
65
- * the Bun namespace. Only the `"dom"` build should emit CSS — the `"ssr"` build ships no stylesheet.
65
+ * the Bun namespace. Only the `"dom"` build should emit CSS - the `"ssr"` build ships no stylesheet.
66
66
  */
67
67
  export function createStylesheetEmitter(build, namespace) {
68
68
  const suffix = `?${namespace}`;
@@ -85,7 +85,7 @@ export function createStylesheetEmitter(build, namespace) {
85
85
  }
86
86
  /**
87
87
  * Load an optional peer compiler at build time, throwing a consistent, actionable install-hint error if
88
- * it's absent — the `@vue/compiler-sfc` peer pattern, centralized. Build-time only, so the dynamic
88
+ * it's absent - the `@vue/compiler-sfc` peer pattern, centralized. Build-time only, so the dynamic
89
89
  * `import` (which keeps the peer out of the package's hard dependencies) is correct here.
90
90
  */
91
91
  export async function requirePeer(specifier, hint) {
@@ -93,7 +93,7 @@ export async function requirePeer(specifier, hint) {
93
93
  return (await import(__rewriteRelativeImportExtension(specifier)));
94
94
  }
95
95
  catch (err) {
96
- // Only a genuine resolution failure means "not installed" — surface anything else (a corrupt
96
+ // Only a genuine resolution failure means "not installed" - surface anything else (a corrupt
97
97
  // install, a native-binding error, a throw at module top-level) as the real error rather than
98
98
  // masking it behind a misleading "please install it" hint.
99
99
  if (err.code === "ERR_MODULE_NOT_FOUND") {
@@ -1,17 +1,17 @@
1
1
  /**
2
- * `@nifrajs/web/plugins/scss` — a SASS/SCSS (`*.scss`, `*.sass`) Bun plugin, in its OWN module so the SSR
2
+ * `@nifrajs/web/plugins/scss` - a SASS/SCSS (`*.scss`, `*.sass`) Bun plugin, in its OWN module so the SSR
3
3
  * preload registers it BEFORE any Sass file loads. Mirrors the `@nifrajs/web-vue/plugin` seam: pass
4
4
  * `"dom"` for the client bundle (`buildClient({ plugins: [...] })`) and preload `"ssr"` for the server
5
5
  * (`bun --preload`).
6
6
  *
7
- * The Dart Sass compiler is an **optional peer** (`sass`, or the faster `sass-embedded` — same API), not
7
+ * The Dart Sass compiler is an **optional peer** (`sass`, or the faster `sass-embedded` - same API), not
8
8
  * a hard dependency: it's loaded on first Sass file and fails loud with an install hint if absent. Pass
9
9
  * your own `compiler` to override (e.g. `sass-embedded`, or a stub in tests).
10
10
  *
11
11
  * Composes with CSS Modules: a `*.module.scss` / `*.module.sass` file is compiled to CSS and then run
12
12
  * through the same scoped-class transform as `@nifrajs/web/plugins/css-modules`, so its `import styles`
13
13
  * yields the `{ original: scoped }` map (SSR/dom class-map parity included). A plain `*.scss` is a
14
- * side-effect import — its CSS is bundled (dom) and it resolves to an empty module (ssr).
14
+ * side-effect import - its CSS is bundled (dom) and it resolves to an empty module (ssr).
15
15
  */
16
16
  import { dirname } from "node:path";
17
17
  import { pathToFileURL } from "node:url";
@@ -0,0 +1,45 @@
1
+ import { type RollupBundleLike } from "../module-graph.js";
2
+ /**
3
+ * The Rollup plugin-context slice this uses: `getModuleInfo` for a module's resolved imports, and `error`
4
+ * to fail the build. Typed structurally so the file needs no `rollup`/`vite` type dependency.
5
+ */
6
+ interface RollupPluginContext {
7
+ getModuleInfo(id: string): {
8
+ readonly importedIds?: readonly string[];
9
+ readonly dynamicallyImportedIds?: readonly string[];
10
+ } | null;
11
+ /**
12
+ * Takes an `Error`, never a string.
13
+ *
14
+ * Handed a string, rolldown synthesizes its own error type and calls `Error.captureStackTrace` on a
15
+ * plain object - which Bun rejects with "First argument must be an Error object", and THAT becomes the
16
+ * build failure. The guard still fires, but its message is replaced by an internal one naming nothing,
17
+ * so a real client leak reports as a stack-trace complaint. Passing an Error skips that construction
18
+ * entirely and the message survives.
19
+ */
20
+ error(error: Error): never;
21
+ }
22
+ /** The minimal Rollup plugin shape this returns - `generateBundle` bound to the plugin context. */
23
+ export interface LeakGuardPlugin {
24
+ readonly name: string;
25
+ /**
26
+ * The finding that failed the build, or `undefined` when the bundle was clean.
27
+ *
28
+ * Recorded as well as thrown because the throw does not reliably survive. A plugin error is handed to
29
+ * the bundler, which builds its own error to report it, and on some Bun + rolldown combinations that
30
+ * construction itself fails (`Error.captureStackTrace` rejecting a non-Error) - so what surfaces is an
31
+ * internal complaint naming neither the leaked module nor its import chain. A caller that owns the
32
+ * build can read this afterwards and raise the real message from its own code, where nothing can
33
+ * rewrite it. A leak is the one failure that must stay legible: the whole point is naming what
34
+ * reached the browser.
35
+ */
36
+ leak?: string;
37
+ generateBundle(this: RollupPluginContext, options: unknown, bundle: RollupBundleLike): void;
38
+ }
39
+ /**
40
+ * A Vite/Rollup plugin that fails the build when server-only code or a `node:` builtin reaches the client
41
+ * bundle - the same two guards, and the same error messages, as nifra's Bun production build.
42
+ */
43
+ export declare function viteLeakGuard(): LeakGuardPlugin;
44
+ export {};
45
+ //# sourceMappingURL=vite-leak-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-leak-guard.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-leak-guard.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE5E;;;GAGG;AACH,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG;QACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QACxC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KACpD,GAAG,IAAI,CAAA;IACR;;;;;;;;OAQG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAA;CAC3B;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAA;CAC5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,eAAe,CAyB/C"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `@nifrajs/web/plugins/vite-leak-guard` - nifra's two client-leak guards, for a Vite/Rollup production
3
+ * build.
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * nifra's production default is Bun, and that is not changing: a Vite/Rollup production build inherits
8
+ * Rollup's build profile and gives up the Bun-native advantage nifra competes on. But some apps depend on
9
+ * a Vite-only transform that has no Bun equivalent, and for those a Vite production build is the escape
10
+ * hatch. The moment that hatch exists, the two client-leak guards have to come with it - they are security
11
+ * guards, not lints: one stops secrets and database access from shipping to a browser. A second production
12
+ * pipeline arriving WITHOUT them, or with a hastily re-implemented "mostly ported" copy, is the failure the
13
+ * neutral module-graph seam was built to prevent.
14
+ *
15
+ * So this is not a second implementation. The detection logic (`detectNodeBuiltinsInClient`,
16
+ * `detectServerOnlyInClient`) and the failure MESSAGES (`formatNodeBuiltinLeak`, `formatServerOnlyLeak`)
17
+ * are the exact same functions the Bun build calls. This plugin only adapts Rollup's bundle shape into the
18
+ * neutral `ClientModuleGraph` (`fromRollupBundle`) and runs them. A leak reads identically whichever
19
+ * bundler produced it, and there is one place to change if a guard changes.
20
+ *
21
+ * ## How
22
+ *
23
+ * In `generateBundle` the whole client graph is available: each output chunk lists its `moduleIds`, and
24
+ * `this.getModuleInfo(id)` gives each module's resolved import edges. That is exactly what
25
+ * `fromRollupBundle` needs. Findings fail the build through Rollup's `this.error`, so the message lands in
26
+ * the build output the same way a Bun-build throw does.
27
+ *
28
+ * Add it to the `plugins` of a Vite production build (the LAST plugin, so it sees the final graph):
29
+ *
30
+ * // vite.config.ts (production client build)
31
+ * import { viteLeakGuard } from "@nifrajs/web/plugins/vite-leak-guard"
32
+ * export default { build: { rollupOptions: { plugins: [viteLeakGuard()] } } }
33
+ */
34
+ import { detectNodeBuiltinsInClient, detectServerOnlyInClient, formatNodeBuiltinLeak, formatServerOnlyLeak, } from "../build.js";
35
+ import { fromRollupBundle } from "../module-graph.js";
36
+ /**
37
+ * A Vite/Rollup plugin that fails the build when server-only code or a `node:` builtin reaches the client
38
+ * bundle - the same two guards, and the same error messages, as nifra's Bun production build.
39
+ */
40
+ export function viteLeakGuard() {
41
+ const plugin = {
42
+ name: "nifra:leak-guard",
43
+ generateBundle(_options, bundle) {
44
+ // Resolved import edges per module, straight from Rollup's graph. Dynamic imports count too: a
45
+ // `node:`/server-only module reached only via `import()` still ships to the browser.
46
+ const importsOf = (id) => {
47
+ const info = this.getModuleInfo(id);
48
+ if (info === null)
49
+ return [];
50
+ return [...(info.importedIds ?? []), ...(info.dynamicallyImportedIds ?? [])];
51
+ };
52
+ const graph = fromRollupBundle(bundle, importsOf);
53
+ // Node-builtin guard first, then server-only - the same order as the Bun build, so the first error a
54
+ // dev sees is the same across pipelines when a module trips both.
55
+ const leak = formatNodeBuiltinLeak(detectNodeBuiltinsInClient(graph)) ??
56
+ formatServerOnlyLeak(detectServerOnlyInClient(graph));
57
+ if (leak === undefined)
58
+ return;
59
+ // Record before throwing: the throw is what fails the build for a standalone user of this plugin,
60
+ // and the record is what lets a caller that owns the build re-raise the real message (see `leak`).
61
+ plugin.leak = leak;
62
+ this.error(new Error(leak));
63
+ },
64
+ };
65
+ return plugin;
66
+ }
67
+ //# sourceMappingURL=vite-leak-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-leak-guard.js","sourceRoot":"","sources":["../../src/plugins/vite-leak-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAA;AAyC5E;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAoB;QAC9B,IAAI,EAAE,kBAAkB;QACxB,cAAc,CAAC,QAAQ,EAAE,MAAM;YAC7B,+FAA+F;YAC/F,qFAAqF;YACrF,MAAM,SAAS,GAAG,CAAC,EAAU,EAAqB,EAAE;gBAClD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;gBACnC,IAAI,IAAI,KAAK,IAAI;oBAAE,OAAO,EAAE,CAAA;gBAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,CAAA;YAC9E,CAAC,CAAA;YACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;YACjD,qGAAqG;YACrG,kEAAkE;YAClE,MAAM,IAAI,GACR,qBAAqB,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;gBACxD,oBAAoB,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAA;YACvD,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAM;YAC9B,kGAAkG;YAClG,mGAAmG;YACnG,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;YAClB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QAC7B,CAAC;KACF,CAAA;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,23 @@
1
+ /** The slice of a Vite/Rollup plugin this returns. Structural, so `vite` stays an optional peer. */
2
+ export interface ServerFnStubPlugin {
3
+ readonly name: string;
4
+ readonly enforce: "pre";
5
+ /** Applied only to the CLIENT build; the server build keeps the real module. */
6
+ readonly applyToEnvironment?: (environment: {
7
+ readonly name: string;
8
+ }) => boolean;
9
+ transform(code: string, id: string, options?: {
10
+ readonly ssr?: boolean;
11
+ }): {
12
+ code: string;
13
+ map: null;
14
+ } | null;
15
+ }
16
+ /**
17
+ * Replace every `*.fn` module with its client stubs.
18
+ *
19
+ * Client-only by construction: nifra passes this to the client build and the dev server, never to the
20
+ * SSR build, because the server is what actually runs these functions.
21
+ */
22
+ export declare function viteServerFnStub(): ServerFnStubPlugin;
23
+ //# sourceMappingURL=vite-server-fn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-server-fn.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-server-fn.ts"],"names":[],"mappings":"AA2BA,oGAAoG;AACpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,gFAAgF;IAChF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjF,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtC;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAiBrD"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `@nifrajs/web/plugins/vite-server-fn` - the Vite half of the server-function client transform.
3
+ *
4
+ * A `*.fn` module must never reach a browser: it holds the function bodies and everything they import.
5
+ * The Bun build replaces each one with client stubs, and this does the same for Vite, in both the dev
6
+ * server and the production build.
7
+ *
8
+ * ## Why the generation is not in here
9
+ *
10
+ * The stub text comes from `internal/server-boundary.ts`, shared verbatim with the Bun plugin. Two
11
+ * implementations of "what a stub looks like" would drift, and the drift would be invisible until a
12
+ * client that worked in dev 404'd in production - the exact shape of failure this codebase has already
13
+ * paid for once. A parity test asserts both pipelines emit the same bytes.
14
+ *
15
+ * ## Why `transform` and not `load`
16
+ *
17
+ * Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
18
+ * `transform` receives the source Vite already read, and returning replacement code discards the
19
+ * original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts this
20
+ * ahead of framework transforms, which have no business seeing server code either.
21
+ */
22
+ import { generateServerFnStub, SERVER_FN_MODULE, serverFnNamespace, } from "../internal/server-boundary.js";
23
+ /** Strips a Vite id's `?query` and `#hash` so the suffix test sees a plain path. */
24
+ const bare = (id) => id.split("?")[0]?.split("#")[0] ?? id;
25
+ /**
26
+ * Replace every `*.fn` module with its client stubs.
27
+ *
28
+ * Client-only by construction: nifra passes this to the client build and the dev server, never to the
29
+ * SSR build, because the server is what actually runs these functions.
30
+ */
31
+ export function viteServerFnStub() {
32
+ return {
33
+ name: "nifra:server-fn-stub",
34
+ enforce: "pre",
35
+ // Vite 6+ environments: the SSR environment must keep the real module. Older Vite ignores this and
36
+ // relies on nifra only registering the plugin on the client side.
37
+ applyToEnvironment: (environment) => environment.name === "client",
38
+ transform(code, id, options) {
39
+ // Vite 5 ignores `applyToEnvironment` but supplies this transform context for SSR.
40
+ if (options?.ssr === true)
41
+ return null;
42
+ const path = bare(id);
43
+ if (!SERVER_FN_MODULE.test(path))
44
+ return null;
45
+ // `map: null` is honest rather than lazy: the stub shares no lines with the source, so any
46
+ // mapping would point a debugger at unrelated code.
47
+ return { code: generateServerFnStub(code, serverFnNamespace(path)), map: null };
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=vite-server-fn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-server-fn.js","sourceRoot":"","sources":["../../src/plugins/vite-server-fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gCAAgC,CAAA;AAevC,oFAAoF;AACpF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,KAAK;QACd,mGAAmG;QACnG,kEAAkE;QAClE,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;QAClE,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO;YACzB,mFAAmF;YACnF,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC7C,2FAA2F;YAC3F,oDAAoD;YACpD,OAAO,EAAE,IAAI,EAAE,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACjF,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
3
+ *
4
+ * A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
5
+ * subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
6
+ * for Vite, in both the dev server and the production build.
7
+ *
8
+ * ## Why this exists separately
9
+ *
10
+ * The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
11
+ * the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
12
+ * and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
13
+ * worse than none, because the file name reads as protection everywhere.
14
+ *
15
+ * `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
16
+ * cannot transform. `.server` now matches it.
17
+ *
18
+ * ## Why `transform` and not `load`
19
+ *
20
+ * Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
21
+ * `transform` receives the source Vite already read, and returning replacement code discards the
22
+ * original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
23
+ * this ahead of framework transforms, which have no business seeing server code either.
24
+ */
25
+ import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement } from "../internal/server-boundary.js";
26
+ export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
27
+ /** The slice of a Vite/Rollup plugin this returns. Structural, so `vite` stays an optional peer. */
28
+ export interface ServerOnlyEmptyPlugin {
29
+ readonly name: string;
30
+ readonly enforce: "pre";
31
+ /** Applied only to the CLIENT build; the server build keeps the real module. */
32
+ readonly applyToEnvironment?: (environment: {
33
+ readonly name: string;
34
+ }) => boolean;
35
+ transform(code: string, id: string, options?: {
36
+ readonly ssr?: boolean;
37
+ }): {
38
+ code: string;
39
+ map: null;
40
+ } | null;
41
+ }
42
+ /**
43
+ * Empty every `*.server` module in the client build.
44
+ *
45
+ * Client-only by construction: nifra passes this to the client build and the dev server, never to the
46
+ * SSR build, where the real module is what runs.
47
+ */
48
+ export declare function viteServerOnlyEmpty(): ServerOnlyEmptyPlugin;
49
+ //# sourceMappingURL=vite-server-only.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-server-only.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,gCAAgC,CAAA;AAIvC,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAEjF,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,gFAAgF;IAChF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjF,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtC;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,qBAAqB,CAiB3D"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
3
+ *
4
+ * A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
5
+ * subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
6
+ * for Vite, in both the dev server and the production build.
7
+ *
8
+ * ## Why this exists separately
9
+ *
10
+ * The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
11
+ * the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
12
+ * and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
13
+ * worse than none, because the file name reads as protection everywhere.
14
+ *
15
+ * `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
16
+ * cannot transform. `.server` now matches it.
17
+ *
18
+ * ## Why `transform` and not `load`
19
+ *
20
+ * Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
21
+ * `transform` receives the source Vite already read, and returning replacement code discards the
22
+ * original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
23
+ * this ahead of framework transforms, which have no business seeing server code either.
24
+ */
25
+ import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement, } from "../internal/server-boundary.js";
26
+ // Re-exported so a caller wiring its own bundler gets the matcher from the same owner the pipelines
27
+ // use, rather than writing a fifth copy of the regex.
28
+ export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
29
+ /** Strips a Vite id's `?query` and `#hash` so the suffix test sees a plain path. */
30
+ const bare = (id) => id.split("?")[0]?.split("#")[0] ?? id;
31
+ /**
32
+ * Empty every `*.server` module in the client build.
33
+ *
34
+ * Client-only by construction: nifra passes this to the client build and the dev server, never to the
35
+ * SSR build, where the real module is what runs.
36
+ */
37
+ export function viteServerOnlyEmpty() {
38
+ return {
39
+ name: "nifra:server-only-empty",
40
+ enforce: "pre",
41
+ // Vite 6+ environments: the SSR environment must keep the real module. Older Vite ignores this and
42
+ // relies on nifra only registering the plugin on the client side.
43
+ applyToEnvironment: (environment) => environment.name === "client",
44
+ transform(code, id, options) {
45
+ // Vite 5 ignores `applyToEnvironment` but supplies this transform context for SSR.
46
+ if (options?.ssr === true)
47
+ return null;
48
+ const path = bare(id);
49
+ if (!SERVER_ONLY_MODULE.test(path))
50
+ return null;
51
+ // `map: null` is honest rather than lazy: the replacement shares no lines with the source, so any
52
+ // mapping would point a debugger at unrelated code.
53
+ return { code: viteServerOnlyReplacement(code), map: null };
54
+ },
55
+ };
56
+ }
57
+ //# sourceMappingURL=vite-server-only.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-server-only.js","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAA;AAEvC,oGAAoG;AACpG,sDAAsD;AACtD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAejF,oFAAoF;AACpF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,KAAK;QACd,mGAAmG;QACnG,kEAAkE;QAClE,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;QAClE,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO;YACzB,mFAAmF;YACnF,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC/C,kGAAkG;YAClG,oDAAoD;YACpD,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QAC7D,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { type RouteEntry } from "./manifest.js";
2
- /** Minimal app surface the driver needs — just a fetch handler (a built `createWebApp`). */
2
+ /** Minimal app surface the driver needs - just a fetch handler (a built `createWebApp`). */
3
3
  export interface PrerenderApp {
4
4
  fetch(req: Request): Response | Promise<Response>;
5
5
  }
@@ -33,7 +33,7 @@ export interface PrerenderResult {
33
33
  readonly path: string;
34
34
  readonly reason: string;
35
35
  }[];
36
- /** Per dynamic route pattern, its `getStaticPaths` `fallback` (`"ssr"` default) — so the deploy
36
+ /** Per dynamic route pattern, its `getStaticPaths` `fallback` (`"ssr"` default) - so the deploy
37
37
  * layer knows whether unlisted paths should hit the worker (`"ssr"`) or 404 (`"404"`). */
38
38
  readonly fallbacks: Readonly<Record<string, "ssr" | "404">>;
39
39
  }
@@ -44,7 +44,7 @@ export declare function htmlFileFor(pattern: string): string;
44
44
  export declare function dataFileFor(pattern: string): string;
45
45
  /**
46
46
  * Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
47
- * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped —
47
+ * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped -
48
48
  * the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
49
49
  * worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
50
50
  * non-opted/dynamic route is recorded in `skipped` and the build continues.
@@ -59,7 +59,7 @@ export interface CloudflarePagesRoutes {
59
59
  }
60
60
  export interface CloudflarePagesRoutesOptions {
61
61
  /** Prerendered request paths (e.g. `prerenderRoutes(...).prerendered.map(p => p.path)`, or the
62
- * build's `prerendered.json`). Each is excluded from the worker — plus its static `_data.json`. */
62
+ * build's `prerendered.json`). Each is excluded from the worker - plus its static `_data.json`. */
63
63
  readonly prerendered: readonly string[];
64
64
  /** Extra globs to keep OFF the worker (CDN-served). Default `["/assets/*"]` (the hashed bundle). */
65
65
  readonly staticGlobs?: readonly string[];
@@ -71,7 +71,7 @@ export interface CloudflarePagesRoutesOptions {
71
71
  *
72
72
  * ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
73
73
  * costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
74
- * `/blog/*`) instead of listing every path — pass those via `staticGlobs` and a smaller `prerendered`.
74
+ * `/blog/*`) instead of listing every path - pass those via `staticGlobs` and a smaller `prerendered`.
75
75
  */
76
76
  export declare function cloudflarePagesRoutes(options: CloudflarePagesRoutesOptions): CloudflarePagesRoutes;
77
77
  //# sourceMappingURL=prerender.d.ts.map
package/dist/prerender.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Build-time SSG driver. Prerendering is framework-agnostic by construction: a nifra SSR page is
3
3
  * "just `app.fetch(GET)` returning a streamed `Response`", so prerendering = drive that same handler
4
4
  * at build with a synthetic request, drain the body to bytes, and write the HTML to disk. No adapter
5
- * (React/Solid/Vue/Preact/Svelte) is touched — this sits entirely above the render seam.
5
+ * (React/Solid/Vue/Preact/Svelte) is touched - this sits entirely above the render seam.
6
6
  *
7
7
  * Handles **static** routes (opt in via `export const prerender = true`) and **dynamic** `:param`
8
8
  * routes (enumerate concrete params via `export const getStaticPaths`). Catch-all/wildcard (`*`)
@@ -28,7 +28,7 @@ const outputPath = (root, file) => {
28
28
  };
29
29
  /**
30
30
  * Render every opted-in static route to a static `index.html` under `outDir`. Run AFTER `buildClient`
31
- * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped —
31
+ * (so the app references the hashed client entry). Returns a report of what was emitted vs skipped -
32
32
  * the caller can use `prerendered` to wire a hybrid deploy (e.g. exclude those paths from the SSR
33
33
  * worker so the CDN serves the static file). Never throws on a per-route miss: a non-OK response or a
34
34
  * non-opted/dynamic route is recorded in `skipped` and the build continues.
@@ -44,7 +44,7 @@ export async function prerenderRoutes(options) {
44
44
  const renderPath = async (path) => {
45
45
  const res = await options.app.fetch(new Request(`${origin}${path}`));
46
46
  if (!res.ok) {
47
- // A failed render is a per-route skip, not a build failure — surfaced for the caller to log.
47
+ // A failed render is a per-route skip, not a build failure - surfaced for the caller to log.
48
48
  skipped.push({ path, reason: `render returned HTTP ${res.status}` });
49
49
  return;
50
50
  }
@@ -59,7 +59,7 @@ export async function prerenderRoutes(options) {
59
59
  writeFileSync(abs, html);
60
60
  // Also emit the loader data as a static `_data.json` next to it, so a client soft-nav into this
61
61
  // route fetches a file instead of hitting the worker. Only when the data-mode response is plain
62
- // JSON — a deferred loader answers with NDJSON, which the static fast-path doesn't cover (the
62
+ // JSON - a deferred loader answers with NDJSON, which the static fast-path doesn't cover (the
63
63
  // client falls back to the worker for those).
64
64
  let dataFile;
65
65
  const dataRes = await options.app.fetch(new Request(`${origin}${path}`, { headers: { [DATA_HEADER]: "1" } }));
@@ -76,13 +76,13 @@ export async function prerenderRoutes(options) {
76
76
  }
77
77
  }
78
78
  else {
79
- await dataRes.body?.cancel(); // not emitting (non-OK or NDJSON) — release the stream
79
+ await dataRes.body?.cancel(); // not emitting (non-OK or NDJSON) - release the stream
80
80
  }
81
81
  prerendered.push({ path, file, bytes: html.length, ...(dataFile ? { dataFile } : {}) });
82
82
  };
83
83
  for (const route of options.routes) {
84
84
  if (route.pattern.includes("*")) {
85
- skipped.push({ path: route.pattern, reason: "catch-all/wildcard route — not supported" });
85
+ skipped.push({ path: route.pattern, reason: "catch-all/wildcard route - not supported" });
86
86
  continue;
87
87
  }
88
88
  const mod = await route.load();
@@ -126,7 +126,7 @@ export async function prerenderRoutes(options) {
126
126
  *
127
127
  * ⚠️ Cloudflare caps `_routes.json` at **100** include+exclude rules total, and each prerendered path
128
128
  * costs 2 (the doc + its `_data.json`). For a large SSG site, exclude by **prefix glob** (e.g.
129
- * `/blog/*`) instead of listing every path — pass those via `staticGlobs` and a smaller `prerendered`.
129
+ * `/blog/*`) instead of listing every path - pass those via `staticGlobs` and a smaller `prerendered`.
130
130
  */
131
131
  export function cloudflarePagesRoutes(options) {
132
132
  const exclude = [...(options.staticGlobs ?? ["/assets/*"])];
@@ -0,0 +1,36 @@
1
+ /** How long each subtree may be cached. Content-hashed bundle output can be immutable; a
2
+ * user-authored file keeps its name across deploys, so it gets a day and a revalidation. */
3
+ export interface PublicDirCache {
4
+ /** `cache-control` for content-hashed assets (default immutable, one year). */
5
+ readonly hashed?: string;
6
+ /** `cache-control` for everything else under `public/` (default one day). */
7
+ readonly assets?: string;
8
+ }
9
+ export interface ServePublicDirOptions {
10
+ /** Absolute path of the directory to serve. */
11
+ readonly dir: string;
12
+ /** URL prefix whose files are content-hashed and may be cached immutably (default `"/assets/"`). */
13
+ readonly hashedPrefix?: string;
14
+ readonly cache?: PublicDirCache;
15
+ /** Optional encoded URL-path allowlist. When present, route misses avoid a filesystem probe. */
16
+ readonly files?: ReadonlySet<string>;
17
+ }
18
+ /**
19
+ * Resolve a URL pathname to an absolute path **confined** to `root`, or `undefined` if it escapes.
20
+ *
21
+ * This is a user-path-to-filesystem sink, which makes it the one part of this feature with a security
22
+ * consequence. Decode first (`%2e%2e%2f` is `../`), then normalize, then verify the result is still
23
+ * under `root` by prefix - checking the *resolved* path rather than scanning the input for `..`,
24
+ * because a blocklist over encodings is exactly the kind of check that gets bypassed.
25
+ */
26
+ export declare function resolvePublicPath(root: string, pathname: string): string | undefined;
27
+ /**
28
+ * Build a static-file handler for `dir`.
29
+ *
30
+ * Returns `undefined` on any miss so the caller **falls through to routing** - a static probe must
31
+ * never shadow a route. That ordering is also why a `routes/robots.txt.tsx` beats a
32
+ * `public/robots.txt` only if the caller checks routes first; the documented precedence is that the
33
+ * static probe runs first, so `public/` wins, and an app wanting the route should not ship both.
34
+ */
35
+ export declare function servePublicDir(options: ServePublicDirOptions): (request: Request) => Promise<Response | undefined>;
36
+ //# sourceMappingURL=public-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-dir.d.ts","sourceRoot":"","sources":["../src/public-dir.ts"],"names":[],"mappings":"AAiBA;4FAC4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,cAAc,CAAA;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAapF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAmCrD"}