@nifrajs/cli 2.1.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 (47) hide show
  1. package/dist/check.d.ts +4 -1
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +71 -0
  4. package/dist/check.js.map +1 -1
  5. package/dist/cli.d.ts +9 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +165 -23
  8. package/dist/cli.js.map +1 -1
  9. package/dist/doctor.d.ts +5 -1
  10. package/dist/doctor.d.ts.map +1 -1
  11. package/dist/doctor.js +66 -6
  12. package/dist/doctor.js.map +1 -1
  13. package/dist/load.d.ts +11 -0
  14. package/dist/load.d.ts.map +1 -1
  15. package/dist/load.js +30 -0
  16. package/dist/load.js.map +1 -1
  17. package/dist/mcp-docs-tools.d.ts.map +1 -1
  18. package/dist/mcp-docs-tools.js +7 -3
  19. package/dist/mcp-docs-tools.js.map +1 -1
  20. package/dist/mcp-http.js +1 -1
  21. package/dist/mcp-render.js +9 -1
  22. package/dist/mcp-render.js.map +1 -1
  23. package/dist/mcp.d.ts.map +1 -1
  24. package/dist/mcp.js +44 -2
  25. package/dist/mcp.js.map +1 -1
  26. package/dist/pipeline-guard.d.ts +83 -0
  27. package/dist/pipeline-guard.d.ts.map +1 -0
  28. package/dist/pipeline-guard.js +136 -0
  29. package/dist/pipeline-guard.js.map +1 -0
  30. package/dist/types-search.d.ts +1 -1
  31. package/dist/types-search.d.ts.map +1 -1
  32. package/dist/types-search.js +47 -2
  33. package/dist/types-search.js.map +1 -1
  34. package/docs/examples.json +6 -0
  35. package/docs/llms-full.txt +81 -30
  36. package/docs/types.json +613 -36
  37. package/package.json +9 -9
  38. package/src/check.ts +91 -0
  39. package/src/cli.ts +173 -24
  40. package/src/doctor.ts +71 -6
  41. package/src/load.ts +44 -0
  42. package/src/mcp-docs-tools.ts +13 -3
  43. package/src/mcp-http.ts +1 -1
  44. package/src/mcp-render.ts +9 -1
  45. package/src/mcp.ts +45 -2
  46. package/src/pipeline-guard.ts +186 -0
  47. package/src/types-search.ts +50 -1
package/docs/types.json CHANGED
@@ -435,6 +435,13 @@
435
435
  "signature": "export declare function assertSafeKey(key: string): void;",
436
436
  "doc": "/** Throw {@link StorageKeyError} unless `key` is a safe relative storage key. */"
437
437
  },
438
+ {
439
+ "name": "assertSingleReactCore",
440
+ "kind": "function",
441
+ "package": "@nifrajs/web-react",
442
+ "signature": "export declare function assertSingleReactCore(reactDomServerPath: string, resolve: ResolveSync, realpath?: (path: string) => string): void;",
443
+ "doc": "/**\n * The dual-React crash, caught on the RESOLVED graph instead of after it detonates.\n *\n * The re-root above makes `react-dom/server` load from the app root, which is right almost always. What it\n * cannot guarantee is that react-dom then pulls in the SAME physical `react` the route components import -\n * a nested `react` under react-dom, or a components tree resolving react elsewhere, still yields two cores.\n * Two cores is two hook dispatchers, and the renderer sees a null (or foreign) one: the SSR throws\n * `resolveDispatcher().useState is null` from deep inside react-dom-server, naming a React internal and\n * nothing about the two directories that actually caused it. That is hours of inference from a message\n * that points nowhere useful.\n *\n * `nifra doctor` checks what is INSTALLED; this checks what SSR actually RESOLVED, which is the only thing\n * that can catch a duplicate the two dev pipelines introduce (Bun for SSR, Vite for the client) rather\n * than the install. It compares the realpath of the `react` react-dom will render with against the\n * `react` the components import, and if they differ throws with BOTH paths - turning a five-hour hunt into\n * a five-second read. Silent when they agree, which is the single-copy common case.\n *\n * Never manufactures a failure: if either side cannot be resolved it returns, because a resolver that\n * cannot answer is not evidence of a duplicate. Exported for direct unit testing.\n */"
444
+ },
438
445
  {
439
446
  "name": "assertStorageAdapterConformance",
440
447
  "kind": "function",
@@ -837,7 +844,21 @@
837
844
  "name": "BuildClientOptions",
838
845
  "kind": "interface",
839
846
  "package": "@nifrajs/web",
840
- "signature": "export interface BuildClientOptions {\n /** The `routes/` directory to discover (absolute path). */\n readonly routesDir: string;\n /** Output directory for the bundle + `manifest.json` (absolute path). */\n readonly outDir: string;\n /** The adapter's client runtime (exports `mountRouter`), e.g. `\"@nifrajs/web-solid/client\"`. */\n readonly clientModule: string;\n /** Route/layout file → import specifier (default: `${routesDir}/${file}`). */\n readonly resolve?: (file: string) => string;\n /** Adapter build plugins (e.g. `solidBunPlugin(\"dom\")`). */\n readonly plugins?: readonly BunPlugin[];\n /** `Bun.build` export conditions (e.g. `[\"bun\", \"solid\", \"browser\"]`). */\n readonly conditions?: readonly string[];\n /** Compile-time replacements (e.g. `{ \"process.env.NODE_ENV\": '\"production\"' }`). */\n readonly define?: Readonly<Record<string, string>>;\n /** Minify the output (default `true`). */\n readonly minify?: boolean;\n /** URL prefix the assets are served under (default `\"/assets/\"`); also Bun's chunk `publicPath`. */\n readonly publicPath?: string;\n /**\n * Prefix that opts an environment variable into the **client** bundle (Vite/Next convention; default\n * `\"PUBLIC_\"`). Every var in the build environment whose name starts with this prefix is baked into\n * the client `define` as `\"process.env.NAME\": JSON.stringify(value)`, so `process.env.PUBLIC_API_URL`\n * compiles to its literal value in the browser. Vars WITHOUT the prefix are never exposed — the bare\n * `process.env` define resolves them to `undefined`, so server secrets can't leak into the client\n * bundle. Set to `\"\"` to disable auto-exposure entirely (no var is baked in). `options.define` still\n * wins over an auto-exposed var (it's layered last). Sourced from `Bun.env` (falls back to\n * `process.env`) at build time. */\n readonly publicEnvPrefix?: string;\n}"
847
+ "signature": "export interface BuildClientOptions {\n /** The `routes/` directory to discover (absolute path). */\n readonly routesDir: string;\n /** Output directory for the bundle + `manifest.json` (absolute path). */\n readonly outDir: string;\n /** The adapter's client runtime (exports `mountRouter`), e.g. `\"@nifrajs/web-solid/client\"`. */\n readonly clientModule: string;\n /** Route/layout file → import specifier (default: `${routesDir}/${file}`). */\n readonly resolve?: (file: string) => string;\n /** Adapter build plugins (e.g. `solidBunPlugin(\"dom\")`). */\n readonly plugins?: readonly BunPlugin[];\n /** `Bun.build` export conditions (e.g. `[\"bun\", \"solid\", \"browser\"]`). */\n readonly conditions?: readonly string[];\n /** Compile-time replacements (e.g. `{ \"process.env.NODE_ENV\": '\"production\"' }`). */\n readonly define?: Readonly<Record<string, string>>;\n /** Minify the output (default `true`). */\n readonly minify?: boolean;\n /** URL prefix the assets are served under (default `\"/assets/\"`); also Bun's chunk `publicPath`. */\n readonly publicPath?: string;\n /**\n * Directory of user-authored static files copied into the build and served at the root (default\n * `\"public\"`). Absent directory ⇒ nothing copied, no error.\n *\n * NOT the same thing as {@link publicPath}, despite the names: that is the URL prefix for\n * content-hashed bundle chunks and never covers files an author put on disk. The collision is a\n * real source of confusion, which is why both are spelled out here.\n */\n readonly publicDir?: string | false;\n /**\n * Prefix that opts an environment variable into the **client** bundle (Vite/Next convention; default\n * `\"PUBLIC_\"`). Every var in the build environment whose name starts with this prefix is baked into\n * the client `define` as `\"process.env.NAME\": JSON.stringify(value)`, so `process.env.PUBLIC_API_URL`\n * compiles to its literal value in the browser. Vars WITHOUT the prefix are never exposed — the bare\n * `process.env` define resolves them to `undefined`, so server secrets can't leak into the client\n * bundle. Set to `\"\"` to disable auto-exposure entirely (no var is baked in). `options.define` still\n * wins over an auto-exposed var (it's layered last). Sourced from `Bun.env` (falls back to\n * `process.env`) at build time. */\n readonly publicEnvPrefix?: string;\n}"
848
+ },
849
+ {
850
+ "name": "buildClientVite",
851
+ "kind": "function",
852
+ "package": "@nifrajs/web",
853
+ "signature": "export declare function buildClientVite(options: BuildClientViteOptions): Promise<BuildManifest>;",
854
+ "doc": "/**\n * Build the client bundle with Vite, emitting the SAME {@link BuildManifest} `buildClient` (Bun) does:\n * a content-hashed entry, per-route chunk lists, aggregate + per-route CSS, and copied `public/` files.\n *\n * The route→chunk and route→CSS mappings come from Vite's own `.vite/manifest.json` (`build.manifest`),\n * which records each entry's emitted `file` and its transitive `css` - exactly the two facts the Bun path\n * reconstructs from Bun's metafile. `viteLeakGuard()` runs during the build, so a client leak fails here\n * with the same error as the Bun build rather than shipping.\n */"
855
+ },
856
+ {
857
+ "name": "BuildClientViteOptions",
858
+ "kind": "interface",
859
+ "package": "@nifrajs/web",
860
+ "signature": "export interface BuildClientViteOptions extends Omit<BuildClientOptions, \"plugins\"> {\n /** Vite plugins for the app's client transforms (e.g. `react()`, `vue()`). */\n readonly vitePlugins?: readonly unknown[];\n /** Vite project root (default: the parent of `routesDir`). Manifest keys are relative to it. */\n readonly root?: string;\n}",
861
+ "doc": "/** Options for {@link buildClientVite} — the Bun {@link BuildClientOptions} minus Bun-plugin specifics,\n * plus the Vite plugin list the app injects for its transforms. */"
841
862
  },
842
863
  {
843
864
  "name": "buildManifest",
@@ -850,8 +871,7 @@
850
871
  "name": "BuildManifest",
851
872
  "kind": "interface",
852
873
  "package": "@nifrajs/web",
853
- "signature": "export interface BuildManifest {\n /** URL of the client entry module (content-hashed). */\n readonly entry: string;\n /** URLs of every emitted asset (entry + chunks) — for serving + preloading. */\n readonly assets: readonly string[];\n /** `routeId → [layout chunk URLs…, own chunk URL]` — the chunks a route needs, for `createWebApp`'s\n * `routePreload` (`<link rel=\"modulepreload\">` the matched route alongside the entry). Each route +\n * layout is also a build entrypoint, so it gets a named chunk the bootstrap's lazy import dedupes to. */\n readonly routes: Readonly<Record<string, readonly string[]>>;\n /** The app's bundled, content-hashed stylesheet(s) — the bootstrap's **aggregate** CSS (every\n * `import './x.css'` reachable from the app). The complete stylesheet regardless of which file\n * imported the CSS; the always-safe fallback `createWebApp` links when a route has no per-route entry\n * in {@link routeStyles}. Omitted when the app imports no CSS. */\n readonly css?: readonly string[];\n /** `routeId → [chain CSS URLs]` — only the stylesheets the matched route's layout chain + own file\n * actually use (Bun emits a per-entrypoint CSS bundle per route/layout, with shared-component CSS\n * inlined into each consumer). `createWebApp` links these instead of the aggregate, so a page ships\n * only its own CSS. A route is omitted (→ aggregate fallback) when its `[name]` collides with another\n * route's basename (ambiguous CSS↔route) or the build emitted orphan shared-chunk CSS — correctness\n * over minimality. Absent entirely when the app imports no CSS. */\n readonly routeStyles?: Readonly<Record<string, readonly string[]>>;\n}",
854
- "doc": "/** The built asset map — the server reads `entry` for the client script + serves `assets`. */"
874
+ "signature": "export interface BuildManifest {\n /** URL of the client entry module (content-hashed). */\n readonly entry: string;\n /** URLs of every emitted asset (entry + chunks) — for serving + preloading. */\n readonly assets: readonly string[];\n /** `routeId → [layout chunk URLs…, own chunk URL]` — the chunks a route needs, for `createWebApp`'s\n * `routePreload` (`<link rel=\"modulepreload\">` the matched route alongside the entry). Each route +\n * layout is also a build entrypoint, so it gets a named chunk the bootstrap's lazy import dedupes to. */\n readonly routes: Readonly<Record<string, readonly string[]>>;\n /** URL paths copied from `publicDir`, sorted. Lets the server entry serve them without scanning a\n * directory per request, and lets an adapter that needs a file list (CDN upload, platform static\n * assets) consume one. Omitted when there is no `public/`. */\n readonly publicFiles?: readonly string[];\n /** The app's bundled, content-hashed stylesheet(s) — the bootstrap's **aggregate** CSS (every\n * `import './x.css'` reachable from the app). The complete stylesheet regardless of which file\n * imported the CSS; the always-safe fallback `createWebApp` links when a route has no per-route entry\n * in {@link routeStyles}. Omitted when the app imports no CSS. */\n readonly css?: readonly string[];\n /** `routeId → [chain CSS URLs]` — only the stylesheets the matched route's layout chain + own file\n * actually use (Bun emits a per-entrypoint CSS bundle per route/layout, with shared-component CSS\n * inlined into each consumer). `createWebApp` links these instead of the aggregate, so a page ships\n * only its own CSS. A route is omitted (→ aggregate fallback) when its `[name]` collides with another\n * route's basename (ambiguous CSS↔route) or the build emitted orphan shared-chunk CSS — correctness\n * over minimality. Absent entirely when the app imports no CSS. */\n readonly routeStyles?: Readonly<Record<string, readonly string[]>>;\n}"
855
875
  },
856
876
  {
857
877
  "name": "buildNifraManifest",
@@ -873,6 +893,20 @@
873
893
  "signature": "export declare function buildOpenApiDocument(routes: readonly RouteLike[], options?: OpenApiOptions): Record<string, unknown>;",
874
894
  "doc": "/**\n * Build an OpenAPI 3.1 document from a route list. Delegates to `@nifrajs/schema`'s `toOpenAPI`, so a\n * route validated with `t` (TypeBox) emits full field-level request/query/response schemas plus\n * `$ref`-reused `components.schemas`; a BYO Standard Schema (zod/valibot/arktype) exposes no portable\n * JSON-Schema form, so its body/response is omitted (supply it via `options.operations`). Path params,\n * wildcards, tags, security, and servers always emit. Exported so you can also generate the doc at\n * build time (write it to disk in CI) without booting the server.\n */"
875
895
  },
896
+ {
897
+ "name": "BuildPipeline",
898
+ "kind": "type",
899
+ "package": "@nifrajs/cli",
900
+ "signature": "export type BuildPipeline = \"bun\" | \"vite\";",
901
+ "doc": "/** Which bundler a build runs on. */"
902
+ },
903
+ {
904
+ "name": "buildRouteManifest",
905
+ "kind": "function",
906
+ "package": "@nifrajs/web",
907
+ "signature": "export declare function buildRouteManifest(manifest: Manifest, options?: {\n readonly target?: string;\n readonly prerendered?: Readonly<Record<string, readonly string[]>>;\n /** Runtime capabilities supplied outside generated entries (for example an explicit `withISR`). */\n readonly capabilities?: readonly RouteCapability[];\n}): Promise<RouteManifest>;",
908
+ "doc": "/**\n * Build the route manifest for a discovered app, optionally resolved against a deploy target.\n *\n * `prerendered` maps a route id to the concrete paths the build actually emitted for it - pass what\n * `prerenderRoutes` produced. Without it, dynamic routes are reported by their declaration alone, which\n * is the right answer before a build has run and the wrong one after.\n */"
909
+ },
876
910
  {
877
911
  "name": "buildRouteTable",
878
912
  "kind": "function",
@@ -893,12 +927,26 @@
893
927
  "package": "@nifrajs/web",
894
928
  "signature": "export interface BuildServerOptions {\n /** The `routes/` directory to discover (absolute path). */\n readonly routesDir: string;\n /** The worker entry module (absolute path) — your `worker.ts`. It imports `{ manifest, clientEntry }`\n * from the generated `./server-manifest`, builds `createWebApp`, and `export default toFetchHandler(app)`. */\n readonly serverEntry: string;\n /** Output directory for the bundled worker (absolute path). */\n readonly outDir: string;\n /** The content-hashed client entry URL (from `buildClient`'s manifest) — **baked** into the generated\n * server manifest, since a disk-less worker can't read `manifest.json` at runtime. */\n readonly clientEntry: string;\n /** The app's aggregate stylesheet URLs (`buildClient`'s `BuildManifest.css`) — baked into the generated\n * manifest so the server entry hands them to `createWebApp` (→ `<link rel=\"stylesheet\">`). Omit ⇒ no CSS\n * link (the built SSR page would otherwise render unstyled). */\n readonly styles?: readonly string[] | undefined;\n /** Per-route stylesheet URLs (`buildClient`'s `BuildManifest.routeStyles`) — baked alongside `styles`. */\n readonly routeStyles?: Readonly<Record<string, readonly string[]>> | undefined;\n /** Route/layout file → import specifier in the generated manifest (default: a relative path from the\n * manifest's location — written next to `serverEntry` — to `routesDir`). */\n readonly resolve?: (file: string) => string;\n /** Filename for the generated server-manifest module, written next to `serverEntry` (default\n * `\"server-manifest.ts\"`); your `serverEntry` imports it as `./server-manifest`. */\n readonly manifestFile?: string;\n /** Adapter build plugins (e.g. `solidBunPlugin(\"ssr\")` — Solid routes need their SSR transform at\n * build time; React's JSX is Bun-native and needs none). */\n readonly plugins?: readonly BunPlugin[];\n /** `Bun.build` resolution conditions (default `[\"workerd\", \"edge-light\", \"browser\"]`) — selects each\n * dependency's edge build. */\n readonly conditions?: readonly string[];\n /** Compile-time replacements (default `{ \"process.env.NODE_ENV\": '\"production\"' }` → production\n * React/Solid on the edge). Pass an explicit object to override (e.g. `{}` to opt out). */\n readonly define?: Readonly<Record<string, string>>;\n /** Minify the output (default `true`). */\n readonly minify?: boolean;\n /** `Bun.build` target (default `\"browser\"` — the right shape for edge runtimes: Cloudflare Workers,\n * Vercel Edge, Deno, Deno Deploy). Use `\"node\"` for a `@nifrajs/node` server (so `node:*` built-ins\n * stay external), or `\"bun\"` for a Bun server. The default `conditions` + the edge resolve shims\n * only apply to the `\"browser\"` target; `\"node\"`/`\"bun\"` resolve their own renderer builds via the\n * matching condition. */\n readonly target?: \"browser\" | \"node\" | \"bun\";\n /** **Lazy/code-split routes** (default `false`): emit `() => import(route)` loaders + bundle with\n * `splitting`, so each route is its own chunk loaded on first request (smaller cold-start parse)\n * instead of all parsed at boot. The output becomes the worker entry **+ chunk files** in `outDir`\n * — on Cloudflare, ship them with wrangler's `no_bundle` + `find_additional_modules` + an ESModule\n * `rule` (Node/Deno import the chunks natively). Eager (one self-contained file) stays the default. */\n readonly lazy?: boolean;\n}"
895
929
  },
930
+ {
931
+ "name": "buildServerVite",
932
+ "kind": "function",
933
+ "package": "@nifrajs/web",
934
+ "signature": "export declare function buildServerVite(options: BuildServerViteOptions): Promise<ServerBuild>;",
935
+ "doc": "/**\n * Build the SSR worker with Vite, emitting the SAME {@link ServerBuild} `buildServer` (Bun) does: a\n * self-contained worker bundle whose path is returned as `worker`.\n *\n * Like `buildServer`, it codegens the statically-analyzable `server-manifest` next to the entry (so no\n * `node:fs` route scan runs on a disk-less edge), then bundles. `ssr.noExternal` bundles the app + adapter\n * + `@nifrajs/*` into ONE file (the self-contained worker), while `node:` builtins stay external on the\n * `node`/`bun` targets that provide them. `NIFRA_SSR_BUNDLED` is defined so the web-react adapter uses the\n * bundled, deduped react-dom rather than re-rooting to a disk copy at runtime - the same tag `buildServer`\n * sets, and the reason a bundled SSR output does not hit the dual-React crash.\n *\n * The client-leak guards are deliberately NOT run here: a server bundle's `node:` imports are legitimate.\n */"
936
+ },
937
+ {
938
+ "name": "BuildServerViteOptions",
939
+ "kind": "interface",
940
+ "package": "@nifrajs/web",
941
+ "signature": "export interface BuildServerViteOptions {\n readonly routesDir: string;\n readonly serverEntry: string;\n readonly outDir: string;\n readonly clientEntry: string;\n readonly styles?: readonly string[] | undefined;\n readonly routeStyles?: Readonly<Record<string, readonly string[]>> | undefined;\n readonly resolve?: (file: string) => string;\n readonly manifestFile?: string;\n readonly vitePlugins?: readonly unknown[];\n readonly conditions?: readonly string[];\n readonly define?: Readonly<Record<string, string>>;\n readonly minify?: boolean;\n /** The runtime the worker targets — mirrors `buildServer`. `browser` = the edge (workerd/edge-light). */\n readonly target?: \"browser\" | \"node\" | \"bun\";\n /** Vite project root (default: the parent of `routesDir`). */\n readonly root?: string;\n}",
942
+ "doc": "/** Options for {@link buildServerVite} — the same surface as `buildServer` minus Bun-plugin specifics. */"
943
+ },
896
944
  {
897
945
  "name": "buildTarget",
898
946
  "kind": "function",
899
947
  "package": "@nifrajs/web",
900
948
  "signature": "export declare function buildTarget(target: BuildTarget, options: BuildTargetOptions): Promise<BuildTargetResult>;",
901
- "doc": "/**\n * Build a full deploy directory for `target` from a file-routed nifra app. Emits the client bundle to\n * `<outDir>/assets/*`, then per target:\n * - `static`: prerenders opted-in routes (`prerenderRoutes`) to `<outDir>/<path>/index.html` (+\n * `_data.json`); needs `prerenderApp`. No server.\n * - `cf-pages`: a `_worker.js` (edge bundle) + a `_routes.json` excluding /assets/* from the worker.\n * - `vercel`: a `.vercel/output`-shaped function isn't emitted here — `vercel` emits the bundled edge\n * entry as `<outDir>/index.js` (the CLI's docs point at `vercel`'s Build Output wrapper). [see note]\n * - `deno`/`node`/`bun`: the self-hosting server bundle (`server.js`) next to the assets.\n * The server entry is GENERATED (`generateServerEntry`) and bundled (`buildServer`); the app supplies\n * only adapter/backend/routes. Returns the manifest + a size report. Throws on any build failure.\n *\n * Note: the heavier platform wrappers (`.vercel/output` v3 layout, wrangler ISR `find_additional_modules`)\n * remain app-owned scripts; this command targets the common single-bundle deploys. See the CLI docs.\n */"
949
+ "doc": "/** Build a full deploy dir for `target` using the default Bun bundler. See {@link buildTargetWith}. */"
902
950
  },
