@nifrajs/web 2.0.0 → 2.2.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 (102) hide show
  1. package/dist/build-vite.d.ts +68 -0
  2. package/dist/build-vite.d.ts.map +1 -0
  3. package/dist/build-vite.js +487 -0
  4. package/dist/build-vite.js.map +1 -0
  5. package/dist/build.d.ts +112 -25
  6. package/dist/build.d.ts.map +1 -1
  7. package/dist/build.js +283 -64
  8. package/dist/build.js.map +1 -1
  9. package/dist/bun-dev-entry.d.ts +83 -0
  10. package/dist/bun-dev-entry.d.ts.map +1 -0
  11. package/dist/bun-dev-entry.js +136 -0
  12. package/dist/bun-dev-entry.js.map +1 -0
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +10 -0
  15. package/dist/client.js.map +1 -1
  16. package/dist/deferred.d.ts +3 -2
  17. package/dist/deferred.d.ts.map +1 -1
  18. package/dist/deferred.js +11 -8
  19. package/dist/deferred.js.map +1 -1
  20. package/dist/dev-port.d.ts +36 -0
  21. package/dist/dev-port.d.ts.map +1 -0
  22. package/dist/dev-port.js +48 -0
  23. package/dist/dev-port.js.map +1 -0
  24. package/dist/dev.d.ts +61 -4
  25. package/dist/dev.d.ts.map +1 -1
  26. package/dist/dev.js +419 -60
  27. package/dist/dev.js.map +1 -1
  28. package/dist/draft.d.ts +37 -0
  29. package/dist/draft.d.ts.map +1 -1
  30. package/dist/draft.js +87 -4
  31. package/dist/draft.js.map +1 -1
  32. package/dist/fs.d.ts +14 -1
  33. package/dist/fs.d.ts.map +1 -1
  34. package/dist/fs.js +7 -1
  35. package/dist/fs.js.map +1 -1
  36. package/dist/index.d.ts +116 -5
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +565 -53
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal/timing-safe-equal.d.ts +16 -0
  41. package/dist/internal/timing-safe-equal.d.ts.map +1 -0
  42. package/dist/internal/timing-safe-equal.js +26 -0
  43. package/dist/internal/timing-safe-equal.js.map +1 -0
  44. package/dist/isr.d.ts.map +1 -1
  45. package/dist/isr.js +1 -14
  46. package/dist/isr.js.map +1 -1
  47. package/dist/llms-txt.d.ts.map +1 -1
  48. package/dist/llms-txt.js +12 -6
  49. package/dist/llms-txt.js.map +1 -1
  50. package/dist/manifest.d.ts +51 -8
  51. package/dist/manifest.d.ts.map +1 -1
  52. package/dist/manifest.js +130 -14
  53. package/dist/manifest.js.map +1 -1
  54. package/dist/module-graph.d.ts +91 -0
  55. package/dist/module-graph.d.ts.map +1 -0
  56. package/dist/module-graph.js +92 -0
  57. package/dist/module-graph.js.map +1 -0
  58. package/dist/plugins/css-modules.d.ts.map +1 -1
  59. package/dist/plugins/css-modules.js +0 -0
  60. package/dist/plugins/css-modules.js.map +1 -1
  61. package/dist/plugins/vite-leak-guard.d.ts +45 -0
  62. package/dist/plugins/vite-leak-guard.d.ts.map +1 -0
  63. package/dist/plugins/vite-leak-guard.js +67 -0
  64. package/dist/plugins/vite-leak-guard.js.map +1 -0
  65. package/dist/public-dir.d.ts +36 -0
  66. package/dist/public-dir.d.ts.map +1 -0
  67. package/dist/public-dir.js +89 -0
  68. package/dist/public-dir.js.map +1 -0
  69. package/dist/route-manifest.d.ts +91 -0
  70. package/dist/route-manifest.d.ts.map +1 -0
  71. package/dist/route-manifest.js +128 -0
  72. package/dist/route-manifest.js.map +1 -0
  73. package/dist/router.d.ts +64 -1
  74. package/dist/router.d.ts.map +1 -1
  75. package/dist/router.js +114 -31
  76. package/dist/router.js.map +1 -1
  77. package/dist/vite.d.ts +10 -4
  78. package/dist/vite.d.ts.map +1 -1
  79. package/dist/vite.js +44 -12
  80. package/dist/vite.js.map +1 -1
  81. package/package.json +17 -2
  82. package/src/build-vite.ts +606 -0
  83. package/src/build.ts +432 -73
  84. package/src/bun-dev-entry.ts +184 -0
  85. package/src/client.ts +8 -0
  86. package/src/deferred.ts +13 -6
  87. package/src/dev-port.ts +63 -0
  88. package/src/dev.ts +467 -65
  89. package/src/draft.ts +116 -4
  90. package/src/fs.ts +22 -4
  91. package/src/index.ts +727 -57
  92. package/src/internal/timing-safe-equal.ts +24 -0
  93. package/src/isr.ts +1 -13
  94. package/src/llms-txt.ts +13 -5
  95. package/src/manifest.ts +188 -13
  96. package/src/module-graph.ts +146 -0
  97. package/src/plugins/css-modules.ts +0 -0
  98. package/src/plugins/vite-leak-guard.ts +110 -0
  99. package/src/public-dir.ts +108 -0
  100. package/src/route-manifest.ts +240 -0
  101. package/src/router.ts +198 -34
  102. package/src/vite.ts +62 -20
