/** * Start/stop helpers for the runtime HTTP API. * * This module owns the Fastify server lifecycle: binding the port, * flipping readiness states, and draining in-flight requests on shutdown * with a hard 15s timeout so teardown always proceeds. * * The plugin glue layer (S6 in `src/index.ts`) calls {@link startApi} during * `api.lifecycle.start` and {@link ApiHandle.close} during `api.lifecycle.stop`. * This module does NOT modify `src/index.ts`. */ import type { FastifyInstance } from "fastify"; import { type CreateApiDeps, type ApiArtifacts } from "./server.js"; import type { Logger } from "../utils/logger.js"; /** * Hard timeout for Fastify drain. * * After this many milliseconds, {@link stopApi} logs a warning and proceeds * to runtime teardown regardless of in-flight requests. 15s is enough * headroom for any notification delivery or close-resolver tick that may * be mid-flight on the underlying runtime. */ export declare const DRAIN_HARD_TIMEOUT_MS = 15000; /** * Handle returned by {@link startApi}. * * The plugin lifecycle layer holds this and calls `ready()` once runtimes * are confirmed healthy, then `close()` at shutdown. */ export interface ApiHandle { /** The bound Fastify instance (exposed for tests that need `fastify.inject`). */ fastify: FastifyInstance; /** * Flip readiness from `"starting"` to `"ok"`. * * Call immediately after {@link startApi} resolves unless you need a * deliberate warm-up window before accepting traffic. */ ready: () => void; /** * Drain in-flight requests and close the HTTP server. * * Sets readiness to `"draining"` (so `/health` starts returning 503), * then awaits `fastify.close()` with a hard {@link DRAIN_HARD_TIMEOUT_MS} * timeout. If the timeout fires, a warning is logged and teardown proceeds * without waiting further. * * Never throws — all errors are logged and swallowed so the caller's * teardown sequence is not interrupted. */ close: () => Promise; } /** * Start the HTTP API server. * * Calls {@link createApi} to build the Fastify instance, binds it to * `deps.apiConfig.host:deps.apiConfig.port`, and returns an {@link ApiHandle}. * * The server is NOT yet marked ready after this call — `/health` will return * 503 until the caller invokes `handle.ready()`. Call `ready()` immediately * after the runtimes are confirmed up, which in the plugin lifecycle is right * after `startApi` returns. * * A boot-time warning is logged when `deps.apiConfig.host !== "127.0.0.1"` so * any operator who opts into Docker port mapping sees it in the logs. * * @param deps Same dependencies passed to {@link createApi}: runtime handles, * validated API config, logger, and plugin version string. * @returns An {@link ApiHandle} whose `ready()` and `close()` methods drive * the lifecycle. * @throws Re-throws any `fastify.listen()` error (e.g. `EADDRINUSE`) so * the plugin lifecycle layer can perform its rollback and fail the * openclaw start cleanly. */ export declare function startApi(deps: CreateApiDeps): Promise; /** * Drain in-flight requests and close the HTTP server. * * Sets readiness to `"draining"` immediately so `/health` stops returning 200, * then races `fastify.close()` against {@link DRAIN_HARD_TIMEOUT_MS}. If the * hard timeout fires first, a warning is logged and a best-effort force-close * is issued (fire-and-forget). The function always resolves — it never rejects. * * @param artifacts The {@link ApiArtifacts} returned by `createApi`. * @param logger Logger for the startup/drain messages. */ declare function stopApi(artifacts: ApiArtifacts, logger: Logger): Promise; /** * Exported for tests (S8) so they can assert drain timeout behavior without * going through the full `startApi` path. * * @internal Not part of the public API surface — import only in `__tests__/`. */ export declare const __test_only: { stopApi: typeof stopApi; }; export {}; //# sourceMappingURL=lifecycle.d.ts.map