903
951
  {
904
952
  "name": "BuildTarget",
@@ -910,7 +958,7 @@
910
958
  "name": "BuildTargetOptions",
911
959
  "kind": "interface",
912
960
  "package": "@nifrajs/web",
913
- "signature": "export interface BuildTargetOptions {\n /** The `routes/` directory to discover (absolute path). */\n readonly routesDir: string;\n /** Output directory for the assembled deploy dir (absolute path). Cleared and recreated. */\n readonly outDir: string;\n /** A scratch directory for intermediate codegen (the generated server entry + manifest) and the\n * server bundle, cleaned up after. Absolute path. */\n readonly workDir: string;\n /** The adapter's client runtime module (exports `mountRouter`), e.g. `\"@nifrajs/web-react/client\"`. */\n readonly clientModule: string;\n /** Import specifier (resolvable from `workDir`) of the module exporting `adapter`. */\n readonly adapterImport: string;\n /** Import specifier (resolvable from `workDir`) of the module exporting `backend`, or `undefined`. */\n readonly backendImport?: string;\n /** Factory that builds the app for `static` prerendering, GIVEN the client build's manifest — so the\n * emitted hydration `<script src>` uses the REAL content-hashed entry (`client.entry`) plus the same\n * styles/route-preload the server targets use. A pre-built instance can't work here: the hash isn't known\n * until `buildClient` runs inside `buildTarget`, so a hardcoded entry 404s → pages render but never\n * hydrate. Required for `target: \"static\"` (SSG drives `app.fetch`); ignored otherwise. */\n readonly prerenderApp?: (client: BuildManifest) => PrerenderAppLike | Promise<PrerenderAppLike>;\n /** Client-build plugins (e.g. the MDX/Vue/Solid Bun plugins). */\n readonly clientPlugins?: readonly BunPlugin[];\n /** Server-build plugins (e.g. the SSR variants). */\n readonly serverPlugins?: readonly BunPlugin[];\n /** Extra Bun.build resolve conditions for the CLIENT build. */\n readonly conditions?: readonly string[];\n /** Compile-time `define` replacements layered onto both builds. */\n readonly define?: Readonly<Record<string, string>>;\n /** Document `<title>` for the generated server entry. */\n readonly title?: string;\n}"
961
+ "signature": "export interface BuildTargetOptions {\n /** The `routes/` directory to discover (absolute path). */\n readonly routesDir: string;\n /** Output directory for the assembled deploy dir (absolute path). Cleared and recreated. */\n readonly outDir: string;\n /** A scratch directory for intermediate codegen (the generated server entry + manifest) and the\n * server bundle, cleaned up after. Absolute path. */\n readonly workDir: string;\n /** The adapter's client runtime module (exports `mountRouter`), e.g. `\"@nifrajs/web-react/client\"`. */\n readonly clientModule: string;\n /** Import specifier (resolvable from `workDir`) of the module exporting `adapter`. */\n readonly adapterImport: string;\n /** Import specifier (resolvable from `workDir`) of the module exporting `backend`, or `undefined`. */\n readonly backendImport?: string;\n /** Factory that builds the app for `static` prerendering, GIVEN the client build's manifest — so the\n * emitted hydration `<script src>` uses the REAL content-hashed entry (`client.entry`) plus the same\n * styles/route-preload the server targets use. A pre-built instance can't work here: the hash isn't known\n * until `buildClient` runs inside `buildTarget`, so a hardcoded entry 404s → pages render but never\n * hydrate. Required for `target: \"static\"` (SSG drives `app.fetch`); ignored otherwise. */\n readonly prerenderApp?: (client: BuildManifest) => PrerenderAppLike | Promise<PrerenderAppLike>;\n /** Client-build plugins (e.g. the MDX/Vue/Solid Bun plugins). */\n readonly clientPlugins?: readonly BunPlugin[];\n /** Server-build plugins (e.g. the SSR variants). */\n readonly serverPlugins?: readonly BunPlugin[];\n /** Extra Bun.build resolve conditions for the CLIENT build. */\n readonly conditions?: readonly string[];\n /** Compile-time `define` replacements layered onto both builds. */\n readonly define?: Readonly<Record<string, string>>;\n /** Project-root static directory copied to deploy root. `false` disables it. */\n readonly publicDir?: string | false;\n /** Prefix of environment variables allowed into the client bundle (default `\"PUBLIC_\"`). */\n readonly publicEnvPrefix?: string;\n /** Document `<title>` for the generated server entry. */\n readonly title?: string;\n}"
914
962
  },
915
963
  {
916
964
  "name": "BuildTargetResult",
@@ -919,6 +967,34 @@
919
967
  "signature": "export interface BuildTargetResult {\n /** The deploy target that was built. */\n readonly target: BuildTarget;\n /** The assembled output directory. */\n readonly outDir: string;\n /** The client build's manifest (entry URL, assets, per-route chunks/styles). */\n readonly client: BuildManifest;\n /** A human-readable note on how to run/deploy the output. */\n readonly run: string;\n /** Per-chunk size report over the emitted client (+ server) outputs. Always computed; the CLI prints\n * it only with `--report`. */\n readonly size: SizeReport;\n}",
920
968
  "doc": "/** The result of a target build — the deploy dir + the client manifest + an optional size report. */"
921
969
  },
970
+ {
971
+ "name": "buildTargetVite",
972
+ "kind": "function",
973
+ "package": "@nifrajs/web",
974
+ "signature": "export declare function buildTargetVite(target: BuildTarget, options: BuildTargetOptions): Promise<BuildTargetResult>;",
975
+ "doc": "/**\n * Build a full deploy dir for `target` using the Vite/Rollup pipeline - the escape hatch for apps that\n * need a Vite-only transform in production. Identical output shape to {@link import(\"./build.js\").buildTarget}\n * (same deploy dir, same server entry, same prerender + size report), because both delegate to the ONE\n * `buildTargetWith` orchestrator; only the two bundling steps differ.\n *\n * Pass the app's Vite plugins via `clientPlugins` / `serverPlugins` (the shared option names) - here they\n * are Vite plugins, not Bun plugins.\n */"
976
+ },
977
+ {
978
+ "name": "buildTargetWith",
979
+ "kind": "function",
980
+ "package": "@nifrajs/web",
981
+ "signature": "export declare function buildTargetWith(target: BuildTarget, options: BuildTargetOptions, bundler: Bundler): Promise<BuildTargetResult>;",
982
+ "doc": "/**\n * The bundler-agnostic deploy orchestrator: everything `buildTarget` does EXCEPT the two bundling steps,\n * which come from `bundler`. `buildTarget` passes {@link bunBundler}; `buildTargetVite`\n * (`@nifrajs/web/build-vite`) passes the Vite strategy. One orchestrator, so the deploy-dir shape,\n * server-entry codegen, prerender and size report are identical across pipelines.\n */"
983
+ },
984
+ {
985
+ "name": "bunBundler",
986
+ "kind": "const",
987
+ "package": "@nifrajs/web",
988
+ "signature": "export declare const bunBundler: Bundler;",
989
+ "doc": "/** The default (Bun) strategy — `buildClient`/`buildServer` from this module. */"
990
+ },
991
+ {
992
+ "name": "Bundler",
993
+ "kind": "interface",
994
+ "package": "@nifrajs/web",
995
+ "signature": "export interface Bundler {\n /** Build the client bundle → the shared {@link BuildManifest}. */\n buildClient(input: {\n readonly routesDir: string;\n readonly outDir: string;\n readonly clientModule: string;\n readonly plugins?: readonly unknown[];\n readonly conditions?: readonly string[];\n readonly define?: Readonly<Record<string, string>>;\n readonly publicDir?: string | false;\n readonly publicEnvPrefix?: string;\n /** Project root (Vite needs it; the Bun strategy ignores it). */\n readonly root?: string;\n }): Promise<BuildManifest>;\n /** Build the server worker → the shared {@link ServerBuild}. */\n buildServer(input: {\n readonly routesDir: string;\n readonly serverEntry: string;\n readonly outDir: string;\n readonly clientEntry: string;\n readonly target: \"browser\" | \"node\" | \"bun\";\n readonly plugins?: readonly unknown[];\n readonly define?: Readonly<Record<string, string>>;\n readonly root?: string;\n }): Promise<ServerBuild>;\n}",
996
+ "doc": "/**\n * A build-tool STRATEGY for {@link buildTargetWith} — the two bundling steps, and nothing else. Everything\n * around them (server-entry codegen, deploy assembly, prerender, size report) is bundler-agnostic and\n * lives in `buildTargetWith`, so a second bundler (Vite) is this interface, not a second orchestrator.\n *\n * Plugin lists are `readonly unknown[]` because a Bun plugin and a Vite plugin are different types; each\n * strategy casts to its own. `buildTargetWith` only forwards them.\n */"
997
+ },
922
998
  {
923
999
  "name": "bunImageBackend",
924
1000
  "kind": "function",
@@ -926,6 +1002,13 @@
926
1002
  "signature": "export declare function bunImageBackend(): ImageBackend;",
927
1003
  "doc": "/**\n * {@link ImageBackend} backed by `Bun.Image` (libjpeg-turbo / libspng / libwebp, decoded off the main\n * thread). Requires the Bun runtime. The default backend of `createImageHandler`.\n */"
928
1004
  },
1005
+ {
1006
+ "name": "BunMetafileLike",
1007
+ "kind": "interface",
1008
+ "package": "@nifrajs/web",
1009
+ "signature": "export interface BunMetafileLike {\n readonly inputs?: Readonly<Record<string, {\n readonly imports?: ReadonlyArray<{\n path?: string;\n original?: string;\n }>;\n }>>;\n readonly outputs?: Readonly<Record<string, {\n readonly entryPoint?: string;\n readonly inputs?: Readonly<Record<string, unknown>>;\n }>>;\n}",
1010
+ "doc": "/** The slice of Bun's metafile this seam consumes. Not yet in `@types/bun`; shape per the docs. */"
1011
+ },
929
1012
  {
930
1013
  "name": "bunResolverFn",
931
1014
  "kind": "function",
@@ -1566,9 +1649,23 @@
1566
1649
  "name": "CheckDiagnostic",
1567
1650
  "kind": "interface",
1568
1651
  "package": "@nifrajs/cli",
1569
- "signature": "export interface CheckDiagnostic {\n readonly rule: \"typecheck\" | \"typed-client\" | \"untyped-client\" | \"server-only-import\" | \"response-route\" | \"undeclared-dependency\" | \"duplicate-install\" | \"server-manifest-drift\" | \"manifest-drift\" | \"capability-assurance\" | \"capability-config\" | \"check-config\";\n /** `error` fails the gate (a real contract break); `warning` is advisory — surfaced to the agent but\n * does NOT fail `nifra check`, for patterns that are sometimes intentional (a route returning a raw\n * `Response`, which silently drops the typed client to `data: never` but is valid for files/redirects). */\n readonly severity: \"error\" | \"warning\";\n readonly file?: string;\n readonly line?: number;\n readonly message: string;\n /** The canonical, rule-level fix — clean of the per-occurrence snippet, so an agent can apply it\n * directly. Set for the lint rules (they have one correct fix); omitted for `typecheck` (the fix is\n * specific to each type error). */\n readonly fix?: string;\n /** A richer, agent-oriented fix hint. Diffs are only emitted when the edit is mechanical and local;\n * ambiguous cases give concrete steps instead of pretending the checker can safely rewrite code. */\n readonly suggestion?: CheckSuggestion;\n /**\n * The import chain that pulls server-only code into the browser bundle, as display labels\n * `[routeFile, …as-written specifiers…, sink]`. Set only on `server-only-import`.\n *\n * #4.4: this is now the FULL **transitive** chain — a bounded import-resolution walk (`Bun.resolveSync`\n * from each file's dir, BFS the local module graph) follows `route → ../data → ../db → node:crypto`,\n * matching the build leak-guard's depth (`detectNodeBuiltinsInClient` in `@nifrajs/web/build`). A\n * length-2 chain (`[routeFile, specifier]`) means the route imports the sink directly. When a hop can't\n * be resolved precisely (a bare pkg, a tsconfig path alias), the walk degrades to the honest direct\n * edge rather than fabricating a deeper path — never a lie.\n */\n readonly chain?: readonly string[];\n}",
1652
+ "signature": "export interface CheckDiagnostic {\n readonly rule: \"typecheck\" | \"typed-client\" | \"untyped-client\" | \"server-only-import\" | \"removed-import\" | \"response-route\" | \"undeclared-dependency\" | \"duplicate-install\" | \"server-manifest-drift\" | \"manifest-drift\" | \"capability-assurance\" | \"capability-config\" | \"check-config\";\n /** `error` fails the gate (a real contract break); `warning` is advisory — surfaced to the agent but\n * does NOT fail `nifra check`, for patterns that are sometimes intentional (a route returning a raw\n * `Response`, which silently drops the typed client to `data: never` but is valid for files/redirects). */\n readonly severity: \"error\" | \"warning\";\n readonly file?: string;\n readonly line?: number;\n readonly message: string;\n /** The canonical, rule-level fix — clean of the per-occurrence snippet, so an agent can apply it\n * directly. Set for the lint rules (they have one correct fix); omitted for `typecheck` (the fix is\n * specific to each type error). */\n readonly fix?: string;\n /** A richer, agent-oriented fix hint. Diffs are only emitted when the edit is mechanical and local;\n * ambiguous cases give concrete steps instead of pretending the checker can safely rewrite code. */\n readonly suggestion?: CheckSuggestion;\n /**\n * The import chain that pulls server-only code into the browser bundle, as display labels\n * `[routeFile, …as-written specifiers…, sink]`. Set only on `server-only-import`.\n *\n * #4.4: this is now the FULL **transitive** chain — a bounded import-resolution walk (`Bun.resolveSync`\n * from each file's dir, BFS the local module graph) follows `route → ../data → ../db → node:crypto`,\n * matching the build leak-guard's depth (`detectNodeBuiltinsInClient` in `@nifrajs/web/build`). A\n * length-2 chain (`[routeFile, specifier]`) means the route imports the sink directly. When a hop can't\n * be resolved precisely (a bare pkg, a tsconfig path alias), the walk degrades to the honest direct\n * edge rather than fabricating a deeper path — never a lie.\n */\n readonly chain?: readonly string[];\n}",
1570
1653
  "doc": "/** A single machine-readable check failure — the unit an agent (or CI) acts on. */"
1571
1654
  },
1655
+ {
1656
+ "name": "checkPipelineSeparation",
1657
+ "kind": "function",
1658
+ "package": "@nifrajs/cli",
1659
+ "signature": "export declare function checkPipelineSeparation(slots: {\n readonly vitePlugins?: readonly unknown[];\n readonly clientPlugins?: readonly unknown[];\n readonly serverPlugins?: readonly unknown[];\n}): PipelineMismatch[];",
1660
+ "doc": "/** Check every slot of a resolved framework config. */"
1661
+ },
1662
+ {
1663
+ "name": "checkPipelineSlot",
1664
+ "kind": "function",
1665
+ "package": "@nifrajs/cli",
1666
+ "signature": "export declare function checkPipelineSlot(slot: PipelineSlot, plugins: readonly unknown[]): PipelineMismatch[];",
1667
+ "doc": "/**\n * Check one slot's resolved plugin list. Pure: takes the already-resolved array so a thunk-valued\n * field is awaited by the caller, and so this stays testable without loading a project.\n */"
1668
+ },
1572
1669
  {
1573
1670
  "name": "CheckResult",
1574
1671
  "kind": "interface",
@@ -1582,6 +1679,13 @@
1582
1679
  "package": "@nifrajs/cli",
1583
1680
  "signature": "export interface CheckSuggestion {\n readonly kind: \"edit\" | \"command\" | \"manual\";\n readonly title: string;\n readonly diff?: string;\n /** argv array, not a shell string, so MCP clients can run it without quoting hazards. */\n readonly command?: readonly string[];\n readonly steps?: readonly string[];\n}"
1584
1681
  },
1682
+ {
1683
+ "name": "chooseBuildPipeline",
1684
+ "kind": "function",
1685
+ "package": "@nifrajs/cli",
1686
+ "signature": "export declare function chooseBuildPipeline(slots: PipelineSlots, forced?: BuildPipeline): PipelineDecision;",
1687
+ "doc": "/**\n * Pick the bundler for `nifra build`, per app rather than per global default.\n *\n * The phase defaults are deliberately asymmetric - `nifra dev` is Vite (for the plugin ecosystem, and\n * because Bun's DEV-server bundler cannot compile CSS Modules), `nifra build` is Bun (faster and\n * Bun-native). For an app with no transforms that asymmetry is free: there is nothing to disagree about.\n * For an app whose ONLY transforms are Vite plugins it is not. Those plugins run in dev and are then\n * dropped by the Bun build - `buildForTarget` reads `clientPlugins`/`serverPlugins`, never `vitePlugins`\n * - so the build succeeds, the output looks plausible, and the transform simply did not happen. That is\n * the same failure {@link checkPipelineSeparation} exists to prevent, reached by crossing PHASES instead\n * of slots, and the slot check cannot see it because the plugins are correctly placed.\n *\n * So the default follows the app: Vite plugins with no Bun counterpart means the app has exactly one\n * pipeline that can build it, and that is the one used. An app declaring BOTH has supplied the Bun\n * equivalent on purpose - nothing is dropped - so the fast Bun default stands.\n *\n * `forced` is `--vite` / `--bun` and always wins, except for the one combination that silently discards\n * work, which throws instead. Refusing rather than warning matches the slot guard: a build that succeeds\n * and omits the transform is worse than one that stops and says so.\n */"
1688
+ },
1585
1689
  {
1586
1690
  "name": "ChunkSize",
1587
1691
  "kind": "interface",
@@ -1629,6 +1733,13 @@
1629
1733
  "signature": "export declare function client<App>(baseUrl: string, options?: ClientOptions): Treaty<App>;",
1630
1734
  "doc": "/**\n * Create an end-to-end-typed client for a nifra server. Two modes:\n *\n * // coupled — typed from the server's type (`typeof app`)\n * const api = client<typeof app>(\"http://localhost:3000\")\n *\n * // decoupled — typed from a contract VALUE, no server import\n * const api = client(contract, \"https://api.example.com\")\n *\n * const { data, error } = await api.users({ id: \"1\" }).get()\n *\n * Both return the same Eden-style proxy. Browser-safe: uses only `fetch` /\n * `Proxy` / `URL`, never the server runtime. Never throws — network and non-2xx\n * responses surface as a `Result` `error`.\n */"
1631
1735
  },
1736
+ {
1737
+ "name": "CLIENT_ENTRY_PATH",
1738
+ "kind": "const",
1739
+ "package": "@nifrajs/web",
1740
+ "signature": "export declare const CLIENT_ENTRY_PATH = \"/__nifra/client.js\";",
1741
+ "doc": "/**\n * The stable URL every SSR'd page points its client entry at.\n *\n * It has to be stable, and Bun's own URL is not: that one is a content hash over the entire client graph,\n * so it changes whenever anything the entry imports changes. This path redirects to whichever hashed\n * chunk Bun is serving at the moment the browser asks, which is the only formulation that stays correct\n * across a rebuild.\n */"
1742
+ },
1632
1743
  {
1633
1744
  "name": "clientCall",
1634
1745
  "kind": "function",
@@ -1643,6 +1754,13 @@
1643
1754
  "signature": "export type ClientIpTrust = {\n readonly trustedHops: number;\n} | {\n readonly header: string;\n};",
1644
1755
  "doc": "/**\n * How much of the forwarding chain to trust when deriving `c.clientIp`. Omit for the safe default\n * (socket peer only, no header believed).\n *\n * - `{ trustedHops: n }` - trust `n` reverse-proxy hops in front of the app. The caller is taken from\n * `X-Forwarded-For` counted from the right past the `n` trusted hops (the socket peer is hop 0). Use\n * this when the app sits behind `n` proxies **you operate**. Fewer entries than trusted hops means\n * the header is too short to trust, so `c.clientIp` is `undefined` (fail closed).\n * - `{ header: name }` - trust a single named header's first value (e.g. a CDN that overwrites it at\n * the edge). Only declare this when the app is reachable **exclusively** through that edge; a\n * directly-reachable app lets any client forge the header.\n */"
1645
1756
  },
