import type { EventEmitter } from "node:events"; /** * Setup-probe containment. * * Every IMAP/SMTP client created by a connection probe (email-setup, * email-status) is registered here. Containment has two layers: * * 1. Per-resource: a persistent 'error' listener attached for the client * object's full lifetime, so a late socket event can never become an * unhandled 'error' emit (which kills the process). * 2. Process-level last resort: uncaughtException/unhandledRejection * handlers that contain ONLY errors attributable to a probe (stack * references imapflow/nodemailer, or a probe is still unreleased). * Anything else preserves the pre-existing crash behaviour: dump and * exit 1. Containment is always logged loudly — it must never hide * the defect. */ export type ProbeSource = "imap" | "smtp"; /** Per-call correlation token for setup-probe log lines. */ export declare function newProbeToken(): string; /** Number of probe resources acquired and not yet released. */ export declare function activeProbeCount(): number; /** * Register a probe-created client. Attaches a persistent 'error' listener * (never removed — the listener dies with the client object) so any late * socket event is contained and logged instead of crashing the process. */ export declare function trackProbe(source: ProbeSource, emitter: EventEmitter, token: string): void; /** Mark a probe resource as closed. The census line is the leak signature. */ export declare function releaseProbe(source: ProbeSource, token: string): void; /** * Contain late errors from a probe client's underlying transport socket. * * trackProbe attaches the 'error' listener to the ImapFlow CLIENT, which catches * only errors ImapFlow forwards from the socket. But ImapFlow.close() removes its * own socket->client forwarder (clearSocketHandlers) before destroying the socket, * so a socket 'error' that fires during or after close() — a half-established TLS * socket reset on the auth-reject path — has no listener and crashes the process * via Node's unhandled-'error' rule. By then the per-call census has returned to * baseline and the raw-socket stack does not reference imapflow, so the * process-level guard does not attribute it either. * * Attaching a persistent no-op listener directly to the socket closes that window: * it survives close() (which removes only ImapFlow's own listeners, by reference) * and lives with the socket object until GC. Guarded for socket existence — on the * auth-reject path ImapFlow leaves the socket live, but a connect that never * established one is a no-op. */ export declare function containProbeSocket(client: unknown, source: ProbeSource, token: string): void; /** * Decide whether an escaped async error is attributable to a setup probe. * Returns true when contained; false means the caller must preserve the * default crash. Exported for tests; wired by installSetupProbeGuard(). */ export declare function handleEscapedError(err: unknown, channel: "uncaughtException" | "unhandledRejection"): boolean; /** * Install the process-level last-resort guard. Idempotent, never throws — * MCP module init must never throw (plugin-system metadata-only spawns). */ export declare function installSetupProbeGuard(): void; //# sourceMappingURL=setup-probe.d.ts.map