import { type BuildClientOptions } from "./build.js"; export { LAST_ERROR_PATH } from "./diagnostic.js"; /** Minimal app surface the dev server needs - `createWebApp(...)` satisfies it. */ interface FetchApp { fetch(request: Request): Response | Promise; } export interface DevServerOptions extends Omit { /** * Build the nifra app for the current client entry. `importQuery` changes on every reload - pass it to * `discoverRoutes(routesDir, { importQuery })` so SSR re-imports edited route modules instead of Bun's * cached copies. */ readonly createApp: (clientEntry: string, importQuery: string) => FetchApp | Promise; /** Directories to watch (default: `[routesDir]`). */ readonly watch?: readonly string[]; /** Port to listen on (default {@link DEFAULT_DEV_PORT}). */ readonly port?: number; /** Directory of user-authored static files served at the root (default `"public"`). The SAME * option the production build copies and serves, so dev and prod cannot drift. */ readonly publicDir?: string | false; /** * Run the client-leak guards on each change (default `true`). * * `buildClient` is what runs them - server-only code reaching the browser, `node:` builtins in client * code - and Bun's dev server does its own bundling, so nothing would run them otherwise. They are * security guards; a dev loop that stops enforcing them is how a leak reaches a deploy unnoticed. The * pass runs in the background off the hot path, so HMR is never waiting on it, and only reports. */ readonly guardLeaks?: boolean; } export interface DevServer { readonly port: number; /** * The URL pages point their client entry at - always {@link CLIENT_ENTRY_PATH}, never Bun's hashed * chunk. Deliberately not the underlying chunk URL: that one moves on every rebuild, and anything * holding onto it is holding onto a URL that will stop working. */ readonly clientEntry: string; stop(): void; } /** * The stable URL every SSR'd page points its client entry at. * * It has to be stable, and Bun's own URL is not: that one is a content hash over the entire client graph, * so it changes whenever anything the entry imports changes. This path redirects to whichever hashed * chunk Bun is serving at the moment the browser asks, which is the only formulation that stays correct * across a rebuild. */ export declare const CLIENT_ENTRY_PATH = "/__nifra/client.js"; /** * The throwaway HTML document. Its only job is to make Bun bundle the entry and assign it a URL - it is * never shown to a user, so it carries no app markup. The `
` is there purely so the * document stands on its own if someone opens the probe path directly while debugging. */ export declare function devHtml(entryHref: string): string; interface WriteDevFilesOptions { readonly routesDir: string; readonly clientModule: string; readonly entryPath: string; readonly htmlPath: string; } /** * Generate the client entry + the HTML route that carries it. * * Route specifiers are written **relative to the entry file**, not root-relative as the Vite path writes * them: Vite resolves `/routes/x.tsx` against its configured root, while Bun's bundler resolves an import * the way the runtime does, so a leading slash there would mean the filesystem root. */ export declare function writeDevFiles(options: WriteDevFilesOptions): void; /** `` tags for Bun's extracted CSS, injected into each SSR'd page's ``. */ export declare function styleTags(styles: readonly string[]): string; /** * Inject Bun's stylesheet links into an SSR'd document. * * Prefers ``; falls back to prepending when a document has no head (a bare fragment from a custom * renderer). Never appends blindly at the end - a stylesheet after `` still applies but arrives * after first paint, so the page flashes unstyled and dev stops resembling production. */ export declare function injectStyles(html: string, styles: readonly string[]): string; /** Start the Bun dev server: generate → bundle → serve → watch → hot-reload on change. */ export declare function createDevServer(options: DevServerOptions): Promise; /** * A debounced, background client-leak check. * * Bun's dev server does its own bundling, so `buildClient` - which is where `detectServerOnlyInClient` * and `detectNodeBuiltinsInClient` run - is no longer on the path that serves the app. Running it beside * the dev loop keeps the guards enforced without HMR ever waiting on a full bundle. It only reports: * failing the dev server on a leak would mean an in-progress edit can take the whole server down, and * the build already blocks the actual deploy. * * Overlapping runs are collapsed - a save during a bundle queues exactly one re-run, so a burst of edits * cannot pile up builds behind each other. */ /** * Format a client-build rejection for the guard's report. Bun.build rejects with an AggregateError * whose message is just "Bundle failed" - the actionable part (which file, which import) lives in * `.errors`. Surface it, or the guard reports a failure while hiding the reason. */ export declare function buildFailureDetail(err: unknown): string; //# sourceMappingURL=dev.d.ts.map