1757
+ {
1758
+ "name": "ClientModuleGraph",
1759
+ "kind": "interface",
1760
+ "package": "@nifrajs/web",
1761
+ "signature": "export interface ClientModuleGraph {\n /** Module id → its imports. Ids are bundler-native (Bun graph keys, Rollup module ids). */\n readonly modules: Readonly<Record<string, GraphModule>>;\n /** Output path → what it contains. */\n readonly chunks: Readonly<Record<string, GraphChunk>>;\n}",
1762
+ "doc": "/** What a client build looks like to the guards, whichever bundler produced it. */"
1763
+ },
1646
1764
  {
1647
1765
  "name": "ClientOptions",
1648
1766
  "kind": "interface",
@@ -1667,7 +1785,7 @@
1667
1785
  "name": "ClientRouterOptions",
1668
1786
  "kind": "interface",
1669
1787
  "package": "@nifrajs/web",
1670
- "signature": "export interface ClientRouterOptions {\n readonly patterns: readonly RoutePattern[];\n readonly initial: RouterState;\n /** Override the loader-data fetch (tests inject a stub; defaults to a same-origin JSON GET). */\n readonly fetchData?: FetchRouteData;\n /** Ensure a route's code chunk is loaded before rendering (code-splitting). Awaited in parallel\n * with the loader data, so `pending` covers both. Omit when the bundle isn't split. */\n readonly loadModule?: (routeId: string) => Promise<void>;\n}"
1788
+ "signature": "export interface ClientRouterOptions {\n readonly patterns: readonly RoutePattern[];\n readonly initial: RouterState;\n /** Override the loader-data fetch (tests inject a stub; defaults to a same-origin JSON GET). */\n readonly fetchData?: FetchRouteData;\n /** Ensure a route's code chunk is loaded before rendering (code-splitting). Awaited in parallel\n * with the loader data, so `pending` covers both. Omit when the bundle isn't split. */\n readonly loadModule?: (routeId: string) => Promise<void>;\n /** Terminal status → client route id. Generated entries populate this from `_404` and\n * `_<status>` files so soft navigation renders the same boundary as a hard request. */\n readonly statusRoutes?: Readonly<Record<number, string>>;\n}"
1671
1789
  },
1672
1790
  {
1673
1791
  "name": "cloudflareLoader",
@@ -1702,6 +1820,19 @@
1702
1820
  "package": "@nifrajs/web",
1703
1821
  "signature": "export interface CloudflarePagesRoutesOptions {\n /** Prerendered request paths (e.g. `prerenderRoutes(...).prerendered.map(p => p.path)`, or the\n * build's `prerendered.json`). Each is excluded from the worker — plus its static `_data.json`. */\n readonly prerendered: readonly string[];\n /** Extra globs to keep OFF the worker (CDN-served). Default `[\"/assets/*\"]` (the hashed bundle). */\n readonly staticGlobs?: readonly string[];\n}"
1704
1822
  },
1823
+ {
1824
+ "name": "cloudflareRouteRules",
1825
+ "kind": "function",
1826
+ "package": "@nifrajs/web",
1827
+ "signature": "export declare function cloudflareRouteRules(publicFiles: readonly string[], \n/**\n * Every route pattern the app serves. Required, not defaulted: an empty list means \"this app serves\n * no routes\", which makes every directory collapsible - a defensible answer to state deliberately and\n * a dangerous one to arrive at by forgetting the argument.\n */\nroutePatterns: readonly string[]): CloudflareRouteRules;",
1828
+ "doc": "/**\n * Build the cf-pages `_routes.json` rules for a set of copied public files, within Cloudflare's budget.\n *\n * `exclude` is what Pages serves straight from the CDN instead of invoking the worker, so naming every\n * public file is ideal - and impossible past ~99 of them. A `public/` of icons, fonts and share images\n * clears 100 easily, and the rejection lands at `wrangler pages deploy`, long after the build reported\n * success.\n *\n * A glob is emitted only where it cannot be wrong. `/assets/*` always is: the build owns that prefix\n * outright, since it is where the hashed bundle is written. A `public/` subdirectory is different - the\n * name is the author's, and a `/blog/*` rule would hand Pages every future path under `/blog/`, so a\n * `/blog/:slug` route would 404 from the CDN in production only. {@link collapsibleDirs} therefore\n * collapses a directory only after checking it against the app's real route patterns, which is what\n * makes the compaction safe rather than merely smaller. Everything else stays an exact path, and a name\n * containing `*` is dropped rather than escaped, since the rule would become a wildcard.\n *\n * What still does not fit is dropped, which is safe because the list is only an optimization: the\n * generated worker serves any allowlisted path it receives through the ASSETS binding, so an omitted\n * file costs one worker invocation, not a 404.\n */"
1829
+ },
1830
+ {
1831
+ "name": "CloudflareRouteRules",
1832
+ "kind": "interface",
1833
+ "package": "@nifrajs/web",
1834
+ "signature": "export interface CloudflareRouteRules {\n readonly include: readonly string[];\n readonly exclude: readonly string[];\n /** Public files the budget could not name, so the build can say so instead of capping silently. */\n readonly omitted: readonly string[];\n}"
1835
+ },
1705
1836
  {
1706
1837
  "name": "codePositionMask",
1707
1838
  "kind": "function",
@@ -1741,7 +1872,7 @@
1741
1872
  "kind": "function",
1742
1873
  "package": "@nifrajs/cli",
1743
1874
  "signature": "export declare function collectDuplicateInstalls(cwd: string, rootPackage: Record<string, unknown>): Promise<DuplicateInstallFinding[]>;",
1744
- "doc": "/** Find identity-sensitive dependencies that resolve to multiple physical directories. Two copies at\n * the same version still fail: module identity (React hooks, Nifra symbols/registries) is path-based. */"
1875
+ "doc": "/** Find identity-sensitive dependencies that resolve to multiple physical directories. Two copies at\n * the same version still fail: module identity (React hooks, Nifra symbols/registries) is path-based.\n *\n * `cwd` is where the user ran the check; the scan is anchored at the workspace root above it (see\n * {@link findWorkspaceRoot}) so sibling packages are probed, while paths are still REPORTED relative\n * to `cwd` so the findings read the way the user's terminal does. */"
1745
1876
  },
1746
1877
  {
1747
1878
  "name": "CollectLevelsOptions",
@@ -1791,12 +1922,19 @@
1791
1922
  "signature": "export declare const commonSecretPatterns: ReadonlyArray<RegExp>;",
1792
1923
  "doc": "/**\n * A conservative, high-signal set of patterns for {@link RedactOptions.valuePatterns} — opt in by\n * passing it (or a subset) to `jsonLogger`/`redactLogFields`. Covers bearer tokens, JWTs, emails, and a\n * few well-known key formats (Stripe, GitHub, AWS access-key ids). Chosen to minimize false positives;\n * add your own (e.g. internal id formats) as needed. Every pattern is global so all matches are scrubbed.\n */"
1793
1924
  },
1925
+ {
1926
+ "name": "compareMixedPartsSpecificity",
1927
+ "kind": "function",
1928
+ "package": "@nifrajs/core",
1929
+ "signature": "export declare function compareMixedPartsSpecificity(left: readonly MixedPart[], right: readonly MixedPart[]): number;",
1930
+ "doc": "/**\n * Total ordering for mixed segment shapes, shared by the trie and regex-based routers.\n *\n * Code-unit comparison, never `localeCompare`: this exists so the server and the browser agree on which\n * of two equally-weighted patterns wins, and `localeCompare` answers by the RUNTIME's locale - a server\n * and a visitor in different locales would order the same pair differently, which is the divergence\n * being prevented.\n */"
1931
+ },
1794
1932
  {
1795
1933
  "name": "compareRoutePatternSpecificity",
1796
1934
  "kind": "function",
1797
1935
  "package": "@nifrajs/core",
1798
1936
  "signature": "export declare function compareRoutePatternSpecificity(left: CompiledRoutePattern, right: CompiledRoutePattern): number;",
1799
- "doc": "/** Core precedence: static > param > wildcard at the first differing segment, independent of order. */"
1937
+ "doc": "/** Core precedence: static > mixed > param > wildcard at the first differing segment, independent of\n * registration order. */"
1800
1938
  },
1801
1939
  {
1802
1940
  "name": "CompiledRoutePattern",
@@ -2120,6 +2258,13 @@
2120
2258
  "signature": "export interface CookieOptions {\n /** Lifetime in seconds. `0` (with a past `expires`) deletes the cookie. */\n readonly maxAge?: number;\n readonly expires?: Date;\n readonly path?: string;\n readonly domain?: string;\n readonly secure?: boolean;\n readonly httpOnly?: boolean;\n readonly sameSite?: \"strict\" | \"lax\" | \"none\";\n readonly partitioned?: boolean;\n}",
2121
2259
  "doc": "/** Attributes for a `Set-Cookie`. `expires` is a `Date`; `maxAge` is in **seconds**. */"
2122
2260
  },
2261
+ {
2262
+ "name": "copyPublicDir",
2263
+ "kind": "function",
2264
+ "package": "@nifrajs/web",
2265
+ "signature": "export declare function copyPublicDir(from: string, to: string): Promise<string[]>;",
2266
+ "doc": "/**\n * Copy `from` into `to`, returning the URL paths copied (sorted).\n *\n * Lives here, not beside `servePublicDir`, because it is BUILD-time: it reaches for `Bun.Glob` and\n * `node:fs`, and `public-dir.ts` is reachable from the client bundle graph through the package\n * entry - a dynamic `import(\"bun\")` there fails the browser build outright.\n */"
2267
+ },
2123
2268
  {
2124
2269
  "name": "cors",
2125
2270
  "kind": "function",
@@ -2204,7 +2349,7 @@
2204
2349
  "kind": "function",
2205
2350
  "package": "@nifrajs/web",
2206
2351
  "signature": "export declare function createDevServer(options: DevServerOptions): Promise<DevServer>;",
2207
- "doc": "/** Start the dev server: build → serve → watch → rebuild + reload on change. */"
2352
+ "doc": "/** Start the Bun dev server: generate → bundle → serve → watch → hot-reload on change. */"
2208
2353
  },
2209
2354
  {
2210
2355
  "name": "createDurableEffectJournal",
@@ -2471,7 +2616,7 @@
2471
2616
  "name": "CreateWebAppOptions",
2472
2617
  "kind": "interface",
2473
2618
  "package": "@nifrajs/web",
2474
- "signature": "export interface CreateWebAppOptions {\n readonly adapter: RenderAdapter;\n readonly manifest: Manifest;\n /** URL of the built client entry (module script) injected into every page. */\n readonly clientEntry: string;\n /** Default document title for all pages. */\n readonly title?: string;\n /** Injected into each loader's `ctx.api` — typically an `inProcessClient(app)` (typed\n * per-route via `@nifrajs/client`'s `createRoutes`). Opaque to the core.\n *\n * **Auto-mount.** Every `inProcessClient(backend)` exposes the symbol-keyed platform-aware backend\n * mount interface from `@nifrajs/core/mount`; `createWebApp` also serves that backend over HTTP at\n * {@link apiPrefix} (default `/api`): a request whose pathname starts with the prefix is dispatched\n * before page routing with the same `env`/`waitUntil` platform context, and the backend's `Response`\n * is returned untouched. The mount runs in `nifra dev` too. Pass `apiPrefix: \"\"` to disable it. */\n readonly api?: unknown;\n /** HTTP path prefix the {@link api} backend is auto-mounted at (default `\"/api\"`). A request whose\n * pathname is exactly the prefix or starts with `prefix + \"/\"` is dispatched to the backend before\n * page routing; the backend therefore defines its routes at the **full** path (`server().post(\"/api/\n * sync\", …)`), matching the in-process `inProcessClient` call sites. Set to `\"\"` to disable the\n * auto-mount entirely (the app serves pages only and `api` stays a loader-only `ctx.api`). Mounting\n * is also a no-op when `api` does not expose the symbol mount. */\n readonly apiPrefix?: string;\n /** Secret for **draft / preview mode** (see `enableDraft`). When set, a request carrying a valid\n * signed `__nifra_draft` cookie gets `ctx.draft === true` in loaders/actions (else always `false`).\n * Pair with `withISR({ draftSecret })` so editors bypass the cache. Omit to disable draft mode. */\n readonly draftSecret?: string;\n /** Per-route chunk URLs (`buildClient`'s `BuildManifest.routes`) — `routeId → [layout chunks…, own\n * chunk]`. When present, each page `modulepreload`s its matched route's chunks alongside the entry,\n * so the route code downloads in parallel (no entry→route-chunk waterfall). Omit ⇒ entry-only. */\n readonly routePreload?: Readonly<Record<string, readonly string[]>>;\n /** The app's bundled stylesheet URLs (`buildClient`'s `BuildManifest.css`) — the aggregate, injected\n * as `<link rel=\"stylesheet\">` in a page's `<head>`. Used as the fallback for any route absent from\n * {@link routeStyles}. Omit ⇒ no links (dev, where Vite injects CSS, or a CSS-free app). */\n readonly styles?: readonly string[];\n /** Per-route stylesheet URLs (`buildClient`'s `BuildManifest.routeStyles`) — `routeId → [chain CSS]`.\n * When a matched route has an entry here, only those (its layout chain + own CSS) are linked instead\n * of the aggregate `styles`, so a page ships only the CSS it uses. An empty array ⇒ no `<link>` (the\n * page imports no CSS). Routes absent here fall back to `styles`. Omit ⇒ always use `styles`. */\n readonly routeStyles?: Readonly<Record<string, readonly string[]>>;\n /** SSG: the prerendered-path set (e.g. `enumerateStaticRoutes(routes).paths` or the build's\n * `prerendered.json`). Injected as `window.__NIFRA_PRERENDERED__` on every page so a client soft-nav\n * into a prerendered route fetches its static `_data.json` instead of hitting the worker. */\n readonly prerenderedPaths?: readonly string[];\n /** SSG: per dynamic route pattern, its `getStaticPaths` `fallback` (from `enumerateStaticRoutes` or\n * the build's `prerendered.json`). A route mapped to `\"404\"` rejects any path NOT in\n * `prerenderedPaths` with the 404 page — the unlisted path simply doesn't exist. `\"ssr\"` (the\n * default for unmapped routes) renders unlisted paths on-demand. */\n readonly staticFallbacks?: Readonly<Record<string, \"ssr\" | \"404\">>;\n /** Observe every loader/action failure — for error-reporting plugins (Sentry-style). Called for\n * real throws (not control-flow `Response`s like `redirect`), **before** the nearest `_error`\n * boundary renders / a soft-nav 500 / a rethrow — so it sees errors that the boundary would\n * otherwise hide. Observation only; its own throws are swallowed so a faulty reporter can't break\n * rendering. (`beforeLoader` is intentionally omitted — the core HTTP hooks already cover\n * pre-request work.) */\n readonly onLoaderError?: (error: unknown, ctx: {\n readonly request: Request;\n readonly params: Readonly<Record<string, string>>;\n readonly route: string;\n }) => void;\n}"
2619
+ "signature": "export interface CreateWebAppOptions {\n readonly adapter: RenderAdapter;\n readonly manifest: Manifest;\n /** URL of the built client entry (module script) injected into every page. */\n readonly clientEntry: string;\n /** Default document title for all pages. */\n readonly title?: string;\n /** Injected into each loader's `ctx.api` — typically an `inProcessClient(app)` (typed\n * per-route via `@nifrajs/client`'s `createRoutes`). Opaque to the core.\n *\n * **Auto-mount.** Every `inProcessClient(backend)` exposes the symbol-keyed platform-aware backend\n * mount interface from `@nifrajs/core/mount`; `createWebApp` also serves that backend over HTTP at\n * {@link apiPrefix} (default `/api`): a request whose pathname starts with the prefix is dispatched\n * before page routing with the same `env`/`waitUntil` platform context, and the backend's `Response`\n * is returned untouched. The mount runs in `nifra dev` too. Pass `apiPrefix: \"\"` to disable it. */\n readonly api?: unknown;\n /** HTTP path prefix the {@link api} backend is auto-mounted at (default `\"/api\"`). A request whose\n * pathname is exactly the prefix or starts with `prefix + \"/\"` is dispatched to the backend before\n * page routing; the backend therefore defines its routes at the **full** path (`server().post(\"/api/\n * sync\", …)`), matching the in-process `inProcessClient` call sites. Set to `\"\"` to disable the\n * auto-mount entirely (the app serves pages only and `api` stays a loader-only `ctx.api`). Mounting\n * is also a no-op when `api` does not expose the symbol mount. */\n readonly apiPrefix?: string;\n /**\n * Strip {@link apiPrefix} from the pathname before dispatching to `api` (default `false`).\n *\n * The default suits a backend that declares FULL paths (`server().post(\"/api/sync\", …)`), which is\n * right when it is only ever mounted here. Set this when the backend declares its routes WITHOUT the\n * prefix because it also runs standalone behind its own shell, so its paths cannot carry a prefix that\n * only exists when it is mounted. Without it every request 404s inside the backend, and the workaround\n * is a `Proxy` that rewrites each URL.\n */\n readonly apiStrip?: boolean;\n /**\n * Sub-apps mounted ahead of page routing - an auth handler, a webhook receiver, a stack's routes.\n *\n * Structural on purpose: anything with `{ path, app: { fetch } }` fits, so a library that exposes its\n * routes as such a list mounts as `mounts: theirRoutes` without `@nifrajs/web` taking a dependency on\n * it. `better-auth` is the motivating case - it is not a `backend` route, so `/api/auth/*` used to 404\n * silently.\n *\n * Tried longest-path-first and BEFORE the `api` mount, so a mount at `/api/auth` wins over a backend\n * at `/api` no matter which was declared first. `stripPrefix` is the per-mount form of {@link apiStrip}:\n * leave it off to pass the full path through.\n */\n readonly mounts?: ReadonlyArray<{\n readonly path: string;\n readonly app: {\n fetch(request: Request): Response | Promise<Response>;\n };\n readonly stripPrefix?: boolean;\n }>;\n /** Secret for **draft / preview mode** (see `enableDraft`). When set, a request carrying a valid\n * signed `__nifra_draft` cookie gets `ctx.draft === true` in loaders/actions (else always `false`).\n * Pair with `withISR({ draftSecret })` so editors bypass the cache. Omit to disable draft mode. */\n readonly draftSecret?: string;\n /** Per-route chunk URLs (`buildClient`'s `BuildManifest.routes`) — `routeId → [layout chunks…, own\n * chunk]`. When present, each page `modulepreload`s its matched route's chunks alongside the entry,\n * so the route code downloads in parallel (no entry→route-chunk waterfall). Omit ⇒ entry-only. */\n readonly routePreload?: Readonly<Record<string, readonly string[]>>;\n /** The app's bundled stylesheet URLs (`buildClient`'s `BuildManifest.css`) — the aggregate, injected\n * as `<link rel=\"stylesheet\">` in a page's `<head>`. Used as the fallback for any route absent from\n * {@link routeStyles}. Omit ⇒ no links (dev, where Vite injects CSS, or a CSS-free app). */\n readonly styles?: readonly string[];\n /** Per-route stylesheet URLs (`buildClient`'s `BuildManifest.routeStyles`) — `routeId → [chain CSS]`.\n * When a matched route has an entry here, only those (its layout chain + own CSS) are linked instead\n * of the aggregate `styles`, so a page ships only the CSS it uses. An empty array ⇒ no `<link>` (the\n * page imports no CSS). Routes absent here fall back to `styles`. Omit ⇒ always use `styles`. */\n readonly routeStyles?: Readonly<Record<string, readonly string[]>>;\n /** SSG: the prerendered-path set (e.g. `enumerateStaticRoutes(routes).paths` or the build's\n * `prerendered.json`). Injected as `window.__NIFRA_PRERENDERED__` on every page so a client soft-nav\n * into a prerendered route fetches its static `_data.json` instead of hitting the worker. */\n readonly prerenderedPaths?: readonly string[];\n /** SSG: per dynamic route pattern, its `getStaticPaths` `fallback` (from `enumerateStaticRoutes` or\n * the build's `prerendered.json`). A route mapped to `\"404\"` rejects any path NOT in\n * `prerenderedPaths` with the 404 page — the unlisted path simply doesn't exist. `\"ssr\"` (the\n * default for unmapped routes) renders unlisted paths on-demand. */\n readonly staticFallbacks?: Readonly<Record<string, \"ssr\" | \"404\">>;\n /** Observe every loader/action failure — for error-reporting plugins (Sentry-style). Called for\n * real throws (not control-flow `Response`s like `redirect`), **before** the nearest `_error`\n * boundary renders / a soft-nav 500 / a rethrow — so it sees errors that the boundary would\n * otherwise hide. Observation only; its own throws are swallowed so a faulty reporter can't break\n * rendering. (`beforeLoader` is intentionally omitted — the core HTTP hooks already cover\n * pre-request work.) */\n readonly onLoaderError?: (error: unknown, ctx: {\n readonly request: Request;\n readonly params: Readonly<Record<string, string>>;\n readonly route: string;\n }) => void;\n}"
2475
2620
  },
2476
2621
  {
2477
2622
  "name": "CronError",
@@ -2914,6 +3059,13 @@
2914
3059
  "package": "@nifrajs/cli",
2915
3060
  "signature": "export interface DependencyMoveChange {\n readonly file: string;\n readonly field: string;\n readonly from: string;\n readonly to: string;\n readonly fromVersion: string;\n readonly toVersion: string;\n readonly action: \"renamed\" | \"removed\";\n}"
2916
3061
  },