@@ -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,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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `public/` - user-authored static files, served identically in dev and in production.
3
+ *
4
+ * The asymmetry this closes: `nifra dev` served `public/` for free because the HMR path runs on Vite
5
+ * and Vite serves `public/` by default, while production had no `publicDir` concept at all. So a file
6
+ * worked all the way through development and 404'd the moment it was deployed. The failure is
7
+ * inverted - it appears only in production, and only for the assets nobody smoke-tests - and it has
8
+ * already shipped once as a self-hosted webfont that silently fell back to a system font in prod.
9
+ *
10
+ * One owner for both sides is the fix, not a second implementation that happens to agree today.
11
+ *
12
+ * Distinct from `publicPath` in `build.ts`, which is the URL prefix for content-hashed bundle chunks.
13
+ * The names collide and the concepts do not: `publicPath` never covers user-authored files.
14
+ */
15
+ import { realpath } from "node:fs/promises";
16
+ import { normalize, resolve, sep } from "node:path";
17
+ const IMMUTABLE = "public, max-age=31536000, immutable";
18
+ const ONE_DAY = "public, max-age=86400";
19
+ /**
20
+ * Resolve a URL pathname to an absolute path **confined** to `root`, or `undefined` if it escapes.
21
+ *
22
+ * This is a user-path-to-filesystem sink, which makes it the one part of this feature with a security
23
+ * consequence. Decode first (`%2e%2e%2f` is `../`), then normalize, then verify the result is still
24
+ * under `root` by prefix - checking the *resolved* path rather than scanning the input for `..`,
25
+ * because a blocklist over encodings is exactly the kind of check that gets bypassed.
26
+ */
27
+ export function resolvePublicPath(root, pathname) {
28
+ let decoded;
29
+ try {
30
+ decoded = decodeURIComponent(pathname);
31
+ }
32
+ catch {
33
+ return undefined; // malformed percent-encoding: not a path we will guess at
34
+ }
35
+ // A NUL can truncate a path in a downstream syscall; refuse rather than normalize it away.
36
+ if (decoded.includes("\0"))
37
+ return undefined;
38
+ const rootResolved = resolve(root);
39
+ const candidate = resolve(rootResolved, `.${normalize(decoded)}`);
40
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + sep))
41
+ return undefined;
42
+ return candidate;
43
+ }
44
+ /**
45
+ * Build a static-file handler for `dir`.
46
+ *
47
+ * Returns `undefined` on any miss so the caller **falls through to routing** - a static probe must
48
+ * never shadow a route. That ordering is also why a `routes/robots.txt.tsx` beats a
49
+ * `public/robots.txt` only if the caller checks routes first; the documented precedence is that the
50
+ * static probe runs first, so `public/` wins, and an app wanting the route should not ship both.
51
+ */
52
+ export function servePublicDir(options) {
53
+ const root = resolve(options.dir);
54
+ const rootReal = realpath(root).catch(() => undefined);
55
+ const hashedPrefix = options.hashedPrefix ?? "/assets/";
56
+ const hashed = options.cache?.hashed ?? IMMUTABLE;
57
+ const assets = options.cache?.assets ?? ONE_DAY;
58
+ return async (request) => {
59
+ if (request.method !== "GET" && request.method !== "HEAD")
60
+ return undefined;
61
+ const { pathname } = new URL(request.url);
62
+ // A production manifest can reject page routes without touching the filesystem.
63
+ if (options.files !== undefined && !options.files.has(pathname))
64
+ return undefined;
65
+ const abs = resolvePublicPath(root, pathname);
66
+ if (abs === undefined)
67
+ return undefined;
68
+ const [resolvedRoot, resolvedFile] = await Promise.all([
69
+ rootReal,
70
+ realpath(abs).catch(() => undefined),
71
+ ]);
72
+ if (resolvedRoot === undefined ||
73
+ resolvedFile === undefined ||
74
+ (resolvedFile !== resolvedRoot && !resolvedFile.startsWith(resolvedRoot + sep))) {
75
+ return undefined;
76
+ }
77
+ const file = Bun.file(resolvedFile);
78
+ if (!(await file.exists()))
79
+ return undefined;
80
+ const headers = new Headers({
81
+ "cache-control": pathname.startsWith(hashedPrefix) ? hashed : assets,
82
+ });
83
+ // HEAD must not carry a body, but must otherwise match GET's headers.
84
+ return request.method === "HEAD"
85
+ ? new Response(null, { headers })
86
+ : new Response(file, { headers });
87
+ };
88
+ }
89
+ //# sourceMappingURL=public-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-dir.js","sourceRoot":"","sources":["../src/public-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAqBnD,MAAM,SAAS,GAAG,qCAAqC,CAAA;AACvD,MAAM,OAAO,GAAG,uBAAuB,CAAA;AAEvC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,QAAgB;IAC9D,IAAI,OAAe,CAAA;IACnB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA,CAAC,0DAA0D;IAC7E,CAAC;IACD,2FAA2F;IAC3F,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjE,IAAI,SAAS,KAAK,YAAY,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,GAAG,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAC7F,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA8B;IAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,UAAU,CAAA;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,IAAI,SAAS,CAAA;IACjD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,IAAI,OAAO,CAAA;IAE/C,OAAO,KAAK,EAAE,OAAgB,EAAiC,EAAE;QAC/D,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO,SAAS,CAAA;QAC3E,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACzC,gFAAgF;QAChF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAA;QACjF,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACvC,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACrD,QAAQ;YACR,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACrC,CAAC,CAAA;QACF,IACE,YAAY,KAAK,SAAS;YAC1B,YAAY,KAAK,SAAS;YAC1B,CAAC,YAAY,KAAK,YAAY,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,EAC/E,CAAC;YACD,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACnC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YAAE,OAAO,SAAS,CAAA;QAC5C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;YAC1B,eAAe,EAAE,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;SACrE,CAAC,CAAA;QACF,sEAAsE;QACtE,OAAO,OAAO,CAAC,MAAM,KAAK,MAAM;YAC9B,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC;YACjC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IACrC,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * `@nifrajs/web/route-manifest` — what each route actually DOES, as one artifact.
3
+ *
4
+ * The facts are already in the route modules: `prerender`, `getStaticPaths`, `revalidate`, `hydrate`.
5
+ * What has never existed is anywhere to read them together. To answer "which pages are static?", "which
6
+ * ones revalidate, and how often?", "which ship no JS?", you open every route file and hold the answer in
7
+ * your head - and the answer changes on the target you deploy to, which is nowhere near the route file.
8
+ *
9
+ * That gap is not just inconvenient. The interesting cases are the ones where a route's declaration and
10
+ * its deploy target disagree, and there is currently nothing that can notice: an ISR route on a target
11
+ * with no revalidation story regenerates on every request; a `static` build containing a route that was
12
+ * never prerenderable ships a page that 404s. Both are silent, and both are decided at build time by
13
+ * information no single place holds.
14
+ *
15
+ * So this derives one record per route - render mode, hydration, cache policy - resolves it against the
16
+ * target, and reports what the target cannot honour. Pure and fs-free: it takes an already-built
17
+ * `Manifest` so it runs at build time, in `nifra routes`, and in a test with a hand-built manifest.
18
+ */
19
+ import type { Manifest, RouteModule } from "./manifest.js";
20
+ /**
21
+ * How a route produces its HTML.
22
+ *
23
+ * - `static` - prerendered at build; served as a file, no server render at request time.
24
+ * - `isr` - server-rendered, then cached and revalidated on a timer (`export const revalidate`).
25
+ * - `ssr` - server-rendered per request. The default, and the only mode that needs a live server.
26
+ */
27
+ export type RenderMode = "static" | "isr" | "ssr";
28
+ /** What a route needs from its host in order to behave as declared. */
29
+ export type RouteCapability = "server" | "revalidation";
30
+ /** One route's resolved behaviour. */
31
+ export interface RouteManifestEntry {
32
+ /** The route's id (its key in the build manifest and in `routes`). */
33
+ readonly id: string;
34
+ /** The matching pattern, e.g. `/blog/:slug`. */
35
+ readonly pattern: string;
36
+ readonly mode: RenderMode;
37
+ /** Whether the full-document client is shipped. `false` means the page loads no framework JS. */
38
+ readonly hydrate: boolean;
39
+ /** Revalidation window in seconds. Present only for `isr`. */
40
+ readonly revalidate?: number;
41
+ /**
42
+ * Concrete paths prerendered at build. Present for `static` routes: a single path for a static route,
43
+ * one per `getStaticPaths` entry for a dynamic one. Absent means "no path was enumerated", which for a
44
+ * dynamic route is exactly the condition that makes it unprerenderable.
45
+ */
46
+ readonly prerenderedPaths?: readonly string[];
47
+ /** What this route needs from the host to behave as declared. */
48
+ readonly requires: readonly RouteCapability[];
49
+ }
50
+ /** A route whose declaration the chosen target cannot honour, and what actually happens if it ships. */
51
+ export interface RouteManifestConflict {
52
+ readonly id: string;
53
+ readonly pattern: string;
54
+ readonly capability: RouteCapability;
55
+ /** What goes wrong on this target - the consequence, not the rule that was broken. */
56
+ readonly consequence: string;
57
+ }
58
+ /** The whole artifact: every route's behaviour, plus anything the target cannot honour. */
59
+ export interface RouteManifest {
60
+ /** The deploy target this was resolved against, when one was given. */
61
+ readonly target?: string;
62
+ readonly routes: readonly RouteManifestEntry[];
63
+ readonly conflicts: readonly RouteManifestConflict[];
64
+ /** Route counts by mode - the summary a report leads with. */
65
+ readonly totals: Readonly<Record<RenderMode, number>>;
66
+ }
67
+ /**
68
+ * Derive one route's behaviour from its module exports.
69
+ *
70
+ * `prerender`/`getStaticPaths` win over `revalidate`: a page rendered at build time is not revalidated at
71
+ * runtime, so if a route declares both, the build-time answer is the one that describes what ships. That
72
+ * combination is worth surfacing rather than silently resolving, which is what `conflicts` is for once a
73
+ * target is known.
74
+ */
75
+ export declare function deriveRouteEntry(id: string, pattern: string, module: Pick<RouteModule, "prerender" | "getStaticPaths" | "revalidate" | "hydrate">, prerenderedPaths?: readonly string[]): RouteManifestEntry;
76
+ /**
77
+ * Build the route manifest for a discovered app, optionally resolved against a deploy target.
78
+ *
79
+ * `prerendered` maps a route id to the concrete paths the build actually emitted for it - pass what
80
+ * `prerenderRoutes` produced. Without it, dynamic routes are reported by their declaration alone, which
81
+ * is the right answer before a build has run and the wrong one after.
82
+ */
83
+ export declare function buildRouteManifest(manifest: Manifest, options?: {
84
+ readonly target?: string;
85
+ readonly prerendered?: Readonly<Record<string, readonly string[]>>;
86
+ /** Runtime capabilities supplied outside generated entries (for example an explicit `withISR`). */
87
+ readonly capabilities?: readonly RouteCapability[];
88
+ }): Promise<RouteManifest>;
89
+ /** Render the manifest as a readable report - the `nifra routes --modes` output. */
90
+ export declare function renderRouteManifest(manifest: RouteManifest): string;
91
+ //# sourceMappingURL=route-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-manifest.d.ts","sourceRoot":"","sources":["../src/route-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAA;AAEjD,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,cAAc,CAAA;AAEvD,sCAAsC;AACtC,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,iGAAiG;IACjG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7C,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;CAC9C;AAED,wGAAwG;AACxG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAA;IACpC,sFAAsF;IACtF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,2FAA2F;AAC3F,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAA;IAC9C,QAAQ,CAAC,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAA;IACpD,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAA;CACtD;AAKD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,gBAAgB,GAAG,YAAY,GAAG,SAAS,CAAC,EACpF,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GACnC,kBAAkB,CAyBpB;AA0BD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE;IACP,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAA;IAClE,mGAAmG;IACnG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;CAC9C,GACL,OAAO,CAAC,aAAa,CAAC,CAuCxB;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CA2CnE"}
@@ -0,0 +1,128 @@
1
+ /** A route is dynamic when its pattern carries a param or wildcard segment. */
2
+ const isDynamic = (pattern) => /[:*]/.test(pattern);
3
+ /**
4
+ * Derive one route's behaviour from its module exports.
5
+ *
6
+ * `prerender`/`getStaticPaths` win over `revalidate`: a page rendered at build time is not revalidated at
7
+ * runtime, so if a route declares both, the build-time answer is the one that describes what ships. That
8
+ * combination is worth surfacing rather than silently resolving, which is what `conflicts` is for once a
9
+ * target is known.
10
+ */
11
+ export function deriveRouteEntry(id, pattern, module, prerenderedPaths) {
12
+ const wantsPrerender = module.prerender === true || module.getStaticPaths !== undefined;
13
+ // A dynamic route is only really static if concrete paths were enumerated for it. `getStaticPaths`
14
+ // declares the intent; the paths are the evidence, and without them there is nothing prerendered to
15
+ // serve. Treating intent as sufficient is how a "static" build ships a page that 404s.
16
+ const enumerated = prerenderedPaths !== undefined && prerenderedPaths.length > 0;
17
+ const isStatic = wantsPrerender && (!isDynamic(pattern) || enumerated);
18
+ const mode = isStatic ? "static" : module.revalidate !== undefined ? "isr" : "ssr";
19
+ const requires = [];
20
+ if (mode !== "static")
21
+ requires.push("server");
22
+ if (mode === "isr")
23
+ requires.push("revalidation");
24
+ const entry = { id, pattern, mode, hydrate: module.hydrate !== false, requires };
25
+ if (mode === "isr" && module.revalidate !== undefined)
26
+ entry.revalidate = module.revalidate;
27
+ if (prerenderedPaths !== undefined)
28
+ entry.prerenderedPaths = prerenderedPaths;
29
+ return entry;
30
+ }
31
+ /**
32
+ * What each deploy target can actually do.
33
+ *
34
+ * `static` is the one that matters: it has no server at all, so every non-prerendered route is a page
35
+ * that will 404 in production while working perfectly in dev. The rest all run a server; they differ in
36
+ * how revalidation is wired, not in whether it exists, so this deliberately does not invent finer
37
+ * distinctions it cannot verify.
38
+ */
39
+ const TARGET_CAPABILITIES = {
40
+ bun: ["server"],
41
+ node: ["server"],
42
+ deno: ["server"],
43
+ "cf-pages": ["server"],
44
+ vercel: ["server"],
45
+ static: [],
46
+ };
47
+ const CONSEQUENCE = {
48
+ server: "this target ships no server, so the route is never rendered - the URL 404s in production while working in dev. Add `export const prerender = true` (or `getStaticPaths` for a dynamic route), or build for a target that runs a server.",
49
+ revalidation: "this target cannot revalidate a cached page, so `revalidate` has no effect and the route renders on every request.",
50
+ };
51
+ /**
52
+ * Build the route manifest for a discovered app, optionally resolved against a deploy target.
53
+ *
54
+ * `prerendered` maps a route id to the concrete paths the build actually emitted for it - pass what
55
+ * `prerenderRoutes` produced. Without it, dynamic routes are reported by their declaration alone, which
56
+ * is the right answer before a build has run and the wrong one after.
57
+ */
58
+ export async function buildRouteManifest(manifest, options = {}) {
59
+ const entries = [];
60
+ for (const route of manifest.routes) {
61
+ // Loading the module is what makes this authoritative rather than a guess from the filename: the
62
+ // render mode lives in the module's exports and nowhere else.
63
+ const module = (await route.load());
64
+ entries.push(deriveRouteEntry(route.id, route.pattern, module, options.prerendered?.[route.id]));
65
+ }
66
+ entries.sort((a, b) => a.pattern.localeCompare(b.pattern));
67
+ const conflicts = [];
68
+ const capabilities = options.capabilities ??
69
+ (options.target === undefined ? undefined : TARGET_CAPABILITIES[options.target]);
70
+ if (capabilities !== undefined) {
71
+ for (const entry of entries) {
72
+ for (const capability of entry.requires) {
73
+ if (capabilities.includes(capability))
74
+ continue;
75
+ conflicts.push({
76
+ id: entry.id,
77
+ pattern: entry.pattern,
78
+ capability,
79
+ consequence: CONSEQUENCE[capability],
80
+ });
81
+ }
82
+ }
83
+ }
84
+ const totals = { static: 0, isr: 0, ssr: 0 };
85
+ for (const entry of entries)
86
+ totals[entry.mode] += 1;
87
+ const result = { routes: entries, conflicts, totals };
88
+ if (options.target !== undefined)
89
+ result.target = options.target;
90
+ return result;
91
+ }
92
+ /** Render the manifest as a readable report - the `nifra routes --modes` output. */
93
+ export function renderRouteManifest(manifest) {
94
+ const lines = [];
95
+ lines.push(manifest.target === undefined
96
+ ? "nifra route manifest"
97
+ : `nifra route manifest — target: ${manifest.target}`);
98
+ lines.push("");
99
+ if (manifest.routes.length === 0) {
100
+ lines.push("no routes found.");
101
+ return lines.join("\n");
102
+ }
103
+ const width = Math.max(10, ...manifest.routes.map((r) => r.pattern.length)) + 2;
104
+ lines.push(`${"route".padEnd(width)}${"mode".padEnd(9)}${"hydrate".padEnd(9)}cache`);
105
+ lines.push("-".repeat(width + 18 + 24));
106
+ for (const route of manifest.routes) {
107
+ const cache = route.mode === "isr"
108
+ ? `revalidate ${route.revalidate}s`
109
+ : route.mode === "static"
110
+ ? `prerendered${route.prerenderedPaths ? ` (${route.prerenderedPaths.length})` : ""}`
111
+ : "per request";
112
+ lines.push(route.pattern.padEnd(width) +
113
+ route.mode.padEnd(9) +
114
+ (route.hydrate ? "yes" : "no").padEnd(9) +
115
+ cache);
116
+ }
117
+ lines.push("");
118
+ lines.push(`${manifest.totals.static} static · ${manifest.totals.isr} isr · ${manifest.totals.ssr} ssr`);
119
+ if (manifest.conflicts.length > 0) {
120
+ lines.push("");
121
+ lines.push(`✗ ${manifest.conflicts.length} route(s) the target cannot honour:`);
122
+ for (const conflict of manifest.conflicts) {
123
+ lines.push(` ✗ ${conflict.pattern} — ${conflict.consequence}`);
124
+ }
125
+ }
126
+ return lines.join("\n");
127
+ }
128
+ //# sourceMappingURL=route-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-manifest.js","sourceRoot":"","sources":["../src/route-manifest.ts"],"names":[],"mappings":"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"}
package/dist/router.d.ts CHANGED
@@ -22,6 +22,60 @@ export declare const REDIRECT_HEADER = "x-nifra-redirect";
22
22
  * the active one. The client validates each path against the manifest matcher before acting on it.
