@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
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Build-time Google Fonts automation — the `next/font/google` equivalent. At **build time** (never on
2
+ * Build-time Google Fonts automation - the `next/font/google` equivalent. At **build time** (never on
3
3
  * the request path) it: builds the Google Fonts CSS2 URL, downloads the stylesheet, parses the
4
4
  * `@font-face` rules, downloads each `.woff2`, content-hashes it, writes it next to your assets, and
5
5
  * hands back a **self-hosted** `@font-face` stylesheet + the matching `<link rel="preload">`s. The
6
- * result is identical to dropping the files in yourself and calling {@link fontFace} — no runtime CDN
6
+ * result is identical to dropping the files in yourself and calling {@link fontFace} - no runtime CDN
7
7
  * hotlink, no layout shift, hashed filenames for immutable caching.
8
8
  *
9
- * // fonts.build.ts — run once at build time (e.g. a prebuild step)
9
+ * // fonts.build.ts - run once at build time (e.g. a prebuild step)
10
10
  * import { loadGoogleFont } from "@nifrajs/web/fonts"
11
11
  * const inter = await loadGoogleFont(
12
12
  * { family: "Inter", weights: [400, 700], subsets: ["latin"] },
@@ -16,7 +16,7 @@
16
16
  * // inter.preloads → spread into a root layout's `meta.link`
17
17
  *
18
18
  * Security: this fetches remote content and writes it to disk, so every input is validated and the
19
- * font-file host is **allowlisted to `fonts.gstatic.com` over https** — a tampered/MITM'd stylesheet
19
+ * font-file host is **allowlisted to `fonts.gstatic.com` over https** - a tampered/MITM'd stylesheet
20
20
  * cannot make the build fetch an arbitrary URL (SSRF) or write an attacker-chosen blob. Downloads are
21
21
  * size-capped. Filenames are derived only from validated tokens + a content hash (no path traversal).
22
22
  */
@@ -39,7 +39,7 @@ const VALID_DISPLAY = new Set([
39
39
  "fallback",
40
40
  "optional",
41
41
  ]);
42
- /** `true` iff `raw` is an `https://fonts.gstatic.com/…` URL — the only host we'll download from. */
42
+ /** `true` iff `raw` is an `https://fonts.gstatic.com/…` URL - the only host we'll download from. */
43
43
  export function isAllowedFontUrl(raw) {
44
44
  let url;
45
45
  try {
@@ -63,7 +63,7 @@ function validateFamily(family) {
63
63
  function normalizeWeight(weight) {
64
64
  if (typeof weight === "number") {
65
65
  if (!Number.isInteger(weight) || weight < 1 || weight > 1000) {
66
- throw new Error(`loadGoogleFont: invalid weight ${weight} (1–1000)`);
66
+ throw new Error(`loadGoogleFont: invalid weight ${weight} (1-1000)`);
67
67
  }
68
68
  return String(weight);
69
69
  }
@@ -134,7 +134,7 @@ function declOf(body, prop) {
134
134
  return m?.[1]?.trim();
135
135
  }
136
136
  /** Parse Google's stylesheet into structured faces, capturing the `/* subset *​/` label that precedes
137
- * each `@font-face`. Pure — exported so callers can run their own download/write pipeline. */
137
+ * each `@font-face`. Pure - exported so callers can run their own download/write pipeline. */
138
138
  export function parseGoogleFontCss(css) {
139
139
  const faces = [];
140
140
  for (const match of css.matchAll(FACE_RE)) {
@@ -165,7 +165,7 @@ export function parseGoogleFontCss(css) {
165
165
  }
166
166
  return faces;
167
167
  }
168
- /** First 16 hex chars of the SHA-256 of the bytes — a content hash for an immutable, cache-busting
168
+ /** First 16 hex chars of the SHA-256 of the bytes - a content hash for an immutable, cache-busting
169
169
  * filename. Web Crypto, so it runs identically on Bun/Node/edge build hosts. `Uint8Array<ArrayBuffer>`
170
170
  * (not the generic `ArrayBufferLike`) to satisfy WebCrypto's `BufferSource` under TS 5.7+ generics. */
171
171
  async function contentHash(bytes) {
@@ -180,7 +180,7 @@ const slug = (s) => s
180
180
  .toLowerCase()
181
181
  .replace(/[^a-z0-9]+/g, "-")
182
182
  .replace(/^-+|-+$/g, "");
183
- /** Host of a URL for error messages — we never echo the full untrusted URL+path. */
183
+ /** Host of a URL for error messages - we never echo the full untrusted URL+path. */
184
184
  const hostOf = (raw) => {
185
185
  try {
186
186
  return new URL(raw).host;
@@ -229,7 +229,7 @@ export async function loadGoogleFont(options, io) {
229
229
  if (faces.length === 0) {
230
230
  throw new Error(`loadGoogleFont: Google returned no @font-face rules for ${JSON.stringify(family)}`);
231
231
  }
232
- // Filter to requested named subsets — but only when not glyph-subsetting (text mode labels faces
232
+ // Filter to requested named subsets - but only when not glyph-subsetting (text mode labels faces
233
233
  // `[0]`, `[1]`, … which carry no subset name).
234
234
  if (wantedSubsets && wantedSubsets.length > 0 && options.text === undefined) {
235
235
  faces = faces.filter((f) => wantedSubsets.includes(f.subset));
package/dist/fonts.d.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  /**
2
- * Font optimization primitives — generate a **CLS-safe `@font-face`** rule and the matching
2
+ * Font optimization primitives - generate a **CLS-safe `@font-face`** rule and the matching
3
3
  * **preload `<link>`** for a self-hosted font. The two fixes that matter most (the ones `next/font`
4
4
  * automates) are right here: `font-display: swap` (text paints immediately, no invisible-text FOIT)
5
5
  * plus a preload so the file downloads with the document, and optional metric overrides
6
6
  * (`size-adjust`/`ascent-override`/…) so swapping from the fallback to the web font doesn't shift layout.
7
7
  *
8
- * Self-host the file (drop a `.woff2` in your assets — never hotlink Google's CDN), then:
8
+ * Self-host the file (drop a `.woff2` in your assets - never hotlink Google's CDN), then:
9
9
  *
10
- * // fonts.css (imported by your app — the CSS pipeline bundles + links it)
10
+ * // fonts.css (imported by your app - the CSS pipeline bundles + links it)
11
11
  * import { fontFace } from "@nifrajs/web"
12
12
  * export default fontFace({ family: "Inter", src: [{ url: "/fonts/inter.woff2" }], weight: "100 900" })
13
13
  *
14
- * // a root layout's meta — becomes a <link rel="preload" as="font"> in <head>
14
+ * // a root layout's meta - becomes a <link rel="preload" as="font"> in <head>
15
15
  * export const meta = { link: [fontPreload({ href: "/fonts/inter.woff2" })] }
16
16
  */
17
17
  import type { LinkDescriptor } from "./manifest.js";
@@ -32,7 +32,7 @@ export interface FontFace {
32
32
  readonly style?: "normal" | "italic" | "oblique";
33
33
  /** Defaults to `"swap"` (no flash of invisible text). */
34
34
  readonly display?: FontDisplay;
35
- /** Restrict the glyphs this face covers, e.g. `"U+0000-00FF"` — lets the browser skip the download
35
+ /** Restrict the glyphs this face covers, e.g. `"U+0000-00FF"` - lets the browser skip the download
36
36
  * for pages without those characters. */
37
37
  readonly unicodeRange?: string;
38
38
  /** Metric overrides that eliminate layout shift when the fallback swaps to the web font (the
@@ -53,13 +53,13 @@ export interface FontPreloadInput {
53
53
  readonly href: string;
54
54
  /** MIME type (`font/woff2`, …). Inferred from the extension when omitted. */
55
55
  readonly type?: string;
56
- /** `crossorigin` mode — defaults to `"anonymous"`. Fonts are always fetched in CORS mode, and a
56
+ /** `crossorigin` mode - defaults to `"anonymous"`. Fonts are always fetched in CORS mode, and a
57
57
  * preload whose `crossorigin` doesn't match the actual fetch is **wasted** (downloaded twice), so
58
58
  * this is on by default. */
59
59
  readonly crossOrigin?: "anonymous" | "use-credentials";
60
60
  }
61
61
  /**
62
- * Build a font preload as a `<link>` attribute set for a route/layout's `meta.link` — nifra injects it
62
+ * Build a font preload as a `<link>` attribute set for a route/layout's `meta.link` - nifra injects it
63
63
  * into `<head>` (`<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous">`). Values are
64
64
  * escaped at injection by the head renderer. Preloading the font file removes a render-blocking round
65
65
  * trip (the browser would otherwise discover the font only after parsing the CSS).
package/dist/fonts.js CHANGED
@@ -69,7 +69,7 @@ export function fontFace(face) {
69
69
  return `@font-face {\n ${declarations.join(";\n ")};\n}`;
70
70
  }
71
71
  /**
72
- * Build a font preload as a `<link>` attribute set for a route/layout's `meta.link` — nifra injects it
72
+ * Build a font preload as a `<link>` attribute set for a route/layout's `meta.link` - nifra injects it
73
73
  * into `<head>` (`<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous">`). Values are
74
74
  * escaped at injection by the head renderer. Preloading the font file removes a render-blocking round
75
75
  * trip (the browser would otherwise discover the font only after parsing the CSS).
package/dist/forms.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * `@nifrajs/web/forms` — a typed form ↔ backend-schema binding. The three artifacts that must agree —
3
- * a form's `<input name>`, the action's `formData.get`, and the typed-client payload — all key off the
2
+ * `@nifrajs/web/forms` - a typed form ↔ backend-schema binding. The three artifacts that must agree -
3
+ * a form's `<input name>`, the action's `formData.get`, and the typed-client payload - all key off the
4
4
  * ONE route body schema, derived purely from `typeof backend`. A typo'd field name, or a field the
5
5
  * schema doesn't have, becomes a COMPILE error instead of a silent runtime empty.
6
6
  *
@@ -8,14 +8,14 @@
8
8
  * import { formFor } from "@nifrajs/web/forms"
9
9
  *
10
10
  * const f = formFor<typeof backend, "/todos">() // method defaults to "post"
11
- * // In the component (any framework — spread the props onto an input):
11
+ * // In the component (any framework - spread the props onto an input):
12
12
  * <input {...f.field("text")} /> // "text" ∈ the body's keys, else a compile error
13
13
  * // In the action:
14
14
  * const text = f.read(await request.formData(), "text") // an orphan key won't compile
15
15
  * await api.todos.post({ text }) // payload already typed by nifra
16
16
  *
17
17
  * Framework-agnostic + dependency-free: no codegen, no schema is bundled into the client, and the
18
- * runtime is a trivial pass-through — all the work is in the types. It checks the field KEY, never the
18
+ * runtime is a trivial pass-through - all the work is in the types. It checks the field KEY, never the
19
19
  * MEANING (a `field("email")` collecting a phone is still valid here); that stays an app/AI concern.
20
20
  */
21
21
  import type { RouteInfo, Server } from "@nifrajs/core/server";
@@ -23,23 +23,23 @@ type FormEntry = ReturnType<FormData["get"]>;
23
23
  type FormEntries = ReturnType<FormData["getAll"]>;
24
24
  /** The route registry baked into a server's type (`typeof app`). */
25
25
  type RegistryOf<App> = App extends Server<infer R, infer _Ctx> ? R : never;
26
- /** Every route path the app declares — constrains `Path`, so a wrong path is itself a type error. */
26
+ /** Every route path the app declares - constrains `Path`, so a wrong path is itself a type error. */
27
27
  export type RoutePaths<App> = keyof RegistryOf<App> & string;
28
28
  /** The body object type of `App`'s `Method Path` route (`never` when the route declares no body). */
29
29
  export type RouteBody<App, Path extends string, Method extends string> = Path extends keyof RegistryOf<App> ? Uppercase<Method> extends keyof RegistryOf<App>[Path] ? (RegistryOf<App>[Path][Uppercase<Method>] & RouteInfo)["body"] : never : never;
30
- /** The valid field names for that route's body — the schema's keys as a string union. */
30
+ /** The valid field names for that route's body - the schema's keys as a string union. */
31
31
  export type FieldKey<App, Path extends string, Method extends string> = [
32
32
  RouteBody<App, Path, Method>
33
33
  ] extends [never] ? never : keyof RouteBody<App, Path, Method> & string;
34
34
  /** Extra attributes merged into the returned input props (id, type, defaultValue, placeholder, …). */
35
35
  export type FieldProps = Record<string, unknown>;
36
36
  export interface FormHandle<App, Path extends string, Method extends string> {
37
- /** Input props for a schema field. `name` is constrained to the body's keys — a typo, or a field the
37
+ /** Input props for a schema field. `name` is constrained to the body's keys - a typo, or a field the
38
38
  * schema doesn't have, is a COMPILE error. Spread onto an element: `<input {...f.field("text")} />`. */
39
39
  field<K extends FieldKey<App, Path, Method>>(name: K, props?: FieldProps): FieldProps & {
40
40
  name: K;
41
41
  };
42
- /** `formData.get`, key-constrained to the body's fields — reading an orphan key won't compile. */
42
+ /** `formData.get`, key-constrained to the body's fields - reading an orphan key won't compile. */
43
43
  read<K extends FieldKey<App, Path, Method>>(form: FormData, name: K): FormEntry;
44
44
  /** `formData.getAll`, key-checked the same way. */
45
45
  readAll<K extends FieldKey<App, Path, Method>>(form: FormData, name: K): FormEntries;
package/dist/fs.d.ts CHANGED
@@ -1,9 +1,22 @@
1
1
  import { type Manifest } from "./manifest.js";
2
2
  /** Options for {@link discoverRoutes}. */
3
3
  export interface DiscoverRoutesOptions {
4
- /** Appended (as `?<importQuery>`) to each route's dynamic import — the dev server sets a
5
- * changing value to bust Bun's module cache so SSR picks up edited route files. */
4
+ /** Appended (as `?<importQuery>`) to each route's dynamic import - the dev server sets a
5
+ * changing value to bust Bun's module cache so SSR picks up edited route files. Ignored when
6
+ * {@link load} is supplied, which does its own cache handling. */
6
7
  readonly importQuery?: string;
8
+ /**
9
+ * Load a route module by its absolute path, instead of a bare dynamic `import()`.
10
+ *
11
+ * This is what lets a pipeline own SSR resolution. A bare `import()` always resolves through the
12
+ * RUNTIME - Bun - so the Vite dev server ended up serving the client while Bun resolved the server,
13
+ * two resolvers disagreeing about one specifier in a single process. That is the dual-React bug and
14
+ * the reason `resolve.dedupe` never reached SSR: the alias governs Vite, and SSR never asked Vite.
15
+ *
16
+ * Pass `vite.ssrLoadModule` here and the Vite pipeline resolves both halves. Omit it and Bun does,
17
+ * which is correct for the Bun pipeline. Either way one toolchain owns the phase.
18
+ */
19
+ readonly load?: (absolutePath: string) => Promise<unknown>;
7
20
  }
8
21
  /** Scan a `routes/` directory (recursively) and build the route manifest. */
9
22
  export declare function discoverRoutes(dir: string, options?: DiscoverRoutesOptions): Manifest;
package/dist/fs.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,QAAQ,EAAoB,MAAM,eAAe,CAAA;AAE9E,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC;uFACmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,QAAQ,CASzF"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,QAAQ,EAAoB,MAAM,eAAe,CAAA;AAE9E,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC;;sEAEkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC3D;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,QAAQ,CAczF"}
package/dist/fs.js CHANGED
@@ -7,7 +7,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  /**
10
- * @nifrajs/web/fs — filesystem route discovery. Separated from the main entry so `app.fetch`
10
+ * @nifrajs/web/fs - filesystem route discovery. Separated from the main entry so `app.fetch`
11
11
  * (and the rest of @nifrajs/web) stays fs-free + edge-portable: this runs at build/startup
12
12
  * only. It scans a routes dir and feeds the pure `buildManifest`.
13
13
  */
@@ -19,6 +19,12 @@ export function discoverRoutes(dir, options = {}) {
19
19
  .map((file) => file.replaceAll("\\", "/")) // normalize Windows separators
20
20
  .filter((file) => /\.(tsx|jsx|svelte|vue|mdx)$/.test(file));
21
21
  const query = options.importQuery ? `?${options.importQuery}` : "";
22
- return buildManifest(files, (file) => () => import(__rewriteRelativeImportExtension(`${dir}/${file}${query}`)));
22
+ const load = options.load;
23
+ return buildManifest(files, (file) => load === undefined
24
+ ? () => import(__rewriteRelativeImportExtension(`${dir}/${file}${query}`))
25
+ : // No `importQuery` here: an injected loader owns its own invalidation (Vite's module graph
26
+ // re-evaluates on change), and appending a cache-buster would defeat it by minting a new
27
+ // module id per request.
28
+ () => load(`${dir}/${file}`));
23
29
  }
24
30
  //# sourceMappingURL=fs.js.map
package/dist/fs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,aAAa,EAAmC,MAAM,eAAe,CAAA;AAS9E,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,UAAiC,EAAE;IAC7E,MAAM,KAAK,GAAI,WAAW,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAc;SAC9D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,+BAA+B;SACzE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAClE,OAAO,aAAa,CAClB,KAAK,EACL,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,kCAAC,GAAG,GAAG,IAAI,IAAI,GAAG,KAAK,EAAE,EAAyB,CACzE,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,aAAa,EAAmC,MAAM,eAAe,CAAA;AAsB9E,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,UAAiC,EAAE;IAC7E,MAAM,KAAK,GAAI,WAAW,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAc;SAC9D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,+BAA+B;SACzE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAClE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IACzB,OAAO,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,kCAAC,GAAG,GAAG,IAAI,IAAI,GAAG,KAAK,EAAE,EAAyB;QAChE,CAAC,CAAC,2FAA2F;YAC3F,yFAAyF;YACzF,yBAAyB;YACzB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAyB,CACzD,CAAA;AACH,CAAC"}