3062
+ {
3063
+ "name": "deriveRouteEntry",
3064
+ "kind": "function",
3065
+ "package": "@nifrajs/web",
3066
+ "signature": "export declare function deriveRouteEntry(id: string, pattern: string, module: Pick<RouteModule, \"prerender\" | \"getStaticPaths\" | \"revalidate\" | \"hydrate\">, prerenderedPaths?: readonly string[]): RouteManifestEntry;",
3067
+ "doc": "/**\n * Derive one route's behaviour from its module exports.\n *\n * `prerender`/`getStaticPaths` win over `revalidate`: a page rendered at build time is not revalidated at\n * runtime, so if a route declares both, the build-time answer is the one that describes what ships. That\n * combination is worth surfacing rather than silently resolving, which is what `conflicts` is for once a\n * target is known.\n */"
3068
+ },
2917
3069
  {
2918
3070
  "name": "describeProject",
2919
3071
  "kind": "function",
@@ -2960,27 +3112,55 @@
2960
3112
  "name": "detectNodeBuiltinsInClient",
2961
3113
  "kind": "function",
2962
3114
  "package": "@nifrajs/web",
2963
- "signature": "export declare function detectNodeBuiltinsInClient(meta: BunMetafile | undefined): ReadonlyArray<NodeBuiltinFinding>;",
3115
+ "signature": "export declare function detectNodeBuiltinsInClient(graph: ClientModuleGraph): ReadonlyArray<NodeBuiltinFinding>;",
2964
3116
  "doc": "/**\n * Scan a build's metafile for any `node:` builtin that a USER module pulled into a CLIENT output\n * chunk, returning a sorted, deduped list of {@link NodeBuiltinFinding}s. Three graph facts combine so\n * the report is precise AND actionable:\n * 1. **What the user wrote** — only builtins imported by a NON-`node:` input count, so Bun's own\n * polyfill chain (`node:crypto` → `node:buffer`/`node:stream`/…) doesn't bury the real cause.\n * 2. **Where it landed** — the chunk is read from the per-output `inputs`, so the error names the\n * emitted file to look at.\n * 3. **How it got there** — the shortest import chain from a user entry to the builtin\n * (`shortestBuiltinChain`), so the error points straight at the offending `import` line instead of\n * leaving the dev to grep the dependency tree (the DX gap this closes).\n * Graph-based (never the emitted text), so it survives minification and can't be fooled by a string\n * literal that merely contains `\"node:crypto\"`. Pure + exported for unit testing. Empty ⇒ clean.\n */"
2965
3117
  },
2966
3118
  {
2967
3119
  "name": "detectServerOnlyInClient",
2968
3120
  "kind": "function",
2969
3121
  "package": "@nifrajs/web",
2970
- "signature": "export declare function detectServerOnlyInClient(meta: BunMetafile | undefined): ReadonlyArray<ServerOnlyFinding>;",
3122
+ "signature": "export declare function detectServerOnlyInClient(graph: ClientModuleGraph): ReadonlyArray<ServerOnlyFinding>;",
2971
3123
  "doc": "/**\n * Scan a build's metafile for any module that opts into the `server-only` marker (a side-effect\n * `import \"@nifrajs/web/server-only\"`) yet landed in a CLIENT output chunk, returning a sorted, deduped\n * list of {@link ServerOnlyFinding}s. Mirrors {@link detectNodeBuiltinsInClient}: it reads the SAME\n * graph facts — which inputs import the marker (the \"marked\" modules), which chunk each landed in (the\n * per-output `inputs`), and the shortest import chain from a user entry to it. The marker module ITSELF\n * (which imports nothing) is excluded — only the modules that *opt in* are flagged. Pure + exported for\n * unit testing. Empty ⇒ clean.\n */"
2972
3124
  },
3125
+ {
3126
+ "name": "DevEntryMatch",
3127
+ "kind": "interface",
3128
+ "package": "@nifrajs/web",
3129
+ "signature": "export interface DevEntryMatch {\n readonly src: string;\n /**\n * Stylesheet URLs Bun extracted from the entry's CSS imports.\n *\n * These are not a nicety. Bun's bundler pulls `import \"./app.css\"` out of the JS graph and links it\n * from the HTML *it* bundled - the throwaway probe page, which nobody ever sees. nifra renders the real\n * pages, so unless these are carried across, a Bun-pipeline dev session serves every page unstyled\n * while the production build (which reads CSS from the build manifest) is fine. That is the worst\n * possible shape for a bug: dev-only, silent, and looks like the app's own CSS is broken.\n */\n readonly styles: readonly string[];\n readonly via: DevEntryVia;\n}",
3130
+ "doc": "/** A located client entry: the URL to hand nifra, its stylesheets, and which signal found it. */"
3131
+ },
3132
+ {
3133
+ "name": "devEntryNotFoundMessage",
3134
+ "kind": "function",
3135
+ "package": "@nifrajs/web",
3136
+ "signature": "export declare function devEntryNotFoundMessage(probeUrl: string, html: string): string;",
3137
+ "doc": "/** The error thrown when Bun's dev server serves a page nothing in it can be recognised as the entry. */"
3138
+ },
3139
+ {
3140
+ "name": "DevEntryVia",
3141
+ "kind": "type",
3142
+ "package": "@nifrajs/web",
3143
+ "signature": "export type DevEntryVia = \"marker\" | \"single-bun-script\";",
3144
+ "doc": "/** How the entry URL was found - reported so a fallback hit is visible rather than silent. */"
3145
+ },
3146
+ {
3147
+ "name": "devHtml",
3148
+ "kind": "function",
3149
+ "package": "@nifrajs/web",
3150
+ "signature": "export declare function devHtml(entryHref: string): string;",
3151
+ "doc": "/**\n * The throwaway HTML document. Its only job is to make Bun bundle the entry and assign it a URL - it is\n * never shown to a user, so it carries no app markup. The `<div id=\"root\">` is there purely so the\n * document stands on its own if someone opens the probe path directly while debugging.\n */"
3152
+ },
2973
3153
  {
2974
3154
  "name": "DevServer",
2975
3155
  "kind": "interface",
2976
3156
  "package": "@nifrajs/web",
2977
- "signature": "export interface DevServer {\n readonly port: number;\n stop(): void;\n}"
3157
+ "signature": "export interface DevServer {\n readonly port: number;\n /**\n * The URL pages point their client entry at — always {@link CLIENT_ENTRY_PATH}, never Bun's hashed\n * chunk. Deliberately not the underlying chunk URL: that one moves on every rebuild, and anything\n * holding onto it is holding onto a URL that will stop working.\n */\n readonly clientEntry: string;\n stop(): void;\n}"
2978
3158
  },
2979
3159
  {
2980
3160
  "name": "DevServerOptions",
2981
3161
  "kind": "interface",
2982
3162
  "package": "@nifrajs/web",
2983
- "signature": "export interface DevServerOptions extends Omit<BuildClientOptions, \"minify\"> {\n /**\n * Build the nifra app for the freshly-built client entry. `importQuery` changes on every\n * rebuild — pass it to `discoverRoutes(routesDir, { importQuery })` so SSR re-imports edited\n * route modules instead of Bun's cached copies.\n */\n readonly createApp: (clientEntry: string, importQuery: string) => FetchApp | Promise<FetchApp>;\n /** Directories to watch (default: `[routesDir]`). */\n readonly watch?: readonly string[];\n /** Port to listen on (default {@link DEFAULT_DEV_PORT}). */\n readonly port?: number;\n}"
3163
+ "signature": "export interface DevServerOptions extends Omit<BuildClientOptions, \"minify\"> {\n /**\n * Build the nifra app for the current client entry. `importQuery` changes on every reload — pass it to\n * `discoverRoutes(routesDir, { importQuery })` so SSR re-imports edited route modules instead of Bun's\n * cached copies.\n */\n readonly createApp: (clientEntry: string, importQuery: string) => FetchApp | Promise<FetchApp>;\n /** Directories to watch (default: `[routesDir]`). */\n readonly watch?: readonly string[];\n /** Port to listen on (default {@link DEFAULT_DEV_PORT}). */\n readonly port?: number;\n /** Directory of user-authored static files served at the root (default `\"public\"`). The SAME\n * option the production build copies and serves, so dev and prod cannot drift. */\n readonly publicDir?: string | false;\n /**\n * Run the client-leak guards on each change (default `true`).\n *\n * `buildClient` is what runs them - server-only code reaching the browser, `node:` builtins in client\n * code - and Bun's dev server does its own bundling, so nothing would run them otherwise. They are\n * security guards; a dev loop that stops enforcing them is how a leak reaches a deploy unnoticed. The\n * pass runs in the background off the hot path, so HMR is never waiting on it, and only reports.\n */\n readonly guardLeaks?: boolean;\n}"
2984
3164
  },
2985
3165
  {
2986
3166
  "name": "devtools",
@@ -3074,7 +3254,7 @@
3074
3254
  "name": "DiscoverRoutesOptions",
3075
3255
  "kind": "interface",
3076
3256
  "package": "@nifrajs/web",
3077
- "signature": "export interface DiscoverRoutesOptions {\n /** Appended (as `?<importQuery>`) to each route's dynamic import — the dev server sets a\n * changing value to bust Bun's module cache so SSR picks up edited route files. */\n readonly importQuery?: string;\n}",
3257
+ "signature": "export interface DiscoverRoutesOptions {\n /** Appended (as `?<importQuery>`) to each route's dynamic import — the dev server sets a\n * changing value to bust Bun's module cache so SSR picks up edited route files. Ignored when\n * {@link load} is supplied, which does its own cache handling. */\n readonly importQuery?: string;\n /**\n * Load a route module by its absolute path, instead of a bare dynamic `import()`.\n *\n * This is what lets a pipeline own SSR resolution. A bare `import()` always resolves through the\n * RUNTIME - Bun - so the Vite dev server ended up serving the client while Bun resolved the server,\n * two resolvers disagreeing about one specifier in a single process. That is the dual-React bug and\n * the reason `resolve.dedupe` never reached SSR: the alias governs Vite, and SSR never asked Vite.\n *\n * Pass `vite.ssrLoadModule` here and the Vite pipeline resolves both halves. Omit it and Bun does,\n * which is correct for the Bun pipeline. Either way one toolchain owns the phase.\n */\n readonly load?: (absolutePath: string) => Promise<unknown>;\n}",
3078
3258
  "doc": "/** Options for {@link discoverRoutes}. */"
3079
3259
  },
3080
3260
  {
@@ -3686,6 +3866,13 @@
3686
3866
  "signature": "export interface ExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n}",
3687
3867
  "doc": "/** A Cloudflare Workers-style execution context (the `fetch` 3rd arg). Structural — only\n * `waitUntil` is used; declared here so `@nifrajs/core` needs no Workers type dependency. */"
3688
3868
  },
3869
+ {
3870
+ "name": "explainBindFailure",
3871
+ "kind": "const",
3872
+ "package": "@nifrajs/web",
3873
+ "signature": "export declare const explainBindFailure: (err: unknown, port: number) => Error;",
3874
+ "doc": "/** Map a bind failure to the port-collision explanation, leaving every other failure as itself. */"
3875
+ },
3689
3876
  {
3690
3877
  "name": "exponentialBackoff",
3691
3878
  "kind": "function",
@@ -3869,7 +4056,7 @@
3869
4056
  "name": "FetchRouteData",
3870
4057
  "kind": "type",
3871
4058
  "package": "@nifrajs/web",
3872
- "signature": "export type FetchRouteData = (path: string, match: RouteMatch, signal?: AbortSignal) => Promise<unknown>;",
4059
+ "signature": "export type FetchRouteData = (path: string, match: RouteMatch, signal?: AbortSignal, navigation?: {\n readonly from: string;\n readonly retain: readonly number[];\n}) => Promise<unknown>;",
3873
4060
  "doc": "/** How a router fetches a route's loader data on navigation. `signal` aborts a superseded fetch\n * (and its deferred stream). */"
3874
4061
  },
3875
4062
  {
@@ -3897,8 +4084,21 @@
3897
4084
  "name": "filePathToPatterns",
3898
4085
  "kind": "function",
3899
4086
  "package": "@nifrajs/web",
3900
- "signature": "export declare function filePathToPatterns(file: string): string[];",
3901
- "doc": "/**\n * Derive **every** nifra router pattern a route file maps to (relative to the routes dir):\n * `index` → the parent path, `[id]` → `:id`, `[...slug]` → `*slug` (catch-all, captures the rest of\n * the path into one param), `(group)` folders are dropped from the URL (organization only), and an\n * optional `[[lang]]` expands the set — once with the segment present (`:lang`) and once absent. A file\n * with no optionals yields exactly one pattern. Throws on an invalid param or a catch-all that isn't\n * the last segment.\n */"
4087
+ "signature": "export declare function filePathToPatterns(file: string): string[];"
4088
+ },
4089
+ {
4090
+ "name": "filePathToRoutes",
4091
+ "kind": "function",
4092
+ "package": "@nifrajs/web",
4093
+ "signature": "export declare function filePathToRoutes(file: string): FileRoutePattern[];",
4094
+ "doc": "/** Expand a route file into its URL patterns, each carrying per-directory segment counts. */"
4095
+ },
4096
+ {
4097
+ "name": "FileRoutePattern",
4098
+ "kind": "interface",
4099
+ "package": "@nifrajs/web",
4100
+ "signature": "export interface FileRoutePattern {\n readonly pattern: string;\n /**\n * URL segments contributed by the first *k* path parts of the file, indexed by *k*.\n *\n * This is what makes a layout's scope derivable without turning layouts into router nodes. A layout\n * lives at a directory; the segments that directory contributes are exactly the prefix of the URL it\n * wraps, so `depths[partsIn(layoutDir)]` is how many leading pattern segments it owns - and the\n * params inside that prefix are the ones it may read.\n *\n * Per EXPANDED pattern, not per file, because the two diverge: `[[lang]]/docs/_layout` owns nothing\n * on `/docs/:slug` and `{lang}` on `/:lang/docs/:slug`. A route group contributes no segment, so\n * `(marketing)/_layout` owns nothing at all.\n */\n readonly depths: readonly number[];\n}",
4101
+ "doc": "/** One URL pattern a route file expands to, plus the scope information layouts need. */"
3902
4102
  },
3903
4103
  {
3904
4104
  "name": "FileStorage",
@@ -3931,7 +4131,7 @@
3931
4131
  "name": "Flags",
3932
4132
  "kind": "interface",
3933
4133
  "package": "@nifrajs/cli",
3934
- "signature": "export interface Flags {\n readonly port: number;\n readonly out: string;\n readonly poll: boolean;\n /** `nifra build --target <t>`: emit a full deploy dir for this target. Defaults to `bun`. */\n readonly target: string;\n /** `nifra build --report`: print a per-chunk size + gzip table after the build. */\n readonly report: boolean;\n}"
4134
+ "signature": "export interface Flags {\n readonly port: number;\n readonly out: string;\n readonly poll: boolean;\n /** `nifra build --target <t>`: emit a full deploy dir for this target. Defaults to `bun`. */\n readonly target: string;\n /** `nifra build --report`: print a per-chunk size + gzip table after the build. */\n readonly report: boolean;\n /** `nifra build --vite`: force the client + server through Vite/Rollup. Without a flag the pipeline is\n * chosen per app (`chooseBuildPipeline`) - Bun unless the app's only transforms are `vitePlugins`. */\n readonly vite: boolean;\n /** `nifra dev --bun`: run the Bun-pipeline dev server (Bun.serve native HMR — including React Fast\n * Refresh with state preserved — and no Vite in the process). Default stays Vite for its plugin\n * ecosystem; `--bun` trades that for one bundler across dev and prod. Refuses CSS-Modules apps.\n *\n * `nifra build --bun`: force the Bun build. Refuses when that would drop the app's `vitePlugins`. */\n readonly bun: boolean;\n}"
3935
4135
  },
3936
4136
  {
3937
4137
  "name": "FontAsset",
@@ -4025,6 +4225,20 @@
4025
4225
  "signature": "export declare function formatManifestDrift(drift: ManifestDrift, manifestPath?: string): string | undefined;",
4026
4226
  "doc": "/**\n * Format a {@link ManifestDrift} as a named, actionable error message, or `undefined` when in sync.\n * Names the exact missing/extra routes + the one fix (regenerate the manifest by re-running the build).\n * `manifestPath` is shown for the dev to locate the stale file. Pure.\n */"
4027
4227
  },
4228
+ {
4229
+ "name": "formatNodeBuiltinLeak",
4230
+ "kind": "function",
4231
+ "package": "@nifrajs/web",
4232
+ "signature": "export declare function formatNodeBuiltinLeak(findings: ReadonlyArray<NodeBuiltinFinding>): string | undefined;",
4233
+ "doc": "/** The build-failing message for `node:` builtins that reached the client bundle. `undefined` ⇒ clean. */"
4234
+ },
4235
+ {
4236
+ "name": "formatServerOnlyLeak",
4237
+ "kind": "function",
4238
+ "package": "@nifrajs/web",
4239
+ "signature": "export declare function formatServerOnlyLeak(findings: ReadonlyArray<ServerOnlyFinding>): string | undefined;",
4240
+ "doc": "/** The build-failing message for `server-only`-marked modules that reached the client. `undefined` ⇒ clean. */"
4241
+ },
4028
4242
  {
4029
4243
  "name": "Formatter",
4030
4244
  "kind": "interface",
@@ -4091,6 +4305,20 @@
4091
4305
  "package": "@nifrajs/core",
4092
4306
  "signature": "export type FrameworkName = typeof FRAMEWORK_NAME;"
4093
4307
  },
4308
+ {
4309
+ "name": "fromBunMetafile",
4310
+ "kind": "function",
4311
+ "package": "@nifrajs/web",
4312
+ "signature": "export declare function fromBunMetafile(meta: BunMetafileLike | undefined): ClientModuleGraph;",
4313
+ "doc": "/**\n * Adapt a `Bun.build` metafile to the neutral graph.\n *\n * A total function: an absent or partial metafile yields an empty graph rather than throwing, because\n * a guard that crashes on an unexpected build shape fails the build for the wrong reason. An empty\n * graph reports no findings, which matches the existing behaviour when the metafile is missing.\n */"
4314
+ },
4315
+ {
4316
+ "name": "fromRollupBundle",
4317
+ "kind": "function",
4318
+ "package": "@nifrajs/web",
4319
+ "signature": "export declare function fromRollupBundle(bundle: RollupBundleLike, importsOf: (moduleId: string) => readonly string[]): ClientModuleGraph;",
4320
+ "doc": "/**\n * Adapt a Rollup/Vite output bundle to the neutral graph — the second producer the seam was built for.\n *\n * The bundle records which modules landed in which chunk (`moduleIds`) but NOT each module's import\n * edges, so those come from `importsOf`, which the caller backs with `this.getModuleInfo(id).importedIds`\n * inside a plugin hook (a test backs it with a plain map). Edges carry only the RESOLVED id — Rollup does\n * not keep the as-written specifier per edge — so `path` is set and `original` is left undefined. Both\n * guards read the resolved `path` as their fallback (a `node:` prefix, the `server-only` basename), so\n * detection is unaffected; only the human-readable chain shows resolved paths instead of as-written ones.\n *\n * Total, like {@link fromBunMetafile}: an empty bundle yields an empty graph (no findings), never a throw\n * that would fail a build for the wrong reason.\n */"
4321
+ },
4094
4322
  {
4095
4323
  "name": "fromTypeBox",
4096
4324
  "kind": "function",
@@ -4130,7 +4358,7 @@
4130
4358
  "name": "GenerateClientEntryOptions",
4131
4359
  "kind": "interface",
4132
4360
  "package": "@nifrajs/web",
4133
- "signature": "export interface GenerateClientEntryOptions {\n /** Module specifier for the adapter's client runtime (exports `mountRouter`), e.g.\n * `\"@nifrajs/web-solid/client\"`. */\n readonly clientModule: string;\n /** Turn a route/layout source file (relative to the routes dir) into an import specifier. */\n readonly resolve: (file: string) => string;\n}"
4361
+ "signature": "export interface GenerateClientEntryOptions {\n /**\n * Module specifier for the adapter's client runtime, e.g. `\"@nifrajs/web-solid/client\"`.\n *\n * **Contract:** the module MUST export `mountRouter({ router, routes, container })`. The generated\n * bootstrap imports the specifier and calls it — so a self-executing entry that mounts on import\n * builds cleanly and then does nothing, which is the trap this contract exists to name. The type is\n * a bare `string` because a specifier is resolved by the bundler, not the type system, so the\n * requirement is enforced by the bootstrap instead: it throws immediately, naming this module and\n * the missing export, rather than failing later as `mountRouter is not a function`.\n */\n readonly clientModule: string;\n /** Turn a route/layout source file (relative to the routes dir) into an import specifier. */\n readonly resolve: (file: string) => string;\n}"
4134
4362
  },
4135
4363
  {
4136
4364
  "name": "generateLlmsTxt",
@@ -4162,7 +4390,7 @@
4162
4390
  "name": "generateServerEntry",
4163
4391
  "kind": "function",
4164
4392
  "package": "@nifrajs/web",
4165
- "signature": "export declare function generateServerEntry(options: {\n readonly target: BuildTarget;\n /** Import specifier for the module exporting `adapter` (e.g. `\"../framework.ts\"`). */\n readonly adapterImport: string;\n /** Import specifier for the module exporting `backend`, or `undefined` for a frontend-only app. */\n readonly backendImport?: string;\n /** Document `<title>` passed to `createWebApp`. */\n readonly title?: string;\n}): string;",
4393
+ "signature": "export declare function generateServerEntry(options: {\n readonly target: BuildTarget;\n /** Import specifier for the module exporting `adapter` (e.g. `\"../framework.ts\"`). */\n readonly adapterImport: string;\n /** Import specifier for the module exporting `backend`, or `undefined` for a frontend-only app. */\n readonly backendImport?: string;\n /** Document `<title>` passed to `createWebApp`. */\n readonly title?: string;\n /** Encoded root-relative public file paths copied into the deploy directory. */\n readonly publicFiles?: readonly string[];\n}): string;",
4166
4394
  "doc": "/**\n * Codegen the per-target **server entry** module (source text) for `buildServer` to bundle. It imports\n * the app's `adapter` (from `framework.ts`), the optional `backend` (from `backend.ts`), and the\n * generated `{ manifest, clientEntry }` (from `./server-manifest`), builds `createWebApp`, then wires\n * the right host:\n * - `cf-pages` / `vercel`: `export default` the fetch handler (the platform serves /assets/* itself).\n * - `deno`: same fetch-handler default, plus `Deno.serve` self-host when run directly.\n * - `bun` / `node`: a self-hosting server that ALSO serves the client bundle from disk (those\n * runtimes have a filesystem; the static `/assets/*` sit next to the entry).\n * `adapterImport`/`backendImport` are the specifiers the entry uses (relative to where it's written) —\n * `buildServer` writes the entry next to `serverEntry`, so they're resolved from there. Pure (string in,\n * string out) so the generation is unit-testable without a real build.\n */"
4167
4395
  },
