import type { Server as NodeHttpServer } from 'node:http'; import type { Server, WebSocketHandler } from 'bun'; import type { EcoPagesAppConfig } from '../../types/internal-types.js'; import type { ApiHandler, ErrorHandler, StaticRoute, EcopagesWebSocketHandler } from '../../types/public-types.js'; import { SharedServerAdapter } from '../shared/runtime/server-adapter.js'; import type { ServerAdapterResult } from '../abstract/server-adapter.js'; import type { StaticPreviewHost } from '../shared/runtime/static-preview-host.js'; import { ClientBridge } from './client-bridge.js'; import { HmrManager } from './hmr-manager.js'; type BunServerInstance = Server; type BunNativeServeOptions = Bun.Serve.Options; export type BunServerRoutes = Bun.Serve.Routes; export type BunServeAdapterServerOptions = Partial & { fetch(this: BunServerInstance, request: Request): Promise; }>; export type BunServeOptions = Omit & { fetch?: (this: BunServerInstance, request: Request, server: BunServerInstance) => Promise; websocket?: WebSocketHandler; }; /** * Construction parameters for the Bun server adapter. * * @remarks * Callers normally provide only the app-facing fields such as routes, handlers, * and `serveOptions`. The transport collaborators remain optional here because * `createBunServerAdapter()` fills in Bun-specific defaults before the concrete * adapter instance is created. */ export interface BunServerAdapterParams { appConfig: EcoPagesAppConfig; runtimeOrigin: string; serveOptions: BunServeAdapterServerOptions; apiHandlers?: ApiHandler[]; staticRoutes?: StaticRoute[]; errorHandler?: ErrorHandler; websocketHandlers?: Map>; options?: { watch?: boolean; }; hostOwnsDevClient?: boolean; deferRuntimeAssetSetup?: boolean; allowPortFallback?: boolean; hmrManager?: HmrManager; bridge?: ClientBridge; previewHost?: StaticPreviewHost; } export interface BunServerAdapterResult extends ServerAdapterResult { getServerOptions: (options?: { enableHmr?: boolean; }) => BunServeOptions; buildStatic: (options?: { preview?: boolean; force?: boolean; }) => Promise; servePreviewOnly: () => Promise; completeInitialization: (server?: BunServerInstance | null) => Promise; handleRequest: (request: Request) => Promise; attachUserWebSocketUpgrades: (server: NodeHttpServer, options?: { passthroughUnmatched?: boolean; }) => void; dispose: () => Promise; } /** * Bun transport adapter that wires shared Ecopages request handling onto a live * `Bun.serve()` runtime. * * @remarks * The adapter owns Bun-specific concerns that do not exist in the shared server * abstraction: websocket-backed HMR transport, runtime plugin registration, and * preview-host startup for static builds. Routing, rendering, and response * composition still delegate to the shared server adapter base. */ export declare class BunServerAdapter extends SharedServerAdapter { appConfig: EcoPagesAppConfig; options: BunServerAdapterParams['options']; serveOptions: BunServeAdapterServerOptions; protected apiHandlers: ApiHandler[]; protected staticRoutes: StaticRoute[]; protected errorHandler?: ErrorHandler; private bridge; hmrManager: HmrManager; private initializationPromise; private fullyInitialized; serverInstance: BunServerInstance | null; private projectWatcher; private adapterDisposed; private readonly deferRuntimeAssetSetup; private readonly allowPortFallback; private readonly previewHost; /** * Reference to the application-level WebSocket handlers map. * * @remarks * This is a reference to the map owned by `AbstractApplicationAdapter`, * passed in via the constructor. The Bun adapter reads from it to wire * WebSocket upgrades for user-registered patterns. */ protected websocketHandlers: Map>; private registerBunRuntimePlugin; private adaptBunWebSocket; /** * Resolves the per-connection context for a Bun user connection. * * @remarks * `context()` is invoked exactly once per accepted connection. Its * resolved value is shared by all subsequent lifecycle hooks. If * `context()` throws, the connection is closed immediately with code * 1011 (server error) and the error is logged. * * @param request - The original upgrade request (best-effort reconstructed) * @param handler - The registered handler * @param kind - The registered route pattern * @param params - Dynamic path parameters * @param search - Query string parameters * @returns The resolved per-connection context */ private resolveBunContext; /** * Creates a Bun server adapter with already-resolved runtime collaborators. * * @remarks * The public params interface keeps `hmrManager`, `bridge`, and `previewHost` * optional so factory callers can omit them. By the time the concrete adapter * is constructed, those collaborators are mandatory because the adapter cannot * initialize Bun HMR or preview flows without them. */ constructor({ appConfig, runtimeOrigin, serveOptions, apiHandlers, staticRoutes, errorHandler, websocketHandlers, options, hostOwnsDevClient, deferRuntimeAssetSetup, allowPortFallback, hmrManager, bridge, previewHost, }: BunServerAdapterParams & { hmrManager: HmrManager; bridge: ClientBridge; previewHost: StaticPreviewHost; }); /** * Wires user WebSocket routes onto a Node HTTP server used by host integrations. */ attachUserWebSocketUpgrades(server: NodeHttpServer, options?: { passthroughUnmatched?: boolean; }): void; /** * Initializes the server adapter's core runtime components. */ initialize(): Promise; /** * Registers runtime plugins and propagates the final HMR manager into each * integration. * * @remarks * This is where Bun's runtime-plugin registration path meets the integration * lifecycle. A failure here leaves the runtime partially bootstrapped, so the * method logs the underlying error and rethrows instead of trying to limp on. */ private initializeRuntimePlugins; /** * Rebuilds the shared routing state and hot-reloads the live Bun server when a * watched route file changes. */ private refreshRouterRoutes; private watch; /** * Builds the `Bun.serve()` options for the current adapter state. * * @remarks * When HMR is enabled the websocket dispatcher merges HMR and user handlers, * routing by the `kind` field embedded in socket data at upgrade time. * This prevents user-registered websocket handlers from being silently overwritten * by the HMR handler in development mode. * * User WebSocket paths are intercepted in the fetch handler via the pattern matcher. * The upgrade happens implicitly — no manual GET route registration needed. */ getServerOptions({ enableHmr }?: { enableHmr?: boolean | undefined; }): BunServeOptions; private createBunUserLifecycle; /** * @remarks * HMR and user sockets share one Bun `websocket` handler. Upgrade tags HMR * connections with `kind: '__hmr__'`; everything else routes to the user lifecycle. */ private createHmrAwareWebSocketHandler; /** * @remarks * When `serveHmrEndpoints` is set, `/_hmr` is checked before user websocket * patterns so HMR upgrades are never captured by app routes. Runtime assets are * served by `SharedServerAdapter.handleSharedRequest` via `tryHandleAssetRequest`. * Production mode with only user handlers skips the HMR branch entirely. */ private wrapFetchWithWebSocketUpgrades; /** * Composes the base Bun server settings that all runtime modes build from. * * @remarks * This method centralizes the Bun-specific error boundary. It preserves the * shared route pipeline while still allowing adapter-level custom error-handler * execution and `HttpError` passthrough. */ private buildServerSettings; /** * Generates a static build of the site for deployment. * @param options.preview - If true, starts a preview server after build */ buildStatic(options?: { preview?: boolean; force?: boolean; }): Promise; /** * Serves an existing static export without running SSG. Used by e2e preview * launchers after a shared prewarm build. */ servePreviewOnly(): Promise; private startPreviewServer; /** * Initializes the server with dynamic routes after server creation. * Must be called before handling any requests. * @param server - The Bun server instance */ completeInitialization(server?: BunServerInstance | null): Promise; /** * Performs the one-time post-bind initialization path for Bun servers. * * @remarks * This is intentionally split from `initialize()` because shared route handling * and file watching need the live server instance to exist before Bun can * reload updated route handlers in place. */ private _performInitialization; /** * Creates and initializes the Bun server adapter. * @returns Configured adapter with server methods */ createAdapter(): Promise; /** * Releases dev-time resources owned by the adapter. * * @remarks * Safe to call multiple times. Does not stop the bound Bun server — callers * should shut down transport through the runtime host before disposing. */ dispose(): Promise; /** * Handles HTTP requests by passing them securely to the shared core router adapter. * * @remarks * HMR HTML injection for API and page responses is owned by * `SharedServerAdapter.handleSharedRequest`. This method only maps the * request into the shared pipeline. */ handleRequest(request: Request): Promise; /** * Ensures server initialization completes before request handling. * Prevents race conditions during startup. */ private waitForInitialization; /** * Handles HTTP requests from the router adapter. */ handleResponse(request: Request): Promise; /** * Handles requests that do not match any routes. */ private handleNoMatch; } /** * Creates the Bun server adapter and fills in the runtime-specific collaborators * that Bun callers usually leave implicit. * * @remarks * This is the canonical entry point for Bun server-adapter construction. It * guarantees that the concrete adapter receives a Bun websocket bridge, HMR * manager, and preview host even though those dependencies are optional on the * public params type. */ export declare function createBunServerAdapter(params: BunServerAdapterParams): Promise; export {};