23
23
  */
24
24
  export declare const REVALIDATE_HEADER = "x-nifra-revalidate";
25
+ /**
26
+ * Response header carrying a **terminal status** a loader signalled with `notFound()` / `gone()` /
27
+ * `statusPage(n)` during a client-side navigation's data fetch.
28
+ *
29
+ * A soft-nav fetches data, not a document, so the server cannot answer by rendering the `_404` page -
30
+ * it has only JSON to return. Without this channel the two halves disagree: a crawler doing a hard
31
+ * navigation gets a correct 404 page while a user who clicked a link gets an error or a blank screen.
32
+ * The client reads it and renders the same boundary the server would have.
33
+ *
34
+ * Carried in a header rather than the body so it survives the NDJSON deferred-data path unchanged,
35
+ * exactly like {@link REDIRECT_HEADER}.
36
+ */
37
+ export declare const STATUS_HEADER = "x-nifra-status";
38
+ /**
39
+ * Layout indices a client navigation is asking the server NOT to re-run, comma separated.
40
+ *
41
+ * The client owns the decision because it holds both the old and new match plus each layout's scoped
42
+ * params. The server treats it as a hint and refuses to skip a `gate` regardless - a guard that runs
43
+ * only when the client says so is not a guard.
44
+ */
45
+ export declare const RETAIN_HEADER = "x-nifra-retain";
46
+ /**
47
+ * The path a client navigation is coming FROM, sent on the data-mode GET.
48
+ *
49
+ * Lets the SERVER decide which layout loaders to skip: it already knows each layout's scoped params
50
+ * (derived at build time), so given both paths it can tell whether a layout's own prefix changed.
51
+ * Doing it server-side means the client never needs the scope table, and the decision lives next to
52
+ * the data it is about.
53
+ *
54
+ * Sent only when the client actually holds layout data to retain, so a request without it is simply
55
+ * the full-chain case.
56
+ */
57
+ export declare const NAV_FROM_HEADER = "x-nifra-from";
58
+ /**
59
+ * The data-mode payload once a chain can carry layout data.
60
+ *
61
+ * Versioned because a prerendered `_data.json` is a static file on a CDN and outlives the deploy that
62
+ * wrote it: a browser running new client code can be handed an envelope written by the previous
63
+ * build. The reader therefore accepts BOTH the bare pre-envelope value and this shape, and decides by
64
+ * structure rather than by assuming the deploy was atomic.
65
+ */
66
+ export interface RouteDataEnvelope {
67
+ readonly v: 1;
68
+ readonly data: unknown;
69
+ readonly layoutData?: readonly unknown[];
70
+ /** Indices whose loader was SKIPPED because the layout's own params did not change. The client
71
+ * keeps its existing value at each of these, so an unchanged layout is neither refetched nor lost. */
72
+ readonly retained?: readonly number[];
73
+ /** Terminal status signalled by a loader. Added by the browser transport after reading
74
+ * `X-Nifra-Status`; old servers and static data files simply omit it. */
75
+ readonly status?: number;
76
+ }
77
+ /** Recognise the envelope without mistaking a plain loader object that happens to have a `data` key. */
78
+ export declare function isRouteDataEnvelope(value: unknown): value is RouteDataEnvelope;
25
79
  /** A URL matched against the manifest patterns: which route + its extracted params. */