4168
4396
  {
@@ -4170,7 +4398,7 @@
4170
4398
  "kind": "function",
4171
4399
  "package": "@nifrajs/web",
4172
4400
  "signature": "export declare function generateServerManifest(manifest: Manifest, options: GenerateServerManifestOptions): string;",
4173
- "doc": "/**\n * Codegen: emit a **server manifest** module (as source) for disk-less edge runtimes (Cloudflare\n * Workers, …) — and, with a `target`, any portable server bundle. `discoverRoutes` scans `node:fs`\n * and dynamic-imports each route by a *runtime* path — neither exists on workerd. This instead emits\n * **statically-analyzable** imports of every route/layout/`_error`/`_404` (so the bundler includes them) and\n * rebuilds the manifest with `buildManifest` — the SAME pure logic `discoverRoutes` feeds, so patterns\n * + layout chains are identical. Eager (`import * as`) by default; `lazy` emits `() => import(...)` so\n * a code-splitting bundler chunks per route. The emitted module exports `manifest` (consumed by\n * `createWebApp`, unchanged) + `clientEntry` (baked). Write it to a file and bundle it into the worker\n * entry (see `buildServer` in `@nifrajs/web/build`).\n */"
4401
+ "doc": "/**\n * Codegen: emit a **server manifest** module (as source) for disk-less edge runtimes (Cloudflare\n * Workers, …) — and, with a `target`, any portable server bundle. `discoverRoutes` scans `node:fs`\n * and dynamic-imports each route by a *runtime* path — neither exists on workerd. This instead emits\n * **statically-analyzable** imports of every route/layout/`_error`/terminal status page (so the bundler includes them) and\n * rebuilds the manifest with `buildManifest` — the SAME pure logic `discoverRoutes` feeds, so patterns\n * + layout chains are identical. Eager (`import * as`) by default; `lazy` emits `() => import(...)` so\n * a code-splitting bundler chunks per route. The emitted module exports `manifest` (consumed by\n * `createWebApp`, unchanged) + `clientEntry` (baked). Write it to a file and bundle it into the worker\n * entry (see `buildServer` in `@nifrajs/web/build`).\n */"
4174
4402
  },
4175
4403
  {
4176
4404
  "name": "GenerateServerManifestOptions",
@@ -4245,6 +4473,13 @@
4245
4473
  "signature": "export declare function githubDeployWorkflow(target: string, appName: string): string;",
4246
4474
  "doc": "/**\n * Build a GitHub Actions workflow that builds on every push/PR and deploys `target` on a push to `main`.\n * Reuses the canonical `bun run build` (the deploy preset repoints it), then runs the target's official\n * deploy mechanism. Exported for unit tests. Throws on an unknown target.\n */"
4247
4475
  },
4476
+ {
4477
+ "name": "gone",
4478
+ "kind": "function",
4479
+ "package": "@nifrajs/web",
4480
+ "signature": "export declare function gone(options?: StatusPageOptions): never;",
4481
+ "doc": "/**\n * Render a terminal page at status **410 Gone**. `throw` it from a loader for a record that existed and\n * was deliberately removed - a withdrawn listing, a deleted post.\n *\n * 410 is not a pedantic 404: it tells a crawler to **drop** the URL rather than re-fetch it for weeks\n * on the assumption the 404 was transient. Uses `_410.tsx` if the app has one, otherwise `_404`.\n */"
4482
+ },
4248
4483
  {
4249
4484
  "name": "GoogleFontOptions",
4250
4485
  "kind": "interface",
@@ -4259,6 +4494,25 @@
4259
4494
  "signature": "export declare function googleFontsCssUrl(options: GoogleFontOptions): string;",
4260
4495
  "doc": "/** Build the Google Fonts CSS2 request URL. Pure + fully validated, so it's safe to feed a dynamic\n * family/weights/text. Exported for advanced callers who fetch + parse the stylesheet themselves. */"
4261
4496
  },
4497
+ {
4498
+ "name": "GraphChunk",
4499
+ "kind": "interface",
4500
+ "package": "@nifrajs/web",
4501
+ "signature": "export interface GraphChunk {\n /** Source module this chunk is the entry for, if it is one. */\n readonly entryPoint?: string;\n /** Module ids that landed in this chunk. */\n readonly modules: readonly string[];\n}"
4502
+ },
4503
+ {
4504
+ "name": "GraphImport",
4505
+ "kind": "interface",
4506
+ "package": "@nifrajs/web",
4507
+ "signature": "export interface GraphImport {\n /** The resolved path, when the bundler resolved it. */\n readonly path?: string;\n /** The specifier as written in source - the only form that survives an unresolved import. */\n readonly original?: string;\n}",
4508
+ "doc": "/** One import edge, as the bundler recorded it. */"
4509
+ },
4510
+ {
4511
+ "name": "GraphModule",
4512
+ "kind": "interface",
4513
+ "package": "@nifrajs/web",
4514
+ "signature": "export interface GraphModule {\n readonly imports: readonly GraphImport[];\n}"
4515
+ },
4262
4516
  {
4263
4517
  "name": "GuardOptions",
4264
4518
  "kind": "interface",
@@ -4858,6 +5112,13 @@
4858
5112
  "package": "@nifrajs/cli",
4859
5113
  "signature": "export interface InitAgentsResult {\n readonly cwd: string;\n readonly files: readonly InitAgentsFileResult[];\n}"
4860
5114
  },
5115
+ {
5116
+ "name": "injectStyles",
5117
+ "kind": "function",
5118
+ "package": "@nifrajs/web",
5119
+ "signature": "export declare function injectStyles(html: string, styles: readonly string[]): string;",
5120
+ "doc": "/**\n * Inject Bun's stylesheet links into an SSR'd document.\n *\n * Prefers `</head>`; falls back to prepending when a document has no head (a bare fragment from a custom\n * renderer). Never appends blindly at the end - a stylesheet after `</body>` still applies but arrives\n * after first paint, so the page flashes unstyled and dev stops resembling production.\n */"
5121
+ },
4861
5122
  {
4862
5123
  "name": "inProcessClient",
4863
5124
  "kind": "function",
@@ -4965,6 +5226,13 @@
4965
5226
  "package": "@nifrajs/middleware",
4966
5227
  "signature": "export interface IpRestrictionOptions {\n readonly allow?: readonly IpMatcher[];\n readonly deny?: readonly IpMatcher[];\n /** Preferred extraction hook when the adapter/app knows the peer address. */\n readonly clientIp?: (request: Request) => MaybePromise<string | null | undefined>;\n /** Trusted proxy count for `X-Forwarded-For` extraction. Default: 0, so XFF is ignored. */\n readonly trustedProxies?: number;\n /** Exact trusted single-IP header, e.g. an infra-set `x-real-ip`. Not used unless configured. */\n readonly header?: string;\n readonly error?: string;\n}"
4967
5228
  },
5229
+ {
5230
+ "name": "isAddressInUse",
5231
+ "kind": "const",
5232
+ "package": "@nifrajs/web",
5233
+ "signature": "export declare const isAddressInUse: (err: unknown) => boolean;",
5234
+ "doc": "/** True when an unknown thrown value carries the \"address already in use\" errno. */"
5235
+ },
4968
5236
  {
4969
5237
  "name": "isAllowedFontUrl",
4970
5238
  "kind": "function",
@@ -5107,6 +5375,13 @@
5107
5375
  "package": "@nifrajs/web",
5108
5376
  "signature": "export interface ISROptions {\n readonly store: CacheStore;\n /** Default freshness window (**seconds**) for a cached page; older ⇒ stale (served, regenerated\n * behind). A route overrides it per-page via `export const revalidate` (surfaced as the\n * `x-nifra-isr-revalidate` response header). */\n readonly revalidate: number;\n /** Monotonic clock (ms) — injected for testability; production passes `() => Date.now()`. */\n readonly now: () => number;\n /** Cache key for a request. Default: `origin + pathname + search` so host-routed apps do not share\n * entries across tenants. Return `null` to bypass the cache for this request (it goes straight to the\n * app, uncached). */\n readonly key?: (req: Request) => string | null;\n /** Draft/preview secret (the same one given to `createWebApp({ draftSecret })` + `enableDraft`). When\n * set, a request carrying a valid signed draft cookie **bypasses the cache** — editors always render\n * fresh, and a draft render is never written to the store (it can't poison the public cache). */\n readonly draftSecret?: string;\n}"
5109
5377
  },
5378
+ {
5379
+ "name": "isRouteDataEnvelope",
5380
+ "kind": "function",
5381
+ "package": "@nifrajs/web",
5382
+ "signature": "export declare function isRouteDataEnvelope(value: unknown): value is RouteDataEnvelope;",
5383
+ "doc": "/** Recognise the envelope without mistaking a plain loader object that happens to have a `data` key. */"
5384
+ },
5110
5385
  {
5111
5386
  "name": "ISRPlatform",
5112
5387
  "kind": "interface",
@@ -5387,6 +5662,13 @@
5387
5662
  "package": "@nifrajs/middleware",
5388
5663
  "signature": "export interface LanguageOptions<L extends readonly string[]> {\n readonly supported: L;\n readonly defaultLanguage: L[number];\n /** Emit `Content-Language`. Default `true`. */\n readonly header?: boolean;\n}"
5389
5664
  },
5665
+ {
5666
+ "name": "LAYOUT_DATA_GLOBAL",
5667
+ "kind": "const",
5668
+ "package": "@nifrajs/web",
5669
+ "signature": "export declare const LAYOUT_DATA_GLOBAL = \"__NIFRA_LAYOUT_DATA__\";",
5670
+ "doc": "/** Per-layout loader data for hydration. Emitted ONLY when some layout in the chain has a loader, so\n * a page-only app's HTML is byte-identical to before layout loaders existed. */"
5671
+ },
5390
5672
  {
5391
5673
  "name": "LayoutEntry",
5392
5674
  "kind": "interface",
@@ -5394,6 +5676,13 @@
5394
5676
  "signature": "export interface LayoutEntry {\n readonly file: string;\n readonly load: () => Promise<{\n default: unknown;\n meta?: MetaInput;\n }>;\n}",
5395
5677
  "doc": "/** A layout (or `_404`/`_error`) entry: its source file (for client codegen) + a lazy loader.\n *\n * A `_layout.tsx` may export `meta` (static {@link Meta} or a function of the loader data + params,\n * same shape as a route's) to contribute sitewide `<head>` tags — `hreflang`/`preconnect`/etc. that\n * belong on every page under the layout. The layout chain's heads merge with the page's: arrays\n * (`meta`/`link`) concatenate outermost→innermost→page; scalars (`title`) are nearest-wins (the page\n * overrides an inner layout, which overrides an outer one). See `mergeHeads` in `@nifrajs/web`. */"
5396
5678
  },
5679
+ {
5680
+ "name": "LeakGuardPlugin",
5681
+ "kind": "interface",
5682
+ "package": "@nifrajs/web",
5683
+ "signature": "export interface LeakGuardPlugin {\n readonly name: string;\n /**\n * The finding that failed the build, or `undefined` when the bundle was clean.\n *\n * Recorded as well as thrown because the throw does not reliably survive. A plugin error is handed to\n * the bundler, which builds its own error to report it, and on some Bun + rolldown combinations that\n * construction itself fails (`Error.captureStackTrace` rejecting a non-Error) - so what surfaces is an\n * internal complaint naming neither the leaked module nor its import chain. A caller that owns the\n * build can read this afterwards and raise the real message from its own code, where nothing can\n * rewrite it. A leak is the one failure that must stay legible: the whole point is naming what\n * reached the browser.\n */\n leak?: string;\n generateBundle(this: RollupPluginContext, options: unknown, bundle: RollupBundleLike): void;\n}",
5684
+ "doc": "/** The minimal Rollup plugin shape this returns — `generateBundle` bound to the plugin context. */"
5685
+ },
5397
5686
  {
5398
5687
  "name": "LedgerSink",
5399
5688
  "kind": "type",
@@ -5422,6 +5711,20 @@
5422
5711
  "signature": "export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\"> {\n /** Same-origin destination path (e.g. `/users/7?tab=posts`). Rendered as the `<a href>`. */\n readonly to: string;\n /** Replace the current history entry instead of pushing. */\n readonly replace?: boolean;\n}",
5423
5712
  "doc": "/** {@link Link} props: every `<a>` attribute except `href` (set from `to`), plus `to` + `replace`. */"
5424
5713
  },
5714
+ {
5715
+ "name": "listenOrExplain",
5716
+ "kind": "function",
5717
+ "package": "@nifrajs/web",
5718
+ "signature": "export declare function listenOrExplain(server: ListenTarget, port: number): Promise<void>;",
5719
+ "doc": "/**\n * `listen`, but a bind failure becomes a readable nifra error instead of Node's raw internal throw.\n *\n * Without an `error` listener attached, Node throws from deep inside `node:events`, the new process dies\n * in the background, and nothing connects that death to the stale page in front of you.\n */"
5720
+ },
5721
+ {
5722
+ "name": "ListenTarget",
5723
+ "kind": "interface",
5724
+ "package": "@nifrajs/web",
5725
+ "signature": "export interface ListenTarget {\n listen(port: number, cb: () => void): void;\n once(event: \"error\", cb: (err: unknown) => void): void;\n removeListener(event: \"error\", cb: (err: unknown) => void): void;\n}",
5726
+ "doc": "/** The bits of a Node server {@link listenOrExplain} touches — structural, so a test can fake it. */"
5727
+ },
5425
5728
  {
5426
5729
  "name": "ListOptions",
5427
5730
  "kind": "interface",
@@ -5478,7 +5781,7 @@
5478
5781
  "name": "LoadedApp",
5479
5782
  "kind": "interface",
5480
5783
  "package": "@nifrajs/cli",
5481
- "signature": "export interface LoadedApp {\n readonly cwd: string;\n readonly routesDir: string;\n /** Build output dir (also where `nifra start` reads `manifest.json` + serves `/assets/*`). */\n readonly outDir: string;\n readonly framework: NifraFramework;\n /** The `backend` export from `backend.ts`, or `undefined` if there's no `backend.ts`. */\n readonly backend: unknown;\n}"
5784
+ "signature": "export interface LoadedApp {\n readonly cwd: string;\n readonly routesDir: string;\n /** Build output dir (also where `nifra start` reads `manifest.json` + serves `/assets/*`). */\n readonly outDir: string;\n readonly framework: NifraFramework;\n /** Plugin thunks resolved exactly once during app loading and reused by every command phase. */\n readonly resolvedPlugins: ResolvedPlugins;\n /** The `backend` export from `backend.ts`, or `undefined` if there's no `backend.ts`. */\n readonly backend: unknown;\n}"
5482
5785
  },
5483
5786
  {
5484
5787
  "name": "Loader",
@@ -5620,7 +5923,7 @@
5620
5923
  "name": "Manifest",
5621
5924
  "kind": "interface",
5622
5925
  "package": "@nifrajs/web",
5623
- "signature": "export interface Manifest {\n readonly routes: readonly RouteEntry[];\n readonly layouts: Readonly<Record<string, LayoutEntry>>;\n /** Per-segment `_error` boundary components, keyed by id (`_error`, `a/_error`, …). Always set by\n * `buildManifest` (optional only so hand-built test manifests may omit it). */\n readonly errors?: Readonly<Record<string, LayoutEntry>>;\n readonly notFound?: LayoutEntry;\n}",
5926
+ "signature": "export interface Manifest {\n readonly routes: readonly RouteEntry[];\n readonly layouts: Readonly<Record<string, LayoutEntry>>;\n /** Per-segment `_error` boundary components, keyed by id (`_error`, `a/_error`, …). Always set by\n * `buildManifest` (optional only so hand-built test manifests may omit it). */\n readonly errors?: Readonly<Record<string, LayoutEntry>>;\n readonly notFound?: LayoutEntry;\n /** Per-status terminal pages from `_<status>.tsx` at the routes root (`_410`, `_451`, …), keyed by\n * the status as a string. Rendered by a loader's `gone()` / `statusPage(n)`; a status with no page\n * falls back to `_404`, and then to plain text. `_404` itself stays on {@link notFound} - it is\n * reached by unmatched paths too, which these are not. */\n readonly statusPages?: Readonly<Record<string, LayoutEntry>>;\n}",
5624
5927
  "doc": "/** The full route manifest. */"
5625
5928
  },
5626
5929
  {
@@ -6119,7 +6422,7 @@
6119
6422
  "name": "Meta",
6120
6423
  "kind": "interface",
6121
6424
  "package": "@nifrajs/web",
6122
- "signature": "export interface Meta {\n readonly title?: string;\n readonly meta?: ReadonlyArray<Record<string, string>>;\n readonly link?: readonly LinkDescriptor[];\n /** Inert head `<script>`s (JSON-LD structured data, etc.). See {@link ScriptDescriptor}. */\n readonly script?: readonly ScriptDescriptor[];\n}",
6425
+ "signature": "export interface Meta {\n readonly title?: string;\n readonly meta?: ReadonlyArray<Record<string, string>>;\n readonly link?: readonly LinkDescriptor[];\n /** Inert head `<script>`s (JSON-LD structured data, etc.). See {@link ScriptDescriptor}. */\n readonly script?: readonly ScriptDescriptor[];\n /**\n * The document language — `<html lang=\"...\">`. Nearest-wins like `title`, so a localized route can\n * override a layout's default. Defaults to `\"en\"` when no head in the chain sets it.\n *\n * This is the ONLY way to set it: the shell's `<html>` is framework-owned, so a multilingual app\n * otherwise serves every URL as `lang=\"en\"` — which tells a screen reader to pronounce every locale\n * with an English voice, and suppresses the browser's translation offer.\n */\n readonly lang?: string;\n /**\n * The document writing direction — `<html dir=\"...\">`. Nearest-wins like `title`. Omitted from the\n * shell when unset, which is HTML's `ltr` default, so an LTR app's output is byte-identical.\n *\n * Required for Arabic/Hebrew/Urdu/Persian: without it the browser lays the page out left-to-right\n * and mis-orders trailing punctuation, so the text renders wrong rather than merely unstyled.\n */\n readonly dir?: \"ltr\" | \"rtl\" | \"auto\";\n}",
6123
6426
  "doc": "/**\n * The document head a route contributes — title + `<meta>`/`<link>`/`<script>` tag sets. Returned by a\n * route/layout `meta` (statically, or from a {@link MetaArgs} function). Every value is serialized into\n * managed (`data-nifra`) head tags: attribute *names* are shape-validated and *values* HTML-escaped at\n * render (see `tagAttrs`/`headTags` in `@nifrajs/web`), and `script[].content` is breakout-escaped — so\n * loader-derived strings (LLM-authored `og:*`, user content) are XSS-safe in the head by construction.\n * Layout-chain heads merge with the page's via `mergeHeads` (arrays concat outermost→page; `title` is\n * nearest-wins). Build `og:*`/`twitter:*` with `openGraph(...)`, canonical with `canonical(...)`, and\n * JSON-LD with `jsonLd(...)` (all from `@nifrajs/web`) rather than hand-writing the records.\n */"
6124
6427
  },
6125
6428
  {
@@ -6196,6 +6499,20 @@
6196
6499
  "signature": "export declare const MIN_DIGEST_KEY_BYTES = 16;",
6197
6500
  "doc": "/** Minimum digest key material. A short key would make the keyed digest brute-forceable. */"
6198
6501
  },
6502
+ {
6503
+ "name": "MixedPart",
6504
+ "kind": "type",
6505
+ "package": "@nifrajs/core",
6506
+ "signature": "export type MixedPart = {\n readonly t: \"lit\";\n readonly v: string;\n} | {\n readonly t: \"param\";\n readonly name: string;\n};",
6507
+ "doc": "/** One piece of a {@link RoutePatternSegment} of kind `mixed`, in left-to-right order. */"
6508
+ },
6509
+ {
6510
+ "name": "mixedSegmentSource",
6511
+ "kind": "function",
6512
+ "package": "@nifrajs/core",
6513
+ "signature": "export declare function mixedSegmentSource(parts: readonly MixedPart[]): string;",
6514
+ "doc": "/** The regex source matching one segment's worth of a mixed pattern, with a capture per parameter. */"
6515
+ },
6199
6516
  {
6200
6517
  "name": "MockableApp",
6201
6518
  "kind": "interface",
@@ -6341,6 +6658,13 @@
6341
6658
  "signature": "export declare function namedCombine(name: string, ...items: readonly Composable[]): NifraPlugin;",
6342
6659
  "doc": "/** Compose middleware/plugins into one idempotent named bundle. */"
6343
6660
  },
