/** Minimal app surface - `createWebApp(...)` satisfies it. */ interface FetchApp { fetch(request: Request): Response | Promise; } export interface ViteDevServerOptions { /** Absolute (or cwd-relative) path to the `routes/` dir. */ readonly routesDir: string; /** Client runtime module providing `mountRouter` (e.g. `"@nifrajs/web-react/client"`). */ readonly clientModule: string; /** * Build the nifra app for the given dev client-entry URL. * * `load` resolves a route module through **Vite's** graph (`ssrLoadModule`). Pass it to * `discoverRoutes(routesDir, { load })` so SSR and the client are resolved by the SAME toolchain. * Without it SSR resolves through Bun while the client resolves through Vite - two resolvers, one * process - which is what makes `resolve.dedupe` fail to reach SSR and produces the dual-React * crash. Vite re-evaluates on change, so no `importQuery` cache-buster is needed alongside it. */ readonly createApp: (clientEntry: string, load: (absolutePath: string) => Promise) => FetchApp | Promise; /** Vite plugins - inject your framework's official plugin, e.g. `[react()]`. */ readonly plugins?: readonly unknown[]; /** * Extra `resolve.conditions` prepended ahead of nifra's defaults - some frameworks need their own * (e.g. Solid's `"solid"` condition routes `solid-js` to its source/JSX-dev build). */ readonly conditions?: readonly string[]; /** * Compile-time `define` replacements (e.g. Vue's `__VUE_OPTIONS_API__` flags). Vite already sets * `process.env.NODE_ENV` in dev; this is for framework feature flags the plugin doesn't inject. */ readonly define?: Readonly>; /** Vite project root (default `process.cwd()`). */ readonly root?: string; /** Port (default {@link DEFAULT_DEV_PORT}). */ readonly port?: number; /** * Use polling for the file watcher. Native fs events (fsevents/inotify) are unreliable inside * containers, networked filesystems, and some sandboxes - there, HMR silently never fires. Set * `true` (or the env var `CHOKIDAR_USEPOLLING=1`) to poll instead. Default: off (native events). */ readonly poll?: boolean; /** Vite public directory. Defaults to `/public`; `false` disables it. */ readonly publicDir?: string | false; /** Client-visible environment prefix (default `"PUBLIC_"`; empty disables exposure). */ readonly publicEnvPrefix?: string; /** * Downgrade the startup identity-parity check from a hard failure to a loud warning (dev only). * The check catches two physical copies of an identity-sensitive package (e.g. React) resolving in * one process, which reliably breaks hydration and framework context. When a duplicate comes from a * linked sibling repo you cannot fix in the moment, set this to keep the dev server running while you * resolve it; `nifra build` never honors it. Wired to `nifra dev --allow-duplicate-identity`. */ readonly allowDuplicateIdentity?: boolean; } export interface ViteDevServer { readonly port: number; stop(): Promise; } export { LAST_ERROR_PATH } from "./diagnostic.js"; /** The bits of a Node ServerResponse `pipeWebBodyToNode` touches - structural, to avoid a node:http dep here. */ interface NodeResLike { flushHeaders?(): void; on(event: "close", cb: () => void): void; write(chunk: Uint8Array): boolean; end(): void; } /** Structural slice of a Node response for header writing. */ interface NodeHeaderSink { setHeader(name: string, value: string | readonly string[]): void; } /** * Copy a Web `Response`'s headers onto a Node response, emitting EACH `Set-Cookie` as its own header. The * `Headers` iterator (and `.get`) join multiple set-cookie values with ", ", which corrupts cookies - e.g. * better-auth's `session_token` + `session_data` collapse into one unparseable cookie and the session is * silently lost. `getSetCookie()` returns them split; Node's `setHeader` emits one header per array element. */ export declare function applyResponseHeaders(headers: Headers, res: NodeHeaderSink): void; /** * Stream a Web `Response` body to a Node response chunk-by-chunk. Buffering the whole body (e.g. * `arrayBuffer()`) waits for the stream to END - which an open-ended SSE (`text/event-stream`) body never * does, so it hung `nifra dev` (the Bun production server streamed it fine). This flushes each chunk as it * arrives and cancels the reader if the client disconnects; a finite body just streams its chunk(s) + ends. */ export declare function pipeWebBodyToNode(body: ReadableStream | null, res: NodeResLike): Promise; /** * Strip `optimizeDeps.rollupOptions.jsx` from a plugin's `config` hook output when running under * rolldown-vite - the source of the scary, harmless `Warning: Invalid input options … "jsx" Invalid * key: Expected never but received "jsx"` on `nifra dev`. * * Why it happens: `@vitejs/plugin-react@4.x` (and peers) target an *older* rolldown-vite optimizeDeps * API - they inject `optimizeDeps.rollupOptions.jsx` to tell the dep pre-bundler to transform JSX. Vite * 8's rolldown dep-optimizer renamed that surface to `optimizeDeps.rolldownOptions` (and moved jsx under * `transform.jsx`), so the stale `rollupOptions.jsx` is an unrecognized input option → the warning. It's * a version-skew artifact, not a real misconfig: the route source JSX transform runs through the * plugin's own `transform` hook (untouched here), and node_modules deps that get pre-bundled almost * never contain raw JSX - so dropping the dead key changes no behavior and keeps HMR/Fast Refresh * intact. We *strip* (rather than translate to `rolldownOptions`) so the fix is version-agnostic: a * plugin already emitting the correct `rolldownOptions` is left untouched, and a future plugin bump that * stops emitting `rollupOptions.jsx` makes this a no-op. * * Scoped narrowly: only the `optimizeDeps.rollupOptions.jsx` key is removed, only under rolldown-vite, * and only from the value a plugin's `config` hook returns. Non-rolldown Vite is passed through verbatim. * * FLATTEN FIRST: a Vite plugin factory may return an ARRAY of plugins - `@vitejs/plugin-react`'s `react()` * returns `[vite:react-babel, vite:react-refresh]`, and it's `react:react-babel`'s `config` hook that emits * the offending `optimizeDeps.rollupOptions.jsx`. `nifra.config.ts` writes `vitePlugins = [react()]`, so the * plugin list arrives NESTED (`[[babel, refresh]]`). Without flattening, `.map` sees the inner array (which * has no `config`), leaves it untouched, and Vite - which flattens plugin arrays itself before running them * - then executes the un-stripped babel hook, so the warning survives. Flattening here (Vite accepts a flat * list identically) is what lets the strip reach every real plugin. */ export declare function normalizeRolldownPlugins(plugins: readonly unknown[], isRolldown: boolean): readonly unknown[]; /** * Start the Vite-backed dev server: Vite serves/HMRs the client; nifra SSRs each request and Vite * injects its HMR client + the framework refresh preamble via `transformIndexHtml`. */ export declare function createViteDevServer(options: ViteDevServerOptions): Promise; //# sourceMappingURL=vite.d.ts.map