26
80
  export interface RouteMatch {
27
81
  readonly routeId: string;
@@ -41,6 +95,9 @@ export interface RouterState {
41
95
  /** The current URL path (used to revalidate the active loader after an action). */
42
96
  readonly path: string;
43
97
  readonly data: unknown;
98
+ /** Per-layout loader data, aligned with the matched chain's leading layout prefix. Absent when no
99
+ * layout in that chain has a loader. */
100
+ readonly layoutData?: readonly unknown[] | undefined;
44
101
  /** An action's data return after a client-side submit (cleared on navigation). */
45
102
  readonly actionData?: unknown;
46
103
  /** True while a navigation or submit is in flight (drives loading UI). */
@@ -64,7 +121,10 @@ export interface RoutePattern {
64
121
  export declare function createMatcher(patterns: readonly RoutePattern[]): (path: string) => RouteMatch | null;
65
122
  /** How a router fetches a route's loader data on navigation. `signal` aborts a superseded fetch
66
123
  * (and its deferred stream). */
67
- export type FetchRouteData = (path: string, match: RouteMatch, signal?: AbortSignal) => Promise<unknown>;
124
+ export type FetchRouteData = (path: string, match: RouteMatch, signal?: AbortSignal, navigation?: {
125
+ readonly from: string;
126
+ readonly retain: readonly number[];
127
+ }) => Promise<unknown>;
68
128
  /** Per-submit options. `revalidate: false` opts out of the post-action loader re-fetch. */
69
129
  export interface SubmitOptions {
70
130
  /** Re-run the active route's loader after the action settles (default `true`). Set `false` to
@@ -154,6 +214,9 @@ export interface ClientRouterOptions {
154
214
  /** Ensure a route's code chunk is loaded before rendering (code-splitting). Awaited in parallel
155
215
  * with the loader data, so `pending` covers both. Omit when the bundle isn't split. */
156
216
  readonly loadModule?: (routeId: string) => Promise<void>;
217
+ /** Terminal status → client route id. Generated entries populate this from `_404` and
218
+ * `_<status>` files so soft navigation renders the same boundary as a hard request. */
219
+ readonly statusRoutes?: Readonly<Record<number, string>>;
157
220
  }
158
221
  /** Options for a per-adapter `mountRouter` (the Router binding that hydrates + re-renders). */
159
222
  export interface MountRouterOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}