6661
+ {
6662
+ "name": "NAV_FROM_HEADER",
6663
+ "kind": "const",
6664
+ "package": "@nifrajs/web",
6665
+ "signature": "export declare const NAV_FROM_HEADER = \"x-nifra-from\";",
6666
+ "doc": "/**\n * The path a client navigation is coming FROM, sent on the data-mode GET.\n *\n * Lets the SERVER decide which layout loaders to skip: it already knows each layout's scoped params\n * (derived at build time), so given both paths it can tell whether a layout's own prefix changed.\n * Doing it server-side means the client never needs the scope table, and the decision lives next to\n * the data it is about.\n *\n * Sent only when the client actually holds layout data to retain, so a request without it is simply\n * the full-chain case.\n */"
6667
+ },
6344
6668
  {
6345
6669
  "name": "Navigate",
6346
6670
  "kind": "function",
@@ -6455,7 +6779,7 @@
6455
6779
  "name": "NifraFramework",
6456
6780
  "kind": "interface",
6457
6781
  "package": "@nifrajs/cli",
6458
- "signature": "export interface NifraFramework {\n /** The render adapter, e.g. `reactAdapter` from `@nifrajs/web-react`. */\n readonly adapter: unknown;\n /** The client runtime module providing `mountRouter`, e.g. `\"@nifrajs/web-react/client\"`. */\n readonly clientModule: string;\n /** Vite plugins for `nifra dev`'s HMR, e.g. `[react()]` / `() => import(\"@vitejs/plugin-vue\")…`. */\n readonly vitePlugins?: PluginsField;\n /** Bun build plugins that compile routes for the CLIENT bundle (`nifra build`), e.g.\n * `[vueBunPlugin(\"dom\")]`. React/Preact JSX is Bun-native → none. */\n readonly clientPlugins?: PluginsField;\n /** Bun runtime plugins that compile routes for SSR — `nifra dev` (Bun-native route import) and\n * `nifra start` register them via `Bun.plugin`, e.g. `[vueBunPlugin(\"ssr\")]`. React/Preact → none. */\n readonly serverPlugins?: PluginsField;\n /** Extra Bun.build / Vite resolve conditions, e.g. Solid's `[\"solid\"]`. */\n readonly conditions?: readonly string[];\n /** Compile-time `define` replacements, e.g. Vue's `__VUE_*` flags. */\n readonly define?: Readonly<Record<string, string>>;\n}",
6782
+ "signature": "export interface NifraFramework {\n /** The render adapter, e.g. `reactAdapter` from `@nifrajs/web-react`. */\n readonly adapter: unknown;\n /** The client runtime module providing `mountRouter`, e.g. `\"@nifrajs/web-react/client\"`. */\n readonly clientModule: string;\n /** Vite plugins for `nifra dev`'s HMR, e.g. `[react()]` / `() => import(\"@vitejs/plugin-vue\")…`. */\n readonly vitePlugins?: PluginsField;\n /** Bun build plugins that compile routes for the CLIENT bundle (`nifra build`), e.g.\n * `[vueBunPlugin(\"dom\")]`. React/Preact JSX is Bun-native → none. */\n readonly clientPlugins?: PluginsField;\n /** Bun runtime plugins that compile routes for SSR — `nifra dev` (Bun-native route import) and\n * `nifra start` register them via `Bun.plugin`, e.g. `[vueBunPlugin(\"ssr\")]`. React/Preact → none. */\n readonly serverPlugins?: PluginsField;\n /** Extra Bun.build / Vite resolve conditions, e.g. Solid's `[\"solid\"]`. */\n readonly conditions?: readonly string[];\n /** Compile-time `define` replacements, e.g. Vue's `__VUE_*` flags. */\n readonly define?: Readonly<Record<string, string>>;\n /** Static files directory. Defaults to `<app>/public`; `false` disables it. */\n readonly publicDir?: string | false;\n /** Client-visible environment prefix (default `\"PUBLIC_\"`; empty disables exposure). */\n readonly publicEnvPrefix?: string;\n}",
6459
6783
  "doc": "/**\n * The CLI's framework wiring — exported from `nifra.config.ts` (or `framework.ts` as a fallback).\n * `create-nifra` generates it. Only `adapter` + `clientModule` are required; the plugin/condition\n * fields are framework-specific extras (Vue/Svelte/Solid need them; React/Preact don't).\n */"
6460
6784
  },
6461
6785
  {
@@ -6621,6 +6945,13 @@
6621
6945
  "package": "@nifrajs/core",
6622
6946
  "signature": "export declare function normalizeRouteCapabilities(values: readonly string[] | undefined): readonly string[];"
6623
6947
  },
6948
+ {
6949
+ "name": "notFound",
6950
+ "kind": "function",
6951
+ "package": "@nifrajs/web",
6952
+ "signature": "export declare function notFound(options?: StatusPageOptions): never;",
6953
+ "doc": "/**\n * Render the nearest `_404` page at status **404**. `throw` it from a loader when the record does not\n * exist.\n *\n * This is the fix for the soft 404: a matched route whose loader finds nothing has otherwise no way to\n * answer 404, so the path of least resistance is to return empty data and render \"not found\" inside a\n * **200**. That looks correct in a browser and is invisible in review, which is why it ships and stays\n * shipped - while search engines penalise it and keep the dead URL indexed.\n *\n * Returns `never`: these throw, so a loader narrows without a redundant `return`, and the type states\n * the thing the \"loaders `throw` redirect, actions `return` it\" rule already trips people on.\n */"
6954
+ },
6624
6955
  {
6625
6956
  "name": "number",
6626
6957
  "kind": "function",
@@ -6946,6 +7277,20 @@
6946
7277
  "signature": "export declare function parseDeadlineHeader(headers: Headers): DeadlineHeaderResult;",
6947
7278
  "doc": "/** Parse the canonical deadline header without trusting or clamping it. */"
6948
7279
  },
7280
+ {
7281
+ "name": "parseDevEntry",
7282
+ "kind": "function",
7283
+ "package": "@nifrajs/web",
7284
+ "signature": "export declare function parseDevEntry(html: string): DevEntryMatch | undefined;",
7285
+ "doc": "/**\n * Find the bundled client-entry URL in a page Bun's dev server rendered. Pure - the whole format\n * dependency lives in this function, which is what makes it testable against captured real output.\n *\n * Returns `undefined` rather than throwing so the caller can attach the context a useful error needs\n * (which URL was probed, what the server returned).\n */"
7286
+ },
7287
+ {
7288
+ "name": "parseDevStyles",
7289
+ "kind": "function",
7290
+ "package": "@nifrajs/web",
7291
+ "signature": "export declare function parseDevStyles(html: string): string[];",
7292
+ "doc": "/** Every stylesheet URL linked from a document, in source order. */"
7293
+ },
6949
7294
  {
6950
7295
  "name": "ParsedFontFace",
6951
7296
  "kind": "interface",
@@ -7076,6 +7421,32 @@
7076
7421
  "signature": "export declare function pinSweepText(text: string, rules: readonly {\n match: string;\n to: string;\n}[]): {\n text: string;\n changes: Array<Omit<PinChange, \"file\">>;\n};",
7077
7422
  "doc": "/**\n * Apply pin rules to one package.json's TEXT (format-preserving — targeted string replaces, never a\n * JSON re-serialize that would reorder keys or drop comments-as-formatting). Returns the new text and\n * the changes made.\n */"
7078
7423
  },
7424
+ {
7425
+ "name": "PipelineDecision",
7426
+ "kind": "interface",
7427
+ "package": "@nifrajs/cli",
7428
+ "signature": "export interface PipelineDecision {\n readonly pipeline: BuildPipeline;\n /**\n * Why this pipeline, for the `nifra build` log line - so an auto-selected Vite build never looks\n * like the user got the default. `undefined` when the choice was the plain default or was forced.\n */\n readonly reason?: string;\n}"
7429
+ },
7430
+ {
7431
+ "name": "PipelineMismatch",
7432
+ "kind": "interface",
7433
+ "package": "@nifrajs/cli",
7434
+ "signature": "export interface PipelineMismatch {\n readonly slot: PipelineSlot;\n /** The plugin's `name`, or a positional label when it has none. */\n readonly plugin: string;\n /** The pipeline the slot feeds. */\n readonly expected: \"vite\" | \"bun\";\n /** The pipeline the plugin's hooks say it belongs to. */\n readonly actual: \"vite\" | \"bun\";\n readonly fix: string;\n}"
7435
+ },
7436
+ {
7437
+ "name": "PipelineSlot",
7438
+ "kind": "type",
7439
+ "package": "@nifrajs/cli",
7440
+ "signature": "export type PipelineSlot = \"vitePlugins\" | \"clientPlugins\" | \"serverPlugins\";",
7441
+ "doc": "/** Which pipeline a slot belongs to. */"
7442
+ },
7443
+ {
7444
+ "name": "PipelineSlots",
7445
+ "kind": "interface",
7446
+ "package": "@nifrajs/cli",
7447
+ "signature": "export interface PipelineSlots {\n readonly vitePlugins?: readonly unknown[];\n readonly clientPlugins?: readonly unknown[];\n readonly serverPlugins?: readonly unknown[];\n}",
7448
+ "doc": "/** The plugin slots a decision reads, already resolved. */"
7449
+ },
7079
7450
  {
7080
7451
  "name": "pipeWebBodyToNode",
7081
7452
  "kind": "function",
@@ -7124,6 +7495,13 @@
7124
7495
  "signature": "export interface PortBlock {\n readonly feature: FeatureId;\n readonly verdict: Extract<Verdict, \"unsupported\" | \"caveat\">;\n readonly reason: string;\n readonly evidence: readonly string[];\n}",
7125
7496
  "doc": "/** One blocking finding: a detected feature unsupported on the resolved target, with its fix. */"
7126
7497
  },
7498
+ {
7499
+ "name": "portInUseMessage",
7500
+ "kind": "function",
7501
+ "package": "@nifrajs/web",
7502
+ "signature": "export declare function portInUseMessage(port: number): string;",
7503
+ "doc": "/** The `EADDRINUSE` explanation. Exported so tests pin the exact text a user will read. */"
7504
+ },
7127
7505
  {
7128
7506
  "name": "PortJson",
7129
7507
  "kind": "interface",
@@ -7336,6 +7714,20 @@
7336
7714
  "package": "@nifrajs/middleware",
7337
7715
  "signature": "export interface PrettyJsonOptions {\n /** JSON indentation spaces. Default `2`. */\n readonly spaces?: number;\n /** Maximum response bytes to inspect. Default `1_000_000`; larger responses pass through. */\n readonly maxBytes?: number;\n /** Append a final newline. Default `true`. */\n readonly newline?: boolean;\n /** Optional query toggle. When set, pretty printing runs only when the parameter is present. */\n readonly query?: string | false;\n /** Only pretty-print matching requests. Default `true`. */\n readonly enabled?: boolean | ((request: Request) => boolean);\n}"
7338
7716
  },
7717
+ {
7718
+ "name": "previewEndpoint",
7719
+ "kind": "function",
7720
+ "package": "@nifrajs/web",
7721
+ "signature": "export declare function previewEndpoint(options: PreviewEndpointOptions): (request: Request) => Promise<Response>;",
7722
+ "doc": "/**\n * A **preview / draft-mode entry point** — a `fetch` handler that checks a preview token, turns draft\n * mode on, and redirects the editor to the page they wanted. `GET` with `?token=<secret>&to=/some/path`;\n * mount it on a nifra route, e.g. `app.get(\"/api/preview\", (c) => handler(c.req))`.\n *\n * This is the link-borne sibling of `revalidateEndpoint`. It exists because the alternative — telling\n * you to gate the route yourself — means hand-rolling two checks that are easy to get subtly wrong and\n * that fail silently when you do: the token compare must not exit early on the first wrong character,\n * and the `?to=` destination must not be allowed to point off-site. Both are handled here.\n *\n * A CMS \"Preview\" button is a plain link, so the token has to ride the query string. That has a cost\n * no endpoint can remove — the secret lands in server logs, the `Referer` header, and browser history.\n * Use a preview token minted for that purpose, rotate it, and never reuse a token that grants anything\n * beyond draft mode.\n *\n * Wrong or missing token → `401`; an off-site `to` → `400`; success → `302` with the signed cookie and\n * `Cache-Control: no-store`, so no shared cache can ever replay one editor's draft session to a visitor.\n */"
7723
+ },
7724
+ {
7725
+ "name": "PreviewEndpointOptions",
7726
+ "kind": "interface",
7727
+ "package": "@nifrajs/web",
7728
+ "signature": "export interface PreviewEndpointOptions {\n /** Shared secret the preview link must carry. Compared in **constant time**. */\n readonly secret: string;\n /** Secret that **signs** the draft cookie — the same one passed to `createWebApp({ draftSecret })`\n * and `withISR({ draftSecret })`. Keep it distinct from {@link secret}: that one travels in URLs\n * (logs, `Referer`, browser history), this one never leaves the server. */\n readonly draftSecret: string;\n /** Query parameter carrying the token. Default `\"token\"`. */\n readonly tokenParam?: string;\n /** Query parameter carrying the destination. Default `\"to\"`. */\n readonly redirectParam?: string;\n /** Where to send the editor when the destination parameter is absent. Default `\"/\"`. Must be\n * site-relative; a non-relative value throws at construction rather than at request time. */\n readonly fallbackPath?: string;\n /** Cookie lifetime/path/secure overrides, exactly as {@link enableDraft} takes them. */\n readonly cookie?: EnableDraftOptions;\n}",
7729
+ "doc": "/** Config for {@link previewEndpoint}. */"
7730
+ },
7339
7731
  {
7340
7732
  "name": "Principal",
7341
7733
  "kind": "interface",
@@ -7444,6 +7836,13 @@
7444
7836
  "signature": "export declare const PROTOCOL_VERSION = \"2024-11-05\";",
7445
7837
  "doc": "/**\n * The pure MCP (Model Context Protocol) JSON-RPC dispatch — no I/O, no `Bun.*`, no side effects, so it\n * unit-tests cleanly. A transport (stdio in `@nifrajs/cli`'s `mcp.ts`, Streamable-HTTP in {@link ./http.ts})\n * wires this to a byte stream; the tools/resources are injected, so the protocol logic is exercised\n * without spawning or reading streams.\n *\n * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** — interactive `ui://`\n * widgets: a tool links a UI resource via `_meta.ui.resourceUri`, `tools/call` returns `structuredContent`\n * for the widget to render, and `initialize` advertises the `io.modelcontextprotocol/ui` extension. All of\n * it is additive — a tool whose handler returns a plain `string` behaves exactly as before.\n */"
7446
7838
  },
7839
+ {
7840
+ "name": "PublicDirCache",
7841
+ "kind": "interface",
7842
+ "package": "@nifrajs/web",
7843
+ "signature": "export interface PublicDirCache {\n /** `cache-control` for content-hashed assets (default immutable, one year). */\n readonly hashed?: string;\n /** `cache-control` for everything else under `public/` (default one day). */\n readonly assets?: string;\n}",
7844
+ "doc": "/** How long each subtree may be cached. Content-hashed bundle output can be immutable; a\n * user-authored file keeps its name across deploys, so it gets a day and a revalidation. */"
7845
+ },
7447
7846
  {
7448
7847
  "name": "publicDocsTools",
7449
7848
  "kind": "function",
@@ -7981,6 +8380,13 @@
7981
8380
  "signature": "export declare function renderInitAgents(result: InitAgentsResult): string;",
7982
8381
  "doc": "/** Format the retrofit result for the terminal. */"
7983
8382
  },
8383
+ {
8384
+ "name": "RenderMode",
8385
+ "kind": "type",
8386
+ "package": "@nifrajs/web",
8387
+ "signature": "export type RenderMode = \"static\" | \"isr\" | \"ssr\";",
8388
+ "doc": "/**\n * How a route produces its HTML.\n *\n * - `static` - prerendered at build; served as a file, no server render at request time.\n * - `isr` - server-rendered, then cached and revalidated on a timer (`export const revalidate`).\n * - `ssr` - server-rendered per request. The default, and the only mode that needs a live server.\n */"
8389
+ },
7984
8390
  {
7985
8391
  "name": "renderOpenApi",
7986
8392
  "kind": "function",
@@ -7999,7 +8405,7 @@
7999
8405
  "name": "RenderPageOptions",
8000
8406
  "kind": "interface",
8001
8407
  "package": "@nifrajs/web",
8002
- "signature": "export interface RenderPageOptions {\n readonly adapter: RenderAdapter;\n /** The layout chain to render — outermost layout → page (opaque; the adapter renders it). */\n readonly chain: readonly unknown[];\n /** The loader output for this request. */\n readonly data: unknown;\n /** An action's data return (POST only) — surfaced to the page as `actionData` + serialized\n * for the client so post-POST hydration matches. Omit on GETs. */\n readonly actionData?: unknown;\n /** URL of the built client entry (loaded as a module script). */\n readonly clientEntry: string;\n /** Chunk URLs for the **matched** route (its layout chain + own chunk) to `modulepreload` in the\n * shell — so the route code downloads in parallel with the entry instead of after it\n * (`buildClient`'s per-route map). Empty/omitted ⇒ only the entry is preloaded (unchanged). */\n readonly preload?: readonly string[];\n /** Stylesheet URLs for the **matched** route (its layout chain + own CSS, from `buildClient`'s\n * `BuildManifest.css`) — injected as `<link rel=\"stylesheet\">` in `<head>` so styles arrive with the\n * first paint (no FOUC). Rendered even on non-hydrated pages. Empty/omitted ⇒ none (unchanged). */\n readonly styles?: readonly string[];\n /** SSG: the prerendered-path set, serialized to `window.__NIFRA_PRERENDERED__` so the client fetches\n * a static `_data.json` on soft-nav into a prerendered route. Empty/omitted ⇒ not injected. */\n readonly prerenderedPaths?: readonly string[];\n /** ISR: route freshness in seconds, emitted as the `x-nifra-isr-revalidate` header for a `withISR`\n * wrapper to read. Omit ⇒ no header (the wrapper's default TTL applies). */\n readonly revalidate?: number;\n /** Matched route id; written to `window.__NIFRA_ROUTE__` so the client hydrates this chain. */\n readonly routeId?: string;\n /** The matched route's decoded path params — surfaced to the page as `params` (via {@link RenderProps})\n * so an adapter's `useParams` is SSR-correct. Omit ⇒ `{}` (a route with no dynamic segments, or an\n * error/404 render). */\n readonly params?: Readonly<Record<string, string>>;\n /** The request's `pathname + search` — surfaced as `path` (via {@link RenderProps}) so an adapter's\n * `useLocation`/`useSearchParams` render the right URL server-side and hydrate without drift. Omit ⇒\n * `\"\"`. */\n readonly path?: string;\n /** HTTP status for the response (default 200; e.g. 404 for a not-found page). */\n readonly status?: number;\n /** Document title (fallback when `head.title` is unset). */\n readonly title?: string;\n /** Resolved route head — `title` overrides `title` above; `meta`/`link` render as managed\n * (`data-nifra`) tags the client updates on navigation.\n *\n * **Head contract (the layout chain contributes).** `createWebApp` resolves this via\n * {@link mergeHeads}: a route's head is its **layout chain's** `meta`/`head` exports merged with the\n * page's. A `_layout.tsx` may `export const meta` (or `export function meta(args)`) — its tags land\n * on every page below it (the home for `hreflang`, `preconnect`, a section-default `<title>`). The\n * merge is **nearest-wins for scalars** (the page's `title` overrides an inner layout's, which\n * overrides an outer one; an undefined page title keeps the layout's) and **concatenated for the\n * `meta`/`link` arrays** (outermost layout first, page last). `<link>` attributes are name-validated\n * (any letter/digit/hyphen name — covers `rel`/`href`/`hreflang`/`crossorigin`/`media`/`sizes`/`as`/\n * `integrity`/`fetchpriority`/…) and value-escaped against XSS. */\n readonly head?: Meta;\n /** Id of the container wrapping the app markup (default `\"root\"`). */\n readonly rootId?: string;\n /** When `false`, emit a complete but **non-hydrated** document — no client entry script, data\n * globals, or modulepreloads. Used for server-rendered `_error` pages: a terminal state that needs no\n * client takeover, and it sidesteps an SSR/hydrate mismatch (the server rendered the boundary, not\n * the page the client manifest maps this route id to). Default `true`. */\n readonly hydrate?: boolean;\n /** Island client bundles (`@nifrajs/web/islands`) to load as `<script type=\"module\">` in the document\n * tail — emitted **regardless of `hydrate`**, so a static (`hydrate: false`) page can still mount\n * no-framework islands. URLs are attribute-escaped. Empty/omitted ⇒ none (unchanged output). */\n readonly islandScripts?: readonly string[];\n}"
8408
+ "signature": "export interface RenderPageOptions {\n readonly adapter: RenderAdapter;\n /** The layout chain to render — outermost layout → page (opaque; the adapter renders it). */\n readonly chain: readonly unknown[];\n /** The loader output for this request. */\n readonly data: unknown;\n /** An action's data return (POST only) — surfaced to the page as `actionData` + serialized\n * for the client so post-POST hydration matches. Omit on GETs. */\n readonly actionData?: unknown;\n /** URL of the built client entry (loaded as a module script). */\n readonly clientEntry: string;\n /** Chunk URLs for the **matched** route (its layout chain + own chunk) to `modulepreload` in the\n * shell — so the route code downloads in parallel with the entry instead of after it\n * (`buildClient`'s per-route map). Empty/omitted ⇒ only the entry is preloaded (unchanged). */\n readonly preload?: readonly string[];\n /** Stylesheet URLs for the **matched** route (its layout chain + own CSS, from `buildClient`'s\n * `BuildManifest.css`) — injected as `<link rel=\"stylesheet\">` in `<head>` so styles arrive with the\n * first paint (no FOUC). Rendered even on non-hydrated pages. Empty/omitted ⇒ none (unchanged). */\n readonly styles?: readonly string[];\n /** SSG: the prerendered-path set, serialized to `window.__NIFRA_PRERENDERED__` so the client fetches\n * a static `_data.json` on soft-nav into a prerendered route. Empty/omitted ⇒ not injected. */\n readonly prerenderedPaths?: readonly string[];\n /** ISR: route freshness in seconds, emitted as the `x-nifra-isr-revalidate` header for a `withISR`\n * wrapper to read. Omit ⇒ no header (the wrapper's default TTL applies). */\n readonly revalidate?: number;\n /** Matched route id; written to `window.__NIFRA_ROUTE__` so the client hydrates this chain. */\n readonly routeId?: string;\n /** The matched route's decoded path params — surfaced to the page as `params` (via {@link RenderProps})\n * so an adapter's `useParams` is SSR-correct. Omit ⇒ `{}` (a route with no dynamic segments, or an\n * error/404 render). */\n readonly params?: Readonly<Record<string, string>>;\n /** The request's `pathname + search` — surfaced as `path` (via {@link RenderProps}) so an adapter's\n * `useLocation`/`useSearchParams` render the right URL server-side and hydrate without drift. Omit ⇒\n * `\"\"`. */\n readonly path?: string;\n /** Per-layout loader data, forwarded to the adapter as `RenderProps.layoutData`. Aligned with the\n * chain's layout prefix; omitted when no layout in the chain has a loader. */\n readonly layoutData?: readonly unknown[];\n /** HTTP status for the response (default 200; e.g. 404 for a not-found page). */\n readonly status?: number;\n /** Extra response headers - e.g. the `cache-control` a terminal status page wants. `content-type`\n * is ignored (the document is always HTML) and the ISR freshness header is applied after these,\n * so neither can be overridden from here. */\n readonly headers?: HeadersLike;\n /** Document title (fallback when `head.title` is unset). */\n readonly title?: string;\n /** Resolved route head — `title` overrides `title` above; `meta`/`link` render as managed\n * (`data-nifra`) tags the client updates on navigation.\n *\n * **Head contract (the layout chain contributes).** `createWebApp` resolves this via\n * {@link mergeHeads}: a route's head is its **layout chain's** `meta`/`head` exports merged with the\n * page's. A `_layout.tsx` may `export const meta` (or `export function meta(args)`) — its tags land\n * on every page below it (the home for `hreflang`, `preconnect`, a section-default `<title>`). The\n * merge is **nearest-wins for scalars** (the page's `title` overrides an inner layout's, which\n * overrides an outer one; an undefined page title keeps the layout's) and **concatenated for the\n * `meta`/`link` arrays** (outermost layout first, page last). `<link>` attributes are name-validated\n * (any letter/digit/hyphen name — covers `rel`/`href`/`hreflang`/`crossorigin`/`media`/`sizes`/`as`/\n * `integrity`/`fetchpriority`/…) and value-escaped against XSS. */\n readonly head?: Meta;\n /** Id of the container wrapping the app markup (default `\"root\"`). */\n readonly rootId?: string;\n /** When `false`, emit a complete but **non-hydrated** document — no client entry script, data\n * globals, or modulepreloads. Used for server-rendered `_error` pages: a terminal state that needs no\n * client takeover, and it sidesteps an SSR/hydrate mismatch (the server rendered the boundary, not\n * the page the client manifest maps this route id to). Default `true`. */\n readonly hydrate?: boolean;\n /** Island client bundles (`@nifrajs/web/islands`) to load as `<script type=\"module\">` in the document\n * tail — emitted **regardless of `hydrate`**, so a static (`hydrate: false`) page can still mount\n * no-framework islands. URLs are attribute-escaped. Empty/omitted ⇒ none (unchanged output). */\n readonly islandScripts?: readonly string[];\n}"
8003
8409
  },
