import { type AddRouteOptions } from '../router.js'; import type { LoggerConfig, LoggerService } from '../logger.js'; import type { ConfiguredModule, Constructor, Guard, HttpMethod, ListenAdapter, Middleware, ProviderDefinition, RequestContext } from '../types.js'; export interface MiiaOptions { logger?: LoggerService | LoggerConfig | false; /** * Register process signal handlers that gracefully call `destroy()` when * the process receives SIGTERM/SIGINT/SIGHUP. Registered on first `listen()` * call (not in the constructor) so embedded `app.fetch` use cases (e.g. for * serverless) do not pick up global handlers. * * - `true` (default) - register handlers for `['SIGTERM', 'SIGINT', 'SIGHUP']` * - `false` - do not register; user is responsible for calling `destroy()` * - `NodeJS.Signals[]` - custom subset of signals (empty array is equivalent * to `false` - no signals registered) * * On signal: logs `Received , shutting down gracefully...`, * awaits `destroy()`, exits with code 0 (or 1 if destroy throws). * Concurrent signals are guarded - only the first one initiates shutdown. * * Notes: * - `process.exit(0)` after `destroy()` cancels non-awaited timers and any * remaining open handles. Acceptable trade-off for deterministic exit. * - Pressing Ctrl+C twice: the first SIGINT triggers Miia shutdown, after * which the handler is gone (`process.once`); the second SIGINT goes to * the OS default action (process kill). This is conventional Unix * behaviour, not a bug. * - Existing user `process.on('SIGTERM', ...)` listeners co-exist with * Miia's; both fire on signal. Pass `shutdownHooks: false` to opt out * if you fully manage shutdown yourself. * - Windows: SIGHUP is not supported. The default array filters it out * silently on win32; SIGTERM/SIGINT still active. * - `TestApp` does not call `listen()`, so handlers are never registered * in tests - safe by construction. * - Multi-Miia (multiple instances in one process): all instances receive * the signal and start their own `destroy()` concurrently. The first to * reach `process.exit(0)` terminates the process; others' cleanup may * be interrupted mid-flight. Pass `shutdownHooks: false` on secondary * instances and have the primary tear them down manually before its * own `destroy()`. */ shutdownHooks?: boolean | NodeJS.Signals[]; /** * Maximum request body size in bytes for non-GET/HEAD requests. Per-route * `@BodyLimit()` overrides this (method > class > this option). `false` * disables the default limit and the adapter-level cap. * * Declared Content-Length is checked in core after route matching against * the per-route limit. A chunked body is counted as it arrives: core hands * the matched route's limit to whichever adapter owns the stream, and only * an adapter without that slot falls back to the ceiling, * `max(maxBodySize, all @BodyLimit values)` - enforced natively on Bun * (`maxRequestBodySize`) and via a counting stream wrapper on Deno. * * @default 1_048_576 (1 MiB) */ maxBodySize?: number | false; /** * Trust proxy headers when resolving `ctx.ip`. Enable only when behind a * trusted reverse proxy. * * - `true` - use the leftmost entry of `x-forwarded-for`. WARNING: the * leftmost element is forgeable if the proxy appends to XFF rather than * overwriting it - prefer your edge's vendor header instead. * - `string` / `string[]` - trusted header(s) in priority order, e.g. * `'cf-connecting-ip'` or `['cf-connecting-ip', 'x-real-ip']`. * * `ctx.conn.remoteAddress` is always the honest socket IP regardless of this. * * @default false */ trustProxy?: boolean | string | string[]; /** * Path prefix prepended to every route the app registers - controllers, * `app.addRoute()`, and `serveStatic()` alike. `'api'`, `'/api'` and `'/api/'` * are equivalent; a route registered as `/users` is then served at `/api/users`. * * The prefix is applied inside the router and never reaches `RESOLVED_PREFIX`, * so `@miiajs/swagger` does not see it: the spec keeps the clean paths * (`/users`), and swagger's own endpoints stay at their configured `path`/ * `uiPath`. Put the prefix into `servers` manually if the spec must carry it. * * `register()` and `addRoute()` resolve routes eagerly, so the prefix is baked * into the route table at registration time. Passing it here applies it to * every route the app will ever register. */ globalPrefix?: string; } /** * Top-level application entry point. * * **Graceful shutdown.** When `listen()` is called, Miia registers SIGTERM, * SIGINT, and SIGHUP handlers that gracefully call `destroy()` and * `process.exit(0)`. This ensures `onDestroy` lifecycle hooks (database * disconnects, broker cleanup, drain) run on Ctrl+C and on container * orchestrator stops (K8s, Docker, systemd). * * For embedded use cases (serverless via `app.fetch`, multi-Miia processes, * custom bootstrap), opt out via `new Miia({ shutdownHooks: false })` and * call `destroy()` manually. `TestApp` does not register handlers because * it never calls `listen()`. * * See `MiiaOptions.shutdownHooks` for full behavioural details. */ export declare class Miia { private container; private router; private globalMiddlewares; private globalGuards; private moduleLoader; private initialized; private initPromise?; private compiled; private compiledGlobalPipeline?; private closeServer?; private logger; private trustProxy; private shutdownHooksOption; private shutdownHandlersRegistered; private shutdownInProgress; /** * Tracks listeners we registered so destroy() can `process.off()` them * symmetrically. Without this, every Miia instance that calls listen() * leaks 3 process listeners (SIGTERM/SIGINT/SIGHUP) - test runs add up * fast and Node will warn MaxListenersExceededWarning after ~10 tests. */ private registeredShutdownHandlers; constructor(options?: MiiaOptions); use(...middlewares: Middleware[]): this; useGuard(...guards: Guard[]): this; register(...modules: (Constructor | ConfiguredModule)[]): this; provide(...providers: ProviderDefinition[]): this; addRoute(method: HttpMethod, path: string, handler: (ctx: RequestContext) => unknown, options?: AddRouteOptions): this; get(token: Constructor | string): T; fetch: (req: Request, env?: unknown) => Response | Promise; init(): Promise; private doInit; listen(port: number): Promise; listen(port: number, hostname: string): Promise; listen(port: number, adapter: ListenAdapter): Promise; listen(port: number, hostname: string, adapter: ListenAdapter): Promise; destroy(): Promise; private registerShutdownHandlers; private initAndFetch; private finalizeResponse; private handleWithPipeline; private handleWithGlobalPipeline; private finalizePipelineResult; private compilePipelines; private isLoggerService; /** * Rejects a body that declares too much, and hands the route's limit to the * adapter for one that declares nothing. * * A chunked body has no length to check, so the limit can only be enforced * as the bytes arrive - and only by whoever owns the stream. Adapters that * build the body themselves expose a `_bodyLimit` slot for exactly that; * the write narrows what they already hold, never widens it. A runtime whose * request is a real `Request` has no slot, and keeps the adapter ceiling. */ private checkBodyLimit; private handleError; } //# sourceMappingURL=app.d.ts.map