/** * Node HTTP convenience starter. * * Dynamically imports `@hono/node-server` so the SDK stays runtime-agnostic * — only Node consumers actually pull in the dep. For Bun / Deno / * Cloudflare, consumers go through `server.app.fetch` directly. */ import type { serve as nodeServe } from '@hono/node-server' import type { Hono } from 'hono' import type { RemoteServerHandle } from './handle.js' export async function startNodeServer(app: Hono, port = 3000): Promise { // Opaque specifier: the `./server` barrel re-exports this module, so worker // bundles (wrangler/esbuild) traverse it even though they never call it. A // literal import() would make them resolve the optional peer and fail. const nodeServerModule = '@hono/node-server' const { serve } = (await import(nodeServerModule)) as { serve: typeof nodeServe } // oxlint-disable-next-line no-explicit-any const server = serve({ fetch: app.fetch, port }) as any await new Promise((resolve, reject) => { if (server.listening) return resolve() server.once('listening', () => resolve()) // Without this, a bind failure (e.g. EADDRINUSE on a busy port) emits // `'error'` and never `'listening'`, leaving the Promise — and startup — // hung forever with no diagnostic. server.once('error', (err: Error) => reject(err)) }) const address = server.address() as { port: number; address: string } | null const actualPort = address?.port ?? port return { port: actualPort, close: () => new Promise((resolve, reject) => { server.close((err: Error | null) => (err ? reject(err) : resolve())) }), } }