8004
8410
  {
8005
8411
  "name": "renderPageResult",
@@ -8018,7 +8424,7 @@
8018
8424
  "name": "RenderProps",
8019
8425
  "kind": "interface",
8020
8426
  "package": "@nifrajs/web",
8021
- "signature": "export interface RenderProps {\n readonly data: unknown;\n readonly actionData?: unknown;\n /** True while a client navigation or submit is in flight (client-only; absent/false on SSR). */\n readonly pending?: boolean;\n /** The path a client navigation is transitioning TO while `pending` (client-only; absent on SSR and\n * when idle). Lets an adapter tell a `NavLink` whether its own target is the one loading. */\n readonly pendingPath?: string;\n /** The in-flight client submit, for optimistic UI (client-only; absent on SSR + when idle). */\n readonly submission?: Submission;\n /** The matched route's decoded path params (`/users/:id` → `{ id: \"7\" }`). Threaded identically on\n * SSR (from the request match) and on client navigation (from router state) so an adapter's\n * `useParams` reads the same value on both, with no hydration mismatch. Absent ⇒ no params (`{}`). */\n readonly params?: Readonly<Record<string, string>>;\n /** The current URL's `pathname + search` (no hash — the hash never reaches the server). Threaded on\n * SSR and client alike so an adapter's `useLocation`/`useSearchParams` hydrate consistently. Absent\n * on adapters/callers that don't supply it (treated as `\"\"`). */\n readonly path?: string;\n}",
8427
+ "signature": "export interface RenderProps {\n readonly data: unknown;\n /**\n * Per-layout loader data, aligned index-for-index with the layout prefix of `chain`.\n *\n * Absent when no layout in the chain exports a `loader`, so a page-only app is byte-identical to\n * before. An adapter that does not read it renders exactly as it did; one that does passes\n * `layoutData[i]` to `chain[i]`.\n */\n readonly layoutData?: readonly unknown[];\n readonly actionData?: unknown;\n /** True while a client navigation or submit is in flight (client-only; absent/false on SSR). */\n readonly pending?: boolean;\n /** The path a client navigation is transitioning TO while `pending` (client-only; absent on SSR and\n * when idle). Lets an adapter tell a `NavLink` whether its own target is the one loading. */\n readonly pendingPath?: string;\n /** The in-flight client submit, for optimistic UI (client-only; absent on SSR + when idle). */\n readonly submission?: Submission;\n /** The matched route's decoded path params (`/users/:id` → `{ id: \"7\" }`). Threaded identically on\n * SSR (from the request match) and on client navigation (from router state) so an adapter's\n * `useParams` reads the same value on both, with no hydration mismatch. Absent ⇒ no params (`{}`). */\n readonly params?: Readonly<Record<string, string>>;\n /** The current URL's `pathname + search` (no hash — the hash never reaches the server). Threaded on\n * SSR and client alike so an adapter's `useLocation`/`useSearchParams` hydrate consistently. Absent\n * on adapters/callers that don't supply it (treated as `\"\"`). */\n readonly path?: string;\n}",
8022
8428
  "doc": "/** The data handed to a route component. Opaque to the core. `actionData` is the return of a\n * route `action` after a POST (absent on plain GETs). `pending` + `submission` are client-only\n * (absent on SSR): they drive **optimistic UI** — render from `submission.formData` while `pending`. */"
8023
8429
  },
8024
8430
  {
@@ -8028,6 +8434,13 @@
8028
8434
  "signature": "export declare function renderReport(result: PortResult, opts: {\n readonly strict?: boolean;\n}): string;",
8029
8435
  "doc": "/** Render the feature × target matrix + evidence + summary as a readable report. */"
8030
8436
  },
8437
+ {
8438
+ "name": "renderRouteManifest",
8439
+ "kind": "function",
8440
+ "package": "@nifrajs/web",
8441
+ "signature": "export declare function renderRouteManifest(manifest: RouteManifest): string;",
8442
+ "doc": "/** Render the manifest as a readable report - the `nifra routes --modes` output. */"
8443
+ },
8031
8444
  {
8032
8445
  "name": "renderRouteTable",
8033
8446
  "kind": "function",
@@ -8053,7 +8466,7 @@
8053
8466
  "name": "renderTypesResult",
8054
8467
  "kind": "function",
8055
8468
  "package": "@nifrajs/cli",
8056
- "signature": "export declare function renderTypesResult(types: readonly TypeEntry[], name: string | undefined, query: string | undefined, limit: number): string;",
8469
+ "signature": "export declare function renderTypesResult(types: readonly TypeEntry[], name: string | undefined, query: string | undefined, limit: number, full?: boolean): string;",
8057
8470
  "doc": "/**\n * Render the tool result. `name` → the exact declaration(s); else `query` → the top matches; else a\n * per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),\n * so this is the complete answer — there is no need to read a `.d.ts` file.\n */"
8058
8471
  },
8059
8472
  {
@@ -8248,6 +8661,19 @@
8248
8661
  "signature": "export interface ResolvedEffectLedger {\n readonly sink: EffectLedgerOptions[\"sink\"];\n readonly maxEntries: number;\n readonly chain: boolean;\n /** The registered route pattern - what the sealed ledger names (never the concrete URL). */\n readonly method: string;\n readonly path: string;\n /** The route's declared capability tokens, surfaced on the sealed ledger. */\n readonly declared: readonly string[];\n}",
8249
8662
  "doc": "/** Registration-resolved effect-ledger wiring for one capability-declaring route. */"
8250
8663
  },
8664
+ {
8665
+ "name": "resolveDevEntry",
8666
+ "kind": "function",
8667
+ "package": "@nifrajs/web",
8668
+ "signature": "export declare function resolveDevEntry(server: ServedOn, options: ResolveDevEntryOptions): Promise<DevEntryMatch>;",
8669
+ "doc": "/**\n * Ask Bun's dev server for the current bundled entry URL. One loopback request against a route the app\n * never exposes.\n *\n * **The answer expires.** The URL is a content hash over the whole client graph, so editing any file it\n * reaches - a route, a component, a stylesheet - re-hashes it. Caching the startup value and injecting it\n * forever produces a spectacular failure: Bun answers a superseded chunk URL with a 104-byte\n * `location.reload()` stub under a 405, so the page reloads, gets the same dead URL from SSR, and reloads\n * again, forever. No console output survives the loop to explain it. Callers must re-resolve rather than\n * remember - see how `@nifrajs/web/dev` serves a stable URL that redirects here per request.\n */"
8670
+ },
8671
+ {
8672
+ "name": "ResolveDevEntryOptions",
8673
+ "kind": "interface",
8674
+ "package": "@nifrajs/web",
8675
+ "signature": "export interface ResolveDevEntryOptions {\n /** Path of the throwaway route serving Bun's bundled HTML (e.g. `/__nifra/entry`). */\n readonly probePath: string;\n /** Injected for tests; defaults to global `fetch`. */\n readonly fetchImpl?: typeof fetch;\n}"
8676
+ },
8251
8677
  {
8252
8678
  "name": "ResolvedIdempotency",
8253
8679
  "kind": "interface",
@@ -8261,6 +8687,12 @@
8261
8687
  "package": "@nifrajs/image",
8262
8688
  "signature": "export interface ResolvedImage {\n readonly src: string;\n readonly srcSet?: string;\n readonly sizes?: string;\n readonly width: number;\n readonly height: number;\n readonly alt: string;\n readonly loading: \"lazy\" | \"eager\";\n readonly decoding: \"async\";\n readonly fetchPriority?: \"high\";\n}"
8263
8689
  },
8690
+ {
8691
+ "name": "ResolvedPlugins",
8692
+ "kind": "interface",
8693
+ "package": "@nifrajs/cli",
8694
+ "signature": "export interface ResolvedPlugins {\n readonly vitePlugins: readonly unknown[];\n readonly clientPlugins: readonly unknown[];\n readonly serverPlugins: readonly unknown[];\n}"
8695
+ },
8264
8696
  {
8265
8697
  "name": "ResolvedTarget",
8266
8698
  "kind": "interface",
@@ -8295,6 +8727,13 @@
8295
8727
  "signature": "export declare function resolveProjectDir(root: string, dir: string | undefined): string | null;",
8296
8728
  "doc": "/**\n * Resolve an optional `dir` tool argument — a subdirectory of the project root the caller wants to scope a\n * check/test to (e.g. `nifra check` on `app/` when the MCP server's root is a monorepo). Returns the\n * absolute target, or `null` if `dir` escapes the root (a path-traversal guard: no `..` out, no absolute\n * path elsewhere). `undefined`/empty → the root itself.\n */"
8297
8729
  },
8730
+ {
8731
+ "name": "resolvePublicPath",
8732
+ "kind": "function",
8733
+ "package": "@nifrajs/web",
8734
+ "signature": "export declare function resolvePublicPath(root: string, pathname: string): string | undefined;",
8735
+ "doc": "/**\n * Resolve a URL pathname to an absolute path **confined** to `root`, or `undefined` if it escapes.\n *\n * This is a user-path-to-filesystem sink, which makes it the one part of this feature with a security\n * consequence. Decode first (`%2e%2e%2f` is `../`), then normalize, then verify the result is still\n * under `root` by prefix - checking the *resolved* path rather than scanning the input for `..`,\n * because a blocklist over encodings is exactly the kind of check that gets bypassed.\n */"
8736
+ },
8298
8737
  {
8299
8738
  "name": "resolveServerOnlyChains",
8300
8739
  "kind": "function",
@@ -8403,6 +8842,13 @@
8403
8842
  "signature": "export declare function resyncServerManifestSource(source: string, manifest: Parameters<typeof generateServerManifest>[0], routesPrefix: string): string;",
8404
8843
  "doc": "/**\n * Re-emit a committed server-manifest from a freshly-discovered route tree, PRESERVING its baked\n * client-asset references (`clientEntry` / `styles` / `routeStyles`) and its eager-vs-lazy shape. This is\n * what makes `nifra sync-manifest` a route-table refresh (renamed / added / removed routes) that does NOT\n * need a full build. It deliberately does NOT rebuild the client bundle: a brand-new HYDRATING route still\n * needs a full build so its client chunk exists - this only re-syncs the server manifest's route table.\n * Pure: `source` + the discovered `manifest` in, new source out.\n */"
8405
8844
  },
8845
+ {
8846
+ "name": "RETAIN_HEADER",
8847
+ "kind": "const",
8848
+ "package": "@nifrajs/web",
8849
+ "signature": "export declare const RETAIN_HEADER = \"x-nifra-retain\";",
8850
+ "doc": "/**\n * Layout indices a client navigation is asking the server NOT to re-run, comma separated.\n *\n * The client owns the decision because it holds both the old and new match plus each layout's scoped\n * params. The server treats it as a hint and refuses to skip a `gate` regardless - a guard that runs\n * only when the client says so is not a guard.\n */"
8851
+ },
8406
8852
  {
8407
8853
  "name": "RetryPolicy",
8408
8854
  "kind": "interface",
@@ -8481,6 +8927,19 @@
8481
8927
  "package": "@nifrajs/core",
8482
8928
  "signature": "export interface RobotsRule {\n /** A single user-agent token (`\"*\"`, `\"Googlebot\"`, …) or several sharing the same directives. */\n readonly userAgent: string | readonly string[];\n readonly allow?: readonly string[];\n readonly disallow?: readonly string[];\n /** Seconds between requests (honored by some crawlers). */\n readonly crawlDelay?: number;\n}"
8483
8929
  },
8930
+ {
8931
+ "name": "RollupBundleLike",
8932
+ "kind": "type",
8933
+ "package": "@nifrajs/web",
8934
+ "signature": "export type RollupBundleLike = Readonly<Record<string, RollupChunkLike>>;"
8935
+ },
8936
+ {
8937
+ "name": "RollupChunkLike",
8938
+ "kind": "interface",
8939
+ "package": "@nifrajs/web",
8940
+ "signature": "export interface RollupChunkLike {\n /** `\"chunk\"` for JS output, `\"asset\"` for CSS/static — assets have no module graph. */\n readonly type?: string;\n /** The entry module id this chunk was built for, if it is an entry. Rollup's `facadeModuleId`. */\n readonly facadeModuleId?: string | null;\n /** Every module id that landed in this chunk. Rollup's `moduleIds`. */\n readonly moduleIds?: readonly string[];\n}",
8941
+ "doc": "/**\n * The slice of a Rollup/Vite output bundle this seam consumes. `OutputBundle` is\n * `Record<fileName, OutputChunk | OutputAsset>`; only chunks carry a module graph. Typed structurally\n * (no `rollup`/`vite` type dependency) — the fields are stable Rollup output API.\n */"
8942
+ },
8484
8943
  {
8485
8944
  "name": "ROUTE_GLOBAL",
8486
8945
  "kind": "const",
@@ -8495,6 +8954,13 @@
8495
8954
  "signature": "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;",
8496
8955
  "doc": "/** The body object type of `App`'s `Method Path` route (`never` when the route declares no body). */"
8497
8956
  },
8957
+ {
8958
+ "name": "RouteCapability",
8959
+ "kind": "type",
8960
+ "package": "@nifrajs/web",
8961
+ "signature": "export type RouteCapability = \"server\" | \"revalidation\";",
8962
+ "doc": "/** What a route needs from its host in order to behave as declared. */"
8963
+ },
8498
8964
  {
8499
8965
  "name": "RouteCapabilityEvidence",
8500
8966
  "kind": "interface",
@@ -8528,6 +8994,13 @@
8528
8994
  "signature": "export type RouteConfigErrorCode = \"DUPLICATE_ROUTE\" | \"DUPLICATE_PARAM\" | \"PARAM_NAME_CONFLICT\" | \"INVALID_PATH\" | \"INVALID_PARAM_NAME\" | \"WILDCARD_NOT_LAST\" | \"INVALID_METHOD\" | \"INVALID_ASSURANCE\" | \"INVALID_IDEMPOTENCY\";",
8529
8995
  "doc": "/** Stable codes for boot-time (L2) route configuration failures. */"
8530
8996
  },
8997
+ {
8998
+ "name": "RouteDataEnvelope",
8999
+ "kind": "interface",
9000
+ "package": "@nifrajs/web",
9001
+ "signature": "export interface RouteDataEnvelope {\n readonly v: 1;\n readonly data: unknown;\n readonly layoutData?: readonly unknown[];\n /** Indices whose loader was SKIPPED because the layout's own params did not change. The client\n * keeps its existing value at each of these, so an unchanged layout is neither refetched nor lost. */\n readonly retained?: readonly number[];\n /** Terminal status signalled by a loader. Added by the browser transport after reading\n * `X-Nifra-Status`; old servers and static data files simply omit it. */\n readonly status?: number;\n}",
9002
+ "doc": "/**\n * The data-mode payload once a chain can carry layout data.\n *\n * Versioned because a prerendered `_data.json` is a static file on a CDN and outlives the deploy that\n * wrote it: a browser running new client code can be handed an envelope written by the previous\n * build. The reader therefore accepts BOTH the bare pre-envelope value and this shape, and decides by\n * structure rather than by assuming the deploy was atomic.\n */"
9003
+ },
8531
9004
  {
8532
9005
  "name": "RouteDescriptor",
8533
9006
  "kind": "interface",
@@ -8539,7 +9012,7 @@
8539
9012
  "name": "RouteEntry",
8540
9013
  "kind": "interface",
8541
9014
  "package": "@nifrajs/web",
8542
- "signature": "export interface RouteEntry {\n readonly id: string;\n readonly pattern: string;\n readonly layoutIds: readonly string[];\n /** `_error` boundary ids in this route's ancestor chain (outermost → innermost). The last is the\n * **nearest** boundary, rendered when the route's loader throws. Always set by `buildManifest`\n * (optional only so hand-built test manifests may omit it); absent/empty ⇒ no boundary (error 500s). */\n readonly errorIds?: readonly string[];\n readonly file: string;\n readonly load: () => Promise<RouteModule>;\n}",
9015
+ "signature": "export interface RouteEntry {\n readonly id: string;\n readonly pattern: string;\n readonly layoutIds: readonly string[];\n /**\n * Param names each layout in {@link layoutIds} owns, aligned by index.\n *\n * A layout wraps a URL prefix, so these are the params it may read and the ones whose change should\n * re-run its loader - everything deeper belongs to routes beneath it. Derived per (route, layout)\n * pair rather than per layout, because one layout can own different params on different expanded\n * patterns: `[[lang]]/docs/_layout` owns nothing on `/docs/:slug` and `{lang}` on `/:lang/docs/:slug`.\n * Optional only so hand-built test manifests may omit it.\n */\n readonly layoutParams?: ReadonlyArray<readonly string[]>;\n /** `_error` boundary ids in this route's ancestor chain (outermost → innermost). The last is the\n * **nearest** boundary, rendered when the route's loader throws. Always set by `buildManifest`\n * (optional only so hand-built test manifests may omit it); absent/empty ⇒ no boundary (error 500s). */\n readonly errorIds?: readonly string[];\n readonly file: string;\n readonly load: () => Promise<RouteModule>;\n}",
8543
9016
  "doc": "/** One matched route: pattern, nested layout ids (outermost → innermost), source file, loader. */"
8544
9017
  },
8545
9018
  {
@@ -8598,6 +9071,27 @@
8598
9071
  "signature": "export interface RouteLike {\n readonly method: string;\n readonly path: string;\n readonly schema?: {\n readonly body?: unknown;\n readonly query?: unknown;\n readonly response?: unknown;\n } | undefined;\n}",
8599
9072
  "doc": "/** A registered route as seen by {@link buildOpenApiDocument} — structurally a `@nifrajs/core`\n * `RouteDescriptor` (so `app.routes()` is passed straight through). */"
8600
9073
  },
9074
+ {
9075
+ "name": "RouteManifest",
9076
+ "kind": "interface",
9077
+ "package": "@nifrajs/web",
9078
+ "signature": "export interface RouteManifest {\n /** The deploy target this was resolved against, when one was given. */\n readonly target?: string;\n readonly routes: readonly RouteManifestEntry[];\n readonly conflicts: readonly RouteManifestConflict[];\n /** Route counts by mode - the summary a report leads with. */\n readonly totals: Readonly<Record<RenderMode, number>>;\n}",
9079
+ "doc": "/** The whole artifact: every route's behaviour, plus anything the target cannot honour. */"
9080
+ },
9081
+ {
9082
+ "name": "RouteManifestConflict",
9083
+ "kind": "interface",
9084
+ "package": "@nifrajs/web",
9085
+ "signature": "export interface RouteManifestConflict {\n readonly id: string;\n readonly pattern: string;\n readonly capability: RouteCapability;\n /** What goes wrong on this target - the consequence, not the rule that was broken. */\n readonly consequence: string;\n}",
9086
+ "doc": "/** A route whose declaration the chosen target cannot honour, and what actually happens if it ships. */"
9087
+ },
9088
+ {
9089
+ "name": "RouteManifestEntry",
9090
+ "kind": "interface",
9091
+ "package": "@nifrajs/web",
9092
+ "signature": "export interface RouteManifestEntry {\n /** The route's id (its key in the build manifest and in `routes`). */\n readonly id: string;\n /** The matching pattern, e.g. `/blog/:slug`. */\n readonly pattern: string;\n readonly mode: RenderMode;\n /** Whether the full-document client is shipped. `false` means the page loads no framework JS. */\n readonly hydrate: boolean;\n /** Revalidation window in seconds. Present only for `isr`. */\n readonly revalidate?: number;\n /**\n * Concrete paths prerendered at build. Present for `static` routes: a single path for a static route,\n * one per `getStaticPaths` entry for a dynamic one. Absent means \"no path was enumerated\", which for a\n * dynamic route is exactly the condition that makes it unprerenderable.\n */\n readonly prerenderedPaths?: readonly string[];\n /** What this route needs from the host to behave as declared. */\n readonly requires: readonly RouteCapability[];\n}",
9093
+ "doc": "/** One route's resolved behaviour. */"
9094
+ },
8601
9095
  {
8602
9096
  "name": "RouteMatch",
8603
9097
  "kind": "interface",
@@ -8643,7 +9137,7 @@
8643
9137
  "name": "RoutePatternSegment",
8644
9138
  "kind": "type",
8645
9139
  "package": "@nifrajs/core",
8646
- "signature": "export type RoutePatternSegment = {\n readonly kind: \"static\";\n readonly value: string;\n} | {\n readonly kind: \"param\";\n readonly name: string;\n} | {\n readonly kind: \"wildcard\";\n readonly name: string;\n};"
9140
+ "signature": "export type RoutePatternSegment = {\n readonly kind: \"static\";\n readonly value: string;\n} | {\n readonly kind: \"param\";\n readonly name: string;\n} | {\n readonly kind: \"wildcard\";\n readonly name: string;\n}\n/** A segment that is part literal, part parameter: `:key.txt`, `feed.:format`, `v:major.:minor`. */\n | {\n readonly kind: \"mixed\";\n readonly parts: readonly MixedPart[];\n};"
8647
9141
  },
8648
9142
  {
8649
9143
  "name": "Router",
@@ -8677,7 +9171,7 @@
8677
9171
  "name": "RouterState",
8678
9172
  "kind": "interface",
8679
9173
  "package": "@nifrajs/web",
8680
- "signature": "export interface RouterState {\n readonly routeId: string;\n readonly params: Record<string, string>;\n /** The current URL path (used to revalidate the active loader after an action). */\n readonly path: string;\n readonly data: unknown;\n /** An action's data return after a client-side submit (cleared on navigation). */\n readonly actionData?: unknown;\n /** True while a navigation or submit is in flight (drives loading UI). */\n readonly pending: boolean;\n /** The path a navigation is transitioning TO while `pending` (cleared when it settles). Lets a\n * `NavLink` know whether its own `to` is the one loading; `undefined` when idle. */\n readonly pendingPath?: string | undefined;\n /** The in-flight submit (set during a `submit`, cleared when it settles) — for optimistic UI. */\n readonly submission?: Submission;\n}",
9174
+ "signature": "export interface RouterState {\n readonly routeId: string;\n readonly params: Record<string, string>;\n /** The current URL path (used to revalidate the active loader after an action). */\n readonly path: string;\n readonly data: unknown;\n /** Per-layout loader data, aligned with the matched chain's leading layout prefix. Absent when no\n * layout in that chain has a loader. */\n readonly layoutData?: readonly unknown[] | undefined;\n /** An action's data return after a client-side submit (cleared on navigation). */\n readonly actionData?: unknown;\n /** True while a navigation or submit is in flight (drives loading UI). */\n readonly pending: boolean;\n /** The path a navigation is transitioning TO while `pending` (cleared when it settles). Lets a\n * `NavLink` know whether its own `to` is the one loading; `undefined` when idle. */\n readonly pendingPath?: string | undefined;\n /** The in-flight submit (set during a `submit`, cleared when it settles) — for optimistic UI. */\n readonly submission?: Submission;\n}",
8681
9175
  "doc": "/** The router's observable state. A new object is published on every transition. */"
8682
9176
  },
8683
9177
  {
@@ -9168,6 +9662,13 @@
9168
9662
  "signature": "export declare function scanProject(cwd: string): Promise<SourceFinding[]>;",
9169
9663
  "doc": "/** Collect own-API `fetch()` findings across the project. */"
9170
9664
  },
9665
+ {
9666
+ "name": "scanRemovedImports",
9667
+ "kind": "function",
9668
+ "package": "@nifrajs/cli",
9669
+ "signature": "export declare function scanRemovedImports(file: string, content: string): SourceFinding[];",
9670
+ "doc": "/** Flag an import of a specifier that no longer exists. Pure; matches the exact specifier or a subpath\n * of it, so `@nifrajs/budget/x` is caught alongside `@nifrajs/budget`. */"
9671
+ },
9171
9672
  {
9172
9673
  "name": "scanResponseRoutes",
9173
9674
  "kind": "function",
@@ -9436,12 +9937,32 @@
9436
9937
  "package": "@nifrajs/mcp-db",
9437
9938
  "signature": "export interface ServeDatabaseAsMcpOptions {\n /**\n * REQUIRED allowlist of tables the MCP surface may see. `list_tables`/`describe_table` are\n * restricted to it and `run_query` plans are verified against it. Empty array → construction\n * throws; expose nothing by accident, ever.\n */\n readonly tables: readonly string[];\n /** Enable the `run_query` tool. Omitted → schema tools only. */\n readonly runQuery?: RunQueryOptions;\n /** MCP server identity (defaults: name `nifra-db`, version `1.0.0`). */\n readonly name?: string;\n readonly version?: string;\n /**\n * Set `PRAGMA query_only = ON` on the connection at construction (default true). Disable ONLY\n * when the same connection must also serve writes elsewhere in the app — with it disabled,\n * `run_query` still gates on SELECT/WITH + plan verification, but the engine-level guarantee is\n * yours to provide (e.g. open a second, read-only connection for the MCP mount — preferred).\n */\n readonly enforceQueryOnly?: boolean;\n}"
9438
9939
  },
9940
+ {
9941
+ "name": "ServedOn",
9942
+ "kind": "interface",
9943
+ "package": "@nifrajs/web",
9944
+ "signature": "export interface ServedOn {\n readonly port: number;\n}",
9945
+ "doc": "/** Structural slice of a running server this needs: just the port it bound. */"
9946
+ },
9439
9947
  {
9440
9948
  "name": "ServeOptions",
9441
9949
  "kind": "interface",
9442
9950
  "package": "@nifrajs/node",
9443
9951
  "signature": "export interface ServeOptions {\n readonly port: number;\n readonly hostname?: string;\n /**\n * Protocol used when the adapter constructs `Request.url`.\n *\n * `@nifrajs/node` creates a plain Node `http` server, so the safe default is `\"http\"`. Deployments behind\n * TLS termination can set `\"https\"` (or a trusted infra-aware function) so app code that reads\n * `request.url` sees the public scheme. Forwarded headers are not trusted implicitly.\n */\n readonly protocol?: RequestProtocolOption;\n /**\n * Install SIGTERM/SIGINT handlers that call `stop()` for a graceful drain on\n * `docker stop` / Ctrl-C. Off by default — taking over process signals is opt-in,\n * mirroring nifra's Bun `listen({ gracefulSignals })`.\n *\n * The app-level request timeout (`server({ requestTimeoutMs })` → 503) and body cap\n * are *not* set here — they live inside `app.fetch`, so they already apply through\n * this adapter. Slow-client protection is Node's built-in `requestTimeout` (300s) /\n * `headersTimeout` (60s) defaults.\n */\n readonly signals?: boolean;\n /**\n * Serve static files from disk for matching GET/HEAD requests *before* the app runs — non-matching\n * requests fall through to `app.fetch` with the node-direct fast path intact (no perf regression on\n * SSR/API routes). Replaces the hand-rolled `/assets/*` reader in self-hosted entries.\n */\n readonly static?: ServeStaticOptions;\n}"
9444
9952
  },
9953
+ {
9954
+ "name": "servePublicDir",
9955
+ "kind": "function",
9956
+ "package": "@nifrajs/web",
9957
+ "signature": "export declare function servePublicDir(options: ServePublicDirOptions): (request: Request) => Promise<Response | undefined>;",
9958
+ "doc": "/**\n * Build a static-file handler for `dir`.\n *\n * Returns `undefined` on any miss so the caller **falls through to routing** - a static probe must\n * never shadow a route. That ordering is also why a `routes/robots.txt.tsx` beats a\n * `public/robots.txt` only if the caller checks routes first; the documented precedence is that the\n * static probe runs first, so `public/` wins, and an app wanting the route should not ship both.\n */"
9959
+ },
9960
+ {
9961
+ "name": "ServePublicDirOptions",
9962
+ "kind": "interface",
9963
+ "package": "@nifrajs/web",
9964
+ "signature": "export interface ServePublicDirOptions {\n /** Absolute path of the directory to serve. */\n readonly dir: string;\n /** URL prefix whose files are content-hashed and may be cached immutably (default `\"/assets/\"`). */\n readonly hashedPrefix?: string;\n readonly cache?: PublicDirCache;\n /** Optional encoded URL-path allowlist. When present, route misses avoid a filesystem probe. */\n readonly files?: ReadonlySet<string>;\n}"
9965
+ },
9445
9966
  {
9446
9967
  "name": "server",
9447
9968
  "kind": "function",
@@ -10075,6 +10596,27 @@
10075
10596
  "signature": "export interface StaticRoutes {\n /** Concrete prerendered paths — static `prerender` routes + each `getStaticPaths` entry. */\n readonly paths: string[];\n /** Per dynamic route pattern, its `getStaticPaths` `fallback` (`\"ssr\"` default). `createWebApp` uses\n * `\"404\"` to reject an unlisted path under that route at runtime (the path simply doesn't exist). */\n readonly fallbacks: Record<string, \"ssr\" | \"404\">;\n}",
10076
10597
  "doc": "/** The static-routing facts a server needs from the route modules: which concrete paths are\n * prerendered, plus each dynamic route's `getStaticPaths` fallback policy. */"
10077
10598
  },
10599
+ {
10600
+ "name": "STATUS_HEADER",
10601
+ "kind": "const",
10602
+ "package": "@nifrajs/web",
10603
+ "signature": "export declare const STATUS_HEADER = \"x-nifra-status\";",
10604
+ "doc": "/**\n * Response header carrying a **terminal status** a loader signalled with `notFound()` / `gone()` /\n * `statusPage(n)` during a client-side navigation's data fetch.\n *\n * A soft-nav fetches data, not a document, so the server cannot answer by rendering the `_404` page -\n * it has only JSON to return. Without this channel the two halves disagree: a crawler doing a hard\n * navigation gets a correct 404 page while a user who clicked a link gets an error or a blank screen.\n * The client reads it and renders the same boundary the server would have.\n *\n * Carried in a header rather than the body so it survives the NDJSON deferred-data path unchanged,\n * exactly like {@link REDIRECT_HEADER}.\n */"
10605
+ },
10606
+ {
10607
+ "name": "statusPage",
10608
+ "kind": "function",
10609
+ "package": "@nifrajs/web",
10610
+ "signature": "export declare function statusPage(status: number, options?: StatusPageOptions): never;",
10611
+ "doc": "/**\n * Render a terminal page at any 4xx/5xx status - the escape hatch behind {@link notFound} and\n * {@link gone} (402, 451, …). Uses `_<status>.tsx` if present, otherwise `_404`.\n */"
10612
+ },
10613
+ {
10614
+ "name": "StatusPageOptions",
10615
+ "kind": "interface",
10616
+ "package": "@nifrajs/web",
10617
+ "signature": "export interface StatusPageOptions {\n /**\n * Extra headers for the rendered response - `cache-control` above all.\n *\n * The defaults differ by status for a reason: a 404 may be a race with publication, so it wants a\n * short TTL, while a 410 is a promise that the URL is permanently gone and can be cached hard.\n * Getting this wrong is the difference between a crawler dropping a dead URL and re-fetching it\n * for weeks.\n */\n readonly headers?: HeadersLike;\n}",
10618
+ "doc": "/** Options shared by {@link notFound}, {@link gone}, and {@link statusPage}. */"
10619
+ },
10078
10620
  {
10079
10621
  "name": "StorageAdapter",
10080
10622
  "kind": "interface",
@@ -10227,6 +10769,13 @@
10227
10769
  "signature": "export interface StylesheetEmitter {\n /** Store `css` for `path`, returning the `import \"<path>?<namespace>\"` line for the JS module. */\n emit(path: string, css: string): string;\n}",
10228
10770
  "doc": "/**\n * Records compiled CSS and wires it into the client bundle through a virtual `?<namespace>` module —\n * the idiom the Vue plugin established (`?vue-css`). Register one per plugin `setup`; call `emit` per\n * file to stash its CSS and get back the `import` line to append to the JS module.\n */"
10229
10771
  },
10772
+ {
10773
+ "name": "styleTags",
10774
+ "kind": "function",
10775
+ "package": "@nifrajs/web",
10776
+ "signature": "export declare function styleTags(styles: readonly string[]): string;",
10777
+ "doc": "/** `<link rel=\"stylesheet\">` tags for Bun's extracted CSS, injected into each SSR'd page's `<head>`. */"
10778
+ },
10230
10779
  {
10231
10780
  "name": "Submission",
10232
10781
  "kind": "interface",
@@ -10477,6 +11026,13 @@
10477
11026
  "package": "@nifrajs/middleware",
10478
11027
  "signature": "export interface TimingOptions {\n /** Name for the automatic total request metric. Default `\"total\"`; set `false` to disable. */\n readonly total?: string | false;\n /** Decimal places for durations. Default `1`. */\n readonly precision?: number;\n /** Only emit timing for matching requests. Default `true`. */\n readonly enabled?: boolean | ((request: Request) => boolean);\n}"
10479
11028
  },
11029
+ {
11030
+ "name": "timingSafeEqual",
11031
+ "kind": "function",
11032
+ "package": "@nifrajs/web",
11033
+ "signature": "export declare function timingSafeEqual(a: string, b: string): boolean;",
11034
+ "doc": "/**\n * Compare two strings without leaking, via timing, where they first differ. A length mismatch returns\n * `false` up front: the length isn't the secret, and different-length buffers can't be compared in\n * constant time anyway. Encodes to bytes first so multi-byte characters compare by content, not by\n * UTF-16 code unit.\n */"
11035
+ },
10480
11036
  {
10481
11037
  "name": "timingSafeEqualBytes",
10482
11038
  "kind": "function",
@@ -10618,7 +11174,7 @@
10618
11174
  "name": "TransportCodecError",
10619
11175
  "kind": "class",
10620
11176
  "package": "@nifrajs/core",
10621
- "signature": "export declare class TransportCodecError extends TypeError {\n constructor(message: string);\n}"
11177
+ "signature": "export declare class TransportCodecError extends TypeError {\n constructor(message: string, options?: ErrorOptions);\n}"
10622
11178
  },
10623
11179
  {
10624
11180
  "name": "TransportCodecRegistry",
@@ -11283,9 +11839,16 @@
11283
11839
  "name": "VERSION",
11284
11840
  "kind": "const",
11285
11841
  "package": "@nifrajs/core",
11286
- "signature": "export declare const VERSION: \"2.1.0\";",
11842
+ "signature": "export declare const VERSION: \"2.2.0\";",
11287
11843
  "doc": "/**\n * Current package version. A hardcoded literal on purpose — core runs on the edge (no fs), so it can't\n * read its own package.json at runtime. `scripts/version.ts` rewrites it on every release bump and\n * `check:publish` asserts it equals `@nifrajs/core`'s package version.\n */"
11288
11844
  },
11845
+ {
11846
+ "name": "viteBundler",
11847
+ "kind": "const",
11848
+ "package": "@nifrajs/web",
11849
+ "signature": "export declare const viteBundler: Bundler;",
11850
+ "doc": "/**\n * The Vite build STRATEGY — plugged into `buildTargetWith`. `plugins` arriving through the shared\n * orchestrator are the app's Vite plugins (the escape hatch's whole reason), cast to Vite's plugin type.\n */"
11851
+ },
11289
11852
  {
11290
11853
  "name": "ViteDevServer",
11291
11854
  "kind": "interface",
@@ -11296,7 +11859,14 @@
11296
11859
  "name": "ViteDevServerOptions",
11297
11860
  "kind": "interface",
11298
11861
  "package": "@nifrajs/web",
11299
- "signature": "export interface ViteDevServerOptions {\n /** Absolute (or cwd-relative) path to the `routes/` dir. */\n readonly routesDir: string;\n /** Client runtime module providing `mountRouter` (e.g. `\"@nifrajs/web-react/client\"`). */\n readonly clientModule: string;\n /**\n * Build the nifra app for the given dev client-entry URL. `importQuery` changes when a route file\n * changes — thread it into `discoverRoutes(routesDir, { importQuery })` so a hard reload SSRs edited\n * modules (HMR handles the live client update; this keeps server-render fresh).\n */\n readonly createApp: (clientEntry: string, importQuery: string) => FetchApp | Promise<FetchApp>;\n /** Vite plugins — inject your framework's official plugin, e.g. `[react()]`. */\n readonly plugins?: readonly unknown[];\n /**\n * Extra `resolve.conditions` prepended ahead of nifra's defaults — some frameworks need their own\n * (e.g. Solid's `\"solid\"` condition routes `solid-js` to its source/JSX-dev build).\n */\n readonly conditions?: readonly string[];\n /**\n * Compile-time `define` replacements (e.g. Vue's `__VUE_OPTIONS_API__` flags). Vite already sets\n * `process.env.NODE_ENV` in dev; this is for framework feature flags the plugin doesn't inject.\n */\n readonly define?: Readonly<Record<string, string>>;\n /** Vite project root (default `process.cwd()`). */\n readonly root?: string;\n /** Port (default {@link DEFAULT_DEV_PORT}). */\n readonly port?: number;\n /**\n * Use polling for the file watcher. Native fs events (fsevents/inotify) are unreliable inside\n * containers, networked filesystems, and some sandboxes — there, HMR silently never fires. Set\n * `true` (or the env var `CHOKIDAR_USEPOLLING=1`) to poll instead. Default: off (native events).\n */\n readonly poll?: boolean;\n}"
11862
+ "signature": "export interface ViteDevServerOptions {\n /** Absolute (or cwd-relative) path to the `routes/` dir. */\n readonly routesDir: string;\n /** Client runtime module providing `mountRouter` (e.g. `\"@nifrajs/web-react/client\"`). */\n readonly clientModule: string;\n /**\n * Build the nifra app for the given dev client-entry URL.\n *\n * `load` resolves a route module through **Vite's** graph (`ssrLoadModule`). Pass it to\n * `discoverRoutes(routesDir, { load })` so SSR and the client are resolved by the SAME toolchain.\n * Without it SSR resolves through Bun while the client resolves through Vite - two resolvers, one\n * process - which is what makes `resolve.dedupe` fail to reach SSR and produces the dual-React\n * crash. Vite re-evaluates on change, so no `importQuery` cache-buster is needed alongside it.\n */\n readonly createApp: (clientEntry: string, load: (absolutePath: string) => Promise<unknown>) => FetchApp | Promise<FetchApp>;\n /** Vite plugins — inject your framework's official plugin, e.g. `[react()]`. */\n readonly plugins?: readonly unknown[];\n /**\n * Extra `resolve.conditions` prepended ahead of nifra's defaults — some frameworks need their own\n * (e.g. Solid's `\"solid\"` condition routes `solid-js` to its source/JSX-dev build).\n */\n readonly conditions?: readonly string[];\n /**\n * Compile-time `define` replacements (e.g. Vue's `__VUE_OPTIONS_API__` flags). Vite already sets\n * `process.env.NODE_ENV` in dev; this is for framework feature flags the plugin doesn't inject.\n */\n readonly define?: Readonly<Record<string, string>>;\n /** Vite project root (default `process.cwd()`). */\n readonly root?: string;\n /** Port (default {@link DEFAULT_DEV_PORT}). */\n readonly port?: number;\n /**\n * Use polling for the file watcher. Native fs events (fsevents/inotify) are unreliable inside\n * containers, networked filesystems, and some sandboxes — there, HMR silently never fires. Set\n * `true` (or the env var `CHOKIDAR_USEPOLLING=1`) to poll instead. Default: off (native events).\n */\n readonly poll?: boolean;\n /** Vite public directory. Defaults to `<root>/public`; `false` disables it. */\n readonly publicDir?: string | false;\n}"
11863
+ },
11864
+ {
11865
+ "name": "viteLeakGuard",
11866
+ "kind": "function",
11867
+ "package": "@nifrajs/web",
11868
+ "signature": "export declare function viteLeakGuard(): LeakGuardPlugin;",
11869
+ "doc": "/**\n * A Vite/Rollup plugin that fails the build when server-only code or a `node:` builtin reaches the client\n * bundle - the same two guards, and the same error messages, as nifra's Bun production build.\n */"
11300
11870
  },
11301
11871
  {
11302
11872
  "name": "vueAdapter",
@@ -11529,6 +12099,13 @@
11529
12099
  "signature": "export declare function writeDbFiles(target: string, choice: DbChoice): Promise<void>;",
11530
12100
  "doc": "/** Write a DB preset's files into `target` and ensure local DB artifacts + .env are gitignored. */"
11531
12101
  },
12102
+ {
12103
+ "name": "writeDevFiles",
12104
+ "kind": "function",
12105
+ "package": "@nifrajs/web",
12106
+ "signature": "export declare function writeDevFiles(options: WriteDevFilesOptions): void;",
12107
+ "doc": "/**\n * Generate the client entry + the HTML route that carries it.\n *\n * Route specifiers are written **relative to the entry file**, not root-relative as the Vite path writes\n * them: Vite resolves `/routes/x.tsx` against its configured root, while Bun's bundler resolves an import\n * the way the runtime does, so a leading slash there would mean the filesystem root.\n */"
12108
+ },
11532
12109
  {
11533
12110
  "name": "writeScaffoldRoute",
11534
12111
  "kind": "function",