@cosyte/mllp 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/framing/registry.ts","../src/framing/error.ts","../src/framing/constants.ts","../src/framing/encoder.ts","../src/framing/decoder.ts","../src/transport/net-transport.ts","../src/transport/tls-transport.ts","../src/transport/security-warnings.ts","../src/connection/error.ts","../src/connection/connection.ts","../src/internal/control-id.ts","../src/server/ack.ts","../src/internal/safe-emit.ts","../src/server/server.ts","../src/client/correlator.ts","../src/client/ack-diagnostics.ts","../src/client/error.ts","../src/client/client.ts","../src/index.ts"],"names":["snippet","randomUUID","s","EventEmitter","tlsCreateServer","netCreateServer","socket","tlsConnect"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * MLLP warning code registry and `MllpWarning` factory.\n *\n * `WarningCode` is a **stable public API**. Renaming or removing a code is a breaking change.\n * New codes may only be added in minor versions.\n *\n * @example\n * ```typescript\n * import { createWarning, type MllpWarning, type WarningCode } from '@cosyte/mllp';\n * const w: MllpWarning = createWarning('MLLP_EMPTY_PAYLOAD', 128, 'Empty payload between VT and FS');\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Union of all stable MLLP warning codes.\n *\n * These codes are a **public API**, they appear in `onWarning` handlers, log pipelines,\n * monitoring dashboards, and error messages. Renaming is a breaking change.\n *\n * @example\n * ```typescript\n * const code: WarningCode = 'MLLP_FS_WITHOUT_CR';\n * ```\n */\nexport type WarningCode =\n | \"MLLP_MISSING_LEADING_VT\"\n | \"MLLP_FS_WITHOUT_CR\"\n | \"MLLP_LF_AFTER_FS\"\n | \"MLLP_LEADING_WHITESPACE\"\n | \"MLLP_TRAILING_BYTES\"\n | \"MLLP_PAYLOAD_CONTAINS_VT\"\n | \"MLLP_PAYLOAD_CONTAINS_FS\"\n | \"MLLP_EMPTY_PAYLOAD\"\n | \"MLLP_FRAME_TOO_LARGE\"\n | \"MLLP_ACK_UNMATCHED_CONTROL_ID\"\n | \"MLLP_ACK_AFTER_TIMEOUT\";\n\n/**\n * A frozen warning object emitted when the decoder tolerates a framing deviation.\n *\n * `connectionId` is `undefined` when emitted by a standalone `FrameReader`.\n * `Connection` enriches it to the real UUIDv4 before forwarding upstream.\n *\n * @example\n * ```typescript\n * const reader = new FrameReader({\n * onFrame: (p) => process(p),\n * onWarning: (w: MllpWarning) => logger.warn(w),\n * allowFsOnly: true,\n * });\n * ```\n */\nexport interface MllpWarning {\n readonly code: WarningCode;\n /**\n * Stable human-readable description. It carries structural facts (a byte\n * offset, an accumulated size) and **never a run of payload content**.\n *\n * Two codes carry exactly one input byte, and it is worth knowing which:\n * `MLLP_MISSING_LEADING_VT` and `MLLP_FS_WITHOUT_CR` render the hex of the\n * single byte found where a framing byte was expected, and on a stream that\n * omits its leading `VT` that byte is the first byte of the unframed content.\n * One byte, never a run, the same bound `MllpFramingError.snippet` carries. If\n * that is more than your threat model allows, log `code` and `byteOffset`\n * rather than `message`.\n *\n * The ACK-correlation codes are stricter: their text comes from a frozen\n * registry and does not vary with the wire at all.\n */\n readonly message: string;\n /** Absolute stream byte offset where the anomaly was detected. */\n readonly byteOffset: number;\n /**\n * Connection identifier. `undefined` at the framing layer;\n * enriched by `Connection` before emitting upstream.\n */\n readonly connectionId: string | undefined;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n\n/**\n * Callback type for receiving MLLP framing warnings.\n *\n * @example\n * ```typescript\n * const onWarning: OnWarning = (w) => logger.warn({ code: w.code, offset: w.byteOffset });\n * ```\n */\nexport type OnWarning = (warning: MllpWarning) => void;\n\n/**\n * Create a frozen `MllpWarning` object. The returned object is `Object.freeze()`'d\n * so subscribers cannot mutate shared warning state.\n *\n * `connectionId` is always `undefined` here; `Connection` enriches it via\n * `{ ...w, connectionId: this.connectionId }` then re-freezes.\n *\n * @example\n * ```typescript\n * const w = createWarning('MLLP_EMPTY_PAYLOAD', 64, 'Empty payload between VT and FS');\n * // w.connectionId === undefined\n * ```\n */\nexport function createWarning(code: WarningCode, byteOffset: number, message: string): MllpWarning {\n return Object.freeze<MllpWarning>({\n code,\n message,\n byteOffset,\n connectionId: undefined,\n timestamp: new Date(),\n });\n}\n","/**\n * Typed framing error class for unrecoverable MLLP wire-format violations.\n *\n * @example\n * ```typescript\n * import { MllpFramingError } from '@cosyte/mllp';\n * try {\n * encodeFrame(payloadWithVt);\n * } catch (err) {\n * if (err instanceof MllpFramingError) {\n * logger.error({ code: err.code, offset: err.byteOffset });\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { WarningCode } from \"./registry.js\";\n\n/** Maximum number of bytes captured in the `snippet` field. */\nconst MAX_SNIPPET_BYTES = 64;\n\n/**\n * Thrown for unrecoverable MLLP wire-format violations.\n *\n * - `code`, stable `WarningCode` identifying the violation\n * - `byteOffset`, absolute stream position where the violation was detected\n * - `snippet`, the single framing-boundary byte that broke the structure, or empty when the\n * anomaly is not a specific byte. **Never a run of payload content** (see the `snippet` PHI\n * contract below); the constructor caps whatever it is given to 64 bytes as a backstop.\n *\n * @example\n * ```typescript\n * // Pass only the offending framing-boundary byte, never a slice of payload content.\n * throw new MllpFramingError('MLLP_MISSING_LEADING_VT', byteOffset, Buffer.from([byte]));\n * ```\n */\nexport class MllpFramingError extends Error {\n override readonly name = \"MllpFramingError\" as const;\n\n /** Stable warning code identifying the violation type. */\n readonly code: WarningCode;\n\n /** Absolute byte offset in the stream where the error was detected. */\n readonly byteOffset: number;\n\n /**\n * Up to 64 bytes copied from around the anomaly.\n *\n * This is a **copied** Buffer, isolated from the source buffer so it remains\n * valid after the underlying buffer is reused or overwritten.\n *\n * **PHI contract:** the decoder only ever populates this with the single\n * framing-boundary byte that violated the frame structure (whose hex value the\n * `message` already discloses), never a run of payload content bytes. For anomalies\n * whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE`, the accumulated size),\n * the snippet is **empty**: a payload slice on a public error field would leak a\n * field-body slice of clinical content. Callers constructing this directly are\n * responsible for the same discipline.\n */\n readonly snippet: Buffer;\n\n /**\n * Construct an MLLP framing error.\n *\n * @param code - The stable warning code classifying the violation.\n * @param byteOffset - Absolute stream byte offset where the violation was detected.\n * @param snippet - Raw bytes copied from around the anomaly (capped to 64 bytes).\n * @param message - Optional human-readable override message.\n */\n constructor(\n code: WarningCode,\n byteOffset: number,\n /** Raw bytes around the anomaly. Capped to MAX_SNIPPET_BYTES (64). */\n snippet: Buffer,\n message?: string,\n ) {\n super(message ?? `MLLP framing error: ${code} at byte offset ${byteOffset}`);\n this.code = code;\n this.byteOffset = byteOffset;\n // Copy and cap, error must outlive source buffer; never store a view.\n this.snippet = Buffer.from(snippet.subarray(0, MAX_SNIPPET_BYTES));\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpFramingError);\n }\n }\n}\n","/**\n * MLLP framing byte constants.\n *\n * All byte values match the MLLP v1 wire protocol as specified in HL7 appendix C.\n *\n * @example\n * ```typescript\n * import { VT, FS, CR } from '@cosyte/mllp';\n * // VT = 0x0B, FS = 0x1C, CR = 0x0D\n * ```\n *\n * @packageDocumentation\n */\n\n/** Vertical Tab (0x0B), marks the start of an MLLP frame. */\nexport const VT = 0x0b;\n\n/** File Separator (0x1C), marks the end of MLLP payload bytes. */\nexport const FS = 0x1c;\n\n/** Carriage Return (0x0D), terminates the FS byte; together FS+CR ends the frame. */\nexport const CR = 0x0d;\n\n/** Line Feed (0x0A), used by the MLLP_LF_AFTER_FS tolerance. */\nexport const LF = 0x0a;\n\n/**\n * Default maximum accumulated payload size in bytes (16 MiB).\n *\n * Exceeding this limit throws `MllpFramingError('MLLP_FRAME_TOO_LARGE')`.\n * Satisfies the DoS-prevention requirement on accumulator growth.\n */\nexport const DEFAULT_MAX_FRAME_SIZE = 16 * 1024 * 1024;\n","/**\n * MLLP frame encoder, canonical `VT + payload + FS + CR` wrapping.\n *\n * The encoder is a **strict emitter** (Postel's Law: conservative on write).\n * There is no option to loosen the output format, it always emits canonical framing.\n * Tolerance for delimiter bytes in the payload is available via `allowDelimiterBytesInPayload`,\n * which passes bytes through verbatim and emits an `MllpWarning` for each offending byte.\n *\n * @example\n * ```typescript\n * import { encodeFrame } from '@cosyte/mllp';\n * const frame = encodeFrame(Buffer.from('MSH|^~\\\\&|...'));\n * // frame[0] === 0x0B (VT), frame[frame.length - 2] === 0x1C (FS), frame[frame.length - 1] === 0x0D (CR)\n * ```\n *\n * @packageDocumentation\n */\n\nimport { VT, FS, CR } from \"./constants.js\";\nimport { MllpFramingError } from \"./error.js\";\nimport { createWarning } from \"./registry.js\";\nimport type { MllpWarning } from \"./registry.js\";\n\n/**\n * Options for {@link encodeFrame}.\n *\n * @example\n * ```typescript\n * const frame = encodeFrame(payload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn(w),\n * });\n * ```\n */\nexport interface EncoderOptions {\n /**\n * When `true`, VT (0x0B) or FS (0x1C) bytes within the payload are preserved\n * verbatim in the output frame instead of throwing `MllpFramingError`.\n * An `MllpWarning` is emitted per offending byte if `onWarning` is provided.\n *\n * Default: `false` (strict, throws on delimiter bytes).\n */\n allowDelimiterBytesInPayload?: boolean;\n\n /**\n * Called for each offending delimiter byte when `allowDelimiterBytesInPayload` is `true`.\n * Invocation is wrapped in try/catch, a throwing handler does not interrupt encoding.\n *\n * @example\n * ```typescript\n * const frame = encodeFrame(payload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn({ code: w.code, offset: w.byteOffset }),\n * });\n * ```\n */\n onWarning?: (w: MllpWarning) => void;\n}\n\n/**\n * Encode `payload` in canonical MLLP framing: `VT (0x0B) + payload + FS (0x1C) + CR (0x0D)`.\n *\n * Strict by default: throws `MllpFramingError` if the payload contains VT or FS bytes.\n * Set `{ allowDelimiterBytesInPayload: true }` to pass those bytes through verbatim\n * and receive an `MllpWarning` per offending byte via `onWarning` instead.\n *\n * The returned Buffer has length `payload.length + 3`. Bytes at positions 1 through\n * `payload.length` are an exact copy of the payload (independent of the input buffer).\n *\n * @throws {MllpFramingError} with code `MLLP_PAYLOAD_CONTAINS_VT` when payload contains\n * byte `0x0B` and `allowDelimiterBytesInPayload` is `false` (default).\n * @throws {MllpFramingError} with code `MLLP_PAYLOAD_CONTAINS_FS` when payload contains\n * byte `0x1C` and `allowDelimiterBytesInPayload` is `false` (default).\n *\n * @example\n * ```typescript\n * import { encodeFrame } from '@cosyte/mllp';\n *\n * // Strict (default), throws on delimiter bytes in payload\n * const frame = encodeFrame(Buffer.from('MSH|^~\\\\&|SEND|FAC|RECV|FAC|...'));\n * socket.write(frame);\n *\n * // Tolerant, passes delimiter bytes through with a warning\n * const frame2 = encodeFrame(dirtyPayload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn(w),\n * });\n * ```\n */\nexport function encodeFrame(payload: Buffer, opts?: EncoderOptions): Buffer {\n const allow = opts?.allowDelimiterBytesInPayload === true;\n const onWarning = opts?.onWarning;\n\n // Scan for delimiter bytes, O(n) but required to ensure correct framing (FRAME-03).\n for (let i = 0; i < payload.length; i++) {\n const byte = payload[i];\n if (byte === undefined) break; // noUncheckedIndexedAccess guard\n\n if (byte === VT || byte === FS) {\n const code = byte === VT ? \"MLLP_PAYLOAD_CONTAINS_VT\" : \"MLLP_PAYLOAD_CONTAINS_FS\";\n\n if (!allow) {\n // PHI: the snippet must NOT carry the surrounding payload bytes, that is a\n // field-body slice of clinical content on a public error field (MLLP-9 PHI\n // audit, mirroring the decoder's MLLP_FRAME_TOO_LARGE fix). The offending byte\n // is itself a framing delimiter (VT/FS, a control byte, never PHI); the `code`\n // already names which and the offset is on the error, so the snippet is just\n // that one boundary byte.\n throw new MllpFramingError(code, i, Buffer.from([byte]));\n }\n\n // Tolerant path: emit warning and continue, bytes pass through verbatim.\n if (onWarning !== undefined) {\n const byteHex = byte === VT ? \"0x0B\" : \"0x1C\";\n const warning = createWarning(\n code,\n i,\n `Payload contains ${byte === VT ? \"VT\" : \"FS\"} byte (${byteHex}) at offset ${i}; frame will be ambiguous`,\n );\n try {\n onWarning(warning);\n } catch {\n // WARN-06: a throwing handler must not interrupt encoding.\n }\n }\n }\n }\n\n // Allocate output: VT + payload + FS + CR, canonical, immutable shape (FRAME-03).\n // allocUnsafe is safe here because all bytes are set before the buffer is returned.\n const frame = Buffer.allocUnsafe(payload.length + 3);\n frame[0] = VT;\n payload.copy(frame, 1);\n frame[payload.length + 1] = FS;\n frame[payload.length + 2] = CR;\n return frame;\n}\n","/**\n * MLLP frame decoder, stateful 3-state FSM for chunked byte-stream parsing.\n *\n * Feed raw TCP chunks via `push(chunk)`. Complete MLLP frames fire synchronously\n * via the `onFrame` callback during each `push()` call.\n *\n * The decoder is **strict by default**: any framing deviation throws `MllpFramingError`\n * unless the matching tolerance option is explicitly enabled (Postel's Law, liberal receiver\n * means explicit tolerance, not silent acceptance).\n *\n * @example\n * ```typescript\n * import { FrameReader } from '@cosyte/mllp';\n * const reader = new FrameReader({\n * onFrame: (payload) => handleMessage(payload),\n * onWarning: (w) => logger.warn(w),\n * allowFsOnly: true,\n * });\n * socket.on('data', (chunk) => reader.push(chunk));\n * socket.on('close', () => reader.reset());\n * ```\n *\n * @packageDocumentation\n */\n\nimport { VT, FS, CR, LF, DEFAULT_MAX_FRAME_SIZE } from \"./constants.js\";\nimport { MllpFramingError } from \"./error.js\";\nimport { createWarning } from \"./registry.js\";\nimport type { MllpWarning, WarningCode } from \"./registry.js\";\n\n/** Internal FSM states. */\ntype ReaderState = \"SCANNING_FOR_VT\" | \"READING_PAYLOAD\" | \"EXPECTING_CR\";\n\n/** Initial accumulator buffer size (4 KiB). Grows by doubling as needed. */\nconst INITIAL_ACCUMULATOR_SIZE = 4096;\n\n/**\n * Shared empty snippet for framing errors whose anomaly is not a specific byte (e.g.\n * `MLLP_FRAME_TOO_LARGE`, where the fault is the accumulated size, not any content byte).\n * Emitting a run of accumulated payload bytes here would leak a field-body slice of clinical\n * content onto a public error field.\n */\nconst EMPTY_SNIPPET = Buffer.alloc(0);\n\n/**\n * Options for `FrameReader`.\n *\n * All tolerance opts default to `false`, every framing deviation throws unless\n * explicitly enabled. Server-level defaults (allowFsOnly, allowLfAfterFs, allowLeadingWhitespace)\n * are applied by the server when constructing readers.\n *\n * @example\n * ```typescript\n * const opts: FrameReaderOptions = {\n * onFrame: (payload, byteOffset, warnings) => process(payload, byteOffset, warnings),\n * onWarning: (w) => logger.warn(w),\n * maxFrameSizeBytes: 4 * 1024 * 1024, // 4 MiB limit\n * allowFsOnly: true,\n * allowLfAfterFs: true,\n * };\n * ```\n */\nexport interface FrameReaderOptions {\n /**\n * Called synchronously during `push()` for each complete MLLP payload.\n *\n * @param payload - Raw MLLP payload bytes (framing stripped).\n * @param byteOffset - Stream byte offset of the VT byte that opened this frame.\n * Monotonic across the connection lifetime; reset to 0 by `reset()`.\n * @param warnings - Framing warnings emitted during decoding of this specific frame.\n * Empty array when no tolerance deviations were detected.\n */\n onFrame: (payload: Buffer, byteOffset: number, warnings: readonly MllpWarning[]) => void;\n /**\n * Called for each tolerated framing deviation. Wrapped in try/catch.\n * A throwing handler will not corrupt FSM state.\n */\n onWarning?: (w: MllpWarning) => void;\n /**\n * Maximum accumulated payload bytes before `MllpFramingError('MLLP_FRAME_TOO_LARGE')`.\n * Default: 16 MiB (DoS prevention).\n */\n maxFrameSizeBytes?: number;\n /** Tolerate FS without trailing CR; emits `MLLP_FS_WITHOUT_CR`. */\n allowFsOnly?: boolean;\n /** Tolerate FS+LF instead of FS+CR; emits `MLLP_LF_AFTER_FS`. */\n allowLfAfterFs?: boolean;\n /** Tolerate missing leading VT; emits `MLLP_MISSING_LEADING_VT`. */\n allowMissingLeadingVt?: boolean;\n /** Tolerate SP/TAB/LF/CR before VT; emits `MLLP_LEADING_WHITESPACE`. */\n allowLeadingWhitespace?: boolean;\n /**\n * When `true`, escalates the following tolerances to thrown `MllpFramingError`\n * even if individual opt-ins (`allowFsOnly`, `allowLfAfterFs`, `allowMissingLeadingVt`,\n * `allowLeadingWhitespace`) are enabled:\n * - `MLLP_MISSING_LEADING_VT`\n * - `MLLP_FS_WITHOUT_CR`\n * - `MLLP_LF_AFTER_FS`\n * - `MLLP_LEADING_WHITESPACE` (leading whitespace escalates as `MLLP_MISSING_LEADING_VT`)\n *\n * `MLLP_EMPTY_PAYLOAD` and `MLLP_TRAILING_BYTES` remain warnings even in strict mode.\n *\n * @example\n * ```typescript\n * // Hardened enforcement, no tolerance, all violations throw\n * const reader = new FrameReader({ onFrame: fn, strict: true });\n * ```\n */\n strict?: boolean;\n}\n\n/**\n * Stateful MLLP frame decoder. Feed raw TCP byte chunks via `push(chunk)`.\n * Complete frames fire synchronously via `onFrame` callback during `push()`.\n *\n * The decoder operates as a 3-state FSM:\n * - `SCANNING_FOR_VT`, waiting for the 0x0B frame-start byte\n * - `READING_PAYLOAD`, accumulating payload bytes until FS (0x1C)\n * - `EXPECTING_CR`, received FS, waiting for CR (0x0D) to complete the frame\n *\n * @example\n * ```typescript\n * const reader = new FrameReader({\n * onFrame: (payload, byteOffset, warnings) => process(payload, byteOffset, warnings),\n * onWarning: (w) => logger.warn(w),\n * });\n * socket.on('data', (chunk) => reader.push(chunk));\n * ```\n */\nexport class FrameReader {\n private readonly _opts: FrameReaderOptions;\n private readonly _maxFrameSize: number;\n\n private _state: ReaderState = \"SCANNING_FOR_VT\";\n /** Reusable accumulator buffer, grown by doubling as needed. */\n private _accumulator: Buffer = Buffer.allocUnsafe(INITIAL_ACCUMULATOR_SIZE);\n /** Number of payload bytes written to accumulator in current frame. */\n private _writePos = 0;\n /** Monotonic absolute stream byte offset across the entire connection lifetime. */\n private _byteOffset = 0;\n /** Byte offset where current leading-whitespace run started. */\n private _wsStart = 0;\n /** Count of leading whitespace bytes accumulated in current run. */\n private _wsCount = 0;\n /** Byte offset of the VT byte that started the current frame. */\n private _frameStartOffset = 0;\n /** Per-frame warning accumulator, cleared after each _deliverFrame(). */\n private _frameWarnings: MllpWarning[] = [];\n\n /**\n * Construct a chunked MLLP frame reader.\n *\n * @param opts - Reader options (`onFrame`/`onWarning` callbacks, tolerance, `maxFrameSizeBytes`).\n */\n constructor(opts: FrameReaderOptions) {\n this._opts = opts;\n this._maxFrameSize =\n opts.maxFrameSizeBytes !== undefined ? opts.maxFrameSizeBytes : DEFAULT_MAX_FRAME_SIZE;\n }\n\n /**\n * Feed a chunk of raw TCP bytes into the FSM. Frames fire synchronously via `onFrame`\n * during this call. May throw `MllpFramingError` for unrecoverable framing violations\n * when the matching tolerance is not enabled.\n *\n * @example\n * ```typescript\n * socket.on('data', (chunk) => reader.push(chunk));\n * ```\n */\n push(chunk: Buffer): void {\n for (let i = 0; i < chunk.length; i++, this._byteOffset++) {\n const byte = chunk[i];\n if (byte === undefined) break; // noUncheckedIndexedAccess guard\n\n this._processByte(byte);\n }\n }\n\n /**\n * Clear internal accumulator state and reset byte offset to 0.\n *\n * Call on reconnect / connection reuse to start fresh without allocating a new reader.\n * Pending partial frame state is discarded silently.\n *\n * @example\n * ```typescript\n * socket.on('close', () => reader.reset());\n * ```\n */\n reset(): void {\n this._state = \"SCANNING_FOR_VT\";\n this._writePos = 0;\n this._byteOffset = 0;\n this._wsStart = 0;\n this._wsCount = 0;\n this._frameStartOffset = 0;\n this._frameWarnings = [];\n }\n\n private _processByte(byte: number): void {\n switch (this._state) {\n case \"SCANNING_FOR_VT\":\n this._scanForVt(byte);\n break;\n case \"READING_PAYLOAD\":\n this._readPayload(byte);\n break;\n case \"EXPECTING_CR\":\n this._expectCr(byte);\n break;\n }\n }\n\n private _scanForVt(byte: number): void {\n if (byte === VT) {\n // Emit leading-whitespace warning now that we know whitespace preceded this VT\n if (this._wsCount > 0) {\n this._emitWarning(\n \"MLLP_LEADING_WHITESPACE\",\n this._wsStart,\n `${this._wsCount} leading whitespace byte(s) before VT at offset ${this._wsStart}`,\n );\n this._wsStart = 0;\n this._wsCount = 0;\n }\n this._frameStartOffset = this._byteOffset;\n this._state = \"READING_PAYLOAD\";\n return;\n }\n\n // Check for whitespace bytes: SP (0x20), TAB (0x09), LF (0x0A), CR (0x0D)\n const isWhitespace = byte === 0x20 || byte === 0x09 || byte === LF || byte === CR;\n\n if (isWhitespace) {\n if (this._opts.allowLeadingWhitespace === true) {\n // Strict mode escalates leading whitespace to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Strict mode: leading whitespace before VT at offset ${this._byteOffset}`,\n );\n }\n if (this._wsCount === 0) {\n this._wsStart = this._byteOffset;\n }\n this._wsCount++;\n return;\n }\n // Whitespace before VT without tolerance → framing error\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Expected VT (0x0B) to start MLLP frame, got whitespace 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n // Non-VT, non-whitespace byte in SCANNING_FOR_VT.\n // If whitespace was accumulated under allowLeadingWhitespace, emit that warning first.\n if (this._wsCount > 0) {\n this._emitWarning(\n \"MLLP_LEADING_WHITESPACE\",\n this._wsStart,\n `${this._wsCount} leading whitespace byte(s) before VT at offset ${this._wsStart}`,\n );\n this._wsStart = 0;\n this._wsCount = 0;\n }\n\n if (this._opts.allowMissingLeadingVt === true) {\n // Strict mode escalates missing leading VT to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Strict mode: missing leading VT at offset ${this._byteOffset}`,\n );\n }\n // Treat this non-VT byte as the first payload byte\n this._emitWarning(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n `Missing leading VT (0x0B), treating byte 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset} as payload start`,\n );\n this._frameStartOffset = this._byteOffset;\n this._state = \"READING_PAYLOAD\";\n this._appendByte(byte);\n return;\n }\n\n // Default: strict framing error\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Expected VT (0x0B) to start MLLP frame, got 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n private _readPayload(byte: number): void {\n if (byte === FS) {\n // End-of-payload marker found\n if (this._writePos === 0) {\n // Empty payload is always a warning (WARN-05), never a throw\n this._emitWarning(\n \"MLLP_EMPTY_PAYLOAD\",\n this._byteOffset,\n `Empty payload between VT and FS at offset ${this._byteOffset}`,\n );\n }\n this._state = \"EXPECTING_CR\";\n return;\n }\n\n if (byte === VT) {\n // VT mid-payload: the current partial payload is abandoned and accumulation restarts after\n // this VT, so the frame that eventually delivers is only the REMNANT after the discard.\n // Emit MLLP_TRAILING_BYTES (always a warning, never a throw), this is the code's **reserved**\n // meaning: \"bytes were discarded; the delivered payload is a fragment.\" It is emitted while\n // accumulating the remnant frame, so it attaches to that frame (never bled onto another), and\n // `MllpServer`'s auto-ACK path keys on it to refuse a positive `AA` for a destroyed message.\n this._emitWarning(\n \"MLLP_TRAILING_BYTES\",\n this._byteOffset,\n `Unexpected VT (0x0B) mid-payload at offset ${this._byteOffset}; discarding ${this._writePos} accumulated bytes`,\n );\n this._writePos = 0;\n // Remain in READING_PAYLOAD, the VT starts a new frame payload accumulation\n this._state = \"READING_PAYLOAD\";\n return;\n }\n\n // Regular payload byte, enforce size cap BEFORE appending\n if (this._writePos >= this._maxFrameSize) {\n // PHI: the snippet must NOT carry a run of accumulated payload bytes, that is a\n // field-body slice of clinical content (the too-large frame is a full HL7 message).\n // The anomaly here is the size, not any specific byte, so the snippet is empty; the\n // structural facts (cap, byte offset) live in the message. (MLLP-9 PHI audit.)\n throw new MllpFramingError(\n \"MLLP_FRAME_TOO_LARGE\",\n this._byteOffset,\n EMPTY_SNIPPET,\n `Frame payload exceeded maxFrameSizeBytes (${this._maxFrameSize}) at offset ${this._byteOffset}`,\n );\n }\n\n this._appendByte(byte);\n }\n\n private _expectCr(byte: number): void {\n if (byte === CR) {\n // Normal frame completion\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n\n if (byte === LF) {\n if (this._opts.allowLfAfterFs === true) {\n // Strict mode escalates LF after FS to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n snippet,\n `Strict mode: LF after FS at offset ${this._byteOffset}`,\n );\n }\n this._emitWarning(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n `LF (0x0A) after FS instead of CR (0x0D) at offset ${this._byteOffset}`,\n );\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got LF (0x0A) at offset ${this._byteOffset}. Enable allowLfAfterFs to tolerate.`,\n );\n }\n\n if (byte === VT) {\n // Next frame starts immediately after FS, no CR between frames\n if (this._opts.allowFsOnly === true) {\n // Strict mode escalates FS without CR to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Strict mode: FS without CR at offset ${this._byteOffset}`,\n );\n }\n this._emitWarning(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n `FS not followed by CR at offset ${this._byteOffset}; next frame VT found immediately`,\n );\n this._deliverFrame();\n // The VT byte is consumed here, transition directly to READING_PAYLOAD\n this._state = \"READING_PAYLOAD\";\n return;\n }\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got VT (0x0B) at offset ${this._byteOffset}. Enable allowFsOnly to tolerate.`,\n );\n }\n\n // Any other non-CR byte after FS (not LF, not VT)\n if (this._opts.allowFsOnly === true) {\n // Strict mode escalates FS without CR to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Strict mode: FS without CR at offset ${this._byteOffset}`,\n );\n }\n // Deliver the completed frame and flag the missing CR; the stray byte is then dropped and\n // we return to scanning. The single FS_WITHOUT_CR warning names the stray byte and is\n // attached to the frame it followed.\n //\n // We deliberately do NOT also emit MLLP_TRAILING_BYTES here. That code is **reserved for a\n // VT appearing mid-payload** (see `_readPayload`), where it means \"this delivered payload is\n // only the remnant after accumulated bytes were discarded\", a signal `MllpServer`'s\n // auto-ACK path keys on to refuse a positive `AA` for a message whose bytes were destroyed.\n // Emitting it for an inter-frame stray byte overloaded that meaning AND mis-attributed it:\n // it was emitted *after* `_deliverFrame()`, so it landed in the NEXT frame's warning\n // accumulator and made a subsequent, perfectly good message look discarded. FS_WITHOUT_CR\n // already, correctly, reports this deviation.\n this._emitWarning(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n `FS not followed by CR at offset ${this._byteOffset}; got 0x${byte.toString(16).padStart(2, \"0\")} (dropped)`,\n );\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n /** Grow the internal accumulator by doubling and write one byte. */\n private _appendByte(byte: number): void {\n if (this._writePos >= this._accumulator.length) {\n const newSize = this._accumulator.length * 2;\n const grown = Buffer.allocUnsafe(newSize);\n this._accumulator.subarray(0, this._writePos).copy(grown);\n this._accumulator = grown;\n }\n this._accumulator[this._writePos++] = byte;\n }\n\n /**\n * Copy accumulated payload bytes into a new Buffer and deliver via `onFrame`.\n * The copy isolates callers from internal accumulator reuse.\n */\n private _deliverFrame(): void {\n const payload = Buffer.from(this._accumulator.subarray(0, this._writePos));\n this._writePos = 0;\n const frameStart = this._frameStartOffset;\n const frameWarnings: readonly MllpWarning[] = this._frameWarnings;\n this._frameWarnings = []; // reset for next frame\n this._opts.onFrame(payload, frameStart, frameWarnings);\n }\n\n /** Emit a warning via the `onWarning` callback, swallowing any handler exceptions. */\n private _emitWarning(code: WarningCode, byteOffset: number, message: string): void {\n const warning = createWarning(code, byteOffset, message);\n // Accumulate per-frame warnings unconditionally (independent of onWarning handler presence)\n this._frameWarnings.push(warning);\n if (this._opts.onWarning !== undefined) {\n try {\n this._opts.onWarning(warning);\n } catch {\n // WARN-06: throwing handler must not corrupt FSM state\n }\n }\n }\n}\n","/**\n * NetTransport, `Transport` implementation wrapping a `net.Socket`.\n *\n * This is the **only** place in the codebase that consumes `net.Socket`'s\n * EventEmitter surface. All higher layers (Connection, Server, Client) work\n * against the `Transport` interface, never against `net.Socket` directly.\n *\n * @example\n * ```typescript\n * import { createConnection } from 'node:net';\n * import { NetTransport } from '@cosyte/mllp';\n *\n * const socket = createConnection({ host: 'mllp.example.com', port: 2575 });\n * const transport = new NetTransport(socket);\n * transport.onConnect(() => console.log('connected'));\n * transport.onData((chunk) => reader.push(chunk));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Socket } from \"node:net\";\nimport type { Transport } from \"./index.js\";\n\n/**\n * Wraps a `net.Socket` as a `Transport`, mapping socket EventEmitter events\n * to registered single-handler callbacks.\n *\n * Each `onXxx(fn)` call replaces the prior handler and re-registers on the socket\n * (uses `socket.removeAllListeners(event)` + `socket.on(event, fn)`).\n *\n * @example\n * ```typescript\n * const t = new NetTransport(socket);\n * t.onData((chunk) => process(chunk));\n * t.onError((err) => handleError(err));\n * ```\n */\nexport class NetTransport implements Transport {\n private readonly _socket: Socket;\n\n /**\n * Wrap an existing `net.Socket` (or `tls.TLSSocket`) as a `Transport`.\n *\n * @param socket - The connected (or connecting) socket to adapt.\n */\n constructor(socket: Socket) {\n this._socket = socket;\n }\n\n /** See {@link Transport.write}. */\n write(buf: Buffer): boolean {\n return this._socket.write(buf);\n }\n\n /** See {@link Transport.close}. */\n close(): void {\n this._socket.end();\n }\n\n /** See {@link Transport.destroy}. */\n destroy(reason?: Error): void {\n this._socket.destroy(reason);\n }\n\n /** See {@link Transport.onData}. */\n onData(fn: (chunk: Buffer) => void): void {\n this._socket.removeAllListeners(\"data\");\n this._socket.on(\"data\", fn);\n }\n\n /** See {@link Transport.onConnect}. */\n onConnect(fn: () => void): void {\n this._socket.removeAllListeners(\"connect\");\n this._socket.on(\"connect\", fn);\n }\n\n /** See {@link Transport.onClose}. */\n onClose(fn: () => void): void {\n this._socket.removeAllListeners(\"close\");\n this._socket.on(\"close\", fn);\n }\n\n /** See {@link Transport.onError}. */\n onError(fn: (err: Error) => void): void {\n this._socket.removeAllListeners(\"error\");\n this._socket.on(\"error\", fn);\n }\n}\n","/**\n * TlsTransport, `Transport` implementation wrapping a `tls.TLSSocket`.\n *\n * Identical wiring to `NetTransport` with one deliberate difference: `onConnect`\n * maps to the socket's `'secureConnect'` event (fires once the TLS handshake\n * completes) rather than `'connect'` (fires once the raw TCP handshake\n * completes). Consumers of `Transport` (Connection, Client, Server) never see\n * the difference, they only ever observe \"the transport is ready.\"\n *\n * @example\n * ```typescript\n * import { connect } from 'node:tls';\n * import { TlsTransport } from '@cosyte/mllp';\n *\n * const socket = connect({ host: 'mllp.example.com', port: 2575, ca: caPem });\n * const transport = new TlsTransport(socket);\n * transport.onConnect(() => console.log('TLS handshake complete'));\n * transport.onData((chunk) => reader.push(chunk));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { TLSSocket } from \"node:tls\";\nimport type { Transport } from \"./index.js\";\n\n/**\n * Wraps a `tls.TLSSocket` as a `Transport`. `onConnect(fn)` is armed on the\n * `'secureConnect'` event (handshake complete, including certificate\n * verification when `rejectUnauthorized` is on) rather than the raw TCP\n * `'connect'` event that `NetTransport` uses.\n *\n * Each `onXxx(fn)` call replaces the prior handler and re-registers on the\n * socket (`socket.removeAllListeners(event)` + `socket.on(event, fn)`),\n * the same set-once semantics as `NetTransport`.\n *\n * @example\n * ```typescript\n * const t = new TlsTransport(tlsSocket);\n * t.onConnect(() => logger.info('secure'));\n * t.onError((err) => handleTlsError(err));\n * ```\n */\nexport class TlsTransport implements Transport {\n private readonly _socket: TLSSocket;\n\n /**\n * Wrap an existing `tls.TLSSocket` (connecting or already secure) as a `Transport`.\n *\n * @param socket - The TLS socket to adapt.\n */\n constructor(socket: TLSSocket) {\n this._socket = socket;\n }\n\n /** See {@link Transport.write}. */\n write(buf: Buffer): boolean {\n return this._socket.write(buf);\n }\n\n /** See {@link Transport.close}. */\n close(): void {\n this._socket.end();\n }\n\n /** See {@link Transport.destroy}. */\n destroy(reason?: Error): void {\n this._socket.destroy(reason);\n }\n\n /** See {@link Transport.onData}. */\n onData(fn: (chunk: Buffer) => void): void {\n this._socket.removeAllListeners(\"data\");\n this._socket.on(\"data\", fn);\n }\n\n /**\n * See {@link Transport.onConnect}. Armed on `'secureConnect'`, the TLS\n * handshake-complete event, not the raw TCP `'connect'` event.\n */\n onConnect(fn: () => void): void {\n this._socket.removeAllListeners(\"secureConnect\");\n this._socket.on(\"secureConnect\", fn);\n }\n\n /** See {@link Transport.onClose}. */\n onClose(fn: () => void): void {\n this._socket.removeAllListeners(\"close\");\n this._socket.on(\"close\", fn);\n }\n\n /** See {@link Transport.onError}. */\n onError(fn: (err: Error) => void): void {\n this._socket.removeAllListeners(\"error\");\n this._socket.on(\"error\", fn);\n }\n}\n","/**\n * Security-warning codes and payload shape shared by the client and server.\n *\n * These codes are a **public API**, they appear in `'securityWarning'` event\n * handlers, log pipelines, and monitoring dashboards. Renaming or removing a\n * code is a breaking change (mirrors the `WarningCode` guardrail in\n * `src/framing/registry.ts`).\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_VERIFY_DISABLED, MLLP_BIND_ALL_INTERFACES } from '@cosyte/mllp';\n * client.on('securityWarning', (w) => {\n * if (w.code === MLLP_TLS_VERIFY_DISABLED) logger.warn(w.message);\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Emitted (client-side) on every successful `secureConnect`, initial connect\n * AND every reconnect, when {@link TlsOptions.allowUnverified} is `true`.\n * Certificate verification is disabled for the connection; this is the loud,\n * per-connection reminder that the channel is not authenticated per IHE ATNA\n * ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_VERIFY_DISABLED } from '@cosyte/mllp';\n * client.on('securityWarning', (w) => {\n * if (w.code === MLLP_TLS_VERIFY_DISABLED) metrics.increment('mllp.tls_verify_disabled');\n * });\n * ```\n */\nexport const MLLP_TLS_VERIFY_DISABLED = \"MLLP_TLS_VERIFY_DISABLED\";\n\n/**\n * Emitted (server-side) once at `listen()` time when the server binds a\n * wildcard host (`'0.0.0.0'` or `'::'`) with `ServerOptions.allowWildcardBind: true`.\n * Binding all interfaces widens the network exposure of the listener; this is\n * the loud, one-time reminder that the operator opted in.\n *\n * @example\n * ```typescript\n * import { MLLP_BIND_ALL_INTERFACES } from '@cosyte/mllp';\n * server.on('securityWarning', (w) => {\n * if (w.code === MLLP_BIND_ALL_INTERFACES) logger.warn(w.message);\n * });\n * ```\n */\nexport const MLLP_BIND_ALL_INTERFACES = \"MLLP_BIND_ALL_INTERFACES\";\n\n/**\n * Union of the two stable security-warning codes.\n *\n * @example\n * ```typescript\n * const code: SecurityWarningCode = 'MLLP_TLS_VERIFY_DISABLED';\n * ```\n */\nexport type SecurityWarningCode = typeof MLLP_TLS_VERIFY_DISABLED | typeof MLLP_BIND_ALL_INTERFACES;\n\n/**\n * Frozen payload of the `'securityWarning'` event, emitted by both\n * {@link MllpClient} (`MLLP_TLS_VERIFY_DISABLED`) and {@link MllpServer}\n * (`MLLP_BIND_ALL_INTERFACES`).\n *\n * Never carries payload bytes or PHI, only routing metadata (host/port) and\n * a fixed, static message string.\n *\n * @example\n * ```typescript\n * client.on('securityWarning', (w: SecurityWarning) => {\n * logger.warn({ code: w.code, host: w.host, port: w.port });\n * });\n * ```\n */\nexport interface SecurityWarning {\n /** The stable security-warning code. */\n readonly code: SecurityWarningCode;\n /** Fixed, human-readable description. Never contains payload bytes or PHI. */\n readonly message: string;\n /** Host associated with the warning (the target host for clients; the bind host for servers). */\n readonly host: string;\n /** Port associated with the warning. */\n readonly port: number;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n","/**\n * Typed error for socket-layer connection problems.\n *\n * @example\n * ```typescript\n * import { MllpConnectionError } from '@cosyte/mllp';\n * try {\n * await client.connect();\n * } catch (err) {\n * if (err instanceof MllpConnectionError) {\n * console.log(err.phase, err.cause);\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Connection lifecycle phase where the error occurred.\n *\n * All 5 phases are defined even though `'reconnect'` is only\n * exercised by the client. Locking the full union now prevents a\n * breaking type change later.\n */\nexport type ConnectionErrorPhase = \"connect\" | \"send\" | \"receive\" | \"close\" | \"reconnect\";\n\n/**\n * Stable cause codes for `MllpConnectionError`.\n *\n * These codes are a **public API**, they appear in error inspection\n * by callers, log pipelines, and monitoring dashboards. Renaming or\n * removing a member is a breaking change.\n *\n * - `'fifo-unsafe'`, A queued send was rejected during reconnect because\n * FIFO ordering cannot be safely resumed across sessions.\n * - `'in-flight-orphan'`, An in-flight send (already write-flushed,\n * ACK timer started) was rejected during reconnect in FIFO mode because\n * the at-most-once delivery contract cannot be preserved across a\n * socket drop (healthcare medication/orders semantics).\n * - `'tls-verify'`, The TLS handshake failed certificate\n * verification (untrusted chain, expired/not-yet-valid cert, hostname\n * mismatch, revocation, …). See {@link isTlsVerificationErrorCode} for the\n * exact underlying error codes. Classified **permanent** by\n * `isTransientConnectionError`, never auto-reconnect-looped into a\n * misconfigured or MITM'd endpoint.\n * - `'tls-handshake'`, A TLS-**protocol**-shaped handshake\n * failure observed before `'secureConnect'`: `ERR_SSL_*` codes, `EPROTO`,\n * or an OpenSSL alert-bearing error (protocol version mismatch, no shared\n * cipher, a required mutual-TLS client certificate rejected by the\n * server, …). See `isTlsProtocolError` for the boundary. Pure TCP-level\n * failures (`ECONNREFUSED`, `ETIMEDOUT`, …) on a TLS-configured\n * connection carry **no** `connectionCause`, the same shape as plaintext.\n *\n * Scope note: the `'tls-verify'`/`'tls-handshake'` values are attached on\n * the client's **initial `connect()` path**. Failures on the auto-reconnect\n * path surface as raw socket errors, their transient/permanent\n * classification still applies, but they do not (yet) carry a\n * `connectionCause`.\n *\n * @example\n * ```typescript\n * if (err instanceof MllpConnectionError && err.connectionCause === 'in-flight-orphan') {\n * // Treat as at-most-once: do NOT auto-retry; bubble up to caller for\n * // application-level dedupe / replay decisions.\n * }\n * ```\n */\nexport type ConnectionErrorCause =\n | \"fifo-unsafe\"\n | \"in-flight-orphan\"\n | \"tls-verify\"\n | \"tls-handshake\"\n | \"framing-fatal\";\n\n/**\n * Thrown (or emitted via `onError`) for socket-layer problems such as\n * connection refused, ECONNRESET, ETIMEDOUT, or DNS failure.\n *\n * - `cause`, the original OS or TLS error\n * - `phase`, which connection lifecycle phase the failure occurred in\n * - `connectionCause`, optional stable cause-code (e.g. for FIFO reconnect rejections)\n *\n * @example\n * ```typescript\n * throw new MllpConnectionError('Connection refused', {\n * cause: osError,\n * phase: 'connect',\n * });\n * ```\n */\nexport class MllpConnectionError extends Error {\n override readonly name = \"MllpConnectionError\" as const;\n\n /** The original OS or TLS error that caused this connection failure. */\n override readonly cause: Error;\n\n /** Which connection lifecycle phase the failure occurred in. */\n readonly phase: ConnectionErrorPhase;\n\n /**\n * Optional stable cause code. Present on FIFO reconnect rejections\n * (`'fifo-unsafe'` for queued sends, `'in-flight-orphan'` for in-flight sends).\n *\n * The set of values is a **public API**; see {@link ConnectionErrorCause}.\n */\n readonly connectionCause?: ConnectionErrorCause;\n\n /**\n * Construct an MLLP connection error.\n *\n * @param message - Human-readable error message.\n * @param opts - Error context (underlying `cause`, the lifecycle `phase`, optional stable cause code).\n */\n constructor(\n message: string,\n opts: {\n cause: Error;\n phase: ConnectionErrorPhase;\n connectionCause?: ConnectionErrorCause;\n },\n ) {\n super(message);\n this.cause = opts.cause;\n this.phase = opts.phase;\n if (opts.connectionCause !== undefined) {\n this.connectionCause = opts.connectionCause;\n }\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpConnectionError);\n }\n }\n}\n","/**\n * Connection, 6-state FSM over a Transport with lifecycle events, warning streams,\n * and getStats() observability.\n *\n * @example\n * ```typescript\n * import { Connection, NetTransport } from '@cosyte/mllp';\n * import { createConnection } from 'node:net';\n *\n * const socket = createConnection({ host: 'localhost', port: 2575 });\n * const transport = new NetTransport(socket);\n * const conn = new Connection({ transport });\n * conn.on('message', ({ payload }) => conn.send(ackBuffer));\n * conn.on('stateChange', ({ from, to }) => logger.info({ from, to }));\n * socket.on('connect', () => conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { EventEmitter } from \"node:events\";\nimport { randomUUID } from \"node:crypto\";\nimport type { Transport } from \"../transport/index.js\";\nimport { FrameReader, MllpFramingError } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning } from \"../framing/index.js\";\nimport { MllpConnectionError } from \"./error.js\";\nimport type { ConnectionErrorPhase } from \"./error.js\";\n\n/**\n * The 6 connection states.\n *\n * Transitions are validated against the legal-transition edge graph. Illegal transitions\n * are silently ignored to preserve FSM integrity.\n */\nexport type ConnectionState =\n | \"CONNECTING\"\n | \"CONNECTED\"\n | \"DRAINING\"\n | \"RECONNECTING\"\n | \"DISCONNECTED\"\n | \"CLOSED\";\n\n/**\n * Payload for the 'stateChange' event. Always `Object.freeze`'d.\n *\n * @example\n * ```typescript\n * conn.on('stateChange', ({ from, to, reason }) => {\n * logger.info({ from, to, reason });\n * });\n * ```\n */\nexport interface StateChangeEvent {\n readonly from: ConnectionState;\n readonly to: ConnectionState;\n readonly reason?: string;\n}\n\n/**\n * Payload for the 'reconnecting' event. Always `Object.freeze`'d.\n *\n * `attempt` and `delayMs` are populated by `MllpClient` when it schedules a\n * reconnect. A `Connection` emitting on its own supplies `connectionId` only.\n *\n * @example\n * ```typescript\n * conn.on('reconnecting', ({ connectionId }) => {\n * logger.info(`Reconnecting connection ${connectionId}`);\n * });\n * ```\n */\nexport interface ReconnectingEvent {\n readonly connectionId: string;\n readonly attempt?: number;\n readonly delayMs?: number;\n}\n\n/**\n * Return type of `connection.getStats()`, JSON-serializable.\n *\n * All timestamps are `Date | null` (not epoch milliseconds). `JSON.stringify()`\n * serialises them to ISO 8601 strings by default with no information loss.\n *\n * @example\n * ```typescript\n * const stats = conn.getStats();\n * logger.info(JSON.stringify(stats)); // safe: all values are JSON-serializable\n * ```\n */\nexport interface ConnectionStats {\n readonly state: ConnectionState;\n readonly connectionId: string;\n readonly remoteAddress: string | null;\n readonly remotePort: number | null;\n readonly warningsByCode: Record<string, number>;\n readonly bytesIn: number;\n readonly bytesOut: number;\n readonly lastByteInAt: Date | null;\n readonly lastByteOutAt: Date | null;\n readonly connectedAt: Date | null;\n readonly warningsTruncated: boolean;\n}\n\n/**\n * Options for constructing a {@link Connection}.\n *\n * @example\n * ```typescript\n * const opts: ConnectionOptions = {\n * transport: new NetTransport(socket),\n * onMessage: (payload) => handleMessage(payload),\n * onWarning: (w) => logger.warn(w),\n * drainTimeoutMs: 10_000,\n * };\n * ```\n */\nexport interface ConnectionOptions {\n /** The transport this Connection will drive. */\n transport: Transport;\n /** Called for each decoded MLLP frame (raw payload bytes, framing stripped). */\n onMessage?: (payload: Buffer) => void;\n /** Per-connection warning subscriber. Replaces previous subscription. */\n onWarning?: (w: MllpWarning) => void;\n /** Drain timeout used by close() (default: 30_000 ms). */\n drainTimeoutMs?: number;\n /** FrameReader options (tolerance, maxFrameSizeBytes). onFrame/onWarning are managed internally. */\n framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n}\n\n/** Warning ring buffer cap. */\nconst MAX_WARNINGS = 100;\n\n/**\n * Legal state transitions.\n *\n * CONNECTING → CONNECTED | RECONNECTING | CLOSED\n * CONNECTED → DRAINING | RECONNECTING | DISCONNECTED | CLOSED\n * DRAINING → DISCONNECTED | CLOSED\n * RECONNECTING → CONNECTING | CLOSED\n * DISCONNECTED → CLOSED\n * CLOSED → (terminal, no outgoing transitions)\n */\nconst LEGAL_TRANSITIONS: ReadonlyMap<ConnectionState, ReadonlySet<ConnectionState>> = new Map([\n [\"CONNECTING\", new Set<ConnectionState>([\"CONNECTED\", \"RECONNECTING\", \"CLOSED\"])],\n [\"CONNECTED\", new Set<ConnectionState>([\"DRAINING\", \"RECONNECTING\", \"DISCONNECTED\", \"CLOSED\"])],\n [\"DRAINING\", new Set<ConnectionState>([\"DISCONNECTED\", \"CLOSED\"])],\n [\"RECONNECTING\", new Set<ConnectionState>([\"CONNECTING\", \"CLOSED\"])],\n [\"DISCONNECTED\", new Set<ConnectionState>([\"CLOSED\"])],\n [\"CLOSED\", new Set<ConnectionState>()],\n]);\n\n/**\n * A single MLLP connection wrapping a {@link Transport} with a 6-state FSM,\n * lifecycle events, per-connection warning streams, and `getStats()` observability.\n *\n * Emits events: `stateChange`, `connect`, `disconnect`, `reconnecting`, `close`,\n * `message`, `warning`, `error`.\n *\n * @example\n * ```typescript\n * import { Connection, NetTransport } from '@cosyte/mllp';\n * import { createConnection } from 'node:net';\n *\n * const socket = createConnection({ host: 'localhost', port: 2575 });\n * const conn = new Connection({ transport: new NetTransport(socket) });\n * conn.on('stateChange', ({ from, to }) => console.log(from, '->', to));\n * conn.on('message', ({ payload }) => console.log('received', payload.length, 'bytes'));\n * socket.on('connect', () =>\n * conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null)\n * );\n * ```\n */\nexport class Connection extends EventEmitter {\n /** Stable UUIDv4 identifier for this connection. */\n readonly connectionId: string;\n\n private _state: ConnectionState = \"CONNECTING\";\n private readonly _transport: Transport;\n private readonly _reader: FrameReader;\n private readonly _opts: ConnectionOptions;\n\n private _remoteAddress: string | null = null;\n private _remotePort: number | null = null;\n private _connectedAt: Date | null = null;\n\n private _bytesIn = 0;\n private _bytesOut = 0;\n private _lastByteInAt: Date | null = null;\n private _lastByteOutAt: Date | null = null;\n\n /** Ring buffer: last MAX_WARNINGS entries. */\n private _warningBuffer: MllpWarning[] = [];\n /** Accurate total counts per code, unaffected by ring buffer truncation. */\n private readonly _warningsByCode: Map<string, number> = new Map();\n /** Set to true once the ring buffer overflows. */\n private _warningsTruncated = false;\n\n private _onWarningFn: ((w: MllpWarning) => void) | null = null;\n private _drainPromise: Promise<void> | null = null;\n\n /**\n * beforeClose hook, no-op default that resolves immediately.\n *\n * An owner may replace this instance property to run work before the socket\n * closes. Connection calls it during `close()`, racing against `drainTimeoutMs`.\n */\n beforeClose: (drainTimeoutMs: number) => Promise<void> = () => Promise.resolve();\n\n /**\n * Construct a connection over the given transport. Starts in `CONNECTING`; call\n * {@link Connection.notifyConnect} once the transport handshake completes.\n *\n * @param opts - Connection options (the transport, message/warning handlers, drain timeout, framing).\n */\n constructor(opts: ConnectionOptions) {\n super();\n this.connectionId = randomUUID();\n this._opts = opts;\n this._transport = opts.transport;\n\n if (opts.onWarning !== undefined) {\n this._onWarningFn = opts.onWarning;\n }\n\n this._reader = new FrameReader({\n ...(opts.framing ?? {}),\n onFrame: (payload, byteOffset, warnings) => {\n this._onFrameDecoded(payload, byteOffset, warnings);\n },\n onWarning: (w) => {\n this._onFramingWarning(w);\n },\n });\n\n // Wire transport callbacks\n this._transport.onData((chunk) => {\n this._bytesIn += chunk.length;\n this._lastByteInAt = new Date();\n try {\n this._reader.push(chunk);\n } catch (err) {\n // Nothing may escape the transport's data callback. On a real socket this callback IS\n // the `'data'` listener, so a throw here becomes an uncaught exception that kills the\n // entire process, taking every OTHER connection and every in-flight durable commit\n // with it. One junk byte from one misbehaving peer must not be able to do that.\n //\n // Reachable on a DEFAULT server: `SERVER_DEFAULT_FRAMING` leaves `allowMissingLeadingVt`\n // off, so any non-whitespace byte where a VT was expected throws MLLP_MISSING_LEADING_VT.\n // `MLLP_FRAME_TOO_LARGE` reaches here too.\n //\n // The two classes are kept DISTINCT on purpose. A framing error is the peer's fault; a\n // subscriber bug is ours. Reporting the second as the first would tell an operator the\n // peer sent bad bytes when in fact our own handler threw.\n if (err instanceof MllpFramingError) {\n this._onFatalFramingError(err);\n } else {\n this._onInternalReceiveError(err instanceof Error ? err : new Error(String(err)));\n }\n }\n });\n this._transport.onClose(() => {\n this._onTransportClose();\n });\n this._transport.onError((err) => {\n this._onTransportError(err);\n });\n }\n\n /**\n * Current FSM state. One of the 6 `ConnectionState` values.\n *\n * Subscribe to `'stateChange'` events for reactive state monitoring.\n */\n get state(): ConnectionState {\n return this._state;\n }\n\n /**\n * Register or replace the per-connection warning subscriber.\n *\n * Subsequent calls replace the previous handler (set-once semantics prevent leaks).\n * The handler is called synchronously and any exception it throws is swallowed.\n *\n * @example\n * ```typescript\n * conn.onWarning((w) => logger.warn({ code: w.code, connectionId: w.connectionId }));\n * ```\n */\n onWarning(fn: (w: MllpWarning) => void): void {\n this._onWarningFn = fn;\n }\n\n /**\n * Notify the connection that the transport handshake has completed.\n *\n * Transitions `CONNECTING → CONNECTED` and emits the `'connect'` event.\n * Called externally by Server/Client after the socket connects.\n *\n * @param remoteAddress - Remote peer IP address, or `null` if unavailable.\n * @param remotePort - Remote peer port, or `null` if unavailable.\n *\n * @example\n * ```typescript\n * socket.on('connect', () =>\n * conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null)\n * );\n * ```\n */\n notifyConnect(remoteAddress: string | null, remotePort: number | null): void {\n this._remoteAddress = remoteAddress;\n this._remotePort = remotePort;\n this._connectedAt = new Date();\n this._transition(\"CONNECTED\");\n // Contained like every other emit in this class, `notifyConnect` is called from the\n // transport's connect callback, so a throwing subscriber would unwind into it.\n this._dispatchContained(() => {\n this.emit(\"connect\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n\n /**\n * Write raw bytes to the transport (no framing applied).\n *\n * The server and the client wrap this with `encodeFrame()` to add MLLP framing.\n * Returns `false` if the connection is CLOSED or DISCONNECTED (no bytes written).\n *\n * @returns `true` if bytes flushed immediately; `false` if buffered (backpressure) or not writable.\n *\n * @example\n * ```typescript\n * const flushed = conn.send(encodeFrame(payload));\n * if (!flushed) logger.warn('backpressure detected');\n * ```\n */\n send(data: Buffer): boolean {\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return false;\n const ok = this._transport.write(data);\n this._bytesOut += data.length;\n this._lastByteOutAt = new Date();\n return ok;\n }\n\n /**\n * Initiate graceful close of the connection.\n *\n * - If `CONNECTING` or `RECONNECTING`: cancels the pending attempt, transitions\n * directly to `CLOSED`, and calls `transport.destroy()`.\n * - If `CONNECTED`: transitions to `DRAINING`, calls `beforeClose(drainTimeoutMs)`,\n * then `DRAINING → DISCONNECTED` once the hook resolves, or `DRAINING → CLOSED`\n * if the drain timeout elapses first.\n * - If `DRAINING`: waits for the existing drain to complete (idempotent).\n * - If `CLOSED` or `DISCONNECTED`: no-op.\n *\n * @param opts.drainTimeoutMs - Override drain timeout (default: `opts.drainTimeoutMs ?? 30_000`).\n *\n * @example\n * ```typescript\n * await conn.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number }): Promise<void> {\n const timeout = opts?.drainTimeoutMs ?? this._opts.drainTimeoutMs ?? 30_000;\n\n // Terminal states, nothing to do\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return;\n\n // CONNECTING or RECONNECTING, cancel the pending attempt (LIFE-05)\n if (this._state === \"CONNECTING\" || this._state === \"RECONNECTING\") {\n this._transition(\"CLOSED\", \"close() during \" + this._state);\n this._transport.destroy();\n return;\n }\n\n // DRAINING already, join the in-progress drain rather than starting a second beforeClose call\n if (this._state === \"DRAINING\") {\n return this._drainPromise ?? Promise.resolve();\n }\n\n // CONNECTED → DRAINING\n this._transition(\"DRAINING\");\n this._drainPromise = this._drainWithTimeout(timeout).finally(() => {\n this._drainPromise = null;\n });\n return this._drainPromise;\n }\n\n /**\n * Race `beforeClose()` against the drain timeout.\n *\n * On normal completion: `DRAINING → DISCONNECTED` + `transport.close()`.\n * On timeout: `DRAINING → CLOSED` + `transport.destroy()`.\n */\n private async _drainWithTimeout(timeoutMs: number): Promise<void> {\n const drainPromise = this.beforeClose(timeoutMs);\n const timeoutPromise = new Promise<\"timeout\">((resolve) => {\n const handle = setTimeout(() => {\n resolve(\"timeout\");\n }, timeoutMs);\n // Unref so this timer does not keep the process alive (T-03-04-01)\n handle.unref();\n });\n\n const result = await Promise.race([drainPromise.then(() => \"done\" as const), timeoutPromise]);\n\n if (result === \"timeout\") {\n // Drain timed out, force to CLOSED (DRAINING → CLOSED per LIFE-02)\n if (this._state === \"DRAINING\") {\n this._transition(\"CLOSED\", \"drain timeout\");\n this._transport.destroy();\n }\n } else {\n // Drain completed, DRAINING → DISCONNECTED (LIFE-02)\n if (this._state === \"DRAINING\") {\n this._transition(\"DISCONNECTED\");\n this._transport.close();\n }\n }\n }\n\n /**\n * Abruptly destroy the connection, discarding any pending writes.\n *\n * Transitions from any non-terminal state directly to `CLOSED` and calls\n * `transport.destroy(reason)`. Emits `'close'` event. Idempotent, safe to\n * call multiple times.\n *\n * @param reason - Optional error to propagate to the transport.\n *\n * @example\n * ```typescript\n * conn.destroy(new Error('Timeout exceeded'));\n * ```\n */\n destroy(reason?: Error): void {\n if (this._state === \"CLOSED\") return;\n this._transition(\"CLOSED\", reason?.message ?? \"destroy()\");\n this._transport.destroy(reason);\n }\n\n /**\n * Return a JSON-serializable observability snapshot.\n *\n * All timestamp fields are `Date | null` (not epoch milliseconds). `JSON.stringify()`\n * serialises them to ISO 8601 strings with no information loss.\n *\n * `warningsByCode` reflects every warning ever received regardless of ring buffer truncation.\n * `warningsTruncated` is `true` if the 100-entry ring buffer has overflowed.\n *\n * @example\n * ```typescript\n * const stats = conn.getStats();\n * console.log(JSON.stringify(stats)); // log-pipeline friendly\n * ```\n */\n getStats(): ConnectionStats {\n const warningsByCode: Record<string, number> = {};\n for (const [code, count] of this._warningsByCode) {\n warningsByCode[code] = count;\n }\n return {\n state: this._state,\n connectionId: this.connectionId,\n remoteAddress: this._remoteAddress,\n remotePort: this._remotePort,\n warningsByCode,\n bytesIn: this._bytesIn,\n bytesOut: this._bytesOut,\n lastByteInAt: this._lastByteInAt,\n lastByteOutAt: this._lastByteOutAt,\n connectedAt: this._connectedAt,\n warningsTruncated: this._warningsTruncated,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private FSM helpers\n // ---------------------------------------------------------------------------\n\n private _transition(to: ConnectionState, reason?: string): void {\n const from = this._state;\n const legal = LEGAL_TRANSITIONS.get(from);\n if (legal === undefined || !legal.has(to)) {\n // Illegal transition, silently ignore to preserve FSM integrity (T-03-03-04)\n return;\n }\n // The state is committed BEFORE any subscriber runs, so a throwing subscriber can never leave\n // the FSM half-transitioned.\n this._state = to;\n const event = Object.freeze<StateChangeEvent>(\n reason !== undefined ? { from, to, reason } : { from, to },\n );\n\n // Every lifecycle emit is contained. `_transition` is reached from `destroy()`, which is\n // called from inside the `catch` block on the receive path, and a throw raised inside a catch\n // block is NOT caught by that block. A throwing 'stateChange'/'close' subscriber would\n // therefore unwind straight out of the socket's 'data' listener and kill the process, which is\n // the same defect as the decoder throw, just four frames up. Contain at the source: no emit in\n // this class is allowed to reach a transport callback.\n this._dispatchContained(() => {\n this.emit(\"stateChange\", event);\n });\n\n // Fire semantic lifecycle events\n if (to === \"DISCONNECTED\") {\n this._dispatchContained(() => {\n this.emit(\"disconnect\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n if (to === \"RECONNECTING\") {\n this._dispatchContained(() => {\n this.emit(\"reconnecting\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n if (to === \"CLOSED\") {\n this._dispatchContained(() => {\n this.emit(\"close\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n }\n\n private _onTransportClose(): void {\n if (this._state === \"DRAINING\") {\n // Transport closed naturally during graceful drain, complete the close\n this._transition(\"DISCONNECTED\");\n return;\n }\n if (this._state === \"CONNECTED\") {\n this._transition(\"DISCONNECTED\", \"peer closed\");\n return;\n }\n if (this._state === \"CONNECTING\" || this._state === \"RECONNECTING\") {\n // Neither CONNECTING nor RECONNECTING has a path to DISCONNECTED.\n // Use CLOSED (terminal) for unexpected peer close here.\n this._transition(\"CLOSED\", \"peer closed\");\n }\n }\n\n /**\n * Emit on `'error'` only when a listener is attached.\n *\n * Node throws `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an `EventEmitter` with no\n * listener. Every `'error'` emission in this class is reached from inside a transport callback\n * (the socket's `'data'`/`'error'` listener), so an unguarded emit would become an **uncaught\n * exception that kills the process**, the exact failure the containment above exists to\n * prevent, merely relocated one frame up the stack.\n *\n * `Connection` is a public export and can legitimately be driven with no `'error'` listener\n * (`MllpServer` and `MllpClient` each attach one; a bare `Connection` need not). The connection\n * is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind,\n * they simply do not get the typed error object they never asked for. Mirrors\n * `MllpServer._emitErrorIfListened`.\n */\n private _emitErrorIfListened(error: MllpConnectionError): void {\n if (this.listenerCount(\"error\") === 0) return;\n try {\n this.emit(\"error\", Object.freeze({ connectionId: this.connectionId, error }));\n } catch {\n // A throwing `'error'` subscriber must not escape either. This method is called from inside\n // socket callbacks AND from inside catch blocks, so a throw here would unwind straight out of\n // the socket handler, the same process kill, reached by a third route. This is the one place\n // a throw is deliberately swallowed: reporting is what just failed, so there is nowhere left\n // to report it to.\n }\n }\n\n /**\n * Handle a fatal framing error thrown out of `FrameReader.push` (FRAME-FATAL).\n *\n * Contract: surface it as a connection `'error'` (frozen payload, `phase: 'receive'`,\n * `connectionCause: 'framing-fatal'`, the original `MllpFramingError` preserved as `cause` so\n * consumers keep the stable `code` and `byteOffset`), then **destroy this connection only**.\n *\n * Why destroy rather than resynchronize: once `push` has thrown, the reader's position within\n * the byte stream is no longer trustworthy, the next bytes could be the middle of a message.\n * Guessing where the next frame starts is how a clinical message gets silently mis-split, so\n * the connection is dropped instead. A server drops that one peer and keeps serving everyone\n * else.\n *\n * `'framing-fatal'` is classified **permanent** by `isTransientConnectionError`, so a client\n * does **not** auto-reconnect into it. That matters: a peer speaking something that is not MLLP\n * (an HTTP probe, a health check, a wrong-port misconfiguration) would otherwise be retried\n * forever, and the backoff would become an unbounded reconnect storm against an interface\n * engine that is already unwell. A compatibility failure is not a network blip.\n *\n * If a peer's quirk is *expected* (trailing junk, keepalive bytes, a missing leading VT), the\n * decoder's tolerance opt-ins are the supported answer, they turn the throw into a warning.\n */\n private _onFatalFramingError(err: MllpFramingError): void {\n if (this._state === \"CLOSED\") return;\n this._emitErrorIfListened(\n new MllpConnectionError(err.message, {\n cause: err,\n phase: \"receive\",\n connectionCause: \"framing-fatal\",\n }),\n );\n this.destroy(err);\n }\n\n /**\n * Backstop for a NON-framing throw escaping `FrameReader.push`, i.e. our own bug, or a\n * subscriber's, rather than the peer's bytes.\n *\n * `_onFrameDecoded` already contains subscriber throws at the dispatch site, so this should be\n * unreachable. It exists because \"unreachable\" and \"cannot kill the process from inside a\n * socket `'data'` handler\" are different claims, and only the second one is safe to bet a\n * clinical interface on. Reported honestly as an internal receive error, never dressed up as\n * a peer framing fault.\n */\n private _onInternalReceiveError(err: Error): void {\n if (this._state === \"CLOSED\") return;\n this._emitErrorIfListened(\n new MllpConnectionError(err.message, { cause: err, phase: \"receive\" }),\n );\n this.destroy(err);\n }\n\n private _onTransportError(err: Error): void {\n const phase: ConnectionErrorPhase =\n this._state === \"CONNECTING\"\n ? \"connect\"\n : this._state === \"RECONNECTING\"\n ? \"reconnect\"\n : this._state === \"DRAINING\"\n ? \"close\"\n : \"receive\";\n\n // Terminal states first. `destroy(err)` forwards the reason to `transport.destroy(err)`, which\n // makes the socket re-surface the same error here, so emitting before this guard reported a\n // single fatal framing error TWICE: once with `connectionCause: 'framing-fatal'`, then again\n // bare. That double-counts on an alerting dashboard and the echo carries no cause.\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return;\n\n // Guarded: this runs inside the transport's error callback, so an unlistened emit would throw\n // ERR_UNHANDLED_ERROR straight out of the socket's 'error' listener and kill the process. A\n // bare Connection (no 'error' listener) previously died on an ordinary ECONNRESET.\n this._emitErrorIfListened(new MllpConnectionError(err.message, { cause: err, phase }));\n\n // CONNECTING and RECONNECTING have no path to DISCONNECTED, use CLOSED\n const target: ConnectionState =\n this._state === \"CONNECTING\" || this._state === \"RECONNECTING\" ? \"CLOSED\" : \"DISCONNECTED\";\n this._transition(target, `error: ${err.message}`);\n }\n\n private _onFrameDecoded(\n payload: Buffer,\n byteOffset: number,\n warnings: readonly MllpWarning[],\n ): void {\n // Only deliver messages when in an active state (CONNECTED or DRAINING)\n if (this._state !== \"CONNECTED\" && this._state !== \"DRAINING\") return;\n const event = Object.freeze({ payload, connectionId: this.connectionId, byteOffset, warnings });\n\n // `onFrame` is dispatched SYNCHRONOUSLY from inside `FrameReader.push()`, which is itself\n // called from the transport's data callback. A throwing subscriber, a metrics tap, a logger,\n // an ordinary consumer bug, would therefore unwind through `push()` and out of the socket's\n // `'data'` listener, killing the process, and on the way it would suppress the ACK for a\n // message the application may already have handled.\n //\n // Contain each subscriber independently (the WARN-06 pattern already used for warnings): one\n // bad subscriber cannot silence the other, cannot suppress the ACK, and cannot be mistaken\n // for a peer framing fault.\n this._dispatchContained(() => {\n this.emit(\"message\", event);\n });\n this._dispatchContained(() => {\n this._opts.onMessage?.(payload);\n });\n }\n\n /**\n * Run a subscriber callback, containing any throw as a connection `'error'` rather than letting\n * it unwind into the socket callback that (transitively) invoked us.\n */\n private _dispatchContained(fn: () => void): void {\n try {\n fn();\n } catch (err) {\n const cause = err instanceof Error ? err : new Error(String(err));\n this._emitErrorIfListened(\n new MllpConnectionError(cause.message, { cause, phase: \"receive\" }),\n );\n }\n }\n\n private _onFramingWarning(w: MllpWarning): void {\n // Enrich with connectionId (D-09) and re-freeze\n const enriched = Object.freeze<MllpWarning>({ ...w, connectionId: this.connectionId });\n\n // Always update the count map, accurate regardless of ring buffer truncation (OBS-05)\n this._warningsByCode.set(enriched.code, (this._warningsByCode.get(enriched.code) ?? 0) + 1);\n\n // Ring buffer: keep last MAX_WARNINGS entries (OBS-05)\n if (this._warningBuffer.length >= MAX_WARNINGS) {\n this._warningBuffer.shift(); // remove oldest\n this._warningsTruncated = true;\n }\n this._warningBuffer.push(enriched);\n\n // Per-connection subscriber, swallow exceptions (WARN-06)\n if (this._onWarningFn !== null) {\n try {\n this._onWarningFn(enriched);\n } catch {\n // WARN-06: throwing handler must not disrupt frame processing\n }\n }\n\n // EventEmitter broadcast (aggregate warning stream).\n //\n // Contained for the same reason as the per-connection subscriber above: this runs synchronously\n // inside `FrameReader.push()`, itself inside the socket's 'data' callback, so a throwing\n // 'warning' listener would unwind out of the data handler and kill the process. WARN-06 already\n // promises a throwing warning handler \"must not disrupt frame processing\", it was only half\n // honored (the `onWarning` option was guarded; the event broadcast was not).\n this._dispatchContained(() => {\n this.emit(\"warning\", enriched);\n });\n }\n}\n","/**\n * Byte-level MSH-10 / MSA-2 control-ID scanners, the **single** implementation\n * of \"read the control ID off the wire\", shared by everything in this package\n * that has to agree on what a control ID *is*.\n *\n * Three call sites depend on these bytes agreeing, and a disagreement between\n * any two of them is a lost clinical message:\n *\n * * `src/client/correlator.ts` keys its in-flight store by the MSH-10 it\n * extracts from the **outbound** frame, and looks an inbound ACK up by the\n * MSA-2 it extracts from the **ACK** frame.\n * * `src/server/ack.ts` (`buildRawAck`) echoes the inbound MSH-10 into MSA-2.\n * * `src/ack-from-hl7/build.ts` verifies, against these same scanners, that\n * the ACK it hands back really does echo MSH-10 verbatim.\n *\n * They live here, not in `client/`, because none of that is client policy.\n * It is the package's byte-level reading of HL7 v2.5.1 §2.5.4 (MSH-1 *defines*\n * the field separator) and §2.9.2.2 (MSA-2 echoes MSH-10 verbatim).\n *\n * **INTERNAL**, not part of the public API.\n *\n * @packageDocumentation\n */\n\nconst ASCII_M = 0x4d;\nconst ASCII_S = 0x53;\nconst ASCII_H = 0x48;\nconst ASCII_A = 0x41;\nconst ASCII_B = 0x42;\nconst ASCII_F = 0x46;\nconst ASCII_T = 0x54;\nconst SEGMENT_SEPARATOR_CR = 0x0d;\nconst SEGMENT_SEPARATOR_LF = 0x0a;\n\n/**\n * Decode used for MSH-10 / MSA-2 control-ID extraction: `latin1`, NOT `ascii`.\n *\n * Node's `ascii` codec masks the high bit (`byte & 0x7f`), so it is a **lossy**\n * decode of the raw HL7 v2 bytes. Two consequences, both of which land squarely\n * on ACK correlation:\n *\n * 1. **Key collision.** The correlator keys its live store, its graveyard, and\n * its ACK lookups by the extracted control-ID *string*. Under `ascii`, the\n * distinct control IDs `A\\x8B` and `A\\x0B` both decode to `A\\x0B`, so two\n * concurrently in-flight sends collapse onto one key: the second `enqueue()`\n * overwrites the first in the `Map`, and the first send's promise can never\n * be settled by its own ACK. `latin1` is a 1:1 byte↔code-unit mapping, so\n * every distinct byte string stays a distinct key.\n * 2. **Corrupted observability.** The extracted ID is what the correlator keys\n * its graveyard on, so a masked ID is a control ID that never existed on the\n * wire: a late ACK for the real one finds no graveyard entry and is reported\n * as unmatched, misdirecting exactly the operator who is trying to trace a\n * lost message. (The diagnostics themselves report the ID's byte length and\n * never the ID, so a masked ID cannot reach a log line; a masked KEY still\n * breaks correlation.)\n *\n * Reachable whenever MSH-18 declares a non-ASCII charset (e.g. `8859/1`), where\n * high-bit bytes in a control ID are legal.\n *\n * **`latin1` is the only byte-faithful decode available.** It is not merely the\n * best of several, it is the one that exists. Node's `Buffer` `latin1` codec is\n * true ISO-8859-1: a 1:1 map between the 256 byte values and U+0000–U+00FF, so\n * `Buffer.from(buf.toString(\"latin1\"), \"latin1\")` is the identity for *every*\n * byte string. Every alternative loses bytes in exactly the range that matters:\n *\n * * `ascii` masks the high bit (above).\n * * `utf8` folds every invalid sequence onto `U+FFFD`, so all high-bit bytes in\n * a non-UTF-8 payload collapse onto one key, the same collision as `ascii`.\n * * `TextDecoder(\"iso-8859-1\")`, which is what a charset-driven decode such as\n * `@cosyte/hl7`'s `parseHL7(buffer, { charset })` uses, is **not** ISO-8859-1\n * at all. The WHATWG Encoding Standard aliases the label `iso-8859-1` to\n * **windows-1252**, which maps 0x80–0x9F to typography (`0x8B` → `U+2039`,\n * `0x9C` → `U+0153`) rather than to `U+008B`/`U+009C`. Re-encoding that back\n * to bytes does not round-trip. That is precisely the byte range a high-bit\n * control ID lives in, which is why this package decodes control-ID bytes\n * itself rather than delegating to a charset-aware parser.\n *\n * The key is byte-faithful, not text: under a multi-byte charset (`UNICODE UTF-8`)\n * a control ID reads back as its `latin1` bytes. Correlation stays correct, the\n * map is injective for any charset, which is the property the key needs, and the\n * one a charset-driven decode would lose.\n *\n * @internal\n */\nexport const CONTROL_ID_ENCODING = \"latin1\" as const;\n\n/**\n * MSH-1 values we refuse to read a message with.\n *\n * MSH-1 *is* the field separator (§2.5.4), but a segment terminator (`CR`/`LF`) or\n * an MLLP framing byte (`VT`/`FS`) cannot be one: the first would end the segment it\n * is supposed to delimit, and the second cannot be written back into an ACK without\n * making the ACK unframeable (strict `encodeFrame` throws `MLLP_PAYLOAD_CONTAINS_VT`/`_FS`).\n *\n * **How a payload with such a byte reaches us.** The plainest route is that `buildRawAck` is\n * a **public export**, so a caller can hand any `Buffer` straight to it, decoder or no\n * decoder; these scanners also run on **outbound** payloads the caller supplies. But it can\n * come off the wire too: a `VT` mid-payload makes `FrameReader` discard what it accumulated\n * and start over (`MLLP_TRAILING_BYTES`), so a *delivered* payload never contains a `VT`, but\n * a delivered payload CAN contain an `FS`. Under the `allowMissingLeadingVt` tolerance a non-VT,\n * non-whitespace first byte is taken as payload byte 0, and `FS` (0x1C) is neither, so\n * `FS \"MSH…\" FS CR` delivers a payload whose byte 0 is `0x1C`. Either way the guard is\n * warranted, and neither route depends on the false premise that the decoder screens these out.\n * @internal\n */\nconst UNSAFE_FIELD_SEPARATORS: ReadonlySet<number> = new Set([\n SEGMENT_SEPARATOR_CR,\n SEGMENT_SEPARATOR_LF,\n 0x0b, // VT, MLLP start block\n 0x1c, // FS, MLLP end block\n]);\n\n/** True iff the `CR`/`LF`-delimited segment starting at `i` is an `MSH`. @internal */\nfunction isMshSegmentAt(buf: Buffer, i: number): boolean {\n return (\n i + 3 < buf.length &&\n buf[i] === ASCII_M &&\n buf[i + 1] === ASCII_S &&\n buf[i + 2] === ASCII_H &&\n !UNSAFE_FIELD_SEPARATORS.has(buf[i + 3] as number)\n );\n}\n\n/** Index of the byte after the `CR`/`LF`-delimited segment starting at `i`. @internal */\nfunction segmentEnd(buf: Buffer, i: number): number {\n let end = i;\n while (\n end < buf.length &&\n buf[end] !== SEGMENT_SEPARATOR_CR &&\n buf[end] !== SEGMENT_SEPARATOR_LF\n ) {\n end++;\n }\n return end;\n}\n\n/**\n * Locate the message's `MSH` segment: the **first** `CR`/`LF`-delimited segment whose\n * first three bytes are `MSH` and whose 4th byte is a usable field separator. Returns\n * its `[start, end)` bounds, or `null` if the payload has no such segment.\n *\n * ## Why we search rather than demand `MSH` at byte 0\n *\n * §2.5.1 does make `MSH` the first segment of a *message*, and it is tempting to read\n * that as \"byte 0 or it is not HL7\", that is what an earlier version of this module\n * did, to force its three consumers into agreement. It was a **tolerance regression**,\n * and it caused the exact harm this module exists to prevent. Two shapes reach us with\n * a perfectly good MSH-10 that is not at byte 0:\n *\n * * **A leading `CR`.** The MLLP decoder passes it straight through into the payload\n * (it special-cases only `VT`/`FS`), and real senders emit it.\n * * **A batch header.** `FHS`/`BHS` precede the `MSH` (§2.10.3).\n *\n * Under the byte-0 rule both read as \"no MSH\", so `buildRawAck` emitted a **positive\n * `AA` with an empty MSA-2 and no warning**: the sender, which keyed on the MSH-10 it\n * sent, cannot correlate it, times out, resends, and the receiver commits a\n * **duplicate clinical message**. Silently discarding a field that is *present* is the\n * worst thing a lenient reader can do, and this package's decoder is emphatically\n * lenient (Postel's Law; see CLAUDE.md).\n *\n * The consumers still agree, they simply agree at the **tolerant** fixed point rather\n * than the lossy one. Agreement was never the hard part; agreeing on the *right* answer\n * is.\n * @internal\n */\nfunction findMshSegment(buf: Buffer): { start: number; end: number } | null {\n let segStart = 0;\n while (segStart < buf.length) {\n const end = segmentEnd(buf, segStart);\n if (isMshSegmentAt(buf, segStart)) return { start: segStart, end };\n // Advance past this segment's terminator bytes (handles `CRLF` as one break).\n let next = end;\n while (\n next < buf.length &&\n (buf[next] === SEGMENT_SEPARATOR_CR || buf[next] === SEGMENT_SEPARATOR_LF)\n ) {\n next++;\n }\n if (next === segStart) return null; // no progress, malformed\n segStart = next;\n }\n return null;\n}\n\n/**\n * True iff the `CR`/`LF`-delimited segment starting at `i` is an HL7 **batch/file\n * envelope** header, `FHS`, `BHS`, `BTS`, or `FTS` (§2.10.3). Matched on the\n * three uppercase ASCII segment-ID bytes only; the 4th byte (the separator) is not\n * inspected, because the mere *presence* of a batch segment is what disqualifies a\n * single-message ACK, regardless of that segment's own delimiter. @internal\n */\nfunction isBatchEnvelopeSegmentAt(buf: Buffer, i: number): boolean {\n if (i + 2 >= buf.length) return false;\n const a = buf[i] as number;\n const b = buf[i + 1] as number;\n const c = buf[i + 2] as number;\n // FHS / FTS\n if (a === ASCII_F && ((b === ASCII_H && c === ASCII_S) || (b === ASCII_T && c === ASCII_S))) {\n return true;\n }\n // BHS / BTS\n if (a === ASCII_B && ((b === ASCII_H && c === ASCII_S) || (b === ASCII_T && c === ASCII_S))) {\n return true;\n }\n return false;\n}\n\n/**\n * True iff the payload is **not a single bare HL7 message**, it carries a batch/file\n * envelope (`FHS`/`BHS`/`BTS`/`FTS`, §2.10.3) or a **second `MSH` segment** (two or more\n * messages concatenated in one frame, a documented real-world quirk).\n *\n * ## Why this is a fail-safe refusal signal, not tolerance\n *\n * A single MSA-2 can echo exactly **one** MSH-10. A batch is a *sequence* of messages with\n * a `BTS-1` count; a concatenated frame is N messages back to back. Either way, a positive\n * `AA` that names the first control ID silently leaves messages 2..N unacknowledged, the\n * sender reads \"batch accepted\", so they are lost outright or time out and resend as\n * **duplicate clinical messages**. This predicate lets the ACK builders **downgrade to a\n * loud non-positive answer** for such a payload rather than fabricate a positive\n * disposition for messages nobody looked at.\n *\n * It does **not** parse the batch, re-base on the first `MSH`, or otherwise widen any\n * reader, it only *detects* the multi-message shape so callers can refuse it. Batch ACK\n * is its own feature; this is the refusal that keeps the door shut until it\n * is designed. A normal single message (`MSH` followed by `PID`/`PV1`/`OBR`/…) contains no\n * batch segment and exactly one `MSH`, so this returns `false` for it.\n *\n * Pure byte-level, never throws.\n *\n * @internal\n */\nexport function containsBatchOrExtraMessage(buf: Buffer): boolean {\n let mshCount = 0;\n let segStart = 0;\n while (segStart < buf.length) {\n const end = segmentEnd(buf, segStart);\n if (isMshSegmentAt(buf, segStart)) {\n mshCount++;\n if (mshCount > 1) return true;\n } else if (isBatchEnvelopeSegmentAt(buf, segStart)) {\n return true;\n }\n // Advance past this segment's terminator bytes (handles `CRLF` as one break).\n let next = end;\n while (\n next < buf.length &&\n (buf[next] === SEGMENT_SEPARATOR_CR || buf[next] === SEGMENT_SEPARATOR_LF)\n ) {\n next++;\n }\n if (next === segStart) return false; // no progress, malformed\n segStart = next;\n }\n return false;\n}\n\n/** The MSH segment, decoded and split into its fields. @internal */\nexport interface MshSegment {\n /** MSH-1, the field separator this message declares (§2.5.4). One `latin1` char. */\n readonly fieldSep: string;\n /**\n * The MSH segment split on {@link fieldSep}. `[0]` is the literal `\"MSH\"`, `[1]` is\n * MSH-2 (the encoding characters), and thereafter the index **is** the field number:\n * `[9]` is MSH-10, `[10]` is MSH-11, `[11]` is MSH-12.\n */\n readonly fields: readonly string[];\n}\n\n/**\n * Read the MSH segment of an HL7 v2 payload, **the** scan, which every consumer in\n * this package goes through.\n *\n * Pure byte-level, never throws, returns `null` for anything it cannot read (Postel's\n * Law decoder side; CLAUDE.md guardrail). It:\n *\n * * **locates** the MSH, the first `CR`/`LF`-delimited segment that starts with\n * `MSH`, rather than demanding it at byte 0, so a leading `CR` or an `FHS`/`BHS`\n * batch header cannot hide a control ID that is plainly there ({@link findMshSegment});\n * * takes the field separator from MSH-1 rather than assuming `|` (§2.5.4);\n * * **bounds the field scan at that segment's terminator**;\n * * decodes only that segment, as `latin1` (see {@link CONTROL_ID_ENCODING}), so a\n * high-bit byte survives and a 16 MB payload is not decoded to read its header.\n *\n * ## The segment terminator is load-bearing, not a detail\n *\n * An earlier version of this scan counted field separators without ever stopping at\n * the segment terminator. On a **truncated MSH**, `MSH|^~\\&|EPIC|HOSP|MIRTH|LAB\\r`,\n * which has only 6 fields, the count therefore ran *past the `CR`* and kept counting\n * separators inside the next segment. The \"MSH-10\" it returned was `PID-3`: the\n * patient's **MRN**. That value became the client's correlation key, and was carried\n * into the ACK timeout error and the `MLLP_ACK_UNMATCHED_CONTROL_ID` /\n * `MLLP_ACK_AFTER_TIMEOUT` warnings, a patient identifier in a log line, and a\n * mis-read one at that. Those diagnostics now report byte lengths only, so that\n * particular route out is closed; the reader must still be right, because a field\n * that does not exist must read as absent, never as the next segment's contents,\n * and the correlation KEY is still the value this returns.\n *\n * The two rules are independent, and both are needed. Bounding the scan is what kills\n * the PID-3 read. **Locating** the MSH (rather than demanding it at byte 0) is what\n * stops the bound from turning into a tolerance regression that silently drops a\n * control ID which is present, see {@link findMshSegment}.\n *\n * @example\n * ```typescript\n * const msh = readMshSegment(payloadBuffer);\n * // msh?.fields[9] === 'MSG00001'; msh?.fieldSep === '|'\n * ```\n *\n * @internal\n */\nexport function readMshSegment(buf: Buffer): MshSegment | null {\n const at = findMshSegment(buf);\n if (at === null) return null;\n\n // The separator is MSH-1, the 4th byte OF THE MSH SEGMENT, wherever that segment\n // begins. `findMshSegment` has already rejected an unusable one.\n const fieldSep = String.fromCharCode(buf[at.start + 3] as number);\n // `at.end` is the segment's terminator: the field split cannot reach past it.\n const fields = buf.subarray(at.start, at.end).toString(CONTROL_ID_ENCODING).split(fieldSep);\n return { fieldSep, fields };\n}\n\n/**\n * Strip **leading segment terminators only**, `CR`/`LF` bytes before the first segment.\n *\n * This is the whole of what the parser-backed ACK builder needs re-based, and it is\n * deliberately the *minimum*. `@cosyte/hl7`'s `parseHL7` requires `MSH` to be the first\n * segment and throws `NO_MSH_SEGMENT` otherwise, so a leading `CR`, which the MLLP\n * decoder passes straight through into the payload, would otherwise send `buildMllpAck`\n * down its unparseable fallback for a message whose MSH-10 is plainly readable. A leading\n * `CR`/`LF` is pure segment-terminator noise: it carries **no data**, so dropping it\n * cannot hide anything.\n *\n * ## What this deliberately does NOT do: skip an `FHS`/`BHS` batch header\n *\n * An earlier version of this function re-based on the *located `MSH`*, which also skipped\n * a batch envelope. That was a serious mistake. A batch (§2.10.3) is\n * `[FHS] { [BHS] { MSH … } [BTS] } [FTS]`, a **sequence of messages**. Re-basing on the\n * first `MSH` handed `parseHL7` message 1 and silently discarded every later `MSH`, the\n * `BTS` count, and the `FTS`. `buildMllpAck` then returned a confident positive **`AA`\n * correlated to message 1, with zero warnings**, for a batch whose messages 2..N it had\n * never looked at. The sender reads that as \"the batch is accepted\", so those messages\n * are lost outright, or time out and resend as **duplicate clinical messages**.\n *\n * Batch ACK is a real feature and is tracked separately. It is not something to arrive at\n * by accident, via a byte-offset helper, on the way to fixing something else. Until it is\n * built deliberately, an `FHS`/`BHS` envelope must fall through to `parseHL7`'s\n * `NO_MSH_SEGMENT` and out into the **warned, non-positive `AE` fallback**, a loud\n * refusal to ACK what we did not read. That is the fail-safe answer and it is what the\n * package did before this item touched it.\n *\n * @internal\n */\nexport function stripLeadingSegmentTerminators(buf: Buffer): Buffer {\n let i = 0;\n while (i < buf.length && (buf[i] === SEGMENT_SEPARATOR_CR || buf[i] === SEGMENT_SEPARATOR_LF)) {\n i++;\n }\n return i === 0 ? buf : buf.subarray(i);\n}\n\n/**\n * Extract MSH-10 (Message Control ID) from an HL7 v2 payload, the correlation key.\n *\n * A thin read off {@link readMshSegment}: `null` when the payload has no readable MSH,\n * when the MSH segment is too short to reach MSH-10, or when MSH-10 is present but\n * empty. It is **never** a value taken from another segment.\n *\n * @example\n * ```typescript\n * const id = extractMshControlId(payloadBuffer);\n * // id === 'MSG00001' | null\n * ```\n *\n * @internal\n */\nexport function extractMshControlId(buf: Buffer): string | null {\n const msh = readMshSegment(buf);\n if (msh === null) return null;\n const id = msh.fields[9];\n return id === undefined || id === \"\" ? null : id;\n}\n\n/**\n * Extract MSA-2 (acknowledged Message Control ID) from an HL7 v2 ACK payload.\n *\n * Pure byte-level scan, never throws, returns `null` for malformed input. The field\n * separator comes from the ACK's own MSH-1, read through {@link readMshSegment}, the\n * same tolerant locate every other read in this module uses, so an ACK whose `MSH` is\n * not at byte 0 is still read rather than silently discarded. The `MSA` segment is\n * then located by scanning segment boundaries (`\\r` / `\\n`).\n *\n * Decoded as `latin1` (see {@link CONTROL_ID_ENCODING}), matching both\n * {@link extractMshControlId} and `buildRawAck`'s verbatim MSH-10 → MSA-2 echo,\n * so an ACK for a high-bit control ID looks up the key the send actually\n * enqueued.\n *\n * @example\n * ```typescript\n * const acked = extractMsaControlId(ackPayloadBuffer);\n * // acked === 'MSG00001' | null\n * ```\n *\n * @internal\n */\nexport function extractMsaControlId(buf: Buffer): string | null {\n // MSH-1 establishes the separator for the whole message, including its MSA.\n const msh = readMshSegment(buf);\n if (msh === null) return null;\n const fieldSep = msh.fieldSep.charCodeAt(0);\n const end = buf.length;\n let segStart = 0;\n while (segStart < end) {\n let segEnd = segStart;\n while (\n segEnd < end &&\n buf[segEnd] !== SEGMENT_SEPARATOR_CR &&\n buf[segEnd] !== SEGMENT_SEPARATOR_LF\n ) {\n segEnd++;\n }\n if (\n segEnd - segStart >= 4 &&\n buf[segStart] === ASCII_M &&\n buf[segStart + 1] === ASCII_S &&\n buf[segStart + 2] === ASCII_A &&\n buf[segStart + 3] === fieldSep\n ) {\n // MSA fields: [0]'MSA' [1]MSA-1 [2]MSA-2 ...\n // Iterate from the first separator (segStart+3) up to and including\n // segEnd. Treat `segEnd` as a synthetic separator so the final field\n // closes cleanly even without a trailing CR.\n let fieldIndex = 0;\n let fieldStart = 0;\n for (let i = segStart + 3; i <= segEnd; i++) {\n const b = i < segEnd ? (buf[i] as number) : fieldSep;\n if (b === fieldSep || b === SEGMENT_SEPARATOR_CR || b === SEGMENT_SEPARATOR_LF) {\n fieldIndex++;\n if (fieldIndex === 2) {\n fieldStart = i + 1;\n } else if (fieldIndex === 3) {\n if (fieldStart >= i) return null; // empty MSA-2\n return buf.subarray(fieldStart, i).toString(CONTROL_ID_ENCODING);\n }\n }\n }\n return null;\n }\n // Skip segment terminator bytes to advance to the next segment.\n while (\n segEnd < end &&\n (buf[segEnd] === SEGMENT_SEPARATOR_CR || buf[segEnd] === SEGMENT_SEPARATOR_LF)\n ) {\n segEnd++;\n }\n if (segEnd === segStart) return null; // no progress, malformed\n segStart = segEnd;\n }\n return null;\n}\n","/**\n * Server-side MLLP acknowledgement construction and the **fail-safe commit contract**.\n *\n * This module owns the byte-level ACK builder used by {@link MllpServer}'s auto-ACK\n * path, the HL7 Table 0008 acknowledgement-code union, and the error type a message\n * handler throws to request a specific negative acknowledgement.\n *\n * ## Why this is safety-critical\n *\n * A positive acknowledgement (`AA`) tells the sender \"you may forget this message, I\n * have it.\" If a server emits `AA` *before* the message is durably handled and then the\n * process crashes, the message is **silently lost** with no record on either side. For\n * clinical messages (an admit, an order, a result) that is a patient-safety failure.\n *\n * The contract this module enforces: a positive ACK (`AA`) is only built once the\n * application handler has resolved success. A handler that throws or rejects yields a\n * **negative** ACK (`AE` by default, `AR` when the handler asks for it), never `AA`.\n *\n * Spec: HL7 v2.5.1 ch. 2 §2.9.2 (original acknowledgement mode), §2.9.3 (enhanced\n * accept/application acknowledgement), Table 0008 (Acknowledgment Code), §2.9.2.2\n * (MSA-2 echoes the inbound MSH-10 message control ID).\n *\n * @packageDocumentation\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport { containsBatchOrExtraMessage, readMshSegment } from \"../internal/control-id.js\";\n\n/**\n * HL7 Table 0008, Acknowledgment Code. A **stable public API**.\n *\n * Two families, by mode (HL7 v2.5.1 §2.9):\n *\n * - **Original mode (§2.9.2):** `AA` accept, `AE` application error, `AR` application\n * reject. The single ACK reports application-level outcome.\n * - **Enhanced mode (§2.9.3):** `CA` commit accept, `CE` commit error, `CR` commit\n * reject, the *accept* acknowledgement, distinct from a later application ACK.\n *\n * `AE` vs `AR`: `AE` is a processing **error** (the sender may resend later, e.g. a\n * transient downstream outage); `AR` is a **reject** (the sender should not resend the\n * message unchanged, e.g. it is structurally unacceptable). `@cosyte/mllp` builds\n * original-mode ACKs; the `C*` codes are surfaced in the type for completeness and for\n * callers that build their own enhanced-mode ACKs via `autoAck: fn`.\n *\n * @example\n * ```typescript\n * import type { AckCode } from '@cosyte/mllp';\n * const code: AckCode = 'AE';\n * ```\n */\nexport type AckCode = \"AA\" | \"AE\" | \"AR\" | \"CA\" | \"CE\" | \"CR\";\n\n/**\n * The negative original-mode acknowledgement codes a failed handler can produce.\n *\n * @example\n * ```typescript\n * import type { NegativeAckCode } from '@cosyte/mllp';\n * const code: NegativeAckCode = 'AR';\n * ```\n */\nexport type NegativeAckCode = \"AE\" | \"AR\";\n\n/**\n * Error a message handler throws to control the negative acknowledgement the server\n * returns. Throwing **any** error from a commit-gated handler yields `AE` by default;\n * throw an `MllpAckError` to choose `AR` (application reject) instead.\n *\n * The `message` is **never** copied into the ACK bytes or any emitted event, it may\n * carry PHI. Only the static, non-PHI `ackCode` influences the wire response.\n *\n * @example\n * ```typescript\n * import { createServer, MllpAckError } from '@cosyte/mllp';\n *\n * createServer({\n * autoAck: 'AA',\n * onMessage: async (payload) => {\n * if (!isAcceptable(payload)) {\n * // sender should NOT resend unchanged -> AR\n * throw new MllpAckError('unsupported message type', { ackCode: 'AR' });\n * }\n * await db.commit(payload); // throw here -> AE (resend may succeed)\n * },\n * });\n * ```\n */\nexport class MllpAckError extends Error {\n /** The negative acknowledgement code to return (`AE` or `AR`). */\n readonly ackCode: NegativeAckCode;\n\n /**\n * @param message - Diagnostic text for the thrower. Never placed on the wire (may carry PHI).\n * @param opts.ackCode - Negative code to return. Default `'AE'`.\n * @param opts.cause - Underlying error, preserved on `.cause`.\n */\n constructor(message: string, opts?: { ackCode?: NegativeAckCode; cause?: unknown }) {\n super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = \"MllpAckError\";\n this.ackCode = opts?.ackCode ?? \"AE\";\n }\n}\n\n/**\n * Resolve the negative acknowledgement code for a handler failure.\n *\n * An {@link MllpAckError} carries an explicit `ackCode`; any other thrown value maps to\n * `AE` (application error, the default, since most handler failures are transient and\n * a resend may succeed).\n *\n * @example\n * ```typescript\n * import { resolveNackCode, MllpAckError } from '@cosyte/mllp';\n * resolveNackCode(new Error('boom')); // 'AE'\n * resolveNackCode(new MllpAckError('nope', { ackCode: 'AR' })); // 'AR'\n * ```\n */\nexport function resolveNackCode(err: unknown): NegativeAckCode {\n if (err instanceof MllpAckError) return err.ackCode;\n if (typeof err === \"object\" && err !== null && \"ackCode\" in err && err.ackCode === \"AR\") {\n return \"AR\";\n }\n return \"AE\";\n}\n\n/** Static, PHI-free MSA-3 text for negative acknowledgements. Never derived from payload. */\nconst NACK_TEXT: Readonly<Record<NegativeAckCode, string>> = Object.freeze({\n AE: \"message could not be processed\",\n AR: \"message rejected\",\n});\n\n/** HL7 default encoding characters (MSH-2), used when the inbound declares none. */\nconst DEFAULT_ENCODING_CHARACTERS = \"^~\\\\&\";\n\n/** HL7 default field separator (MSH-1), used when the inbound declares none usable. */\nconst DEFAULT_FIELD_SEPARATOR = \"|\";\n\n/**\n * Stand-in for whichever HL7 default encoding character collides with the inbound's MSH-1.\n *\n * Reached only when an inbound declares a field separator that is one of `^ ~ \\ &` AND declares no\n * usable MSH-2 of its own, a message already malformed twice over (§2.16 requires MSH-2). We\n * substitute the one colliding character rather than change the ACK's field separator, because\n * `fieldSep` is the only byte that can truncate MSA-2 (see {@link buildRawAck}).\n *\n * `#` is chosen because it is not one of the four HL7 defaults, so the substitution can never\n * collide with the three characters it sits beside; and it is not `CR`/`LF`/`VT`/`FS`, so it cannot\n * break segmentation or framing. It is only ever *a* delimiter for a message that declared none.\n */\nconst ENCODING_CHAR_SUBSTITUTE = \"#\";\n\n/**\n * Encoding characters (MSH-2) we refuse to echo into the ACK we build.\n *\n * The inbound's MSH-1/MSH-2 are echoed so the ACK's *structure* matches the\n * message it answers, but a delimiter is a byte we then write ourselves, all\n * over the ACK. `VT` (0x0B) and `FS` (0x1C) are the MLLP framing bytes, and `CR`\n * / `LF` are segment terminators: any of them adopted as an encoding character\n * would put that byte at every component boundary of the ACK. Fall back to the\n * HL7 defaults.\n *\n * **Where such a payload comes from.** The plainest route is that `buildRawAck` is a\n * **public export**: a caller can hand it any `Buffer` at all, having never gone near the\n * decoder. It can also arrive off the wire, a mid-payload `VT` makes `FrameReader` discard\n * what it accumulated and start over (`MLLP_TRAILING_BYTES`), so a *delivered* payload never\n * contains a `VT`, but a delivered payload CAN contain an `FS`: under the\n * `allowMissingLeadingVt` tolerance a non-VT, non-whitespace first byte becomes payload byte 0,\n * and `FS` (0x1C) is neither. (The `MLLP_PAYLOAD_CONTAINS_VT`/`_FS` codes are thrown by\n * **`encodeFrame`**, on the way out, never by the decoder, on the way in.) The guard does not\n * rest on the decoder screening these out; it rests on the public export.\n *\n * **It guards the DELIMITERS ONLY, it is not a guarantee that the ACK frames.** A\n * `VT`/`FS` inside echoed field *content* (MSH-3..6, MSH-10) is still copied verbatim,\n * so such a caller can still produce an ACK payload carrying a framing byte, and strict\n * `encodeFrame` will throw on it. Echoing content verbatim is required by §2.9.2.2 and\n * this builder will not corrupt a control ID to make its own framing easier; the\n * delimiter guard exists only so that a nonsense MSH-1/MSH-2 cannot *multiply* one stray\n * byte across every field boundary of the ACK.\n *\n * The equivalent rule for MSH-1 lives in `src/internal/control-id.ts`, where it\n * belongs: an unusable field separator makes the message unreadable for *every*\n * consumer, not just for this builder, and `readMshSegment` returning `null` is\n * what keeps them all agreeing on that.\n */\nconst UNSAFE_DELIMITER = /[\\r\\n\\v\\x1c]/;\n\n/**\n * True iff a **positive** raw acknowledgement (`AA`/`CA`) cannot be safely correlated to\n * this payload, so {@link buildRawAck} (and the server's auto-ACK path) must **downgrade**\n * it to a non-positive `AE`/`CE` rather than tell the sender \"I have it.\"\n *\n * The rule this enforces: **never answer `AA` for a message you could not correlate.** A\n * positive ACK is a promise the sender may forget the message; if it names a control ID\n * the sender cannot match, or names one of several messages it never read, the sender\n * times out and resends, committing a **duplicate clinical message** (or worse, believes a\n * destroyed message was delivered). Three payload-shaped reasons make a positive ACK\n * uncorrelatable, all peer-reachable off the wire:\n *\n * 1. **No readable `MSH`.** `readMshSegment` returns `null`, e.g. a `BOM`/`SP`/`TAB`\n * before `MSH` (which shares the `MSH`'s segment line, so `MSH` heads no segment), or\n * a bare fragment delivered after a mid-payload `VT` discard (`MLLP_TRAILING_BYTES`).\n * MSA-2 would be empty: an ACK that correlates to nothing.\n * 2. **Empty MSH-10.** The `MSH` is readable but carries no message control ID, so there\n * is nothing to echo, again `MSA|AA|` with an empty MSA-2.\n * 3. **A batch or concatenated messages.** An `FHS`/`BHS`/`BTS`/`FTS` envelope (§2.10.3)\n * or a second `MSH` in the same frame: a single MSA-2 can echo only ONE control ID, so\n * a positive ACK naming the first silently drops the rest (see\n * {@link containsBatchOrExtraMessage}). Batch ACK is its own feature; until it is\n * designed, a batch must stay a **loud non-positive** answer.\n *\n * This is a **refusal**, not a tolerance widening: it never makes an unreadable message\n * readable, never re-bases on a located `MSH`, never parses a batch. It only recognizes\n * the shapes for which a positive disposition would be a lie, so the builder can fall back\n * to `AE`. A **negative** requested code (`AE`/`AR`/`CE`/`CR`) is unaffected, it is\n * already non-positive, and echoing whatever control ID it can find is still useful.\n *\n * Pure, byte-level, never throws.\n *\n * @example\n * ```typescript\n * import { rawAckUncorrelatable } from '@cosyte/mllp';\n * rawAckUncorrelatable(oneGoodMessage); // false → AA is safe\n * rawAckUncorrelatable(twoConcatenatedMsh); // true → downgrade AA to AE\n * ```\n */\nexport function rawAckUncorrelatable(payload: Buffer): boolean {\n const msh = readMshSegment(payload);\n if (msh === null) return true; // (1) no readable MSH\n const controlId = msh.fields[9];\n if (controlId === undefined || controlId === \"\") return true; // (2) empty MSH-10\n return containsBatchOrExtraMessage(payload); // (3) batch / concatenated messages\n}\n\n/** Non-positive counterpart of a positive HL7 Table 0008 code: `AA`→`AE`, `CA`→`CE`. @internal */\nfunction downgradePositiveAck(code: AckCode): AckCode {\n if (code === \"AA\") return \"AE\";\n if (code === \"CA\") return \"CE\";\n return code;\n}\n\n/**\n * Build a minimal original-mode HL7 v2 acknowledgement from raw inbound payload bytes,\n * **without a parser** (parser-driven ACKs are the `@cosyte/mllp/ack-from-hl7` subpath).\n *\n * Locates the `MSH` segment (the first `CR`/`LF`-delimited segment starting with `MSH`,\n * so a leading `CR` or an `FHS`/`BHS` batch header does not hide it), reads the field\n * separator from **MSH-1** and the encoding characters from **MSH-2**, then splits that\n * segment on that separator to read fields. The ACK swaps sender/receiver per HL7 ACK\n * rules, echoes the inbound MSH-10 into MSA-2 (§2.9.2.2), sets MSA-1 to `code`, and, for\n * negative codes, adds a **static, PHI-free** MSA-3 reason. A fresh control ID fills the\n * ACK's own MSH-10.\n *\n * ## Why the delimiters are read, not assumed\n *\n * MSH-1 **is** the field separator (HL7 v2.5.1 §2.5.4): the byte at offset 3 of the MSH\n * segment *defines* it for the whole message. `|` is overwhelmingly common but it is a\n * convention, not the spec. Assuming it was wrong in two compounding ways:\n *\n * 1. **Reading.** Splitting a `!`-delimited message on `|` yields ONE field, so every\n * echoed field, including MSH-10, came back empty. The ACK went out as\n * `MSA|AA|` with **no correlation id at all**: the sender cannot match it, times\n * out, and resends → a duplicate clinical message. That is the exact failure this\n * package's correlator exists to prevent, manufactured by the ACK builder itself.\n * 2. **Writing.** The echoed MSH-3..6 and MSH-10 field *content* is copied verbatim\n * from the inbound, still escaped against the **inbound's** encoding characters. Re-\n * emitting that content under a different delimiter set silently reinterprets it:\n * an inbound whose component separator is `#` and whose MSH-10 is `ID#X` would be\n * re-emitted under `^~\\&` as the literal `ID#X`, which the sender then reads as a\n * single component rather than two. Echoing MSH-1 and MSH-2 keeps the content and\n * the delimiters that define it together.\n *\n * ## It reads the MSH through the SHARED scanner\n *\n * The MSH read is `readMshSegment` from `src/internal/control-id.ts`, the same call the\n * client's correlator makes to derive the key it will later match this ACK against. That\n * is deliberate and it is the whole point: this builder used to re-derive the read itself\n * (`payload.toString(\"latin1\").split(\"\\r\")`, hunting for an `MSH` anywhere in the\n * payload), and the two disagreed on real inputs. On a **truncated MSH** followed by a\n * `PID` the correlator keyed on the PID's MRN while this builder echoed an empty MSA-2. On\n * a payload with a **leading `CR`**, which the MLLP decoder passes straight through, and\n * which real senders emit, this builder found the `MSH` and echoed MSH-10 correctly while\n * the correlator, requiring `MSH` at byte 0, gave up. Every such disagreement is an ACK the\n * sender cannot match → timeout → resend → **duplicate clinical message**.\n *\n * The fix is one scan, but note *which* scan. The first attempt made them agree by\n * requiring `MSH` at byte 0 everywhere, which \"resolved\" the leading-`CR` disagreement by\n * degrading the side that had been **right**: `buildRawAck` began emitting a positive `AA`\n * with an empty MSA-2, silently, for a message whose MSH-10 was plainly present. Agreement\n * is not the goal; agreeing on the *correct*, *tolerant* answer is. A lenient reader may\n * never drop data that is there (Postel's Law, CLAUDE.md).\n *\n * ## The fail-safe downgrade: never a positive `AA`/`CA` it cannot correlate\n *\n * A positive acknowledgement is a promise the sender may forget the message. If it names a\n * control ID the sender cannot match, or names one message out of several it never read,\n * the sender times out and resends, committing a **duplicate clinical message**. So a\n * requested positive code is **downgraded** to its non-positive counterpart (`AA`→`AE`,\n * `CA`→`CE`) whenever the payload cannot carry a correlatable positive ACK: no readable\n * `MSH`, an empty MSH-10, or a batch/concatenated-message shape that a single MSA-2 cannot\n * acknowledge. See {@link rawAckUncorrelatable} for the exact conditions and why each is a\n * refusal rather than a widened reader. A requested **negative** code (`AE`/`AR`/`CE`/`CR`)\n * is never touched. This mirrors the parser-backed `buildMllpAck`, which downgrades and\n * warns on an unparseable inbound, the two builders' fail-safe semantics now agree.\n *\n * **Never throws** and **never copies payload content** beyond the routing/control\n * metadata above, `readMshSegment` stops at the MSH's segment terminator, so no field of\n * any later segment (PID and friends) can be reached, let alone echoed. On a missing or\n * unreadable `MSH` it returns a minimal well-formed ACK carrying the (downgraded) `code` so\n * the caller can still respond.\n *\n * @param payload - Raw decoded HL7 v2 payload bytes (MLLP framing already stripped).\n * @param code - Requested MSA-1 acknowledgement code. A positive `AA`/`CA` is downgraded to\n * `AE`/`CE` when the message cannot be correlated (see above).\n * @returns ACK payload bytes (no framing, the caller wraps with `encodeFrame`).\n *\n * @example\n * ```typescript\n * import { buildRawAck } from '@cosyte/mllp';\n * const ack = buildRawAck(inboundPayload, 'AE'); // MSA|AE|<inbound MSH-10>|message could not be processed\n * ```\n */\nexport function buildRawAck(payload: Buffer, code: AckCode): Buffer {\n // THE shared read (see the docblock). `latin1`, MSH-1 taken from the message, scan bounded\n // at the segment terminator. `null` means \"this package cannot read this message\", and it\n // means that for the correlator too, which is exactly the agreement we need.\n const msh = readMshSegment(payload);\n\n // FAIL-SAFE: never emit a positive disposition (`AA`/`CA`) for a message we cannot correlate.\n // A positive ACK the sender cannot match → timeout → resend → duplicate clinical message.\n // The downgrade is defense in depth here (a direct caller of this public export is protected\n // even if it never touched the server), and the server's auto-ACK path applies the SAME\n // predicate so it can emit a `'nack'` observability signal alongside. See\n // {@link rawAckUncorrelatable}. A requested negative code passes through unchanged.\n if ((code === \"AA\" || code === \"CA\") && rawAckUncorrelatable(payload)) {\n code = downgradePositiveAck(code);\n }\n\n const newControlId = randomUUID().replace(/-/g, \"\").substring(0, 20);\n const now = timestamp14();\n\n if (msh === null) {\n // No usable MSH to echo: emit a well-formed ACK carrying the (already fail-safe-downgraded)\n // code, no payload content. HL7 defaults, since the inbound declared no delimiters we can\n // trust. A positive `AA`/`CA` has been turned into `AE`/`CE` above, there is no control ID\n // to correlate, so a positive disposition here would be exactly the uncorrelatable lie.\n const s = DEFAULT_FIELD_SEPARATOR;\n const e = DEFAULT_ENCODING_CHARACTERS;\n const tail = code === \"AE\" || code === \"AR\" ? `${s}${NACK_TEXT[code]}` : \"\";\n return Buffer.from(\n `MSH${s}${e}${s}${s}${s}${s}${s}${now}${s}${s}ACK${s}${newControlId}${s}P${s}2.3\\r` +\n `MSA${s}${code}${s}${tail}\\r`,\n \"latin1\",\n );\n }\n\n const { fieldSep, fields } = msh;\n // MSH field indices after splitting on the field separator:\n // [1]=MSH-2 encoding chars\n // [2]=sendingApp [3]=sendingFacility [4]=receivingApp [5]=receivingFacility\n // [9]=MSH-10 control ID [10]=processingId [11]=version\n const declaredEnc = fields[1] ?? \"\";\n const usableEnc =\n declaredEnc !== \"\" && !UNSAFE_DELIMITER.test(declaredEnc)\n ? declaredEnc\n : DEFAULT_ENCODING_CHARACTERS;\n\n // MSH-2 must not contain MSH-1 (§2.5.4, §2.16, the delimiters are distinct characters).\n // `declaredEnc` structurally cannot: it is a product of splitting ON `fieldSep`. The DEFAULT\n // fallback can, an inbound declaring MSH-1 = `^` with no usable MSH-2 would take `^~\\&`, whose\n // first character IS the field separator, so the ACK reads back with an EMPTY MSH-2 and every\n // later MSH field shifted by one.\n //\n // ## Fix the ENCODING CHARACTERS, never the field separator\n //\n // The one thing this builder must not do is change `fieldSep` for the ACK. **`fieldSep` is the\n // only byte that can truncate MSA-2**, and MSH-10 provably cannot contain it: MSH-10 is a product\n // of splitting the MSH *on* `fieldSep`. Keep the inbound's separator and the verbatim echo is\n // guaranteed by construction. Swap it for `|` and that guarantee evaporates, because under\n // §2.5.4 a `|` inside an `^`-delimited message is **ordinary data**, needing no escape. An\n // earlier version of this guard did exactly that, and an MSH-10 of `ID|X` went out as\n // `MSA|AA|ID|X`, which a receiver reads back as **`ID`**: silently TRUNCATED.\n //\n // Truncated is far worse than empty. Empty correlates to nothing; truncated is *plausible*, and\n // can match a **different** in-flight send, settling it, so `send()` resolves and the sender\n // forgets a message the receiver never acknowledged, while the message that WAS acknowledged\n // stays in flight and resends. One silently lost clinical message and one duplicate, from a\n // single positive `AA`.\n //\n // So: keep `fieldSep`, and pick encoding characters that do not contain it. The encoding\n // characters cannot truncate MSA-2 (the MSA scan splits on `fieldSep` alone), so substituting one\n // is free, it costs only the component/repetition/escape/subcomponent *semantics* of a message\n // that declared none (its MSH-2 was empty or unusable; §2.16 requires it).\n //\n // Note what we still do NOT do: fall through to the minimal ACK. That drops the MSA-2 echo, and\n // an ACK that is well-formed but uncorrelatable is worse than one that correlates with an\n // imperfect header. The control-ID echo is the thing this file exists to protect.\n const s = fieldSep;\n const encodingChars = usableEnc.includes(fieldSep)\n ? usableEnc.replace(fieldSep, ENCODING_CHAR_SUBSTITUTE)\n : usableEnc;\n\n const sendingApp = fields[2] ?? \"\";\n const sendingFacility = fields[3] ?? \"\";\n const receivingApp = fields[4] ?? \"\";\n const receivingFacility = fields[5] ?? \"\";\n const inboundControlId = fields[9] ?? \"\";\n const processingId = fields[10] ?? \"P\";\n const version = fields[11] ?? \"2.3\";\n\n const msaTail = code === \"AE\" || code === \"AR\" ? `${s}${NACK_TEXT[code]}` : \"\";\n const ackStr =\n `MSH${s}${encodingChars}${s}${receivingApp}${s}${receivingFacility}${s}${sendingApp}${s}${sendingFacility}${s}${now}${s}${s}ACK${s}${newControlId}${s}${processingId}${s}${version}\\r` +\n `MSA${s}${code}${s}${inboundControlId}${msaTail}\\r`;\n\n // `latin1` on the way back out too, so a high-bit byte echoed from the inbound MSH-10 is\n // preserved rather than re-masked. Paired with the `latin1` decode above, the inbound control ID\n // round-trips byte-exact into MSA-2, and because the ACK carries the inbound's own MSH-1, the\n // sender's MSA-2 scanner reads back the very bytes it keyed its in-flight store on.\n return Buffer.from(ackStr, \"latin1\");\n}\n\n/** 14-char HL7 timestamp `YYYYMMDDHHmmss` in UTC. */\nfunction timestamp14(): string {\n const d = new Date();\n const pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n return (\n String(d.getUTCFullYear()) +\n pad(d.getUTCMonth() + 1) +\n pad(d.getUTCDate()) +\n pad(d.getUTCHours()) +\n pad(d.getUTCMinutes()) +\n pad(d.getUTCSeconds())\n );\n}\n","/**\n * Containment for every event emission that can be reached from a **callback we do not own**,\n * a socket's `'data'`/`'error'`/`'connect'`/`'secureConnect'` listener, a `net.Server`'s\n * `'connection'` listener, a `tls.Server`'s `'tlsClientError'` listener, or the `catch` block of a\n * `void`-ed async task.\n *\n * ## Why this module exists\n *\n * Node's `EventEmitter.emit()` calls its listeners **synchronously**. So a throwing subscriber does\n * not merely fail itself, it unwinds the whole call stack it was invoked from. When that stack\n * bottoms out in a socket callback, the throw becomes an **uncaught exception and kills the\n * process**, taking every other connection and every in-flight durable commit with it. When it\n * bottoms out in a `void`-ed async method, it becomes an **unhandled rejection**, same outcome,\n * and it also skips whatever the method had left to do, which on the ACK path means the\n * acknowledgement is never sent.\n *\n * A metrics tap, an audit logger, a `console.log` with a typo: any of them, in a consumer's code,\n * could take down an MLLP interface. That is not an acceptable failure mode for a transport\n * carrying clinical messages.\n *\n * ## The rule\n *\n * **No `emit()` reachable from a transport, accept, or handshake callback, in *any* class, may go\n * uncontained.** Scoping this rule to one class is exactly the mistake that let it regress: the\n * hazard belongs to the *call stack*, not to `Connection`. `MllpServer` and `MllpClient` emit from\n * those callbacks too.\n *\n * @packageDocumentation\n */\n\nimport type { EventEmitter } from \"node:events\";\n\n/**\n * Emit an event, containing any throw from a subscriber.\n *\n * The subscriber's throw is reported through `onSubscriberThrow` (normally an `'error'` emission)\n * rather than being allowed to unwind into the callback that invoked us. Emission continues to be\n * synchronous and in-order for well-behaved subscribers, this only changes what happens when one\n * of them is broken.\n *\n * @param emitter - The emitter to emit on.\n * @param event - Event name.\n * @param payload - The (already frozen) event payload.\n * @param onSubscriberThrow - Reporter for a throwing subscriber. Must not itself throw.\n *\n * @example\n * ```typescript\n * safeEmit(this, \"connection\", frozenEvent, (err) => this._reportSubscriberThrow(err));\n * ```\n */\nexport function safeEmit(\n emitter: EventEmitter,\n event: string,\n payload: unknown,\n onSubscriberThrow?: (err: Error) => void,\n): void {\n try {\n emitter.emit(event, payload);\n } catch (err) {\n onSubscriberThrow?.(err instanceof Error ? err : new Error(String(err)));\n }\n}\n\n/**\n * Emit `'error'`, but only when someone is listening, and never letting the emission escape.\n *\n * Two hazards, both fatal, both closed here:\n *\n * 1. **No listener.** Node raises `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an\n * `EventEmitter` with no `'error'` listener. Since error reporting is reached from inside socket\n * callbacks and `catch` blocks, that throw would escape by the very route the containment exists\n * to close.\n * 2. **A throwing `'error'` listener.** Swallowed, deliberately, and this is the one place a throw\n * is dropped on the floor. Reporting is what just failed, so there is nowhere left to report it\n * to; the alternative is killing the process to complain that we could not complain.\n *\n * @example\n * ```typescript\n * safeEmitError(this, Object.freeze({ connectionId, error }));\n * ```\n */\nexport function safeEmitError(emitter: EventEmitter, payload: unknown): void {\n if (emitter.listenerCount(\"error\") === 0) return;\n try {\n emitter.emit(\"error\", payload);\n } catch {\n // Deliberately swallowed, see the JSDoc above.\n }\n}\n","/**\n * MLLP Server, createServer(), createStarterServer(), and MllpServer class.\n *\n * Provides the server-side MLLP transport: listen for inbound TCP connections,\n * decode MLLP-framed messages, surface them as Buffer payloads, and support\n * optional auto-ACK, keepalive, graceful shutdown, and AbortSignal cancellation.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received', payload.length, 'bytes from', meta.connectionId);\n * },\n * });\n * await server.listen(2575);\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createServer as netCreateServer, isIP, SocketAddress } from \"node:net\";\nimport type { Server as NetServer, Socket } from \"node:net\";\nimport { createServer as tlsCreateServer } from \"node:tls\";\nimport type { TLSSocket } from \"node:tls\";\nimport { EventEmitter } from \"node:events\";\nimport { Connection } from \"../connection/index.js\";\nimport { MllpConnectionError } from \"../connection/index.js\";\nimport { NetTransport, TlsTransport } from \"../transport/index.js\";\nimport type { ServerTlsOptions } from \"../transport/tls-options.js\";\nimport { MLLP_BIND_ALL_INTERFACES, type SecurityWarning } from \"../transport/security-warnings.js\";\nimport { encodeFrame } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning } from \"../framing/index.js\";\nimport { buildRawAck, rawAckUncorrelatable, resolveNackCode } from \"./ack.js\";\nimport type { AckCode, NegativeAckCode } from \"./ack.js\";\nimport { safeEmit, safeEmitError } from \"../internal/safe-emit.js\";\n\n/**\n * Wildcard (unspecified-address) bind detection for bind safety,\n * hosts that bind ALL interfaces and therefore require\n * `ServerOptions.allowWildcardBind: true`.\n *\n * NORMALIZES rather than string-matching:\n * - `''` (empty string, Node binds all interfaces)\n * - IPv4: every dotted-quad whose octets are all numerically zero (`'0.0.0.0'`)\n * - IPv6: any spelling that canonicalizes (via `net.SocketAddress`) to `'::'`\n * (`'::0'`, `'0:0:0:0:0:0:0:0'`, …) or to the IPv4-mapped unspecified\n * address `'::ffff:0.0.0.0'` (also `'::ffff:0:0'`)\n *\n * Non-IP strings return `false` here, but that is NOT the enforcement\n * boundary. getaddrinfo/inet_aton resolves shorthands this predicate cannot\n * see as strings (`'0'`, `'0.0'`, `'0.0.0'`, `'00.0.0.0'`, `'0x0.0.0.0'`,\n * wildcard-resolving hostnames), so `listen()` applies this predicate\n * **twice**: pre-bind on the requested host string (fast path), and\n * post-bind on the **OS-normalized bound address** from `server.address()`\n * (always canonical `'0.0.0.0'`/`'::'`), the authoritative check. The\n * guarantee is enforced against the address actually bound, not the\n * spelling requested.\n */\nfunction isWildcardHost(host: string): boolean {\n if (host === \"\") return true;\n const family = isIP(host);\n if (family === 4) {\n return host.split(\".\").every((octet) => Number(octet) === 0);\n }\n if (family === 6) {\n let canonical: string;\n try {\n canonical = new SocketAddress({ address: host, family: \"ipv6\" }).address;\n } catch {\n // isIP said valid IPv6 but SocketAddress refused, treat as non-wildcard\n // (the bind itself will fail downstream with the OS error).\n return false;\n }\n return canonical === \"::\" || canonical === \"::ffff:0.0.0.0\";\n }\n return false;\n}\n\n/**\n * Minimal, content-free peer-certificate summary surfaced on the `'connection'`\n * event when `ServerOptions.tls.clientAuth` is `'WANT'` or `'MUST'`\n * (ATNA ITI-19 mutual authentication). Never the full certificate object,\n * CN strings, the expiry date, and the verification outcome only.\n *\n * @example\n * ```typescript\n * server.on('connection', ({ peerCertificate }) => {\n * if (peerCertificate !== null && peerCertificate.authorized) {\n * logger.info({ cn: peerCertificate.subjectCN });\n * }\n * });\n * ```\n */\nexport interface PeerCertificateSummary {\n /** Subject Common Name, or `null` if absent/unavailable. */\n readonly subjectCN: string | null;\n /** Issuer Common Name, or `null` if absent/unavailable. */\n readonly issuerCN: string | null;\n /** Certificate expiry (`notAfter`), or `null` if absent/unavailable. */\n readonly validTo: string | null;\n /**\n * Whether the peer certificate chain was **verified** against\n * {@link ServerTlsOptions.ca} (`socket.authorized === true`).\n *\n * ⚠️ Under `clientAuth: 'WANT'` a peer certificate may be present yet\n * **unverified**, the connection is accepted regardless. Never make\n * authorization decisions on `subjectCN` (or any other field of this\n * summary) unless `authorized` is `true`. Under `clientAuth: 'MUST'` an\n * unverified certificate never reaches this point (the handshake is\n * rejected), so `authorized` is always `true` there.\n */\n readonly authorized: boolean;\n}\n\n/**\n * Metadata attached to each decoded MLLP message.\n *\n * All fields are `readonly`, the object is `Object.freeze()`'d before emission.\n *\n * @example\n * ```typescript\n * server.on('message', ({ payload, meta }) => {\n * console.log(meta.connectionId, '@', meta.byteOffset, 'warnings:', meta.warnings.length);\n * });\n * ```\n */\nexport interface MessageMeta {\n /** Stable UUID identifying the connection that delivered this message. */\n readonly connectionId: string;\n /** Byte offset of the frame start in the connection's data stream. */\n readonly byteOffset: number;\n /** Framing warnings emitted during decoding of this frame. */\n readonly warnings: readonly MllpWarning[];\n}\n\n/**\n * Why the server sent a **negative** acknowledgement instead of `AA` on the auto-ACK path.\n * A stable, **PHI-free** enum (no payload bytes, no control ID), safe to log and to key\n * metrics on.\n *\n * - `'handler-rejected'`, a commit-gated `onMessage` handler threw/rejected (the commit\n * contract: a positive ACK cannot precede a successful commit).\n * - `'uncorrelatable-inbound'`, the inbound could not carry a correlatable positive ACK\n * (no readable `MSH`, an empty MSH-10, or a batch/concatenated-message shape). A positive\n * `AA` here names a control ID the sender cannot match → timeout → resend → **duplicate\n * clinical message**. See {@link rawAckUncorrelatable}.\n * - `'discarded-bytes'`, the decoder flagged `MLLP_TRAILING_BYTES` for this frame: a\n * mid-payload `VT` made it **discard accumulated bytes** and deliver only the fragment\n * after it. The clinical message was destroyed in transit; a positive `AA` would tell the\n * sender a message we never received was delivered.\n */\nexport type NackReason = \"handler-rejected\" | \"uncorrelatable-inbound\" | \"discarded-bytes\";\n\n/**\n * Payload of the server `'nack'` event, emitted whenever the server responds with a\n * **negative** acknowledgement instead of `AA` on the auto-ACK path (the fail-safe commit\n * contract). This fires both when a commit-gated `autoAck: 'AA'` handler throws/rejects and\n * when the server **downgrades** a positive auto-ACK because the inbound could not be\n * correlated ({@link NackReason}).\n *\n * **PHI-safe by construction:** carries only the connection ID, the resolved\n * acknowledgement code, and a static `reason`, never the payload, the inbound control ID,\n * or the thrown error's message (which may carry PHI). The object is `Object.freeze()`'d\n * before emission.\n *\n * @example\n * ```typescript\n * server.on('nack', ({ connectionId, ackCode, reason }) => {\n * metrics.increment('mllp.nack', { code: ackCode, reason }); // e.g. reason='discarded-bytes'\n * });\n * ```\n */\nexport interface NackEvent {\n /** Connection that produced the negative acknowledgement. */\n readonly connectionId: string;\n /** The negative acknowledgement code sent to the peer (`AE` or `AR`). */\n readonly ackCode: NegativeAckCode;\n /** Why the negative acknowledgement was sent (PHI-free). */\n readonly reason: NackReason;\n}\n\n/**\n * Observability snapshot returned by `server.getStats()`.\n *\n * All fields are JSON-serializable. `connections` and `activeConnections` both\n * reflect the current live connection count.\n *\n * @example\n * ```typescript\n * const stats = server.getStats();\n * console.log(JSON.stringify(stats)); // log-pipeline friendly\n * ```\n */\nexport interface ServerStats {\n /** Whether the server is currently accepting connections. */\n readonly listening: boolean;\n /** Bound port, or `null` before listen(). */\n readonly port: number | null;\n /** Bound host, or `null` before listen(). */\n readonly host: string | null;\n /**\n * Current live connection count.\n * Same value as `activeConnections`.\n */\n readonly connections: number;\n /** Current live connection count. Same value as `connections`. */\n readonly activeConnections: number;\n /** Aggregate bytes received across all current connections. */\n readonly totalBytesIn: number;\n /** Aggregate bytes sent across all current connections. */\n readonly totalBytesOut: number;\n /** Total connections accepted since listen() (monotonically increasing). */\n readonly acceptedTotal: number;\n /** Total connections closed since listen() (monotonically increasing). */\n readonly closedTotal: number;\n /** Whether this server is configured for TLS. Mirrors `ServerOptions.tls` being set. */\n readonly tls: boolean;\n /** Total `'tlsClientError'` events (failed TLS handshakes, incl. rejected client certs) since listen(). */\n readonly tlsClientErrorsTotal: number;\n}\n\n/**\n * Options for `createServer()`.\n *\n * @example\n * ```typescript\n * const opts: ServerOptions = {\n * onMessage: (payload, meta, conn) => {\n * const ack = buildAck(payload);\n * conn.send(ack);\n * },\n * framing: { maxFrameSizeBytes: 4 * 1024 * 1024 },\n * keepaliveIntervalMs: 60_000,\n * deadPeerTimeoutMs: 300_000,\n * drainTimeoutMs: 30_000,\n * };\n * ```\n */\nexport interface ServerOptions {\n /**\n * Called for each decoded MLLP message.\n *\n * Its role depends on `autoAck`, this is the **commit contract** (HL7 v2.5.1 §2.9.2):\n *\n * - **`autoAck: 'AA'` + this handler ⇒ commit-gated (the safe default).** The handler\n * is the durable-commit step. The server **awaits** it and only then sends the ACK:\n * resolve ⇒ `AA`; **throw/reject ⇒ `AE`** (or `AR` via {@link MllpAckError}), a\n * positive ACK can never precede a successful commit. Do **not** call `conn.send()`\n * here in this mode.\n * - **`autoAck` unset ⇒ manual mode.** The handler owns the response; build and send the\n * ACK yourself via `conn.send(encodeFrame(ackPayload))`. Its return value is ignored.\n * - **`autoAck: fn` ⇒ observation only.** `fn` builds the ACK; this handler runs first as\n * a side effect and its return value is ignored. Do not call `conn.send()` here.\n *\n * May be sync or async; an async handler is awaited in commit-gated mode.\n */\n onMessage?: (payload: Buffer, meta: MessageMeta, conn: Connection) => void | Promise<void>;\n\n /**\n * FrameReader tolerance options applied to every accepted connection.\n * Merged with SERVER_DEFAULT_FRAMING, caller-supplied values override defaults.\n * `onFrame` and `onWarning` are managed internally and must not be supplied here.\n */\n framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n\n /**\n * Per-connection warning subscriber. Called for every framing warning on every connection.\n */\n onWarning?: (w: MllpWarning) => void;\n\n /**\n * Auto-ACK mode. When set, the server builds and sends the ACK for each message.\n *\n * - **`'AA'`**, auto-acknowledge with the fail-safe **commit contract**:\n * - **With an `onMessage` handler ⇒ commit-gated (recommended).** The server awaits\n * `onMessage` (the durable-commit step), then sends `AA` on success or a **negative**\n * ACK on failure (`AE` by default; `AR` via {@link MllpAckError}). The positive ACK\n * **cannot precede a successful commit**, a handler throw can never yield `AA`.\n * - **Without an `onMessage` handler ⇒ transport-accept.** `AA` is sent on frame\n * receipt. This `AA` means only **\"bytes received and framed\"**, *not*\n * \"application-processed\". ⚠️ For clinical messages this is unsafe on its own:\n * pair `'AA'` with an `onMessage` handler that durably commits, so the ACK reflects\n * real processing.\n * - **`fn`**, `fn(payload, meta, conn)` builds the ACK bytes the server sends; the\n * caller fully owns MSA-1 (e.g. to emit enhanced-mode `CA`/`CE`/`CR`).\n *\n * The `'message'` event always fires BEFORE the ACK is sent. Do NOT call\n * `conn.send()` in `onMessage` when `autoAck` is set, this results in two ACKs.\n */\n autoAck?:\n | \"AA\"\n | ((payload: Buffer, meta: MessageMeta, conn: Connection) => Buffer | Promise<Buffer>);\n\n /**\n * TCP keepalive probe interval in ms.\n * Calls `socket.setKeepAlive(true, ms)` on each accepted socket.\n * Uses OS TCP stack to detect dead peers (half-open, network partitions).\n * Distinct from `deadPeerTimeoutMs` (application-level idle close).\n */\n keepaliveIntervalMs?: number;\n\n /**\n * Application-level idle close timeout in ms.\n * If no HL7 messages are received on a connection for this interval, the connection\n * is destroyed via `conn.destroy(new Error('idle timeout'))`.\n * Resets on every `'message'` event. Distinct from `keepaliveIntervalMs` (OS TCP probe).\n */\n deadPeerTimeoutMs?: number;\n\n /**\n * Graceful drain timeout passed to `conn.close()` during `server.close()`.\n * Default: 30 000 ms.\n */\n drainTimeoutMs?: number;\n\n /**\n * Enable TLS (MLLPS) for this server. When set, the server binds a\n * `tls.Server` instead of a plain `net.Server`, consumes `'secureConnection'`\n * (post-handshake sockets) instead of `'connection'`, and surfaces failed\n * handshakes via the `'tlsClientError'` event rather than crashing.\n *\n * Spec anchor: IHE ATNA ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @default undefined (plaintext TCP)\n */\n tls?: ServerTlsOptions;\n\n /**\n * Opt-in required to bind a wildcard host, a bind-safety guardrail.\n * Without this flag, `listen()` rejects a wildcard host in\n * two tiers: **literal spellings** (`'0.0.0.0'`, `'::'`, `''`, `'::0'`,\n * `'0:0:0:0:0:0:0:0'`, `'::ffff:0.0.0.0'`, …) reject **pre-bind** (fast\n * path, nothing is ever bound); **resolver-only shorthands** (`'0'`,\n * `'0.0'`, `'0x0.0.0.0'`, hostnames resolving to the unspecified\n * address, …) are caught **post-bind** against the OS-normalized bound\n * address, the just-bound server closes before any connection can be\n * accepted and `listen()` rejects, with no listening state and no\n * `'listening'` event either way. When `true`, binding a wildcard host\n * emits a one-time `'securityWarning'` (`MLLP_BIND_ALL_INTERFACES`) at\n * listen time.\n *\n * @default false\n */\n allowWildcardBind?: boolean;\n}\n\n/**\n * Options for `createStarterServer()`, the \"three lines of code\" factory.\n *\n * Extends `ServerOptions` with `port`, `host`, and `handleSignals`. Defaults:\n * `autoAck: 'AA'`, `drainTimeoutMs: 30_000`, `Symbol.asyncDispose` wired.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n *\n * const server = await createStarterServer({\n * port: 2575,\n * onMessage: async (payload) => {\n * await db.commit(payload); // the durable-commit step; a throw answers a negative ACK\n * },\n * });\n * // server is listening, auto-ACK enabled, Symbol.asyncDispose wired\n * await using _ = server; // closes on scope exit\n * ```\n */\nexport interface StarterServerOptions extends ServerOptions {\n /** Port to listen on. */\n port: number;\n /**\n * Host to bind to. Default `'127.0.0.1'` (bind-safety hardening,\n * was `'0.0.0.0'`; binding all interfaces now requires\n * `ServerOptions.allowWildcardBind: true`).\n */\n host?: string;\n /**\n * Register `process.once('SIGTERM')` and `process.once('SIGINT')` handlers that\n * call `server.close()` then `process.exit(0)`. Default: `false`.\n *\n * Handlers are automatically removed when `server.close()` is called, so\n * `process.listenerCount('SIGTERM') === 0` after close() completes, preventing\n * handler accumulation across test instances or multiple server restarts.\n */\n handleSignals?: boolean;\n}\n\n/**\n * Default framing options applied to every server-side FrameReader.\n *\n * Reflects real-world MLLP device behavior:\n * - `allowFsOnly: true`, accept FS without trailing CR\n * - `allowLfAfterFs: true`, accept FS+LF (common vendor deviation)\n * - `allowLeadingWhitespace: true`, accept leading whitespace before VT\n * - `allowMissingLeadingVt: false`, require VT framing (stricter than FS-only tolerance)\n */\nconst SERVER_DEFAULT_FRAMING: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\"> = {\n allowFsOnly: true,\n allowLfAfterFs: true,\n allowLeadingWhitespace: true,\n allowMissingLeadingVt: false,\n};\n\n/**\n * MLLP TCP server, wraps `net.Server` without extending it.\n *\n * Each accepted socket is wrapped in a `NetTransport`, connected to a `Connection`\n * with server-level framing options, and added to `_connections`. Messages are\n * surfaced via the `'message'` event and the `onMessage` callback.\n *\n * Public events: `'listening'`, `'connection'`, `'message'`, `'nack'`, `'error'`, `'close'`.\n * The `'nack'` event ({@link NackEvent}) fires whenever the server returns a negative ACK\n * instead of `AA` on the auto-ACK path, a commit-gated `autoAck: 'AA'` handler failing, or\n * a positive auto-ACK **downgraded** because the inbound could not be correlated (an\n * unreadable/uncorrelatable message, or one whose bytes the decoder discarded). The\n * {@link NackReason} distinguishes the causes.\n *\n * **`'error'` contract:** underlying `net.Server`/`tls.Server` errors are forwarded to the\n * `'error'` event whenever a listener is attached. With **no** listener, the outcome depends on\n * server state: during a `listen()` (the bind window) the error **rejects the `listen()` promise**\n * the primary error surface, and the process never crashes on a bind error; with no `listen()`\n * in flight and the server not serving (e.g. a stale async error after `close()`) the error is\n * dropped; but **while serving**, an unlistened runtime error (e.g. accept-loop `EMFILE`) keeps\n * Node's fail-loud crash-on-unlistened-`'error'` convention, a silent accept outage is\n * impossible. Caveat for `'error'` listeners: the forwarder runs **before** the internal\n * `listen()` rejection handler, so an `'error'` listener that synchronously calls `close()`\n * during the bind window changes the `listen()` rejection from the bind error to the typed\n * close-during-listen `MllpConnectionError`, match on the `'error'` event payload, not the\n * rejection, if you do that.\n * TLS (MLLPS) adds two more: `'tlsClientError'` (a failed TLS handshake, the\n * server logs it and keeps serving other connections) and `'securityWarning'` (loud,\n * one-time notice when a wildcard host is bound via `allowWildcardBind: true`).\n * All event payloads are `Object.freeze()`'d before emission.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received from', meta.connectionId);\n * },\n * });\n * await server.listen(2575);\n * // await using server = createServer({ ... }); // Symbol.asyncDispose\n * ```\n */\nexport class MllpServer extends EventEmitter {\n private readonly _netServer: NetServer;\n private readonly _connections: Set<Connection> = new Set();\n private readonly _opts: ServerOptions;\n /** `true` when `ServerOptions.tls` is set, `_netServer` is actually a `tls.Server`. */\n private readonly _isTls: boolean;\n\n private _listening = false;\n /** Single-flight guard: `true` while a `listen()` call is settling. */\n private _listenInFlight = false;\n /**\n * Settle hook for an in-flight `listen()`. Non-null only while a\n * `listen()` is in flight; invoked by `close()` so a close racing startup\n * rejects the pending `listen()` (typed) instead of leaving it hung with\n * the single-flight guard stuck. Cleared on every settle path.\n */\n private _pendingListenSettle: (() => void) | null = null;\n private _port: number | null = null;\n private _host: string | null = null;\n private _acceptedTotal = 0;\n private _closedTotal = 0;\n /** Count of `'tlsClientError'` events since listen(). */\n private _tlsClientErrorsTotal = 0;\n\n /**\n * Construct an MLLP server. Created idle; call `listen()` (or use\n * {@link createServer}/{@link createStarterServer}) to begin accepting connections.\n *\n * @param opts - Server options (bind host/port, auto-ACK policy, message handler, framing, …).\n */\n constructor(opts: ServerOptions) {\n super();\n this._opts = opts;\n this._isTls = opts.tls !== undefined;\n\n if (opts.tls !== undefined) {\n const tlsOpts: ServerTlsOptions = opts.tls;\n const clientAuth = tlsOpts.clientAuth ?? \"NONE\";\n const tlsServer = tlsCreateServer({\n cert: tlsOpts.cert,\n key: tlsOpts.key,\n ...(tlsOpts.ca !== undefined ? { ca: tlsOpts.ca } : {}),\n ...(tlsOpts.passphrase !== undefined ? { passphrase: tlsOpts.passphrase } : {}),\n minVersion: tlsOpts.minVersion ?? \"TLSv1.2\",\n ...(tlsOpts.maxVersion !== undefined ? { maxVersion: tlsOpts.maxVersion } : {}),\n ...(tlsOpts.ciphers !== undefined ? { ciphers: tlsOpts.ciphers } : {}),\n requestCert: clientAuth !== \"NONE\",\n rejectUnauthorized: clientAuth === \"MUST\",\n });\n this._netServer = tlsServer;\n tlsServer.on(\"secureConnection\", (socket: TLSSocket) => {\n this._onSocketAccepted(socket);\n });\n // 'tlsClientError' fires for failed handshakes (incl. rejected client\n // certs under mTLS 'MUST'). The server MUST NOT crash and MUST keep\n // accepting other connections, only the error's message/code and the\n // remote address are surfaced; never payload bytes or a cert dump.\n tlsServer.on(\"tlsClientError\", (err: Error, socket: TLSSocket) => {\n this._tlsClientErrorsTotal += 1;\n const nodeErr = err as NodeJS.ErrnoException;\n const event: {\n remoteAddress: string | null;\n remotePort: number | null;\n message: string;\n code: string | null;\n timestamp: Date;\n } = Object.freeze({\n remoteAddress: socket.remoteAddress ?? null,\n remotePort: socket.remotePort ?? null,\n message: err.message,\n code: typeof nodeErr.code === \"string\" ? nodeErr.code : null,\n timestamp: new Date(),\n });\n // Contained: this runs inside tls.Server's own 'tlsClientError' listener, so a throwing\n // subscriber would kill the process on a failed handshake, flatly contradicting this\n // handler's whole purpose (\"MUST NOT crash and MUST keep accepting other connections\").\n this._emitContained(\"tlsClientError\", event);\n });\n } else {\n this._netServer = netCreateServer();\n this._netServer.on(\"connection\", (socket: Socket) => {\n this._onSocketAccepted(socket);\n });\n }\n\n // Forward net.Server errors to the public 'error' event, guarded by\n // server state, an unlistened EventEmitter 'error' emission THROWS,\n // and this constructor-time listener runs BEFORE listen()'s own\n // once('error') rejection handler (registration order), so an unguarded\n // re-emit crashed the process on a plain bind error (EADDRINUSE,\n // EACCES, …) instead of letting listen() reject. Contract (also in the\n // class JSDoc):\n // • 'error' listener attached ⇒ always forwarded.\n // • no listener + a listen() in flight (bind window) ⇒ dropped here;\n // the listen() promise rejects with this error (primary surface).\n // • no listener + not listening (e.g. stale async error after\n // close()) ⇒ dropped; there is no consumer to surface it to.\n // • no listener + SERVING (this._listening) ⇒ re-emit unguarded,\n // which throws, Node's fail-loud convention is deliberately kept\n // for runtime accept-loop errors (EMFILE/ENFILE): a silent accept\n // outage on a healthcare listener must be impossible.\n // MLLP-10 refinement: the deliberate fail-loud branch below is PRESERVED exactly (unlistened +\n // serving ⇒ unguarded re-emit ⇒ throw), but a *throwing subscriber* is a different thing from\n // an unobservable accept outage, and it must not crash the server. When a listener exists, the\n // emit is contained.\n this._netServer.on(\"error\", (err: Error) => {\n if (this.listenerCount(\"error\") > 0) {\n safeEmitError(this, err); // contained, a throwing 'error' subscriber is not fail-loud\n } else if (this._listening) {\n this.emit(\"error\", err); // DELIBERATE: unlistened + serving ⇒ Node's fail-loud convention\n }\n });\n }\n\n /**\n * Start listening on the given port.\n *\n * Resolves once the TCP socket is bound and emits `'listening'` with\n * `Object.freeze({ port: actualPort, host: actualHost })`.\n *\n * **Single-flight:** one `listen()` per server lifecycle at a time. A call\n * while the server is already listening, or while another `listen()` is\n * still in flight, rejects with a typed `MllpConnectionError` (concurrent\n * binds raced each other's post-bind safety checks). Call `close()` before\n * re-listening; sequential `listen()` → `close()` → `listen()` is fine.\n *\n * **`close()` during an in-flight `listen()`** rejects that `listen()` with\n * a typed `MllpConnectionError` (never a hang) and clears the single-flight\n * guard, a subsequent `listen()` on the same server works. This makes\n * `Symbol.asyncDispose` (which delegates to `close()`) safe even before a\n * `listen()` has settled. (Exception: `close({ signal })` with an\n * already-aborted signal is a no-op AbortError rejection, the in-flight\n * `listen()` is left to settle on its own bind outcome; see `close()`.)\n *\n * @param port - TCP port to bind. Use `0` to let the OS assign an ephemeral port.\n * @param hostOrOpts - Host string or object with `host` and optional `signal`.\n * Default host: `'127.0.0.1'` (bind-safety hardening). Wildcard hosts\n * are rejected unless `ServerOptions.allowWildcardBind: true`, literal\n * spellings pre-bind, resolver-only shorthands post-bind via the\n * OS-normalized bound address.\n *\n * @example\n * ```typescript\n * await server.listen(2575); // binds 127.0.0.1\n * await server.listen(0, '127.0.0.1');\n * await server.listen(0, { host: '127.0.0.1', signal: ac.signal });\n * ```\n */\n listen(\n port: number,\n hostOrOpts?: string | { host?: string; signal?: AbortSignal },\n ): Promise<void> {\n const host = typeof hostOrOpts === \"string\" ? hostOrOpts : (hostOrOpts?.host ?? \"127.0.0.1\");\n const signal =\n typeof hostOrOpts === \"object\" && hostOrOpts !== null ? hostOrOpts.signal : undefined;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Phase 8 bind safety, SINGLE-FLIGHT guard. Two concurrent listen()\n // calls on one server race each other's post-bind checks: the losing\n // call's 'listening' handler can observe the winner's (or a just-closed)\n // socket and record listening state for a bind that no longer exists,\n // a green health check with nothing bound. One listen per lifecycle;\n // close() before re-listening.\n if (this._listening || this._listenInFlight) {\n return Promise.reject(\n new MllpConnectionError(\n this._listening\n ? \"listen() rejected: server is already listening, call close() before re-listening\"\n : \"listen() rejected: another listen() is already in flight on this server\",\n {\n cause: new Error(\"listen already listening or in flight\"),\n phase: \"connect\",\n },\n ),\n );\n }\n\n // Phase 8 bind safety, pre-bind FAST PATH: reject a literal wildcard\n // host BEFORE binding unless the operator explicitly opted in via\n // allowWildcardBind. isWildcardHost normalizes IP literals ('::0',\n // '0:0:0:0:0:0:0:0', '::ffff:0.0.0.0' …); getaddrinfo shorthands it\n // cannot see as strings ('0', '0x0.0.0.0', …) are caught by the\n // authoritative POST-BIND check in onListening below.\n if (isWildcardHost(host) && this._opts.allowWildcardBind !== true) {\n return Promise.reject(\n new MllpConnectionError(\n `refusing to bind wildcard host '${host}', set ServerOptions.allowWildcardBind: true to bind all interfaces`,\n {\n cause: new Error(\"wildcard bind requires allowWildcardBind\"),\n phase: \"connect\",\n },\n ),\n );\n }\n\n this._listenInFlight = true;\n\n return new Promise<void>((resolve, reject) => {\n // ONE idempotent settle point for every outcome of this listen()\n // (success, abort, close-during-listen, bind error, addr-null reject,\n // post-bind wildcard reject). First caller wins; every path gets the\n // same cleanup, abort listener, netServer listeners, the close()\n // settle hook, and the single-flight guard, so no path can leak a\n // listener, strand the guard, or re-settle a settled promise.\n let settled = false;\n const settle = (\n outcome: { ok: true } | { ok: false; error: Error },\n opts?: { closeServer?: boolean },\n ): void => {\n if (settled) return;\n settled = true;\n this._pendingListenSettle = null;\n signal?.removeEventListener(\"abort\", abortHandler);\n this._netServer.removeListener(\"listening\", onListening);\n this._netServer.removeListener(\"error\", onError);\n if (opts?.closeServer === true) {\n this._netServer.close();\n }\n this._listenInFlight = false;\n if (outcome.ok) {\n resolve();\n } else {\n // Pass the rejection reason through untouched, AbortError\n // (DOMException), MllpConnectionError, and raw bind errors keep\n // their identity for callers matching on name/instanceof.\n reject(outcome.error);\n }\n };\n\n const abortHandler = () => {\n settle(\n { ok: false, error: new DOMException(\"Aborted\", \"AbortError\") },\n {\n closeServer: true,\n },\n );\n };\n\n if (signal !== undefined) {\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n // close() (or Symbol.asyncDispose) racing an in-flight listen():\n // net.Server.close() nulls the handle, and the pending 'listening'\n // emission is guarded on the handle, so NEITHER 'listening' NOR\n // 'error' ever fires for the in-flight bind. Without a settle path the\n // promise hangs forever and every later listen() rejects \"in flight\".\n // close() settles the pending listen PROACTIVELY through this hook.\n // (Deliberately NOT a netServer once('close') listener: the 'close'\n // event from a PREVIOUS lifecycle's close() is delivered\n // asynchronously and can land after a NEW listen() has registered its\n // listeners, spuriously rejecting the fresh attempt. The netServer is\n // private, so our own close(), and the settle paths below, which\n // settle themselves, are the only close initiators. close() itself\n // closes the netServer right after invoking this hook, so the hook\n // does not.)\n this._pendingListenSettle = () => {\n settle({\n ok: false,\n error: new MllpConnectionError(\n \"listen() failed: server was closed while listen() was in flight\",\n {\n cause: new Error(\"close() during in-flight listen()\"),\n phase: \"connect\",\n },\n ),\n });\n };\n\n const onListening = () => {\n if (settled) return;\n // The bind outcome is now known and this handler settles the promise\n // synchronously below, drop the close()-settle hook AND the abort\n // listener FIRST. The hook: a close() called from inside the\n // 'listening' event handler must not reject a bind that succeeded.\n // The abort listener: the success path below emits 'listening' /\n // 'securityWarning' BEFORE settling, and an abort fired from inside\n // one of those handlers would otherwise close the just-bound server\n // AFTER listening state is recorded, stranding `listening: true`\n // with nothing bound (the exact hazard the post-bind checks exist to\n // prevent). Once the bind has succeeded, an abort of the listen\n // signal is too late and is deliberately ignored; use close().\n this._pendingListenSettle = null;\n signal?.removeEventListener(\"abort\", abortHandler);\n\n const addr = this._netServer.address();\n // address() is always an AddressInfo object for a bound TCP server;\n // null (or a string, pipe/IPC servers, which this class never\n // creates) means the server was closed out from under this bind.\n // NEVER fall back to the requested port/host strings, that records\n // listening state for a bind that does not exist. The single-flight\n // guard above should make this branch unreachable; it stays as\n // defense-in-depth.\n if (addr === null || typeof addr === \"string\") {\n settle(\n {\n ok: false,\n error: new MllpConnectionError(\n \"listen() failed: server was closed before the bind completed\",\n {\n cause: new Error(\"server.address() returned no TCP address\"),\n phase: \"connect\",\n },\n ),\n },\n { closeServer: true },\n );\n return;\n }\n const actualPort = addr.port;\n const actualHost = addr.address;\n\n // Phase 8 bind safety, POST-BIND enforcement against the\n // OS-NORMALIZED bound address. getaddrinfo/inet_aton resolves\n // shorthands the pre-bind string check cannot see ('0', '0.0',\n // '0.0.0', '00.0.0.0', '0x0.0.0.0', hostnames that resolve to the\n // unspecified address, …); the kernel-reported address is canonical\n // ('0.0.0.0' / '::'), so this check is authoritative. On violation:\n // close the just-bound server immediately and reject, no listening\n // state is recorded and no 'listening' event is emitted.\n // The sub-tick bind window is ACCEPT-SAFE, this handler runs\n // synchronously on 'listening', before the event loop can deliver\n // any 'connection'/'secureConnection' for the just-bound socket, so\n // no connection is ever accepted on a rejected wildcard bind. Do\n // not reorder or defer this branch.\n if (isWildcardHost(actualHost) && this._opts.allowWildcardBind !== true) {\n settle(\n {\n ok: false,\n error: new MllpConnectionError(\n `refusing to bind wildcard host '${host}' (bound address '${actualHost}'), ` +\n \"set ServerOptions.allowWildcardBind: true to bind all interfaces\",\n {\n cause: new Error(\"wildcard bind requires allowWildcardBind\"),\n phase: \"connect\",\n },\n ),\n },\n { closeServer: true },\n );\n return;\n }\n\n this._listening = true;\n this._port = actualPort;\n this._host = actualHost;\n\n // The bind has succeeded, nothing a 'listening'/'securityWarning'\n // subscriber does may undo that. A THROWING subscriber must not\n // strand the settle (the promise would hang and the single-flight\n // guard would wedge every later listen()) and must not suppress a\n // LATER emission: each emit is contained SEPARATELY (a throw in\n // 'listening' cannot swallow the securityWarning), surfaced via the\n // guarded 'error' tap (D-04, a handler error never crashes the\n // server) with the tap itself contained too (a throwing 'error'\n // listener on top of a throwing subscriber drops rather than\n // strands), so listen() always resolves.\n const emitSubscriberSafe = (event: \"listening\" | \"securityWarning\", payload: unknown) => {\n try {\n this.emit(event, payload);\n } catch (subscriberErr: unknown) {\n try {\n this._emitErrorIfListened(\n subscriberErr instanceof Error ? subscriberErr : new Error(String(subscriberErr)),\n );\n } catch {\n // Double user bug ('listening'/'securityWarning' subscriber\n // threw AND the 'error' listener threw): dropping beats\n // stranding the settle or crashing a successfully-bound server.\n }\n }\n };\n\n // Phase 8, loud, one-time notice when a wildcard host is actually\n // bound (keyed off the OS-normalized actualHost so shorthand\n // spellings warn too; single emission site). The operator channel\n // (process.emitWarning) fires FIRST, it has no subscribers, so no\n // throwing event listener can ever suppress the mandated\n // MLLP_BIND_ALL_INTERFACES notice on a live wildcard bind.\n const wildcardBound = isWildcardHost(actualHost);\n const wildcardMessage =\n `MLLP server is bound to ALL network interfaces (host='${actualHost}'), ` +\n \"set a specific bind address unless this is intentional.\";\n if (wildcardBound) {\n process.emitWarning(wildcardMessage, { code: MLLP_BIND_ALL_INTERFACES });\n }\n\n emitSubscriberSafe(\"listening\", Object.freeze({ port: actualPort, host: actualHost }));\n\n if (wildcardBound) {\n const warning: SecurityWarning = Object.freeze({\n code: MLLP_BIND_ALL_INTERFACES,\n message: wildcardMessage,\n host: actualHost,\n port: actualPort,\n timestamp: new Date(),\n });\n emitSubscriberSafe(\"securityWarning\", warning);\n }\n\n settle({ ok: true });\n };\n\n const onError = (err: Error) => {\n settle({ ok: false, error: err });\n };\n\n this._netServer.once(\"listening\", onListening);\n this._netServer.once(\"error\", onError);\n this._netServer.listen(port, host);\n });\n }\n\n /**\n * Stop accepting new connections and gracefully close all active connections.\n *\n * Sequence:\n * 1. `net.Server.close()`, stops accepting new connections immediately\n * 2. If `_connections` is empty: resolves immediately (no drain needed)\n * 3. Calls `_drainAll(drainTimeoutMs)`, Promise.all + side-effect setTimeout\n * that force-destroys stragglers after the drain window\n *\n * Calling `close()` while a `listen()` is still in flight proactively\n * settles that `listen()` with a typed `MllpConnectionError` rejection\n * and clears the single-flight guard, the server is immediately\n * re-listenable. `Symbol.asyncDispose` (which delegates here) is\n * therefore safe before `listen()` settles. **Qualification:** a\n * `close({ signal })` whose signal is **already aborted** rejects\n * immediately with `AbortError` and performs no work, it does NOT close\n * the server and does NOT settle the in-flight `listen()`, which simply\n * continues and settles on its own bind outcome.\n *\n * @param opts.drainTimeoutMs - Override drain timeout (default: `opts.drainTimeoutMs ?? 30_000`).\n * @param opts.signal - AbortSignal to cancel the close operation. On abort, all\n * active connections are destroyed and the promise rejects with AbortError.\n *\n * @example\n * ```typescript\n * await server.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number; signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Phase 8, settle an in-flight listen() FIRST (typed rejection). A\n // close racing startup (SIGTERM handler, test teardown, an `await using`\n // scope exiting before listen() was awaited) would otherwise leave the\n // listen() promise hung forever: net.Server.close() nulls the handle and\n // the pending 'listening' emission is handle-guarded, so neither\n // 'listening' nor 'error' ever fires for the in-flight bind, and the\n // stuck single-flight guard would reject every later listen().\n this._pendingListenSettle?.();\n\n // Stop accepting new connections\n this._netServer.close();\n this._listening = false;\n\n // If no active connections, we're done, emit 'close' and resolve\n // No abort handler registered on this path, nothing to remove.\n if (this._connections.size === 0) {\n // Contained: a throwing 'close' subscriber must not reject close() nor skip its cleanup.\n this._emitContained(\"close\", Object.freeze({}));\n return Promise.resolve();\n }\n\n // Wire AbortSignal, abort during drain force-destroys all connections\n let abortHandler: (() => void) | undefined;\n const abortPromise =\n signal !== undefined\n ? new Promise<\"aborted\">((_resolve, reject) => {\n abortHandler = () => {\n // Force-destroy all active connections on abort\n for (const conn of this._connections) {\n conn.destroy();\n }\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n })\n : null;\n\n try {\n const drainTimeoutMs = opts?.drainTimeoutMs ?? this._opts.drainTimeoutMs ?? 30_000;\n if (abortPromise !== null) {\n await Promise.race([this._drainAll(drainTimeoutMs), abortPromise]);\n } else {\n await this._drainAll(drainTimeoutMs);\n }\n } finally {\n if (abortHandler !== undefined && signal !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n // Emit 'close' after all connections have drained (SERVER-10: frozen payload). Contained.\n this._emitContained(\"close\", Object.freeze({}));\n }\n\n /**\n * Drain all active connections with a shared timeout.\n *\n * Uses Promise.all (not Promise.race) for coordination, the timeout is a\n * side effect that calls `conn.destroy()` on stragglers after `drainTimeoutMs`.\n * Promise.all resolves when all `conn.close()` promises settle (which happens\n * because `conn.destroy()` transitions connections to CLOSED).\n *\n * @param drainTimeoutMs - Maximum time to wait for connections to drain.\n */\n private async _drainAll(drainTimeoutMs: number): Promise<void> {\n // Snapshot connections for the close() call map (fixed at call time)\n const snapshot = [...this._connections];\n const closePromises = snapshot.map((conn) => conn.close({ drainTimeoutMs }));\n\n // Side-effect timeout: iterate LIVE this._connections (not snapshot) to only\n // destroy() connections that haven't already closed during the drain window\n const timeoutHandle = setTimeout(() => {\n for (const conn of this._connections) {\n conn.destroy();\n }\n }, drainTimeoutMs);\n // Do not keep the process alive just for this drain timer\n timeoutHandle.unref();\n\n try {\n await Promise.all(closePromises);\n } finally {\n clearTimeout(timeoutHandle);\n }\n }\n\n /**\n * Async disposal, delegates to `close()` for `await using` support.\n *\n * @example\n * ```typescript\n * await using server = createServer({ onMessage: handler });\n * await server.listen(2575);\n * // server.close() is called automatically at end of block\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n return this.close();\n }\n\n /**\n * Return a JSON-serializable observability snapshot.\n *\n * `totalBytesIn` and `totalBytesOut` aggregate from live connections at call time.\n *\n * @example\n * ```typescript\n * const stats = server.getStats();\n * logger.info(JSON.stringify(stats));\n * ```\n */\n getStats(): ServerStats {\n // Aggregate totalBytesIn/Out from live connections at call time (OBS-02, D-13)\n let totalBytesIn = 0;\n let totalBytesOut = 0;\n for (const conn of this._connections) {\n const s = conn.getStats();\n totalBytesIn += s.bytesIn;\n totalBytesOut += s.bytesOut;\n }\n return {\n listening: this._listening,\n port: this._port,\n host: this._host,\n connections: this._connections.size,\n activeConnections: this._connections.size,\n totalBytesIn,\n totalBytesOut,\n acceptedTotal: this._acceptedTotal,\n closedTotal: this._closedTotal,\n tls: this._isTls,\n tlsClientErrorsTotal: this._tlsClientErrorsTotal,\n };\n }\n\n /**\n * Emit on `'error'` only when a listener is attached, an unlistened\n * `EventEmitter` `'error'` emission throws (see the class JSDoc error\n * contract). The single guard shared by the optional-tap forwarding sites\n * in this class; the constructor's net.Server forwarder deliberately does\n * NOT use it (its while-serving branch re-emits unguarded to keep Node's\n * fail-loud convention).\n */\n /**\n * Emit a server event, containing a throwing subscriber.\n *\n * Every event this server emits is reachable from a callback we do not own, the `net.Server`\n * `'connection'` listener, the `tls.Server` `'tlsClientError'` listener, a socket's `'data'`\n * listener (via `Connection`), or the `catch` block of a `void`-ed async ACK task. A throwing\n * subscriber would therefore unwind into that callback and kill the process (or reject a void-ed\n * promise, which does the same and additionally skips the rest of the method).\n *\n * See `src/internal/safe-emit.ts` for the full rationale. The throw is re-surfaced on `'error'`.\n */\n private _emitContained(event: string, payload: unknown): void {\n safeEmit(this, event, payload, (err) => {\n this._emitErrorIfListened(err);\n });\n }\n\n private _emitErrorIfListened(errEvent: unknown): void {\n // Guards the unlistened case AND contains a throwing 'error' subscriber, see safe-emit.ts.\n safeEmitError(this, errEvent);\n }\n\n // ---------------------------------------------------------------------------\n // Private: per-socket setup\n // ---------------------------------------------------------------------------\n\n private _onSocketAccepted(socket: Socket | TLSSocket): void {\n // TCP keepalive, must be set on the raw socket BEFORE passing to NetTransport (D-10)\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n\n const transport = this._isTls\n ? new TlsTransport(socket as TLSSocket)\n : new NetTransport(socket);\n const mergedFraming: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\"> = {\n ...SERVER_DEFAULT_FRAMING,\n ...(this._opts.framing ?? {}),\n };\n\n const connOpts =\n this._opts.onWarning !== undefined\n ? { transport, framing: mergedFraming, onWarning: this._opts.onWarning }\n : { transport, framing: mergedFraming };\n const conn = new Connection(connOpts);\n\n // Set beforeClose no-op hook (Plan 04 will wire autoAck drain here if needed)\n conn.beforeClose = () => Promise.resolve();\n\n // Default 'error' handler on connection, prevents ERR_UNHANDLED_ERROR when\n // auto-ACK or transport errors are emitted on a connection with no user-attached\n // error listener. Forwards to server's 'error' event only when listeners exist;\n // otherwise silently swallows (D-04: server never crashes on connection errors).\n conn.on(\"error\", (errEvent: unknown) => {\n this._emitErrorIfListened(errEvent);\n });\n\n this._acceptedTotal++;\n this._connections.add(conn);\n\n // Remove from tracking when connection closes (CLOSED) or disconnects (DISCONNECTED).\n // Connection transitions to DISCONNECTED when peer closes gracefully (CONNECTED → DISCONNECTED).\n // It reaches CLOSED on destroy() / drain timeout / CONNECTING or RECONNECTING close.\n // We remove on either terminal-ish state to avoid leaking connections in _connections.\n // Single-fire guard prevents double-counting when both 'disconnect' and 'close' fire.\n let ended = false;\n const _onConnEnded = () => {\n if (ended) return;\n ended = true;\n this._connections.delete(conn);\n this._closedTotal++;\n };\n conn.once(\"close\", _onConnEnded);\n conn.once(\"disconnect\", _onConnEnded);\n\n // Wire dead-peer idle timeout (D-11), reset on every message\n if (this._opts.deadPeerTimeoutMs !== undefined) {\n const timeoutMs = this._opts.deadPeerTimeoutMs;\n let deadPeerTimer: ReturnType<typeof setTimeout> = setTimeout(() => {\n conn.destroy(new Error(\"idle timeout\"));\n }, timeoutMs);\n deadPeerTimer.unref();\n\n conn.on(\"message\", () => {\n clearTimeout(deadPeerTimer);\n deadPeerTimer = setTimeout(() => {\n conn.destroy(new Error(\"idle timeout\"));\n }, timeoutMs);\n deadPeerTimer.unref();\n });\n\n conn.once(\"close\", () => {\n clearTimeout(deadPeerTimer);\n });\n }\n\n // Notify the connection that the socket is connected (server-side, already connected)\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n\n // Wire message handler: always emit 'message' (received+framed) first, then route the\n // ACK by mode (the commit contract, see ServerOptions.autoAck). D-04: a handler error\n // never crashes the server. Async dispatch is wrapped in void; rejections are handled\n // inside the dispatch methods.\n conn.on(\n \"message\",\n (event: {\n payload: Buffer;\n connectionId: string;\n byteOffset: number;\n warnings: readonly MllpWarning[];\n }) => {\n const { payload, connectionId, byteOffset, warnings } = event;\n const meta: MessageMeta = Object.freeze({\n connectionId,\n byteOffset,\n warnings,\n });\n\n // D-03: emit 'message' BEFORE any ACK dispatch.\n //\n // Contained: a `'message'` subscriber is an OBSERVER (a metrics tap, a logger). The ACK\n // decision belongs to the commit contract below, `ServerOptions.onMessage` is the\n // durable-commit step, not this event. An observer that throws must therefore not be able\n // to suppress the ACK: before containment, one broken logger silently turned every message\n // into a no-ACK, so every sender resent forever with nothing to diagnose it by. The throw\n // is surfaced on `'error'` instead, and the commit contract proceeds untouched.\n try {\n this.emit(\"message\", Object.freeze({ payload, meta }));\n } catch (err) {\n this._emitErrorIfListened(\n Object.freeze({\n connectionId,\n error: err instanceof Error ? err : new Error(String(err)),\n }),\n );\n }\n\n const autoAck = this._opts.autoAck;\n if (autoAck === \"AA\") {\n // Commit-gated: onMessage is the durable-commit step; AA only on success,\n // AE/AR on failure, a positive ACK can never precede commit.\n void this._sendCommitAck(payload, meta, conn);\n } else if (autoAck !== undefined) {\n // Custom-ACK mode: onMessage is observation only; the fn owns the ACK bytes.\n this._observeMessage(payload, meta, conn);\n void this._sendCustomAck(autoAck, payload, meta, conn);\n } else {\n // Manual mode: onMessage owns the response via conn.send().\n this._observeMessage(payload, meta, conn);\n }\n },\n );\n\n // Phase 8, surface a minimal, content-free peer-certificate summary\n // (WANT/MUST only) on the 'connection' event. Never the full cert object.\n const peerCertificate: PeerCertificateSummary | null =\n this._isTls && (this._opts.tls?.clientAuth ?? \"NONE\") !== \"NONE\"\n ? this._extractPeerCertificateSummary(socket as TLSSocket)\n : null;\n\n // Emit server-level 'connection' event with frozen payload.\n //\n // Contained: `_onSocketAccepted` runs inside the net.Server/tls.Server 'connection' listener,\n // so a throwing subscriber (an audit logger, an allow-list check) would kill the whole server\n // on the next accept.\n this._emitContained(\n \"connection\",\n Object.freeze({\n connectionId: conn.connectionId,\n remoteAddress: socket.remoteAddress ?? null,\n remotePort: socket.remotePort ?? null,\n peerCertificate,\n }),\n );\n }\n\n /**\n * Extract a minimal, content-free peer-certificate summary (ATNA ITI-19\n * mutual authentication), CN strings, expiry, and the verification\n * outcome (`authorized`) only; never the full certificate object. Returns\n * `null` when no peer certificate was presented (e.g. `clientAuth: 'WANT'`\n * with no client cert).\n */\n private _extractPeerCertificateSummary(socket: TLSSocket): PeerCertificateSummary | null {\n const cert = socket.getPeerCertificate();\n if (cert === undefined || cert === null || Object.keys(cert).length === 0) {\n return null;\n }\n const asCn = (value: string | string[] | undefined): string | null => {\n if (value === undefined) return null;\n return Array.isArray(value) ? (value[0] ?? null) : value;\n };\n return Object.freeze({\n subjectCN: asCn(cert.subject?.CN),\n issuerCN: asCn(cert.issuer?.CN),\n validTo: cert.valid_to ?? null,\n // Verification outcome, under 'WANT' a certificate can be present yet\n // UNVERIFIED (see PeerCertificateSummary.authorized JSDoc).\n authorized: socket.authorized === true,\n });\n }\n\n /**\n * The fail-safe commit path for `autoAck: 'AA'` (HL7 v2.5.1 §2.9.2).\n *\n * With an `onMessage` handler this **awaits** it (the durable-commit step) and only\n * then dispatches: resolve ⇒ `AA`; throw/reject ⇒ a negative ACK (`AE` by default,\n * `AR` via {@link MllpAckError}). A positive ACK can never precede a successful commit.\n *\n * Without an `onMessage` handler, `'AA'` degrades to a **transport-accept**, `AA`\n * meaning \"bytes received and framed\", not \"application-processed\".\n *\n * A handler failure is **expected flow**, not a server error: it produces a negative\n * ACK and a `'nack'` event. The thrown error's message is never placed on the wire or\n * in the event (it may carry PHI), only the static `ackCode` and the inbound control\n * metadata reach the peer.\n */\n private async _sendCommitAck(\n payload: Buffer,\n meta: MessageMeta,\n conn: Connection,\n ): Promise<void> {\n const handler = this._opts.onMessage;\n\n // No commit handler: 'AA' is a transport-accept (received+framed only), but still\n // fail-safe-downgraded if the message cannot carry a correlatable positive ACK.\n if (handler === undefined) {\n this._dispatchAck(\n conn,\n buildRawAck(payload, this._resolveAutoAckPositive(payload, meta, conn)),\n );\n return;\n }\n\n let code: AckCode;\n try {\n await handler(payload, meta, conn); // durable-commit step\n // Commit succeeded, but only answer `AA` if the message is one we could correlate.\n code = this._resolveAutoAckPositive(payload, meta, conn);\n } catch (err: unknown) {\n const nack: NegativeAckCode = resolveNackCode(err);\n code = nack;\n // PHI-safe observability: control ID + outcome only, never the payload or error text.\n //\n // CONTAINED, and this one is load-bearing. This emit sits inside a `catch` block of an\n // `async` method that the caller `void`s, so a throwing `'nack'` subscriber (a metrics tap,\n // an alerting hook) would (a) reject the void-ed promise → unhandled rejection → process\n // death, and (b) skip the `_dispatchAck` below, so **the negative ACK would never be sent**.\n // The sender would be left waiting on an acknowledgement for a message the server had already\n // failed to commit. A broken metrics tap must never be able to suppress the fail-safe ACK.\n this._emitContained(\n \"nack\",\n Object.freeze({\n connectionId: conn.connectionId,\n ackCode: nack,\n reason: \"handler-rejected\",\n }),\n );\n }\n\n // Unconditional: reached whether the handler committed or threw, and no subscriber can skip it.\n this._dispatchAck(conn, buildRawAck(payload, code));\n }\n\n /**\n * Resolve the auto-ACK code for a message the server would otherwise acknowledge\n * **positively** (`AA`), the no-handler transport-accept and the post-commit success\n * paths. Fail-safe: downgrade to `AE` and emit a PHI-safe `'nack'` whenever the message\n * cannot carry a correlatable positive ACK, so `AA` never names a control ID the sender\n * cannot match (→ timeout → resend → **duplicate clinical message**).\n *\n * Two independent disqualifiers, one from the bytes and one from the frame:\n *\n * - **`rawAckUncorrelatable(payload)`**, no readable `MSH`, an empty MSH-10, or a\n * batch/concatenated-message shape. This is the SAME predicate `buildRawAck` enforces\n * on the wire; it is re-checked here so the downgrade is **observable** (a `'nack'`\n * event) rather than a silent code change inside the builder.\n * - **`MLLP_TRAILING_BYTES`** in the frame's warnings, a mid-payload `VT` made the\n * decoder discard accumulated bytes and deliver only the fragment after it. The\n * clinical message was destroyed in transit, so even a fragment that happens to carry\n * a readable MSH must not be positively acknowledged. This condition is invisible to\n * `buildRawAck` (it sees only the fragment), which is why it lives here. The code is\n * **reserved** for that discard (see the decoder's `_readPayload`) and is emitted while\n * accumulating the delivered frame, so it is frame-scoped: it names *this* payload's own\n * discard, never a neighbouring frame's trailing junk.\n *\n * `'discarded-bytes'` takes precedence in the reason when both hold, it is the root cause.\n */\n private _resolveAutoAckPositive(payload: Buffer, meta: MessageMeta, conn: Connection): AckCode {\n const discarded = meta.warnings.some((w) => w.code === \"MLLP_TRAILING_BYTES\");\n const uncorrelatable = rawAckUncorrelatable(payload);\n if (!discarded && !uncorrelatable) return \"AA\";\n\n const reason: NackReason = discarded ? \"discarded-bytes\" : \"uncorrelatable-inbound\";\n // CONTAINED, this runs inside `_sendCommitAck`, a `void`-ed async task. A throwing `'nack'`\n // subscriber must not unwind into it (unhandled rejection → process death) nor skip the `AE`\n // that the caller dispatches next. PHI-safe: `reason` and `ackCode` are static, no payload.\n this._emitContained(\n \"nack\",\n Object.freeze({\n connectionId: conn.connectionId,\n ackCode: \"AE\",\n reason,\n }),\n );\n return \"AE\";\n }\n\n /**\n * Run `onMessage` for its side effects only (custom-ACK and manual modes), guarding\n * against a synchronous throw or a rejected promise. In these modes the handler does\n * **not** gate the ACK, so a failure is re-emitted as a PHI-safe `'error'` on the\n * connection (a handler error never crashes the server) rather than escaping as\n * an unhandled rejection. The error text is never placed on the wire.\n */\n private _observeMessage(payload: Buffer, meta: MessageMeta, conn: Connection): void {\n const handler = this._opts.onMessage;\n if (handler === undefined) return;\n void Promise.resolve()\n .then(() => handler(payload, meta, conn))\n .catch((err: unknown) => {\n const connErr = err instanceof Error ? err : new Error(String(err));\n // safeEmitError, not conn.emit: a raw emit bypasses Connection's containment, so an\n // unlistened or throwing 'error' subscriber would unwind into this void-ed async task\n // (unhandled rejection → process death).\n safeEmitError(conn, Object.freeze({ connectionId: conn.connectionId, error: connErr }));\n });\n }\n\n /**\n * The custom-builder path for `autoAck: fn`. Awaits the builder and dispatches its\n * Buffer as the ACK; the caller owns MSA-1. A builder error is re-emitted as `'error'`\n * on the connection, the server never crashes.\n */\n private async _sendCustomAck(\n fn: (payload: Buffer, meta: MessageMeta, conn: Connection) => Buffer | Promise<Buffer>,\n payload: Buffer,\n meta: MessageMeta,\n conn: Connection,\n ): Promise<void> {\n try {\n const ackPayload = await Promise.resolve(fn(payload, meta, conn));\n this._dispatchAck(conn, ackPayload);\n } catch (err: unknown) {\n const connErr = err instanceof Error ? err : new Error(String(err));\n // See above, never a raw conn.emit from inside a callback/async catch.\n safeEmitError(conn, Object.freeze({ connectionId: conn.connectionId, error: connErr }));\n }\n }\n\n /**\n * Frame an ACK payload and write it to the connection. `Connection.send()` returns\n * `false` under backpressure (socket write buffer full); that drops the ACK and emits\n * a `MllpConnectionError({ phase: 'send' })` on the connection, the peer will\n * time out waiting and may retry. The server never crashes.\n */\n private _dispatchAck(conn: Connection, ackPayload: Buffer): void {\n // TOTAL by contract: this is reached from `void`-ed async tasks (`_sendCommitAck`, the\n // transport-accept branch), so a throw here becomes an **unhandled rejection that kills the\n // process**, and it would do so on peer-controlled input. `encodeFrame` is strict and throws\n // `MLLP_PAYLOAD_CONTAINS_VT`/`_FS` if the ACK payload contains a framing byte.\n //\n // On the auto-ACK path this is **hard to provoke, but not impossible, and the containment is\n // what makes that safe.** `buildRawAck` decodes and encodes as `latin1`, a 1:1 byte↔code-unit\n // map, so it cannot *synthesize* a framing byte the way `ascii` masking once did\n // (`0x8B & 0x7F` = VT, the Phase 10 bug): it only ever echoes bytes that were already in the\n // inbound. A delivered payload never contains a VT (`FrameReader` discards its accumulator on a\n // mid-payload VT, `MLLP_TRAILING_BYTES`), so an echoed VT is not a concern here. It CAN,\n // however, contain an FS: under the `allowMissingLeadingVt` tolerance a non-VT, non-whitespace\n // first byte becomes payload byte 0, and FS (0x1C) qualifies, so an FS echoed out of MSH-10\n // into MSA-2 reaches this `encodeFrame`, which throws `MLLP_PAYLOAD_CONTAINS_FS`. (Those codes\n // are thrown by `encodeFrame`, on the way out; the decoder never emits them.) And a caller's\n // `autoAck: fn` can of course return arbitrary bytes.\n //\n // Whichever route, defending the process must not depend on the caller's, or the peer's,\n // discipline. A build/frame failure is surfaced as a connection `'error'` and the message goes\n // un-ACKed (fail-safe: better an un-ACKed message the sender will resend than a dead server),\n // never as a process kill.\n let framed: Buffer;\n try {\n framed = encodeFrame(ackPayload);\n } catch (err: unknown) {\n const cause = err instanceof Error ? err : new Error(String(err));\n safeEmitError(\n conn,\n Object.freeze({\n connectionId: conn.connectionId,\n error: new MllpConnectionError(\"ACK could not be framed\", { cause, phase: \"send\" }),\n }),\n );\n return;\n }\n\n const sent = conn.send(framed);\n if (!sent) {\n safeEmitError(\n conn,\n Object.freeze({\n connectionId: conn.connectionId,\n error: new MllpConnectionError(\"ACK dropped: socket backpressure\", {\n cause: new Error(\"backpressure\"),\n phase: \"send\",\n }),\n }),\n );\n }\n }\n}\n\n/**\n * Factory function, creates a new `MllpServer` with the supplied options.\n *\n * Prefer `createServer()` over `new MllpServer()` for forward-compatible construction.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received', payload.length, 'bytes');\n * },\n * });\n * await server.listen(2575);\n * ```\n */\nexport function createServer(opts: ServerOptions = {}): MllpServer {\n return new MllpServer(opts);\n}\n\n/**\n * Starter factory, creates, configures, and starts an `MllpServer` in one call.\n *\n * Provides the \"three lines of code\" north-star experience with sensible defaults:\n * `autoAck: 'AA'`, `drainTimeoutMs: 30_000`, `Symbol.asyncDispose` wired.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n *\n * const server = await createStarterServer({\n * port: 2575,\n * onMessage: async (payload) => {\n * await db.commit(payload); // the durable-commit step; a throw answers a negative ACK\n * },\n * });\n * ```\n */\nexport async function createStarterServer(opts: StarterServerOptions): Promise<MllpServer> {\n const server = createServer({\n ...opts,\n autoAck: opts.autoAck ?? \"AA\",\n drainTimeoutMs: opts.drainTimeoutMs ?? 30_000,\n });\n\n if (opts.handleSignals === true) {\n // D-09: register process.once('SIGTERM'/'SIGINT') so signal fires close() + exit(0).\n // Use once() (not on()) so handlers self-remove after first fire, preventing accumulation\n // across multiple server instances.\n const sigHandler = (): void => {\n void server\n .close()\n .then(() => process.exit(0))\n .catch(() => process.exit(1));\n };\n process.once(\"SIGTERM\", sigHandler);\n process.once(\"SIGINT\", sigHandler);\n\n // Clean up handlers on server close so tests do not accumulate listeners.\n // process.once handlers self-remove on first fire; this removes them early when\n // close() is called before any signal fires (the common test path).\n server.once(\"close\", () => {\n process.removeListener(\"SIGTERM\", sigHandler);\n process.removeListener(\"SIGINT\", sigHandler);\n });\n }\n\n await server.listen(opts.port, opts.host ?? \"127.0.0.1\");\n return server;\n}\n","/**\n * MLLP Client ACK correlator, pure data structure.\n *\n * Unified `Map<correlationKey, PendingAck>` with ES2015 insertion-ordered\n * iteration is the single source of truth for in-flight + queued sends.\n *\n * - FIFO mode keys by synthetic monotonic `sendSeq: number`.\n * - controlId mode keys by MSH-10 `string`.\n * - `maxInFlight=1` (`pipeline:false`) is enforced as a guard\n * on the same store; not a separate class.\n *\n * **INTERNAL**, not re-exported from the package barrel. The class knows\n * nothing about `Connection`, the event emitter, sockets, or timers. Sweep\n * cadence is driven externally by the periodic sweep tick on `MllpClient`\n * so the Correlator itself stays timer-free.\n *\n * @packageDocumentation\n */\n\nimport type { AckCorrelationCode } from \"./ack-diagnostics.js\";\n\n/** Correlation key, number for FIFO (`sendSeq`), string for controlId (MSH-10). */\nexport type CorrelationKey = number | string;\n\n/**\n * The MSH-10 / MSA-2 byte-level scanners the correlator keys on.\n *\n * They live in `src/internal/control-id.ts`, not here: the client is one of\n * **three** call sites that must agree byte-for-byte on what a control ID is\n * (the others are `buildRawAck` in `src/server/ack.ts` and the verbatim-echo\n * check in `src/ack-from-hl7/build.ts`), and any disagreement between two of\n * them is an ACK the sender cannot match, a timeout, a resend, and a duplicate\n * clinical message. One implementation, one `latin1` decode, one dynamic read\n * of MSH-1. Re-exported here because they were part of this module's `@internal`\n * surface before the move.\n */\nexport {\n CONTROL_ID_ENCODING,\n extractMshControlId,\n extractMsaControlId,\n} from \"../internal/control-id.js\";\n\n/** A single pending send awaiting its ACK. */\nexport interface PendingAck {\n readonly key: CorrelationKey;\n readonly frame: Buffer;\n readonly controlId: string | null;\n readonly byteCount: number;\n /** Epoch ms recorded by `markFlushed()`; `null` until transport flush. */\n sentAt: number | null;\n readonly resolve: (ack: Buffer) => void;\n readonly reject: (err: Error) => void;\n}\n\n/** Graveyard bookkeeping for late-ACK detection. */\nexport interface GraveyardEntry {\n readonly timedOutAt: number;\n readonly controlId: string | null;\n}\n\n/** JSON-serializable snapshot used by `client.getStats()`. */\nexport interface CorrelatorStats {\n readonly size: number;\n readonly queueBytes: number;\n readonly graveyardSize: number;\n readonly sendSeq: number;\n /**\n * Count of live entries with `sentAt !== null`.\n *\n * Distinct from `size`. `size` includes pre-flush entries (an entry was\n * `enqueue()`'d but `markFlushed()` has not been called yet) AND\n * serialization-queued entries (`pipeline:false`'s deferred sends). A\n * pre-flush entry contributes to `size` but NOT to `inFlight`.\n */\n readonly inFlight: number;\n}\n\n/** Constructor options. INTERNAL callback-bag pattern (mirrors `FrameReaderOptions`). */\nexport interface CorrelatorOptions {\n /** `'fifo'` (default) or `'controlId'`. */\n readonly mode?: \"fifo\" | \"controlId\";\n /** Default `30_000`. */\n readonly ackTimeoutMs?: number;\n /** Default `Infinity`; set to `1` for `pipeline:false`. */\n readonly maxInFlight?: number;\n /**\n * Emits `MLLP_ACK_AFTER_TIMEOUT` and `MLLP_ACK_UNMATCHED_CONTROL_ID`.\n * `byteOffset` is forwarded from the inbound ACK frame\n * for observability.\n *\n * **The context carries numbers, never the control ID itself.**\n * `controlIdBytes` is its byte length (`null` when there is no id), because\n * this context is handed to a warning that goes to a log, and a control ID is\n * payload content. The correlator withholds the string at the source so no\n * consumer downstream of it can interpolate one.\n */\n readonly onWarning: (\n code: AckCorrelationCode,\n ctx: {\n controlIdBytes: number | null;\n elapsedSinceSendMs: number;\n byteOffset: number;\n },\n ) => void;\n /**\n * Called by `matchAck()` in controlId mode on unmatched-ACK.\n *\n * Receives the **byte length** of the inbound MSA-2, or `null` when no MSA-2\n * could be extracted at all. The bytes themselves are deliberately not passed:\n * see {@link CorrelatorOptions.onWarning}.\n */\n readonly onUnmatchedAck?: (controlIdBytes: number | null) => void;\n /** Fired by `expireDue()`; `MllpClient` turns into `MllpTimeoutError`. */\n readonly onTimeout: (entry: PendingAck, elapsedMs: number) => void;\n /** Injected clock; default `Date.now`. Tests inject deterministic clock. */\n readonly now?: () => number;\n}\n\n/**\n * Pure data structure backing `MllpClient.send()` ACK correlation.\n *\n * @example\n * ```typescript\n * const correlator = new Correlator({\n * mode: 'fifo',\n * ackTimeoutMs: 30_000,\n * onTimeout: (entry, elapsed) => entry.reject(new MllpTimeoutError('timeout', { ... })),\n * onWarning: (code, ctx) => emitter.emit('warning', { code, ...ctx }),\n * });\n * const key = correlator.enqueue(frame, null, resolve, reject);\n * correlator.markFlushed(key);\n * const matched = correlator.matchAck(ackPayload);\n * if (matched !== null) matched.resolve(ackPayload);\n * ```\n */\nexport class Correlator {\n private readonly _opts: {\n readonly mode: \"fifo\" | \"controlId\";\n readonly ackTimeoutMs: number;\n readonly maxInFlight: number;\n readonly onWarning: CorrelatorOptions[\"onWarning\"];\n readonly onTimeout: CorrelatorOptions[\"onTimeout\"];\n readonly onUnmatchedAck: CorrelatorOptions[\"onUnmatchedAck\"];\n readonly now: () => number;\n };\n private readonly _pending: Map<CorrelationKey, PendingAck> = new Map();\n private readonly _graveyard: Map<CorrelationKey, GraveyardEntry> = new Map();\n private _sendSeq = 0;\n private _queueBytes = 0;\n /**\n * Count of pending entries with `sentAt !== null`.\n * Maintained at every site that mutates `entry.sentAt` or removes a\n * flushed entry: `markFlushed`, `remove`, `matchAck`, `expireDue`, `clear`.\n */\n private _inFlight = 0;\n\n /**\n * Construct a send/ACK correlator.\n *\n * @param opts - Correlation options (FIFO vs control-id mode, ACK timeout, max in-flight).\n */\n constructor(opts: CorrelatorOptions) {\n this._opts = {\n mode: opts.mode ?? \"fifo\",\n ackTimeoutMs: opts.ackTimeoutMs ?? 30_000,\n maxInFlight: opts.maxInFlight ?? Number.POSITIVE_INFINITY,\n onWarning: opts.onWarning,\n onTimeout: opts.onTimeout,\n onUnmatchedAck: opts.onUnmatchedAck,\n now: opts.now ?? Date.now,\n };\n }\n\n /** Number of live pending entries. */\n get size(): number {\n return this._pending.size;\n }\n /** Sum of `frame.length` across live entries. */\n get queueBytes(): number {\n return this._queueBytes;\n }\n /** Number of graveyard entries awaiting lazy eviction. */\n get graveyardSize(): number {\n return this._graveyard.size;\n }\n\n /**\n * Enqueue a new send awaiting its ACK. Returns the assigned\n * `correlationKey`, or `null` if `maxInFlight` is reached (caller awaits\n * drain, `pipeline:false`).\n */\n enqueue(\n frame: Buffer,\n controlIdOrNull: string | null,\n resolve: (ack: Buffer) => void,\n reject: (err: Error) => void,\n ): CorrelationKey | null {\n if (this._pending.size >= this._opts.maxInFlight) return null;\n // controlId mode keys by MSH-10 (string). When MSH-10 is absent, we fall\n // back to a synthetic `__seq-N` key, the send is best-effort matchable\n // by the FIFO live-store walk, but the peer realistically cannot ACK it\n // by control ID. Acceptable corner case (D-03/A1).\n const key: CorrelationKey =\n this._opts.mode === \"controlId\"\n ? (controlIdOrNull ?? `__seq-${++this._sendSeq}`)\n : ++this._sendSeq;\n const entry: PendingAck = {\n key,\n frame,\n controlId: controlIdOrNull,\n byteCount: frame.length,\n sentAt: null,\n resolve,\n reject,\n };\n this._pending.set(key, entry);\n this._queueBytes += frame.length;\n return key;\n }\n\n /**\n * Record write-flush timestamp (the clock starts at flush, NOT\n * at `send()` call). No-op if key is unknown (e.g. removed by abort).\n */\n markFlushed(key: CorrelationKey, now?: number): void {\n const entry = this._pending.get(key);\n if (entry === undefined) return;\n // PLAN-06: only the first flush bumps _inFlight. Subsequent re-flush\n // (e.g. PLAN-04's controlId reflushAll on reconnect) is idempotent.\n if (entry.sentAt === null) this._inFlight += 1;\n entry.sentAt = now ?? this._opts.now();\n }\n\n /**\n * Match an inbound ACK against the live store.\n *\n * - FIFO: returns first pending entry by insertion order; entry is\n * removed from live store. Caller calls `entry.resolve(ackPayload)`.\n * - controlId: keyed lookup by `controlIdFromAck`. Live-store hit returns\n * the entry; graveyard hit fires `MLLP_ACK_AFTER_TIMEOUT` warning\n * and returns `null`; otherwise fires `onUnmatchedAck` with the id's\n * **byte length** and returns `null`.\n *\n * Triggers lazy graveyard eviction.\n *\n * @param _payload Inbound ACK bytes (framing stripped). MSA-2 extraction\n * happens at MllpClient's `_onAckPayload` hook; this method takes the\n * already-extracted control ID as a parameter.\n * @param controlIdFromAck MSA-2 extracted from ACK (controlId mode only).\n * @param byteOffsetFromAck Stream offset; forwarded to `onWarning`.\n */\n matchAck(\n _payload: Buffer,\n controlIdFromAck: string | null = null,\n byteOffsetFromAck = 0,\n ): PendingAck | null {\n this._evictGraveyardDue(this._opts.now());\n if (this._opts.mode === \"fifo\") {\n const iter = this._pending.values().next();\n if (iter.done === true) return null;\n const entry = iter.value;\n this._pending.delete(entry.key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06 (D-26): only flushed entries count toward _inFlight.\n if (entry.sentAt !== null) this._inFlight -= 1;\n return entry;\n }\n // controlId mode (PLAN-03)\n if (controlIdFromAck === null) {\n // Caller failed to extract MSA-2; treat as unmatched. (MllpClient is\n // responsible for extraction; this is a defensive fallback.)\n if (this._opts.onUnmatchedAck !== undefined) {\n this._opts.onUnmatchedAck(null);\n }\n return null;\n }\n const live = this._pending.get(controlIdFromAck);\n if (live !== undefined) {\n this._pending.delete(controlIdFromAck);\n this._queueBytes -= live.byteCount;\n // PLAN-06 (D-26): only flushed entries count toward _inFlight.\n if (live.sentAt !== null) this._inFlight -= 1;\n return live;\n }\n const grave = this._graveyard.get(controlIdFromAck);\n if (grave !== undefined) {\n // CLIENT-16: late ACK after timeout. Forward the inbound ACK frame's\n // byte offset (W-05) so observers see where in the stream it landed.\n const elapsedSinceSendMs = this._opts.now() - grave.timedOutAt;\n this._opts.onWarning(\"MLLP_ACK_AFTER_TIMEOUT\", {\n // The LENGTH, never the id. Control IDs are decoded `latin1`, a 1:1\n // byte to code-unit map, so `.length` is a byte count.\n controlIdBytes: grave.controlId === null ? null : grave.controlId.length,\n elapsedSinceSendMs,\n byteOffset: byteOffsetFromAck,\n });\n // One-shot: drop the graveyard entry now that we've seen the late ACK.\n this._graveyard.delete(controlIdFromAck);\n return null;\n }\n // CLIENT-15: unmatched controlId (live store empty + not in graveyard).\n // The LENGTH crosses this boundary, never the peer's bytes.\n if (this._opts.onUnmatchedAck !== undefined) {\n this._opts.onUnmatchedAck(controlIdFromAck.length);\n }\n return null;\n }\n\n /**\n * Sweep live entries; expire those past `sentAt + ackTimeoutMs`.\n * Fires `onTimeout(entry, elapsedMs)`; entries move to graveyard.\n * Driven externally by `MllpClient`'s periodic sweep tick, Correlator\n * itself owns no timers.\n */\n expireDue(now?: number): void {\n const t = now ?? this._opts.now();\n const ackTimeoutMs = this._opts.ackTimeoutMs;\n for (const [key, entry] of this._pending) {\n if (entry.sentAt !== null && entry.sentAt + ackTimeoutMs <= t) {\n const elapsed = t - entry.sentAt;\n this._pending.delete(key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06: expired entry was flushed by the guard above.\n this._inFlight -= 1;\n this._graveyard.set(key, {\n timedOutAt: t,\n controlId: entry.controlId,\n });\n this._opts.onTimeout(entry, elapsed);\n }\n }\n }\n\n /**\n * Reject every live entry with `reason` (insertion order) and clear\n * the live store. Graveyard is left intact (ages out via lazy eviction).\n * Used by `MllpClient`'s correlator teardown to cancel pending sends.\n */\n clear(reason: Error): void {\n for (const entry of this._pending.values()) entry.reject(reason);\n this._pending.clear();\n this._queueBytes = 0;\n // PLAN-06: reset _inFlight unconditionally, clear() drops every entry,\n // flushed or not. Avoids any over-decrement edge case.\n this._inFlight = 0;\n }\n\n /** Iterate live entries in insertion order (reconnect-resend). */\n *liveEntries(): IterableIterator<PendingAck> {\n for (const entry of this._pending.values()) yield entry;\n }\n\n /**\n * Remove a live entry by key WITHOUT resolving/rejecting. Returns the\n * removed entry, or `null` if no entry with that key exists.\n * Used by `MllpClient.send()` for AbortSignal cleanup and by the\n * reconnect-reject FSM walk.\n */\n remove(key: CorrelationKey): PendingAck | null {\n const entry = this._pending.get(key);\n if (entry === undefined) return null;\n this._pending.delete(key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06: only flushed entries count toward _inFlight.\n if (entry.sentAt !== null) this._inFlight -= 1;\n return entry;\n }\n\n /** JSON-serializable stats snapshot (no Buffers, no class instances). */\n getStats(): CorrelatorStats {\n return {\n size: this._pending.size,\n queueBytes: this._queueBytes,\n graveyardSize: this._graveyard.size,\n sendSeq: this._sendSeq,\n inFlight: this._inFlight,\n };\n }\n\n /** Lazy graveyard eviction: drop entries past `timedOutAt + 2 * ackTimeoutMs`. */\n private _evictGraveyardDue(now: number): void {\n const threshold = 2 * this._opts.ackTimeoutMs;\n for (const [key, entry] of this._graveyard) {\n if (entry.timedOutAt + threshold <= now) this._graveyard.delete(key);\n }\n }\n}\n","/**\n * The frozen diagnostic registry for ACK correlation.\n *\n * Every message an ACK-correlation diagnostic can carry lives here as a\n * literal, and the lookup takes **only a code**. There is no value parameter\n * anywhere in this module, which is the whole point: a factory that cannot be\n * handed a value cannot interpolate one, regardless of what arrives on the\n * wire.\n *\n * ## What this replaces, and why the shape matters more than the wording\n *\n * The `correlateByControlId` path used to build its diagnostics by\n * interpolation, and both ends of it were consumer-controlled and unbounded.\n * The unmatched-ACK error read the **peer's** MSA-2 straight out of an inbound\n * frame and put it on an `Error.message`; measured against a peer sending a\n * one-megabyte MSA-2, that produced a 1,000,026-byte `message` and an\n * equally large field on the frozen error payload. The late-ACK warning did\n * the same with the control id of the send being reported on. Both go to a\n * log, and in this domain a log is a place a patient identifier must not\n * reach. That is not a hypothetical about control ids either: this package has\n * already shipped one scanner that ran past a segment terminator and returned\n * PID-3, a patient's medical record number, as the \"control ID\" of a truncated\n * MSH. Withholding the bytes is the defence that survives the next such bug.\n *\n * ## What a correlation diagnostic says instead\n *\n * That a control id did not match, plus **numbers**: a byte length, a stream\n * offset, an elapsed time. A number is a shape, not content. A truncated id is\n * not an acceptable middle ground, and neither is a hex or base64 rendering:\n * both still grow the diagnostic with the input and both still disclose the\n * bytes. Nothing is lost by withholding them, because the caller already holds\n * them: the outbound payload is the caller's own argument to `send()`, and the\n * inbound frame is on the `'message'` event.\n *\n * Mirrors the same answer this package already reached for the ACK adapter's\n * verbatim-echo warning, which reports byte lengths and withholds MSH-10 and\n * MSA-2 themselves.\n *\n * @packageDocumentation\n */\n\n/**\n * The two warning codes emitted by ACK correlation rather than by framing.\n *\n * A subset of the framing `WarningCode` union; named separately so the\n * registry below is exhaustive over exactly the codes it owns and a new\n * correlation code cannot be added without a message for it.\n */\nexport type AckCorrelationCode = \"MLLP_ACK_UNMATCHED_CONTROL_ID\" | \"MLLP_ACK_AFTER_TIMEOUT\";\n\n/**\n * Frozen diagnostic text, one literal per code, no interpolation.\n *\n * Do **not** add a value parameter to anything that reads this table. The\n * distinguishing property of every diagnostic surface that has leaked across\n * this ecosystem is that its factory took a value at all.\n */\nconst ACK_DIAGNOSTIC_MESSAGES: Readonly<Record<AckCorrelationCode, string>> = Object.freeze({\n MLLP_ACK_UNMATCHED_CONTROL_ID:\n \"Inbound ACK control ID matched no in-flight send and no timed-out send; the ACK was dropped. \" +\n \"Field values are withheld: MSA-2 is inbound payload content and this diagnostic goes to a log. \" +\n \"The inbound bytes are on the 'message' event; controlIdBytes gives the length.\",\n MLLP_ACK_AFTER_TIMEOUT:\n \"ACK arrived after its send had already timed out; the ACK was dropped. \" +\n \"Field values are withheld: a control ID is payload content and this diagnostic goes to a log. \" +\n \"The outbound bytes are the payload you passed to send(); controlIdBytes gives the length.\",\n});\n\n/**\n * Look up the frozen diagnostic text for an ACK-correlation code.\n *\n * A pure table read. It takes no value, so no caller can widen it into an\n * interpolation site.\n *\n * @param code - The correlation code being reported.\n * @returns The registered message for that code, byte-for-byte.\n * @example\n * ```typescript\n * const message = ackDiagnosticMessage('MLLP_ACK_AFTER_TIMEOUT');\n * ```\n */\nexport function ackDiagnosticMessage(code: AckCorrelationCode): string {\n return ACK_DIAGNOSTIC_MESSAGES[code];\n}\n","/**\n * MLLP Client typed errors and classifiers.\n *\n * Exports:\n * - `MllpTimeoutError`, ACK timeout\n * - `MllpBackpressureError`, high-water mark exceeded\n * - `isTransientConnectionError`, transient/permanent classifier\n *\n * Re-exported from `src/client/index.ts` and `src/index.ts`.\n *\n * @packageDocumentation\n */\n\n/**\n * Thrown (or rejects the `send()` promise) when an ACK does not arrive within\n * the configured `ackTimeoutMs`.\n *\n * The timeout clock starts at the underlying `write()` flush callback, NOT\n * at the `send()` call, pre-flush queue time is not charged to the peer.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpTimeoutError) {\n * logger.warn({ elapsedMs: err.elapsedMs, idBytes: err.messageControlIdBytes });\n * }\n * }\n * ```\n */\nexport class MllpTimeoutError extends Error {\n override readonly name = \"MllpTimeoutError\" as const;\n\n /**\n * Byte length of the timed-out send's MSH-10 control ID, or `undefined` when\n * there was none to read (FIFO mode, or a payload with no MSH-10).\n *\n * **The control ID itself is deliberately not here.** An `Error` is a\n * diagnostic surface: it is logged, and its `stack` is what an error reporter\n * ships off the box. MSH-10 is payload content, and a scanner that returns\n * the wrong field returns payload content of some other kind, which is how a\n * patient identifier reaches a log line. Nothing is lost by withholding it,\n * because this error rejects the very `send()` whose payload the caller\n * passed in, so the caller already holds the bytes. Control IDs are decoded\n * `latin1`, a 1:1 byte to code-unit map, so this count is a byte count.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /** Milliseconds elapsed between write-flush and timeout fire. */\n readonly elapsedMs: number;\n\n /** Epoch ms timestamp recorded at write-flush callback. */\n readonly sentAt: number;\n\n /**\n * Construct an MLLP timeout error.\n *\n * @param message - Human-readable error message. Structural facts only, never field content.\n * @param opts - Timeout context (control-id byte length, elapsed time, flush timestamp).\n */\n constructor(\n message: string,\n opts: {\n messageControlIdBytes: number | undefined;\n elapsedMs: number;\n sentAt: number;\n },\n ) {\n super(message);\n this.messageControlIdBytes = opts.messageControlIdBytes;\n this.elapsedMs = opts.elapsedMs;\n this.sentAt = opts.sentAt;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpTimeoutError);\n }\n }\n}\n\n/**\n * Set of Node/OpenSSL error codes that indicate a **certificate-verification**\n * failure (as opposed to some other TLS handshake failure), untrusted chain,\n * expired/not-yet-valid certificate, hostname mismatch, revocation, etc.\n *\n * Used by `MllpClient` to classify a TLS connect failure's\n * `connectionCause` as `'tls-verify'` (this set) vs `'tls-handshake'`\n * (everything else observed before `'secureConnect'`). Exported so callers\n * can apply the same classification to their own error handling.\n *\n * @example\n * ```typescript\n * import { isTlsVerificationErrorCode } from '@cosyte/mllp';\n * if (isTlsVerificationErrorCode('CERT_HAS_EXPIRED')) {\n * // definitely a verification failure, not a protocol/cipher mismatch\n * }\n * ```\n */\nexport function isTlsVerificationErrorCode(code: string): boolean {\n switch (code) {\n case \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\":\n case \"DEPTH_ZERO_SELF_SIGNED_CERT\":\n case \"SELF_SIGNED_CERT_IN_CHAIN\":\n case \"UNABLE_TO_GET_ISSUER_CERT\":\n case \"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\":\n case \"CERT_HAS_EXPIRED\":\n case \"CERT_NOT_YET_VALID\":\n case \"CERT_REVOKED\":\n case \"CERT_UNTRUSTED\":\n case \"CERT_REJECTED\":\n case \"CERT_SIGNATURE_FAILURE\":\n case \"HOSTNAME_MISMATCH\":\n case \"ERR_TLS_CERT_ALTNAME_INVALID\":\n return true;\n default:\n return code.startsWith(\"CERT_\");\n }\n}\n\n/**\n * Detects **TLS-protocol-shaped** errors, failures of the TLS protocol\n * itself, as opposed to plain TCP-level network failures.\n *\n * Apply this only to errors raised on a **TLS** connection; `MllpClient`\n * does exactly that (the predicate is consulted only when `ClientOptions.tls`\n * is set). The boundary:\n *\n * **TLS-protocol-shaped (`true`):**\n * - `code` starting `ERR_SSL_` (Node's TLS alert codes, e.g.\n * `ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED`, a `clientAuth: 'MUST'`\n * server rejecting the client's certificate).\n * - `code === 'EPROTO'`, on a TLS connection this is OpenSSL failing the\n * handshake (protocol version mismatch, no shared cipher, a TLS ≤1.2\n * mTLS rejection).\n * - `message` containing `ssl` or `alert` (`/\\bssl\\b|\\balert\\b/i`, \"SSL\n * routines\", \"tlsv13 alert certificate required\", …). This message check\n * is a **heuristic backstop** over the code-based checks above, not a\n * precise boundary: it exists to catch OpenSSL errors that surface without\n * a usable `code`, and `MllpClient` consults it only on connections where\n * TLS is configured. An arbitrary non-TLS error whose message happens to\n * contain those words would also match.\n *\n * **NOT TLS-protocol-shaped (`false`), plain network failures, which stay\n * transient for the reconnect classifier:** `ECONNREFUSED`, `ETIMEDOUT`,\n * `EHOSTUNREACH`, `ENETUNREACH`, `EPIPE`, and a plain `ECONNRESET` carrying\n * no TLS alert context, a network blip during (or after) a handshake\n * should still auto-heal.\n *\n * Certificate-**verification** failures are a separate class, see\n * {@link isTlsVerificationErrorCode}; `MllpClient` checks that first and\n * labels those `connectionCause: 'tls-verify'`.\n *\n * Why this matters: under TLS 1.3 (RFC 8446 §4.4.2) a `clientAuth: 'MUST'`\n * server can reject the client's certificate AFTER the client's own\n * `'secureConnect'`, the rejection then surfaces as a post-connect socket\n * error. A misconfigured mTLS client must never auto-reconnect-loop against\n * a server that will always reject it, so `MllpClient` classifies\n * TLS-protocol-shaped errors as **permanent**.\n *\n * @example\n * ```typescript\n * import { isTlsProtocolError, MllpConnectionError } from '@cosyte/mllp';\n * client.on('error', ({ error }) => {\n * if (error instanceof MllpConnectionError && isTlsProtocolError(error.cause)) {\n * // TLS protocol failure, a configuration problem, not a network blip.\n * }\n * });\n * ```\n */\nexport function isTlsProtocolError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return false;\n const code = (err as { code?: unknown }).code;\n if (typeof code === \"string\") {\n if (code.startsWith(\"ERR_SSL_\")) return true;\n if (code === \"EPROTO\") return true;\n }\n const message = (err as { message?: unknown }).message;\n if (typeof message === \"string\" && /\\bssl\\b|\\balert\\b/i.test(message)) return true;\n return false;\n}\n\n/**\n * Classifies a connection error as transient (eligible for auto-reconnect)\n * or permanent (halts auto-reconnect, transitions to CLOSED).\n *\n * Used internally by `MllpClient` BEFORE invoking `retryStrategy`\n * (see `RetryContext.classifiedAs`). Re-exported so callers can implement\n * their own retry policies.\n *\n * Classification table:\n * - `ENOTFOUND`, `EACCES` → **permanent** (`false`)\n * - `ECONNREFUSED`, `ECONNRESET`, `ETIMEDOUT`, `EHOSTUNREACH`,\n * `ENETUNREACH`, `EPIPE` → **transient** (`true`)\n * - `CERT_*` and `UNABLE_TO_VERIFY_LEAF_SIGNATURE` /\n * `DEPTH_ZERO_SELF_SIGNED_CERT` / `SELF_SIGNED_CERT_IN_CHAIN`\n * (any {@link isTlsVerificationErrorCode} code) → **permanent** (`false`),\n * never auto-reconnect-loop into a misconfigured or MITM'd endpoint.\n * - `ERR_SSL_*` (Node TLS alert codes) → **permanent** (`false`), a TLS\n * protocol failure such as a `clientAuth: 'MUST'` server rejecting the\n * client certificate recurs on every attempt. On TLS-configured\n * connections `MllpClient` additionally consults {@link isTlsProtocolError},\n * which also catches `EPROTO`/alert-bearing OpenSSL errors that this\n * generic classifier (which cannot know the connection was TLS) leaves\n * transient.\n * - `MLLP_*` (any {@link MllpFramingError} code, a fatal decoder throw, surfaced with\n * `connectionCause: 'framing-fatal'`) → **permanent** (`false`). The peer is not\n * speaking MLLP, an HTTP probe, a health check, a wrong-port misconfiguration, or is emitting\n * frames past `maxFrameSizeBytes`. Every reconnect meets the same bytes, so retrying is an\n * unbounded storm against a peer that is already misconfigured. If a peer's quirk is *expected*,\n * the decoder's tolerance opt-ins are the supported answer, they make it a warning, not a fatal.\n * - non-Error / unknown / no-code → **transient** (`true`), Postel's Law\n * default. Reconnect attempts are bounded by `retryStrategy` and the\n * 30s backoff cap, so the default is safe.\n *\n * @example\n * ```typescript\n * import { isTransientConnectionError } from '@cosyte/mllp';\n * client.on('error', (err) => {\n * if (isTransientConnectionError(err)) {\n * metrics.increment('mllp.transient_error');\n * } else {\n * metrics.increment('mllp.permanent_error');\n * }\n * });\n * ```\n */\nexport function isTransientConnectionError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return true;\n const code = (err as { code?: unknown }).code;\n if (typeof code !== \"string\") return true;\n switch (code) {\n case \"ENOTFOUND\":\n case \"EACCES\":\n return false;\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"ETIMEDOUT\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n case \"EPIPE\":\n return true;\n default:\n // TLS cert error codes (CERT_*) and *_VERIFY_* names → permanent.\n if (isTlsVerificationErrorCode(code)) return false;\n // Node TLS alert codes → permanent (Phase 8; recur on every attempt).\n if (code.startsWith(\"ERR_SSL_\")) return false;\n // A fatal MLLP framing error → permanent (MLLP-10). The peer is not speaking MLLP (an HTTP\n // probe, a health check, a wrong-port misconfiguration) or is emitting frames past the size\n // cap. Every reconnect meets the same bytes, so retrying is an unbounded reconnect storm\n // against a peer that is already misconfigured, the same reasoning that makes the TLS\n // classes permanent. A compatibility failure is not a network blip.\n if (code.startsWith(\"MLLP_\")) return false;\n // Default: transient (Postel's Law, be permissive about peer behavior).\n return true;\n }\n}\n\n/**\n * Thrown (or rejects the `send()` promise) when the in-flight queue exceeds\n * the configured high-water mark and `onBackpressure: 'reject'` is set.\n *\n * `highWaterMark` accepts a count cap, a byte cap, or both, when both are\n * present, the stricter-of-two trigger wins.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpBackpressureError) {\n * logger.warn({\n * queueDepth: err.queueDepth,\n * queueBytes: err.queueBytes,\n * cap: err.highWaterMark,\n * });\n * }\n * }\n * ```\n */\nexport class MllpBackpressureError extends Error {\n override readonly name = \"MllpBackpressureError\" as const;\n\n /** Number of in-flight + queued sends at the moment of rejection. */\n readonly queueDepth: number;\n\n /** Total bytes of in-flight + queued frames at the moment of rejection. */\n readonly queueBytes: number;\n\n /** The high-water-mark configuration that was triggered. */\n readonly highWaterMark: { readonly count?: number; readonly bytes?: number };\n\n /**\n * Construct an MLLP backpressure error.\n *\n * @param message - Human-readable error message.\n * @param opts - Backpressure context (queue depth, queued bytes, the high-water-mark hit).\n */\n constructor(\n message: string,\n opts: {\n queueDepth: number;\n queueBytes: number;\n highWaterMark: { count?: number; bytes?: number };\n },\n ) {\n super(message);\n this.queueDepth = opts.queueDepth;\n this.queueBytes = opts.queueBytes;\n this.highWaterMark = opts.highWaterMark;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpBackpressureError);\n }\n }\n}\n","/**\n * MLLP Client, `createClient()` factory and `MllpClient` class.\n *\n * Provides the client-side MLLP transport: connect to an MLLP server over TCP,\n * encode and send framed messages, decode inbound ACKs, and surface lifecycle\n * events with frozen payloads. Supports `AbortSignal` cancellation on every\n * awaitable and `Symbol.asyncDispose` for `await using` ergonomics.\n *\n * The `correlateByControlId` option (MSH-10 → MSA-2 ACK matching) enables\n * out-of-order ACK handling.\n *\n * @example\n * ```typescript\n * import { createClient } from '@cosyte/mllp';\n *\n * const client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createConnection } from \"node:net\";\nimport type { Socket } from \"node:net\";\nimport { connect as tlsConnect } from \"node:tls\";\nimport type { TLSSocket } from \"node:tls\";\nimport { EventEmitter } from \"node:events\";\nimport { Connection } from \"../connection/index.js\";\nimport type { ConnectionState, StateChangeEvent } from \"../connection/index.js\";\nimport { MllpConnectionError } from \"../connection/index.js\";\nimport { safeEmit, safeEmitError } from \"../internal/safe-emit.js\";\nimport { NetTransport, TlsTransport } from \"../transport/index.js\";\nimport type { Transport } from \"../transport/index.js\";\nimport type { TlsOptions } from \"../transport/tls-options.js\";\nimport { MLLP_TLS_VERIFY_DISABLED, type SecurityWarning } from \"../transport/security-warnings.js\";\nimport { encodeFrame } from \"../framing/index.js\";\nimport { MllpFramingError } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning, WarningCode } from \"../framing/index.js\";\nimport { Correlator, extractMshControlId, extractMsaControlId } from \"./correlator.js\";\nimport type { PendingAck } from \"./correlator.js\";\nimport { ackDiagnosticMessage } from \"./ack-diagnostics.js\";\nimport type { AckCorrelationCode } from \"./ack-diagnostics.js\";\nimport {\n MllpTimeoutError,\n MllpBackpressureError,\n isTransientConnectionError,\n isTlsVerificationErrorCode,\n isTlsProtocolError,\n} from \"./error.js\";\n\n/**\n * Module-level \"never aborts\" sentinel for `RetryContext.signal`.\n *\n * When `connect()` is called WITHOUT a signal, `RetryContext.signal` must\n * still be a real `AbortSignal` (the type is non-optional). This sentinel\n * is constructed once and reused across all signal-less reconnect cycles\n * no new AbortController is allocated per cycle.\n *\n * The originating `AbortController` is held in module-private scope and\n * never exposed; hostile callers cannot abort the sentinel.\n */\nconst NEVER_ABORTING_SIGNAL: AbortSignal = new AbortController().signal;\n\n/**\n * The `'warning'` payload the client emits for an ACK-correlation deviation\n * (`MLLP_ACK_UNMATCHED_CONTROL_ID`, `MLLP_ACK_AFTER_TIMEOUT`).\n *\n * A `MllpWarning` with two extra numeric fields. `message` is a **frozen\n * registry entry**, byte-for-byte identical for a given code no matter what\n * arrived on the wire, and everything input-derived is a number: the control\n * ID's byte length and the elapsed time. A warning is a log line, so it carries\n * no field content.\n *\n * @example\n * ```typescript\n * client.on('warning', (w: AckCorrelationWarning) => {\n * logger.warn({ code: w.code, idBytes: w.controlIdBytes, elapsedMs: w.elapsedSinceSendMs });\n * });\n * ```\n */\nexport interface AckCorrelationWarning extends MllpWarning {\n /** The correlation code this warning reports. */\n readonly code: AckCorrelationCode;\n /**\n * Byte length of the control ID involved, or `null` when there was none to\n * read. Control IDs are decoded `latin1`, so a code-unit count is a byte count.\n */\n readonly controlIdBytes: number | null;\n /**\n * Milliseconds between the send's write-flush (or its timeout, for a late\n * ACK) and this warning.\n */\n readonly elapsedSinceSendMs: number;\n}\n\n/**\n * Context passed to a custom `retryStrategy` hook on each reconnect attempt.\n *\n * Frozen via `Object.freeze` before invocation, handlers cannot mutate.\n *\n * @example\n * ```typescript\n * const retryStrategy: RetryStrategy = (ctx) => {\n * if (ctx.attempt >= 5) return null;\n * if (ctx.classifiedAs === 'permanent') return null;\n * return Math.min(30_000, 1000 * (ctx.attempt + 1));\n * };\n * ```\n */\nexport interface RetryContext {\n /** 0-indexed attempt counter for the current reconnect cycle. */\n readonly attempt: number;\n /** The error that triggered the disconnect. */\n readonly lastError: Error;\n /** Delay used for the previous attempt (ms). 0 on the first attempt. */\n readonly lastDelayMs: number;\n /** Total wall-clock ms elapsed since the disconnect that started this cycle. */\n readonly totalElapsedMs: number;\n /** Ms since the last successful ACK. `Infinity` if no success seen. */\n readonly sinceLastSuccessMs: number;\n /** How the failure that triggered this reconnect attempt was classified. */\n readonly classifiedAs: \"transient\" | \"permanent\";\n /**\n * The same `AbortSignal` passed into `connect()`. If no signal was\n * supplied, the module-level `NEVER_ABORTING_SIGNAL` sentinel is provided\n * so handlers always have a real `AbortSignal` to inspect.\n */\n readonly signal: AbortSignal;\n}\n\n/**\n * Custom reconnect-backoff hook. Return `null` to halt\n * reconnection, the FSM transitions to `CLOSED`.\n */\nexport type RetryStrategy = (ctx: RetryContext) => number | null;\n\n/**\n * Combined count + byte-based queue cap. Stricter-of-two wins.\n *\n * - `number`, count cap only (default 64).\n * - `{ bytes }`, byte cap only.\n * - `{ count, bytes }`, both caps; whichever trips first wins.\n *\n * @example\n * ```typescript\n * const opts: ClientOptions = {\n * host: 'localhost', port: 2575,\n * highWaterMark: { count: 100, bytes: 1_000_000 },\n * };\n * ```\n */\nexport type HighWaterMark = number | { readonly count?: number; readonly bytes?: number };\n\n/**\n * Options for {@link createClient} and the {@link MllpClient} constructor.\n *\n * @example\n * ```typescript\n * const opts: ClientOptions = { host: 'localhost', port: 2575, drainTimeoutMs: 10_000 };\n * ```\n */\nexport interface ClientOptions {\n /** Host to connect to (e.g. `'localhost'` or `'mllp.example.com'`). */\n readonly host: string;\n /** TCP port. */\n readonly port: number;\n /** FrameReader tolerance / size options. `onFrame` and `onWarning` are managed internally. */\n readonly framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n /** Drain timeout for {@link MllpClient.close} (default: `30_000` ms). */\n readonly drainTimeoutMs?: number;\n /**\n * Per-message ACK timeout in milliseconds. The clock starts at\n * the underlying `write()` flush callback, NOT at the `send()` call,\n * pre-flush queue time is not charged to the peer. Default: `30_000`.\n */\n readonly ackTimeoutMs?: number;\n /**\n * If `true`, ACKs are matched against outgoing sends by MSH-10 → MSA-2.\n * Default `false` (FIFO mode).\n *\n * Out-of-order ACKs from the peer are supported in this mode. MSH-10 is\n * extracted from the outbound payload before send; MSA-2 is extracted from\n * the inbound ACK payload. An ACK whose MSA-2 matches no pending send\n * (and is not in the late-ACK graveyard) emits a frozen\n * `MllpFramingError('MLLP_ACK_UNMATCHED_CONTROL_ID')` to the `'error'` event.\n * A late ACK whose MSA-2 matches a graveyard entry emits a\n * `MLLP_ACK_AFTER_TIMEOUT` warning and is dropped.\n *\n * @default false\n */\n readonly correlateByControlId?: boolean;\n /**\n * Auto-reconnect on transient disconnect. Default `false`.\n *\n * When `true`, dropped connections caused by transient errors (per\n * {@link isTransientConnectionError}) trigger the FSM cycle\n * `CONNECTED → DISCONNECTED → RECONNECTING → CONNECTING → CONNECTED`\n * with exponential backoff unless overridden by\n * {@link ClientOptions.retryStrategy}. Permanent errors halt and\n * transition directly to `CLOSED`.\n */\n readonly autoReconnect?: boolean;\n /**\n * Custom reconnect-backoff hook. Return `null` to halt\n * reconnection. Receives a frozen {@link RetryContext}. Defaults\n * to the exponential strategy.\n */\n readonly retryStrategy?: RetryStrategy;\n /** First delay (ms) on auto-reconnect; default 100. */\n readonly initialDelayMs?: number;\n /** Maximum backoff cap (ms); default 30_000. */\n readonly maxDelayMs?: number;\n /** Backoff multiplier; default 2. */\n readonly multiplier?: number;\n /** Jitter fraction, e.g. 0.2 = ±20%; default 0.2. */\n readonly jitter?: number;\n /**\n * Application-level high-water mark on the in-flight + queued send set.\n * `number` configures a count cap (default 64);\n * `{ bytes }` configures a byte cap; `{ count, bytes }` configures\n * both, with the stricter-of-two trigger winning.\n *\n * When the cap is exceeded, behavior is governed by\n * {@link ClientOptions.onBackpressure}.\n *\n * @default 64\n */\n readonly highWaterMark?: HighWaterMark;\n /**\n * Behavior when the high-water mark is exceeded.\n *\n * - `'reject'` (default), `send()` rejects with `MllpBackpressureError`.\n * - `'wait'`, `send()` awaits the `'drain'` event OR the per-message\n * `ackTimeoutMs` OR `signal` abort, whichever fires first.\n *\n * @default 'reject'\n */\n readonly onBackpressure?: \"reject\" | \"wait\";\n /**\n * Strict serialization send → await-ACK → send.\n *\n * - `true` (default), concurrent in-flight sends up to\n * {@link ClientOptions.highWaterMark}.\n * - `false`, collapses the in-flight set to ≤1 (the unified Correlator's\n * `maxInFlight=1`); the next `send()` waits for the prior ACK before\n * reaching the wire.\n *\n * @default true\n */\n readonly pipeline?: boolean;\n /**\n * TCP keepalive interval (ms). Sets `socket.setKeepAlive(true, ms)` on\n * the underlying `net.Socket` BEFORE wrapping in `NetTransport`. OS-level\n * half-open detection (network partitions, NAT-table eviction). Independent\n * of {@link ClientOptions.deadPeerTimeoutMs}.\n *\n * @default undefined (off)\n */\n readonly keepaliveIntervalMs?: number;\n /**\n * Application-idle timeout (ms) keyed on last inbound bytes / ACK / warning.\n * On trip, calls `connection.destroy(new Error('dead\n * peer timeout'))` which surfaces as `MllpConnectionError({ phase: 'receive' })`.\n * Trip honors {@link ClientOptions.autoReconnect}. Independent of\n * {@link ClientOptions.keepaliveIntervalMs}.\n *\n * @default undefined (off)\n */\n readonly deadPeerTimeoutMs?: number;\n /**\n * Enable TLS (MLLPS) for this connection. `true` enables TLS with\n * all defaults, including certificate verification **on**. Pass a\n * {@link TlsOptions} object to customize (`ca`/`cert`/`key`, minimum\n * version, ciphers, `allowUnverified`, …).\n *\n * Spec anchor: IHE ATNA ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @default undefined (plaintext TCP)\n */\n readonly tls?: TlsOptions | true;\n}\n\n/**\n * Observability snapshot returned by {@link MllpClient.getStats}.\n *\n * All fields are JSON-serializable, no Buffers, no class instances,\n * no Maps, no circular references. `lastConnectedAt` and `lastAckAt` are\n * **epoch milliseconds** (numbers), NOT `Date` instances, log-pipeline\n * friendly.\n *\n * `warningsByCode` keys are constrained to the public {@link WarningCode}\n * union, adding/removing a code is a breaking change (CLAUDE.md\n * stable-codes guardrail enforced at the type boundary).\n *\n * @example\n * ```typescript\n * const stats = client.getStats();\n * logger.info(JSON.stringify(stats));\n * // {\"state\":\"CONNECTED\",\"connectionId\":\"…\",\"queueDepth\":0, … }\n * ```\n */\nexport interface ClientStats {\n /** Current FSM state, mirrors `client.state`. */\n readonly state: ConnectionState;\n /** Live Connection's id, or `null` before the first connect (or post-CLOSED). */\n readonly connectionId: string | null;\n /** Total live correlator entries (in-flight + pre-flush + serialization-queued). */\n readonly queueDepth: number;\n /** Sum of `frame.length` across live correlator entries. */\n readonly queueBytes: number;\n /** Entries with `sentAt !== null`, actually written to the wire / awaiting ACK. */\n readonly inFlight: number;\n /**\n * Aggregated warning counts. Keys are constrained to the public\n * {@link WarningCode} union. Connection-level warnings + Correlator\n * `MLLP_ACK_*` warnings are merged.\n */\n readonly warningsByCode: Partial<Record<WarningCode, number>>;\n /** Bytes received from the peer (current Connection). */\n readonly totalBytesIn: number;\n /** Bytes written to the peer (current Connection). */\n readonly totalBytesOut: number;\n /** Total successful `connection.send()` calls since construction. */\n readonly sentTotal: number;\n /** Total ACKs matched + resolved since construction. */\n readonly ackedTotal: number;\n /** Total ACK timeouts since construction. */\n readonly timedOutTotal: number;\n /** Total reconnect attempts since construction. */\n readonly reconnectAttempts: number;\n /** Epoch ms of the last `CONNECTED` transition. `null` until first connect. */\n readonly lastConnectedAt: number | null;\n /** Epoch ms of the most recent successful ACK. `null` until first ACK. */\n readonly lastAckAt: number | null;\n /** Whether this client is configured for TLS. Mirrors `ClientOptions.tls` being set. */\n readonly tls: boolean;\n}\n\n/**\n * MLLP client, composes a single {@link Connection} over a {@link NetTransport}\n * (production) or any other `Transport` (testing via {@link InMemoryTransport}).\n *\n * Public events, every payload `Object.freeze`'d before emission:\n * - `'stateChange'`, `{ from, to, reason? }` from the underlying Connection FSM\n * - `'connect'`, `{ connectionId }` once the FSM enters `CONNECTED`\n * - `'disconnect'`, `{ connectionId }` once the FSM enters `DISCONNECTED`\n * - `'reconnecting'`, `{ connectionId, attempt?, delayMs? }`\n * - `'close'`, `{ connectionId }` once the FSM enters terminal `CLOSED`\n * - `'message'`, `{ payload, connectionId, byteOffset, warnings }` for every inbound frame\n * - `'warning'`, `MllpWarning | AckCorrelationWarning`. A framing deviation arrives as a\n * `MllpWarning` enriched with `connectionId` from the Connection layer; the two\n * ACK-correlation codes arrive as {@link AckCorrelationWarning}, which adds\n * `controlIdBytes` and `elapsedSinceSendMs`. Narrow on `code` before reading either.\n * - `'securityWarning'`, `SecurityWarning`. Emitted on every successful\n * `secureConnect` (initial + every reconnect) when `tls.allowUnverified` is `true`\n * with code `MLLP_TLS_VERIFY_DISABLED`. Also mirrored to `process.emitWarning`.\n * - `'error'`, re-emitted from Connection. Guarded by `listenerCount('error') > 0` so\n * absence of a listener does NOT crash the process (server precedent).\n *\n * @example\n * ```typescript\n * const client = createClient({ host: 'localhost', port: 2575 });\n * client.on('stateChange', ({ from, to }) => logger.info({ from, to }));\n * client.on('message', ({ payload }) => logger.info({ bytes: payload.length }));\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n */\nexport class MllpClient extends EventEmitter {\n private readonly _opts: ClientOptions;\n private _connection: Connection | null = null;\n private _socket: Socket | null = null;\n /**\n * Initial state for `get state()` before `_connection` exists. Once a Connection\n * is attached, `state` mirrors `_connection.state`.\n */\n private _state: ConnectionState = \"DISCONNECTED\";\n\n /** Per-message ACK timeout in ms. Resolved at construction. */\n private readonly _ackTimeoutMs: number;\n /** controlId-mode flag. `false` → FIFO. */\n private readonly _correlateByControlId: boolean;\n /** Unified ACK correlator. Built during `_attachConnection`. */\n private _correlator: Correlator | null = null;\n /**\n * Periodic ACK-timeout sweep timer. Drives `_correlator.expireDue()` because\n * the Correlator is timer-free. Cleared on close/destroy.\n */\n private _ackSweepTimer: ReturnType<typeof setInterval> | null = null;\n\n // ── Reconnect state (Plan 04, CLIENT-05/06/12/17/18) ─────────────────────\n private readonly _autoReconnect: boolean;\n private readonly _initialDelayMs: number;\n private readonly _maxDelayMs: number;\n private readonly _multiplier: number;\n private readonly _jitter: number;\n private readonly _retryStrategy: RetryStrategy | undefined;\n\n /** 0-indexed attempt counter for the current reconnect cycle. */\n private _attempt = 0;\n /**\n * Total reconnect attempts since construction, surfaced as\n * `getStats().reconnectAttempts`. Incremented at the entry of every\n * `_handleDisconnect` invocation that proceeds to schedule a backoff.\n */\n private _reconnectAttempts = 0;\n /** Epoch ms of the last successful ACK. Drives backoff-reset. */\n private _lastSuccessAt: number | null = null;\n /** Epoch ms when the current reconnect cycle began. `null` outside a cycle. */\n private _reconnectCycleStartedAt: number | null = null;\n /** Active backoff `setTimeout` handle. `null` when no backoff is armed. */\n private _backoffTimer: ReturnType<typeof setTimeout> | null = null;\n /** Last delay (ms) used by the strategy. Surfaced via RetryContext.lastDelayMs. */\n private _lastDelayMs = 0;\n\n // ── Backpressure + pipeline state (Plan 05, CLIENT-07/CLIENT-19, D-23) ───\n /** Count cap; `Number.POSITIVE_INFINITY` when only bytes configured. */\n private readonly _hwmCount: number;\n /** Byte cap; `Number.POSITIVE_INFINITY` when only count configured. */\n private readonly _hwmBytes: number;\n /** Backpressure policy; default `'reject'`. */\n private readonly _onBackpressure: \"reject\" | \"wait\";\n /** Pipeline flag; default `true` (parallel up to highWaterMark). */\n private readonly _pipeline: boolean;\n /** Dead-peer idle timer. `null` when not armed. */\n private _deadPeerTimer: ReturnType<typeof setTimeout> | null = null;\n /** True once the self-'ack' listener that resets the dead-peer timer\n * has been attached. Guards against duplicate listeners on reconnect. */\n private _ackResetWired = false;\n /** Most recently bound `connect()` signal. Reread on every RetryContext build. */\n private _connectSignal: AbortSignal | undefined;\n /** Set when close()/destroy()/abort fires; reconnect handler short-circuits. */\n private _userClosed = false;\n /** Captured Connection error feeding `RetryContext.lastError`. */\n private _lastError: Error | null = null;\n /** Listener-removal handle for the abort listener bound in connect(). */\n private _abortListener: { signal: AbortSignal; handler: () => void } | null = null;\n /**\n * Test-only seam, when set, `_beginReconnectAttempt` builds the new\n * Connection through this factory instead of opening a real net.Socket.\n *\n * @internal\n */\n private _reconnectFactory: (() => { conn: Connection; arm: () => void }) | null = null;\n\n // ── PLAN-06, observability counters for getStats (OBS-01, D-26) ─────────\n /** Total successful `conn.send()` flushes since construction. */\n private _sentTotal = 0;\n /** Total ACKs resolved since construction. */\n private _ackedTotal = 0;\n /** Total ACK timeouts since construction. */\n private _timedOutTotal = 0;\n /** Epoch ms of the most recent CONNECTED transition (null until first). */\n private _lastConnectedAt: number | null = null;\n /** Epoch ms of the most recent successful ACK (null until first). */\n private _lastAckAt: number | null = null;\n /**\n * Aggregated Correlator-emitted warning counts (MLLP_ACK_*). Connection-level\n * warnings are read directly from `_connection.getStats().warningsByCode`\n * at observation time and merged into the snapshot.\n */\n private _aggregatedWarningsByCode: Partial<Record<WarningCode, number>> = {};\n\n /**\n * Construct an MLLP client. Created idle; call `connect()` (or use\n * {@link createClient}/{@link createStarterClient}) to open the connection.\n *\n * @param opts - Client options (host/port, ACK timeout, reconnect/backpressure policy, …).\n */\n constructor(opts: ClientOptions) {\n super();\n this._opts = opts;\n this._ackTimeoutMs = opts.ackTimeoutMs ?? 30_000;\n this._correlateByControlId = opts.correlateByControlId === true;\n this._autoReconnect = opts.autoReconnect === true;\n this._initialDelayMs = opts.initialDelayMs ?? 100;\n this._maxDelayMs = opts.maxDelayMs ?? 30_000;\n this._multiplier = opts.multiplier ?? 2;\n this._jitter = opts.jitter ?? 0.2;\n this._retryStrategy = opts.retryStrategy;\n\n // Plan 05, backpressure + pipeline (D-23, D-06).\n const hwm: HighWaterMark = opts.highWaterMark ?? 64;\n if (typeof hwm === \"number\") {\n this._hwmCount = hwm;\n this._hwmBytes = Number.POSITIVE_INFINITY;\n } else {\n this._hwmCount = hwm.count ?? Number.POSITIVE_INFINITY;\n this._hwmBytes = hwm.bytes ?? Number.POSITIVE_INFINITY;\n }\n this._onBackpressure = opts.onBackpressure ?? \"reject\";\n this._pipeline = opts.pipeline !== false;\n }\n\n /**\n * Current FSM state. Mirrors the underlying Connection's state once attached;\n * before `connect()` (or after a `CLOSED` Connection is dropped) reports\n * the client-level baseline (`'DISCONNECTED'`).\n */\n get state(): ConnectionState {\n return this._connection?.state ?? this._state;\n }\n\n /**\n * Open a TCP (or TLS) connection to the configured `host:port`\n * and attach a {@link Connection} to it. Resolves once the FSM\n * enters `CONNECTED`, for TLS, on `'secureConnect'` (handshake complete,\n * including certificate verification when it is on).\n *\n * Rejects with:\n * - `DOMException('Aborted', 'AbortError')` if `signal` is provided and aborts\n * before the connect resolves.\n * - `MllpConnectionError({ phase: 'connect' })` if the underlying socket emits\n * `error` before connecting, OR if the client is already connecting/connected.\n * TLS failures carry a `connectionCause`: `'tls-verify'` for certificate\n * verification failures, `'tls-handshake'` for TLS-protocol-shaped failures\n * ({@link isTlsProtocolError}); pure TCP failures carry none.\n *\n * **Dual failure signal on initial connect:** the Connection's transport\n * error handler is attached before this promise's own error listener, so a\n * pre-connect socket error produces BOTH the promise rejection AND a\n * client `'error'` event (when an `'error'` listener is attached). Handle\n * whichever fits your flow; they describe the same underlying failure.\n *\n * **TLS 1.3 + mutual TLS caveat (RFC 8446 §4.4.2):** `connect()` resolving\n * does NOT guarantee that a `clientAuth: 'MUST'` server accepted your\n * client certificate. Under TLS 1.3 the client's handshake, and its\n * `'secureConnect'`, can complete before the server finishes validating\n * the certificate; a rejection then surfaces moments later as a typed\n * post-connect error (`'error'` event with an `ERR_SSL_*`/alert cause,\n * classified **permanent**, no auto-reconnect loop). ACK correlation via\n * {@link MllpClient.send} remains the delivery guarantee: no send resolves\n * without its ACK, so a rejected session can never silently \"deliver\".\n *\n * @example\n * ```typescript\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5_000);\n * await client.connect({ signal: ac.signal });\n * ```\n */\n connect(opts?: { signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Reject if we already hold a live Connection. Once a Connection has reached\n // CLOSED or DISCONNECTED we drop the reference and allow a fresh connect().\n if (\n this._connection !== null &&\n this._connection.state !== \"CLOSED\" &&\n this._connection.state !== \"DISCONNECTED\"\n ) {\n return Promise.reject(\n new MllpConnectionError(\"already connected or connecting\", {\n cause: new Error(\"already connected\"),\n phase: \"connect\",\n }),\n );\n }\n\n // Capture the connect signal for the reconnect cycle (W-07). Each call\n // overwrites the prior binding; `RetryContext.signal` reads `_connectSignal`\n // at the moment a RetryContext is built.\n if (signal !== undefined) {\n this._captureConnectSignal(signal);\n }\n\n return new Promise<void>((resolve, reject) => {\n let aborted = false;\n\n const { socket, transport } = this._createSocketAndTransport();\n // Plan 05, TCP keepalive set on the raw socket BEFORE NetTransport\n // wrap (CLIENT-08, D-11/A3, mirrors Phase 4 server). OS-level\n // half-open detection. No JS-side timer (W-03).\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n this._socket = socket;\n\n const connOpts =\n this._opts.framing !== undefined\n ? { transport, framing: this._opts.framing }\n : { transport };\n if (this._opts.drainTimeoutMs !== undefined) {\n (connOpts as { drainTimeoutMs?: number }).drainTimeoutMs = this._opts.drainTimeoutMs;\n }\n const conn = new Connection(connOpts);\n this._attachConnection(conn);\n\n const connectEventName = this._opts.tls !== undefined ? \"secureConnect\" : \"connect\";\n\n const cleanup = (): void => {\n if (signal !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n socket.removeListener(connectEventName, onSocketConnect);\n socket.removeListener(\"error\", onSocketError);\n };\n\n const abortHandler = (): void => {\n aborted = true;\n cleanup();\n // Tear down the in-flight attempt, also clears the correlator so\n // any sweep timer armed by _attachConnection is released.\n this._teardownCorrelator(\n new MllpConnectionError(\"connect aborted\", {\n cause: new Error(\"aborted\"),\n phase: \"connect\",\n }),\n );\n conn.destroy(new Error(\"aborted\"));\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n\n const onSocketConnect = (): void => {\n if (aborted) return;\n // TLS and plaintext behave identically here: 'secureConnect' (TLS)\n // or 'connect' (plaintext) immediately arms the Connection and\n // resolves. Deferring notifyConnect for TLS was tried and removed,\n // any delay leaves the Connection in CONNECTING while post-handshake\n // frames arrive, and Connection discards frames outside\n // CONNECTED/DRAINING (a silent inbound-frame drop). See the\n // TLS 1.3 note in the method JSDoc for what this means for mTLS.\n cleanup();\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n this._emitInsecureWarningIfNeeded();\n resolve();\n };\n\n const onSocketError = (err: Error): void => {\n if (aborted) return;\n cleanup();\n // Surface the OS error wrapped in MllpConnectionError (Connection's\n // _onTransportError handles the same wrap once attached, but the\n // socket's 'error' may arrive before NetTransport hands it off).\n reject(this._wrapConnectError(err, \"connect\"));\n };\n\n if (signal !== undefined) {\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n socket.once(connectEventName, onSocketConnect);\n socket.once(\"error\", onSocketError);\n });\n }\n\n /**\n * Build the raw socket + `Transport` pair for a connect / reconnect attempt.\n *\n * Plaintext (`ClientOptions.tls` unset): a `net.Socket` wrapped in\n * `NetTransport`. TLS: a `tls.TLSSocket` wrapped in\n * `TlsTransport`, verification defaults **on**\n * (`rejectUnauthorized: !allowUnverified`), floor `minVersion: 'TLSv1.2'`\n * (the IHE ATNA ITI-19 BCP195 floor), `servername` defaulting to\n * `ClientOptions.host`.\n */\n private _createSocketAndTransport(): {\n socket: Socket | TLSSocket;\n transport: Transport;\n } {\n const tlsOpt = this._opts.tls;\n if (tlsOpt === undefined) {\n const socket = createConnection({ host: this._opts.host, port: this._opts.port });\n return { socket, transport: new NetTransport(socket) };\n }\n const tlsOpts: TlsOptions = tlsOpt === true ? {} : tlsOpt;\n const socket = tlsConnect({\n host: this._opts.host,\n port: this._opts.port,\n servername: tlsOpts.servername ?? this._opts.host,\n ...(tlsOpts.ca !== undefined ? { ca: tlsOpts.ca } : {}),\n ...(tlsOpts.cert !== undefined ? { cert: tlsOpts.cert } : {}),\n ...(tlsOpts.key !== undefined ? { key: tlsOpts.key } : {}),\n ...(tlsOpts.passphrase !== undefined ? { passphrase: tlsOpts.passphrase } : {}),\n minVersion: tlsOpts.minVersion ?? \"TLSv1.2\",\n ...(tlsOpts.maxVersion !== undefined ? { maxVersion: tlsOpts.maxVersion } : {}),\n ...(tlsOpts.ciphers !== undefined ? { ciphers: tlsOpts.ciphers } : {}),\n rejectUnauthorized: tlsOpts.allowUnverified !== true,\n });\n return { socket, transport: new TlsTransport(socket) };\n }\n\n /**\n * Wrap a connect-phase socket error as `MllpConnectionError`, classifying\n * TLS failures into the additive `connectionCause`:\n *\n * - `'tls-verify'`, certificate-verification failures\n * ({@link isTlsVerificationErrorCode}).\n * - `'tls-handshake'`, TLS-**protocol**-shaped failures only\n * ({@link isTlsProtocolError}): `ERR_SSL_*`, `EPROTO`, OpenSSL\n * alert-bearing errors.\n * - **No `connectionCause`**, pure TCP-level failures (`ECONNREFUSED`,\n * `ETIMEDOUT`, …) even on a TLS-configured connection; these carry the\n * same shape as plaintext connect failures.\n */\n private _wrapConnectError(err: Error, phase: \"connect\" | \"reconnect\"): MllpConnectionError {\n if (this._opts.tls === undefined) {\n return new MllpConnectionError(err.message, { cause: err, phase });\n }\n const code = (err as NodeJS.ErrnoException).code;\n if (typeof code === \"string\" && isTlsVerificationErrorCode(code)) {\n return new MllpConnectionError(err.message, {\n cause: err,\n phase,\n connectionCause: \"tls-verify\",\n });\n }\n if (isTlsProtocolError(err)) {\n return new MllpConnectionError(err.message, {\n cause: err,\n phase,\n connectionCause: \"tls-handshake\",\n });\n }\n // Pure TCP-level failure on a TLS-configured connection, same shape as\n // a plaintext connect failure; no TLS-specific connectionCause.\n return new MllpConnectionError(err.message, { cause: err, phase });\n }\n\n /**\n * Emit a client event, containing a throwing subscriber.\n *\n * Every event this client emits is reached from a callback we do not own, a socket's\n * `'connect'`/`'secureConnect'`/`'data'`/`'error'` listener, or a backoff timer. A throwing\n * subscriber would unwind into it and kill the process, and on several paths it would also skip\n * the work queued after the emit (the `resolve()` of `connect()`, the backoff scheduling).\n *\n * See `src/internal/safe-emit.ts`. The throw is re-surfaced on `'error'`.\n */\n private _emitContained(event: string, payload: unknown): void {\n safeEmit(this, event, payload, (err) => {\n safeEmitError(this, Object.freeze({ error: err }));\n });\n }\n\n /**\n * Emit the per-connection insecure-TLS warning when\n * `tls.allowUnverified === true`, fires on EVERY successful\n * `secureConnect`, initial connect and every reconnect. Emits both a frozen\n * `'securityWarning'` event and `process.emitWarning`. No-op for plaintext\n * connections or when verification is on.\n */\n private _emitInsecureWarningIfNeeded(): void {\n const tlsOpt = this._opts.tls;\n if (tlsOpt === undefined || tlsOpt === true) return;\n if (tlsOpt.allowUnverified !== true) return;\n const message =\n \"MLLP TLS certificate verification is DISABLED (allowUnverified: true), \" +\n \"this connection does not authenticate the peer.\";\n const warning: SecurityWarning = Object.freeze({\n code: MLLP_TLS_VERIFY_DISABLED,\n message,\n host: this._opts.host,\n port: this._opts.port,\n timestamp: new Date(),\n });\n // Contained: this is reached from the socket's 'secureConnect' listener. A throwing\n // subscriber (a security-audit hook, exactly the kind of thing that subscribes here) would\n // both kill the process AND skip the resolve() that follows in onSocketConnect, hanging\n // connect() forever.\n this._emitContained(\"securityWarning\", warning);\n process.emitWarning(message, { code: MLLP_TLS_VERIFY_DISABLED });\n }\n\n /**\n * Wire a Connection's events through to this MllpClient. Every re-emitted\n * payload is `Object.freeze`'d before emission, even though the\n * Connection layer already freezes, defense-in-depth, harmless on\n * already-frozen objects.\n *\n * Builds the unified `Correlator` bound to this Connection and\n * arms the periodic ACK-timeout sweep. The Correlator is teardown-aware:\n * `close()` / `destroy()` clear the sweep timer and reject pending sends.\n *\n * @param conn - Connection to subscribe to.\n */\n private _attachConnection(conn: Connection): void {\n // Plan 04, preserve correlator state across reconnect cycles. In\n // controlId mode, in-flight sends are re-transmitted on the new\n // connection (D-08 / CLIENT-17), so the correlator must survive the\n // transition. The closures below dereference `this._connection`\n // lazily so they always see the CURRENT connection (not the dead\n // one captured at attach-time).\n if (this._correlator === null) {\n this._correlator = new Correlator({\n mode: this._correlateByControlId ? \"controlId\" : \"fifo\",\n ackTimeoutMs: this._ackTimeoutMs,\n // Plan 05, pipeline:false collapses the in-flight set to ≤1 (D-06).\n maxInFlight: this._pipeline ? Number.POSITIVE_INFINITY : 1,\n onWarning: (code, ctx) => {\n // PLAN-06 (OBS-01, D-26), aggregate Correlator-emitted warning counts.\n this._aggregatedWarningsByCode[code] = (this._aggregatedWarningsByCode[code] ?? 0) + 1;\n // The message is a REGISTRY LOOKUP, not an interpolation. Everything\n // input-derived is a number on its own field. See ack-diagnostics.ts.\n const warning: AckCorrelationWarning = Object.freeze({\n code,\n byteOffset: ctx.byteOffset,\n message: ackDiagnosticMessage(code),\n controlIdBytes: ctx.controlIdBytes,\n elapsedSinceSendMs: ctx.elapsedSinceSendMs,\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n timestamp: new Date(),\n });\n this._emitContained(\"warning\", warning);\n },\n onUnmatchedAck: (controlIdBytes) => {\n // CLIENT-15: unmatched ACK in controlId mode. Emit a frozen\n // MllpFramingError('MLLP_ACK_UNMATCHED_CONTROL_ID') to the 'error'\n // event. listenerCount-guarded so absent listeners don't crash the\n // process (T-05-03-02 mitigation).\n if (this.listenerCount(\"error\") === 0) return;\n const err = new MllpFramingError(\n \"MLLP_ACK_UNMATCHED_CONTROL_ID\",\n 0,\n Buffer.alloc(0),\n ackDiagnosticMessage(\"MLLP_ACK_UNMATCHED_CONTROL_ID\"),\n );\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n error: err,\n // The peer's MSA-2 byte LENGTH. The bytes themselves used to be\n // here and on the message above; a peer sending a one-megabyte\n // MSA-2 produced a one-megabyte Error.message, straight into a log.\n controlIdBytes,\n }),\n );\n },\n onTimeout: (entry, elapsedMs) => {\n // PLAN-06 (OBS-01, D-26), observability counter.\n this._timedOutTotal += 1;\n entry.reject(\n new MllpTimeoutError(`ACK timeout after ${elapsedMs}ms`, {\n messageControlIdBytes: entry.controlId?.length,\n elapsedMs,\n sentAt: entry.sentAt ?? 0,\n }),\n );\n // Plan 05, a timeout removes the entry from the live store too,\n // so emit 'drain' if the queue now sits below both caps. This is\n // critical for pipeline:false (D-06): an expired send must free\n // the in-flight slot so the next send can flush.\n this._maybeEmitDrain();\n },\n });\n }\n\n // Periodic sweep: smaller of (ackTimeoutMs / 4) and 1000 ms; floor 50 ms.\n // .unref() so this timer never keeps the process alive.\n if (this._ackSweepTimer === null) {\n const sweepIntervalMs = Math.max(50, Math.min(1000, Math.floor(this._ackTimeoutMs / 4)));\n this._ackSweepTimer = setInterval(() => {\n this._correlator?.expireDue();\n }, sweepIntervalMs);\n this._ackSweepTimer.unref();\n }\n\n // Single 'stateChange' listener delegates to _onStateChange (B-04 anchor).\n conn.on(\"stateChange\", (e: StateChangeEvent) => {\n this._onStateChange(e);\n });\n // Single 'message' listener: re-emit + delegate to _onAckPayload (B-04 anchor).\n conn.on(\n \"message\",\n (e: {\n payload: Buffer;\n connectionId: string;\n byteOffset: number;\n warnings: readonly MllpWarning[];\n }) => {\n // Contained: a throwing 'message' subscriber used to abort this handler BEFORE\n // _onAckPayload ran, so the ACK never reached the correlator and send() hung forever.\n // An observer must never be able to break ACK correlation.\n this._emitContained(\"message\", Object.freeze({ ...e }));\n // Plan 05, last-bytes-received signal resets dead-peer timer\n // (D-11 \"last bytes/ACK received\").\n this._armDeadPeerTimer();\n this._onAckPayload(e.payload, e.byteOffset);\n },\n );\n // PLAN-01 lifecycle re-emitters preserved unchanged.\n conn.on(\"connect\", (e: unknown) => {\n this._emitContained(\"connect\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"disconnect\", (e: unknown) => {\n this._emitContained(\"disconnect\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"reconnecting\", (e: unknown) => {\n this._emitContained(\"reconnecting\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"close\", (e: unknown) => {\n this._emitContained(\"close\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"warning\", (w: MllpWarning) => {\n this._emitContained(\"warning\", w);\n // Plan 05, Connection 'warning' is also a \"bytes received\" signal.\n this._armDeadPeerTimer();\n });\n conn.on(\"error\", (e: unknown) => {\n // Plan 04: capture the underlying Error for `RetryContext.lastError`.\n // The Connection emits frozen `{ connectionId, error: MllpConnectionError }`\n // payloads. We unwrap to the original transport error so the CLIENT-18\n // classifier (which inspects `err.code`) receives the OS-level code.\n const wrapper = e instanceof Error ? e : (e as { error?: unknown })?.error;\n if (wrapper instanceof Error) {\n // If the wrapper has a `.cause` Error (MllpConnectionError pattern),\n // prefer the inner cause for classification.\n const inner = (wrapper as { cause?: unknown }).cause;\n this._lastError = inner instanceof Error ? inner : wrapper;\n }\n // Guards the unlistened case (ERR_UNHANDLED_ERROR would crash the process, T-05-01-03)\n // AND contains a throwing 'error' subscriber. See src/internal/safe-emit.ts.\n safeEmitError(this, e);\n });\n this._connection = conn;\n\n // Plan 05, dead-peer timer self-listener on 'ack'. Connection emits\n // 'message' (already wired above); the MllpClient itself emits 'ack'\n // after matchAck succeeds. Both are \"last bytes/ACK received\" signals\n // (D-11). The 'ack' reset is effectively a no-op when 'message' just\n // armed it, but keeps the contract literal-true.\n if (!this._ackResetWired) {\n this._ackResetWired = true;\n this.on(\"ack\", () => {\n this._armDeadPeerTimer();\n });\n }\n\n // Arm the dead-peer timer if the connection is ALREADY in CONNECTED\n // (test seam path: _attachExistingConnection called after notifyConnect).\n // The state-change branch arms it for the normal path\n // (CONNECTING → CONNECTED transition).\n if (conn.state === \"CONNECTED\") {\n this._armDeadPeerTimer();\n }\n }\n\n /**\n * Arm (or re-arm) the dead-peer idle timer.\n * No-op when `deadPeerTimeoutMs` is unset.\n */\n private _armDeadPeerTimer(): void {\n if (this._opts.deadPeerTimeoutMs === undefined) return;\n if (this._deadPeerTimer !== null) {\n clearTimeout(this._deadPeerTimer);\n }\n this._deadPeerTimer = setTimeout(() => {\n this._connection?.destroy(new Error(\"dead peer timeout\"));\n }, this._opts.deadPeerTimeoutMs);\n this._deadPeerTimer.unref();\n }\n\n /**\n * Clear the dead-peer idle timer on every transition out of CONNECTED.\n */\n private _clearDeadPeerTimer(): void {\n if (this._deadPeerTimer !== null) {\n clearTimeout(this._deadPeerTimer);\n this._deadPeerTimer = null;\n }\n }\n\n /**\n * Disconnect handler, reconnect FSM core.\n *\n * Implements:\n * - Hybrid in-flight handling: controlId mode preserves\n * pending sends for resend; FIFO mode rejects in-flight with\n * `connectionCause: 'in-flight-orphan'` and queued with `'fifo-unsafe'`.\n * - Classifier-first: permanent errors\n * transition straight to CLOSED without invoking `retryStrategy`.\n * - Backoff-reset on recent success: first disconnect after a\n * successful ACK on the prior session resets `_attempt` to 0.\n * - `_reconnectAttempts` counter increment.\n * - Frozen RetryContext; a `null` return from the strategy halts reconnection.\n * - Default exponential strategy.\n *\n * Invoked from the SINGLE `_onStateChange` hook, no parallel listener.\n * Idempotent across same-cycle re-entry: cycle-start flag\n * coordinates first-disconnect vs subsequent-within-cycle behavior.\n */\n private _handleDisconnect(err: Error): void {\n if (this._userClosed) return;\n\n // CLIENT-17 hybrid: handle queued + in-flight sends per mode.\n if (this._correlator !== null) {\n if (this._correlateByControlId) {\n // Hold sends for resend after reconnect, DO NOT clear or reject.\n // The correlator's live store survives the FSM transition; the\n // entries are re-transmitted in `_beginReconnectAttempt` once the\n // new Connection enters CONNECTED.\n } else {\n // FIFO: split between in-flight (sentAt set) and queued (sentAt null).\n // In-flight sends → 'in-flight-orphan'; queued sends → 'fifo-unsafe'.\n const orphans: PendingAck[] = [];\n const queued: PendingAck[] = [];\n for (const entry of this._correlator.liveEntries()) {\n if (entry.sentAt !== null) orphans.push(entry);\n else queued.push(entry);\n }\n for (const o of orphans) {\n o.reject(\n new MllpConnectionError(\"in-flight send orphaned by reconnect\", {\n cause: err,\n phase: \"reconnect\",\n connectionCause: \"in-flight-orphan\",\n }),\n );\n }\n for (const q of queued) {\n q.reject(\n new MllpConnectionError(\"queued send rejected by FIFO reconnect\", {\n cause: err,\n phase: \"reconnect\",\n connectionCause: \"fifo-unsafe\",\n }),\n );\n }\n for (const entry of [...orphans, ...queued]) {\n this._correlator.remove(entry.key);\n }\n }\n }\n\n // CLIENT-18 classification first (Composition A, D-16). Permanent\n // errors transition directly to CLOSED without invoking retryStrategy.\n // Phase 8: on a TLS-configured connection, TLS-protocol-shaped errors\n // (ERR_SSL_*, EPROTO, OpenSSL alert-bearing, see isTlsProtocolError)\n // are ALSO permanent: a clientAuth 'MUST' server that rejects this\n // client's certificate will reject every retry, never reconnect-loop\n // into it. Pure TCP-level errors (ECONNREFUSED, ETIMEDOUT, plain\n // ECONNRESET) stay transient so a network blip still auto-heals.\n const tlsProtocolShaped = this._opts.tls !== undefined && isTlsProtocolError(err);\n const classifiedAs: \"transient\" | \"permanent\" =\n !tlsProtocolShaped && isTransientConnectionError(err) ? \"transient\" : \"permanent\";\n if (classifiedAs === \"permanent\") {\n // Halt: force the dead Connection to CLOSED (terminal); future\n // connect() must be called explicitly.\n this._userClosed = true;\n this._connection?.destroy(err);\n return;\n }\n\n // W-02: bump the global reconnect-attempts counter once per disconnect\n // entering a cycle. PLAN-06 reads this for getStats().reconnectAttempts.\n this._reconnectAttempts += 1;\n\n // W-01: backoff reset on recent success.\n // First disconnect AFTER any successful ACK on the prior session\n // (`_reconnectCycleStartedAt === null` AND `_lastSuccessAt !== null`)\n // resets attempt to 0. Subsequent disconnects within the same cycle\n // do NOT re-reset, the cycle-start flag persists.\n if (this._reconnectCycleStartedAt === null && this._lastSuccessAt !== null) {\n this._attempt = 0;\n }\n if (this._reconnectCycleStartedAt === null) {\n this._reconnectCycleStartedAt = Date.now();\n }\n\n // Build RetryContext (W-07: NEVER_ABORTING_SIGNAL when no caller signal).\n const ctx: RetryContext = Object.freeze({\n attempt: this._attempt,\n lastError: err,\n lastDelayMs: this._lastDelayMs,\n totalElapsedMs: Date.now() - this._reconnectCycleStartedAt,\n sinceLastSuccessMs:\n this._lastSuccessAt !== null ? Date.now() - this._lastSuccessAt : Number.POSITIVE_INFINITY,\n classifiedAs,\n signal: this._connectSignal ?? NEVER_ABORTING_SIGNAL,\n });\n\n // Invoke strategy (T-05-04-05: defensive try/catch, caller-supplied hook).\n let delay: number | null;\n try {\n const strategy = this._retryStrategy ?? this._defaultRetryStrategy;\n delay = strategy(ctx);\n } catch (hookErr) {\n // Strategy threw, bail to CLOSED, surface error.\n this._lastError = hookErr instanceof Error ? hookErr : new Error(String(hookErr));\n {\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? \"<none>\",\n error: this._lastError,\n }),\n );\n }\n this._userClosed = true;\n this._connection?.destroy(this._lastError);\n return;\n }\n\n if (delay === null) {\n // D-17 null-return halt → CLOSED.\n this._userClosed = true;\n this._connection?.destroy(err);\n return;\n }\n\n // Emit 'reconnecting' with populated fields (Phase 3 D-CR-01 promise).\n //\n // Contained: a throwing subscriber here used to skip the backoff scheduling immediately below,\n // so auto-reconnect silently never fired again, the connection just stopped retrying, with no\n // error that named the cause.\n this._emitContained(\n \"reconnecting\",\n Object.freeze({\n connectionId: this._connection?.connectionId ?? \"<none>\",\n attempt: this._attempt,\n delayMs: delay,\n }),\n );\n\n // Schedule next CONNECTING attempt. .unref() so the timer never keeps\n // the process alive (test-suite ergonomics).\n this._lastDelayMs = delay;\n this._backoffTimer = setTimeout(() => {\n this._backoffTimer = null;\n this._attempt += 1;\n this._beginReconnectAttempt();\n }, delay);\n this._backoffTimer.unref();\n }\n\n /**\n * Default retry strategy: `min(maxDelay, initialDelay * multiplier^attempt)`\n * with ±jitter applied.\n */\n private _defaultRetryStrategy = (ctx: RetryContext): number => {\n const base = Math.min(\n this._maxDelayMs,\n this._initialDelayMs * Math.pow(this._multiplier, ctx.attempt),\n );\n const jitterFactor = 1 + (Math.random() * 2 - 1) * this._jitter;\n return Math.max(0, Math.floor(base * jitterFactor));\n };\n\n /**\n * Open a fresh Connection for the next reconnect attempt.\n *\n * In production, builds a new `net.Socket` + `NetTransport` + `Connection`.\n * In tests, the `_reconnectFactory` seam returns a pre-built Connection\n * over `InMemoryTransport.pair()`.\n *\n * On successful CONNECTED transition:\n * - controlId mode: re-transmits every live correlator entry via the\n * already-encoded `PendingAck.frame`, then `markFlushed`'s each one\n * so ACK timeouts restart from the new flush time.\n * - All modes: clears the cycle-start flag so the next disconnect\n * can re-enter `_handleDisconnect` cleanly.\n */\n private _beginReconnectAttempt(): void {\n if (this._userClosed) return;\n try {\n let conn: Connection;\n let arm: () => void;\n if (this._reconnectFactory !== null) {\n ({ conn, arm } = this._reconnectFactory());\n } else {\n const { socket, transport } = this._createSocketAndTransport();\n // Plan 05, TCP keepalive on every reconnect attempt too\n // (CLIENT-08, D-11/A3, mirror connect() site).\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n this._socket = socket;\n const connOpts =\n this._opts.framing !== undefined\n ? { transport, framing: this._opts.framing }\n : { transport };\n if (this._opts.drainTimeoutMs !== undefined) {\n (connOpts as { drainTimeoutMs?: number }).drainTimeoutMs = this._opts.drainTimeoutMs;\n }\n conn = new Connection(connOpts);\n arm = (): void => {\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n this._emitInsecureWarningIfNeeded();\n };\n const connectEventName = this._opts.tls !== undefined ? \"secureConnect\" : \"connect\";\n socket.once(\"error\", (sErr: Error) => {\n // Phase 8, the raw OS/TLS error (with its original `.code`) is\n // what the reconnect classifier inspects in _handleDisconnect;\n // keep it unwrapped here so TLS cert-verification codes (CERT_*,\n // UNABLE_TO_VERIFY_LEAF_SIGNATURE, …) and TLS-protocol-shaped\n // codes (ERR_SSL_*, EPROTO with an SSL alert) are still visible\n // to the classifier and correctly permanent (never reconnect-loop\n // into a misconfigured or MITM'd endpoint).\n if (this._lastError === null || this._lastError.message !== sErr.message) {\n this._lastError = sErr;\n }\n });\n socket.once(connectEventName, () => {\n // TLS and plaintext arm identically and immediately, see the\n // matching note in connect()'s onSocketConnect.\n arm();\n this._afterReconnectArmed();\n });\n // Replace prior Connection. Drop the dead reference; new Connection\n // is wired below.\n this._connection = null;\n this._attachConnection(conn);\n return;\n }\n // Test-seam path\n this._connection = null;\n this._attachConnection(conn);\n arm();\n this._afterReconnectArmed();\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err));\n this._handleDisconnect(this._lastError);\n }\n }\n\n /**\n * Post-CONNECTED steps for a reconnect attempt:\n * - controlId mode: resend every live correlator entry's frame, then\n * re-stamp `markFlushed` so ACK timers reset from the new flush.\n * - Clear the cycle-start flag so the next disconnect re-enters cleanly.\n */\n private _afterReconnectArmed(): void {\n if (this._correlateByControlId && this._correlator !== null && this._connection !== null) {\n const conn = this._connection;\n const corr = this._correlator;\n const entries = [...corr.liveEntries()];\n for (const entry of entries) conn.send(entry.frame);\n const now = Date.now();\n for (const entry of entries) corr.markFlushed(entry.key, now);\n }\n this._reconnectCycleStartedAt = null;\n }\n\n /**\n * Test seam, install a factory that produces the next reconnect Connection.\n *\n * @internal\n */\n _setReconnectFactory(factory: () => { conn: Connection; arm: () => void }): void {\n this._reconnectFactory = factory;\n }\n\n /**\n * Test seam, capture or rebind the connect-signal mid-flight.\n *\n * @internal\n */\n _captureConnectSignal(signal: AbortSignal): void {\n this._connectSignal = signal;\n if (this._abortListener !== null) {\n this._abortListener.signal.removeEventListener(\"abort\", this._abortListener.handler);\n this._abortListener = null;\n }\n const handler = (): void => {\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n this._connection?.destroy(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", handler, { once: true });\n this._abortListener = { signal, handler };\n }\n\n /**\n * Single source-of-truth for inbound ACK payload handling.\n *\n * - FIFO mode: passes `null` controlId so `matchAck` returns the head of\n * the live store.\n * - controlId mode: extracts MSA-2 and passes it to `matchAck` for keyed\n * lookup.\n *\n * Called from the SINGLE `'message'` listener registered in `_attachConnection`.\n * No parallel listener is registered.\n */\n private _onAckPayload(ackPayload: Buffer, byteOffset: number): void {\n if (this._correlator === null) return;\n // HOOK_EXTENSION_POINT: ack-payload\n const ackControlId: string | null = this._correlateByControlId\n ? extractMsaControlId(ackPayload)\n : null;\n const matched = this._correlator.matchAck(ackPayload, ackControlId, byteOffset);\n if (matched !== null) {\n this._onAckMatched(matched, ackPayload);\n }\n }\n\n /**\n * Single source-of-truth for a successfully matched ACK (live-store hit).\n *\n * Emits the frozen 'ack' event and resolves the pending send. Updates\n * `_lastSuccessAt`, `_ackedTotal` and `_lastAckAt`.\n *\n * Called from _onAckPayload when matchAck() returns a non-null PendingAck.\n */\n private _onAckMatched(matched: PendingAck, ackPayload: Buffer): void {\n const latencyMs = matched.sentAt !== null ? Date.now() - matched.sentAt : 0;\n this._emitContained(\n \"ack\",\n Object.freeze({\n payload: ackPayload,\n controlId: matched.controlId,\n latencyMs,\n }),\n );\n // HOOK_EXTENSION_POINT: ack-matched\n // Plan 04, backoff-reset signal (W-01): record the most recent successful\n // ACK so the next disconnect resets attempt counter to 0 if it's the\n // first disconnect AFTER a successful exchange on the prior session.\n this._lastSuccessAt = Date.now();\n // PLAN-06 (OBS-01, D-26), observability counters.\n this._ackedTotal += 1;\n this._lastAckAt = Date.now();\n matched.resolve(ackPayload);\n // Plan 05, emit 'drain' when queue depth crosses below high-water mark\n // (D-24). Fires once per ACK that brings the queue under both caps.\n this._maybeEmitDrain();\n }\n\n /**\n * Emit a frozen `'drain'` event when the queue depth and bytes fall below\n * both configured caps. Called from `_onAckMatched`\n * (every successful ACK) and from the Correlator's `onTimeout` callback\n * (every expired send), both code paths free a live-store slot.\n */\n private _maybeEmitDrain(): void {\n const corr = this._correlator;\n if (corr === null) return;\n const belowCount = corr.size < this._hwmCount;\n const belowBytes = corr.queueBytes < this._hwmBytes;\n if (belowCount && belowBytes) {\n this._emitContained(\n \"drain\",\n Object.freeze({\n queueDepth: corr.size,\n queueBytes: corr.queueBytes,\n }),\n );\n }\n }\n\n /**\n * Single source-of-truth for Connection FSM transitions.\n *\n * Re-emits a frozen 'stateChange' event, detects\n * CONNECTED → DISCONNECTED|RECONNECTING to trigger `_handleDisconnect`, and\n * clears or arms the dead-peer timer on transitions out of / into CONNECTED.\n *\n * Called from the SINGLE 'stateChange' listener registered in _attachConnection.\n */\n private _onStateChange(e: StateChangeEvent): void {\n // Contained: everything below (dead-peer timer, the _handleDisconnect reconnect trigger) must\n // run even if a subscriber throws. A throwing 'stateChange' tap used to silently disable\n // auto-reconnect.\n this._emitContained(\"stateChange\", Object.freeze({ ...e }));\n // HOOK_EXTENSION_POINT: state-change\n // Plan 05, dead-peer timer arm/clear (D-14). Cleared on every\n // transition OUT of CONNECTED; re-armed on entry TO CONNECTED.\n if (e.to === \"CONNECTED\") {\n this._armDeadPeerTimer();\n // PLAN-06 (OBS-01, D-26), record CONNECTED epoch for getStats.\n this._lastConnectedAt = Date.now();\n }\n if (e.from === \"CONNECTED\" && e.to !== \"CONNECTED\") {\n this._clearDeadPeerTimer();\n }\n // Plan 04, disconnect detection (CLIENT-05/06/17). Trigger\n // `_handleDisconnect` on transitions out of CONNECTED into a\n // disconnect-leaning state, OR on a CONNECTING/RECONNECTING attempt\n // failing into CLOSED while we are inside a reconnect cycle (so the\n // cycle continues incrementing `_attempt`). The cycle-start flag plus\n // `_userClosed` guard against re-entry.\n const isPostConnectedDrop =\n e.from === \"CONNECTED\" &&\n (e.to === \"DISCONNECTED\" || e.to === \"RECONNECTING\" || e.to === \"CLOSED\");\n const isReconnectAttemptFailure =\n this._reconnectCycleStartedAt !== null &&\n (e.from === \"CONNECTING\" || e.from === \"RECONNECTING\") &&\n (e.to === \"CLOSED\" || e.to === \"DISCONNECTED\");\n if (isPostConnectedDrop || isReconnectAttemptFailure) {\n const cause = this._lastError ?? new Error(e.reason ?? \"disconnect\");\n if (this._userClosed) return;\n if (!this._autoReconnect) {\n // Reject pending sends, same teardown path as close() but with a\n // disconnect-flavored MllpConnectionError so callers see the cause.\n this._teardownCorrelator(\n new MllpConnectionError(\"disconnected; autoReconnect disabled\", {\n cause,\n phase: \"send\",\n }),\n );\n return;\n }\n this._handleDisconnect(cause);\n }\n }\n\n /**\n * Send an MLLP-framed payload and await the inbound ACK.\n *\n * Resolves with the ACK Buffer (framing stripped). Rejects with:\n * - `DOMException('Aborted', 'AbortError')` if `signal` aborts before the ACK.\n * - `MllpTimeoutError` if no ACK arrives within `ackTimeoutMs`.\n * The clock starts at the underlying `write()` flush callback, NOT at\n * the `send()` call.\n * - `MllpConnectionError({ phase: 'send' })` if the client is not connected.\n *\n * Emits a frozen `'ack'` event on every successful match.\n *\n * @example\n * ```typescript\n * const ack = await client.send(payloadBuffer);\n * logger.info({ ack: ack.toString('utf8') });\n * ```\n *\n * @param payload Raw bytes; MLLP framing is added internally via `encodeFrame`.\n * @param opts.signal AbortSignal, aborting cancels the ACK wait.\n */\n send(payload: Buffer, opts?: { signal?: AbortSignal; ackTimeoutMs?: number }): Promise<Buffer> {\n const signal = opts?.signal;\n if (signal?.aborted === true) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n if (\n this._connection === null ||\n this._correlator === null ||\n this._connection.state !== \"CONNECTED\"\n ) {\n return Promise.reject(\n new MllpConnectionError(\"send before connect\", {\n cause: new Error(`client state is ${this.state}`),\n phase: \"send\",\n }),\n );\n }\n // controlId mode: extract MSH-10 BEFORE enqueue so the live-store key\n // is the same string the peer will echo back as MSA-2.\n const controlId: string | null = this._correlateByControlId\n ? extractMshControlId(payload)\n : null;\n const correlator = this._correlator;\n const conn = this._connection;\n // Frame once at enqueue time, same bytes go to the wire AND get held\n // for Plan 04 reconnect-resend (D-08 / CLIENT-17 controlId branch).\n const frame = encodeFrame(payload);\n\n // Plan 05, backpressure gate (CLIENT-07, D-23). Runs BEFORE enqueue\n // so a rejected send never touches the live store. The gate measures\n // the current `correlator.size` + `queueBytes` against the configured\n // high-water mark and applies the configured policy.\n const newQueueDepth = correlator.size + 1;\n const newQueueBytes = correlator.queueBytes + frame.length;\n const overCount = newQueueDepth > this._hwmCount;\n const overBytes = newQueueBytes > this._hwmBytes;\n if (overCount || overBytes) {\n const hwmDesc: { count?: number; bytes?: number } = {};\n if (this._hwmCount !== Number.POSITIVE_INFINITY) {\n hwmDesc.count = this._hwmCount;\n }\n if (this._hwmBytes !== Number.POSITIVE_INFINITY) {\n hwmDesc.bytes = this._hwmBytes;\n }\n if (this._onBackpressure === \"reject\") {\n return Promise.reject(\n new MllpBackpressureError(\n `queue at high-water mark (depth=${correlator.size}, bytes=${correlator.queueBytes})`,\n {\n queueDepth: correlator.size,\n queueBytes: correlator.queueBytes,\n highWaterMark: hwmDesc,\n },\n ),\n );\n }\n // 'wait' mode (CLIENT-07/CLIENT-11): defer until 'drain' fires OR\n // ackTimeoutMs elapses OR the caller's signal aborts (B-06).\n return this._waitThenSend(payload, opts);\n }\n\n return new Promise<Buffer>((resolve, reject) => {\n let abortListener: (() => void) | null = null;\n const wrappedResolve = (ack: Buffer): void => {\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n resolve(ack);\n };\n const wrappedReject = (err: Error): void => {\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n reject(err);\n };\n const key = correlator.enqueue(frame, controlId, wrappedResolve, wrappedReject);\n if (key === null) {\n // pipeline:false (Plan 05, D-06). Correlator's maxInFlight=1 is\n // saturated. Wait for the next 'drain' event (the prior ACK\n // releases the slot) and then re-enter `send()`, the high-water\n // mark gate above has already approved this send.\n const onDrain = (): void => {\n this.off(\"drain\", onDrain);\n this.send(payload, opts).then(wrappedResolve, wrappedReject);\n };\n this.on(\"drain\", onDrain);\n if (signal !== undefined) {\n abortListener = (): void => {\n this.off(\"drain\", onDrain);\n wrappedReject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n return;\n }\n if (signal !== undefined) {\n abortListener = (): void => {\n correlator.remove(key);\n wrappedReject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n // Connection.send returns boolean; `false` indicates socket-level\n // backpressure (the OS still buffers the bytes). The application-level\n // high-water mark is what the gate above enforces.\n conn.send(frame);\n correlator.markFlushed(key, Date.now());\n // PLAN-06 (OBS-01, D-26), count flushed sends. Synchronous post-send\n // increment per T-05-06-05 (counter race is bounded; observability is\n // \"good enough\" per D-26).\n this._sentTotal += 1;\n });\n }\n\n /**\n * 'wait'-mode backpressure handler.\n *\n * Awaits one of three terminating signals, in order:\n * - `'drain'` event → re-enter `send()` (the gate will now pass).\n * - `ackTimeoutMs` elapses → reject with `MllpTimeoutError`.\n * - Caller's `signal` aborts → reject with `AbortError`. Cleanup\n * removes the drain listener AND the abort listener AND clears the\n * timer to prevent leaks.\n */\n private _waitThenSend(\n payload: Buffer,\n opts?: { signal?: AbortSignal; ackTimeoutMs?: number },\n ): Promise<Buffer> {\n const signal = opts?.signal;\n return new Promise<Buffer>((resolve, reject) => {\n const ackTimeoutMs = opts?.ackTimeoutMs ?? this._ackTimeoutMs;\n let abortListener: (() => void) | null = null;\n const cleanup = (): void => {\n this.off(\"drain\", onDrain);\n clearTimeout(timer);\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n };\n const onDrain = (): void => {\n cleanup();\n // Re-enter send(): the gate will now pass because the queue\n // shrank. Forward both branches to our promise.\n this.send(payload, opts).then(resolve, reject);\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new MllpTimeoutError(`waiting for drain timed out after ${ackTimeoutMs}ms`, {\n messageControlIdBytes: undefined,\n elapsedMs: ackTimeoutMs,\n sentAt: Date.now(),\n }),\n );\n }, ackTimeoutMs);\n timer.unref();\n this.on(\"drain\", onDrain);\n if (signal !== undefined) {\n // B-06: 'wait' mode MUST honor signal abort mid-wait. Cleanup\n // removes the drain listener so listenerCount('drain') returns to\n // its pre-send baseline, the timer is cleared, and the abort\n // listener removes itself.\n abortListener = (): void => {\n cleanup();\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n });\n }\n\n /**\n * Tear down per-connection state (sweep timer + correlator) when the\n * client is closing or destroying. Safe to call multiple times.\n */\n private _teardownCorrelator(reason: Error): void {\n if (this._ackSweepTimer !== null) {\n clearInterval(this._ackSweepTimer);\n this._ackSweepTimer = null;\n }\n if (this._correlator !== null) {\n this._correlator.clear(reason);\n // Drop the reference so subsequent send() calls reject via the\n // _connection / _correlator null check.\n this._correlator = null;\n }\n // Plan 05, dead-peer timer cleanup. Belt-and-suspenders for the\n // destroy() path that may bypass an explicit FSM transition.\n this._clearDeadPeerTimer();\n }\n\n /**\n * **Test seam**, attach an externally-built {@link Connection} directly,\n * bypassing the `net.createConnection` + `NetTransport` path. Used by\n * lifecycle tests driving `InMemoryTransport.pair()` for determinism.\n *\n * @internal\n */\n _attachExistingConnection(conn: Connection): void {\n if (this._connection !== null) {\n throw new MllpConnectionError(\"connection already attached\", {\n cause: new Error(\"attach twice\"),\n phase: \"connect\",\n });\n }\n this._attachConnection(conn);\n }\n\n /**\n * Gracefully close the client.\n *\n * Delegates to {@link Connection.close}, which transitions `CONNECTED → DRAINING\n * → DISCONNECTED` (or `CLOSED` on drain timeout). No-op if no Connection is\n * attached.\n *\n * Rejects with `DOMException('Aborted', 'AbortError')` if `signal` aborts mid-drain;\n * on abort, the underlying Connection is force-destroyed.\n *\n * @example\n * ```typescript\n * await client.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number; signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Plan 04: suppress reconnect for the rest of this client's lifetime.\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n\n const conn = this._connection;\n if (conn === null) {\n // No connection attached; still tear down any stray correlator state\n // (defensive, this branch is unreachable in normal flow).\n this._teardownCorrelator(\n new MllpConnectionError(\"client closed\", {\n cause: new Error(\"closed\"),\n phase: \"close\",\n }),\n );\n return;\n }\n\n // Reject pending sends BEFORE delegating to Connection.close so callers\n // observe the rejection promptly rather than waiting for the drain.\n this._teardownCorrelator(\n new MllpConnectionError(\"client closed\", {\n cause: new Error(\"closed\"),\n phase: \"close\",\n }),\n );\n\n if (signal === undefined) {\n const closeOpts =\n opts?.drainTimeoutMs !== undefined ? { drainTimeoutMs: opts.drainTimeoutMs } : undefined;\n await conn.close(closeOpts);\n return;\n }\n\n // Wire AbortSignal, abort during drain force-destroys the Connection\n let abortHandler: (() => void) | undefined;\n const abortPromise = new Promise<never>((_resolve, reject) => {\n abortHandler = (): void => {\n conn.destroy(new Error(\"aborted\"));\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n });\n\n try {\n const closeOpts =\n opts?.drainTimeoutMs !== undefined ? { drainTimeoutMs: opts.drainTimeoutMs } : undefined;\n await Promise.race([conn.close(closeOpts), abortPromise]);\n } finally {\n if (abortHandler !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n }\n\n /**\n * Abruptly destroy the client, force-transitions the underlying Connection\n * to `CLOSED` immediately. No-op if no Connection is attached. Idempotent.\n *\n * @example\n * ```typescript\n * client.destroy(new Error('shutting down'));\n * ```\n */\n destroy(reason?: Error): void {\n // Plan 04: suppress reconnect for the rest of this client's lifetime.\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n const teardownReason =\n reason ??\n new MllpConnectionError(\"client destroyed\", {\n cause: new Error(\"destroyed\"),\n phase: \"close\",\n });\n this._teardownCorrelator(teardownReason);\n const conn = this._connection;\n if (conn === null) return;\n conn.destroy(reason);\n }\n\n /**\n * Returns a JSON-serializable observability snapshot.\n *\n * All fields are plain values, no Buffers, no class instances, no Maps,\n * no circular refs. Safe to `JSON.stringify` directly.\n *\n * `inFlight` is the count of correlator entries with `sentAt !== null`\n * (entries actually written to the wire and awaiting ACK), distinct from\n * `queueDepth` which counts ALL live correlator entries (including\n * pre-flush and serialization-queued sends).\n *\n * @example\n * ```typescript\n * setInterval(() => logger.info(JSON.stringify(client.getStats())), 60_000);\n * ```\n */\n getStats(): ClientStats {\n const connStats = this._connection?.getStats();\n const corrStats = this._correlator?.getStats();\n // Merge warningsByCode: Connection-level + Client-aggregated (Correlator-emitted).\n const merged: Partial<Record<WarningCode, number>> = {\n ...this._aggregatedWarningsByCode,\n };\n if (connStats !== undefined) {\n for (const [k, v] of Object.entries(connStats.warningsByCode)) {\n const code = k as WarningCode;\n merged[code] = (merged[code] ?? 0) + v;\n }\n }\n return {\n state: this.state,\n connectionId: connStats?.connectionId ?? null,\n queueDepth: corrStats?.size ?? 0,\n queueBytes: corrStats?.queueBytes ?? 0,\n inFlight: corrStats?.inFlight ?? 0,\n warningsByCode: merged,\n totalBytesIn: connStats?.bytesIn ?? 0,\n totalBytesOut: connStats?.bytesOut ?? 0,\n sentTotal: this._sentTotal,\n ackedTotal: this._ackedTotal,\n timedOutTotal: this._timedOutTotal,\n reconnectAttempts: this._reconnectAttempts,\n lastConnectedAt: this._lastConnectedAt,\n lastAckAt: this._lastAckAt,\n tls: this._opts.tls !== undefined,\n };\n }\n\n /**\n * Async disposal, delegates to {@link MllpClient.close} for `await using` support.\n *\n * @example\n * ```typescript\n * await using client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * // client.close() is called automatically at end of block\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n return this.close();\n }\n}\n\n/**\n * Create an {@link MllpClient}. Equivalent to `new MllpClient(opts)`.\n *\n * @example\n * ```typescript\n * import { createClient } from '@cosyte/mllp';\n *\n * const client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n */\nexport function createClient(opts: ClientOptions): MllpClient {\n return new MllpClient(opts);\n}\n\n/**\n * Options for {@link createStarterClient}.\n *\n * The starter applies opinionated defaults on top of `ClientOptions`,\n * so every override here is optional except `host` + `port`. The\n * starter-specific addition is `handleSignals` (mirrors `createStarterServer`).\n *\n * @example\n * ```typescript\n * const opts: StarterClientOptions = {\n * host: 'localhost',\n * port: 2575,\n * onMessage: (payload) => logger.info({ bytes: payload.length }),\n * handleSignals: true,\n * };\n * ```\n */\nexport interface StarterClientOptions {\n /** Host to connect to. */\n readonly host: string;\n /** TCP port. */\n readonly port: number;\n /**\n * Inbound-message callback (any framed payload from the peer, including\n * non-ACK messages on bidirectional channels). Mirrors the server-side\n * `onMessage` ergonomics.\n */\n readonly onMessage?: (payload: Buffer) => void;\n /** Override default `30_000`. */\n readonly ackTimeoutMs?: number;\n /** Override default `false` (FIFO mode). */\n readonly correlateByControlId?: boolean;\n /** Override default `true` (parallel up to highWaterMark). */\n readonly pipeline?: boolean;\n /** Override default `64`. */\n readonly highWaterMark?: HighWaterMark;\n /** Override default `'reject'`. */\n readonly onBackpressure?: \"reject\" | \"wait\";\n /** Override default `true` (auto-reconnect on transient errors). */\n readonly autoReconnect?: boolean;\n /** Custom reconnect-backoff hook. */\n readonly retryStrategy?: RetryStrategy;\n /** Drain timeout for `close()` (default `30_000`). */\n readonly drainTimeoutMs?: number;\n /** FrameReader options (passthrough). */\n readonly framing?: ClientOptions[\"framing\"];\n /** TCP keepalive interval ms. */\n readonly keepaliveIntervalMs?: number;\n /** Application-idle dead-peer timeout ms. */\n readonly deadPeerTimeoutMs?: number;\n /** Enable TLS (MLLPS) for this connection. Passthrough to `ClientOptions.tls`. */\n readonly tls?: TlsOptions | true;\n /**\n * Register process SIGTERM/SIGINT handlers that close the client. Default\n * `false`. When `true`, SIGTERM/SIGINT both call `client.close()`\n * and exit the process. Handlers self-deregister on `'close'`.\n */\n readonly handleSignals?: boolean;\n}\n\n/**\n * Three-line MLLP client with batteries-included defaults. The returned\n * client is already CONNECTED, `connect()` has been awaited.\n *\n * Defaults:\n * - `autoReconnect: true`\n * - `ackTimeoutMs: 30_000`\n * - `correlateByControlId: false` (FIFO mode, simplest mental model)\n * - `pipeline: true`\n * - `highWaterMark: 64`\n * - `onBackpressure: 'reject'`\n * - `handleSignals: false` (opt-in)\n *\n * The factory is **async**, so the literal three-line north-star snippet\n * has an explicit `await` BEFORE `createStarterClient(...)`, without it,\n * the `using` declaration would receive a `Promise`, not an `MllpClient`,\n * and `Symbol.asyncDispose` would not run at scope exit.\n *\n * @example\n * ```typescript\n * import { createStarterClient } from '@cosyte/mllp';\n * await using c = await createStarterClient({ host: 'localhost', port: 2575 });\n * const ack = await c.send(payloadBuffer);\n * ```\n */\nexport async function createStarterClient(opts: StarterClientOptions): Promise<MllpClient> {\n // Build ClientOptions, applying D-22 defaults only for unset fields.\n const clientOpts: ClientOptions = {\n host: opts.host,\n port: opts.port,\n autoReconnect: opts.autoReconnect ?? true,\n ackTimeoutMs: opts.ackTimeoutMs ?? 30_000,\n correlateByControlId: opts.correlateByControlId ?? false,\n pipeline: opts.pipeline ?? true,\n highWaterMark: opts.highWaterMark ?? 64,\n onBackpressure: opts.onBackpressure ?? \"reject\",\n ...(opts.drainTimeoutMs !== undefined ? { drainTimeoutMs: opts.drainTimeoutMs } : {}),\n ...(opts.framing !== undefined ? { framing: opts.framing } : {}),\n ...(opts.retryStrategy !== undefined ? { retryStrategy: opts.retryStrategy } : {}),\n ...(opts.keepaliveIntervalMs !== undefined\n ? { keepaliveIntervalMs: opts.keepaliveIntervalMs }\n : {}),\n ...(opts.deadPeerTimeoutMs !== undefined ? { deadPeerTimeoutMs: opts.deadPeerTimeoutMs } : {}),\n ...(opts.tls !== undefined ? { tls: opts.tls } : {}),\n };\n const client = createClient(clientOpts);\n\n if (opts.onMessage !== undefined) {\n const handler = opts.onMessage;\n client.on(\"message\", (e: { payload: Buffer }) => {\n handler(e.payload);\n });\n }\n\n if (opts.handleSignals === true) {\n // T-05-06-01: handler self-deregisters on 'close' to avoid per-process\n // listener accumulation (mirror createStarterServer pattern).\n const sigHandler = (): void => {\n void client\n .close()\n .then(() => process.exit(0))\n .catch(() => process.exit(1));\n };\n process.once(\"SIGTERM\", sigHandler);\n process.once(\"SIGINT\", sigHandler);\n client.once(\"close\", () => {\n process.removeListener(\"SIGTERM\", sigHandler);\n process.removeListener(\"SIGINT\", sigHandler);\n });\n }\n\n await client.connect();\n return client;\n}\n","/**\n * `@cosyte/mllp`, Production-grade MLLP client and server for Node.js.\n *\n * Transport-only sibling to `@cosyte/hl7`. Handles framing, ACKs, reconnects,\n * backpressure, and TLS without requiring knowledge of the MLLP spec.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n * const server = await createStarterServer({ port: 2575, onMessage: (buf) => buf });\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Package version marker exported from the `@cosyte/mllp` root.\n *\n * Kept in lockstep with `package.json` by `scripts/sync-version.mjs`, which the `version` script\n * runs immediately after `changeset version`. The `: string` annotation is deliberate, without it\n * TypeScript infers the *literal* type (`declare const VERSION = \"0.0.0\"`), which leaks the current\n * release into consumers' types and makes an equality check against any other version a compile\n * error.\n *\n * @example\n * ```typescript\n * import { VERSION } from '@cosyte/mllp';\n * console.log(VERSION);\n * ```\n */\nexport const VERSION: string = \"0.0.10\";\n\n// Phase 2: framing codec public surface\nexport type { WarningCode, MllpWarning } from \"./framing/index.js\";\nexport { MllpFramingError, encodeFrame, FrameReader, createWarning } from \"./framing/index.js\";\nexport type { FrameReaderOptions, EncoderOptions } from \"./framing/index.js\";\n\n// Phase 3: transport abstraction, connection FSM, and observability\nexport type { Transport } from \"./transport/index.js\";\nexport { NetTransport } from \"./transport/index.js\";\n\n// Phase 8: TLS / MLLPS hardening\nexport { TlsTransport } from \"./transport/index.js\";\nexport type { TlsOptions, ServerTlsOptions, ClientAuth, PemInput } from \"./transport/index.js\";\nexport {\n MLLP_TLS_VERIFY_DISABLED,\n MLLP_BIND_ALL_INTERFACES,\n type SecurityWarning,\n type SecurityWarningCode,\n} from \"./transport/index.js\";\n\nexport {\n Connection,\n type ConnectionOptions,\n type ConnectionState,\n type ConnectionStats,\n type StateChangeEvent,\n type ReconnectingEvent,\n MllpConnectionError,\n type ConnectionErrorPhase,\n type ConnectionErrorCause,\n} from \"./connection/index.js\";\n\n// Phase 4: server\nexport {\n MllpServer,\n createServer,\n createStarterServer,\n type ServerOptions,\n type StarterServerOptions,\n type ServerStats,\n type MessageMeta,\n type NackEvent,\n type NackReason,\n} from \"./server/index.js\";\n\n// Phase 6: fail-safe ACK semantics & the commit contract\nexport {\n buildRawAck,\n rawAckUncorrelatable,\n resolveNackCode,\n MllpAckError,\n type AckCode,\n type NegativeAckCode,\n} from \"./server/index.js\";\n\n// Phase 5: client\nexport {\n MllpClient,\n createClient,\n createStarterClient,\n type ClientOptions,\n type ClientStats,\n type StarterClientOptions,\n type RetryContext,\n type RetryStrategy,\n MllpTimeoutError,\n MllpBackpressureError,\n isTransientConnectionError,\n isTlsVerificationErrorCode,\n isTlsProtocolError,\n ackDiagnosticMessage,\n type AckCorrelationCode,\n type AckCorrelationWarning,\n} from \"./client/index.js\";\n"]}
1
+ {"version":3,"sources":["../src/framing/registry.ts","../src/framing/error.ts","../src/framing/constants.ts","../src/framing/encoder.ts","../src/framing/decoder.ts","../src/transport/net-transport.ts","../src/transport/tls-transport.ts","../src/transport/security-warnings.ts","../src/transport/error.ts","../src/transport/dh-parameters.ts","../src/transport/tls-cipher-policy.ts","../src/transport/negotiated-tls.ts","../src/connection/error.ts","../src/connection/connection.ts","../src/internal/control-id.ts","../src/server/ack.ts","../src/internal/ack-mode.ts","../src/internal/ack-mode-diagnostics.ts","../src/internal/safe-emit.ts","../src/server/server.ts","../src/client/correlator.ts","../src/client/ack-diagnostics.ts","../src/client/error.ts","../src/client/client.ts","../src/differential/corpus.ts","../src/differential/error.ts","../src/differential/peer.ts","../src/differential/run.ts","../src/index.ts"],"names":["snippet","randomUUID","s","EventEmitter","netCreateServer","tlsCreateServer","socket","tlsConnect","createConnection"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * MLLP warning code registry and `MllpWarning` factory.\n *\n * `WarningCode` is a **stable public API**. Renaming or removing a code is a breaking change.\n * New codes may only be added in minor versions.\n *\n * @example\n * ```typescript\n * import { createWarning, type MllpWarning, type WarningCode } from '@cosyte/mllp';\n * const w: MllpWarning = createWarning('MLLP_EMPTY_PAYLOAD', 128, 'Empty payload between VT and FS');\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Union of all stable MLLP warning codes.\n *\n * These codes are a **public API**, they appear in `onWarning` handlers, log pipelines,\n * monitoring dashboards, and error messages. Renaming is a breaking change.\n *\n * @example\n * ```typescript\n * const code: WarningCode = 'MLLP_FS_WITHOUT_CR';\n * ```\n */\nexport type WarningCode =\n | \"MLLP_MISSING_LEADING_VT\"\n | \"MLLP_FS_WITHOUT_CR\"\n | \"MLLP_LF_AFTER_FS\"\n | \"MLLP_LEADING_WHITESPACE\"\n | \"MLLP_TRAILING_BYTES\"\n | \"MLLP_PAYLOAD_CONTAINS_VT\"\n | \"MLLP_PAYLOAD_CONTAINS_FS\"\n | \"MLLP_EMPTY_PAYLOAD\"\n | \"MLLP_FRAME_TOO_LARGE\"\n | \"MLLP_ACK_UNMATCHED_CONTROL_ID\"\n | \"MLLP_ACK_AFTER_TIMEOUT\";\n\n/**\n * A frozen warning object emitted when the decoder tolerates a framing deviation.\n *\n * `connectionId` is `undefined` when emitted by a standalone `FrameReader`.\n * `Connection` enriches it to the real UUIDv4 before forwarding upstream.\n *\n * @example\n * ```typescript\n * const reader = new FrameReader({\n * onFrame: (p) => process(p),\n * onWarning: (w: MllpWarning) => logger.warn(w),\n * allowFsOnly: true,\n * });\n * ```\n */\nexport interface MllpWarning {\n readonly code: WarningCode;\n /**\n * Stable human-readable description. It carries structural facts (a byte\n * offset, an accumulated size) and **never a run of payload content**.\n *\n * Two codes carry exactly one input byte, and it is worth knowing which:\n * `MLLP_MISSING_LEADING_VT` and `MLLP_FS_WITHOUT_CR` render the hex of the\n * single byte found where a framing byte was expected, and on a stream that\n * omits its leading `VT` that byte is the first byte of the unframed content.\n * One byte, never a run, the same bound `MllpFramingError.snippet` carries. If\n * that is more than your threat model allows, log `code` and `byteOffset`\n * rather than `message`.\n *\n * The ACK-correlation codes are stricter: their text comes from a frozen\n * registry and does not vary with the wire at all.\n */\n readonly message: string;\n /** Absolute stream byte offset where the anomaly was detected. */\n readonly byteOffset: number;\n /**\n * Connection identifier. `undefined` at the framing layer;\n * enriched by `Connection` before emitting upstream.\n */\n readonly connectionId: string | undefined;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n\n/**\n * Callback type for receiving MLLP framing warnings.\n *\n * @example\n * ```typescript\n * const onWarning: OnWarning = (w) => logger.warn({ code: w.code, offset: w.byteOffset });\n * ```\n */\nexport type OnWarning = (warning: MllpWarning) => void;\n\n/**\n * Create a frozen `MllpWarning` object. The returned object is `Object.freeze()`'d\n * so subscribers cannot mutate shared warning state.\n *\n * `connectionId` is always `undefined` here; `Connection` enriches it via\n * `{ ...w, connectionId: this.connectionId }` then re-freezes.\n *\n * @example\n * ```typescript\n * const w = createWarning('MLLP_EMPTY_PAYLOAD', 64, 'Empty payload between VT and FS');\n * // w.connectionId === undefined\n * ```\n */\nexport function createWarning(code: WarningCode, byteOffset: number, message: string): MllpWarning {\n return Object.freeze<MllpWarning>({\n code,\n message,\n byteOffset,\n connectionId: undefined,\n timestamp: new Date(),\n });\n}\n","/**\n * Typed framing error class for unrecoverable MLLP wire-format violations.\n *\n * @example\n * ```typescript\n * import { MllpFramingError } from '@cosyte/mllp';\n * try {\n * encodeFrame(payloadWithVt);\n * } catch (err) {\n * if (err instanceof MllpFramingError) {\n * logger.error({ code: err.code, offset: err.byteOffset });\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { WarningCode } from \"./registry.js\";\n\n/** Maximum number of bytes captured in the `snippet` field. */\nconst MAX_SNIPPET_BYTES = 64;\n\n/**\n * Thrown for unrecoverable MLLP wire-format violations.\n *\n * - `code`, stable `WarningCode` identifying the violation\n * - `byteOffset`, absolute stream position where the violation was detected\n * - `snippet`, the single framing-boundary byte that broke the structure, or empty when the\n * anomaly is not a specific byte. **Never a run of payload content** (see the `snippet` PHI\n * contract below); the constructor caps whatever it is given to 64 bytes as a backstop.\n *\n * @example\n * ```typescript\n * // Pass only the offending framing-boundary byte, never a slice of payload content.\n * throw new MllpFramingError('MLLP_MISSING_LEADING_VT', byteOffset, Buffer.from([byte]));\n * ```\n */\nexport class MllpFramingError extends Error {\n override readonly name = \"MllpFramingError\" as const;\n\n /** Stable warning code identifying the violation type. */\n readonly code: WarningCode;\n\n /** Absolute byte offset in the stream where the error was detected. */\n readonly byteOffset: number;\n\n /**\n * Up to 64 bytes copied from around the anomaly.\n *\n * This is a **copied** Buffer, isolated from the source buffer so it remains\n * valid after the underlying buffer is reused or overwritten.\n *\n * **PHI contract:** the decoder only ever populates this with the single\n * framing-boundary byte that violated the frame structure (whose hex value the\n * `message` already discloses), never a run of payload content bytes. For anomalies\n * whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE`, the accumulated size),\n * the snippet is **empty**: a payload slice on a public error field would leak a\n * field-body slice of clinical content. Callers constructing this directly are\n * responsible for the same discipline.\n */\n readonly snippet: Buffer;\n\n /**\n * Construct an MLLP framing error.\n *\n * @param code - The stable warning code classifying the violation.\n * @param byteOffset - Absolute stream byte offset where the violation was detected.\n * @param snippet - Raw bytes copied from around the anomaly (capped to 64 bytes).\n * @param message - Optional human-readable override message.\n */\n constructor(\n code: WarningCode,\n byteOffset: number,\n /** Raw bytes around the anomaly. Capped to MAX_SNIPPET_BYTES (64). */\n snippet: Buffer,\n message?: string,\n ) {\n super(message ?? `MLLP framing error: ${code} at byte offset ${byteOffset}`);\n this.code = code;\n this.byteOffset = byteOffset;\n // Copy and cap, error must outlive source buffer; never store a view.\n this.snippet = Buffer.from(snippet.subarray(0, MAX_SNIPPET_BYTES));\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpFramingError);\n }\n }\n}\n","/**\n * MLLP framing byte constants.\n *\n * All byte values match the MLLP v1 wire protocol as specified in HL7 appendix C.\n *\n * @example\n * ```typescript\n * import { VT, FS, CR } from '@cosyte/mllp';\n * // VT = 0x0B, FS = 0x1C, CR = 0x0D\n * ```\n *\n * @packageDocumentation\n */\n\n/** Vertical Tab (0x0B), marks the start of an MLLP frame. */\nexport const VT = 0x0b;\n\n/** File Separator (0x1C), marks the end of MLLP payload bytes. */\nexport const FS = 0x1c;\n\n/** Carriage Return (0x0D), terminates the FS byte; together FS+CR ends the frame. */\nexport const CR = 0x0d;\n\n/** Line Feed (0x0A), used by the MLLP_LF_AFTER_FS tolerance. */\nexport const LF = 0x0a;\n\n/**\n * Default maximum accumulated payload size in bytes (16 MiB).\n *\n * Exceeding this limit throws `MllpFramingError('MLLP_FRAME_TOO_LARGE')`.\n * Satisfies the DoS-prevention requirement on accumulator growth.\n */\nexport const DEFAULT_MAX_FRAME_SIZE = 16 * 1024 * 1024;\n","/**\n * MLLP frame encoder, canonical `VT + payload + FS + CR` wrapping.\n *\n * The encoder is a **strict emitter** (Postel's Law: conservative on write).\n * There is no option to loosen the output format, it always emits canonical framing.\n * Tolerance for delimiter bytes in the payload is available via `allowDelimiterBytesInPayload`,\n * which passes bytes through verbatim and emits an `MllpWarning` for each offending byte.\n *\n * @example\n * ```typescript\n * import { encodeFrame } from '@cosyte/mllp';\n * const frame = encodeFrame(Buffer.from('MSH|^~\\\\&|...'));\n * // frame[0] === 0x0B (VT), frame[frame.length - 2] === 0x1C (FS), frame[frame.length - 1] === 0x0D (CR)\n * ```\n *\n * @packageDocumentation\n */\n\nimport { VT, FS, CR } from \"./constants.js\";\nimport { MllpFramingError } from \"./error.js\";\nimport { createWarning } from \"./registry.js\";\nimport type { MllpWarning } from \"./registry.js\";\n\n/**\n * Options for {@link encodeFrame}.\n *\n * @example\n * ```typescript\n * const frame = encodeFrame(payload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn(w),\n * });\n * ```\n */\nexport interface EncoderOptions {\n /**\n * When `true`, VT (0x0B) or FS (0x1C) bytes within the payload are preserved\n * verbatim in the output frame instead of throwing `MllpFramingError`.\n * An `MllpWarning` is emitted per offending byte if `onWarning` is provided.\n *\n * Default: `false` (strict, throws on delimiter bytes).\n */\n allowDelimiterBytesInPayload?: boolean;\n\n /**\n * Called for each offending delimiter byte when `allowDelimiterBytesInPayload` is `true`.\n * Invocation is wrapped in try/catch, a throwing handler does not interrupt encoding.\n *\n * @example\n * ```typescript\n * const frame = encodeFrame(payload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn({ code: w.code, offset: w.byteOffset }),\n * });\n * ```\n */\n onWarning?: (w: MllpWarning) => void;\n}\n\n/**\n * Encode `payload` in canonical MLLP framing: `VT (0x0B) + payload + FS (0x1C) + CR (0x0D)`.\n *\n * Strict by default: throws `MllpFramingError` if the payload contains VT or FS bytes.\n * Set `{ allowDelimiterBytesInPayload: true }` to pass those bytes through verbatim\n * and receive an `MllpWarning` per offending byte via `onWarning` instead.\n *\n * The returned Buffer has length `payload.length + 3`. Bytes at positions 1 through\n * `payload.length` are an exact copy of the payload (independent of the input buffer).\n *\n * @throws {MllpFramingError} with code `MLLP_PAYLOAD_CONTAINS_VT` when payload contains\n * byte `0x0B` and `allowDelimiterBytesInPayload` is `false` (default).\n * @throws {MllpFramingError} with code `MLLP_PAYLOAD_CONTAINS_FS` when payload contains\n * byte `0x1C` and `allowDelimiterBytesInPayload` is `false` (default).\n *\n * @example\n * ```typescript\n * import { encodeFrame } from '@cosyte/mllp';\n *\n * // Strict (default), throws on delimiter bytes in payload\n * const frame = encodeFrame(Buffer.from('MSH|^~\\\\&|SEND|FAC|RECV|FAC|...'));\n * socket.write(frame);\n *\n * // Tolerant, passes delimiter bytes through with a warning\n * const frame2 = encodeFrame(dirtyPayload, {\n * allowDelimiterBytesInPayload: true,\n * onWarning: (w) => logger.warn(w),\n * });\n * ```\n */\nexport function encodeFrame(payload: Buffer, opts?: EncoderOptions): Buffer {\n const allow = opts?.allowDelimiterBytesInPayload === true;\n const onWarning = opts?.onWarning;\n\n // Scan for delimiter bytes, O(n) but required to ensure correct framing (FRAME-03).\n for (let i = 0; i < payload.length; i++) {\n const byte = payload[i];\n if (byte === undefined) break; // noUncheckedIndexedAccess guard\n\n if (byte === VT || byte === FS) {\n const code = byte === VT ? \"MLLP_PAYLOAD_CONTAINS_VT\" : \"MLLP_PAYLOAD_CONTAINS_FS\";\n\n if (!allow) {\n // PHI: the snippet must NOT carry the surrounding payload bytes, that is a\n // field-body slice of clinical content on a public error field (MLLP-9 PHI\n // audit, mirroring the decoder's MLLP_FRAME_TOO_LARGE fix). The offending byte\n // is itself a framing delimiter (VT/FS, a control byte, never PHI); the `code`\n // already names which and the offset is on the error, so the snippet is just\n // that one boundary byte.\n throw new MllpFramingError(code, i, Buffer.from([byte]));\n }\n\n // Tolerant path: emit warning and continue, bytes pass through verbatim.\n if (onWarning !== undefined) {\n const byteHex = byte === VT ? \"0x0B\" : \"0x1C\";\n const warning = createWarning(\n code,\n i,\n `Payload contains ${byte === VT ? \"VT\" : \"FS\"} byte (${byteHex}) at offset ${i}; frame will be ambiguous`,\n );\n try {\n onWarning(warning);\n } catch {\n // WARN-06: a throwing handler must not interrupt encoding.\n }\n }\n }\n }\n\n // Allocate output: VT + payload + FS + CR, canonical, immutable shape (FRAME-03).\n // allocUnsafe is safe here because all bytes are set before the buffer is returned.\n const frame = Buffer.allocUnsafe(payload.length + 3);\n frame[0] = VT;\n payload.copy(frame, 1);\n frame[payload.length + 1] = FS;\n frame[payload.length + 2] = CR;\n return frame;\n}\n","/**\n * MLLP frame decoder, stateful 3-state FSM for chunked byte-stream parsing.\n *\n * Feed raw TCP chunks via `push(chunk)`. Complete MLLP frames fire synchronously\n * via the `onFrame` callback during each `push()` call.\n *\n * The decoder is **strict by default**: any framing deviation throws `MllpFramingError`\n * unless the matching tolerance option is explicitly enabled (Postel's Law, liberal receiver\n * means explicit tolerance, not silent acceptance).\n *\n * @example\n * ```typescript\n * import { FrameReader } from '@cosyte/mllp';\n * const reader = new FrameReader({\n * onFrame: (payload) => handleMessage(payload),\n * onWarning: (w) => logger.warn(w),\n * allowFsOnly: true,\n * });\n * socket.on('data', (chunk) => reader.push(chunk));\n * socket.on('close', () => reader.reset());\n * ```\n *\n * @packageDocumentation\n */\n\nimport { VT, FS, CR, LF, DEFAULT_MAX_FRAME_SIZE } from \"./constants.js\";\nimport { MllpFramingError } from \"./error.js\";\nimport { createWarning } from \"./registry.js\";\nimport type { MllpWarning, WarningCode } from \"./registry.js\";\n\n/** Internal FSM states. */\ntype ReaderState = \"SCANNING_FOR_VT\" | \"READING_PAYLOAD\" | \"EXPECTING_CR\";\n\n/** Initial accumulator buffer size (4 KiB). Grows by doubling as needed. */\nconst INITIAL_ACCUMULATOR_SIZE = 4096;\n\n/**\n * Shared empty snippet for framing errors whose anomaly is not a specific byte (e.g.\n * `MLLP_FRAME_TOO_LARGE`, where the fault is the accumulated size, not any content byte).\n * Emitting a run of accumulated payload bytes here would leak a field-body slice of clinical\n * content onto a public error field.\n */\nconst EMPTY_SNIPPET = Buffer.alloc(0);\n\n/**\n * Options for `FrameReader`.\n *\n * All tolerance opts default to `false`, every framing deviation throws unless\n * explicitly enabled. Server-level defaults (allowFsOnly, allowLfAfterFs, allowLeadingWhitespace)\n * are applied by the server when constructing readers.\n *\n * @example\n * ```typescript\n * const opts: FrameReaderOptions = {\n * onFrame: (payload, byteOffset, warnings) => process(payload, byteOffset, warnings),\n * onWarning: (w) => logger.warn(w),\n * maxFrameSizeBytes: 4 * 1024 * 1024, // 4 MiB limit\n * allowFsOnly: true,\n * allowLfAfterFs: true,\n * };\n * ```\n */\nexport interface FrameReaderOptions {\n /**\n * Called synchronously during `push()` for each complete MLLP payload.\n *\n * @param payload - Raw MLLP payload bytes (framing stripped).\n * @param byteOffset - Stream byte offset of the VT byte that opened this frame.\n * Monotonic across the connection lifetime; reset to 0 by `reset()`.\n * @param warnings - Framing warnings emitted during decoding of this specific frame.\n * Empty array when no tolerance deviations were detected.\n */\n onFrame: (payload: Buffer, byteOffset: number, warnings: readonly MllpWarning[]) => void;\n /**\n * Called for each tolerated framing deviation. Wrapped in try/catch.\n * A throwing handler will not corrupt FSM state.\n */\n onWarning?: (w: MllpWarning) => void;\n /**\n * Maximum accumulated payload bytes before `MllpFramingError('MLLP_FRAME_TOO_LARGE')`.\n * Default: 16 MiB (DoS prevention).\n */\n maxFrameSizeBytes?: number;\n /** Tolerate FS without trailing CR; emits `MLLP_FS_WITHOUT_CR`. */\n allowFsOnly?: boolean;\n /** Tolerate FS+LF instead of FS+CR; emits `MLLP_LF_AFTER_FS`. */\n allowLfAfterFs?: boolean;\n /** Tolerate missing leading VT; emits `MLLP_MISSING_LEADING_VT`. */\n allowMissingLeadingVt?: boolean;\n /** Tolerate SP/TAB/LF/CR before VT; emits `MLLP_LEADING_WHITESPACE`. */\n allowLeadingWhitespace?: boolean;\n /**\n * When `true`, escalates the following tolerances to thrown `MllpFramingError`\n * even if individual opt-ins (`allowFsOnly`, `allowLfAfterFs`, `allowMissingLeadingVt`,\n * `allowLeadingWhitespace`) are enabled:\n * - `MLLP_MISSING_LEADING_VT`\n * - `MLLP_FS_WITHOUT_CR`\n * - `MLLP_LF_AFTER_FS`\n * - `MLLP_LEADING_WHITESPACE` (leading whitespace escalates as `MLLP_MISSING_LEADING_VT`)\n *\n * `MLLP_EMPTY_PAYLOAD` and `MLLP_TRAILING_BYTES` remain warnings even in strict mode.\n *\n * @example\n * ```typescript\n * // Hardened enforcement, no tolerance, all violations throw\n * const reader = new FrameReader({ onFrame: fn, strict: true });\n * ```\n */\n strict?: boolean;\n}\n\n/**\n * Stateful MLLP frame decoder. Feed raw TCP byte chunks via `push(chunk)`.\n * Complete frames fire synchronously via `onFrame` callback during `push()`.\n *\n * The decoder operates as a 3-state FSM:\n * - `SCANNING_FOR_VT`, waiting for the 0x0B frame-start byte\n * - `READING_PAYLOAD`, accumulating payload bytes until FS (0x1C)\n * - `EXPECTING_CR`, received FS, waiting for CR (0x0D) to complete the frame\n *\n * @example\n * ```typescript\n * const reader = new FrameReader({\n * onFrame: (payload, byteOffset, warnings) => process(payload, byteOffset, warnings),\n * onWarning: (w) => logger.warn(w),\n * });\n * socket.on('data', (chunk) => reader.push(chunk));\n * ```\n */\nexport class FrameReader {\n private readonly _opts: FrameReaderOptions;\n private readonly _maxFrameSize: number;\n\n private _state: ReaderState = \"SCANNING_FOR_VT\";\n /** Reusable accumulator buffer, grown by doubling as needed. */\n private _accumulator: Buffer = Buffer.allocUnsafe(INITIAL_ACCUMULATOR_SIZE);\n /** Number of payload bytes written to accumulator in current frame. */\n private _writePos = 0;\n /** Monotonic absolute stream byte offset across the entire connection lifetime. */\n private _byteOffset = 0;\n /** Byte offset where current leading-whitespace run started. */\n private _wsStart = 0;\n /** Count of leading whitespace bytes accumulated in current run. */\n private _wsCount = 0;\n /** Byte offset of the VT byte that started the current frame. */\n private _frameStartOffset = 0;\n /** Per-frame warning accumulator, cleared after each _deliverFrame(). */\n private _frameWarnings: MllpWarning[] = [];\n\n /**\n * Construct a chunked MLLP frame reader.\n *\n * @param opts - Reader options (`onFrame`/`onWarning` callbacks, tolerance, `maxFrameSizeBytes`).\n */\n constructor(opts: FrameReaderOptions) {\n this._opts = opts;\n this._maxFrameSize =\n opts.maxFrameSizeBytes !== undefined ? opts.maxFrameSizeBytes : DEFAULT_MAX_FRAME_SIZE;\n }\n\n /**\n * Feed a chunk of raw TCP bytes into the FSM. Frames fire synchronously via `onFrame`\n * during this call. May throw `MllpFramingError` for unrecoverable framing violations\n * when the matching tolerance is not enabled.\n *\n * @example\n * ```typescript\n * socket.on('data', (chunk) => reader.push(chunk));\n * ```\n */\n push(chunk: Buffer): void {\n for (let i = 0; i < chunk.length; i++, this._byteOffset++) {\n const byte = chunk[i];\n if (byte === undefined) break; // noUncheckedIndexedAccess guard\n\n this._processByte(byte);\n }\n }\n\n /**\n * Clear internal accumulator state and reset byte offset to 0.\n *\n * Call on reconnect / connection reuse to start fresh without allocating a new reader.\n * Pending partial frame state is discarded silently.\n *\n * @example\n * ```typescript\n * socket.on('close', () => reader.reset());\n * ```\n */\n reset(): void {\n this._state = \"SCANNING_FOR_VT\";\n this._writePos = 0;\n this._byteOffset = 0;\n this._wsStart = 0;\n this._wsCount = 0;\n this._frameStartOffset = 0;\n this._frameWarnings = [];\n }\n\n private _processByte(byte: number): void {\n switch (this._state) {\n case \"SCANNING_FOR_VT\":\n this._scanForVt(byte);\n break;\n case \"READING_PAYLOAD\":\n this._readPayload(byte);\n break;\n case \"EXPECTING_CR\":\n this._expectCr(byte);\n break;\n }\n }\n\n private _scanForVt(byte: number): void {\n if (byte === VT) {\n // Emit leading-whitespace warning now that we know whitespace preceded this VT\n if (this._wsCount > 0) {\n this._emitWarning(\n \"MLLP_LEADING_WHITESPACE\",\n this._wsStart,\n `${this._wsCount} leading whitespace byte(s) before VT at offset ${this._wsStart}`,\n );\n this._wsStart = 0;\n this._wsCount = 0;\n }\n this._frameStartOffset = this._byteOffset;\n this._state = \"READING_PAYLOAD\";\n return;\n }\n\n // Check for whitespace bytes: SP (0x20), TAB (0x09), LF (0x0A), CR (0x0D)\n const isWhitespace = byte === 0x20 || byte === 0x09 || byte === LF || byte === CR;\n\n if (isWhitespace) {\n if (this._opts.allowLeadingWhitespace === true) {\n // Strict mode escalates leading whitespace to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Strict mode: leading whitespace before VT at offset ${this._byteOffset}`,\n );\n }\n if (this._wsCount === 0) {\n this._wsStart = this._byteOffset;\n }\n this._wsCount++;\n return;\n }\n // Whitespace before VT without tolerance → framing error\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Expected VT (0x0B) to start MLLP frame, got whitespace 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n // Non-VT, non-whitespace byte in SCANNING_FOR_VT.\n // If whitespace was accumulated under allowLeadingWhitespace, emit that warning first.\n if (this._wsCount > 0) {\n this._emitWarning(\n \"MLLP_LEADING_WHITESPACE\",\n this._wsStart,\n `${this._wsCount} leading whitespace byte(s) before VT at offset ${this._wsStart}`,\n );\n this._wsStart = 0;\n this._wsCount = 0;\n }\n\n if (this._opts.allowMissingLeadingVt === true) {\n // Strict mode escalates missing leading VT to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Strict mode: missing leading VT at offset ${this._byteOffset}`,\n );\n }\n // Treat this non-VT byte as the first payload byte\n this._emitWarning(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n `Missing leading VT (0x0B), treating byte 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset} as payload start`,\n );\n this._frameStartOffset = this._byteOffset;\n this._state = \"READING_PAYLOAD\";\n this._appendByte(byte);\n return;\n }\n\n // Default: strict framing error\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_MISSING_LEADING_VT\",\n this._byteOffset,\n snippet,\n `Expected VT (0x0B) to start MLLP frame, got 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n private _readPayload(byte: number): void {\n if (byte === FS) {\n // End-of-payload marker found\n if (this._writePos === 0) {\n // Empty payload is always a warning (WARN-05), never a throw\n this._emitWarning(\n \"MLLP_EMPTY_PAYLOAD\",\n this._byteOffset,\n `Empty payload between VT and FS at offset ${this._byteOffset}`,\n );\n }\n this._state = \"EXPECTING_CR\";\n return;\n }\n\n if (byte === VT) {\n // VT mid-payload: the current partial payload is abandoned and accumulation restarts after\n // this VT, so the frame that eventually delivers is only the REMNANT after the discard.\n // Emit MLLP_TRAILING_BYTES (always a warning, never a throw), this is the code's **reserved**\n // meaning: \"bytes were discarded; the delivered payload is a fragment.\" It is emitted while\n // accumulating the remnant frame, so it attaches to that frame (never bled onto another), and\n // `MllpServer`'s auto-ACK path keys on it to refuse a positive `AA` for a destroyed message.\n this._emitWarning(\n \"MLLP_TRAILING_BYTES\",\n this._byteOffset,\n `Unexpected VT (0x0B) mid-payload at offset ${this._byteOffset}; discarding ${this._writePos} accumulated bytes`,\n );\n this._writePos = 0;\n // Remain in READING_PAYLOAD, the VT starts a new frame payload accumulation\n this._state = \"READING_PAYLOAD\";\n return;\n }\n\n // Regular payload byte, enforce size cap BEFORE appending\n if (this._writePos >= this._maxFrameSize) {\n // PHI: the snippet must NOT carry a run of accumulated payload bytes, that is a\n // field-body slice of clinical content (the too-large frame is a full HL7 message).\n // The anomaly here is the size, not any specific byte, so the snippet is empty; the\n // structural facts (cap, byte offset) live in the message. (MLLP-9 PHI audit.)\n throw new MllpFramingError(\n \"MLLP_FRAME_TOO_LARGE\",\n this._byteOffset,\n EMPTY_SNIPPET,\n `Frame payload exceeded maxFrameSizeBytes (${this._maxFrameSize}) at offset ${this._byteOffset}`,\n );\n }\n\n this._appendByte(byte);\n }\n\n private _expectCr(byte: number): void {\n if (byte === CR) {\n // Normal frame completion\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n\n if (byte === LF) {\n if (this._opts.allowLfAfterFs === true) {\n // Strict mode escalates LF after FS to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n snippet,\n `Strict mode: LF after FS at offset ${this._byteOffset}`,\n );\n }\n this._emitWarning(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n `LF (0x0A) after FS instead of CR (0x0D) at offset ${this._byteOffset}`,\n );\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_LF_AFTER_FS\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got LF (0x0A) at offset ${this._byteOffset}. Enable allowLfAfterFs to tolerate.`,\n );\n }\n\n if (byte === VT) {\n // Next frame starts immediately after FS, no CR between frames\n if (this._opts.allowFsOnly === true) {\n // Strict mode escalates FS without CR to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Strict mode: FS without CR at offset ${this._byteOffset}`,\n );\n }\n this._emitWarning(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n `FS not followed by CR at offset ${this._byteOffset}; next frame VT found immediately`,\n );\n this._deliverFrame();\n // The VT byte is consumed here, transition directly to READING_PAYLOAD\n this._state = \"READING_PAYLOAD\";\n return;\n }\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got VT (0x0B) at offset ${this._byteOffset}. Enable allowFsOnly to tolerate.`,\n );\n }\n\n // Any other non-CR byte after FS (not LF, not VT)\n if (this._opts.allowFsOnly === true) {\n // Strict mode escalates FS without CR to an error (WARN-08)\n if (this._opts.strict === true) {\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Strict mode: FS without CR at offset ${this._byteOffset}`,\n );\n }\n // Deliver the completed frame and flag the missing CR; the stray byte is then dropped and\n // we return to scanning. The single FS_WITHOUT_CR warning names the stray byte and is\n // attached to the frame it followed.\n //\n // We deliberately do NOT also emit MLLP_TRAILING_BYTES here. That code is **reserved for a\n // VT appearing mid-payload** (see `_readPayload`), where it means \"this delivered payload is\n // only the remnant after accumulated bytes were discarded\", a signal `MllpServer`'s\n // auto-ACK path keys on to refuse a positive `AA` for a message whose bytes were destroyed.\n // Emitting it for an inter-frame stray byte overloaded that meaning AND mis-attributed it:\n // it was emitted *after* `_deliverFrame()`, so it landed in the NEXT frame's warning\n // accumulator and made a subsequent, perfectly good message look discarded. FS_WITHOUT_CR\n // already, correctly, reports this deviation.\n this._emitWarning(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n `FS not followed by CR at offset ${this._byteOffset}; got 0x${byte.toString(16).padStart(2, \"0\")} (dropped)`,\n );\n this._deliverFrame();\n this._state = \"SCANNING_FOR_VT\";\n return;\n }\n\n const snippet = Buffer.from([byte]);\n throw new MllpFramingError(\n \"MLLP_FS_WITHOUT_CR\",\n this._byteOffset,\n snippet,\n `Expected CR (0x0D) after FS, got 0x${byte.toString(16).padStart(2, \"0\")} at offset ${this._byteOffset}`,\n );\n }\n\n /** Grow the internal accumulator by doubling and write one byte. */\n private _appendByte(byte: number): void {\n if (this._writePos >= this._accumulator.length) {\n const newSize = this._accumulator.length * 2;\n const grown = Buffer.allocUnsafe(newSize);\n this._accumulator.subarray(0, this._writePos).copy(grown);\n this._accumulator = grown;\n }\n this._accumulator[this._writePos++] = byte;\n }\n\n /**\n * Copy accumulated payload bytes into a new Buffer and deliver via `onFrame`.\n * The copy isolates callers from internal accumulator reuse.\n */\n private _deliverFrame(): void {\n const payload = Buffer.from(this._accumulator.subarray(0, this._writePos));\n this._writePos = 0;\n const frameStart = this._frameStartOffset;\n const frameWarnings: readonly MllpWarning[] = this._frameWarnings;\n this._frameWarnings = []; // reset for next frame\n this._opts.onFrame(payload, frameStart, frameWarnings);\n }\n\n /** Emit a warning via the `onWarning` callback, swallowing any handler exceptions. */\n private _emitWarning(code: WarningCode, byteOffset: number, message: string): void {\n const warning = createWarning(code, byteOffset, message);\n // Accumulate per-frame warnings unconditionally (independent of onWarning handler presence)\n this._frameWarnings.push(warning);\n if (this._opts.onWarning !== undefined) {\n try {\n this._opts.onWarning(warning);\n } catch {\n // WARN-06: throwing handler must not corrupt FSM state\n }\n }\n }\n}\n","/**\n * NetTransport, `Transport` implementation wrapping a `net.Socket`.\n *\n * This is the **only** place in the codebase that consumes `net.Socket`'s\n * EventEmitter surface. All higher layers (Connection, Server, Client) work\n * against the `Transport` interface, never against `net.Socket` directly.\n *\n * @example\n * ```typescript\n * import { createConnection } from 'node:net';\n * import { NetTransport } from '@cosyte/mllp';\n *\n * const socket = createConnection({ host: 'mllp.example.com', port: 2575 });\n * const transport = new NetTransport(socket);\n * transport.onConnect(() => console.log('connected'));\n * transport.onData((chunk) => reader.push(chunk));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Socket } from \"node:net\";\nimport type { Transport } from \"./index.js\";\n\n/**\n * Wraps a `net.Socket` as a `Transport`, mapping socket EventEmitter events\n * to registered single-handler callbacks.\n *\n * Each `onXxx(fn)` call replaces the prior handler and re-registers on the socket\n * (uses `socket.removeAllListeners(event)` + `socket.on(event, fn)`).\n *\n * @example\n * ```typescript\n * const t = new NetTransport(socket);\n * t.onData((chunk) => process(chunk));\n * t.onError((err) => handleError(err));\n * ```\n */\nexport class NetTransport implements Transport {\n private readonly _socket: Socket;\n\n /**\n * Wrap an existing `net.Socket` (or `tls.TLSSocket`) as a `Transport`.\n *\n * @param socket - The connected (or connecting) socket to adapt.\n */\n constructor(socket: Socket) {\n this._socket = socket;\n }\n\n /** See {@link Transport.write}. */\n write(buf: Buffer): boolean {\n return this._socket.write(buf);\n }\n\n /** See {@link Transport.close}. */\n close(): void {\n this._socket.end();\n }\n\n /** See {@link Transport.destroy}. */\n destroy(reason?: Error): void {\n this._socket.destroy(reason);\n }\n\n /** See {@link Transport.onData}. */\n onData(fn: (chunk: Buffer) => void): void {\n this._socket.removeAllListeners(\"data\");\n this._socket.on(\"data\", fn);\n }\n\n /** See {@link Transport.onConnect}. */\n onConnect(fn: () => void): void {\n this._socket.removeAllListeners(\"connect\");\n this._socket.on(\"connect\", fn);\n }\n\n /** See {@link Transport.onClose}. */\n onClose(fn: () => void): void {\n this._socket.removeAllListeners(\"close\");\n this._socket.on(\"close\", fn);\n }\n\n /** See {@link Transport.onError}. */\n onError(fn: (err: Error) => void): void {\n this._socket.removeAllListeners(\"error\");\n this._socket.on(\"error\", fn);\n }\n}\n","/**\n * TlsTransport, `Transport` implementation wrapping a `tls.TLSSocket`.\n *\n * Identical wiring to `NetTransport` with one deliberate difference: `onConnect`\n * maps to the socket's `'secureConnect'` event (fires once the TLS handshake\n * completes) rather than `'connect'` (fires once the raw TCP handshake\n * completes). Consumers of `Transport` (Connection, Client, Server) never see\n * the difference, they only ever observe \"the transport is ready.\"\n *\n * @example\n * ```typescript\n * import { connect } from 'node:tls';\n * import { TlsTransport } from '@cosyte/mllp';\n *\n * const socket = connect({ host: 'mllp.example.com', port: 2575, ca: caPem });\n * const transport = new TlsTransport(socket);\n * transport.onConnect(() => console.log('TLS handshake complete'));\n * transport.onData((chunk) => reader.push(chunk));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { TLSSocket } from \"node:tls\";\nimport type { Transport } from \"./index.js\";\n\n/**\n * Wraps a `tls.TLSSocket` as a `Transport`. `onConnect(fn)` is armed on the\n * `'secureConnect'` event (handshake complete, including certificate\n * verification when `rejectUnauthorized` is on) rather than the raw TCP\n * `'connect'` event that `NetTransport` uses.\n *\n * Each `onXxx(fn)` call replaces the prior handler and re-registers on the\n * socket (`socket.removeAllListeners(event)` + `socket.on(event, fn)`),\n * the same set-once semantics as `NetTransport`.\n *\n * @example\n * ```typescript\n * const t = new TlsTransport(tlsSocket);\n * t.onConnect(() => logger.info('secure'));\n * t.onError((err) => handleTlsError(err));\n * ```\n */\nexport class TlsTransport implements Transport {\n private readonly _socket: TLSSocket;\n\n /**\n * Wrap an existing `tls.TLSSocket` (connecting or already secure) as a `Transport`.\n *\n * @param socket - The TLS socket to adapt.\n */\n constructor(socket: TLSSocket) {\n this._socket = socket;\n }\n\n /** See {@link Transport.write}. */\n write(buf: Buffer): boolean {\n return this._socket.write(buf);\n }\n\n /** See {@link Transport.close}. */\n close(): void {\n this._socket.end();\n }\n\n /** See {@link Transport.destroy}. */\n destroy(reason?: Error): void {\n this._socket.destroy(reason);\n }\n\n /** See {@link Transport.onData}. */\n onData(fn: (chunk: Buffer) => void): void {\n this._socket.removeAllListeners(\"data\");\n this._socket.on(\"data\", fn);\n }\n\n /**\n * See {@link Transport.onConnect}. Armed on `'secureConnect'`, the TLS\n * handshake-complete event, not the raw TCP `'connect'` event.\n */\n onConnect(fn: () => void): void {\n this._socket.removeAllListeners(\"secureConnect\");\n this._socket.on(\"secureConnect\", fn);\n }\n\n /** See {@link Transport.onClose}. */\n onClose(fn: () => void): void {\n this._socket.removeAllListeners(\"close\");\n this._socket.on(\"close\", fn);\n }\n\n /** See {@link Transport.onError}. */\n onError(fn: (err: Error) => void): void {\n this._socket.removeAllListeners(\"error\");\n this._socket.on(\"error\", fn);\n }\n}\n","/**\n * Security-warning codes and payload shape shared by the client and server.\n *\n * These codes are a **public API**, they appear in `'securityWarning'` event\n * handlers, log pipelines, and monitoring dashboards. Renaming or removing a\n * code is a breaking change (mirrors the `WarningCode` guardrail in\n * `src/framing/registry.ts`).\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_VERIFY_DISABLED, MLLP_BIND_ALL_INTERFACES } from '@cosyte/mllp';\n * client.on('securityWarning', (w) => {\n * if (w.code === MLLP_TLS_VERIFY_DISABLED) logger.warn(w.message);\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Emitted (client-side) on every successful `secureConnect`, initial connect\n * AND every reconnect, when {@link TlsOptions.allowUnverified} is `true`.\n * Certificate verification is disabled for the connection; this is the loud,\n * per-connection reminder that the channel is not authenticated per IHE ATNA\n * ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_VERIFY_DISABLED } from '@cosyte/mllp';\n * client.on('securityWarning', (w) => {\n * if (w.code === MLLP_TLS_VERIFY_DISABLED) metrics.increment('mllp.tls_verify_disabled');\n * });\n * ```\n */\nexport const MLLP_TLS_VERIFY_DISABLED = \"MLLP_TLS_VERIFY_DISABLED\";\n\n/**\n * Emitted (server-side) once at `listen()` time when the server binds a\n * wildcard host (`'0.0.0.0'` or `'::'`) with `ServerOptions.allowWildcardBind: true`.\n * Binding all interfaces widens the network exposure of the listener; this is\n * the loud, one-time reminder that the operator opted in.\n *\n * @example\n * ```typescript\n * import { MLLP_BIND_ALL_INTERFACES } from '@cosyte/mllp';\n * server.on('securityWarning', (w) => {\n * if (w.code === MLLP_BIND_ALL_INTERFACES) logger.warn(w.message);\n * });\n * ```\n */\nexport const MLLP_BIND_ALL_INTERFACES = \"MLLP_BIND_ALL_INTERFACES\";\n\n/**\n * Union of the two stable security-warning codes.\n *\n * @example\n * ```typescript\n * const code: SecurityWarningCode = 'MLLP_TLS_VERIFY_DISABLED';\n * ```\n */\nexport type SecurityWarningCode = typeof MLLP_TLS_VERIFY_DISABLED | typeof MLLP_BIND_ALL_INTERFACES;\n\n/**\n * Frozen payload of the `'securityWarning'` event, emitted by both\n * {@link MllpClient} (`MLLP_TLS_VERIFY_DISABLED`) and {@link MllpServer}\n * (`MLLP_BIND_ALL_INTERFACES`).\n *\n * Never carries payload bytes or PHI, only routing metadata (host/port) and\n * a fixed, static message string.\n *\n * @example\n * ```typescript\n * client.on('securityWarning', (w: SecurityWarning) => {\n * logger.warn({ code: w.code, host: w.host, port: w.port });\n * });\n * ```\n */\nexport interface SecurityWarning {\n /** The stable security-warning code. */\n readonly code: SecurityWarningCode;\n /** Fixed, human-readable description. Never contains payload bytes or PHI. */\n readonly message: string;\n /** Host associated with the warning (the target host for clients; the bind host for servers). */\n readonly host: string;\n /** Port associated with the warning. */\n readonly port: number;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n","/**\n * Typed TLS **configuration** errors and their stable codes.\n *\n * A configuration error is raised before any socket exists: what this package\n * was asked to offer cannot be honoured, so the connection is refused rather\n * than opened on some other configuration. Two things can be asked for, and\n * they carry separate codes: the offered cipher-suite list, and the server's\n * ephemeral Diffie-Hellman parameters. It rejects the `connect()` / `listen()`\n * call that asked for it, never a live connection.\n *\n * These codes are a **public API**. They appear in caller `catch` blocks, log\n * pipelines, and monitoring dashboards. Renaming or removing a code is a\n * breaking change (the same guardrail the security-warning codes carry).\n *\n * @example\n * ```typescript\n * import { MllpTlsConfigurationError, MLLP_TLS_CIPHER_LIST_REJECTED } from '@cosyte/mllp';\n * try {\n * await client.connect();\n * } catch (err) {\n * if (err instanceof MllpTlsConfigurationError && err.code === MLLP_TLS_CIPHER_LIST_REJECTED) {\n * logger.error({ code: err.code });\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * The runtime refused the cipher-suite list it was handed: no suite in it is\n * available in this Node build's TLS library. Nothing falls back to the\n * runtime default list, the connection or the bind is refused instead.\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_CIPHER_LIST_REJECTED } from '@cosyte/mllp';\n * if (err.code === MLLP_TLS_CIPHER_LIST_REJECTED) process.exit(78); // EX_CONFIG\n * ```\n */\nexport const MLLP_TLS_CIPHER_LIST_REJECTED = \"MLLP_TLS_CIPHER_LIST_REJECTED\";\n\n/**\n * Two different offered-suite declarations were made at once:\n * `atnaTransportSecurity: true` fixes the list, and `ciphers` replaces it.\n * Honouring either one silently discards the other, so both are refused.\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_CIPHER_OPTION_CONFLICT } from '@cosyte/mllp';\n * if (err.code === MLLP_TLS_CIPHER_OPTION_CONFLICT) logger.error('pick one of the two');\n * ```\n */\nexport const MLLP_TLS_CIPHER_OPTION_CONFLICT = \"MLLP_TLS_CIPHER_OPTION_CONFLICT\";\n\n/**\n * The ephemeral Diffie-Hellman parameters a server supplied are not usable: not\n * PEM content, not a Diffie-Hellman parameter block, or a group the TLS library\n * refuses. Distinct from {@link MLLP_TLS_CIPHER_LIST_REJECTED} because the\n * remedy is different: one is the offered suite list, the other is the key\n * exchange behind it.\n *\n * Nothing is bound and nothing falls back to a server running with no\n * Diffie-Hellman parameters, which would advertise its DHE suites and then fail\n * every handshake in them.\n *\n * @example\n * ```typescript\n * import { MLLP_TLS_DH_PARAMETERS_REJECTED } from '@cosyte/mllp';\n * if (err.code === MLLP_TLS_DH_PARAMETERS_REJECTED) process.exit(78); // EX_CONFIG\n * ```\n */\nexport const MLLP_TLS_DH_PARAMETERS_REJECTED = \"MLLP_TLS_DH_PARAMETERS_REJECTED\";\n\n/**\n * Union of the stable TLS-configuration error codes.\n *\n * @example\n * ```typescript\n * const code: TlsConfigurationErrorCode = 'MLLP_TLS_CIPHER_LIST_REJECTED';\n * ```\n */\nexport type TlsConfigurationErrorCode =\n | typeof MLLP_TLS_CIPHER_LIST_REJECTED\n | typeof MLLP_TLS_CIPHER_OPTION_CONFLICT\n | typeof MLLP_TLS_DH_PARAMETERS_REJECTED;\n\n/**\n * Frozen message registry for {@link TlsConfigurationErrorCode}.\n *\n * Deliberately a **lookup, never an interpolation**. The factory below takes\n * only a code and no value parameter at all, which is what keeps caller-supplied\n * material (a key, a passphrase, a cipher string) out of an error message that\n * lands in a log.\n */\nconst TLS_CONFIGURATION_MESSAGES: Readonly<Record<TlsConfigurationErrorCode, string>> =\n Object.freeze({\n [MLLP_TLS_CIPHER_LIST_REJECTED]:\n \"the TLS library rejected the configured cipher-suite list; no connection was opened and \" +\n \"no fallback to the runtime default list was made\",\n [MLLP_TLS_CIPHER_OPTION_CONFLICT]:\n \"tls.atnaTransportSecurity and tls.ciphers both declare the offered cipher-suite list; \" +\n \"set exactly one of them\",\n [MLLP_TLS_DH_PARAMETERS_REJECTED]:\n \"the configured ephemeral Diffie-Hellman parameters are not usable; nothing was bound and \" +\n \"no fallback to a server without Diffie-Hellman parameters was made\",\n });\n\n/**\n * Fixed, human-readable description for a TLS-configuration code.\n *\n * Byte-for-byte identical for a given code, whatever the configuration was.\n *\n * @param code - The stable configuration-error code.\n * @returns The frozen registry text for `code`.\n *\n * @example\n * ```typescript\n * import { tlsConfigurationMessage, MLLP_TLS_CIPHER_OPTION_CONFLICT } from '@cosyte/mllp';\n * logger.error(tlsConfigurationMessage(MLLP_TLS_CIPHER_OPTION_CONFLICT));\n * ```\n */\nexport function tlsConfigurationMessage(code: TlsConfigurationErrorCode): string {\n return TLS_CONFIGURATION_MESSAGES[code];\n}\n\n/**\n * Thrown for a TLS configuration this package will not open a connection on.\n * Rejects the originating `MllpClient.connect()` or `MllpServer.listen()`; no\n * socket is opened and no listener is left bound.\n *\n * Identify it by `instanceof` **and** by the stable {@link MllpTlsConfigurationError.code},\n * never by matching on the message text. `message` is a frozen registry entry\n * and carries no key material and no passphrase; `cause`, when present, is the\n * TLS library's own error for one input on its own (the suite list and the\n * Diffie-Hellman parameters are each validated with no credential in scope).\n *\n * @example\n * ```typescript\n * import { MllpTlsConfigurationError } from '@cosyte/mllp';\n * try {\n * await server.listen(2575);\n * } catch (err) {\n * if (err instanceof MllpTlsConfigurationError) logger.error({ code: err.code });\n * }\n * ```\n */\nexport class MllpTlsConfigurationError extends Error {\n override readonly name = \"MllpTlsConfigurationError\" as const;\n\n /**\n * The stable configuration-error code. Public API; branch on this rather\n * than on the message.\n */\n readonly code: TlsConfigurationErrorCode;\n\n /**\n * The TLS library's own error, when the runtime is what refused the list.\n * `undefined` when this package refused the configuration itself.\n */\n override readonly cause: Error | undefined;\n\n /**\n * Construct a TLS configuration error.\n *\n * @param code - The stable configuration-error code; also selects the message.\n * @param opts - Optional underlying `cause` from the TLS library.\n */\n constructor(code: TlsConfigurationErrorCode, opts?: { cause?: Error }) {\n super(tlsConfigurationMessage(code));\n this.code = code;\n this.cause = opts?.cause;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpTlsConfigurationError);\n }\n }\n}\n","/**\n * Structural reading of caller-supplied ephemeral Diffie-Hellman parameters.\n *\n * WHY THIS EXISTS RATHER THAN A BARE PASSTHROUGH. The TLS library **silently\n * discards** parameters it cannot read: measured on Node v24.20.0,\n * `tls.createSecureContext({ dhparam: 'not a pem at all' })` returns a context\n * with no error at all, and a server built on it advertises its DHE suites and\n * then fails every DHE handshake in them. A server that answers no handshake is\n * the failure mode this package refuses to arrive at silently, so what a caller\n * hands over is read here first, before any socket is bound.\n *\n * WHAT THIS READ DOES AND DOES NOT ESTABLISH. It establishes that the input is\n * a PKCS#3 `DH PARAMETERS` PEM block whose body decodes to the whole template\n * the TLS library reads and nothing besides, and that the two values it carries\n * are inside the bounds the library's own parameter check applies before it will\n * answer a key exchange with them. The one thing it does **not** establish is\n * that the prime is prime. The check that would\n * (`crypto.createDiffieHellman(prime, generator).verifyError`) runs a full\n * primality test, measured at 1.8 s for a 3072-bit group and 30 s for an\n * 8192-bit one on this container, and `listen()` is not a place to spend that.\n * A structurally sound block carrying a composite modulus is therefore accepted\n * here, and the library accepts it too: measured, the committed 3072-bit prime\n * plus two completes a real handshake and reports a 3072-bit group. Said plainly\n * rather than left to be inferred from a promise this module does not keep.\n *\n * THE TWO FAILURES ARE DISTINGUISHABLE, AND BOTH ARE REFUSED HERE. Measured on\n * this container against a server-side TLS error rather than the client's:\n * parameters the library DISCARDED leave a server that cannot offer a DHE suite\n * at all and dies in `tls_post_process_client_hello` with `no shared cipher`,\n * byte for byte what a server given no parameters gives. Parameters the library\n * LOADED whose values are out of bounds get as far as\n * `tls_construct_server_key_exchange` and die there instead, with\n * `DH_check_params_ex: not suitable generator` or an internal-error alert. The\n * first class is a server running with no parameters at all and the second is a\n * server running with parameters it cannot use, and a caller cannot tell them\n * apart from the outside: both are a bound listener that answers no handshake in\n * the suite it advertises. So this reader refuses both, and the only thing left\n * to fail at handshake time is a composite modulus.\n *\n * WHAT THE BODY HAS TO BE. The library parses the PKCS#3 template\n * `SEQUENCE { INTEGER prime, INTEGER base, INTEGER privateValueLength OPTIONAL }`\n * and refuses anything else as silently as it refuses damaged armour. Measured:\n * a block carrying a good prime and generator with ONE extra element behind them\n * gives `no shared cipher`, whether the extra is an `OCTET STRING`, a `NULL`, a\n * `BOOLEAN`, a nested `SEQUENCE`, a fourth field, or a third `INTEGER` too wide\n * to be a private-value length, which is the shape an X9.42 or DSA parameter\n * file has under this label. So the sequence has to END where the template ends,\n * and the optional third field has to be readable at the width the library reads\n * it at.\n *\n * WHAT THE TWO VALUES HAVE TO BE. The library reads both as unsigned magnitudes,\n * sign bit and leading zero octets alike ignored: measured, a prime and a\n * generator written with the high bit set and no padding octet both complete a\n * handshake at the size they spell. On those magnitudes it then applies its own\n * parameter check, and the line it draws was measured one value at a time\n * against a real key exchange: the prime must be ODD, and the generator must be\n * at least 2 and no greater than the prime minus 2. A generator of 0, of 1, of\n * the prime minus 1, of the prime, or wider than the prime, and an even prime,\n * each leave a bound listener that advertises its DHE suite and answers no\n * handshake in it. Every one of those is a comparison over octets this reader has\n * already located, so refusing them costs microseconds and buys back the whole\n * class; the generator at the prime minus 2 is accepted, by the library and so\n * here.\n *\n * WHY THE ARMOUR IS READ LINE BY LINE. The encapsulation boundary is the one\n * place where \"looks like a PEM block\" and \"is a PEM block the library reads\"\n * come apart, and the gap is reachable by accident rather than by typing\n * garbage: a value that crosses an environment variable or a single-line JSON\n * field arrives with its newlines collapsed, and it is still a boundary, a\n * body and a boundary in the right order. The library requires each boundary to\n * occupy a line of its own with exactly five dashes on each side, so that\n * collapsed value is not parameters at all and is discarded in silence. This\n * reader therefore mirrors what the library's own PEM reader does with a line\n * rather than searching the text for a boundary-shaped substring.\n *\n * WHERE THIS READER AND THE LIBRARY DELIBERATELY DIVERGE. The agreement is\n * measured input by input rather than asserted, and it is not total: FOUR forms\n * the library would have put on a real link are refused here anyway. `'auto'`\n * (see below); a good block behind a first `DH PARAMETERS` block whose body is\n * junk, because the library skips a block it cannot decode and keeps looking\n * while this reader takes the first opening boundary and the first closing\n * boundary after it; trailing bytes after the parameter sequence inside one\n * body; and the indefinite-length form, which is BER and not DER. That is the\n * whole list as measured, and it is deliberately not padded with the forms the\n * library merely PARSES: an `INTEGER` with no content octets at all, for\n * instance, is refused here and is refused by the library too, loudly for the\n * prime and at key-exchange time for the generator, so refusing it forfeits no\n * working link.\n *\n * None of the four is reachable without hand-built bytes, and each fails LOUDLY\n * at `listen()` with a typed error rather than silently at handshake time. That\n * asymmetry is the whole reason to accept them: refusing more than the library\n * does costs a configuration that says it will not start, and accepting more\n * costs a listener that answers no handshake and says nothing.\n *\n * The bytes are read as an opaque parameter block: nothing derived from them\n * reaches a message, an error, or a log line.\n *\n * @packageDocumentation\n */\n\n/**\n * The encapsulation boundaries of a PKCS#3 parameter block, exactly as the TLS\n * library's PEM reader requires them: five dashes, the keyword, the label, five\n * dashes, alone on a line.\n */\nconst BEGIN_BOUNDARY = \"-----BEGIN DH PARAMETERS-----\";\nconst END_BOUNDARY = \"-----END DH PARAMETERS-----\";\n\n/**\n * The byte-order mark, in both spellings this module can meet: the decoded\n * character when the caller passed a string, and the same three bytes seen\n * through the latin1 decode when the caller passed a buffer. The library's PEM\n * reader strips it from the first line, so it cannot be what refuses a block.\n */\nconst BOM_SPELLINGS: readonly string[] = [\n String.fromCharCode(0xfeff),\n String.fromCharCode(0xef, 0xbb, 0xbf),\n];\n\n/** A base64 body: full quartets, padding only at the end. */\nconst BASE64_BODY = /^[A-Za-z0-9+/]+={0,2}$/;\n\n/** ASN.1 DER tag numbers this reader accepts, and no others. */\nconst DER_SEQUENCE = 0x30;\nconst DER_INTEGER = 0x02;\n\n/**\n * The widest contents a DER `INTEGER` can carry and still be read as the\n * template's optional private-value length.\n *\n * The library reads that field at 32 bits, so four content octets is the whole\n * range: five or more is either out of range or illegally padded, and both were\n * measured to leave a server with no parameters.\n */\nconst INT32_CONTENT_OCTETS = 4;\n\n/** One definite-length DER element: its tag, and where its contents begin and end. */\ninterface DerElement {\n readonly tag: number;\n readonly contentStart: number;\n readonly contentEnd: number;\n}\n\n/**\n * Read one definite-length DER element starting at `offset`.\n *\n * Returns `null` for a truncated element, for the indefinite-length form (`0x80`,\n * which is BER and not DER), and for a length field wider than this reader will\n * follow. Refusing is always the safe answer here: the caller treats `null` as\n * \"these are not parameters this package will hand to a TLS context\".\n *\n * @param der - The decoded DER bytes.\n * @param offset - Index of the element's tag byte.\n * @returns The element's tag and content bounds, or `null` when it does not parse.\n */\nfunction readDerElement(der: Buffer, offset: number): DerElement | null {\n if (offset + 2 > der.length) return null;\n const tag = der[offset];\n const first = der[offset + 1];\n if (tag === undefined || first === undefined) return null;\n\n let contentStart: number;\n let length: number;\n if (first < 0x80) {\n contentStart = offset + 2;\n length = first;\n } else {\n const lengthBytes = first & 0x7f;\n // 0x80 is the indefinite-length form (BER, never DER); a length field wider\n // than four bytes describes contents no PEM block here could carry.\n if (lengthBytes === 0 || lengthBytes > 4) return null;\n if (offset + 2 + lengthBytes > der.length) return null;\n length = 0;\n for (let i = 0; i < lengthBytes; i += 1) {\n const byte = der[offset + 2 + i];\n if (byte === undefined) return null;\n length = length * 256 + byte;\n }\n contentStart = offset + 2 + lengthBytes;\n }\n\n const contentEnd = contentStart + length;\n if (contentEnd > der.length) return null;\n return { tag, contentStart, contentEnd };\n}\n\n/**\n * Whether `element`'s contents read as a 32-bit integer the way the library\n * reads the template's optional private-value length.\n *\n * Two rules, both measured against real handshakes rather than inferred. The\n * contents may be no wider than four octets: five accepted nothing, at any\n * value tried. And an `INTEGER` two octets or wider may not be padded\n * redundantly, which is what the library's own integer reader refuses first: a\n * leading `0x00` in front of a non-negative octet, or a leading `0xff` in front\n * of a negative one. Empty contents ARE accepted, by the library and so here.\n *\n * The two required fields are deliberately not held to this: the library reads\n * a prime and a generator as arbitrary-width unsigned values with no padding\n * rule at all, and refusing there would refuse blocks it would have used.\n *\n * @param der - The decoded DER bytes.\n * @param element - The element to judge, already located.\n * @returns `true` when the library would read it at 32 bits.\n */\nfunction readsAsInt32(der: Buffer, element: DerElement): boolean {\n const octets = element.contentEnd - element.contentStart;\n if (octets > INT32_CONTENT_OCTETS) return false;\n if (octets < 2) return true;\n\n const first = der[element.contentStart];\n const second = der[element.contentStart + 1];\n if (first === undefined || second === undefined) return false;\n const secondIsNegative = (second & 0x80) !== 0;\n if (first === 0x00 && !secondIsNegative) return false;\n if (first === 0xff && secondIsNegative) return false;\n return true;\n}\n\n/**\n * The value a DER `INTEGER`'s content octets carry, as the TLS library reads it:\n * an unsigned big-endian magnitude with its leading zero octets dropped.\n *\n * The sign bit is deliberately ignored, because the library ignores it: measured,\n * a prime and a generator each written with the high bit set and no padding\n * octet complete a real handshake at the size they spell. Reading them as\n * negative numbers here would refuse a group the runtime would have used.\n *\n * @param der - The decoded DER bytes.\n * @param element - The element to read, already located.\n * @returns The magnitude, never empty for an element with content octets.\n */\nfunction magnitudeOf(der: Buffer, element: DerElement): Buffer {\n let start = element.contentStart;\n while (start + 1 < element.contentEnd && der[start] === 0x00) start += 1;\n return der.subarray(start, element.contentEnd);\n}\n\n/**\n * Compare two unsigned big-endian magnitudes, each already stripped of leading\n * zero octets.\n *\n * @param left - The first magnitude.\n * @param right - The second magnitude.\n * @returns A negative number, zero, or a positive number, as `left` is less\n * than, equal to, or greater than `right`.\n */\nfunction compareMagnitudes(left: Buffer, right: Buffer): number {\n if (left.length !== right.length) return left.length < right.length ? -1 : 1;\n return Buffer.compare(left, right);\n}\n\n/**\n * Whether `value` is exactly one less than `oddPrime`.\n *\n * Written as a comparison rather than a subtraction, which the shape of the\n * input makes exact: an odd number's last octet is odd, so subtracting one from\n * it cannot borrow, and every other octet of `oddPrime` is unchanged. The caller\n * establishes the oddness first, and this is only ever asked about a prime it\n * has already accepted as odd.\n *\n * @param value - The magnitude to test.\n * @param oddPrime - The prime magnitude, known to be odd.\n * @returns `true` when `value` is the prime minus one.\n */\nfunction isOneBelow(value: Buffer, oddPrime: Buffer): boolean {\n if (value.length !== oddPrime.length) return false;\n const last = value.length - 1;\n for (let i = 0; i < last; i += 1) {\n if (value[i] !== oddPrime[i]) return false;\n }\n const tail = value[last];\n const primeTail = oddPrime[last];\n return tail !== undefined && primeTail !== undefined && tail === primeTail - 1;\n}\n\n/**\n * Whether the prime and the generator are inside the bounds the TLS library's\n * own parameter check applies before it will answer a key exchange with them.\n *\n * Two rules, and the line each draws was measured one value at a time against a\n * real TLS 1.2 DHE handshake rather than read off a specification. The prime\n * must be ODD: an even one leaves a listener that advertises its DHE suite and\n * answers no handshake in it. And the generator must be at least 2 and no\n * greater than the prime minus 2: 0, 1, the prime minus 1, the prime, and\n * anything wider than the prime all fail the library's `DH_check_params`, while\n * 2, 3, 5 and the prime minus 2 all complete the handshake.\n *\n * This is the CHEAP half of what makes a group usable, and it is the only half\n * done here. The expensive half, that the prime is actually prime, is a full\n * primality test measured in seconds and is declined (see the module doc).\n *\n * @param der - The decoded DER bytes.\n * @param prime - The first template field, already located.\n * @param generator - The second template field, already located.\n * @returns `true` when the library would accept both values.\n */\nfunction isUsableGroup(der: Buffer, prime: DerElement, generator: DerElement): boolean {\n const primeValue = magnitudeOf(der, prime);\n const generatorValue = magnitudeOf(der, generator);\n\n const lowest = primeValue[primeValue.length - 1];\n if (lowest === undefined || (lowest & 1) === 0) return false;\n\n const smallest = generatorValue[0];\n if (generatorValue.length === 1 && (smallest === undefined || smallest < 2)) return false;\n if (compareMagnitudes(generatorValue, primeValue) >= 0) return false;\n return !isOneBelow(generatorValue, primeValue);\n}\n\n/**\n * One line as the TLS library's PEM reader sees it: every trailing character at\n * or below `U+0020` removed.\n *\n * That single rule is why a file written with CRLF endings, a boundary followed\n * by stray spaces, and the canonical block all read identically. It deliberately\n * does **not** touch LEADING whitespace, because the library does not either: an\n * indented boundary is not a boundary, and a server built on one runs with no\n * parameters.\n *\n * @param line - One line of the input, split on line feeds.\n * @returns The line with its trailing blanks removed.\n */\nfunction sanitizeLine(line: string): string {\n let end = line.length;\n while (end > 0 && line.charCodeAt(end - 1) <= 0x20) end -= 1;\n return line.slice(0, end);\n}\n\n/**\n * The base64 body of the `DH PARAMETERS` block in `text`, or `null` when the\n * armour is not armour the TLS library reads.\n *\n * WHAT COUNTS AS A BOUNDARY, AND WHY IT IS THIS STRICT. The library reads PEM a\n * line at a time and requires an encapsulation boundary to be the whole line:\n * exactly five dashes, the keyword, the label, exactly five dashes. Measured on\n * this container, every one of these advertises a DHE suite and then answers no\n * handshake, because the library found no parameters and discarded them without\n * a word: the same block with its newlines collapsed, with an extra dash on\n * either boundary, with either boundary indented by a space or a tab, with the\n * body sharing a line with a boundary, and with a boundary preceded by anything\n * else on its line. Matching a boundary-shaped substring anywhere in the text\n * accepts all of them.\n *\n * WHAT IT STILL ADMITS, BECAUSE THE LIBRARY DOES. Text before the opening\n * boundary and after the closing one, a byte-order mark, CRLF endings, a missing\n * final newline, trailing blanks on any line, leading blanks on a BODY line, and\n * a body wrapped at any width or not wrapped at all. Refusing one of those would\n * be this same defect pointed the other way: a configuration the runtime accepts\n * turned into a listener that will not start.\n *\n * A blank line between the boundaries opens the encapsulated header section, and\n * the library refuses every header this option could not answer anyway, so a\n * blank line is admitted only where the header it opens is empty.\n *\n * @param text - The caller's input, decoded to text.\n * @returns The body with all whitespace removed, or `null`.\n */\nfunction readArmouredBody(text: string): string | null {\n let head = text;\n for (const bom of BOM_SPELLINGS) {\n if (head.startsWith(bom)) {\n head = head.slice(bom.length);\n break;\n }\n }\n\n // A line feed is the only line ending the reader knows; a lone carriage\n // return is an ordinary character, so a file that uses one is not lines.\n const lines = head.split(\"\\n\").map(sanitizeLine);\n const begin = lines.indexOf(BEGIN_BOUNDARY);\n if (begin === -1) return null;\n const end = lines.indexOf(END_BOUNDARY, begin + 1);\n if (end === -1) return null;\n\n const inner = lines.slice(begin + 1, end);\n const blank = inner.indexOf(\"\");\n if (blank !== -1) {\n // Everything before the first blank line is a header, and this option takes\n // no passphrase, so there is no header it could honour.\n if (blank !== 0) return null;\n // A second blank line would truncate the body the same way.\n if (inner.indexOf(\"\", 1) !== -1) return null;\n }\n\n const body = inner.join(\"\").replace(/\\s+/g, \"\");\n return body.length === 0 ? null : body;\n}\n\n/**\n * Whether `input` is a PKCS#3 Diffie-Hellman parameter block this package will\n * hand to a TLS context.\n *\n * The five refusals it exists for, in the order a caller hits them: content\n * that is not PEM at all, a PEM block of some other kind (a certificate, a key),\n * a `DH PARAMETERS` block whose encapsulation boundaries are not the ones the\n * TLS library reads a line at a time, one whose body is not the whole PKCS#3\n * template and only that template, and one whose two values are outside the\n * bounds the library will answer a key exchange with. The first four are\n * discarded in silence by the library itself and the fifth is loaded and then\n * refused at handshake time; from outside, both are a listener that advertises\n * a DHE suite and answers nothing, which is why all five are caught here.\n *\n * `'auto'` is deliberately **not** accepted: the automatic selection is reached\n * through `ServerTlsOptions.atnaTransportSecurity`, and a magic string on a\n * field documented as PEM content would be a second, undocumented spelling of\n * it.\n *\n * @param input - Candidate PEM content, as a string or as raw bytes.\n * @returns `true` when the block parses as PKCS#3 DH parameters.\n *\n * @example\n * ```typescript\n * isDhParametersPem(\"-----BEGIN CERTIFICATE-----\\nMIIB...\\n-----END CERTIFICATE-----\\n\");\n * // => false\n * ```\n */\nexport function isDhParametersPem(input: string | Buffer): boolean {\n // PEM armour is ASCII, so latin1 round-trips any byte without substituting\n // U+FFFD the way a utf8 decode of arbitrary bytes would.\n const text = typeof input === \"string\" ? input : input.toString(\"latin1\");\n const body = readArmouredBody(text);\n if (body === null) return false;\n\n // Checked BEFORE decoding: `Buffer.from(s, 'base64')` never throws and skips\n // whatever it cannot read, so a body that is not base64 at all decodes to a\n // shorter buffer that could still parse. The regexp is what refuses it.\n if (body.length % 4 !== 0 || !BASE64_BODY.test(body)) return false;\n\n const der = Buffer.from(body, \"base64\");\n const sequence = readDerElement(der, 0);\n if (sequence === null || sequence.tag !== DER_SEQUENCE) return false;\n // Trailing bytes after the SEQUENCE mean the block is not what it claims.\n // This line also carries the bound every field below relies on: the sequence\n // now ends exactly where the decoded bytes do, and `readDerElement` never\n // returns an element reaching past them, so no field inside it can overrun\n // the sequence and nothing below needs to re-check that.\n if (sequence.contentEnd !== der.length) return false;\n\n const prime = readDerElement(der, sequence.contentStart);\n if (prime === null || prime.tag !== DER_INTEGER) return false;\n const generator = readDerElement(der, prime.contentEnd);\n if (generator === null || generator.tag !== DER_INTEGER) return false;\n // An `INTEGER` carries at least one content octet; a required field with none\n // is not an encoding any generator produces, and it is the one place refusing\n // more than the library does costs nothing at all (see the module doc).\n if (prime.contentEnd === prime.contentStart) return false;\n if (generator.contentEnd === generator.contentStart) return false;\n // The values, not just the shape: a group outside these bounds is loaded by\n // the library and then refused at key-exchange time, which a caller cannot\n // tell from parameters it discarded outright.\n if (!isUsableGroup(der, prime, generator)) return false;\n // The two-field form: the template ends here, and so must the sequence.\n if (generator.contentEnd === sequence.contentEnd) return true;\n\n // The only thing the template allows behind the generator is one private-value\n // length. A `SEQUENCE` that runs on past it is not these parameters, and the\n // library reads it as no parameters at all rather than saying so.\n const privateValueLength = readDerElement(der, generator.contentEnd);\n if (privateValueLength === null || privateValueLength.tag !== DER_INTEGER) return false;\n if (privateValueLength.contentEnd !== sequence.contentEnd) return false;\n return readsAsInt32(der, privateValueLength);\n}\n","/**\n * The offered-cipher-suite policy behind `tls.atnaTransportSecurity`.\n *\n * Spec anchor: IHE ATNA, ITI-19 Authenticate Node\n * (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html), the \"STX: TLS 1.2\n * Floor using BCP195\" option, ITI TF-2 §3.19.6.2.3, which names four TLS 1.2\n * cipher suites an actor claiming that option supports, and allows\n * \"additional cipher suites of similar or greater cryptographic strength\".\n *\n * With the option off (the default) this module imposes nothing and the\n * offered list is the runtime's, which the runtime's own documentation says\n * a distribution may configure at build time and an operator may replace\n * wholesale from outside the process. With the option on, the offered list is\n * a property of this package instead, and what a link negotiated is\n * observable on the `'tlsNegotiated'` event.\n *\n * Two of the four suites are DHE, so the server half of the policy also settles\n * which ephemeral Diffie-Hellman group is in force: the caller's own, when\n * `ServerTlsOptions.dhParameters` names one, and otherwise the selection the\n * runtime makes for the certificate in use.\n *\n * @example\n * ```typescript\n * import { ATNA_CIPHER_SUITES } from '@cosyte/mllp';\n * console.log(ATNA_CIPHER_SUITES.length); // => 4\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createSecureContext } from \"node:tls\";\nimport {\n MllpTlsConfigurationError,\n MLLP_TLS_CIPHER_LIST_REJECTED,\n MLLP_TLS_CIPHER_OPTION_CONFLICT,\n MLLP_TLS_DH_PARAMETERS_REJECTED,\n} from \"./error.js\";\nimport { isDhParametersPem } from \"./dh-parameters.js\";\n\n/**\n * The four TLS 1.2 cipher suites ITI TF-2 §3.19.6.2.3 names, in the OpenSSL\n * spelling a cipher-list string is written in. Their IANA spellings, which are\n * what the standard prints and what the `'tlsNegotiated'` event reports, are\n * the same four prefixed `TLS_` and joined with `_WITH_`.\n *\n * @example\n * ```typescript\n * import { ATNA_CIPHER_SUITES } from '@cosyte/mllp';\n * console.log(ATNA_CIPHER_SUITES.includes('ECDHE-RSA-AES128-GCM-SHA256')); // => true\n * ```\n */\nexport const ATNA_CIPHER_SUITES: readonly string[] = Object.freeze([\n \"DHE-RSA-AES128-GCM-SHA256\",\n \"ECDHE-RSA-AES128-GCM-SHA256\",\n \"DHE-RSA-AES256-GCM-SHA384\",\n \"ECDHE-RSA-AES256-GCM-SHA384\",\n]);\n\n/**\n * The three TLS 1.3 cipher suites the runtime enables by default.\n *\n * They are carried alongside the four above for one reason: a TLS 1.3 suite is\n * enabled only by its full name in the cipher list, so a list holding the four\n * TLS 1.2 suites alone would turn TLS 1.3 **off**. Selecting the option must\n * never remove a protocol version that was reachable without it, so these are\n * restated rather than dropped.\n *\n * @example\n * ```typescript\n * import { TLS13_DEFAULT_CIPHER_SUITES } from '@cosyte/mllp';\n * console.log(TLS13_DEFAULT_CIPHER_SUITES.length); // => 3\n * ```\n */\nexport const TLS13_DEFAULT_CIPHER_SUITES: readonly string[] = Object.freeze([\n \"TLS_AES_256_GCM_SHA384\",\n \"TLS_CHACHA20_POLY1305_SHA256\",\n \"TLS_AES_128_GCM_SHA256\",\n]);\n\n/**\n * The OpenSSL cipher-list string `tls.atnaTransportSecurity: true` offers:\n * the three TLS 1.3 default suites followed by the four ITI TF-2 §3.19.6.2.3\n * suites, and nothing else.\n *\n * @example\n * ```typescript\n * import { ATNA_CIPHER_LIST } from '@cosyte/mllp';\n * console.log(ATNA_CIPHER_LIST.split(':').length); // => 7\n * ```\n */\nexport const ATNA_CIPHER_LIST: string = [\n ...TLS13_DEFAULT_CIPHER_SUITES,\n ...ATNA_CIPHER_SUITES,\n].join(\":\");\n\n/**\n * The cipher-suite half of a client's or server's TLS options, the two fields\n * that can declare what is offered.\n */\nexport interface TlsCipherPolicyInput {\n /** See `TlsOptions.atnaTransportSecurity`. */\n readonly atnaTransportSecurity?: boolean;\n /** See `TlsOptions.ciphers`. */\n readonly ciphers?: string;\n /**\n * See `ServerTlsOptions.dhParameters`. Diffie-Hellman parameters are supplied\n * by the end that answers the key exchange, so this is read when `side` is\n * `'server'` and **ignored, without an error, when `side` is `'client'`**.\n *\n * That asymmetry is stated rather than enforced because the field is\n * unreachable from the client option type in the first place: `TlsOptions`\n * carries no Diffie-Hellman parameter field at all, so a client value can\n * only arrive through a direct call to\n * {@link resolveTlsCipherPolicy}.\n */\n readonly dhParameters?: string | Buffer;\n}\n\n/**\n * The TLS-context fields the resolved policy contributes. Empty when the\n * option is off and no `ciphers` passthrough is set: this package then imposes\n * no cipher list at all, exactly as before the option existed.\n */\nexport interface ResolvedTlsCipherPolicy {\n /** OpenSSL cipher-list string, or absent to impose none. */\n readonly ciphers?: string;\n /**\n * Ephemeral Diffie-Hellman parameters, server-side only. Two of the four\n * named suites are DHE, and a server with no DH parameters cannot offer a\n * DHE suite at all: without this it would advertise the list and then fail\n * every DHE handshake in it.\n *\n * Exactly two things reach this field. The literal `'auto'` is the TLS\n * library's own spelling for the selection it makes from the certificate in\n * use, which is what `atnaTransportSecurity: true` alone yields. Anything\n * else is PEM content: the caller's own group, taken verbatim from\n * `ServerTlsOptions.dhParameters` and preferred over the automatic selection.\n * Absent when neither was asked for, so the not-selected path is untouched.\n *\n * Typed `string | Buffer` rather than `'auto' | string | Buffer` because the\n * literal is absorbed by `string` in a union and says nothing to a reader\n * there; it is stated here instead.\n */\n readonly dhparam?: string | Buffer;\n}\n\n/**\n * Prove the runtime will use the caller's own Diffie-Hellman parameters, or\n * refuse the configuration.\n *\n * TWO NETS, BECAUSE ONE OF THEM CANNOT SEE ITS OWN CASE. The TLS library\n * silently discards parameters it cannot read, so content that is not a\n * `DH PARAMETERS` block reaches `createSecureContext` without an error and\n * leaves a server that advertises DHE and answers no DHE handshake:\n * {@link isDhParametersPem} is the net for that, and for the values that parse\n * and then fail the library's own parameter check just as quietly at handshake\n * time. A block the library reads and whose group it refuses OUT LOUD (too\n * small, or below its configured security level) throws instead, and the second\n * net is that throw. Neither net covers the other's case.\n *\n * Validated with the parameters **alone**, no certificate, key or passphrase in\n * scope, so the error cannot carry credential material. The parameter bytes\n * themselves never reach the error either: the structural refusal carries no\n * `cause` at all, and the library's own refusals name a property of the group\n * rather than echoing it.\n *\n * @param dhParameters - PEM content the caller supplied.\n * @throws {MllpTlsConfigurationError} `MLLP_TLS_DH_PARAMETERS_REJECTED`.\n */\nfunction assertUsableDhParameters(dhParameters: string | Buffer): void {\n if (!isDhParametersPem(dhParameters)) {\n throw new MllpTlsConfigurationError(MLLP_TLS_DH_PARAMETERS_REJECTED);\n }\n try {\n createSecureContext({ dhparam: dhParameters });\n } catch (err) {\n throw new MllpTlsConfigurationError(MLLP_TLS_DH_PARAMETERS_REJECTED, {\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n }\n}\n\n/**\n * Resolve the offered-cipher-suite policy for one side of a connection, and\n * prove the runtime accepts it before any socket is opened.\n *\n * Refuses, rather than picking a winner, when both `atnaTransportSecurity` and\n * `ciphers` are set: each declares the offered list, and honouring one would\n * silently discard the other on a link whose whole point is being able to say\n * what it offered.\n *\n * **Diffie-Hellman parameters are the one place a winner IS picked, and the\n * caller wins.** `atnaTransportSecurity: true` selects a group automatically\n * because two of its four suites are DHE; a caller that also names its own\n * group is stating site policy, and site policy is more specific than a default\n * this package chose. So the two are not in conflict and the configuration is\n * accepted with the caller's group in force.\n *\n * Each input is validated **alone**, with no certificate, key or passphrase in\n * scope, so a rejection can only ever be about that input and the error it\n * raises cannot carry credential material.\n *\n * @param opts - The cipher-suite half of the caller's TLS options.\n * @param side - Which end of the connection is being configured.\n * @returns The TLS-context fields to spread into `tls.connect` / `tls.createServer`.\n * @throws {MllpTlsConfigurationError} On a conflicting declaration, a list the\n * runtime rejects, or Diffie-Hellman parameters it cannot use. Never falls back\n * to the runtime default list, and never to a server with no Diffie-Hellman\n * parameters.\n *\n * @example\n * ```typescript\n * import { resolveTlsCipherPolicy } from '@cosyte/mllp';\n * const policy = resolveTlsCipherPolicy({ atnaTransportSecurity: true }, 'server');\n * console.log(policy.dhparam); // => 'auto'\n * ```\n */\nexport function resolveTlsCipherPolicy(\n opts: TlsCipherPolicyInput,\n side: \"client\" | \"server\",\n): ResolvedTlsCipherPolicy {\n const atna = opts.atnaTransportSecurity === true;\n if (atna && opts.ciphers !== undefined) {\n throw new MllpTlsConfigurationError(MLLP_TLS_CIPHER_OPTION_CONFLICT);\n }\n const ciphers = atna ? ATNA_CIPHER_LIST : opts.ciphers;\n\n // Server side only. The client option type carries no such field, so this is\n // reachable only through a direct call to this function.\n const supplied = side === \"server\" ? opts.dhParameters : undefined;\n if (supplied !== undefined) assertUsableDhParameters(supplied);\n const dhparam: string | Buffer | undefined =\n supplied ?? (atna && side === \"server\" ? \"auto\" : undefined);\n\n if (ciphers !== undefined) {\n try {\n createSecureContext({ ciphers });\n } catch (err) {\n throw new MllpTlsConfigurationError(MLLP_TLS_CIPHER_LIST_REJECTED, {\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n if (ciphers === undefined) return dhparam === undefined ? {} : { dhparam };\n return dhparam === undefined ? { ciphers } : { ciphers, dhparam };\n}\n","/**\n * The `'tlsNegotiated'` event payload: what a completed TLS handshake actually\n * agreed on.\n *\n * A deployment that has declared the IHE ATNA ITI-19 transport-security option\n * on a link needs to be able to show, from its own logs, which protocol\n * version and which cipher suite that link negotiated. This is that record,\n * emitted once per completed handshake on both the client and the server.\n *\n * It is a **log line**, so it carries no field content: both values are read\n * from the TLS session and are names out of a closed registry, never anything\n * derived from the payload, the certificate, or the key.\n *\n * @example\n * ```typescript\n * import type { NegotiatedTlsParameters } from '@cosyte/mllp';\n * client.on('tlsNegotiated', (p: NegotiatedTlsParameters) => {\n * logger.info({ tls: p.protocolVersion, suite: p.cipherSuite });\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Socket } from \"node:net\";\nimport type { TLSSocket } from \"node:tls\";\n\n/**\n * Frozen payload of the `'tlsNegotiated'` event, emitted by both\n * {@link MllpClient} and {@link MllpServer} once per completed TLS handshake,\n * including every reconnect. Never emitted on a plaintext connection.\n *\n * Carries no HL7 payload content and no certificate or key material: only the\n * two negotiated names and the same routing metadata a `SecurityWarning`\n * carries. It is emitted at handshake-completion time, before any HL7 byte has\n * crossed the link.\n *\n * @example\n * ```typescript\n * server.on('tlsNegotiated', (p: NegotiatedTlsParameters) => {\n * if (p.protocolVersion !== 'TLSv1.3') metrics.increment('mllp.tls12_link');\n * });\n * ```\n */\nexport interface NegotiatedTlsParameters {\n /**\n * Negotiated protocol version as the TLS library reports it, e.g.\n * `'TLSv1.2'` or `'TLSv1.3'`.\n */\n readonly protocolVersion: string;\n /**\n * Negotiated cipher suite in its **IANA** spelling, e.g.\n * `'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'`. That is the spelling the IHE\n * and IETF documents print, so it can be compared with a conformance claim\n * directly. The OpenSSL spelling, which is what a `ciphers` list is written\n * in, is a different rendering of the same suite.\n */\n readonly cipherSuite: string;\n /** Host associated with the link (the target host for clients; the bind host for servers). */\n readonly host: string;\n /** Port associated with the link. */\n readonly port: number;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n\n/**\n * Read the negotiated parameters off a socket whose handshake has completed.\n *\n * Returns `null` for a socket that is not a TLS socket at all, and for a TLS\n * socket with no session to report. That is what keeps a plaintext connection\n * from producing an event: it is a structural property of the read, not a\n * caller-side condition anyone has to remember.\n *\n * @param socket - The socket whose handshake completed; plaintext is allowed and yields `null`.\n * @param host - Routing metadata for the payload's `host`.\n * @param port - Routing metadata for the payload's `port`.\n * @returns A frozen payload, or `null` when there is nothing negotiated to report.\n *\n * @example\n * ```typescript\n * const params = readNegotiatedTlsParameters(tlsSocket, 'mllp.example.com', 2575);\n * if (params !== null) logger.info({ suite: params.cipherSuite });\n * ```\n */\nexport function readNegotiatedTlsParameters(\n socket: Socket | TLSSocket,\n host: string,\n port: number,\n): NegotiatedTlsParameters | null {\n if (!(\"getProtocol\" in socket)) return null;\n const protocolVersion = socket.getProtocol();\n if (protocolVersion === null) return null;\n const cipher = socket.getCipher();\n // `standardName` is the IANA spelling; `name` is OpenSSL's. Both come from\n // the library's own suite registry, so neither can carry caller data.\n const cipherSuite = cipher.standardName === \"\" ? cipher.name : cipher.standardName;\n if (cipherSuite === \"\") return null;\n return Object.freeze({\n protocolVersion,\n cipherSuite,\n host,\n port,\n timestamp: new Date(),\n });\n}\n","/**\n * Typed error for socket-layer connection problems.\n *\n * @example\n * ```typescript\n * import { MllpConnectionError } from '@cosyte/mllp';\n * try {\n * await client.connect();\n * } catch (err) {\n * if (err instanceof MllpConnectionError) {\n * console.log(err.phase, err.cause);\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Connection lifecycle phase where the error occurred.\n *\n * All 5 phases are defined even though `'reconnect'` is only\n * exercised by the client. Locking the full union now prevents a\n * breaking type change later.\n */\nexport type ConnectionErrorPhase = \"connect\" | \"send\" | \"receive\" | \"close\" | \"reconnect\";\n\n/**\n * Stable cause codes for `MllpConnectionError`.\n *\n * These codes are a **public API**, they appear in error inspection\n * by callers, log pipelines, and monitoring dashboards. Renaming or\n * removing a member is a breaking change.\n *\n * - `'fifo-unsafe'`, A queued send was rejected during reconnect because\n * FIFO ordering cannot be safely resumed across sessions.\n * - `'in-flight-orphan'`, An in-flight send (already write-flushed,\n * ACK timer started) was rejected during reconnect in FIFO mode because\n * the at-most-once delivery contract cannot be preserved across a\n * socket drop (healthcare medication/orders semantics).\n * - `'tls-verify'`, The TLS handshake failed certificate\n * verification (untrusted chain, expired/not-yet-valid cert, hostname\n * mismatch, revocation, …). See {@link isTlsVerificationErrorCode} for the\n * exact underlying error codes. Classified **permanent** by\n * `isTransientConnectionError`, never auto-reconnect-looped into a\n * misconfigured or MITM'd endpoint.\n * - `'tls-handshake'`, A TLS-**protocol**-shaped handshake\n * failure observed before `'secureConnect'`: `ERR_SSL_*` codes, `EPROTO`,\n * or an OpenSSL alert-bearing error (protocol version mismatch, no shared\n * cipher, a required mutual-TLS client certificate rejected by the\n * server, …). See `isTlsProtocolError` for the boundary. Pure TCP-level\n * failures (`ECONNREFUSED`, `ETIMEDOUT`, …) on a TLS-configured\n * connection carry **no** `connectionCause`, the same shape as plaintext.\n *\n * Scope note: the `'tls-verify'`/`'tls-handshake'` values are attached on\n * the client's **initial `connect()` path**. Failures on the auto-reconnect\n * path surface as raw socket errors, their transient/permanent\n * classification still applies, but they do not (yet) carry a\n * `connectionCause`.\n *\n * @example\n * ```typescript\n * if (err instanceof MllpConnectionError && err.connectionCause === 'in-flight-orphan') {\n * // Treat as at-most-once: do NOT auto-retry; bubble up to caller for\n * // application-level dedupe / replay decisions.\n * }\n * ```\n */\nexport type ConnectionErrorCause =\n | \"fifo-unsafe\"\n | \"in-flight-orphan\"\n | \"tls-verify\"\n | \"tls-handshake\"\n | \"framing-fatal\";\n\n/**\n * Thrown (or emitted via `onError`) for socket-layer problems such as\n * connection refused, ECONNRESET, ETIMEDOUT, or DNS failure.\n *\n * - `cause`, the original OS or TLS error\n * - `phase`, which connection lifecycle phase the failure occurred in\n * - `connectionCause`, optional stable cause-code (e.g. for FIFO reconnect rejections)\n *\n * @example\n * ```typescript\n * throw new MllpConnectionError('Connection refused', {\n * cause: osError,\n * phase: 'connect',\n * });\n * ```\n */\nexport class MllpConnectionError extends Error {\n override readonly name = \"MllpConnectionError\" as const;\n\n /** The original OS or TLS error that caused this connection failure. */\n override readonly cause: Error;\n\n /** Which connection lifecycle phase the failure occurred in. */\n readonly phase: ConnectionErrorPhase;\n\n /**\n * Optional stable cause code. Present on FIFO reconnect rejections\n * (`'fifo-unsafe'` for queued sends, `'in-flight-orphan'` for in-flight sends).\n *\n * The set of values is a **public API**; see {@link ConnectionErrorCause}.\n */\n readonly connectionCause?: ConnectionErrorCause;\n\n /**\n * Construct an MLLP connection error.\n *\n * @param message - Human-readable error message.\n * @param opts - Error context (underlying `cause`, the lifecycle `phase`, optional stable cause code).\n */\n constructor(\n message: string,\n opts: {\n cause: Error;\n phase: ConnectionErrorPhase;\n connectionCause?: ConnectionErrorCause;\n },\n ) {\n super(message);\n this.cause = opts.cause;\n this.phase = opts.phase;\n if (opts.connectionCause !== undefined) {\n this.connectionCause = opts.connectionCause;\n }\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpConnectionError);\n }\n }\n}\n","/**\n * Connection, 6-state FSM over a Transport with lifecycle events, warning streams,\n * and getStats() observability.\n *\n * @example\n * ```typescript\n * import { Connection, NetTransport } from '@cosyte/mllp';\n * import { createConnection } from 'node:net';\n *\n * const socket = createConnection({ host: 'localhost', port: 2575 });\n * const transport = new NetTransport(socket);\n * const conn = new Connection({ transport });\n * conn.on('message', ({ payload }) => conn.send(ackBuffer));\n * conn.on('stateChange', ({ from, to }) => logger.info({ from, to }));\n * socket.on('connect', () => conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { EventEmitter } from \"node:events\";\nimport { randomUUID } from \"node:crypto\";\nimport type { Transport } from \"../transport/index.js\";\nimport { FrameReader, MllpFramingError } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning } from \"../framing/index.js\";\nimport { MllpConnectionError } from \"./error.js\";\nimport type { ConnectionErrorPhase } from \"./error.js\";\n\n/**\n * The 6 connection states.\n *\n * Transitions are validated against the legal-transition edge graph. Illegal transitions\n * are silently ignored to preserve FSM integrity.\n */\nexport type ConnectionState =\n | \"CONNECTING\"\n | \"CONNECTED\"\n | \"DRAINING\"\n | \"RECONNECTING\"\n | \"DISCONNECTED\"\n | \"CLOSED\";\n\n/**\n * Payload for the 'stateChange' event. Always `Object.freeze`'d.\n *\n * @example\n * ```typescript\n * conn.on('stateChange', ({ from, to, reason }) => {\n * logger.info({ from, to, reason });\n * });\n * ```\n */\nexport interface StateChangeEvent {\n readonly from: ConnectionState;\n readonly to: ConnectionState;\n readonly reason?: string;\n}\n\n/**\n * Payload for the 'reconnecting' event. Always `Object.freeze`'d.\n *\n * `attempt` and `delayMs` are populated by `MllpClient` when it schedules a\n * reconnect. A `Connection` emitting on its own supplies `connectionId` only.\n *\n * @example\n * ```typescript\n * conn.on('reconnecting', ({ connectionId }) => {\n * logger.info(`Reconnecting connection ${connectionId}`);\n * });\n * ```\n */\nexport interface ReconnectingEvent {\n readonly connectionId: string;\n readonly attempt?: number;\n readonly delayMs?: number;\n}\n\n/**\n * Return type of `connection.getStats()`, JSON-serializable.\n *\n * All timestamps are `Date | null` (not epoch milliseconds). `JSON.stringify()`\n * serialises them to ISO 8601 strings by default with no information loss.\n *\n * @example\n * ```typescript\n * const stats = conn.getStats();\n * logger.info(JSON.stringify(stats)); // safe: all values are JSON-serializable\n * ```\n */\nexport interface ConnectionStats {\n readonly state: ConnectionState;\n readonly connectionId: string;\n readonly remoteAddress: string | null;\n readonly remotePort: number | null;\n readonly warningsByCode: Record<string, number>;\n readonly bytesIn: number;\n readonly bytesOut: number;\n readonly lastByteInAt: Date | null;\n readonly lastByteOutAt: Date | null;\n readonly connectedAt: Date | null;\n readonly warningsTruncated: boolean;\n}\n\n/**\n * Options for constructing a {@link Connection}.\n *\n * @example\n * ```typescript\n * const opts: ConnectionOptions = {\n * transport: new NetTransport(socket),\n * onMessage: (payload) => handleMessage(payload),\n * onWarning: (w) => logger.warn(w),\n * drainTimeoutMs: 10_000,\n * };\n * ```\n */\nexport interface ConnectionOptions {\n /** The transport this Connection will drive. */\n transport: Transport;\n /** Called for each decoded MLLP frame (raw payload bytes, framing stripped). */\n onMessage?: (payload: Buffer) => void;\n /** Per-connection warning subscriber. Replaces previous subscription. */\n onWarning?: (w: MllpWarning) => void;\n /** Drain timeout used by close() (default: 30_000 ms). */\n drainTimeoutMs?: number;\n /** FrameReader options (tolerance, maxFrameSizeBytes). onFrame/onWarning are managed internally. */\n framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n}\n\n/** Warning ring buffer cap. */\nconst MAX_WARNINGS = 100;\n\n/**\n * Legal state transitions.\n *\n * CONNECTING → CONNECTED | RECONNECTING | CLOSED\n * CONNECTED → DRAINING | RECONNECTING | DISCONNECTED | CLOSED\n * DRAINING → DISCONNECTED | CLOSED\n * RECONNECTING → CONNECTING | CLOSED\n * DISCONNECTED → CLOSED\n * CLOSED → (terminal, no outgoing transitions)\n */\nconst LEGAL_TRANSITIONS: ReadonlyMap<ConnectionState, ReadonlySet<ConnectionState>> = new Map([\n [\"CONNECTING\", new Set<ConnectionState>([\"CONNECTED\", \"RECONNECTING\", \"CLOSED\"])],\n [\"CONNECTED\", new Set<ConnectionState>([\"DRAINING\", \"RECONNECTING\", \"DISCONNECTED\", \"CLOSED\"])],\n [\"DRAINING\", new Set<ConnectionState>([\"DISCONNECTED\", \"CLOSED\"])],\n [\"RECONNECTING\", new Set<ConnectionState>([\"CONNECTING\", \"CLOSED\"])],\n [\"DISCONNECTED\", new Set<ConnectionState>([\"CLOSED\"])],\n [\"CLOSED\", new Set<ConnectionState>()],\n]);\n\n/**\n * A single MLLP connection wrapping a {@link Transport} with a 6-state FSM,\n * lifecycle events, per-connection warning streams, and `getStats()` observability.\n *\n * Emits events: `stateChange`, `connect`, `disconnect`, `reconnecting`, `close`,\n * `message`, `warning`, `error`.\n *\n * @example\n * ```typescript\n * import { Connection, NetTransport } from '@cosyte/mllp';\n * import { createConnection } from 'node:net';\n *\n * const socket = createConnection({ host: 'localhost', port: 2575 });\n * const conn = new Connection({ transport: new NetTransport(socket) });\n * conn.on('stateChange', ({ from, to }) => console.log(from, '->', to));\n * conn.on('message', ({ payload }) => console.log('received', payload.length, 'bytes'));\n * socket.on('connect', () =>\n * conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null)\n * );\n * ```\n */\nexport class Connection extends EventEmitter {\n /** Stable UUIDv4 identifier for this connection. */\n readonly connectionId: string;\n\n private _state: ConnectionState = \"CONNECTING\";\n private readonly _transport: Transport;\n private readonly _reader: FrameReader;\n private readonly _opts: ConnectionOptions;\n\n private _remoteAddress: string | null = null;\n private _remotePort: number | null = null;\n private _connectedAt: Date | null = null;\n\n private _bytesIn = 0;\n private _bytesOut = 0;\n private _lastByteInAt: Date | null = null;\n private _lastByteOutAt: Date | null = null;\n\n /** Ring buffer: last MAX_WARNINGS entries. */\n private _warningBuffer: MllpWarning[] = [];\n /** Accurate total counts per code, unaffected by ring buffer truncation. */\n private readonly _warningsByCode: Map<string, number> = new Map();\n /** Set to true once the ring buffer overflows. */\n private _warningsTruncated = false;\n\n private _onWarningFn: ((w: MllpWarning) => void) | null = null;\n private _drainPromise: Promise<void> | null = null;\n\n /**\n * beforeClose hook, no-op default that resolves immediately.\n *\n * An owner may replace this instance property to run work before the socket\n * closes. Connection calls it during `close()`, racing against `drainTimeoutMs`.\n */\n beforeClose: (drainTimeoutMs: number) => Promise<void> = () => Promise.resolve();\n\n /**\n * Construct a connection over the given transport. Starts in `CONNECTING`; call\n * {@link Connection.notifyConnect} once the transport handshake completes.\n *\n * @param opts - Connection options (the transport, message/warning handlers, drain timeout, framing).\n */\n constructor(opts: ConnectionOptions) {\n super();\n this.connectionId = randomUUID();\n this._opts = opts;\n this._transport = opts.transport;\n\n if (opts.onWarning !== undefined) {\n this._onWarningFn = opts.onWarning;\n }\n\n this._reader = new FrameReader({\n ...(opts.framing ?? {}),\n onFrame: (payload, byteOffset, warnings) => {\n this._onFrameDecoded(payload, byteOffset, warnings);\n },\n onWarning: (w) => {\n this._onFramingWarning(w);\n },\n });\n\n // Wire transport callbacks\n this._transport.onData((chunk) => {\n this._bytesIn += chunk.length;\n this._lastByteInAt = new Date();\n try {\n this._reader.push(chunk);\n } catch (err) {\n // Nothing may escape the transport's data callback. On a real socket this callback IS\n // the `'data'` listener, so a throw here becomes an uncaught exception that kills the\n // entire process, taking every OTHER connection and every in-flight durable commit\n // with it. One junk byte from one misbehaving peer must not be able to do that.\n //\n // Reachable on a DEFAULT server: `SERVER_DEFAULT_FRAMING` leaves `allowMissingLeadingVt`\n // off, so any non-whitespace byte where a VT was expected throws MLLP_MISSING_LEADING_VT.\n // `MLLP_FRAME_TOO_LARGE` reaches here too.\n //\n // The two classes are kept DISTINCT on purpose. A framing error is the peer's fault; a\n // subscriber bug is ours. Reporting the second as the first would tell an operator the\n // peer sent bad bytes when in fact our own handler threw.\n if (err instanceof MllpFramingError) {\n this._onFatalFramingError(err);\n } else {\n this._onInternalReceiveError(err instanceof Error ? err : new Error(String(err)));\n }\n }\n });\n this._transport.onClose(() => {\n this._onTransportClose();\n });\n this._transport.onError((err) => {\n this._onTransportError(err);\n });\n }\n\n /**\n * Current FSM state. One of the 6 `ConnectionState` values.\n *\n * Subscribe to `'stateChange'` events for reactive state monitoring.\n */\n get state(): ConnectionState {\n return this._state;\n }\n\n /**\n * Register or replace the per-connection warning subscriber.\n *\n * Subsequent calls replace the previous handler (set-once semantics prevent leaks).\n * The handler is called synchronously and any exception it throws is swallowed.\n *\n * @example\n * ```typescript\n * conn.onWarning((w) => logger.warn({ code: w.code, connectionId: w.connectionId }));\n * ```\n */\n onWarning(fn: (w: MllpWarning) => void): void {\n this._onWarningFn = fn;\n }\n\n /**\n * Notify the connection that the transport handshake has completed.\n *\n * Transitions `CONNECTING → CONNECTED` and emits the `'connect'` event.\n * Called externally by Server/Client after the socket connects.\n *\n * @param remoteAddress - Remote peer IP address, or `null` if unavailable.\n * @param remotePort - Remote peer port, or `null` if unavailable.\n *\n * @example\n * ```typescript\n * socket.on('connect', () =>\n * conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null)\n * );\n * ```\n */\n notifyConnect(remoteAddress: string | null, remotePort: number | null): void {\n this._remoteAddress = remoteAddress;\n this._remotePort = remotePort;\n this._connectedAt = new Date();\n this._transition(\"CONNECTED\");\n // Contained like every other emit in this class, `notifyConnect` is called from the\n // transport's connect callback, so a throwing subscriber would unwind into it.\n this._dispatchContained(() => {\n this.emit(\"connect\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n\n /**\n * Write raw bytes to the transport (no framing applied).\n *\n * The server and the client wrap this with `encodeFrame()` to add MLLP framing.\n * Returns `false` if the connection is CLOSED or DISCONNECTED (no bytes written).\n *\n * @returns `true` if bytes flushed immediately; `false` if buffered (backpressure) or not writable.\n *\n * @example\n * ```typescript\n * const flushed = conn.send(encodeFrame(payload));\n * if (!flushed) logger.warn('backpressure detected');\n * ```\n */\n send(data: Buffer): boolean {\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return false;\n const ok = this._transport.write(data);\n this._bytesOut += data.length;\n this._lastByteOutAt = new Date();\n return ok;\n }\n\n /**\n * Initiate graceful close of the connection.\n *\n * - If `CONNECTING` or `RECONNECTING`: cancels the pending attempt, transitions\n * directly to `CLOSED`, and calls `transport.destroy()`.\n * - If `CONNECTED`: transitions to `DRAINING`, calls `beforeClose(drainTimeoutMs)`,\n * then `DRAINING → DISCONNECTED` once the hook resolves, or `DRAINING → CLOSED`\n * if the drain timeout elapses first.\n * - If `DRAINING`: waits for the existing drain to complete (idempotent).\n * - If `CLOSED` or `DISCONNECTED`: no-op.\n *\n * @param opts.drainTimeoutMs - Override drain timeout (default: `opts.drainTimeoutMs ?? 30_000`).\n *\n * @example\n * ```typescript\n * await conn.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number }): Promise<void> {\n const timeout = opts?.drainTimeoutMs ?? this._opts.drainTimeoutMs ?? 30_000;\n\n // Terminal states, nothing to do\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return;\n\n // CONNECTING or RECONNECTING, cancel the pending attempt (LIFE-05)\n if (this._state === \"CONNECTING\" || this._state === \"RECONNECTING\") {\n this._transition(\"CLOSED\", \"close() during \" + this._state);\n this._transport.destroy();\n return;\n }\n\n // DRAINING already, join the in-progress drain rather than starting a second beforeClose call\n if (this._state === \"DRAINING\") {\n return this._drainPromise ?? Promise.resolve();\n }\n\n // CONNECTED → DRAINING\n this._transition(\"DRAINING\");\n this._drainPromise = this._drainWithTimeout(timeout).finally(() => {\n this._drainPromise = null;\n });\n return this._drainPromise;\n }\n\n /**\n * Race `beforeClose()` against the drain timeout.\n *\n * On normal completion: `DRAINING → DISCONNECTED` + `transport.close()`.\n * On timeout: `DRAINING → CLOSED` + `transport.destroy()`.\n */\n private async _drainWithTimeout(timeoutMs: number): Promise<void> {\n const drainPromise = this.beforeClose(timeoutMs);\n const timeoutPromise = new Promise<\"timeout\">((resolve) => {\n const handle = setTimeout(() => {\n resolve(\"timeout\");\n }, timeoutMs);\n // Unref so this timer does not keep the process alive (T-03-04-01)\n handle.unref();\n });\n\n const result = await Promise.race([drainPromise.then(() => \"done\" as const), timeoutPromise]);\n\n if (result === \"timeout\") {\n // Drain timed out, force to CLOSED (DRAINING → CLOSED per LIFE-02)\n if (this._state === \"DRAINING\") {\n this._transition(\"CLOSED\", \"drain timeout\");\n this._transport.destroy();\n }\n } else {\n // Drain completed, DRAINING → DISCONNECTED (LIFE-02)\n if (this._state === \"DRAINING\") {\n this._transition(\"DISCONNECTED\");\n this._transport.close();\n }\n }\n }\n\n /**\n * Abruptly destroy the connection, discarding any pending writes.\n *\n * Transitions from any non-terminal state directly to `CLOSED` and calls\n * `transport.destroy(reason)`. Emits `'close'` event. Idempotent, safe to\n * call multiple times.\n *\n * @param reason - Optional error to propagate to the transport.\n *\n * @example\n * ```typescript\n * conn.destroy(new Error('Timeout exceeded'));\n * ```\n */\n destroy(reason?: Error): void {\n if (this._state === \"CLOSED\") return;\n this._transition(\"CLOSED\", reason?.message ?? \"destroy()\");\n this._transport.destroy(reason);\n }\n\n /**\n * Return a JSON-serializable observability snapshot.\n *\n * All timestamp fields are `Date | null` (not epoch milliseconds). `JSON.stringify()`\n * serialises them to ISO 8601 strings with no information loss.\n *\n * `warningsByCode` reflects every warning ever received regardless of ring buffer truncation.\n * `warningsTruncated` is `true` if the 100-entry ring buffer has overflowed.\n *\n * @example\n * ```typescript\n * const stats = conn.getStats();\n * console.log(JSON.stringify(stats)); // log-pipeline friendly\n * ```\n */\n getStats(): ConnectionStats {\n const warningsByCode: Record<string, number> = {};\n for (const [code, count] of this._warningsByCode) {\n warningsByCode[code] = count;\n }\n return {\n state: this._state,\n connectionId: this.connectionId,\n remoteAddress: this._remoteAddress,\n remotePort: this._remotePort,\n warningsByCode,\n bytesIn: this._bytesIn,\n bytesOut: this._bytesOut,\n lastByteInAt: this._lastByteInAt,\n lastByteOutAt: this._lastByteOutAt,\n connectedAt: this._connectedAt,\n warningsTruncated: this._warningsTruncated,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Private FSM helpers\n // ---------------------------------------------------------------------------\n\n private _transition(to: ConnectionState, reason?: string): void {\n const from = this._state;\n const legal = LEGAL_TRANSITIONS.get(from);\n if (legal === undefined || !legal.has(to)) {\n // Illegal transition, silently ignore to preserve FSM integrity (T-03-03-04)\n return;\n }\n // The state is committed BEFORE any subscriber runs, so a throwing subscriber can never leave\n // the FSM half-transitioned.\n this._state = to;\n const event = Object.freeze<StateChangeEvent>(\n reason !== undefined ? { from, to, reason } : { from, to },\n );\n\n // Every lifecycle emit is contained. `_transition` is reached from `destroy()`, which is\n // called from inside the `catch` block on the receive path, and a throw raised inside a catch\n // block is NOT caught by that block. A throwing 'stateChange'/'close' subscriber would\n // therefore unwind straight out of the socket's 'data' listener and kill the process, which is\n // the same defect as the decoder throw, just four frames up. Contain at the source: no emit in\n // this class is allowed to reach a transport callback.\n this._dispatchContained(() => {\n this.emit(\"stateChange\", event);\n });\n\n // Fire semantic lifecycle events\n if (to === \"DISCONNECTED\") {\n this._dispatchContained(() => {\n this.emit(\"disconnect\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n if (to === \"RECONNECTING\") {\n this._dispatchContained(() => {\n this.emit(\"reconnecting\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n if (to === \"CLOSED\") {\n this._dispatchContained(() => {\n this.emit(\"close\", Object.freeze({ connectionId: this.connectionId }));\n });\n }\n }\n\n private _onTransportClose(): void {\n if (this._state === \"DRAINING\") {\n // Transport closed naturally during graceful drain, complete the close\n this._transition(\"DISCONNECTED\");\n return;\n }\n if (this._state === \"CONNECTED\") {\n this._transition(\"DISCONNECTED\", \"peer closed\");\n return;\n }\n if (this._state === \"CONNECTING\" || this._state === \"RECONNECTING\") {\n // Neither CONNECTING nor RECONNECTING has a path to DISCONNECTED.\n // Use CLOSED (terminal) for unexpected peer close here.\n this._transition(\"CLOSED\", \"peer closed\");\n }\n }\n\n /**\n * Emit on `'error'` only when a listener is attached.\n *\n * Node throws `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an `EventEmitter` with no\n * listener. Every `'error'` emission in this class is reached from inside a transport callback\n * (the socket's `'data'`/`'error'` listener), so an unguarded emit would become an **uncaught\n * exception that kills the process**, the exact failure the containment above exists to\n * prevent, merely relocated one frame up the stack.\n *\n * `Connection` is a public export and can legitimately be driven with no `'error'` listener\n * (`MllpServer` and `MllpClient` each attach one; a bare `Connection` need not). The connection\n * is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind,\n * they simply do not get the typed error object they never asked for. Mirrors\n * `MllpServer._emitErrorIfListened`.\n */\n private _emitErrorIfListened(error: MllpConnectionError): void {\n if (this.listenerCount(\"error\") === 0) return;\n try {\n this.emit(\"error\", Object.freeze({ connectionId: this.connectionId, error }));\n } catch {\n // A throwing `'error'` subscriber must not escape either. This method is called from inside\n // socket callbacks AND from inside catch blocks, so a throw here would unwind straight out of\n // the socket handler, the same process kill, reached by a third route. This is the one place\n // a throw is deliberately swallowed: reporting is what just failed, so there is nowhere left\n // to report it to.\n }\n }\n\n /**\n * Handle a fatal framing error thrown out of `FrameReader.push` (FRAME-FATAL).\n *\n * Contract: surface it as a connection `'error'` (frozen payload, `phase: 'receive'`,\n * `connectionCause: 'framing-fatal'`, the original `MllpFramingError` preserved as `cause` so\n * consumers keep the stable `code` and `byteOffset`), then **destroy this connection only**.\n *\n * Why destroy rather than resynchronize: once `push` has thrown, the reader's position within\n * the byte stream is no longer trustworthy, the next bytes could be the middle of a message.\n * Guessing where the next frame starts is how a clinical message gets silently mis-split, so\n * the connection is dropped instead. A server drops that one peer and keeps serving everyone\n * else.\n *\n * `'framing-fatal'` is classified **permanent** by `isTransientConnectionError`, so a client\n * does **not** auto-reconnect into it. That matters: a peer speaking something that is not MLLP\n * (an HTTP probe, a health check, a wrong-port misconfiguration) would otherwise be retried\n * forever, and the backoff would become an unbounded reconnect storm against an interface\n * engine that is already unwell. A compatibility failure is not a network blip.\n *\n * If a peer's quirk is *expected* (trailing junk, keepalive bytes, a missing leading VT), the\n * decoder's tolerance opt-ins are the supported answer, they turn the throw into a warning.\n */\n private _onFatalFramingError(err: MllpFramingError): void {\n if (this._state === \"CLOSED\") return;\n this._emitErrorIfListened(\n new MllpConnectionError(err.message, {\n cause: err,\n phase: \"receive\",\n connectionCause: \"framing-fatal\",\n }),\n );\n this.destroy(err);\n }\n\n /**\n * Backstop for a NON-framing throw escaping `FrameReader.push`, i.e. our own bug, or a\n * subscriber's, rather than the peer's bytes.\n *\n * `_onFrameDecoded` already contains subscriber throws at the dispatch site, so this should be\n * unreachable. It exists because \"unreachable\" and \"cannot kill the process from inside a\n * socket `'data'` handler\" are different claims, and only the second one is safe to bet a\n * clinical interface on. Reported honestly as an internal receive error, never dressed up as\n * a peer framing fault.\n */\n private _onInternalReceiveError(err: Error): void {\n if (this._state === \"CLOSED\") return;\n this._emitErrorIfListened(\n new MllpConnectionError(err.message, { cause: err, phase: \"receive\" }),\n );\n this.destroy(err);\n }\n\n private _onTransportError(err: Error): void {\n const phase: ConnectionErrorPhase =\n this._state === \"CONNECTING\"\n ? \"connect\"\n : this._state === \"RECONNECTING\"\n ? \"reconnect\"\n : this._state === \"DRAINING\"\n ? \"close\"\n : \"receive\";\n\n // Terminal states first. `destroy(err)` forwards the reason to `transport.destroy(err)`, which\n // makes the socket re-surface the same error here, so emitting before this guard reported a\n // single fatal framing error TWICE: once with `connectionCause: 'framing-fatal'`, then again\n // bare. That double-counts on an alerting dashboard and the echo carries no cause.\n if (this._state === \"CLOSED\" || this._state === \"DISCONNECTED\") return;\n\n // Guarded: this runs inside the transport's error callback, so an unlistened emit would throw\n // ERR_UNHANDLED_ERROR straight out of the socket's 'error' listener and kill the process. A\n // bare Connection (no 'error' listener) previously died on an ordinary ECONNRESET.\n this._emitErrorIfListened(new MllpConnectionError(err.message, { cause: err, phase }));\n\n // CONNECTING and RECONNECTING have no path to DISCONNECTED, use CLOSED\n const target: ConnectionState =\n this._state === \"CONNECTING\" || this._state === \"RECONNECTING\" ? \"CLOSED\" : \"DISCONNECTED\";\n this._transition(target, `error: ${err.message}`);\n }\n\n private _onFrameDecoded(\n payload: Buffer,\n byteOffset: number,\n warnings: readonly MllpWarning[],\n ): void {\n // Only deliver messages when in an active state (CONNECTED or DRAINING)\n if (this._state !== \"CONNECTED\" && this._state !== \"DRAINING\") return;\n const event = Object.freeze({ payload, connectionId: this.connectionId, byteOffset, warnings });\n\n // `onFrame` is dispatched SYNCHRONOUSLY from inside `FrameReader.push()`, which is itself\n // called from the transport's data callback. A throwing subscriber, a metrics tap, a logger,\n // an ordinary consumer bug, would therefore unwind through `push()` and out of the socket's\n // `'data'` listener, killing the process, and on the way it would suppress the ACK for a\n // message the application may already have handled.\n //\n // Contain each subscriber independently (the WARN-06 pattern already used for warnings): one\n // bad subscriber cannot silence the other, cannot suppress the ACK, and cannot be mistaken\n // for a peer framing fault.\n this._dispatchContained(() => {\n this.emit(\"message\", event);\n });\n this._dispatchContained(() => {\n this._opts.onMessage?.(payload);\n });\n }\n\n /**\n * Run a subscriber callback, containing any throw as a connection `'error'` rather than letting\n * it unwind into the socket callback that (transitively) invoked us.\n */\n private _dispatchContained(fn: () => void): void {\n try {\n fn();\n } catch (err) {\n const cause = err instanceof Error ? err : new Error(String(err));\n this._emitErrorIfListened(\n new MllpConnectionError(cause.message, { cause, phase: \"receive\" }),\n );\n }\n }\n\n private _onFramingWarning(w: MllpWarning): void {\n // Enrich with connectionId (D-09) and re-freeze\n const enriched = Object.freeze<MllpWarning>({ ...w, connectionId: this.connectionId });\n\n // Always update the count map, accurate regardless of ring buffer truncation (OBS-05)\n this._warningsByCode.set(enriched.code, (this._warningsByCode.get(enriched.code) ?? 0) + 1);\n\n // Ring buffer: keep last MAX_WARNINGS entries (OBS-05)\n if (this._warningBuffer.length >= MAX_WARNINGS) {\n this._warningBuffer.shift(); // remove oldest\n this._warningsTruncated = true;\n }\n this._warningBuffer.push(enriched);\n\n // Per-connection subscriber, swallow exceptions (WARN-06)\n if (this._onWarningFn !== null) {\n try {\n this._onWarningFn(enriched);\n } catch {\n // WARN-06: throwing handler must not disrupt frame processing\n }\n }\n\n // EventEmitter broadcast (aggregate warning stream).\n //\n // Contained for the same reason as the per-connection subscriber above: this runs synchronously\n // inside `FrameReader.push()`, itself inside the socket's 'data' callback, so a throwing\n // 'warning' listener would unwind out of the data handler and kill the process. WARN-06 already\n // promises a throwing warning handler \"must not disrupt frame processing\", it was only half\n // honored (the `onWarning` option was guarded; the event broadcast was not).\n this._dispatchContained(() => {\n this.emit(\"warning\", enriched);\n });\n }\n}\n","/**\n * Byte-level MSH-10 / MSA-2 control-ID scanners, the **single** implementation\n * of \"read the control ID off the wire\", shared by everything in this package\n * that has to agree on what a control ID *is*.\n *\n * Three call sites depend on these bytes agreeing, and a disagreement between\n * any two of them is a lost clinical message:\n *\n * * `src/client/correlator.ts` keys its in-flight store by the MSH-10 it\n * extracts from the **outbound** frame, and looks an inbound ACK up by the\n * MSA-2 it extracts from the **ACK** frame.\n * * `src/server/ack.ts` (`buildRawAck`) echoes the inbound MSH-10 into MSA-2.\n * * `src/ack-from-hl7/build.ts` verifies, against these same scanners, that\n * the ACK it hands back really does echo MSH-10 verbatim.\n *\n * They live here, not in `client/`, because none of that is client policy.\n * It is the package's byte-level reading of HL7 v2.5.1 §2.5.4 (MSH-1 *defines*\n * the field separator) and §2.9.2.2 (MSA-2 echoes MSH-10 verbatim).\n *\n * **INTERNAL**, not part of the public API.\n *\n * @packageDocumentation\n */\n\nconst ASCII_M = 0x4d;\nconst ASCII_S = 0x53;\nconst ASCII_H = 0x48;\nconst ASCII_A = 0x41;\nconst ASCII_B = 0x42;\nconst ASCII_F = 0x46;\nconst ASCII_T = 0x54;\nconst SEGMENT_SEPARATOR_CR = 0x0d;\nconst SEGMENT_SEPARATOR_LF = 0x0a;\n\n/**\n * Decode used for MSH-10 / MSA-2 control-ID extraction: `latin1`, NOT `ascii`.\n *\n * Node's `ascii` codec masks the high bit (`byte & 0x7f`), so it is a **lossy**\n * decode of the raw HL7 v2 bytes. Two consequences, both of which land squarely\n * on ACK correlation:\n *\n * 1. **Key collision.** The correlator keys its live store, its graveyard, and\n * its ACK lookups by the extracted control-ID *string*. Under `ascii`, the\n * distinct control IDs `A\\x8B` and `A\\x0B` both decode to `A\\x0B`, so two\n * concurrently in-flight sends collapse onto one key: the second `enqueue()`\n * overwrites the first in the `Map`, and the first send's promise can never\n * be settled by its own ACK. `latin1` is a 1:1 byte↔code-unit mapping, so\n * every distinct byte string stays a distinct key.\n * 2. **Corrupted observability.** The extracted ID is what the correlator keys\n * its graveyard on, so a masked ID is a control ID that never existed on the\n * wire: a late ACK for the real one finds no graveyard entry and is reported\n * as unmatched, misdirecting exactly the operator who is trying to trace a\n * lost message. (The diagnostics themselves report the ID's byte length and\n * never the ID, so a masked ID cannot reach a log line; a masked KEY still\n * breaks correlation.)\n *\n * Reachable whenever MSH-18 declares a non-ASCII charset (e.g. `8859/1`), where\n * high-bit bytes in a control ID are legal.\n *\n * **`latin1` is the only byte-faithful decode available.** It is not merely the\n * best of several, it is the one that exists. Node's `Buffer` `latin1` codec is\n * true ISO-8859-1: a 1:1 map between the 256 byte values and U+0000–U+00FF, so\n * `Buffer.from(buf.toString(\"latin1\"), \"latin1\")` is the identity for *every*\n * byte string. Every alternative loses bytes in exactly the range that matters:\n *\n * * `ascii` masks the high bit (above).\n * * `utf8` folds every invalid sequence onto `U+FFFD`, so all high-bit bytes in\n * a non-UTF-8 payload collapse onto one key, the same collision as `ascii`.\n * * `TextDecoder(\"iso-8859-1\")`, which is what a charset-driven decode such as\n * `@cosyte/hl7`'s `parseHL7(buffer, { charset })` uses, is **not** ISO-8859-1\n * at all. The WHATWG Encoding Standard aliases the label `iso-8859-1` to\n * **windows-1252**, which maps 0x80–0x9F to typography (`0x8B` → `U+2039`,\n * `0x9C` → `U+0153`) rather than to `U+008B`/`U+009C`. Re-encoding that back\n * to bytes does not round-trip. That is precisely the byte range a high-bit\n * control ID lives in, which is why this package decodes control-ID bytes\n * itself rather than delegating to a charset-aware parser.\n *\n * The key is byte-faithful, not text: under a multi-byte charset (`UNICODE UTF-8`)\n * a control ID reads back as its `latin1` bytes. Correlation stays correct, the\n * map is injective for any charset, which is the property the key needs, and the\n * one a charset-driven decode would lose.\n *\n * @internal\n */\nexport const CONTROL_ID_ENCODING = \"latin1\" as const;\n\n/**\n * MSH-1 values we refuse to read a message with.\n *\n * MSH-1 *is* the field separator (§2.5.4), but a segment terminator (`CR`/`LF`) or\n * an MLLP framing byte (`VT`/`FS`) cannot be one: the first would end the segment it\n * is supposed to delimit, and the second cannot be written back into an ACK without\n * making the ACK unframeable (strict `encodeFrame` throws `MLLP_PAYLOAD_CONTAINS_VT`/`_FS`).\n *\n * **How a payload with such a byte reaches us.** The plainest route is that `buildRawAck` is\n * a **public export**, so a caller can hand any `Buffer` straight to it, decoder or no\n * decoder; these scanners also run on **outbound** payloads the caller supplies. But it can\n * come off the wire too: a `VT` mid-payload makes `FrameReader` discard what it accumulated\n * and start over (`MLLP_TRAILING_BYTES`), so a *delivered* payload never contains a `VT`, but\n * a delivered payload CAN contain an `FS`. Under the `allowMissingLeadingVt` tolerance a non-VT,\n * non-whitespace first byte is taken as payload byte 0, and `FS` (0x1C) is neither, so\n * `FS \"MSH…\" FS CR` delivers a payload whose byte 0 is `0x1C`. Either way the guard is\n * warranted, and neither route depends on the false premise that the decoder screens these out.\n * @internal\n */\nconst UNSAFE_FIELD_SEPARATORS: ReadonlySet<number> = new Set([\n SEGMENT_SEPARATOR_CR,\n SEGMENT_SEPARATOR_LF,\n 0x0b, // VT, MLLP start block\n 0x1c, // FS, MLLP end block\n]);\n\n/** True iff the `CR`/`LF`-delimited segment starting at `i` is an `MSH`. @internal */\nfunction isMshSegmentAt(buf: Buffer, i: number): boolean {\n return (\n i + 3 < buf.length &&\n buf[i] === ASCII_M &&\n buf[i + 1] === ASCII_S &&\n buf[i + 2] === ASCII_H &&\n !UNSAFE_FIELD_SEPARATORS.has(buf[i + 3] as number)\n );\n}\n\n/** Index of the byte after the `CR`/`LF`-delimited segment starting at `i`. @internal */\nfunction segmentEnd(buf: Buffer, i: number): number {\n let end = i;\n while (\n end < buf.length &&\n buf[end] !== SEGMENT_SEPARATOR_CR &&\n buf[end] !== SEGMENT_SEPARATOR_LF\n ) {\n end++;\n }\n return end;\n}\n\n/**\n * Locate the message's `MSH` segment: the **first** `CR`/`LF`-delimited segment whose\n * first three bytes are `MSH` and whose 4th byte is a usable field separator. Returns\n * its `[start, end)` bounds, or `null` if the payload has no such segment.\n *\n * ## Why we search rather than demand `MSH` at byte 0\n *\n * §2.5.1 does make `MSH` the first segment of a *message*, and it is tempting to read\n * that as \"byte 0 or it is not HL7\", that is what an earlier version of this module\n * did, to force its three consumers into agreement. It was a **tolerance regression**,\n * and it caused the exact harm this module exists to prevent. Two shapes reach us with\n * a perfectly good MSH-10 that is not at byte 0:\n *\n * * **A leading `CR`.** The MLLP decoder passes it straight through into the payload\n * (it special-cases only `VT`/`FS`), and real senders emit it.\n * * **A batch header.** `FHS`/`BHS` precede the `MSH` (§2.10.3).\n *\n * Under the byte-0 rule both read as \"no MSH\", so `buildRawAck` emitted a **positive\n * `AA` with an empty MSA-2 and no warning**: the sender, which keyed on the MSH-10 it\n * sent, cannot correlate it, times out, resends, and the receiver commits a\n * **duplicate clinical message**. Silently discarding a field that is *present* is the\n * worst thing a lenient reader can do, and this package's decoder is emphatically\n * lenient (Postel's Law; see CLAUDE.md).\n *\n * The consumers still agree, they simply agree at the **tolerant** fixed point rather\n * than the lossy one. Agreement was never the hard part; agreeing on the *right* answer\n * is.\n * @internal\n */\nfunction findMshSegment(buf: Buffer): { start: number; end: number } | null {\n let segStart = 0;\n while (segStart < buf.length) {\n const end = segmentEnd(buf, segStart);\n if (isMshSegmentAt(buf, segStart)) return { start: segStart, end };\n // Advance past this segment's terminator bytes (handles `CRLF` as one break).\n let next = end;\n while (\n next < buf.length &&\n (buf[next] === SEGMENT_SEPARATOR_CR || buf[next] === SEGMENT_SEPARATOR_LF)\n ) {\n next++;\n }\n if (next === segStart) return null; // no progress, malformed\n segStart = next;\n }\n return null;\n}\n\n/**\n * True iff the `CR`/`LF`-delimited segment starting at `i` is an HL7 **batch/file\n * envelope** header, `FHS`, `BHS`, `BTS`, or `FTS` (§2.10.3). Matched on the\n * three uppercase ASCII segment-ID bytes only; the 4th byte (the separator) is not\n * inspected, because the mere *presence* of a batch segment is what disqualifies a\n * single-message ACK, regardless of that segment's own delimiter. @internal\n */\nfunction isBatchEnvelopeSegmentAt(buf: Buffer, i: number): boolean {\n if (i + 2 >= buf.length) return false;\n const a = buf[i] as number;\n const b = buf[i + 1] as number;\n const c = buf[i + 2] as number;\n // FHS / FTS\n if (a === ASCII_F && ((b === ASCII_H && c === ASCII_S) || (b === ASCII_T && c === ASCII_S))) {\n return true;\n }\n // BHS / BTS\n if (a === ASCII_B && ((b === ASCII_H && c === ASCII_S) || (b === ASCII_T && c === ASCII_S))) {\n return true;\n }\n return false;\n}\n\n/**\n * True iff the payload is **not a single bare HL7 message**, it carries a batch/file\n * envelope (`FHS`/`BHS`/`BTS`/`FTS`, §2.10.3) or a **second `MSH` segment** (two or more\n * messages concatenated in one frame, a documented real-world quirk).\n *\n * ## Why this is a fail-safe refusal signal, not tolerance\n *\n * A single MSA-2 can echo exactly **one** MSH-10. A batch is a *sequence* of messages with\n * a `BTS-1` count; a concatenated frame is N messages back to back. Either way, a positive\n * `AA` that names the first control ID silently leaves messages 2..N unacknowledged, the\n * sender reads \"batch accepted\", so they are lost outright or time out and resend as\n * **duplicate clinical messages**. This predicate lets the ACK builders **downgrade to a\n * loud non-positive answer** for such a payload rather than fabricate a positive\n * disposition for messages nobody looked at.\n *\n * It does **not** parse the batch, re-base on the first `MSH`, or otherwise widen any\n * reader, it only *detects* the multi-message shape so callers can refuse it. Batch ACK\n * is its own feature; this is the refusal that keeps the door shut until it\n * is designed. A normal single message (`MSH` followed by `PID`/`PV1`/`OBR`/…) contains no\n * batch segment and exactly one `MSH`, so this returns `false` for it.\n *\n * Pure byte-level, never throws.\n *\n * @internal\n */\nexport function containsBatchOrExtraMessage(buf: Buffer): boolean {\n let mshCount = 0;\n let segStart = 0;\n while (segStart < buf.length) {\n const end = segmentEnd(buf, segStart);\n if (isMshSegmentAt(buf, segStart)) {\n mshCount++;\n if (mshCount > 1) return true;\n } else if (isBatchEnvelopeSegmentAt(buf, segStart)) {\n return true;\n }\n // Advance past this segment's terminator bytes (handles `CRLF` as one break).\n let next = end;\n while (\n next < buf.length &&\n (buf[next] === SEGMENT_SEPARATOR_CR || buf[next] === SEGMENT_SEPARATOR_LF)\n ) {\n next++;\n }\n if (next === segStart) return false; // no progress, malformed\n segStart = next;\n }\n return false;\n}\n\n/** The MSH segment, decoded and split into its fields. @internal */\nexport interface MshSegment {\n /** MSH-1, the field separator this message declares (§2.5.4). One `latin1` char. */\n readonly fieldSep: string;\n /**\n * The MSH segment split on {@link fieldSep}. `[0]` is the literal `\"MSH\"`, `[1]` is\n * MSH-2 (the encoding characters), and thereafter the index **is** the field number:\n * `[9]` is MSH-10, `[10]` is MSH-11, `[11]` is MSH-12.\n */\n readonly fields: readonly string[];\n}\n\n/**\n * Read the MSH segment of an HL7 v2 payload, **the** scan, which every consumer in\n * this package goes through.\n *\n * Pure byte-level, never throws, returns `null` for anything it cannot read (Postel's\n * Law decoder side; CLAUDE.md guardrail). It:\n *\n * * **locates** the MSH, the first `CR`/`LF`-delimited segment that starts with\n * `MSH`, rather than demanding it at byte 0, so a leading `CR` or an `FHS`/`BHS`\n * batch header cannot hide a control ID that is plainly there ({@link findMshSegment});\n * * takes the field separator from MSH-1 rather than assuming `|` (§2.5.4);\n * * **bounds the field scan at that segment's terminator**;\n * * decodes only that segment, as `latin1` (see {@link CONTROL_ID_ENCODING}), so a\n * high-bit byte survives and a 16 MB payload is not decoded to read its header.\n *\n * ## The segment terminator is load-bearing, not a detail\n *\n * An earlier version of this scan counted field separators without ever stopping at\n * the segment terminator. On a **truncated MSH**, `MSH|^~\\&|EPIC|HOSP|MIRTH|LAB\\r`,\n * which has only 6 fields, the count therefore ran *past the `CR`* and kept counting\n * separators inside the next segment. The \"MSH-10\" it returned was `PID-3`: the\n * patient's **MRN**. That value became the client's correlation key, and was carried\n * into the ACK timeout error and the `MLLP_ACK_UNMATCHED_CONTROL_ID` /\n * `MLLP_ACK_AFTER_TIMEOUT` warnings, a patient identifier in a log line, and a\n * mis-read one at that. Those diagnostics now report byte lengths only, so that\n * particular route out is closed; the reader must still be right, because a field\n * that does not exist must read as absent, never as the next segment's contents,\n * and the correlation KEY is still the value this returns.\n *\n * The two rules are independent, and both are needed. Bounding the scan is what kills\n * the PID-3 read. **Locating** the MSH (rather than demanding it at byte 0) is what\n * stops the bound from turning into a tolerance regression that silently drops a\n * control ID which is present, see {@link findMshSegment}.\n *\n * @example\n * ```typescript\n * const msh = readMshSegment(payloadBuffer);\n * // msh?.fields[9] === 'MSG00001'; msh?.fieldSep === '|'\n * ```\n *\n * @internal\n */\nexport function readMshSegment(buf: Buffer): MshSegment | null {\n const at = findMshSegment(buf);\n if (at === null) return null;\n\n // The separator is MSH-1, the 4th byte OF THE MSH SEGMENT, wherever that segment\n // begins. `findMshSegment` has already rejected an unusable one.\n const fieldSep = String.fromCharCode(buf[at.start + 3] as number);\n // `at.end` is the segment's terminator: the field split cannot reach past it.\n const fields = buf.subarray(at.start, at.end).toString(CONTROL_ID_ENCODING).split(fieldSep);\n return { fieldSep, fields };\n}\n\n/**\n * Strip **leading segment terminators only**, `CR`/`LF` bytes before the first segment.\n *\n * This is the whole of what the parser-backed ACK builder needs re-based, and it is\n * deliberately the *minimum*. `@cosyte/hl7`'s `parseHL7` requires `MSH` to be the first\n * segment and throws `NO_MSH_SEGMENT` otherwise, so a leading `CR`, which the MLLP\n * decoder passes straight through into the payload, would otherwise send `buildMllpAck`\n * down its unparseable fallback for a message whose MSH-10 is plainly readable. A leading\n * `CR`/`LF` is pure segment-terminator noise: it carries **no data**, so dropping it\n * cannot hide anything.\n *\n * ## What this deliberately does NOT do: skip an `FHS`/`BHS` batch header\n *\n * An earlier version of this function re-based on the *located `MSH`*, which also skipped\n * a batch envelope. That was a serious mistake. A batch (§2.10.3) is\n * `[FHS] { [BHS] { MSH … } [BTS] } [FTS]`, a **sequence of messages**. Re-basing on the\n * first `MSH` handed `parseHL7` message 1 and silently discarded every later `MSH`, the\n * `BTS` count, and the `FTS`. `buildMllpAck` then returned a confident positive **`AA`\n * correlated to message 1, with zero warnings**, for a batch whose messages 2..N it had\n * never looked at. The sender reads that as \"the batch is accepted\", so those messages\n * are lost outright, or time out and resend as **duplicate clinical messages**.\n *\n * Batch ACK is a real feature and is tracked separately. It is not something to arrive at\n * by accident, via a byte-offset helper, on the way to fixing something else. Until it is\n * built deliberately, an `FHS`/`BHS` envelope must fall through to `parseHL7`'s\n * `NO_MSH_SEGMENT` and out into the **warned, non-positive `AE` fallback**, a loud\n * refusal to ACK what we did not read. That is the fail-safe answer and it is what the\n * package did before this item touched it.\n *\n * @internal\n */\nexport function stripLeadingSegmentTerminators(buf: Buffer): Buffer {\n let i = 0;\n while (i < buf.length && (buf[i] === SEGMENT_SEPARATOR_CR || buf[i] === SEGMENT_SEPARATOR_LF)) {\n i++;\n }\n return i === 0 ? buf : buf.subarray(i);\n}\n\n/**\n * Extract MSH-10 (Message Control ID) from an HL7 v2 payload, the correlation key.\n *\n * A thin read off {@link readMshSegment}: `null` when the payload has no readable MSH,\n * when the MSH segment is too short to reach MSH-10, or when MSH-10 is present but\n * empty. It is **never** a value taken from another segment.\n *\n * @example\n * ```typescript\n * const id = extractMshControlId(payloadBuffer);\n * // id === 'MSG00001' | null\n * ```\n *\n * @internal\n */\nexport function extractMshControlId(buf: Buffer): string | null {\n return controlIdFromMsh(readMshSegment(buf));\n}\n\n/**\n * MSH-10 read off an already-scanned {@link MshSegment}, the half of\n * {@link extractMshControlId} that does not repeat the scan.\n *\n * A caller that needs MSH-10 **and** another MSH field off the same message reads the\n * segment once through {@link readMshSegment} and then asks this for the control ID,\n * rather than scanning the header twice. `null` when the segment is absent, too short to\n * reach MSH-10, or carries an empty MSH-10, byte-identical to {@link extractMshControlId},\n * which is defined as this composed with the scan.\n *\n * @example\n * ```typescript\n * const msh = readMshSegment(payloadBuffer);\n * const id = controlIdFromMsh(msh);\n * // id === 'MSG00001' | null\n * ```\n *\n * @internal\n */\nexport function controlIdFromMsh(msh: MshSegment | null): string | null {\n if (msh === null) return null;\n const id = msh.fields[9];\n return id === undefined || id === \"\" ? null : id;\n}\n\n/**\n * Extract MSA-2 (acknowledged Message Control ID) from an HL7 v2 ACK payload.\n *\n * Pure byte-level scan, never throws, returns `null` for malformed input. The field\n * separator comes from the ACK's own MSH-1, read through {@link readMshSegment}, the\n * same tolerant locate every other read in this module uses, so an ACK whose `MSH` is\n * not at byte 0 is still read rather than silently discarded. The `MSA` segment is\n * then located by scanning segment boundaries (`\\r` / `\\n`).\n *\n * Decoded as `latin1` (see {@link CONTROL_ID_ENCODING}), matching both\n * {@link extractMshControlId} and `buildRawAck`'s verbatim MSH-10 → MSA-2 echo,\n * so an ACK for a high-bit control ID looks up the key the send actually\n * enqueued.\n *\n * @example\n * ```typescript\n * const acked = extractMsaControlId(ackPayloadBuffer);\n * // acked === 'MSG00001' | null\n * ```\n *\n * @internal\n */\nexport function extractMsaControlId(buf: Buffer): string | null {\n const msa = readMsaSegment(buf);\n if (msa === null) return null;\n const id = msa.fields[2];\n return id === undefined || id === \"\" ? null : id;\n}\n\n/**\n * Read the `MSA` segment of an HL7 v2 acknowledgement, decoded and split into its fields.\n *\n * **The** MSA scan, and the segment rule every reader of an acknowledgement shares:\n * {@link extractMsaControlId} is defined as `fields[2]` off this, and the MSA-1\n * acknowledgement-code read is defined as `fields[1]` off the same call. That is not\n * tidiness, it is the reason a classification can never disagree with a correlation.\n * If one reader split segments on `CR` alone while the other split on `CR`/`LF`, a peer\n * whose interface engine terminates segments with `CRLF` would have its MSA-2 found (so\n * the acknowledgement matches a send) and its MSA-1 missed (so the send is not settled by\n * it): a correctly answered message left pending to its timeout. One scan, one answer.\n *\n * Pure byte-level, never throws, returns `null` for anything it cannot read:\n *\n * * the field separator comes from the acknowledgement's **own** MSH-1, read through\n * {@link readMshSegment}, so an `MSH` that is not at byte 0 is still read rather than\n * silently discarded, and a payload with no readable `MSH` at all yields `null`;\n * * the `MSA` is the first `CR`/`LF`-delimited segment whose first three bytes are `MSA`\n * **and** whose fourth byte is that field separator, so a segment that merely starts\n * with those letters cannot be mistaken for one;\n * * the field split is bounded at that segment's terminator and decoded as `latin1`\n * (see {@link CONTROL_ID_ENCODING}), so a high-bit byte survives.\n *\n * Fields are indexed by position after the segment name: `[0]` is the literal `\"MSA\"`,\n * `[1]` is MSA-1 (the acknowledgement code) and `[2]` is MSA-2 (the acknowledged control\n * ID). Unlike the MSH there is no off-by-one here, because MSA-1 is an ordinary field and\n * MSH-1 is the field separator itself.\n *\n * @example\n * ```typescript\n * const msa = readMsaSegment(ackPayloadBuffer);\n * // msa?.fields[1] === 'AA'; msa?.fields[2] === 'MSG00001'\n * ```\n *\n * @internal\n */\nexport function readMsaSegment(buf: Buffer): MshSegment | null {\n // MSH-1 establishes the separator for the whole message, including its MSA.\n const msh = readMshSegment(buf);\n if (msh === null) return null;\n const fieldSep = msh.fieldSep.charCodeAt(0);\n const end = buf.length;\n let segStart = 0;\n while (segStart < end) {\n let segEnd = segStart;\n while (\n segEnd < end &&\n buf[segEnd] !== SEGMENT_SEPARATOR_CR &&\n buf[segEnd] !== SEGMENT_SEPARATOR_LF\n ) {\n segEnd++;\n }\n if (\n segEnd - segStart >= 4 &&\n buf[segStart] === ASCII_M &&\n buf[segStart + 1] === ASCII_S &&\n buf[segStart + 2] === ASCII_A &&\n buf[segStart + 3] === fieldSep\n ) {\n // The split is bounded at `segEnd`, so no later segment's bytes can be reached.\n const fields = buf\n .subarray(segStart, segEnd)\n .toString(CONTROL_ID_ENCODING)\n .split(msh.fieldSep);\n return { fieldSep: msh.fieldSep, fields };\n }\n // Skip segment terminator bytes to advance to the next segment.\n while (\n segEnd < end &&\n (buf[segEnd] === SEGMENT_SEPARATOR_CR || buf[segEnd] === SEGMENT_SEPARATOR_LF)\n ) {\n segEnd++;\n }\n if (segEnd === segStart) return null; // no progress, malformed\n segStart = segEnd;\n }\n return null;\n}\n","/**\n * Server-side MLLP acknowledgement construction and the **fail-safe commit contract**.\n *\n * This module owns the byte-level ACK builder used by {@link MllpServer}'s auto-ACK\n * path, the HL7 Table 0008 acknowledgement-code union, and the error type a message\n * handler throws to request a specific negative acknowledgement.\n *\n * ## Why this is safety-critical\n *\n * A positive acknowledgement (`AA`) tells the sender \"you may forget this message, I\n * have it.\" If a server emits `AA` *before* the message is durably handled and then the\n * process crashes, the message is **silently lost** with no record on either side. For\n * clinical messages (an admit, an order, a result) that is a patient-safety failure.\n *\n * The contract this module enforces: a positive ACK (`AA`) is only built once the\n * application handler has resolved success. A handler that throws or rejects yields a\n * **negative** ACK (`AE` by default, `AR` when the handler asks for it), never `AA`.\n *\n * Spec: HL7 v2.5.1 ch. 2 §2.9.2 (original acknowledgement mode), §2.9.3 (enhanced\n * accept/application acknowledgement), Table 0008 (Acknowledgment Code), §2.9.2.2\n * (MSA-2 echoes the inbound MSH-10 message control ID).\n *\n * @packageDocumentation\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport { containsBatchOrExtraMessage, readMshSegment } from \"../internal/control-id.js\";\n\n/**\n * HL7 Table 0008, Acknowledgment Code. A **stable public API**.\n *\n * Two families, by mode (HL7 v2.5.1 §2.9):\n *\n * - **Original mode (§2.9.2):** `AA` accept, `AE` application error, `AR` application\n * reject. The single ACK reports application-level outcome.\n * - **Enhanced mode (§2.9.3):** `CA` commit accept, `CE` commit error, `CR` commit\n * reject, the *accept* acknowledgement, distinct from a later application ACK.\n *\n * `AE` vs `AR`: `AE` is a processing **error** (the sender may resend later, e.g. a\n * transient downstream outage); `AR` is a **reject** (the sender should not resend the\n * message unchanged, e.g. it is structurally unacceptable). `@cosyte/mllp` builds\n * original-mode ACKs; the `C*` codes are surfaced in the type for completeness and for\n * callers that build their own enhanced-mode ACKs via `autoAck: fn`.\n *\n * @example\n * ```typescript\n * import type { AckCode } from '@cosyte/mllp';\n * const code: AckCode = 'AE';\n * ```\n */\nexport type AckCode = \"AA\" | \"AE\" | \"AR\" | \"CA\" | \"CE\" | \"CR\";\n\n/**\n * The negative original-mode acknowledgement codes a failed handler can produce.\n *\n * @example\n * ```typescript\n * import type { NegativeAckCode } from '@cosyte/mllp';\n * const code: NegativeAckCode = 'AR';\n * ```\n */\nexport type NegativeAckCode = \"AE\" | \"AR\";\n\n/**\n * Error a message handler throws to control the negative acknowledgement the server\n * returns. Throwing **any** error from a commit-gated handler yields `AE` by default;\n * throw an `MllpAckError` to choose `AR` (application reject) instead.\n *\n * The `message` is **never** copied into the ACK bytes or any emitted event, it may\n * carry PHI. Only the static, non-PHI `ackCode` influences the wire response.\n *\n * @example\n * ```typescript\n * import { createServer, MllpAckError } from '@cosyte/mllp';\n *\n * createServer({\n * autoAck: 'AA',\n * onMessage: async (payload) => {\n * if (!isAcceptable(payload)) {\n * // sender should NOT resend unchanged -> AR\n * throw new MllpAckError('unsupported message type', { ackCode: 'AR' });\n * }\n * await db.commit(payload); // throw here -> AE (resend may succeed)\n * },\n * });\n * ```\n */\nexport class MllpAckError extends Error {\n /** The negative acknowledgement code to return (`AE` or `AR`). */\n readonly ackCode: NegativeAckCode;\n\n /**\n * @param message - Diagnostic text for the thrower. Never placed on the wire (may carry PHI).\n * @param opts.ackCode - Negative code to return. Default `'AE'`.\n * @param opts.cause - Underlying error, preserved on `.cause`.\n */\n constructor(message: string, opts?: { ackCode?: NegativeAckCode; cause?: unknown }) {\n super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = \"MllpAckError\";\n this.ackCode = opts?.ackCode ?? \"AE\";\n }\n}\n\n/**\n * Resolve the negative acknowledgement code for a handler failure.\n *\n * An {@link MllpAckError} carries an explicit `ackCode`; any other thrown value maps to\n * `AE` (application error, the default, since most handler failures are transient and\n * a resend may succeed).\n *\n * @example\n * ```typescript\n * import { resolveNackCode, MllpAckError } from '@cosyte/mllp';\n * resolveNackCode(new Error('boom')); // 'AE'\n * resolveNackCode(new MllpAckError('nope', { ackCode: 'AR' })); // 'AR'\n * ```\n */\nexport function resolveNackCode(err: unknown): NegativeAckCode {\n if (err instanceof MllpAckError) return err.ackCode;\n if (typeof err === \"object\" && err !== null && \"ackCode\" in err && err.ackCode === \"AR\") {\n return \"AR\";\n }\n return \"AE\";\n}\n\n/** Static, PHI-free MSA-3 text for negative acknowledgements. Never derived from payload. */\nconst NACK_TEXT: Readonly<Record<NegativeAckCode, string>> = Object.freeze({\n AE: \"message could not be processed\",\n AR: \"message rejected\",\n});\n\n/** HL7 default encoding characters (MSH-2), used when the inbound declares none. */\nconst DEFAULT_ENCODING_CHARACTERS = \"^~\\\\&\";\n\n/** HL7 default field separator (MSH-1), used when the inbound declares none usable. */\nconst DEFAULT_FIELD_SEPARATOR = \"|\";\n\n/**\n * Stand-in for whichever HL7 default encoding character collides with the inbound's MSH-1.\n *\n * Reached only when an inbound declares a field separator that is one of `^ ~ \\ &` AND declares no\n * usable MSH-2 of its own, a message already malformed twice over (§2.16 requires MSH-2). We\n * substitute the one colliding character rather than change the ACK's field separator, because\n * `fieldSep` is the only byte that can truncate MSA-2 (see {@link buildRawAck}).\n *\n * `#` is chosen because it is not one of the four HL7 defaults, so the substitution can never\n * collide with the three characters it sits beside; and it is not `CR`/`LF`/`VT`/`FS`, so it cannot\n * break segmentation or framing. It is only ever *a* delimiter for a message that declared none.\n */\nconst ENCODING_CHAR_SUBSTITUTE = \"#\";\n\n/**\n * Encoding characters (MSH-2) we refuse to echo into the ACK we build.\n *\n * The inbound's MSH-1/MSH-2 are echoed so the ACK's *structure* matches the\n * message it answers, but a delimiter is a byte we then write ourselves, all\n * over the ACK. `VT` (0x0B) and `FS` (0x1C) are the MLLP framing bytes, and `CR`\n * / `LF` are segment terminators: any of them adopted as an encoding character\n * would put that byte at every component boundary of the ACK. Fall back to the\n * HL7 defaults.\n *\n * **Where such a payload comes from.** The plainest route is that `buildRawAck` is a\n * **public export**: a caller can hand it any `Buffer` at all, having never gone near the\n * decoder. It can also arrive off the wire, a mid-payload `VT` makes `FrameReader` discard\n * what it accumulated and start over (`MLLP_TRAILING_BYTES`), so a *delivered* payload never\n * contains a `VT`, but a delivered payload CAN contain an `FS`: under the\n * `allowMissingLeadingVt` tolerance a non-VT, non-whitespace first byte becomes payload byte 0,\n * and `FS` (0x1C) is neither. (The `MLLP_PAYLOAD_CONTAINS_VT`/`_FS` codes are thrown by\n * **`encodeFrame`**, on the way out, never by the decoder, on the way in.) The guard does not\n * rest on the decoder screening these out; it rests on the public export.\n *\n * **It guards the DELIMITERS ONLY, it is not a guarantee that the ACK frames.** A\n * `VT`/`FS` inside echoed field *content* (MSH-3..6, MSH-10) is still copied verbatim,\n * so such a caller can still produce an ACK payload carrying a framing byte, and strict\n * `encodeFrame` will throw on it. Echoing content verbatim is required by §2.9.2.2 and\n * this builder will not corrupt a control ID to make its own framing easier; the\n * delimiter guard exists only so that a nonsense MSH-1/MSH-2 cannot *multiply* one stray\n * byte across every field boundary of the ACK.\n *\n * The equivalent rule for MSH-1 lives in `src/internal/control-id.ts`, where it\n * belongs: an unusable field separator makes the message unreadable for *every*\n * consumer, not just for this builder, and `readMshSegment` returning `null` is\n * what keeps them all agreeing on that.\n */\nconst UNSAFE_DELIMITER = /[\\r\\n\\v\\x1c]/;\n\n/**\n * True iff a **positive** raw acknowledgement (`AA`/`CA`) cannot be safely correlated to\n * this payload, so {@link buildRawAck} (and the server's auto-ACK path) must **downgrade**\n * it to a non-positive `AE`/`CE` rather than tell the sender \"I have it.\"\n *\n * The rule this enforces: **never answer `AA` for a message you could not correlate.** A\n * positive ACK is a promise the sender may forget the message; if it names a control ID\n * the sender cannot match, or names one of several messages it never read, the sender\n * times out and resends, committing a **duplicate clinical message** (or worse, believes a\n * destroyed message was delivered). Three payload-shaped reasons make a positive ACK\n * uncorrelatable, all peer-reachable off the wire:\n *\n * 1. **No readable `MSH`.** `readMshSegment` returns `null`, e.g. a `BOM`/`SP`/`TAB`\n * before `MSH` (which shares the `MSH`'s segment line, so `MSH` heads no segment), or\n * a bare fragment delivered after a mid-payload `VT` discard (`MLLP_TRAILING_BYTES`).\n * MSA-2 would be empty: an ACK that correlates to nothing.\n * 2. **Empty MSH-10.** The `MSH` is readable but carries no message control ID, so there\n * is nothing to echo, again `MSA|AA|` with an empty MSA-2.\n * 3. **A batch or concatenated messages.** An `FHS`/`BHS`/`BTS`/`FTS` envelope (§2.10.3)\n * or a second `MSH` in the same frame: a single MSA-2 can echo only ONE control ID, so\n * a positive ACK naming the first silently drops the rest (see\n * {@link containsBatchOrExtraMessage}). Batch ACK is its own feature; until it is\n * designed, a batch must stay a **loud non-positive** answer.\n *\n * This is a **refusal**, not a tolerance widening: it never makes an unreadable message\n * readable, never re-bases on a located `MSH`, never parses a batch. It only recognizes\n * the shapes for which a positive disposition would be a lie, so the builder can fall back\n * to `AE`. A **negative** requested code (`AE`/`AR`/`CE`/`CR`) is unaffected, it is\n * already non-positive, and echoing whatever control ID it can find is still useful.\n *\n * Pure, byte-level, never throws.\n *\n * @example\n * ```typescript\n * import { rawAckUncorrelatable } from '@cosyte/mllp';\n * rawAckUncorrelatable(oneGoodMessage); // false → AA is safe\n * rawAckUncorrelatable(twoConcatenatedMsh); // true → downgrade AA to AE\n * ```\n */\nexport function rawAckUncorrelatable(payload: Buffer): boolean {\n const msh = readMshSegment(payload);\n if (msh === null) return true; // (1) no readable MSH\n const controlId = msh.fields[9];\n if (controlId === undefined || controlId === \"\") return true; // (2) empty MSH-10\n return containsBatchOrExtraMessage(payload); // (3) batch / concatenated messages\n}\n\n/** Non-positive counterpart of a positive HL7 Table 0008 code: `AA`→`AE`, `CA`→`CE`. @internal */\nfunction downgradePositiveAck(code: AckCode): AckCode {\n if (code === \"AA\") return \"AE\";\n if (code === \"CA\") return \"CE\";\n return code;\n}\n\n/**\n * Build a minimal original-mode HL7 v2 acknowledgement from raw inbound payload bytes,\n * **without a parser** (parser-driven ACKs are the `@cosyte/mllp/ack-from-hl7` subpath).\n *\n * Locates the `MSH` segment (the first `CR`/`LF`-delimited segment starting with `MSH`,\n * so a leading `CR` or an `FHS`/`BHS` batch header does not hide it), reads the field\n * separator from **MSH-1** and the encoding characters from **MSH-2**, then splits that\n * segment on that separator to read fields. The ACK swaps sender/receiver per HL7 ACK\n * rules, echoes the inbound MSH-10 into MSA-2 (§2.9.2.2), sets MSA-1 to `code`, and, for\n * negative codes, adds a **static, PHI-free** MSA-3 reason. A fresh control ID fills the\n * ACK's own MSH-10.\n *\n * ## Why the delimiters are read, not assumed\n *\n * MSH-1 **is** the field separator (HL7 v2.5.1 §2.5.4): the byte at offset 3 of the MSH\n * segment *defines* it for the whole message. `|` is overwhelmingly common but it is a\n * convention, not the spec. Assuming it was wrong in two compounding ways:\n *\n * 1. **Reading.** Splitting a `!`-delimited message on `|` yields ONE field, so every\n * echoed field, including MSH-10, came back empty. The ACK went out as\n * `MSA|AA|` with **no correlation id at all**: the sender cannot match it, times\n * out, and resends → a duplicate clinical message. That is the exact failure this\n * package's correlator exists to prevent, manufactured by the ACK builder itself.\n * 2. **Writing.** The echoed MSH-3..6 and MSH-10 field *content* is copied verbatim\n * from the inbound, still escaped against the **inbound's** encoding characters. Re-\n * emitting that content under a different delimiter set silently reinterprets it:\n * an inbound whose component separator is `#` and whose MSH-10 is `ID#X` would be\n * re-emitted under `^~\\&` as the literal `ID#X`, which the sender then reads as a\n * single component rather than two. Echoing MSH-1 and MSH-2 keeps the content and\n * the delimiters that define it together.\n *\n * ## It reads the MSH through the SHARED scanner\n *\n * The MSH read is `readMshSegment` from `src/internal/control-id.ts`, the same call the\n * client's correlator makes to derive the key it will later match this ACK against. That\n * is deliberate and it is the whole point: this builder used to re-derive the read itself\n * (`payload.toString(\"latin1\").split(\"\\r\")`, hunting for an `MSH` anywhere in the\n * payload), and the two disagreed on real inputs. On a **truncated MSH** followed by a\n * `PID` the correlator keyed on the PID's MRN while this builder echoed an empty MSA-2. On\n * a payload with a **leading `CR`**, which the MLLP decoder passes straight through, and\n * which real senders emit, this builder found the `MSH` and echoed MSH-10 correctly while\n * the correlator, requiring `MSH` at byte 0, gave up. Every such disagreement is an ACK the\n * sender cannot match → timeout → resend → **duplicate clinical message**.\n *\n * The fix is one scan, but note *which* scan. The first attempt made them agree by\n * requiring `MSH` at byte 0 everywhere, which \"resolved\" the leading-`CR` disagreement by\n * degrading the side that had been **right**: `buildRawAck` began emitting a positive `AA`\n * with an empty MSA-2, silently, for a message whose MSH-10 was plainly present. Agreement\n * is not the goal; agreeing on the *correct*, *tolerant* answer is. A lenient reader may\n * never drop data that is there (Postel's Law, CLAUDE.md).\n *\n * ## The fail-safe downgrade: never a positive `AA`/`CA` it cannot correlate\n *\n * A positive acknowledgement is a promise the sender may forget the message. If it names a\n * control ID the sender cannot match, or names one message out of several it never read,\n * the sender times out and resends, committing a **duplicate clinical message**. So a\n * requested positive code is **downgraded** to its non-positive counterpart (`AA`→`AE`,\n * `CA`→`CE`) whenever the payload cannot carry a correlatable positive ACK: no readable\n * `MSH`, an empty MSH-10, or a batch/concatenated-message shape that a single MSA-2 cannot\n * acknowledge. See {@link rawAckUncorrelatable} for the exact conditions and why each is a\n * refusal rather than a widened reader. A requested **negative** code (`AE`/`AR`/`CE`/`CR`)\n * is never touched. This mirrors the parser-backed `buildMllpAck`, which downgrades and\n * warns on an unparseable inbound, the two builders' fail-safe semantics now agree.\n *\n * **Never throws** and **never copies payload content** beyond the routing/control\n * metadata above, `readMshSegment` stops at the MSH's segment terminator, so no field of\n * any later segment (PID and friends) can be reached, let alone echoed. On a missing or\n * unreadable `MSH` it returns a minimal well-formed ACK carrying the (downgraded) `code` so\n * the caller can still respond.\n *\n * @param payload - Raw decoded HL7 v2 payload bytes (MLLP framing already stripped).\n * @param code - Requested MSA-1 acknowledgement code. A positive `AA`/`CA` is downgraded to\n * `AE`/`CE` when the message cannot be correlated (see above).\n * @returns ACK payload bytes (no framing, the caller wraps with `encodeFrame`).\n *\n * @example\n * ```typescript\n * import { buildRawAck } from '@cosyte/mllp';\n * const ack = buildRawAck(inboundPayload, 'AE'); // MSA|AE|<inbound MSH-10>|message could not be processed\n * ```\n */\nexport function buildRawAck(payload: Buffer, code: AckCode): Buffer {\n // THE shared read (see the docblock). `latin1`, MSH-1 taken from the message, scan bounded\n // at the segment terminator. `null` means \"this package cannot read this message\", and it\n // means that for the correlator too, which is exactly the agreement we need.\n const msh = readMshSegment(payload);\n\n // FAIL-SAFE: never emit a positive disposition (`AA`/`CA`) for a message we cannot correlate.\n // A positive ACK the sender cannot match → timeout → resend → duplicate clinical message.\n // The downgrade is defense in depth here (a direct caller of this public export is protected\n // even if it never touched the server), and the server's auto-ACK path applies the SAME\n // predicate so it can emit a `'nack'` observability signal alongside. See\n // {@link rawAckUncorrelatable}. A requested negative code passes through unchanged.\n if ((code === \"AA\" || code === \"CA\") && rawAckUncorrelatable(payload)) {\n code = downgradePositiveAck(code);\n }\n\n const newControlId = randomUUID().replace(/-/g, \"\").substring(0, 20);\n const now = timestamp14();\n\n if (msh === null) {\n // No usable MSH to echo: emit a well-formed ACK carrying the (already fail-safe-downgraded)\n // code, no payload content. HL7 defaults, since the inbound declared no delimiters we can\n // trust. A positive `AA`/`CA` has been turned into `AE`/`CE` above, there is no control ID\n // to correlate, so a positive disposition here would be exactly the uncorrelatable lie.\n const s = DEFAULT_FIELD_SEPARATOR;\n const e = DEFAULT_ENCODING_CHARACTERS;\n const tail = code === \"AE\" || code === \"AR\" ? `${s}${NACK_TEXT[code]}` : \"\";\n return Buffer.from(\n `MSH${s}${e}${s}${s}${s}${s}${s}${now}${s}${s}ACK${s}${newControlId}${s}P${s}2.3\\r` +\n `MSA${s}${code}${s}${tail}\\r`,\n \"latin1\",\n );\n }\n\n const { fieldSep, fields } = msh;\n // MSH field indices after splitting on the field separator:\n // [1]=MSH-2 encoding chars\n // [2]=sendingApp [3]=sendingFacility [4]=receivingApp [5]=receivingFacility\n // [9]=MSH-10 control ID [10]=processingId [11]=version\n const declaredEnc = fields[1] ?? \"\";\n const usableEnc =\n declaredEnc !== \"\" && !UNSAFE_DELIMITER.test(declaredEnc)\n ? declaredEnc\n : DEFAULT_ENCODING_CHARACTERS;\n\n // MSH-2 must not contain MSH-1 (§2.5.4, §2.16, the delimiters are distinct characters).\n // `declaredEnc` structurally cannot: it is a product of splitting ON `fieldSep`. The DEFAULT\n // fallback can, an inbound declaring MSH-1 = `^` with no usable MSH-2 would take `^~\\&`, whose\n // first character IS the field separator, so the ACK reads back with an EMPTY MSH-2 and every\n // later MSH field shifted by one.\n //\n // ## Fix the ENCODING CHARACTERS, never the field separator\n //\n // The one thing this builder must not do is change `fieldSep` for the ACK. **`fieldSep` is the\n // only byte that can truncate MSA-2**, and MSH-10 provably cannot contain it: MSH-10 is a product\n // of splitting the MSH *on* `fieldSep`. Keep the inbound's separator and the verbatim echo is\n // guaranteed by construction. Swap it for `|` and that guarantee evaporates, because under\n // §2.5.4 a `|` inside an `^`-delimited message is **ordinary data**, needing no escape. An\n // earlier version of this guard did exactly that, and an MSH-10 of `ID|X` went out as\n // `MSA|AA|ID|X`, which a receiver reads back as **`ID`**: silently TRUNCATED.\n //\n // Truncated is far worse than empty. Empty correlates to nothing; truncated is *plausible*, and\n // can match a **different** in-flight send, settling it, so `send()` resolves and the sender\n // forgets a message the receiver never acknowledged, while the message that WAS acknowledged\n // stays in flight and resends. One silently lost clinical message and one duplicate, from a\n // single positive `AA`.\n //\n // So: keep `fieldSep`, and pick encoding characters that do not contain it. The encoding\n // characters cannot truncate MSA-2 (the MSA scan splits on `fieldSep` alone), so substituting one\n // is free, it costs only the component/repetition/escape/subcomponent *semantics* of a message\n // that declared none (its MSH-2 was empty or unusable; §2.16 requires it).\n //\n // Note what we still do NOT do: fall through to the minimal ACK. That drops the MSA-2 echo, and\n // an ACK that is well-formed but uncorrelatable is worse than one that correlates with an\n // imperfect header. The control-ID echo is the thing this file exists to protect.\n const s = fieldSep;\n const encodingChars = usableEnc.includes(fieldSep)\n ? usableEnc.replace(fieldSep, ENCODING_CHAR_SUBSTITUTE)\n : usableEnc;\n\n const sendingApp = fields[2] ?? \"\";\n const sendingFacility = fields[3] ?? \"\";\n const receivingApp = fields[4] ?? \"\";\n const receivingFacility = fields[5] ?? \"\";\n const inboundControlId = fields[9] ?? \"\";\n const processingId = fields[10] ?? \"P\";\n const version = fields[11] ?? \"2.3\";\n\n const msaTail = code === \"AE\" || code === \"AR\" ? `${s}${NACK_TEXT[code]}` : \"\";\n const ackStr =\n `MSH${s}${encodingChars}${s}${receivingApp}${s}${receivingFacility}${s}${sendingApp}${s}${sendingFacility}${s}${now}${s}${s}ACK${s}${newControlId}${s}${processingId}${s}${version}\\r` +\n `MSA${s}${code}${s}${inboundControlId}${msaTail}\\r`;\n\n // `latin1` on the way back out too, so a high-bit byte echoed from the inbound MSH-10 is\n // preserved rather than re-masked. Paired with the `latin1` decode above, the inbound control ID\n // round-trips byte-exact into MSA-2, and because the ACK carries the inbound's own MSH-1, the\n // sender's MSA-2 scanner reads back the very bytes it keyed its in-flight store on.\n return Buffer.from(ackStr, \"latin1\");\n}\n\n/** 14-char HL7 timestamp `YYYYMMDDHHmmss` in UTC. */\nfunction timestamp14(): string {\n const d = new Date();\n const pad = (n: number, w = 2): string => String(n).padStart(w, \"0\");\n return (\n String(d.getUTCFullYear()) +\n pad(d.getUTCMonth() + 1) +\n pad(d.getUTCDate()) +\n pad(d.getUTCHours()) +\n pad(d.getUTCMinutes()) +\n pad(d.getUTCSeconds())\n );\n}\n","/**\n * Parser-free reads of the three acknowledgement-mode fields: MSH-15 and MSH-16 on the way\n * out, MSA-1 on the way in.\n *\n * HL7 v2.5.1 §2.9 splits acknowledgement into two protocols. In the **original** protocol\n * one message draws one acknowledgement. In the **enhanced** protocol it draws two: an\n * *accept* acknowledgement carrying `CA`/`CE`/`CR` (\"I have committed these bytes to safe\n * storage; you may stop resending\"), and a later *application* acknowledgement carrying\n * `AA`/`AE`/`AR` (\"here is what my application did with it\"). A message asks for the\n * enhanced protocol through MSH-15 and MSH-16, and §2.9 states the equivalence that makes\n * the two one contract rather than two: the original protocol is the enhanced protocol\n * with MSH-15 = `NE` and MSH-16 = `AL`.\n *\n * This module reads those fields the way the rest of this package reads MSH-10: byte-level,\n * off the shared MSH scan, never through a parser. **Reading a field is not validating\n * one.** Nothing here refuses, delays or alters a message because of the value of a field;\n * an unrecognised value reads as its default and is reported, which is the only behaviour\n * compatible with a transport that does not own HL7 content.\n *\n * Two code systems are involved and neither is interpreted beyond membership:\n *\n * * **Table 0008** (MSA-1, acknowledgment code), six codes in two halves:\n * accept-mode `CA`/`CE`/`CR`, application-mode `AA`/`AE`/`AR`.\n * * **Table 0155** (MSH-15, MSH-16, accept/application acknowledgment conditions), four\n * codes: `AL` always, `NE` never, `ER` error or reject conditions only, `SU`\n * successful completion only.\n *\n * **INTERNAL**, not part of the public API.\n *\n * @packageDocumentation\n */\n\nimport { readMshSegment, readMsaSegment, type MshSegment } from \"./control-id.js\";\n\n/**\n * HL7 Table 0155, accept/application acknowledgment conditions. The value space of MSH-15\n * and MSH-16.\n *\n * @internal\n */\nexport type AckConditionCode = \"AL\" | \"NE\" | \"ER\" | \"SU\";\n\n/**\n * HL7 Table 0008, acknowledgment code. The value space of MSA-1, in two halves: the\n * accept-mode `CA`/`CE`/`CR` and the application-mode `AA`/`AE`/`AR`.\n *\n * Structurally identical to the package's public `AckCode`, and declared here so this\n * module can be read by the client without reaching into the server's surface.\n *\n * @internal\n */\nexport type Table0008Code = \"AA\" | \"AE\" | \"AR\" | \"CA\" | \"CE\" | \"CR\";\n\n/** The four Table 0155 codes, in table order. @internal */\nconst TABLE_0155: readonly AckConditionCode[] = Object.freeze([\"AL\", \"NE\", \"ER\", \"SU\"]);\n\n/** The six Table 0008 codes, in table order. @internal */\nconst TABLE_0008: readonly Table0008Code[] = Object.freeze([\"AA\", \"AE\", \"AR\", \"CA\", \"CE\", \"CR\"]);\n\n/**\n * Position of MSH-15 in the field array {@link readMshSegment} returns, **and the one\n * place this off-by-one is written down**.\n *\n * The array is the MSH segment split on its own field separator, so `[0]` is the literal\n * `\"MSH\"` and `[1]` is MSH-2. From there the index is the field number minus one, because\n * MSH-1 **is** the field separator (§2.5.4) and therefore occupies no token of its own:\n * MSH-15 is the *fourteenth* separator-delimited token after `MSH`, not the fifteenth.\n *\n * The convention does not carry over from any other segment. In an `MSA` the segment name\n * is followed by MSA-1, so there `[1]` is field 1. Transposing the ordinary convention\n * onto the MSH reads MSH-16 out of MSH-17: on a header declaring a country code and no\n * acknowledgement request at all, that turns an original-mode message into an\n * enhanced-mode one and makes it wait for an acknowledgement no peer will send.\n *\n * @internal\n */\nconst MSH_15_INDEX = 14;\n\n/** Position of MSH-16 in the same field array. See {@link MSH_15_INDEX}. @internal */\nconst MSH_16_INDEX = 15;\n\n/** ASCII space, the only byte {@link readTableField} trims. @internal */\nconst SPACE = \" \";\n\n/**\n * The component and repetition separators a message declares in MSH-2.\n *\n * Either may be `null`, meaning the message declared none: MSH-2 is required by §2.16 but\n * this package reads what arrives rather than what should have. A `null` separator is not\n * substituted with the HL7 default, it is simply not applied, so a field is read whole\n * rather than split on a delimiter its own message never declared.\n *\n * @internal\n */\nexport interface FieldDelimiters {\n /** First byte of MSH-2, or `null` when MSH-2 is empty or absent. */\n readonly componentSep: string | null;\n /** Second byte of MSH-2, or `null` when MSH-2 is shorter than two bytes. */\n readonly repetitionSep: string | null;\n}\n\n/**\n * Derive {@link FieldDelimiters} from a message's MSH-2 (the encoding characters).\n *\n * @internal\n */\nexport function delimitersFrom(encodingCharacters: string | undefined): FieldDelimiters {\n const chars = encodingCharacters ?? \"\";\n return {\n componentSep: chars.length >= 1 ? chars.charAt(0) : null,\n repetitionSep: chars.length >= 2 ? chars.charAt(1) : null,\n };\n}\n\n/**\n * The outcome of reading one table-valued field. Exactly one of three, for any input bytes.\n *\n * `byteLength` is the raw field's length before any trimming, and it is deliberately the\n * **only** thing a diagnostic may report about an unrecognised value: a field value is\n * payload content, and a diagnostic goes to a log.\n *\n * @internal\n */\nexport type TableRead<C extends string> =\n | { readonly kind: \"null\"; readonly byteLength: number }\n | { readonly kind: \"code\"; readonly code: C; readonly byteLength: number }\n | { readonly kind: \"unrecognised\"; readonly byteLength: number };\n\n/** Drop leading and trailing ASCII spaces (`0x20`) only. @internal */\nfunction trimSpaces(value: string): string {\n let start = 0;\n let end = value.length;\n while (start < end && value.charAt(start) === SPACE) start++;\n while (end > start && value.charAt(end - 1) === SPACE) end--;\n return value.substring(start, end);\n}\n\n/**\n * Read one table-valued field, the single reading rule this module has.\n *\n * Take the field's bytes as delimited by the field separator (the caller has already split\n * on it); take the bytes before the first repetition separator, then before the first\n * component separator, both as the message itself declared them; drop leading and trailing\n * spaces; compare what remains **byte-exact and case-sensitively** against the table.\n *\n * So `AL` reads as `AL`, and so do `\" AL \"`, `AL^HL70155` (a coded element whose first\n * component is the code) and `AL~NE` (a repetition whose first repeat is the code). An\n * empty field, an absent field and the two-byte HL7 explicit null `\"\"` all read as NULL.\n * `al` and `ALW` are unrecognised: the comparison is not softened, because a lenient\n * *locate* and a lenient *equality* are different things and only the first is Postel's Law.\n *\n * @internal\n */\nfunction readTableField<C extends string>(\n raw: string | undefined,\n delimiters: FieldDelimiters,\n table: readonly C[],\n): TableRead<C> {\n if (raw === undefined) return { kind: \"null\", byteLength: 0 };\n // `latin1` decoding is 1:1 with bytes, so a code-unit count is a byte count.\n const byteLength = raw.length;\n let value = raw;\n if (delimiters.repetitionSep !== null) {\n const at = value.indexOf(delimiters.repetitionSep);\n if (at !== -1) value = value.substring(0, at);\n }\n if (delimiters.componentSep !== null) {\n const at = value.indexOf(delimiters.componentSep);\n if (at !== -1) value = value.substring(0, at);\n }\n value = trimSpaces(value);\n if (value === \"\" || value === '\"\"') return { kind: \"null\", byteLength };\n for (const code of table) {\n if (code === value) return { kind: \"code\", code, byteLength };\n }\n return { kind: \"unrecognised\", byteLength };\n}\n\n/**\n * What an outbound message asks its peer for, derived from MSH-15 and MSH-16.\n *\n * @internal\n */\nexport interface OutboundAckMode {\n /**\n * `true` when MSH-15 or MSH-16 is non-NULL, which is the standard's own entry test for\n * the enhanced protocol (§2.9). Every other message, **including one whose header cannot\n * be scanned at all**, is an original-mode message and gets this package's unchanged\n * one-acknowledgement behaviour.\n */\n readonly enhanced: boolean;\n /** MSH-15 read against Table 0155; a NULL or unrecognised value reads as `NE`. */\n readonly acceptCondition: AckConditionCode;\n /**\n * MSH-16 read against Table 0155; a NULL or unrecognised value reads as `AL`, which is\n * the equivalence §2.9 states between the original protocol and the enhanced one.\n */\n readonly applicationCondition: AckConditionCode;\n /**\n * `true` when an application acknowledgement may follow the accept acknowledgement, i.e.\n * the application condition is `AL`, `ER` or `SU`. On `NE` the accept acknowledgement is\n * the last word and settles the send by itself.\n */\n readonly awaitsApplicationAck: boolean;\n /** MSH-15 carried a non-NULL value that is not one of the four Table 0155 codes. */\n readonly acceptFieldUnrecognised: boolean;\n /** MSH-16 carried a non-NULL value that is not one of the four Table 0155 codes. */\n readonly applicationFieldUnrecognised: boolean;\n}\n\n/**\n * The reading every original-mode message gets, including one whose header is unreadable.\n *\n * @internal\n */\nexport const ORIGINAL_MODE: OutboundAckMode = Object.freeze({\n enhanced: false,\n acceptCondition: \"NE\",\n applicationCondition: \"AL\",\n awaitsApplicationAck: false,\n acceptFieldUnrecognised: false,\n applicationFieldUnrecognised: false,\n});\n\n/**\n * Classify an outbound message from an already-scanned MSH, and derive what it expects.\n *\n * Takes the scanned segment rather than the payload so a caller that also needs MSH-10 (the\n * correlation key) reads the header once. A `null` segment, a header this package cannot\n * scan, is an original-mode message and is never failed for that reason.\n *\n * @internal\n */\nexport function classifyOutboundAckMode(msh: MshSegment | null): OutboundAckMode {\n if (msh === null) return ORIGINAL_MODE;\n const delimiters = delimitersFrom(msh.fields[1]);\n const accept = readTableField(msh.fields[MSH_15_INDEX], delimiters, TABLE_0155);\n const application = readTableField(msh.fields[MSH_16_INDEX], delimiters, TABLE_0155);\n if (accept.kind === \"null\" && application.kind === \"null\") return ORIGINAL_MODE;\n const applicationCondition: AckConditionCode =\n application.kind === \"code\" ? application.code : \"AL\";\n return Object.freeze({\n enhanced: true,\n acceptCondition: accept.kind === \"code\" ? accept.code : \"NE\",\n applicationCondition,\n awaitsApplicationAck: applicationCondition !== \"NE\",\n acceptFieldUnrecognised: accept.kind === \"unrecognised\",\n applicationFieldUnrecognised: application.kind === \"unrecognised\",\n });\n}\n\n/**\n * Read an inbound message's MSH-15 against Table 0155, keeping the NULL / code /\n * unrecognised distinction the caller needs to decide whether to warn.\n *\n * @internal\n */\nexport function readAcceptCondition(msh: MshSegment | null): TableRead<AckConditionCode> {\n if (msh === null) return { kind: \"null\", byteLength: 0 };\n return readTableField(msh.fields[MSH_15_INDEX], delimitersFrom(msh.fields[1]), TABLE_0155);\n}\n\n/**\n * The classification of an inbound acknowledgement's MSA-1: **total** over every byte\n * sequence a payload can carry, and mutually exclusive between its three outcomes.\n *\n * `byteLength` is the MSA-1 field's raw byte length, the only thing a diagnostic reports\n * about a value that could not be classified.\n *\n * @internal\n */\nexport type Msa1Classification =\n | { readonly kind: \"null\"; readonly byteLength: number }\n | { readonly kind: \"code\"; readonly code: Table0008Code; readonly byteLength: number }\n | { readonly kind: \"unclassifiable\"; readonly byteLength: number };\n\n/**\n * Classify an inbound acknowledgement's MSA-1 into one of the six Table 0008 codes, NULL,\n * or unclassifiable.\n *\n * The read goes through the **same** scans that correlate the acknowledgement to a send\n * ({@link readMshSegment} for the delimiters, {@link readMsaSegment} for the segment), so a\n * payload whose MSA-2 was found can never have its MSA-1 missed for a segment-splitting\n * reason. Then the ordinary field read applies, against Table 0008.\n *\n * Every input lands somewhere, which is what makes the caller's own rules decidable:\n *\n * * no readable `MSH` at all: **unclassifiable**, nothing about this payload can be read\n * with confidence, so nothing about it is guessed;\n * * a readable `MSH` but no `MSA` segment: **NULL** (such a payload carries no MSA-2\n * either, so it never correlates to a send in the first place);\n * * an `MSA` whose first field is empty, absent, or the explicit null `\"\"`: **NULL**;\n * * a value that is not one of the six codes: **unclassifiable**.\n *\n * @internal\n */\nexport function classifyMsa1(ackPayload: Buffer): Msa1Classification {\n const msh = readMshSegment(ackPayload);\n if (msh === null) return { kind: \"unclassifiable\", byteLength: 0 };\n const msa = readMsaSegment(ackPayload);\n if (msa === null) return { kind: \"null\", byteLength: 0 };\n const read = readTableField(msa.fields[1], delimitersFrom(msh.fields[1]), TABLE_0008);\n if (read.kind === \"code\") return { kind: \"code\", code: read.code, byteLength: read.byteLength };\n if (read.kind === \"null\") return { kind: \"null\", byteLength: read.byteLength };\n return { kind: \"unclassifiable\", byteLength: read.byteLength };\n}\n\n/**\n * Per-send report that the peer has **committed** a message but has not yet said what its\n * application did with it, handed to the `onCommitAck` callback of the `send()` that drew\n * it.\n *\n * It is scoped to one caller's own send, and it arrives at a point where that send has not\n * settled: `send()` is still pending and will resolve on the application acknowledgement,\n * or reject if none arrives. That scoping is the point. The commit disposition belongs to\n * one send, and a caller learns which send it belongs to by being the one that made it,\n * never by anything being logged.\n *\n * @example\n * ```typescript\n * const ack = await client.send(payload, {\n * onCommitAck: ({ code }) => logger.info({ commit: code }), // 'CA': the peer has custody\n * });\n * ```\n */\nexport interface CommitAckReport {\n /** The accept-mode Table 0008 code received. Always the positive `CA`. */\n readonly code: \"CA\";\n /** The accept acknowledgement's own payload bytes, framing stripped. */\n readonly payload: Buffer;\n /** Milliseconds between the send's write-flush and this acknowledgement. */\n readonly latencyMs: number;\n}\n\n/**\n * Whether MSH-15 asks for an **accept-mode** acknowledgement given the disposition the\n * responder has already reached for that message.\n *\n * Table 0155 makes the request conditional, so the answer needs both halves: `AL` asks\n * always; `NE` never; `ER` only where the disposition is an error or a reject; `SU` only\n * where it is positive. A NULL or unrecognised MSH-15 asks for nothing, which leaves the\n * responder's own application-mode answer in place.\n *\n * @internal\n */\nexport function acceptAckRequested(\n accept: TableRead<AckConditionCode>,\n disposition: Table0008Code,\n): boolean {\n if (accept.kind !== \"code\") return false;\n switch (accept.code) {\n case \"AL\":\n return true;\n case \"NE\":\n return false;\n case \"ER\":\n return disposition === \"AE\" || disposition === \"AR\";\n case \"SU\":\n return disposition === \"AA\";\n }\n}\n\n/**\n * The accept-mode counterpart of an application-mode Table 0008 code: `AA` to `CA`, `AE`\n * to `CE`, `AR` to `CR`. An accept-mode code is already its own counterpart.\n *\n * `CR` for a rejected message is what the acknowledgement profile of this transport\n * requires for an unrecognised message type or trigger event.\n *\n * @internal\n */\nexport function acceptModeCounterpart(code: Table0008Code): Table0008Code {\n switch (code) {\n case \"AA\":\n return \"CA\";\n case \"AE\":\n return \"CE\";\n case \"AR\":\n return \"CR\";\n case \"CA\":\n case \"CE\":\n case \"CR\":\n return code;\n }\n}\n","/**\n * The frozen diagnostic registry for acknowledgement-mode reporting.\n *\n * Every message an acknowledgement-mode diagnostic can carry lives here as a literal, and\n * the lookup takes **only a code**. There is no value parameter anywhere in this module,\n * which is the whole point: a factory that cannot be handed a value cannot interpolate one,\n * regardless of what arrives on the wire. It is the same shape, and for the same reason, as\n * the correlation registry beside it.\n *\n * ## What these diagnostics may carry\n *\n * Stable codes, byte counts, byte offsets, elapsed times, and an acknowledgement code drawn\n * from the closed six-code Table 0008 set. Nothing else. A control ID is payload content, a\n * field value is payload content, and these go to a log. An MSA-1 that could not be\n * classified is reported by its **byte length** only, never by its bytes: the whole reason a\n * value is unclassifiable is that nobody knows what it is, so echoing it into a log is\n * echoing unknown wire content into a log.\n *\n * @packageDocumentation\n */\n\n/**\n * The stable codes acknowledgement-mode reporting emits.\n *\n * A **public API**: consumers narrow on them in warning handlers, log pipelines and\n * monitoring. Renaming or removing one is a breaking change. They are deliberately a\n * family of their own rather than additions to the framing warning-code union, because\n * that union is the decoder's registry and these are not decoder events.\n *\n * @example\n * ```typescript\n * import type { AckModeCode } from '@cosyte/mllp';\n * const code: AckModeCode = 'MLLP_ACK_TWO_PHASE_UNAVAILABLE';\n * ```\n */\nexport type AckModeCode =\n | \"MLLP_ACK_ACCEPT_TYPE_UNRECOGNISED\"\n | \"MLLP_ACK_APPLICATION_TYPE_UNRECOGNISED\"\n | \"MLLP_ACK_TWO_PHASE_UNAVAILABLE\"\n | \"MLLP_ACK_COMMIT_ALREADY_REPORTED\"\n | \"MLLP_ACK_MSA1_ABSENT\"\n | \"MLLP_ACK_MSA1_UNCLASSIFIABLE\"\n | \"MLLP_ACK_SEND_ALREADY_DISPOSED\";\n\n/**\n * Frozen diagnostic text, one literal per code, no interpolation.\n *\n * Do **not** add a value parameter to anything that reads this table.\n */\nconst ACK_MODE_MESSAGES: Readonly<Record<AckModeCode, string>> = Object.freeze({\n MLLP_ACK_ACCEPT_TYPE_UNRECOGNISED:\n \"MSH-15 (accept acknowledgment type) carried a value outside HL7 Table 0155; it was read \" +\n \"as the table's default and the message was transmitted or answered unchanged. \" +\n \"Field values are withheld: a field value is payload content and this diagnostic goes to a log.\",\n MLLP_ACK_APPLICATION_TYPE_UNRECOGNISED:\n \"MSH-16 (application acknowledgment type) carried a value outside HL7 Table 0155; it was \" +\n \"read as the table's default and the message was transmitted unchanged. \" +\n \"Field values are withheld: a field value is payload content and this diagnostic goes to a log.\",\n MLLP_ACK_TWO_PHASE_UNAVAILABLE:\n \"An enhanced-mode send was made on a client that correlates acknowledgements in order \" +\n \"rather than by control ID, so two-phase correlation was not applied and the first \" +\n \"acknowledgement matched to this send settles it. Set correlateByControlId to enable it.\",\n MLLP_ACK_COMMIT_ALREADY_REPORTED:\n \"A further commit-accept acknowledgement arrived for a send whose commit disposition had \" +\n \"already been reported; it was surfaced and neither re-reported nor allowed to restart \" +\n \"the application-acknowledgement wait. The send is still pending.\",\n MLLP_ACK_MSA1_ABSENT:\n \"An acknowledgement matched to an enhanced-mode send carried no MSA-1 acknowledgement \" +\n \"code, so it was not classified into a mode and the send was left pending. \" +\n \"Field values are withheld; msa1Bytes gives the field's length.\",\n MLLP_ACK_MSA1_UNCLASSIFIABLE:\n \"An acknowledgement matched to an enhanced-mode send carried an MSA-1 value outside HL7 \" +\n \"Table 0008, so it was not classified into a mode and the send was left pending. \" +\n \"Field values are withheld; msa1Bytes gives the field's length.\",\n MLLP_ACK_SEND_ALREADY_DISPOSED:\n \"An acknowledgement arrived for a send that had already been settled or failed; it was \" +\n \"dropped and that outcome was left unchanged. \" +\n \"Field values are withheld: a control ID is payload content and this diagnostic goes to a log.\",\n});\n\n/**\n * Look up the frozen diagnostic text for an acknowledgement-mode code.\n *\n * A pure table read. It takes no value, so no caller can widen it into an interpolation\n * site.\n *\n * @param code - The acknowledgement-mode code being reported.\n * @returns The registered message for that code, byte-for-byte.\n * @example\n * ```typescript\n * const message = ackModeDiagnosticMessage('MLLP_ACK_MSA1_UNCLASSIFIABLE');\n * ```\n */\nexport function ackModeDiagnosticMessage(code: AckModeCode): string {\n return ACK_MODE_MESSAGES[code];\n}\n","/**\n * Containment for every event emission that can be reached from a **callback we do not own**,\n * a socket's `'data'`/`'error'`/`'connect'`/`'secureConnect'` listener, a `net.Server`'s\n * `'connection'` listener, a `tls.Server`'s `'tlsClientError'` listener, or the `catch` block of a\n * `void`-ed async task.\n *\n * ## Why this module exists\n *\n * Node's `EventEmitter.emit()` calls its listeners **synchronously**. So a throwing subscriber does\n * not merely fail itself, it unwinds the whole call stack it was invoked from. When that stack\n * bottoms out in a socket callback, the throw becomes an **uncaught exception and kills the\n * process**, taking every other connection and every in-flight durable commit with it. When it\n * bottoms out in a `void`-ed async method, it becomes an **unhandled rejection**, same outcome,\n * and it also skips whatever the method had left to do, which on the ACK path means the\n * acknowledgement is never sent.\n *\n * A metrics tap, an audit logger, a `console.log` with a typo: any of them, in a consumer's code,\n * could take down an MLLP interface. That is not an acceptable failure mode for a transport\n * carrying clinical messages.\n *\n * ## The rule\n *\n * **No `emit()` reachable from a transport, accept, or handshake callback, in *any* class, may go\n * uncontained.** Scoping this rule to one class is exactly the mistake that let it regress: the\n * hazard belongs to the *call stack*, not to `Connection`. `MllpServer` and `MllpClient` emit from\n * those callbacks too.\n *\n * @packageDocumentation\n */\n\nimport type { EventEmitter } from \"node:events\";\n\n/**\n * Emit an event, containing any throw from a subscriber.\n *\n * The subscriber's throw is reported through `onSubscriberThrow` (normally an `'error'` emission)\n * rather than being allowed to unwind into the callback that invoked us. Emission continues to be\n * synchronous and in-order for well-behaved subscribers, this only changes what happens when one\n * of them is broken.\n *\n * @param emitter - The emitter to emit on.\n * @param event - Event name.\n * @param payload - The (already frozen) event payload.\n * @param onSubscriberThrow - Reporter for a throwing subscriber. Must not itself throw.\n *\n * @example\n * ```typescript\n * safeEmit(this, \"connection\", frozenEvent, (err) => this._reportSubscriberThrow(err));\n * ```\n */\nexport function safeEmit(\n emitter: EventEmitter,\n event: string,\n payload: unknown,\n onSubscriberThrow?: (err: Error) => void,\n): void {\n try {\n emitter.emit(event, payload);\n } catch (err) {\n onSubscriberThrow?.(err instanceof Error ? err : new Error(String(err)));\n }\n}\n\n/**\n * Emit `'error'`, but only when someone is listening, and never letting the emission escape.\n *\n * Two hazards, both fatal, both closed here:\n *\n * 1. **No listener.** Node raises `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an\n * `EventEmitter` with no `'error'` listener. Since error reporting is reached from inside socket\n * callbacks and `catch` blocks, that throw would escape by the very route the containment exists\n * to close.\n * 2. **A throwing `'error'` listener.** Swallowed, deliberately, and this is the one place a throw\n * is dropped on the floor. Reporting is what just failed, so there is nowhere left to report it\n * to; the alternative is killing the process to complain that we could not complain.\n *\n * @example\n * ```typescript\n * safeEmitError(this, Object.freeze({ connectionId, error }));\n * ```\n */\nexport function safeEmitError(emitter: EventEmitter, payload: unknown): void {\n if (emitter.listenerCount(\"error\") === 0) return;\n try {\n emitter.emit(\"error\", payload);\n } catch {\n // Deliberately swallowed, see the JSDoc above.\n }\n}\n","/**\n * MLLP Server, createServer(), createStarterServer(), and MllpServer class.\n *\n * Provides the server-side MLLP transport: listen for inbound TCP connections,\n * decode MLLP-framed messages, surface them as Buffer payloads, and support\n * optional auto-ACK, keepalive, graceful shutdown, and AbortSignal cancellation.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received', payload.length, 'bytes from', meta.connectionId);\n * },\n * });\n * await server.listen(2575);\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createServer as netCreateServer, isIP, SocketAddress } from \"node:net\";\nimport type { Server as NetServer, Socket } from \"node:net\";\nimport { createServer as tlsCreateServer } from \"node:tls\";\nimport type { TLSSocket } from \"node:tls\";\nimport { EventEmitter } from \"node:events\";\nimport { Connection } from \"../connection/index.js\";\nimport { MllpConnectionError } from \"../connection/index.js\";\nimport { NetTransport, TlsTransport } from \"../transport/index.js\";\nimport type { ServerTlsOptions } from \"../transport/tls-options.js\";\nimport { MLLP_BIND_ALL_INTERFACES, type SecurityWarning } from \"../transport/security-warnings.js\";\nimport {\n resolveTlsCipherPolicy,\n type ResolvedTlsCipherPolicy,\n} from \"../transport/tls-cipher-policy.js\";\nimport { MllpTlsConfigurationError } from \"../transport/error.js\";\nimport { readNegotiatedTlsParameters } from \"../transport/negotiated-tls.js\";\nimport { encodeFrame } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning } from \"../framing/index.js\";\nimport { buildRawAck, rawAckUncorrelatable, resolveNackCode } from \"./ack.js\";\nimport type { AckCode, NegativeAckCode } from \"./ack.js\";\nimport { containsBatchOrExtraMessage, readMshSegment } from \"../internal/control-id.js\";\nimport {\n acceptAckRequested,\n acceptModeCounterpart,\n readAcceptCondition,\n} from \"../internal/ack-mode.js\";\nimport { ackModeDiagnosticMessage, type AckModeCode } from \"../internal/ack-mode-diagnostics.js\";\nimport { safeEmit, safeEmitError } from \"../internal/safe-emit.js\";\n\n/**\n * Wildcard (unspecified-address) bind detection for bind safety,\n * hosts that bind ALL interfaces and therefore require\n * `ServerOptions.allowWildcardBind: true`.\n *\n * NORMALIZES rather than string-matching:\n * - `''` (empty string, Node binds all interfaces)\n * - IPv4: every dotted-quad whose octets are all numerically zero (`'0.0.0.0'`)\n * - IPv6: any spelling that canonicalizes (via `net.SocketAddress`) to `'::'`\n * (`'::0'`, `'0:0:0:0:0:0:0:0'`, …) or to the IPv4-mapped unspecified\n * address `'::ffff:0.0.0.0'` (also `'::ffff:0:0'`)\n *\n * Non-IP strings return `false` here, but that is NOT the enforcement\n * boundary. getaddrinfo/inet_aton resolves shorthands this predicate cannot\n * see as strings (`'0'`, `'0.0'`, `'0.0.0'`, `'00.0.0.0'`, `'0x0.0.0.0'`,\n * wildcard-resolving hostnames), so `listen()` applies this predicate\n * **twice**: pre-bind on the requested host string (fast path), and\n * post-bind on the **OS-normalized bound address** from `server.address()`\n * (always canonical `'0.0.0.0'`/`'::'`), the authoritative check. The\n * guarantee is enforced against the address actually bound, not the\n * spelling requested.\n */\nfunction isWildcardHost(host: string): boolean {\n if (host === \"\") return true;\n const family = isIP(host);\n if (family === 4) {\n return host.split(\".\").every((octet) => Number(octet) === 0);\n }\n if (family === 6) {\n let canonical: string;\n try {\n canonical = new SocketAddress({ address: host, family: \"ipv6\" }).address;\n } catch {\n // isIP said valid IPv6 but SocketAddress refused, treat as non-wildcard\n // (the bind itself will fail downstream with the OS error).\n return false;\n }\n return canonical === \"::\" || canonical === \"::ffff:0.0.0.0\";\n }\n return false;\n}\n\n/**\n * Minimal, content-free peer-certificate summary surfaced on the `'connection'`\n * event when `ServerOptions.tls.clientAuth` is `'WANT'` or `'MUST'`\n * (ATNA ITI-19 mutual authentication). Never the full certificate object,\n * CN strings, the expiry date, and the verification outcome only.\n *\n * @example\n * ```typescript\n * server.on('connection', ({ peerCertificate }) => {\n * if (peerCertificate !== null && peerCertificate.authorized) {\n * logger.info({ cn: peerCertificate.subjectCN });\n * }\n * });\n * ```\n */\nexport interface PeerCertificateSummary {\n /** Subject Common Name, or `null` if absent/unavailable. */\n readonly subjectCN: string | null;\n /** Issuer Common Name, or `null` if absent/unavailable. */\n readonly issuerCN: string | null;\n /** Certificate expiry (`notAfter`), or `null` if absent/unavailable. */\n readonly validTo: string | null;\n /**\n * Whether the peer certificate chain was **verified** against\n * {@link ServerTlsOptions.ca} (`socket.authorized === true`).\n *\n * ⚠️ Under `clientAuth: 'WANT'` a peer certificate may be present yet\n * **unverified**, the connection is accepted regardless. Never make\n * authorization decisions on `subjectCN` (or any other field of this\n * summary) unless `authorized` is `true`. Under `clientAuth: 'MUST'` an\n * unverified certificate never reaches this point (the handshake is\n * rejected), so `authorized` is always `true` there.\n */\n readonly authorized: boolean;\n}\n\n/**\n * Metadata attached to each decoded MLLP message.\n *\n * All fields are `readonly`, the object is `Object.freeze()`'d before emission.\n *\n * @example\n * ```typescript\n * server.on('message', ({ payload, meta }) => {\n * console.log(meta.connectionId, '@', meta.byteOffset, 'warnings:', meta.warnings.length);\n * });\n * ```\n */\nexport interface MessageMeta {\n /** Stable UUID identifying the connection that delivered this message. */\n readonly connectionId: string;\n /** Byte offset of the frame start in the connection's data stream. */\n readonly byteOffset: number;\n /** Framing warnings emitted during decoding of this frame. */\n readonly warnings: readonly MllpWarning[];\n}\n\n/**\n * Why the server sent a **negative** acknowledgement instead of `AA` on the auto-ACK path.\n * A stable, **PHI-free** enum (no payload bytes, no control ID), safe to log and to key\n * metrics on.\n *\n * - `'handler-rejected'`, a commit-gated `onMessage` handler threw/rejected (the commit\n * contract: a positive ACK cannot precede a successful commit).\n * - `'uncorrelatable-inbound'`, the inbound could not carry a correlatable positive ACK\n * (no readable `MSH`, an empty MSH-10, or a batch/concatenated-message shape). A positive\n * `AA` here names a control ID the sender cannot match → timeout → resend → **duplicate\n * clinical message**. See {@link rawAckUncorrelatable}.\n * - `'discarded-bytes'`, the decoder flagged `MLLP_TRAILING_BYTES` for this frame: a\n * mid-payload `VT` made it **discard accumulated bytes** and deliver only the fragment\n * after it. The clinical message was destroyed in transit; a positive `AA` would tell the\n * sender a message we never received was delivered.\n */\nexport type NackReason = \"handler-rejected\" | \"uncorrelatable-inbound\" | \"discarded-bytes\";\n\n/**\n * Payload of the server `'nack'` event, emitted whenever the server responds with a\n * **negative** acknowledgement instead of `AA` on the auto-ACK path (the fail-safe commit\n * contract). This fires both when a commit-gated `autoAck: 'AA'` handler throws/rejects and\n * when the server **downgrades** a positive auto-ACK because the inbound could not be\n * correlated ({@link NackReason}).\n *\n * **PHI-safe by construction:** carries only the connection ID, the resolved\n * acknowledgement code, and a static `reason`, never the payload, the inbound control ID,\n * or the thrown error's message (which may carry PHI). The object is `Object.freeze()`'d\n * before emission.\n *\n * @example\n * ```typescript\n * server.on('nack', ({ connectionId, ackCode, reason }) => {\n * metrics.increment('mllp.nack', { code: ackCode, reason }); // e.g. reason='discarded-bytes'\n * });\n * ```\n */\nexport interface NackEvent {\n /** Connection that produced the negative acknowledgement. */\n readonly connectionId: string;\n /**\n * The negative acknowledgement code actually sent to the peer.\n *\n * `AE` or `AR` for a message acknowledged in the original mode, and the accept-mode\n * counterparts `CE` or `CR` where the inbound MSH-15 asked for an accept acknowledgement\n * for this disposition. It is always the code that went on the wire, so an observer\n * counting negative acknowledgements sees what the peer saw.\n */\n readonly ackCode: NackAckCode;\n /** Why the negative acknowledgement was sent (PHI-free). */\n readonly reason: NackReason;\n}\n\n/**\n * The negative acknowledgement codes a {@link NackEvent} can carry: both halves of the\n * negative side of HL7 Table 0008.\n *\n * @example\n * ```typescript\n * import type { NackAckCode } from '@cosyte/mllp';\n * const code: NackAckCode = 'CR';\n * ```\n */\nexport type NackAckCode = \"AE\" | \"AR\" | \"CE\" | \"CR\";\n\n/**\n * Payload of the server `'ackModeWarning'` event: something about an inbound message's\n * acknowledgement-mode request was read and reported, and the acknowledgement went out\n * anyway.\n *\n * Emitted when MSH-15 carries a value outside HL7 Table 0155. The acknowledgement is **not**\n * downgraded, negated or otherwise changed for that reason: reading a field is not\n * validating it, and answering a message the handler committed with anything but a positive\n * code would make a sender resend a message that is already stored.\n *\n * **PHI-safe by construction:** a connection ID, a stable code and a frozen registry\n * message, never the payload and never the field's value. The object is `Object.freeze()`'d\n * before emission.\n *\n * @example\n * ```typescript\n * server.on('ackModeWarning', ({ code }) => metrics.increment('mllp.ack_mode', { code }));\n * ```\n */\nexport interface AckModeWarningEvent {\n /** Connection the inbound message arrived on. */\n readonly connectionId: string;\n /** The acknowledgement-mode code being reported. */\n readonly code: AckModeCode;\n /** Frozen registry text for `code`. Never interpolated. */\n readonly message: string;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n\n/**\n * Observability snapshot returned by `server.getStats()`.\n *\n * All fields are JSON-serializable. `connections` and `activeConnections` both\n * reflect the current live connection count.\n *\n * @example\n * ```typescript\n * const stats = server.getStats();\n * console.log(JSON.stringify(stats)); // log-pipeline friendly\n * ```\n */\nexport interface ServerStats {\n /** Whether the server is currently accepting connections. */\n readonly listening: boolean;\n /** Bound port, or `null` before listen(). */\n readonly port: number | null;\n /** Bound host, or `null` before listen(). */\n readonly host: string | null;\n /**\n * Current live connection count.\n * Same value as `activeConnections`.\n */\n readonly connections: number;\n /** Current live connection count. Same value as `connections`. */\n readonly activeConnections: number;\n /** Aggregate bytes received across all current connections. */\n readonly totalBytesIn: number;\n /** Aggregate bytes sent across all current connections. */\n readonly totalBytesOut: number;\n /** Total connections accepted since listen() (monotonically increasing). */\n readonly acceptedTotal: number;\n /** Total connections closed since listen() (monotonically increasing). */\n readonly closedTotal: number;\n /** Whether this server is configured for TLS. Mirrors `ServerOptions.tls` being set. */\n readonly tls: boolean;\n /** Total `'tlsClientError'` events (failed TLS handshakes, incl. rejected client certs) since listen(). */\n readonly tlsClientErrorsTotal: number;\n}\n\n/**\n * Options for `createServer()`.\n *\n * @example\n * ```typescript\n * const opts: ServerOptions = {\n * onMessage: (payload, meta, conn) => {\n * const ack = buildAck(payload);\n * conn.send(ack);\n * },\n * framing: { maxFrameSizeBytes: 4 * 1024 * 1024 },\n * keepaliveIntervalMs: 60_000,\n * deadPeerTimeoutMs: 300_000,\n * drainTimeoutMs: 30_000,\n * };\n * ```\n */\nexport interface ServerOptions {\n /**\n * Called for each decoded MLLP message.\n *\n * Its role depends on `autoAck`, this is the **commit contract** (HL7 v2.5.1 §2.9.2):\n *\n * - **`autoAck: 'AA'` + this handler ⇒ commit-gated (the safe default).** The handler\n * is the durable-commit step. The server **awaits** it and only then sends the ACK:\n * resolve ⇒ `AA`; **throw/reject ⇒ `AE`** (or `AR` via {@link MllpAckError}), a\n * positive ACK can never precede a successful commit. Do **not** call `conn.send()`\n * here in this mode.\n * - **`autoAck` unset ⇒ manual mode.** The handler owns the response; build and send the\n * ACK yourself via `conn.send(encodeFrame(ackPayload))`. Its return value is ignored.\n * - **`autoAck: fn` ⇒ observation only.** `fn` builds the ACK; this handler runs first as\n * a side effect and its return value is ignored. Do not call `conn.send()` here.\n *\n * May be sync or async; an async handler is awaited in commit-gated mode.\n */\n onMessage?: (payload: Buffer, meta: MessageMeta, conn: Connection) => void | Promise<void>;\n\n /**\n * FrameReader tolerance options applied to every accepted connection.\n * Merged with SERVER_DEFAULT_FRAMING, caller-supplied values override defaults.\n * `onFrame` and `onWarning` are managed internally and must not be supplied here.\n */\n framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n\n /**\n * Per-connection warning subscriber. Called for every framing warning on every connection.\n */\n onWarning?: (w: MllpWarning) => void;\n\n /**\n * Auto-ACK mode. When set, the server builds and sends the ACK for each message.\n *\n * - **`'AA'`**, auto-acknowledge with the fail-safe **commit contract**:\n * - **With an `onMessage` handler ⇒ commit-gated (recommended).** The server awaits\n * `onMessage` (the durable-commit step), then sends `AA` on success or a **negative**\n * ACK on failure (`AE` by default; `AR` via {@link MllpAckError}). The positive ACK\n * **cannot precede a successful commit**, a handler throw can never yield `AA`.\n * - **Without an `onMessage` handler ⇒ transport-accept.** `AA` is sent on frame\n * receipt. This `AA` means only **\"bytes received and framed\"**, *not*\n * \"application-processed\". ⚠️ For clinical messages this is unsafe on its own:\n * pair `'AA'` with an `onMessage` handler that durably commits, so the ACK reflects\n * real processing.\n * - **`fn`**, `fn(payload, meta, conn)` builds the ACK bytes the server sends; the\n * caller fully owns MSA-1 (e.g. to emit enhanced-mode `CA`/`CE`/`CR`).\n *\n * The `'message'` event always fires BEFORE the ACK is sent. Do NOT call\n * `conn.send()` in `onMessage` when `autoAck` is set, this results in two ACKs.\n */\n autoAck?:\n | \"AA\"\n | ((payload: Buffer, meta: MessageMeta, conn: Connection) => Buffer | Promise<Buffer>);\n\n /**\n * TCP keepalive probe interval in ms.\n * Calls `socket.setKeepAlive(true, ms)` on each accepted socket.\n * Uses OS TCP stack to detect dead peers (half-open, network partitions).\n * Distinct from `deadPeerTimeoutMs` (application-level idle close).\n */\n keepaliveIntervalMs?: number;\n\n /**\n * Application-level idle close timeout in ms.\n * If no HL7 messages are received on a connection for this interval, the connection\n * is destroyed via `conn.destroy(new Error('idle timeout'))`.\n * Resets on every `'message'` event. Distinct from `keepaliveIntervalMs` (OS TCP probe).\n */\n deadPeerTimeoutMs?: number;\n\n /**\n * Graceful drain timeout passed to `conn.close()` during `server.close()`.\n * Default: 30 000 ms.\n */\n drainTimeoutMs?: number;\n\n /**\n * Enable TLS (MLLPS) for this server. When set, the server binds a\n * `tls.Server` instead of a plain `net.Server`, consumes `'secureConnection'`\n * (post-handshake sockets) instead of `'connection'`, and surfaces failed\n * handshakes via the `'tlsClientError'` event rather than crashing.\n *\n * Spec anchor: IHE ATNA ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @default undefined (plaintext TCP)\n */\n tls?: ServerTlsOptions;\n\n /**\n * Opt-in required to bind a wildcard host, a bind-safety guardrail.\n * Without this flag, `listen()` rejects a wildcard host in\n * two tiers: **literal spellings** (`'0.0.0.0'`, `'::'`, `''`, `'::0'`,\n * `'0:0:0:0:0:0:0:0'`, `'::ffff:0.0.0.0'`, …) reject **pre-bind** (fast\n * path, nothing is ever bound); **resolver-only shorthands** (`'0'`,\n * `'0.0'`, `'0x0.0.0.0'`, hostnames resolving to the unspecified\n * address, …) are caught **post-bind** against the OS-normalized bound\n * address, the just-bound server closes before any connection can be\n * accepted and `listen()` rejects, with no listening state and no\n * `'listening'` event either way. When `true`, binding a wildcard host\n * emits a one-time `'securityWarning'` (`MLLP_BIND_ALL_INTERFACES`) at\n * listen time.\n *\n * @default false\n */\n allowWildcardBind?: boolean;\n}\n\n/**\n * Options for `createStarterServer()`, the \"three lines of code\" factory.\n *\n * Extends `ServerOptions` with `port`, `host`, and `handleSignals`. Defaults:\n * `autoAck: 'AA'`, `drainTimeoutMs: 30_000`, `Symbol.asyncDispose` wired.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n *\n * const server = await createStarterServer({\n * port: 2575,\n * onMessage: async (payload) => {\n * await db.commit(payload); // the durable-commit step; a throw answers a negative ACK\n * },\n * });\n * // server is listening, auto-ACK enabled, Symbol.asyncDispose wired\n * await using _ = server; // closes on scope exit\n * ```\n */\nexport interface StarterServerOptions extends ServerOptions {\n /** Port to listen on. */\n port: number;\n /**\n * Host to bind to. Default `'127.0.0.1'` (bind-safety hardening,\n * was `'0.0.0.0'`; binding all interfaces now requires\n * `ServerOptions.allowWildcardBind: true`).\n */\n host?: string;\n /**\n * Register `process.once('SIGTERM')` and `process.once('SIGINT')` handlers that\n * call `server.close()` then `process.exit(0)`. Default: `false`.\n *\n * Handlers are automatically removed when `server.close()` is called, so\n * `process.listenerCount('SIGTERM') === 0` after close() completes, preventing\n * handler accumulation across test instances or multiple server restarts.\n */\n handleSignals?: boolean;\n}\n\n/**\n * Default framing options applied to every server-side FrameReader.\n *\n * Reflects real-world MLLP device behavior:\n * - `allowFsOnly: true`, accept FS without trailing CR\n * - `allowLfAfterFs: true`, accept FS+LF (common vendor deviation)\n * - `allowLeadingWhitespace: true`, accept leading whitespace before VT\n * - `allowMissingLeadingVt: false`, require VT framing (stricter than FS-only tolerance)\n */\nconst SERVER_DEFAULT_FRAMING: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\"> = {\n allowFsOnly: true,\n allowLfAfterFs: true,\n allowLeadingWhitespace: true,\n allowMissingLeadingVt: false,\n};\n\n/**\n * MLLP TCP server, wraps `net.Server` without extending it.\n *\n * Each accepted socket is wrapped in a `NetTransport`, connected to a `Connection`\n * with server-level framing options, and added to `_connections`. Messages are\n * surfaced via the `'message'` event and the `onMessage` callback.\n *\n * Public events: `'listening'`, `'connection'`, `'message'`, `'nack'`, `'error'`, `'close'`.\n * The `'nack'` event ({@link NackEvent}) fires whenever the server returns a negative ACK\n * instead of `AA` on the auto-ACK path, a commit-gated `autoAck: 'AA'` handler failing, or\n * a positive auto-ACK **downgraded** because the inbound could not be correlated (an\n * unreadable/uncorrelatable message, or one whose bytes the decoder discarded). The\n * {@link NackReason} distinguishes the causes.\n *\n * **`'error'` contract:** underlying `net.Server`/`tls.Server` errors are forwarded to the\n * `'error'` event whenever a listener is attached. With **no** listener, the outcome depends on\n * server state: during a `listen()` (the bind window) the error **rejects the `listen()` promise**\n * the primary error surface, and the process never crashes on a bind error; with no `listen()`\n * in flight and the server not serving (e.g. a stale async error after `close()`) the error is\n * dropped; but **while serving**, an unlistened runtime error (e.g. accept-loop `EMFILE`) keeps\n * Node's fail-loud crash-on-unlistened-`'error'` convention, a silent accept outage is\n * impossible. Caveat for `'error'` listeners: the forwarder runs **before** the internal\n * `listen()` rejection handler, so an `'error'` listener that synchronously calls `close()`\n * during the bind window changes the `listen()` rejection from the bind error to the typed\n * close-during-listen `MllpConnectionError`, match on the `'error'` event payload, not the\n * rejection, if you do that.\n * TLS (MLLPS) adds three more: `'tlsClientError'` (a failed TLS handshake, the\n * server logs it and keeps serving other connections), `'securityWarning'` (loud,\n * one-time notice when a wildcard host is bound via `allowWildcardBind: true`), and\n * `'tlsNegotiated'` ({@link NegotiatedTlsParameters}, the protocol version and cipher\n * suite each accepted link actually negotiated, once per completed handshake and never\n * on a plaintext listener).\n * All event payloads are `Object.freeze()`'d before emission.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received from', meta.connectionId);\n * },\n * });\n * await server.listen(2575);\n * // await using server = createServer({ ... }); // Symbol.asyncDispose\n * ```\n */\nexport class MllpServer extends EventEmitter {\n private readonly _netServer: NetServer;\n private readonly _connections: Set<Connection> = new Set();\n private readonly _opts: ServerOptions;\n /** `true` when `ServerOptions.tls` is set, `_netServer` is actually a `tls.Server`. */\n private readonly _isTls: boolean;\n\n private _listening = false;\n /** Single-flight guard: `true` while a `listen()` call is settling. */\n private _listenInFlight = false;\n /**\n * Settle hook for an in-flight `listen()`. Non-null only while a\n * `listen()` is in flight; invoked by `close()` so a close racing startup\n * rejects the pending `listen()` (typed) instead of leaving it hung with\n * the single-flight guard stuck. Cleared on every settle path.\n */\n private _pendingListenSettle: (() => void) | null = null;\n private _port: number | null = null;\n private _host: string | null = null;\n private _acceptedTotal = 0;\n private _closedTotal = 0;\n /** Count of `'tlsClientError'` events since listen(). */\n private _tlsClientErrorsTotal = 0;\n /**\n * A TLS configuration this server will not serve on: an offered-suite list\n * it cannot honour, or ephemeral Diffie-Hellman parameters it cannot use. Set\n * at construction, and the ONLY thing `listen()` consults before it binds:\n * such a server never binds, never accepts, and never negotiates anything,\n * which is what makes \"no fallback to the default list, and none to a server\n * with no Diffie-Hellman parameters\" a structural property rather than a\n * promise. `null` when the configuration is fine.\n */\n private readonly _tlsConfigError: MllpTlsConfigurationError | null = null;\n\n /**\n * Construct an MLLP server. Created idle; call `listen()` (or use\n * {@link createServer}/{@link createStarterServer}) to begin accepting connections.\n *\n * @param opts - Server options (bind host/port, auto-ACK policy, message handler, framing, …).\n */\n constructor(opts: ServerOptions) {\n super();\n this._opts = opts;\n this._isTls = opts.tls !== undefined;\n\n // Resolve and PROVE the offered-suite list before any TLS context exists.\n // A construction-time throw could not reject listen(), which is what a\n // caller awaits, so the refusal is carried to listen() instead. On that\n // path no TLS context is built at all and the placeholder below is never\n // bound, which is what makes \"no fallback to the runtime default list\" a\n // structural property of a refused configuration rather than a promise.\n let cipherPolicy: ResolvedTlsCipherPolicy | null = null;\n if (opts.tls !== undefined) {\n try {\n cipherPolicy = resolveTlsCipherPolicy(opts.tls, \"server\");\n } catch (err) {\n if (!(err instanceof MllpTlsConfigurationError)) throw err;\n this._tlsConfigError = err;\n }\n }\n\n if (opts.tls !== undefined && cipherPolicy === null) {\n // Refused configuration: an unbound, connection-less placeholder that\n // exists only so the field is definitely assigned. listen() rejects\n // before it is ever touched.\n this._netServer = netCreateServer();\n } else if (opts.tls !== undefined && cipherPolicy !== null) {\n const tlsOpts: ServerTlsOptions = opts.tls;\n const clientAuth = tlsOpts.clientAuth ?? \"NONE\";\n const tlsServer = tlsCreateServer({\n cert: tlsOpts.cert,\n key: tlsOpts.key,\n ...(tlsOpts.ca !== undefined ? { ca: tlsOpts.ca } : {}),\n ...(tlsOpts.passphrase !== undefined ? { passphrase: tlsOpts.passphrase } : {}),\n minVersion: tlsOpts.minVersion ?? \"TLSv1.2\",\n ...(tlsOpts.maxVersion !== undefined ? { maxVersion: tlsOpts.maxVersion } : {}),\n ...cipherPolicy,\n requestCert: clientAuth !== \"NONE\",\n rejectUnauthorized: clientAuth === \"MUST\",\n });\n this._netServer = tlsServer;\n tlsServer.on(\"secureConnection\", (socket: TLSSocket) => {\n this._onSocketAccepted(socket);\n });\n // 'tlsClientError' fires for failed handshakes (incl. rejected client\n // certs under mTLS 'MUST'). The server MUST NOT crash and MUST keep\n // accepting other connections, only the error's message/code and the\n // remote address are surfaced; never payload bytes or a cert dump.\n tlsServer.on(\"tlsClientError\", (err: Error, socket: TLSSocket) => {\n this._tlsClientErrorsTotal += 1;\n const nodeErr = err as NodeJS.ErrnoException;\n const event: {\n remoteAddress: string | null;\n remotePort: number | null;\n message: string;\n code: string | null;\n timestamp: Date;\n } = Object.freeze({\n remoteAddress: socket.remoteAddress ?? null,\n remotePort: socket.remotePort ?? null,\n message: err.message,\n code: typeof nodeErr.code === \"string\" ? nodeErr.code : null,\n timestamp: new Date(),\n });\n // Contained: this runs inside tls.Server's own 'tlsClientError' listener, so a throwing\n // subscriber would kill the process on a failed handshake, flatly contradicting this\n // handler's whole purpose (\"MUST NOT crash and MUST keep accepting other connections\").\n this._emitContained(\"tlsClientError\", event);\n });\n } else {\n this._netServer = netCreateServer();\n this._netServer.on(\"connection\", (socket: Socket) => {\n this._onSocketAccepted(socket);\n });\n }\n\n // Forward net.Server errors to the public 'error' event, guarded by\n // server state, an unlistened EventEmitter 'error' emission THROWS,\n // and this constructor-time listener runs BEFORE listen()'s own\n // once('error') rejection handler (registration order), so an unguarded\n // re-emit crashed the process on a plain bind error (EADDRINUSE,\n // EACCES, …) instead of letting listen() reject. Contract (also in the\n // class JSDoc):\n // • 'error' listener attached ⇒ always forwarded.\n // • no listener + a listen() in flight (bind window) ⇒ dropped here;\n // the listen() promise rejects with this error (primary surface).\n // • no listener + not listening (e.g. stale async error after\n // close()) ⇒ dropped; there is no consumer to surface it to.\n // • no listener + SERVING (this._listening) ⇒ re-emit unguarded,\n // which throws, Node's fail-loud convention is deliberately kept\n // for runtime accept-loop errors (EMFILE/ENFILE): a silent accept\n // outage on a healthcare listener must be impossible.\n // MLLP-10 refinement: the deliberate fail-loud branch below is PRESERVED exactly (unlistened +\n // serving ⇒ unguarded re-emit ⇒ throw), but a *throwing subscriber* is a different thing from\n // an unobservable accept outage, and it must not crash the server. When a listener exists, the\n // emit is contained.\n this._netServer.on(\"error\", (err: Error) => {\n if (this.listenerCount(\"error\") > 0) {\n safeEmitError(this, err); // contained, a throwing 'error' subscriber is not fail-loud\n } else if (this._listening) {\n this.emit(\"error\", err); // DELIBERATE: unlistened + serving ⇒ Node's fail-loud convention\n }\n });\n }\n\n /**\n * Start listening on the given port.\n *\n * Resolves once the TCP socket is bound and emits `'listening'` with\n * `Object.freeze({ port: actualPort, host: actualHost })`.\n *\n * **Single-flight:** one `listen()` per server lifecycle at a time. A call\n * while the server is already listening, or while another `listen()` is\n * still in flight, rejects with a typed `MllpConnectionError` (concurrent\n * binds raced each other's post-bind safety checks). Call `close()` before\n * re-listening; sequential `listen()` → `close()` → `listen()` is fine.\n *\n * **A refused TLS configuration** (the runtime rejects the offered suite\n * list, `tls.atnaTransportSecurity` and `tls.ciphers` both declare one, or\n * `tls.dhParameters` are not usable) rejects every `listen()` on this server\n * with a typed `MllpTlsConfigurationError`, checked before the bind. Such a\n * server never listens, never negotiates on any other list and never runs\n * without the Diffie-Hellman parameters it was given; construct a new one\n * with a configuration that resolves.\n *\n * **`close()` during an in-flight `listen()`** rejects that `listen()` with\n * a typed `MllpConnectionError` (never a hang) and clears the single-flight\n * guard, a subsequent `listen()` on the same server works. This makes\n * `Symbol.asyncDispose` (which delegates to `close()`) safe even before a\n * `listen()` has settled. (Exception: `close({ signal })` with an\n * already-aborted signal is a no-op AbortError rejection, the in-flight\n * `listen()` is left to settle on its own bind outcome; see `close()`.)\n *\n * @param port - TCP port to bind. Use `0` to let the OS assign an ephemeral port.\n * @param hostOrOpts - Host string or object with `host` and optional `signal`.\n * Default host: `'127.0.0.1'` (bind-safety hardening). Wildcard hosts\n * are rejected unless `ServerOptions.allowWildcardBind: true`, literal\n * spellings pre-bind, resolver-only shorthands post-bind via the\n * OS-normalized bound address.\n *\n * @example\n * ```typescript\n * await server.listen(2575); // binds 127.0.0.1\n * await server.listen(0, '127.0.0.1');\n * await server.listen(0, { host: '127.0.0.1', signal: ac.signal });\n * ```\n */\n listen(\n port: number,\n hostOrOpts?: string | { host?: string; signal?: AbortSignal },\n ): Promise<void> {\n const host = typeof hostOrOpts === \"string\" ? hostOrOpts : (hostOrOpts?.host ?? \"127.0.0.1\");\n const signal =\n typeof hostOrOpts === \"object\" && hostOrOpts !== null ? hostOrOpts.signal : undefined;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // A TLS configuration that cannot be honoured is refused BEFORE the bind,\n // and before the single-flight guard is taken: nothing is listening,\n // nothing was accepted, no other cipher list was ever substituted, and no\n // server ran without the Diffie-Hellman parameters it was handed. It is\n // checked on every call, so a refused server stays refused rather than\n // becoming listenable on a retry.\n if (this._tlsConfigError !== null) {\n return Promise.reject(this._tlsConfigError);\n }\n\n // Phase 8 bind safety, SINGLE-FLIGHT guard. Two concurrent listen()\n // calls on one server race each other's post-bind checks: the losing\n // call's 'listening' handler can observe the winner's (or a just-closed)\n // socket and record listening state for a bind that no longer exists,\n // a green health check with nothing bound. One listen per lifecycle;\n // close() before re-listening.\n if (this._listening || this._listenInFlight) {\n return Promise.reject(\n new MllpConnectionError(\n this._listening\n ? \"listen() rejected: server is already listening, call close() before re-listening\"\n : \"listen() rejected: another listen() is already in flight on this server\",\n {\n cause: new Error(\"listen already listening or in flight\"),\n phase: \"connect\",\n },\n ),\n );\n }\n\n // Phase 8 bind safety, pre-bind FAST PATH: reject a literal wildcard\n // host BEFORE binding unless the operator explicitly opted in via\n // allowWildcardBind. isWildcardHost normalizes IP literals ('::0',\n // '0:0:0:0:0:0:0:0', '::ffff:0.0.0.0' …); getaddrinfo shorthands it\n // cannot see as strings ('0', '0x0.0.0.0', …) are caught by the\n // authoritative POST-BIND check in onListening below.\n if (isWildcardHost(host) && this._opts.allowWildcardBind !== true) {\n return Promise.reject(\n new MllpConnectionError(\n `refusing to bind wildcard host '${host}', set ServerOptions.allowWildcardBind: true to bind all interfaces`,\n {\n cause: new Error(\"wildcard bind requires allowWildcardBind\"),\n phase: \"connect\",\n },\n ),\n );\n }\n\n this._listenInFlight = true;\n\n return new Promise<void>((resolve, reject) => {\n // ONE idempotent settle point for every outcome of this listen()\n // (success, abort, close-during-listen, bind error, addr-null reject,\n // post-bind wildcard reject). First caller wins; every path gets the\n // same cleanup, abort listener, netServer listeners, the close()\n // settle hook, and the single-flight guard, so no path can leak a\n // listener, strand the guard, or re-settle a settled promise.\n let settled = false;\n const settle = (\n outcome: { ok: true } | { ok: false; error: Error },\n opts?: { closeServer?: boolean },\n ): void => {\n if (settled) return;\n settled = true;\n this._pendingListenSettle = null;\n signal?.removeEventListener(\"abort\", abortHandler);\n this._netServer.removeListener(\"listening\", onListening);\n this._netServer.removeListener(\"error\", onError);\n if (opts?.closeServer === true) {\n this._netServer.close();\n }\n this._listenInFlight = false;\n if (outcome.ok) {\n resolve();\n } else {\n // Pass the rejection reason through untouched, AbortError\n // (DOMException), MllpConnectionError, and raw bind errors keep\n // their identity for callers matching on name/instanceof.\n reject(outcome.error);\n }\n };\n\n const abortHandler = () => {\n settle(\n { ok: false, error: new DOMException(\"Aborted\", \"AbortError\") },\n {\n closeServer: true,\n },\n );\n };\n\n if (signal !== undefined) {\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n // close() (or Symbol.asyncDispose) racing an in-flight listen():\n // net.Server.close() nulls the handle, and the pending 'listening'\n // emission is guarded on the handle, so NEITHER 'listening' NOR\n // 'error' ever fires for the in-flight bind. Without a settle path the\n // promise hangs forever and every later listen() rejects \"in flight\".\n // close() settles the pending listen PROACTIVELY through this hook.\n // (Deliberately NOT a netServer once('close') listener: the 'close'\n // event from a PREVIOUS lifecycle's close() is delivered\n // asynchronously and can land after a NEW listen() has registered its\n // listeners, spuriously rejecting the fresh attempt. The netServer is\n // private, so our own close(), and the settle paths below, which\n // settle themselves, are the only close initiators. close() itself\n // closes the netServer right after invoking this hook, so the hook\n // does not.)\n this._pendingListenSettle = () => {\n settle({\n ok: false,\n error: new MllpConnectionError(\n \"listen() failed: server was closed while listen() was in flight\",\n {\n cause: new Error(\"close() during in-flight listen()\"),\n phase: \"connect\",\n },\n ),\n });\n };\n\n const onListening = () => {\n if (settled) return;\n // The bind outcome is now known and this handler settles the promise\n // synchronously below, drop the close()-settle hook AND the abort\n // listener FIRST. The hook: a close() called from inside the\n // 'listening' event handler must not reject a bind that succeeded.\n // The abort listener: the success path below emits 'listening' /\n // 'securityWarning' BEFORE settling, and an abort fired from inside\n // one of those handlers would otherwise close the just-bound server\n // AFTER listening state is recorded, stranding `listening: true`\n // with nothing bound (the exact hazard the post-bind checks exist to\n // prevent). Once the bind has succeeded, an abort of the listen\n // signal is too late and is deliberately ignored; use close().\n this._pendingListenSettle = null;\n signal?.removeEventListener(\"abort\", abortHandler);\n\n const addr = this._netServer.address();\n // address() is always an AddressInfo object for a bound TCP server;\n // null (or a string, pipe/IPC servers, which this class never\n // creates) means the server was closed out from under this bind.\n // NEVER fall back to the requested port/host strings, that records\n // listening state for a bind that does not exist. The single-flight\n // guard above should make this branch unreachable; it stays as\n // defense-in-depth.\n if (addr === null || typeof addr === \"string\") {\n settle(\n {\n ok: false,\n error: new MllpConnectionError(\n \"listen() failed: server was closed before the bind completed\",\n {\n cause: new Error(\"server.address() returned no TCP address\"),\n phase: \"connect\",\n },\n ),\n },\n { closeServer: true },\n );\n return;\n }\n const actualPort = addr.port;\n const actualHost = addr.address;\n\n // Phase 8 bind safety, POST-BIND enforcement against the\n // OS-NORMALIZED bound address. getaddrinfo/inet_aton resolves\n // shorthands the pre-bind string check cannot see ('0', '0.0',\n // '0.0.0', '00.0.0.0', '0x0.0.0.0', hostnames that resolve to the\n // unspecified address, …); the kernel-reported address is canonical\n // ('0.0.0.0' / '::'), so this check is authoritative. On violation:\n // close the just-bound server immediately and reject, no listening\n // state is recorded and no 'listening' event is emitted.\n // The sub-tick bind window is ACCEPT-SAFE, this handler runs\n // synchronously on 'listening', before the event loop can deliver\n // any 'connection'/'secureConnection' for the just-bound socket, so\n // no connection is ever accepted on a rejected wildcard bind. Do\n // not reorder or defer this branch.\n if (isWildcardHost(actualHost) && this._opts.allowWildcardBind !== true) {\n settle(\n {\n ok: false,\n error: new MllpConnectionError(\n `refusing to bind wildcard host '${host}' (bound address '${actualHost}'), ` +\n \"set ServerOptions.allowWildcardBind: true to bind all interfaces\",\n {\n cause: new Error(\"wildcard bind requires allowWildcardBind\"),\n phase: \"connect\",\n },\n ),\n },\n { closeServer: true },\n );\n return;\n }\n\n this._listening = true;\n this._port = actualPort;\n this._host = actualHost;\n\n // The bind has succeeded, nothing a 'listening'/'securityWarning'\n // subscriber does may undo that. A THROWING subscriber must not\n // strand the settle (the promise would hang and the single-flight\n // guard would wedge every later listen()) and must not suppress a\n // LATER emission: each emit is contained SEPARATELY (a throw in\n // 'listening' cannot swallow the securityWarning), surfaced via the\n // guarded 'error' tap (D-04, a handler error never crashes the\n // server) with the tap itself contained too (a throwing 'error'\n // listener on top of a throwing subscriber drops rather than\n // strands), so listen() always resolves.\n const emitSubscriberSafe = (event: \"listening\" | \"securityWarning\", payload: unknown) => {\n try {\n this.emit(event, payload);\n } catch (subscriberErr: unknown) {\n try {\n this._emitErrorIfListened(\n subscriberErr instanceof Error ? subscriberErr : new Error(String(subscriberErr)),\n );\n } catch {\n // Double user bug ('listening'/'securityWarning' subscriber\n // threw AND the 'error' listener threw): dropping beats\n // stranding the settle or crashing a successfully-bound server.\n }\n }\n };\n\n // Phase 8, loud, one-time notice when a wildcard host is actually\n // bound (keyed off the OS-normalized actualHost so shorthand\n // spellings warn too; single emission site). The operator channel\n // (process.emitWarning) fires FIRST, it has no subscribers, so no\n // throwing event listener can ever suppress the mandated\n // MLLP_BIND_ALL_INTERFACES notice on a live wildcard bind.\n const wildcardBound = isWildcardHost(actualHost);\n const wildcardMessage =\n `MLLP server is bound to ALL network interfaces (host='${actualHost}'), ` +\n \"set a specific bind address unless this is intentional.\";\n if (wildcardBound) {\n process.emitWarning(wildcardMessage, { code: MLLP_BIND_ALL_INTERFACES });\n }\n\n emitSubscriberSafe(\"listening\", Object.freeze({ port: actualPort, host: actualHost }));\n\n if (wildcardBound) {\n const warning: SecurityWarning = Object.freeze({\n code: MLLP_BIND_ALL_INTERFACES,\n message: wildcardMessage,\n host: actualHost,\n port: actualPort,\n timestamp: new Date(),\n });\n emitSubscriberSafe(\"securityWarning\", warning);\n }\n\n settle({ ok: true });\n };\n\n const onError = (err: Error) => {\n settle({ ok: false, error: err });\n };\n\n this._netServer.once(\"listening\", onListening);\n this._netServer.once(\"error\", onError);\n this._netServer.listen(port, host);\n });\n }\n\n /**\n * Stop accepting new connections and gracefully close all active connections.\n *\n * Sequence:\n * 1. `net.Server.close()`, stops accepting new connections immediately\n * 2. If `_connections` is empty: resolves immediately (no drain needed)\n * 3. Calls `_drainAll(drainTimeoutMs)`, Promise.all + side-effect setTimeout\n * that force-destroys stragglers after the drain window\n *\n * Calling `close()` while a `listen()` is still in flight proactively\n * settles that `listen()` with a typed `MllpConnectionError` rejection\n * and clears the single-flight guard, the server is immediately\n * re-listenable. `Symbol.asyncDispose` (which delegates here) is\n * therefore safe before `listen()` settles. **Qualification:** a\n * `close({ signal })` whose signal is **already aborted** rejects\n * immediately with `AbortError` and performs no work, it does NOT close\n * the server and does NOT settle the in-flight `listen()`, which simply\n * continues and settles on its own bind outcome.\n *\n * @param opts.drainTimeoutMs - Override drain timeout (default: `opts.drainTimeoutMs ?? 30_000`).\n * @param opts.signal - AbortSignal to cancel the close operation. On abort, all\n * active connections are destroyed and the promise rejects with AbortError.\n *\n * @example\n * ```typescript\n * await server.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number; signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Phase 8, settle an in-flight listen() FIRST (typed rejection). A\n // close racing startup (SIGTERM handler, test teardown, an `await using`\n // scope exiting before listen() was awaited) would otherwise leave the\n // listen() promise hung forever: net.Server.close() nulls the handle and\n // the pending 'listening' emission is handle-guarded, so neither\n // 'listening' nor 'error' ever fires for the in-flight bind, and the\n // stuck single-flight guard would reject every later listen().\n this._pendingListenSettle?.();\n\n // Stop accepting new connections\n this._netServer.close();\n this._listening = false;\n\n // If no active connections, we're done, emit 'close' and resolve\n // No abort handler registered on this path, nothing to remove.\n if (this._connections.size === 0) {\n // Contained: a throwing 'close' subscriber must not reject close() nor skip its cleanup.\n this._emitContained(\"close\", Object.freeze({}));\n return Promise.resolve();\n }\n\n // Wire AbortSignal, abort during drain force-destroys all connections\n let abortHandler: (() => void) | undefined;\n const abortPromise =\n signal !== undefined\n ? new Promise<\"aborted\">((_resolve, reject) => {\n abortHandler = () => {\n // Force-destroy all active connections on abort\n for (const conn of this._connections) {\n conn.destroy();\n }\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n })\n : null;\n\n try {\n const drainTimeoutMs = opts?.drainTimeoutMs ?? this._opts.drainTimeoutMs ?? 30_000;\n if (abortPromise !== null) {\n await Promise.race([this._drainAll(drainTimeoutMs), abortPromise]);\n } else {\n await this._drainAll(drainTimeoutMs);\n }\n } finally {\n if (abortHandler !== undefined && signal !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n // Emit 'close' after all connections have drained (SERVER-10: frozen payload). Contained.\n this._emitContained(\"close\", Object.freeze({}));\n }\n\n /**\n * Drain all active connections with a shared timeout.\n *\n * Uses Promise.all (not Promise.race) for coordination, the timeout is a\n * side effect that calls `conn.destroy()` on stragglers after `drainTimeoutMs`.\n * Promise.all resolves when all `conn.close()` promises settle (which happens\n * because `conn.destroy()` transitions connections to CLOSED).\n *\n * @param drainTimeoutMs - Maximum time to wait for connections to drain.\n */\n private async _drainAll(drainTimeoutMs: number): Promise<void> {\n // Snapshot connections for the close() call map (fixed at call time)\n const snapshot = [...this._connections];\n const closePromises = snapshot.map((conn) => conn.close({ drainTimeoutMs }));\n\n // Side-effect timeout: iterate LIVE this._connections (not snapshot) to only\n // destroy() connections that haven't already closed during the drain window\n const timeoutHandle = setTimeout(() => {\n for (const conn of this._connections) {\n conn.destroy();\n }\n }, drainTimeoutMs);\n // Do not keep the process alive just for this drain timer\n timeoutHandle.unref();\n\n try {\n await Promise.all(closePromises);\n } finally {\n clearTimeout(timeoutHandle);\n }\n }\n\n /**\n * Async disposal, delegates to `close()` for `await using` support.\n *\n * @example\n * ```typescript\n * await using server = createServer({ onMessage: handler });\n * await server.listen(2575);\n * // server.close() is called automatically at end of block\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n return this.close();\n }\n\n /**\n * Return a JSON-serializable observability snapshot.\n *\n * `totalBytesIn` and `totalBytesOut` aggregate from live connections at call time.\n *\n * @example\n * ```typescript\n * const stats = server.getStats();\n * logger.info(JSON.stringify(stats));\n * ```\n */\n getStats(): ServerStats {\n // Aggregate totalBytesIn/Out from live connections at call time (OBS-02, D-13)\n let totalBytesIn = 0;\n let totalBytesOut = 0;\n for (const conn of this._connections) {\n const s = conn.getStats();\n totalBytesIn += s.bytesIn;\n totalBytesOut += s.bytesOut;\n }\n return {\n listening: this._listening,\n port: this._port,\n host: this._host,\n connections: this._connections.size,\n activeConnections: this._connections.size,\n totalBytesIn,\n totalBytesOut,\n acceptedTotal: this._acceptedTotal,\n closedTotal: this._closedTotal,\n tls: this._isTls,\n tlsClientErrorsTotal: this._tlsClientErrorsTotal,\n };\n }\n\n /**\n * Emit on `'error'` only when a listener is attached, an unlistened\n * `EventEmitter` `'error'` emission throws (see the class JSDoc error\n * contract). The single guard shared by the optional-tap forwarding sites\n * in this class; the constructor's net.Server forwarder deliberately does\n * NOT use it (its while-serving branch re-emits unguarded to keep Node's\n * fail-loud convention).\n */\n /**\n * Emit a server event, containing a throwing subscriber.\n *\n * Every event this server emits is reachable from a callback we do not own, the `net.Server`\n * `'connection'` listener, the `tls.Server` `'tlsClientError'` listener, a socket's `'data'`\n * listener (via `Connection`), or the `catch` block of a `void`-ed async ACK task. A throwing\n * subscriber would therefore unwind into that callback and kill the process (or reject a void-ed\n * promise, which does the same and additionally skips the rest of the method).\n *\n * See `src/internal/safe-emit.ts` for the full rationale. The throw is re-surfaced on `'error'`.\n */\n private _emitContained(event: string, payload: unknown): void {\n safeEmit(this, event, payload, (err) => {\n this._emitErrorIfListened(err);\n });\n }\n\n private _emitErrorIfListened(errEvent: unknown): void {\n // Guards the unlistened case AND contains a throwing 'error' subscriber, see safe-emit.ts.\n safeEmitError(this, errEvent);\n }\n\n // ---------------------------------------------------------------------------\n // Private: per-socket setup\n // ---------------------------------------------------------------------------\n\n private _onSocketAccepted(socket: Socket | TLSSocket): void {\n // TCP keepalive, must be set on the raw socket BEFORE passing to NetTransport (D-10)\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n\n const transport = this._isTls\n ? new TlsTransport(socket as TLSSocket)\n : new NetTransport(socket);\n const mergedFraming: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\"> = {\n ...SERVER_DEFAULT_FRAMING,\n ...(this._opts.framing ?? {}),\n };\n\n const connOpts =\n this._opts.onWarning !== undefined\n ? { transport, framing: mergedFraming, onWarning: this._opts.onWarning }\n : { transport, framing: mergedFraming };\n const conn = new Connection(connOpts);\n\n // Set beforeClose no-op hook (Plan 04 will wire autoAck drain here if needed)\n conn.beforeClose = () => Promise.resolve();\n\n // Default 'error' handler on connection, prevents ERR_UNHANDLED_ERROR when\n // auto-ACK or transport errors are emitted on a connection with no user-attached\n // error listener. Forwards to server's 'error' event only when listeners exist;\n // otherwise silently swallows (D-04: server never crashes on connection errors).\n conn.on(\"error\", (errEvent: unknown) => {\n this._emitErrorIfListened(errEvent);\n });\n\n this._acceptedTotal++;\n this._connections.add(conn);\n\n // Remove from tracking when connection closes (CLOSED) or disconnects (DISCONNECTED).\n // Connection transitions to DISCONNECTED when peer closes gracefully (CONNECTED → DISCONNECTED).\n // It reaches CLOSED on destroy() / drain timeout / CONNECTING or RECONNECTING close.\n // We remove on either terminal-ish state to avoid leaking connections in _connections.\n // Single-fire guard prevents double-counting when both 'disconnect' and 'close' fire.\n let ended = false;\n const _onConnEnded = () => {\n if (ended) return;\n ended = true;\n this._connections.delete(conn);\n this._closedTotal++;\n };\n conn.once(\"close\", _onConnEnded);\n conn.once(\"disconnect\", _onConnEnded);\n\n // Wire dead-peer idle timeout (D-11), reset on every message\n if (this._opts.deadPeerTimeoutMs !== undefined) {\n const timeoutMs = this._opts.deadPeerTimeoutMs;\n let deadPeerTimer: ReturnType<typeof setTimeout> = setTimeout(() => {\n conn.destroy(new Error(\"idle timeout\"));\n }, timeoutMs);\n deadPeerTimer.unref();\n\n conn.on(\"message\", () => {\n clearTimeout(deadPeerTimer);\n deadPeerTimer = setTimeout(() => {\n conn.destroy(new Error(\"idle timeout\"));\n }, timeoutMs);\n deadPeerTimer.unref();\n });\n\n conn.once(\"close\", () => {\n clearTimeout(deadPeerTimer);\n });\n }\n\n // Notify the connection that the socket is connected (server-side, already connected)\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n\n // Wire message handler: always emit 'message' (received+framed) first, then route the\n // ACK by mode (the commit contract, see ServerOptions.autoAck). D-04: a handler error\n // never crashes the server. Async dispatch is wrapped in void; rejections are handled\n // inside the dispatch methods.\n conn.on(\n \"message\",\n (event: {\n payload: Buffer;\n connectionId: string;\n byteOffset: number;\n warnings: readonly MllpWarning[];\n }) => {\n const { payload, connectionId, byteOffset, warnings } = event;\n const meta: MessageMeta = Object.freeze({\n connectionId,\n byteOffset,\n warnings,\n });\n\n // D-03: emit 'message' BEFORE any ACK dispatch.\n //\n // Contained: a `'message'` subscriber is an OBSERVER (a metrics tap, a logger). The ACK\n // decision belongs to the commit contract below, `ServerOptions.onMessage` is the\n // durable-commit step, not this event. An observer that throws must therefore not be able\n // to suppress the ACK: before containment, one broken logger silently turned every message\n // into a no-ACK, so every sender resent forever with nothing to diagnose it by. The throw\n // is surfaced on `'error'` instead, and the commit contract proceeds untouched.\n try {\n this.emit(\"message\", Object.freeze({ payload, meta }));\n } catch (err) {\n this._emitErrorIfListened(\n Object.freeze({\n connectionId,\n error: err instanceof Error ? err : new Error(String(err)),\n }),\n );\n }\n\n const autoAck = this._opts.autoAck;\n if (autoAck === \"AA\") {\n // Commit-gated: onMessage is the durable-commit step; AA only on success,\n // AE/AR on failure, a positive ACK can never precede commit.\n void this._sendCommitAck(payload, meta, conn);\n } else if (autoAck !== undefined) {\n // Custom-ACK mode: onMessage is observation only; the fn owns the ACK bytes.\n this._observeMessage(payload, meta, conn);\n void this._sendCustomAck(autoAck, payload, meta, conn);\n } else {\n // Manual mode: onMessage owns the response via conn.send().\n this._observeMessage(payload, meta, conn);\n }\n },\n );\n\n // Phase 8, surface a minimal, content-free peer-certificate summary\n // (WANT/MUST only) on the 'connection' event. Never the full cert object.\n const peerCertificate: PeerCertificateSummary | null =\n this._isTls && (this._opts.tls?.clientAuth ?? \"NONE\") !== \"NONE\"\n ? this._extractPeerCertificateSummary(socket as TLSSocket)\n : null;\n\n // Emit server-level 'connection' event with frozen payload.\n //\n // Contained: `_onSocketAccepted` runs inside the net.Server/tls.Server 'connection' listener,\n // so a throwing subscriber (an audit logger, an allow-list check) would kill the whole server\n // on the next accept.\n this._emitContained(\n \"connection\",\n Object.freeze({\n connectionId: conn.connectionId,\n remoteAddress: socket.remoteAddress ?? null,\n remotePort: socket.remotePort ?? null,\n peerCertificate,\n }),\n );\n\n // The negotiated-parameters record for this accepted link. Reads `null` for\n // a plaintext listener, so no event is emitted there at all. Host and port\n // are this server's own bound address, the same routing metadata its\n // security warnings carry.\n const negotiated = readNegotiatedTlsParameters(socket, this._host ?? \"\", this._port ?? 0);\n if (negotiated !== null) {\n // Contained for the same reason as the 'connection' emit above: a conformance auditor is\n // exactly the kind of subscriber this event attracts, and one that throws must not take the\n // accept loop down with it.\n this._emitContained(\"tlsNegotiated\", negotiated);\n }\n }\n\n /**\n * Extract a minimal, content-free peer-certificate summary (ATNA ITI-19\n * mutual authentication), CN strings, expiry, and the verification\n * outcome (`authorized`) only; never the full certificate object. Returns\n * `null` when no peer certificate was presented (e.g. `clientAuth: 'WANT'`\n * with no client cert).\n */\n private _extractPeerCertificateSummary(socket: TLSSocket): PeerCertificateSummary | null {\n const cert = socket.getPeerCertificate();\n if (cert === undefined || cert === null || Object.keys(cert).length === 0) {\n return null;\n }\n const asCn = (value: string | string[] | undefined): string | null => {\n if (value === undefined) return null;\n return Array.isArray(value) ? (value[0] ?? null) : value;\n };\n return Object.freeze({\n subjectCN: asCn(cert.subject?.CN),\n issuerCN: asCn(cert.issuer?.CN),\n validTo: cert.valid_to ?? null,\n // Verification outcome, under 'WANT' a certificate can be present yet\n // UNVERIFIED (see PeerCertificateSummary.authorized JSDoc).\n authorized: socket.authorized === true,\n });\n }\n\n /**\n * The fail-safe commit path for `autoAck: 'AA'` (HL7 v2.5.1 §2.9.2).\n *\n * With an `onMessage` handler this **awaits** it (the durable-commit step) and only\n * then dispatches: resolve ⇒ `AA`; throw/reject ⇒ a negative ACK (`AE` by default,\n * `AR` via {@link MllpAckError}). A positive ACK can never precede a successful commit.\n *\n * Without an `onMessage` handler, `'AA'` degrades to a **transport-accept**, `AA`\n * meaning \"bytes received and framed\", not \"application-processed\".\n *\n * A handler failure is **expected flow**, not a server error: it produces a negative\n * ACK and a `'nack'` event. The thrown error's message is never placed on the wire or\n * in the event (it may carry PHI), only the static `ackCode` and the inbound control\n * metadata reach the peer.\n */\n private async _sendCommitAck(\n payload: Buffer,\n meta: MessageMeta,\n conn: Connection,\n ): Promise<void> {\n const handler = this._opts.onMessage;\n // Read MSH-15 ONCE per message, before any disposition is reached, so the same reading\n // decides every branch below and any report about it is made exactly once.\n const selectCode = this._ackModeSelector(payload, conn);\n\n // No commit handler: 'AA' is a transport-accept (received+framed only), but still\n // fail-safe-downgraded if the message cannot carry a correlatable positive ACK.\n if (handler === undefined) {\n this._dispatchAck(\n conn,\n buildRawAck(payload, this._resolveAutoAckPositive(payload, meta, conn, selectCode)),\n );\n return;\n }\n\n let code: AckCode;\n try {\n await handler(payload, meta, conn); // durable-commit step\n // Commit succeeded, but only answer `AA` if the message is one we could correlate.\n code = this._resolveAutoAckPositive(payload, meta, conn, selectCode);\n } catch (err: unknown) {\n const nack: NegativeAckCode = resolveNackCode(err);\n code = selectCode(nack);\n // PHI-safe observability: control ID + outcome only, never the payload or error text.\n //\n // CONTAINED, and this one is load-bearing. This emit sits inside a `catch` block of an\n // `async` method that the caller `void`s, so a throwing `'nack'` subscriber (a metrics tap,\n // an alerting hook) would (a) reject the void-ed promise → unhandled rejection → process\n // death, and (b) skip the `_dispatchAck` below, so **the negative ACK would never be sent**.\n // The sender would be left waiting on an acknowledgement for a message the server had already\n // failed to commit. A broken metrics tap must never be able to suppress the fail-safe ACK.\n this._emitContained(\n \"nack\",\n Object.freeze({\n connectionId: conn.connectionId,\n ackCode: code,\n reason: \"handler-rejected\",\n }),\n );\n }\n\n // Unconditional: reached whether the handler committed or threw, and no subscriber can skip it.\n this._dispatchAck(conn, buildRawAck(payload, code));\n }\n\n /**\n * Resolve the auto-ACK code for a message the server would otherwise acknowledge\n * **positively** (`AA`), the no-handler transport-accept and the post-commit success\n * paths. Fail-safe: downgrade to `AE` and emit a PHI-safe `'nack'` whenever the message\n * cannot carry a correlatable positive ACK, so `AA` never names a control ID the sender\n * cannot match (→ timeout → resend → **duplicate clinical message**).\n *\n * Two independent disqualifiers, one from the bytes and one from the frame:\n *\n * - **`rawAckUncorrelatable(payload)`**, no readable `MSH`, an empty MSH-10, or a\n * batch/concatenated-message shape. This is the SAME predicate `buildRawAck` enforces\n * on the wire; it is re-checked here so the downgrade is **observable** (a `'nack'`\n * event) rather than a silent code change inside the builder.\n * - **`MLLP_TRAILING_BYTES`** in the frame's warnings, a mid-payload `VT` made the\n * decoder discard accumulated bytes and deliver only the fragment after it. The\n * clinical message was destroyed in transit, so even a fragment that happens to carry\n * a readable MSH must not be positively acknowledged. This condition is invisible to\n * `buildRawAck` (it sees only the fragment), which is why it lives here. The code is\n * **reserved** for that discard (see the decoder's `_readPayload`) and is emitted while\n * accumulating the delivered frame, so it is frame-scoped: it names *this* payload's own\n * discard, never a neighbouring frame's trailing junk.\n *\n * `'discarded-bytes'` takes precedence in the reason when both hold, it is the root cause.\n *\n * `selectCode` puts the resolved disposition in the half of HL7 Table 0008 the inbound\n * asked for; it is the identity for a message that asked for nothing.\n */\n private _resolveAutoAckPositive(\n payload: Buffer,\n meta: MessageMeta,\n conn: Connection,\n selectCode: (disposition: AckCode) => AckCode,\n ): AckCode {\n const discarded = meta.warnings.some((w) => w.code === \"MLLP_TRAILING_BYTES\");\n const uncorrelatable = rawAckUncorrelatable(payload);\n if (!discarded && !uncorrelatable) return selectCode(\"AA\");\n\n const reason: NackReason = discarded ? \"discarded-bytes\" : \"uncorrelatable-inbound\";\n const code = selectCode(\"AE\");\n // CONTAINED, this runs inside `_sendCommitAck`, a `void`-ed async task. A throwing `'nack'`\n // subscriber must not unwind into it (unhandled rejection → process death) nor skip the `AE`\n // that the caller dispatches next. PHI-safe: `reason` and `ackCode` are static, no payload.\n this._emitContained(\n \"nack\",\n Object.freeze({\n connectionId: conn.connectionId,\n ackCode: code,\n reason,\n }),\n );\n return code;\n }\n\n /**\n * Build this message's acknowledgement-code selector: the function that puts a resolved\n * disposition in the half of HL7 Table 0008 the inbound asked for.\n *\n * MSH-15 (accept acknowledgment type, HL7 Table 0155) says under which conditions the\n * sender wants an **accept** acknowledgement, and it is a condition rather than a switch:\n * `AL` always, `NE` never, `ER` on an error or reject only, `SU` on success only. So the\n * request cannot be evaluated until the disposition is known, which is why this returns a\n * function rather than a code. Where the answer is yes, the disposition is emitted as its\n * accept-mode counterpart: `CA` where this server would answer `AA`, `CE` where it would\n * answer `AE`, `CR` where it would answer `AR`.\n *\n * Exactly one acknowledgement still goes out per inbound message; nothing here sends a\n * second one, and the later application acknowledgement of the two-part protocol remains\n * the consumer's to send.\n *\n * Three inbound shapes leave the table entirely and keep this server's own answer:\n *\n * * a header this server cannot scan for MSH-15: it behaves exactly as it would have,\n * adding no new failure and no new report;\n * * a **batch or concatenated** frame: it keeps its warned non-positive answer and\n * never selects an accept-mode code, whatever MSH-15 says. A single MSA-2 can echo one\n * control ID, and commit-accepting a batch nobody read is the same lie in the other\n * half of the table;\n * * an MSH-15 outside Table 0155: reported, and answered exactly as it would have been.\n * Downgrading a message the handler committed would make a discharged sender resend a\n * message already in storage.\n */\n private _ackModeSelector(payload: Buffer, conn: Connection): (disposition: AckCode) => AckCode {\n const identity = (disposition: AckCode): AckCode => disposition;\n const msh = readMshSegment(payload);\n if (msh === null) return identity;\n const accept = readAcceptCondition(msh);\n if (accept.kind === \"unrecognised\") {\n this._emitAckModeWarning(\"MLLP_ACK_ACCEPT_TYPE_UNRECOGNISED\", conn);\n return identity;\n }\n if (accept.kind === \"null\") return identity;\n if (containsBatchOrExtraMessage(payload)) return identity;\n return (disposition) =>\n acceptAckRequested(accept, disposition) ? acceptModeCounterpart(disposition) : disposition;\n }\n\n /**\n * Emit the frozen `'ackModeWarning'` event.\n *\n * CONTAINED for the same reason every other emit on this path is: it is reached from a\n * `void`-ed async acknowledgement task, and a throwing metrics tap must never be able to\n * suppress the acknowledgement that follows it.\n */\n private _emitAckModeWarning(code: AckModeCode, conn: Connection): void {\n const event: AckModeWarningEvent = Object.freeze({\n connectionId: conn.connectionId,\n code,\n message: ackModeDiagnosticMessage(code),\n timestamp: new Date(),\n });\n this._emitContained(\"ackModeWarning\", event);\n }\n\n /**\n * Run `onMessage` for its side effects only (custom-ACK and manual modes), guarding\n * against a synchronous throw or a rejected promise. In these modes the handler does\n * **not** gate the ACK, so a failure is re-emitted as a PHI-safe `'error'` on the\n * connection (a handler error never crashes the server) rather than escaping as\n * an unhandled rejection. The error text is never placed on the wire.\n */\n private _observeMessage(payload: Buffer, meta: MessageMeta, conn: Connection): void {\n const handler = this._opts.onMessage;\n if (handler === undefined) return;\n void Promise.resolve()\n .then(() => handler(payload, meta, conn))\n .catch((err: unknown) => {\n const connErr = err instanceof Error ? err : new Error(String(err));\n // safeEmitError, not conn.emit: a raw emit bypasses Connection's containment, so an\n // unlistened or throwing 'error' subscriber would unwind into this void-ed async task\n // (unhandled rejection → process death).\n safeEmitError(conn, Object.freeze({ connectionId: conn.connectionId, error: connErr }));\n });\n }\n\n /**\n * The custom-builder path for `autoAck: fn`. Awaits the builder and dispatches its\n * Buffer as the ACK; the caller owns MSA-1. A builder error is re-emitted as `'error'`\n * on the connection, the server never crashes.\n */\n private async _sendCustomAck(\n fn: (payload: Buffer, meta: MessageMeta, conn: Connection) => Buffer | Promise<Buffer>,\n payload: Buffer,\n meta: MessageMeta,\n conn: Connection,\n ): Promise<void> {\n try {\n const ackPayload = await Promise.resolve(fn(payload, meta, conn));\n this._dispatchAck(conn, ackPayload);\n } catch (err: unknown) {\n const connErr = err instanceof Error ? err : new Error(String(err));\n // See above, never a raw conn.emit from inside a callback/async catch.\n safeEmitError(conn, Object.freeze({ connectionId: conn.connectionId, error: connErr }));\n }\n }\n\n /**\n * Frame an ACK payload and write it to the connection. `Connection.send()` returns\n * `false` under backpressure (socket write buffer full); that drops the ACK and emits\n * a `MllpConnectionError({ phase: 'send' })` on the connection, the peer will\n * time out waiting and may retry. The server never crashes.\n */\n private _dispatchAck(conn: Connection, ackPayload: Buffer): void {\n // TOTAL by contract: this is reached from `void`-ed async tasks (`_sendCommitAck`, the\n // transport-accept branch), so a throw here becomes an **unhandled rejection that kills the\n // process**, and it would do so on peer-controlled input. `encodeFrame` is strict and throws\n // `MLLP_PAYLOAD_CONTAINS_VT`/`_FS` if the ACK payload contains a framing byte.\n //\n // On the auto-ACK path this is **hard to provoke, but not impossible, and the containment is\n // what makes that safe.** `buildRawAck` decodes and encodes as `latin1`, a 1:1 byte↔code-unit\n // map, so it cannot *synthesize* a framing byte the way `ascii` masking once did\n // (`0x8B & 0x7F` = VT, the Phase 10 bug): it only ever echoes bytes that were already in the\n // inbound. A delivered payload never contains a VT (`FrameReader` discards its accumulator on a\n // mid-payload VT, `MLLP_TRAILING_BYTES`), so an echoed VT is not a concern here. It CAN,\n // however, contain an FS: under the `allowMissingLeadingVt` tolerance a non-VT, non-whitespace\n // first byte becomes payload byte 0, and FS (0x1C) qualifies, so an FS echoed out of MSH-10\n // into MSA-2 reaches this `encodeFrame`, which throws `MLLP_PAYLOAD_CONTAINS_FS`. (Those codes\n // are thrown by `encodeFrame`, on the way out; the decoder never emits them.) And a caller's\n // `autoAck: fn` can of course return arbitrary bytes.\n //\n // Whichever route, defending the process must not depend on the caller's, or the peer's,\n // discipline. A build/frame failure is surfaced as a connection `'error'` and the message goes\n // un-ACKed (fail-safe: better an un-ACKed message the sender will resend than a dead server),\n // never as a process kill.\n let framed: Buffer;\n try {\n framed = encodeFrame(ackPayload);\n } catch (err: unknown) {\n const cause = err instanceof Error ? err : new Error(String(err));\n safeEmitError(\n conn,\n Object.freeze({\n connectionId: conn.connectionId,\n error: new MllpConnectionError(\"ACK could not be framed\", { cause, phase: \"send\" }),\n }),\n );\n return;\n }\n\n const sent = conn.send(framed);\n if (!sent) {\n safeEmitError(\n conn,\n Object.freeze({\n connectionId: conn.connectionId,\n error: new MllpConnectionError(\"ACK dropped: socket backpressure\", {\n cause: new Error(\"backpressure\"),\n phase: \"send\",\n }),\n }),\n );\n }\n }\n}\n\n/**\n * Factory function, creates a new `MllpServer` with the supplied options.\n *\n * Prefer `createServer()` over `new MllpServer()` for forward-compatible construction.\n *\n * @example\n * ```typescript\n * import { createServer } from '@cosyte/mllp';\n *\n * const server = createServer({\n * onMessage: (payload, meta, conn) => {\n * console.log('received', payload.length, 'bytes');\n * },\n * });\n * await server.listen(2575);\n * ```\n */\nexport function createServer(opts: ServerOptions = {}): MllpServer {\n return new MllpServer(opts);\n}\n\n/**\n * Starter factory, creates, configures, and starts an `MllpServer` in one call.\n *\n * Provides the \"three lines of code\" north-star experience with sensible defaults:\n * `autoAck: 'AA'`, `drainTimeoutMs: 30_000`, `Symbol.asyncDispose` wired.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n *\n * const server = await createStarterServer({\n * port: 2575,\n * onMessage: async (payload) => {\n * await db.commit(payload); // the durable-commit step; a throw answers a negative ACK\n * },\n * });\n * ```\n */\nexport async function createStarterServer(opts: StarterServerOptions): Promise<MllpServer> {\n const server = createServer({\n ...opts,\n autoAck: opts.autoAck ?? \"AA\",\n drainTimeoutMs: opts.drainTimeoutMs ?? 30_000,\n });\n\n if (opts.handleSignals === true) {\n // D-09: register process.once('SIGTERM'/'SIGINT') so signal fires close() + exit(0).\n // Use once() (not on()) so handlers self-remove after first fire, preventing accumulation\n // across multiple server instances.\n const sigHandler = (): void => {\n void server\n .close()\n .then(() => process.exit(0))\n .catch(() => process.exit(1));\n };\n process.once(\"SIGTERM\", sigHandler);\n process.once(\"SIGINT\", sigHandler);\n\n // Clean up handlers on server close so tests do not accumulate listeners.\n // process.once handlers self-remove on first fire; this removes them early when\n // close() is called before any signal fires (the common test path).\n server.once(\"close\", () => {\n process.removeListener(\"SIGTERM\", sigHandler);\n process.removeListener(\"SIGINT\", sigHandler);\n });\n }\n\n await server.listen(opts.port, opts.host ?? \"127.0.0.1\");\n return server;\n}\n","/**\n * MLLP Client ACK correlator, pure data structure.\n *\n * Unified `Map<correlationKey, PendingAck>` with ES2015 insertion-ordered\n * iteration is the single source of truth for in-flight + queued sends.\n *\n * - FIFO mode keys by synthetic monotonic `sendSeq: number`.\n * - controlId mode keys by MSH-10 `string`.\n * - `maxInFlight=1` (`pipeline:false`) is enforced as a guard\n * on the same store; not a separate class.\n *\n * **INTERNAL**, not re-exported from the package barrel. The class knows\n * nothing about `Connection`, the event emitter, sockets, or timers. Sweep\n * cadence is driven externally by the periodic sweep tick on `MllpClient`\n * so the Correlator itself stays timer-free.\n *\n * @packageDocumentation\n */\n\nimport type { AckCorrelationCode } from \"./ack-diagnostics.js\";\nimport { classifyMsa1, type CommitAckReport, type Table0008Code } from \"../internal/ack-mode.js\";\nimport type { AckModeCode } from \"../internal/ack-mode-diagnostics.js\";\n\n/** Correlation key, number for FIFO (`sendSeq`), string for controlId (MSH-10). */\nexport type CorrelationKey = number | string;\n\n/**\n * The MSH-10 / MSA-2 byte-level scanners the correlator keys on.\n *\n * They live in `src/internal/control-id.ts`, not here: the client is one of\n * **three** call sites that must agree byte-for-byte on what a control ID is\n * (the others are `buildRawAck` in `src/server/ack.ts` and the verbatim-echo\n * check in `src/ack-from-hl7/build.ts`), and any disagreement between two of\n * them is an ACK the sender cannot match, a timeout, a resend, and a duplicate\n * clinical message. One implementation, one `latin1` decode, one dynamic read\n * of MSH-1. Re-exported here because they were part of this module's `@internal`\n * surface before the move.\n */\nexport {\n CONTROL_ID_ENCODING,\n extractMshControlId,\n extractMsaControlId,\n} from \"../internal/control-id.js\";\n\n/**\n * Two-phase state carried by an **enhanced-mode** send that is being correlated by control\n * ID. `null` on every other send, and a `null` here is exactly what makes a send behave the\n * way this package has always behaved: the first acknowledgement matched to it settles it,\n * whatever its MSA-1 carries.\n */\nexport interface TwoPhaseState {\n /**\n * Whether an application acknowledgement may follow the accept acknowledgement (MSH-16\n * is `AL`, `ER` or `SU`). When it is `false` the accept acknowledgement is the last word\n * and a `CA` settles the send by itself, which is what a peer answering\n * MSH-15 `AL` with MSH-16 `NE` has correctly done.\n */\n readonly awaitsApplicationAck: boolean;\n /**\n * Bound on the second wait, measured from the moment the accept acknowledgement was\n * received. Finite by construction: an unbounded wait is a defect.\n */\n readonly applicationAckTimeoutMs: number;\n /**\n * The caller's own per-send hook for the commit report, or `null` when that send asked\n * for none. Scoped to one send by construction: it is the callback the caller passed to\n * the `send()` this entry belongs to.\n */\n readonly onCommitReport: ((report: CommitAckReport) => void) | null;\n /** The accept-mode code already reported for this send, `null` until one arrives. */\n commitCode: \"CA\" | null;\n /** Epoch ms at which the accept acknowledgement was received, `null` until then. */\n commitReceivedAt: number | null;\n}\n\n/** A single pending send awaiting its ACK. */\nexport interface PendingAck {\n readonly key: CorrelationKey;\n readonly frame: Buffer;\n readonly controlId: string | null;\n readonly byteCount: number;\n /** Epoch ms recorded by `markFlushed()`; `null` until transport flush. */\n sentAt: number | null;\n readonly resolve: (ack: Buffer) => void;\n readonly reject: (err: Error) => void;\n /**\n * Two-phase acknowledgement state, or `null` for a send correlated the way every send\n * was before enhanced mode existed. See {@link TwoPhaseState}.\n */\n readonly twoPhase: TwoPhaseState | null;\n}\n\n/** Graveyard bookkeeping for late-ACK detection. */\nexport interface GraveyardEntry {\n readonly timedOutAt: number;\n readonly controlId: string | null;\n}\n\n/** JSON-serializable snapshot used by `client.getStats()`. */\nexport interface CorrelatorStats {\n readonly size: number;\n readonly queueBytes: number;\n readonly graveyardSize: number;\n readonly sendSeq: number;\n /**\n * Count of live entries with `sentAt !== null`.\n *\n * Distinct from `size`. `size` includes pre-flush entries (an entry was\n * `enqueue()`'d but `markFlushed()` has not been called yet) AND\n * serialization-queued entries (`pipeline:false`'s deferred sends). A\n * pre-flush entry contributes to `size` but NOT to `inFlight`.\n */\n readonly inFlight: number;\n}\n\n/** Constructor options. INTERNAL callback-bag pattern (mirrors `FrameReaderOptions`). */\nexport interface CorrelatorOptions {\n /** `'fifo'` (default) or `'controlId'`. */\n readonly mode?: \"fifo\" | \"controlId\";\n /** Default `30_000`. */\n readonly ackTimeoutMs?: number;\n /** Default `Infinity`; set to `1` for `pipeline:false`. */\n readonly maxInFlight?: number;\n /**\n * Emits `MLLP_ACK_AFTER_TIMEOUT` and `MLLP_ACK_UNMATCHED_CONTROL_ID`.\n * `byteOffset` is forwarded from the inbound ACK frame\n * for observability.\n *\n * **The context carries numbers, never the control ID itself.**\n * `controlIdBytes` is its byte length (`null` when there is no id), because\n * this context is handed to a warning that goes to a log, and a control ID is\n * payload content. The correlator withholds the string at the source so no\n * consumer downstream of it can interpolate one.\n */\n readonly onWarning: (\n code: AckCorrelationCode,\n ctx: {\n controlIdBytes: number | null;\n elapsedSinceSendMs: number;\n byteOffset: number;\n },\n ) => void;\n /**\n * Called by `matchAck()` in controlId mode on unmatched-ACK.\n *\n * Receives the **byte length** of the inbound MSA-2, or `null` when no MSA-2\n * could be extracted at all. The bytes themselves are deliberately not passed:\n * see {@link CorrelatorOptions.onWarning}.\n */\n readonly onUnmatchedAck?: (controlIdBytes: number | null) => void;\n /** Fired by `expireDue()`; `MllpClient` turns into `MllpTimeoutError`. */\n readonly onTimeout: (entry: PendingAck, elapsedMs: number) => void;\n /**\n * Emits the acknowledgement-mode codes: an acknowledgement that could not be classified\n * into a mode, a repeat commit accept, and one for a send already disposed of.\n *\n * **The context carries numbers and a closed-set code, never field bytes**, for the same\n * reason {@link CorrelatorOptions.onWarning} does.\n */\n readonly onAckModeWarning?: (code: AckModeCode, ctx: AckModeWarningContext) => void;\n /**\n * Fired when a commit accept (`CA`) is matched to a send that awaits an application\n * acknowledgement. The send stays pending; this is the report, not the settlement.\n */\n readonly onCommitReported?: (entry: PendingAck, ack: Buffer, latencyMs: number) => void;\n /**\n * Fired when a commit error or reject (`CE`/`CR`) is matched to a two-phase send. The\n * peer did not take custody of the bytes, so no application acknowledgement is coming and\n * the entry has already been removed: the handler fails the send.\n */\n readonly onCommitRejected?: (entry: PendingAck, code: \"CE\" | \"CR\") => void;\n /**\n * Fired by `expireDue()` when the wait that started at the accept acknowledgement\n * expires. Falls back to {@link CorrelatorOptions.onTimeout} when not supplied, so the\n * second wait is bounded whatever the caller wires.\n */\n readonly onApplicationAckTimeout?: (entry: PendingAck, elapsedSinceCommitMs: number) => void;\n /** Injected clock; default `Date.now`. Tests inject deterministic clock. */\n readonly now?: () => number;\n}\n\n/**\n * Context handed to {@link CorrelatorOptions.onAckModeWarning}.\n *\n * Everything here is a number or a member of the closed six-code Table 0008 set. An MSA-1\n * the reader could not classify is reported by its **byte length** alone.\n */\nexport interface AckModeWarningContext {\n /** Byte length of the MSA-1 field involved, or `null` when the code reports none. */\n readonly msa1Bytes: number | null;\n /** Table 0008 code involved, or `null` when the code reports none. */\n readonly ackCode: Table0008Code | null;\n /** Byte length of the control ID involved, or `null` when there was none to read. */\n readonly controlIdBytes: number | null;\n /** Inbound frame's stream byte offset. */\n readonly byteOffset: number;\n /** Milliseconds since the send's write-flush, or since its disposal for a late ACK. */\n readonly elapsedSinceSendMs: number;\n}\n\n/**\n * Pure data structure backing `MllpClient.send()` ACK correlation.\n *\n * @example\n * ```typescript\n * const correlator = new Correlator({\n * mode: 'fifo',\n * ackTimeoutMs: 30_000,\n * onTimeout: (entry, elapsed) => entry.reject(new MllpTimeoutError('timeout', { ... })),\n * onWarning: (code, ctx) => emitter.emit('warning', { code, ...ctx }),\n * });\n * const key = correlator.enqueue(frame, null, resolve, reject);\n * correlator.markFlushed(key);\n * const matched = correlator.matchAck(ackPayload);\n * if (matched !== null) matched.resolve(ackPayload);\n * ```\n */\nexport class Correlator {\n private readonly _opts: {\n readonly mode: \"fifo\" | \"controlId\";\n readonly ackTimeoutMs: number;\n readonly maxInFlight: number;\n readonly onWarning: CorrelatorOptions[\"onWarning\"];\n readonly onTimeout: CorrelatorOptions[\"onTimeout\"];\n readonly onUnmatchedAck: CorrelatorOptions[\"onUnmatchedAck\"];\n readonly onAckModeWarning: CorrelatorOptions[\"onAckModeWarning\"];\n readonly onCommitReported: CorrelatorOptions[\"onCommitReported\"];\n readonly onCommitRejected: CorrelatorOptions[\"onCommitRejected\"];\n readonly onApplicationAckTimeout: CorrelatorOptions[\"onApplicationAckTimeout\"];\n readonly now: () => number;\n };\n private readonly _pending: Map<CorrelationKey, PendingAck> = new Map();\n private readonly _graveyard: Map<CorrelationKey, GraveyardEntry> = new Map();\n /**\n * Control IDs of two-phase sends already disposed of (settled or failed), with the epoch\n * ms at which that happened.\n *\n * Separate from the graveyard, and separate on purpose. The graveyard is a memory of\n * **timed-out** sends and is evicted one-shot, on the first late acknowledgement that\n * hits it. This one is a memory of **every** disposal and is evicted by elapsed time\n * alone, so a peer that repeats an acknowledgement twice inside the window is told\n * \"already disposed\" both times rather than \"already disposed\" and then \"no such send\".\n * The two windows are the same length; only the eviction rules differ.\n */\n private readonly _disposed: Map<CorrelationKey, number> = new Map();\n private _sendSeq = 0;\n private _queueBytes = 0;\n /**\n * Count of pending entries with `sentAt !== null`.\n * Maintained at every site that mutates `entry.sentAt` or removes a\n * flushed entry: `markFlushed`, `remove`, `matchAck`, `expireDue`, `clear`.\n */\n private _inFlight = 0;\n\n /**\n * Construct a send/ACK correlator.\n *\n * @param opts - Correlation options (FIFO vs control-id mode, ACK timeout, max in-flight).\n */\n constructor(opts: CorrelatorOptions) {\n this._opts = {\n mode: opts.mode ?? \"fifo\",\n ackTimeoutMs: opts.ackTimeoutMs ?? 30_000,\n maxInFlight: opts.maxInFlight ?? Number.POSITIVE_INFINITY,\n onWarning: opts.onWarning,\n onTimeout: opts.onTimeout,\n onUnmatchedAck: opts.onUnmatchedAck,\n onAckModeWarning: opts.onAckModeWarning,\n onCommitReported: opts.onCommitReported,\n onCommitRejected: opts.onCommitRejected,\n onApplicationAckTimeout: opts.onApplicationAckTimeout,\n now: opts.now ?? Date.now,\n };\n }\n\n /** Number of live pending entries. */\n get size(): number {\n return this._pending.size;\n }\n /** Sum of `frame.length` across live entries. */\n get queueBytes(): number {\n return this._queueBytes;\n }\n /** Number of graveyard entries awaiting lazy eviction. */\n get graveyardSize(): number {\n return this._graveyard.size;\n }\n /** Number of disposed two-phase sends still inside their remembered window. */\n get disposedSize(): number {\n return this._disposed.size;\n }\n\n /**\n * Enqueue a new send awaiting its ACK. Returns the assigned\n * `correlationKey`, or `null` if `maxInFlight` is reached (caller awaits\n * drain, `pipeline:false`).\n *\n * `twoPhase` is the enhanced-mode state for a send being correlated by control ID.\n * Omitting it (or passing `null`) is what every other send does, and such an entry is\n * settled by the first acknowledgement matched to it, exactly as before.\n */\n enqueue(\n frame: Buffer,\n controlIdOrNull: string | null,\n resolve: (ack: Buffer) => void,\n reject: (err: Error) => void,\n twoPhase: TwoPhaseState | null = null,\n ): CorrelationKey | null {\n if (this._pending.size >= this._opts.maxInFlight) return null;\n // controlId mode keys by MSH-10 (string). When MSH-10 is absent, we fall\n // back to a synthetic `__seq-N` key, the send is best-effort matchable\n // by the FIFO live-store walk, but the peer realistically cannot ACK it\n // by control ID. Acceptable corner case (D-03/A1).\n const key: CorrelationKey =\n this._opts.mode === \"controlId\"\n ? (controlIdOrNull ?? `__seq-${++this._sendSeq}`)\n : ++this._sendSeq;\n const entry: PendingAck = {\n key,\n frame,\n controlId: controlIdOrNull,\n byteCount: frame.length,\n sentAt: null,\n resolve,\n reject,\n twoPhase,\n };\n this._pending.set(key, entry);\n this._queueBytes += frame.length;\n return key;\n }\n\n /**\n * Record write-flush timestamp (the clock starts at flush, NOT\n * at `send()` call). No-op if key is unknown (e.g. removed by abort).\n */\n markFlushed(key: CorrelationKey, now?: number): void {\n const entry = this._pending.get(key);\n if (entry === undefined) return;\n // PLAN-06: only the first flush bumps _inFlight. Subsequent re-flush\n // (e.g. PLAN-04's controlId reflushAll on reconnect) is idempotent.\n if (entry.sentAt === null) this._inFlight += 1;\n entry.sentAt = now ?? this._opts.now();\n }\n\n /**\n * Match an inbound ACK against the live store.\n *\n * - FIFO: returns first pending entry by insertion order; entry is\n * removed from live store. Caller calls `entry.resolve(ackPayload)`.\n * - controlId: keyed lookup by `controlIdFromAck`. Live-store hit returns\n * the entry; graveyard hit fires `MLLP_ACK_AFTER_TIMEOUT` warning\n * and returns `null`; otherwise fires `onUnmatchedAck` with the id's\n * **byte length** and returns `null`.\n *\n * A live-store hit on a **two-phase** entry is decided by the acknowledgement's MSA-1\n * instead, see {@link Correlator.matchTwoPhase}.\n *\n * Triggers lazy graveyard eviction.\n *\n * @param payload Inbound ACK bytes (framing stripped). MSA-2 extraction\n * happens at MllpClient's `_onAckPayload` hook; this method takes the\n * already-extracted control ID as a parameter. The bytes themselves are read\n * only for a two-phase entry's MSA-1.\n * @param controlIdFromAck MSA-2 extracted from ACK (controlId mode only).\n * @param byteOffsetFromAck Stream offset; forwarded to `onWarning`.\n */\n matchAck(\n payload: Buffer,\n controlIdFromAck: string | null = null,\n byteOffsetFromAck = 0,\n ): PendingAck | null {\n this._evictGraveyardDue(this._opts.now());\n if (this._opts.mode === \"fifo\") {\n const iter = this._pending.values().next();\n if (iter.done === true) return null;\n const entry = iter.value;\n this._pending.delete(entry.key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06 (D-26): only flushed entries count toward _inFlight.\n if (entry.sentAt !== null) this._inFlight -= 1;\n return entry;\n }\n // controlId mode (PLAN-03)\n if (controlIdFromAck === null) {\n // Caller failed to extract MSA-2; treat as unmatched. (MllpClient is\n // responsible for extraction; this is a defensive fallback.)\n if (this._opts.onUnmatchedAck !== undefined) {\n this._opts.onUnmatchedAck(null);\n }\n return null;\n }\n const live = this._pending.get(controlIdFromAck);\n if (live !== undefined) {\n if (live.twoPhase !== null) {\n return this.matchTwoPhase(live, payload, byteOffsetFromAck);\n }\n this._pending.delete(controlIdFromAck);\n this._queueBytes -= live.byteCount;\n // PLAN-06 (D-26): only flushed entries count toward _inFlight.\n if (live.sentAt !== null) this._inFlight -= 1;\n return live;\n }\n const disposedAt = this._disposed.get(controlIdFromAck);\n if (disposedAt !== undefined) {\n // A further ACK for a send already settled or failed. It changes nothing, and it is\n // NOT evicted here: every ACK inside the window draws this same answer, however many\n // arrive, so a peer repeating itself is never told \"no such send\" for a send that is\n // still remembered.\n this._ackModeWarn(\"MLLP_ACK_SEND_ALREADY_DISPOSED\", {\n msa1Bytes: null,\n ackCode: null,\n controlIdBytes: controlIdFromAck.length,\n byteOffset: byteOffsetFromAck,\n elapsedSinceSendMs: this._opts.now() - disposedAt,\n });\n return null;\n }\n const grave = this._graveyard.get(controlIdFromAck);\n if (grave !== undefined) {\n // CLIENT-16: late ACK after timeout. Forward the inbound ACK frame's\n // byte offset (W-05) so observers see where in the stream it landed.\n const elapsedSinceSendMs = this._opts.now() - grave.timedOutAt;\n this._opts.onWarning(\"MLLP_ACK_AFTER_TIMEOUT\", {\n // The LENGTH, never the id. Control IDs are decoded `latin1`, a 1:1\n // byte to code-unit map, so `.length` is a byte count.\n controlIdBytes: grave.controlId === null ? null : grave.controlId.length,\n elapsedSinceSendMs,\n byteOffset: byteOffsetFromAck,\n });\n // One-shot: drop the graveyard entry now that we've seen the late ACK.\n this._graveyard.delete(controlIdFromAck);\n return null;\n }\n // CLIENT-15: unmatched controlId (live store empty + not in graveyard).\n // The LENGTH crosses this boundary, never the peer's bytes.\n if (this._opts.onUnmatchedAck !== undefined) {\n this._opts.onUnmatchedAck(controlIdFromAck.length);\n }\n return null;\n }\n\n /**\n * Decide what an acknowledgement matched to a **two-phase** send does to it, from its\n * MSA-1 and from nothing else. No other property of the acknowledgement, its length, its\n * MSH-9 or its other segments, takes part.\n *\n * Six outcomes, one per classification:\n *\n * * `AA`, `AE` or `AR`: the application acknowledgement. It **settles** the send,\n * whether or not an accept-mode acknowledgement was seen first, and all three settle\n * it the same way, successfully, with the acknowledgement handed to the caller. `AE`\n * and `AR` are the receiving application's clinical verdict on a message it did take\n * custody of, and judging that verdict is not this package's job. The caller reads\n * MSA-1 off the acknowledgement it is given.\n * * `CA` on a send awaiting no application acknowledgement: it **settles** the send.\n * MSH-15 `AL` with MSH-16 `NE` is a legal pair and a peer answering it with one `CA`\n * has done exactly what was asked.\n * * `CA` on a send awaiting one: **reported, not settled.** The send stays pending and\n * its second wait starts here.\n * * a second `CA` on a send already reported: surfaced, and neither re-reported nor\n * allowed to restart the second wait.\n * * `CE` or `CR`: the peer refused custody of the bytes, so no application\n * acknowledgement is coming. The send **fails now**, whatever its application\n * condition and whether or not a `CA` was already reported. Waiting out the second\n * window would report the same failure later and less precisely.\n * * NULL or unclassifiable: surfaced with a code that distinguishes the two, and the\n * send is left pending until its outstanding wait expires. An acknowledgement that\n * cannot be classified into a mode is not guessed.\n *\n * Returns the entry when it is to be settled (the caller resolves it), `null` otherwise.\n */\n private matchTwoPhase(\n entry: PendingAck,\n payload: Buffer,\n byteOffsetFromAck: number,\n ): PendingAck | null {\n const twoPhase = entry.twoPhase;\n /* c8 ignore next */\n if (twoPhase === null) return entry;\n const now = this._opts.now();\n const classification = classifyMsa1(payload);\n const elapsedSinceSendMs = entry.sentAt === null ? 0 : now - entry.sentAt;\n const controlIdBytes = entry.controlId === null ? null : entry.controlId.length;\n\n if (classification.kind === \"code\") {\n const code = classification.code;\n if (code === \"AA\" || code === \"AE\" || code === \"AR\") {\n this._disposeTwoPhase(entry, now);\n return entry;\n }\n if (code === \"CE\" || code === \"CR\") {\n this._disposeTwoPhase(entry, now);\n this._opts.onCommitRejected?.(entry, code);\n return null;\n }\n // `CA`.\n if (!twoPhase.awaitsApplicationAck) {\n this._disposeTwoPhase(entry, now);\n return entry;\n }\n if (twoPhase.commitCode !== null) {\n this._ackModeWarn(\"MLLP_ACK_COMMIT_ALREADY_REPORTED\", {\n msa1Bytes: classification.byteLength,\n ackCode: code,\n controlIdBytes,\n byteOffset: byteOffsetFromAck,\n elapsedSinceSendMs,\n });\n return null;\n }\n twoPhase.commitCode = code;\n twoPhase.commitReceivedAt = now;\n this._opts.onCommitReported?.(entry, payload, elapsedSinceSendMs);\n return null;\n }\n\n this._ackModeWarn(\n classification.kind === \"null\" ? \"MLLP_ACK_MSA1_ABSENT\" : \"MLLP_ACK_MSA1_UNCLASSIFIABLE\",\n {\n msa1Bytes: classification.byteLength,\n ackCode: null,\n controlIdBytes,\n byteOffset: byteOffsetFromAck,\n elapsedSinceSendMs,\n },\n );\n return null;\n }\n\n /**\n * Sweep live entries; expire those past `sentAt + ackTimeoutMs`.\n * Fires `onTimeout(entry, elapsedMs)`; entries move to graveyard.\n * Driven externally by `MllpClient`'s periodic sweep tick, Correlator\n * itself owns no timers.\n *\n * A two-phase entry whose accept acknowledgement has arrived is bounded by the **second**\n * window instead, measured from that acknowledgement: the first-acknowledgement timeout\n * is stopped, so it can no longer expire against a send the peer has already answered.\n * Both windows are finite, so no send waits forever either way.\n */\n expireDue(now?: number): void {\n const t = now ?? this._opts.now();\n const ackTimeoutMs = this._opts.ackTimeoutMs;\n for (const [key, entry] of this._pending) {\n if (entry.sentAt === null) continue;\n const twoPhase = entry.twoPhase;\n const commitReceivedAt = twoPhase?.commitReceivedAt ?? null;\n if (twoPhase !== null && commitReceivedAt !== null) {\n if (commitReceivedAt + twoPhase.applicationAckTimeoutMs <= t) {\n this._removeExpired(key, entry);\n this._disposed.set(key, t);\n const elapsed = t - commitReceivedAt;\n if (this._opts.onApplicationAckTimeout !== undefined) {\n this._opts.onApplicationAckTimeout(entry, elapsed);\n } else {\n this._opts.onTimeout(entry, elapsed);\n }\n }\n continue;\n }\n if (entry.sentAt + ackTimeoutMs <= t) {\n const elapsed = t - entry.sentAt;\n this._removeExpired(key, entry);\n if (twoPhase !== null) {\n // A two-phase send is remembered by elapsed time alone, so a peer that repeats an\n // acknowledgement is answered the same way every time inside the window.\n this._disposed.set(key, t);\n } else {\n this._graveyard.set(key, {\n timedOutAt: t,\n controlId: entry.controlId,\n });\n }\n this._opts.onTimeout(entry, elapsed);\n }\n }\n }\n\n /**\n * Reject every live entry with `reason` (insertion order) and clear\n * the live store. Graveyard is left intact (ages out via lazy eviction).\n * Used by `MllpClient`'s correlator teardown to cancel pending sends.\n *\n * `reasonFor` lets the caller substitute a different error per entry, which is how a send\n * whose commit disposition has already been reported is failed with an error that carries\n * that disposition rather than with the generic one.\n */\n clear(reason: Error, reasonFor?: (entry: PendingAck, reason: Error) => Error): void {\n const now = this._opts.now();\n for (const entry of this._pending.values()) {\n if (entry.twoPhase !== null) this._disposed.set(entry.key, now);\n entry.reject(reasonFor === undefined ? reason : reasonFor(entry, reason));\n }\n this._pending.clear();\n this._queueBytes = 0;\n // PLAN-06: reset _inFlight unconditionally, clear() drops every entry,\n // flushed or not. Avoids any over-decrement edge case.\n this._inFlight = 0;\n }\n\n /** Iterate live entries in insertion order (reconnect-resend). */\n *liveEntries(): IterableIterator<PendingAck> {\n for (const entry of this._pending.values()) yield entry;\n }\n\n /**\n * Remove a live entry by key WITHOUT resolving/rejecting. Returns the\n * removed entry, or `null` if no entry with that key exists.\n * Used by `MllpClient.send()` for AbortSignal cleanup and by the\n * reconnect-reject FSM walk.\n */\n remove(key: CorrelationKey): PendingAck | null {\n const entry = this._pending.get(key);\n if (entry === undefined) return null;\n this._pending.delete(key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06: only flushed entries count toward _inFlight.\n if (entry.sentAt !== null) this._inFlight -= 1;\n return entry;\n }\n\n /**\n * Remove a live entry by key WITHOUT resolving/rejecting it, and remember that it was\n * disposed of, so a later acknowledgement for it is answered \"already disposed\" rather\n * than \"no such send\" for as long as the window lasts.\n *\n * The caller settles or fails the returned entry itself. Used where the client fails a\n * send that had already drawn a commit disposition.\n */\n dispose(key: CorrelationKey): PendingAck | null {\n const entry = this.remove(key);\n if (entry === null) return null;\n if (entry.twoPhase !== null) this._disposed.set(key, this._opts.now());\n return entry;\n }\n\n /** JSON-serializable stats snapshot (no Buffers, no class instances). */\n getStats(): CorrelatorStats {\n return {\n size: this._pending.size,\n queueBytes: this._queueBytes,\n graveyardSize: this._graveyard.size,\n sendSeq: this._sendSeq,\n inFlight: this._inFlight,\n };\n }\n\n /**\n * Lazy eviction of both bounded memories: graveyard entries past\n * `timedOutAt + 2 * ackTimeoutMs`, and disposed two-phase sends past\n * `disposedAt + 2 * ackTimeoutMs`. Same window, so \"already disposed\" and \"arrived after\n * its timeout\" are remembered for the same length of time, and past it both correctly\n * read as \"no such send\". The memory is bounded, not perpetual.\n */\n private _evictGraveyardDue(now: number): void {\n const threshold = 2 * this._opts.ackTimeoutMs;\n for (const [key, entry] of this._graveyard) {\n if (entry.timedOutAt + threshold <= now) this._graveyard.delete(key);\n }\n for (const [key, disposedAt] of this._disposed) {\n if (disposedAt + threshold <= now) this._disposed.delete(key);\n }\n }\n\n /** Remove a live entry that has just expired, keeping the counters straight. */\n private _removeExpired(key: CorrelationKey, entry: PendingAck): void {\n this._pending.delete(key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06: the caller has already established that this entry was flushed.\n this._inFlight -= 1;\n }\n\n /** Drop a two-phase entry from the live store and remember that it was disposed of. */\n private _disposeTwoPhase(entry: PendingAck, now: number): void {\n this._pending.delete(entry.key);\n this._queueBytes -= entry.byteCount;\n // PLAN-06 (D-26): only flushed entries count toward _inFlight.\n if (entry.sentAt !== null) this._inFlight -= 1;\n this._disposed.set(entry.key, now);\n }\n\n /** Emit an acknowledgement-mode diagnostic, when the caller wired a sink for them. */\n private _ackModeWarn(code: AckModeCode, ctx: AckModeWarningContext): void {\n this._opts.onAckModeWarning?.(code, ctx);\n }\n}\n","/**\n * The frozen diagnostic registry for ACK correlation.\n *\n * Every message an ACK-correlation diagnostic can carry lives here as a\n * literal, and the lookup takes **only a code**. There is no value parameter\n * anywhere in this module, which is the whole point: a factory that cannot be\n * handed a value cannot interpolate one, regardless of what arrives on the\n * wire.\n *\n * ## What this replaces, and why the shape matters more than the wording\n *\n * The `correlateByControlId` path used to build its diagnostics by\n * interpolation, and both ends of it were consumer-controlled and unbounded.\n * The unmatched-ACK error read the **peer's** MSA-2 straight out of an inbound\n * frame and put it on an `Error.message`; measured against a peer sending a\n * one-megabyte MSA-2, that produced a 1,000,026-byte `message` and an\n * equally large field on the frozen error payload. The late-ACK warning did\n * the same with the control id of the send being reported on. Both go to a\n * log, and in this domain a log is a place a patient identifier must not\n * reach. That is not a hypothetical about control ids either: this package has\n * already shipped one scanner that ran past a segment terminator and returned\n * PID-3, a patient's medical record number, as the \"control ID\" of a truncated\n * MSH. Withholding the bytes is the defence that survives the next such bug.\n *\n * ## What a correlation diagnostic says instead\n *\n * That a control id did not match, plus **numbers**: a byte length, a stream\n * offset, an elapsed time. A number is a shape, not content. A truncated id is\n * not an acceptable middle ground, and neither is a hex or base64 rendering:\n * both still grow the diagnostic with the input and both still disclose the\n * bytes. Nothing is lost by withholding them, because the caller already holds\n * them: the outbound payload is the caller's own argument to `send()`, and the\n * inbound frame is on the `'message'` event.\n *\n * Mirrors the same answer this package already reached for the ACK adapter's\n * verbatim-echo warning, which reports byte lengths and withholds MSH-10 and\n * MSA-2 themselves.\n *\n * @packageDocumentation\n */\n\n/**\n * The two warning codes emitted by ACK correlation rather than by framing.\n *\n * A subset of the framing `WarningCode` union; named separately so the\n * registry below is exhaustive over exactly the codes it owns and a new\n * correlation code cannot be added without a message for it.\n */\nexport type AckCorrelationCode = \"MLLP_ACK_UNMATCHED_CONTROL_ID\" | \"MLLP_ACK_AFTER_TIMEOUT\";\n\n/**\n * Frozen diagnostic text, one literal per code, no interpolation.\n *\n * Do **not** add a value parameter to anything that reads this table. The\n * distinguishing property of every diagnostic surface that has leaked across\n * this ecosystem is that its factory took a value at all.\n */\nconst ACK_DIAGNOSTIC_MESSAGES: Readonly<Record<AckCorrelationCode, string>> = Object.freeze({\n MLLP_ACK_UNMATCHED_CONTROL_ID:\n \"Inbound ACK control ID matched no in-flight send and no timed-out send; the ACK was dropped. \" +\n \"Field values are withheld: MSA-2 is inbound payload content and this diagnostic goes to a log. \" +\n \"The inbound bytes are on the 'message' event; controlIdBytes gives the length.\",\n MLLP_ACK_AFTER_TIMEOUT:\n \"ACK arrived after its send had already timed out; the ACK was dropped. \" +\n \"Field values are withheld: a control ID is payload content and this diagnostic goes to a log. \" +\n \"The outbound bytes are the payload you passed to send(); controlIdBytes gives the length.\",\n});\n\n/**\n * Look up the frozen diagnostic text for an ACK-correlation code.\n *\n * A pure table read. It takes no value, so no caller can widen it into an\n * interpolation site.\n *\n * @param code - The correlation code being reported.\n * @returns The registered message for that code, byte-for-byte.\n * @example\n * ```typescript\n * const message = ackDiagnosticMessage('MLLP_ACK_AFTER_TIMEOUT');\n * ```\n */\nexport function ackDiagnosticMessage(code: AckCorrelationCode): string {\n return ACK_DIAGNOSTIC_MESSAGES[code];\n}\n","/**\n * MLLP Client typed errors and classifiers.\n *\n * Exports:\n * - `MllpTimeoutError`, ACK timeout\n * - `MllpBackpressureError`, high-water mark exceeded\n * - `MllpNeverDeliveredError`, a send that never reached the transport\n * - `MllpUnknownFateError`, a send whose bytes went out and drew no answer\n * - `isTransientConnectionError`, transient/permanent classifier\n *\n * Re-exported from `src/client/index.ts` and `src/index.ts`.\n *\n * @packageDocumentation\n */\n\n/**\n * Thrown (or rejects the `send()` promise) when an ACK does not arrive within\n * the configured `ackTimeoutMs`.\n *\n * The timeout clock starts at the underlying `write()` flush callback, NOT\n * at the `send()` call, pre-flush queue time is not charged to the peer.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpTimeoutError) {\n * logger.warn({ elapsedMs: err.elapsedMs, idBytes: err.messageControlIdBytes });\n * }\n * }\n * ```\n */\nexport class MllpTimeoutError extends Error {\n override readonly name = \"MllpTimeoutError\" as const;\n\n /**\n * Byte length of the timed-out send's MSH-10 control ID, or `undefined` when\n * there was none to read (FIFO mode, or a payload with no MSH-10).\n *\n * **The control ID itself is deliberately not here.** An `Error` is a\n * diagnostic surface: it is logged, and its `stack` is what an error reporter\n * ships off the box. MSH-10 is payload content, and a scanner that returns\n * the wrong field returns payload content of some other kind, which is how a\n * patient identifier reaches a log line. Nothing is lost by withholding it,\n * because this error rejects the very `send()` whose payload the caller\n * passed in, so the caller already holds the bytes. Control IDs are decoded\n * `latin1`, a 1:1 byte to code-unit map, so this count is a byte count.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /** Milliseconds elapsed between write-flush and timeout fire. */\n readonly elapsedMs: number;\n\n /** Epoch ms timestamp recorded at write-flush callback. */\n readonly sentAt: number;\n\n /**\n * Construct an MLLP timeout error.\n *\n * @param message - Human-readable error message. Structural facts only, never field content.\n * @param opts - Timeout context (control-id byte length, elapsed time, flush timestamp).\n */\n constructor(\n message: string,\n opts: {\n messageControlIdBytes: number | undefined;\n elapsedMs: number;\n sentAt: number;\n },\n ) {\n super(message);\n this.messageControlIdBytes = opts.messageControlIdBytes;\n this.elapsedMs = opts.elapsedMs;\n this.sentAt = opts.sentAt;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpTimeoutError);\n }\n }\n}\n\n/**\n * Why a send waiting on its application acknowledgement was failed.\n *\n * - `'timeout'`, the wait that started at the accept acknowledgement expired.\n * - `'connection-lost'`, the link failed or was closed while the send was still pending on\n * it. The send is failed rather than left pending or reported as successful, because\n * nobody can say what the receiving application did with a message whose second\n * acknowledgement never arrived.\n *\n * @example\n * ```typescript\n * import { MllpApplicationAckError } from '@cosyte/mllp';\n * if (err instanceof MllpApplicationAckError && err.reason === 'timeout') {\n * // committed by the peer, application disposition unknown\n * }\n * ```\n */\nexport type ApplicationAckFailure = \"timeout\" | \"connection-lost\";\n\n/**\n * Rejects a `send()` whose peer **committed** the message and then never reported what its\n * application did with it.\n *\n * This is the enhanced-mode outcome that has no original-mode counterpart, and it is\n * deliberately a distinct type from {@link MllpTimeoutError}: that one means \"no\n * acknowledgement at all arrived\", and this one means \"the accept acknowledgement arrived,\n * the peer took custody, and the application acknowledgement did not follow\". Confusing the\n * two would tell an operator a message may never have been received when the peer has said\n * in writing that it was.\n *\n * `commitCode` is the Table 0008 code already received, so a caller can act on the custody\n * transfer even though the application disposition is unknown. It is a member of a closed\n * six-code set, never wire content.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpApplicationAckError) {\n * logger.warn({ commit: err.commitCode, reason: err.reason, elapsedMs: err.elapsedMs });\n * }\n * }\n * ```\n */\nexport class MllpApplicationAckError extends Error {\n override readonly name = \"MllpApplicationAckError\" as const;\n\n /** Why the wait ended. See {@link ApplicationAckFailure}. */\n readonly reason: ApplicationAckFailure;\n\n /** The commit disposition already received. Always the positive accept-mode code. */\n readonly commitCode: \"CA\";\n\n /**\n * Byte length of the send's MSH-10 control ID, or `undefined` when there was none to\n * read. The control ID itself is deliberately not here, for the reason given on\n * {@link MllpTimeoutError.messageControlIdBytes}.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /** Milliseconds between the accept acknowledgement and this failure. */\n readonly elapsedMs: number;\n\n /** Epoch ms at which the accept acknowledgement was received. */\n readonly commitReceivedAt: number;\n\n /**\n * Construct an application-acknowledgement failure.\n *\n * @param message - Human-readable error message. Structural facts only, never field content.\n * @param opts - Failure context (why, the commit code, control-id byte length, timings).\n */\n constructor(\n message: string,\n opts: {\n reason: ApplicationAckFailure;\n commitCode: \"CA\";\n messageControlIdBytes: number | undefined;\n elapsedMs: number;\n commitReceivedAt: number;\n },\n ) {\n super(message);\n this.reason = opts.reason;\n this.commitCode = opts.commitCode;\n this.messageControlIdBytes = opts.messageControlIdBytes;\n this.elapsedMs = opts.elapsedMs;\n this.commitReceivedAt = opts.commitReceivedAt;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpApplicationAckError);\n }\n }\n}\n\n/**\n * Rejects a `send()` whose peer answered with a **negative commit**: HL7 Table 0008 `CE`\n * (commit error) or `CR` (commit reject).\n *\n * Either says the peer did not take custody of the bytes, so no application acknowledgement\n * is coming and there is nothing to wait for. The send fails immediately rather than sitting\n * out a window that would report the same failure later and less precisely.\n *\n * `commitCode` is a member of a closed six-code set, never wire content.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpCommitRejectedError && err.commitCode === 'CR') {\n * // the peer will not take this message; do not resend it unchanged\n * }\n * }\n * ```\n */\nexport class MllpCommitRejectedError extends Error {\n override readonly name = \"MllpCommitRejectedError\" as const;\n\n /** The negative accept-mode code the peer sent. */\n readonly commitCode: \"CE\" | \"CR\";\n\n /**\n * Byte length of the send's MSH-10 control ID, or `undefined` when there was none to\n * read. The control ID itself is deliberately not here.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /** Milliseconds between the send's write-flush and this acknowledgement. */\n readonly elapsedMs: number;\n\n /**\n * Construct a negative-commit error.\n *\n * @param message - Human-readable error message. Structural facts only, never field content.\n * @param opts - Failure context (the commit code, control-id byte length, elapsed time).\n */\n constructor(\n message: string,\n opts: {\n commitCode: \"CE\" | \"CR\";\n messageControlIdBytes: number | undefined;\n elapsedMs: number;\n },\n ) {\n super(message);\n this.commitCode = opts.commitCode;\n this.messageControlIdBytes = opts.messageControlIdBytes;\n this.elapsedMs = opts.elapsedMs;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpCommitRejectedError);\n }\n }\n}\n\n/**\n * The whole text of a {@link MllpNeverDeliveredError}, held here rather than composed at the\n * throw site.\n *\n * Fixed, and fixed on purpose: nothing input-derived may reach an error message, so the only\n * safe builder is one that takes no value at all. Everything this report has to say beyond\n * this sentence is a count, and a count travels on its own field.\n */\nconst NEVER_DELIVERED_MESSAGE =\n \"delivery did not occur: the message was still held by the client and no bytes were written \" +\n \"to the transport, so the peer never saw it\";\n\n/**\n * The whole text of a {@link MllpUnknownFateError}. Fixed for the reason given on\n * {@link NEVER_DELIVERED_MESSAGE}, and here the reason is at its sharpest: this error reports\n * a message that WAS on the wire, so anything copied off it is payload content that has left\n * the building the moment an error reporter ships the box.\n */\nconst UNKNOWN_FATE_MESSAGE =\n \"fate unknown: the message was written to the transport and no acknowledgement arrived \" +\n \"before the client finished closing, so whether the peer received it cannot be determined \" +\n \"from here\";\n\n/**\n * Rejects a `send()` whose bytes **never reached the transport**: the message was still held\n * inside the client when the client shut down, waiting for room in the send queue or for the\n * single in-flight slot.\n *\n * This is the safe half of a shutdown report. Nothing was written, so the peer cannot have\n * seen this message, cannot have committed it, and cannot produce a duplicate if the\n * application sends it again. It is deliberately a distinct type from\n * {@link MllpUnknownFateError}, so a caller decides between resending and escalating on\n * `instanceof` rather than on the wording of a message.\n *\n * Every field is a count. There is nothing else to carry: the caller passed the payload to\n * `send()` and still holds it.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpNeverDeliveredError) {\n * // safe to resend: these bytes were never written\n * await backupClient.send(payload);\n * }\n * }\n * ```\n */\nexport class MllpNeverDeliveredError extends Error {\n override readonly name = \"MllpNeverDeliveredError\" as const;\n\n /** Byte count of the framed message this send would have written. */\n readonly byteCount: number;\n\n /**\n * Byte length of the send's MSH-10 control ID, or `undefined` when there was none to read.\n * The control ID itself is deliberately not here, for the reason given on\n * {@link MllpTimeoutError.messageControlIdBytes}.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /**\n * Construct a never-delivered report.\n *\n * There is no `message` parameter, deliberately: see {@link NEVER_DELIVERED_MESSAGE}.\n *\n * @param opts - Report context (framed byte count, control-id byte length).\n */\n constructor(opts: { byteCount: number; messageControlIdBytes: number | undefined }) {\n super(NEVER_DELIVERED_MESSAGE);\n this.byteCount = opts.byteCount;\n this.messageControlIdBytes = opts.messageControlIdBytes;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpNeverDeliveredError);\n }\n }\n}\n\n/**\n * Rejects a `send()` that **was** written to the transport and whose acknowledgement had still\n * not arrived when the client finished closing.\n *\n * The ambiguous half of a shutdown report, and it is reported as ambiguous because it is: an\n * acknowledgement lost on the way back is indistinguishable from a message the peer never\n * received. Nothing is retried automatically. HL7 makes the accept acknowledgement the thing\n * that releases a sender from resending, and puts the retransmission decision on the\n * application and on its peer's duplicate detection, keyed on MSH-10 plus MSH-7.\n *\n * Distinct from {@link MllpNeverDeliveredError} (nothing was written, so a resend is safe) and\n * from {@link MllpApplicationAckError} (the peer said in writing that it took custody, so a\n * resend would commit the message twice). Confusing the three is how a consumer's replay logic\n * either duplicates a clinical message or drops one.\n *\n * `flushedAt` is what a replay decision reasons about: it says when these bytes went out, so a\n * caller can compare it against the peer's own record.\n *\n * **Every field is a byte count or a timestamp, and the message is a constant.** No part of\n * the payload reaches this error, not the control ID, not a truncation of it, and not a hex\n * rendering. An `Error` is a diagnostic surface: it is logged, and its `stack` plus its own\n * properties are what an error reporter ships off the box.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpUnknownFateError) {\n * // do NOT blindly resend: the peer may already hold this message\n * logger.warn({ flushedAt: err.flushedAt, bytes: err.byteCount });\n * }\n * }\n * ```\n */\nexport class MllpUnknownFateError extends Error {\n override readonly name = \"MllpUnknownFateError\" as const;\n\n /** Epoch ms at which this send's bytes were written to the transport. */\n readonly flushedAt: number;\n\n /** Milliseconds between that write and this report. */\n readonly elapsedMs: number;\n\n /** Byte count of the framed message that was written. */\n readonly byteCount: number;\n\n /**\n * Byte length of the send's MSH-10 control ID, or `undefined` when there was none to read.\n * The control ID itself is deliberately not here, for the reason given on\n * {@link MllpTimeoutError.messageControlIdBytes}.\n */\n readonly messageControlIdBytes: number | undefined;\n\n /**\n * Construct an unknown-fate report.\n *\n * There is no `message` parameter, deliberately: see {@link UNKNOWN_FATE_MESSAGE}.\n *\n * @param opts - Report context (flush timestamp, elapsed time, byte counts).\n */\n constructor(opts: {\n flushedAt: number;\n elapsedMs: number;\n byteCount: number;\n messageControlIdBytes: number | undefined;\n }) {\n super(UNKNOWN_FATE_MESSAGE);\n this.flushedAt = opts.flushedAt;\n this.elapsedMs = opts.elapsedMs;\n this.byteCount = opts.byteCount;\n this.messageControlIdBytes = opts.messageControlIdBytes;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpUnknownFateError);\n }\n }\n}\n\n/**\n * Set of Node/OpenSSL error codes that indicate a **certificate-verification**\n * failure (as opposed to some other TLS handshake failure), untrusted chain,\n * expired/not-yet-valid certificate, hostname mismatch, revocation, etc.\n *\n * Used by `MllpClient` to classify a TLS connect failure's\n * `connectionCause` as `'tls-verify'` (this set) vs `'tls-handshake'`\n * (everything else observed before `'secureConnect'`). Exported so callers\n * can apply the same classification to their own error handling.\n *\n * @example\n * ```typescript\n * import { isTlsVerificationErrorCode } from '@cosyte/mllp';\n * if (isTlsVerificationErrorCode('CERT_HAS_EXPIRED')) {\n * // definitely a verification failure, not a protocol/cipher mismatch\n * }\n * ```\n */\nexport function isTlsVerificationErrorCode(code: string): boolean {\n switch (code) {\n case \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\":\n case \"DEPTH_ZERO_SELF_SIGNED_CERT\":\n case \"SELF_SIGNED_CERT_IN_CHAIN\":\n case \"UNABLE_TO_GET_ISSUER_CERT\":\n case \"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\":\n case \"CERT_HAS_EXPIRED\":\n case \"CERT_NOT_YET_VALID\":\n case \"CERT_REVOKED\":\n case \"CERT_UNTRUSTED\":\n case \"CERT_REJECTED\":\n case \"CERT_SIGNATURE_FAILURE\":\n case \"HOSTNAME_MISMATCH\":\n case \"ERR_TLS_CERT_ALTNAME_INVALID\":\n return true;\n default:\n return code.startsWith(\"CERT_\");\n }\n}\n\n/**\n * Detects **TLS-protocol-shaped** errors, failures of the TLS protocol\n * itself, as opposed to plain TCP-level network failures.\n *\n * Apply this only to errors raised on a **TLS** connection; `MllpClient`\n * does exactly that (the predicate is consulted only when `ClientOptions.tls`\n * is set). The boundary:\n *\n * **TLS-protocol-shaped (`true`):**\n * - `code` starting `ERR_SSL_` (Node's TLS alert codes, e.g.\n * `ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED`, a `clientAuth: 'MUST'`\n * server rejecting the client's certificate).\n * - `code === 'EPROTO'`, on a TLS connection this is OpenSSL failing the\n * handshake (protocol version mismatch, no shared cipher, a TLS ≤1.2\n * mTLS rejection).\n * - `message` containing `ssl` or `alert` (`/\\bssl\\b|\\balert\\b/i`, \"SSL\n * routines\", \"tlsv13 alert certificate required\", …). This message check\n * is a **heuristic backstop** over the code-based checks above, not a\n * precise boundary: it exists to catch OpenSSL errors that surface without\n * a usable `code`, and `MllpClient` consults it only on connections where\n * TLS is configured. An arbitrary non-TLS error whose message happens to\n * contain those words would also match.\n *\n * **NOT TLS-protocol-shaped (`false`), plain network failures, which stay\n * transient for the reconnect classifier:** `ECONNREFUSED`, `ETIMEDOUT`,\n * `EHOSTUNREACH`, `ENETUNREACH`, `EPIPE`, and a plain `ECONNRESET` carrying\n * no TLS alert context, a network blip during (or after) a handshake\n * should still auto-heal.\n *\n * Certificate-**verification** failures are a separate class, see\n * {@link isTlsVerificationErrorCode}; `MllpClient` checks that first and\n * labels those `connectionCause: 'tls-verify'`.\n *\n * Why this matters: under TLS 1.3 (RFC 8446 §4.4.2) a `clientAuth: 'MUST'`\n * server can reject the client's certificate AFTER the client's own\n * `'secureConnect'`, the rejection then surfaces as a post-connect socket\n * error. A misconfigured mTLS client must never auto-reconnect-loop against\n * a server that will always reject it, so `MllpClient` classifies\n * TLS-protocol-shaped errors as **permanent**.\n *\n * @example\n * ```typescript\n * import { isTlsProtocolError, MllpConnectionError } from '@cosyte/mllp';\n * client.on('error', ({ error }) => {\n * if (error instanceof MllpConnectionError && isTlsProtocolError(error.cause)) {\n * // TLS protocol failure, a configuration problem, not a network blip.\n * }\n * });\n * ```\n */\nexport function isTlsProtocolError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return false;\n const code = (err as { code?: unknown }).code;\n if (typeof code === \"string\") {\n if (code.startsWith(\"ERR_SSL_\")) return true;\n if (code === \"EPROTO\") return true;\n }\n const message = (err as { message?: unknown }).message;\n if (typeof message === \"string\" && /\\bssl\\b|\\balert\\b/i.test(message)) return true;\n return false;\n}\n\n/**\n * Classifies a connection error as transient (eligible for auto-reconnect)\n * or permanent (halts auto-reconnect, transitions to CLOSED).\n *\n * Used internally by `MllpClient` BEFORE invoking `retryStrategy`\n * (see `RetryContext.classifiedAs`). Re-exported so callers can implement\n * their own retry policies.\n *\n * Classification table:\n * - `ENOTFOUND`, `EACCES` → **permanent** (`false`)\n * - `ECONNREFUSED`, `ECONNRESET`, `ETIMEDOUT`, `EHOSTUNREACH`,\n * `ENETUNREACH`, `EPIPE` → **transient** (`true`)\n * - `CERT_*` and `UNABLE_TO_VERIFY_LEAF_SIGNATURE` /\n * `DEPTH_ZERO_SELF_SIGNED_CERT` / `SELF_SIGNED_CERT_IN_CHAIN`\n * (any {@link isTlsVerificationErrorCode} code) → **permanent** (`false`),\n * never auto-reconnect-loop into a misconfigured or MITM'd endpoint.\n * - `ERR_SSL_*` (Node TLS alert codes) → **permanent** (`false`), a TLS\n * protocol failure such as a `clientAuth: 'MUST'` server rejecting the\n * client certificate recurs on every attempt. On TLS-configured\n * connections `MllpClient` additionally consults {@link isTlsProtocolError},\n * which also catches `EPROTO`/alert-bearing OpenSSL errors that this\n * generic classifier (which cannot know the connection was TLS) leaves\n * transient.\n * - `MLLP_*` (any {@link MllpFramingError} code, a fatal decoder throw, surfaced with\n * `connectionCause: 'framing-fatal'`) → **permanent** (`false`). The peer is not\n * speaking MLLP, an HTTP probe, a health check, a wrong-port misconfiguration, or is emitting\n * frames past `maxFrameSizeBytes`. Every reconnect meets the same bytes, so retrying is an\n * unbounded storm against a peer that is already misconfigured. If a peer's quirk is *expected*,\n * the decoder's tolerance opt-ins are the supported answer, they make it a warning, not a fatal.\n * - non-Error / unknown / no-code → **transient** (`true`), Postel's Law\n * default. Reconnect attempts are bounded by `retryStrategy` and the\n * 30s backoff cap, so the default is safe.\n *\n * @example\n * ```typescript\n * import { isTransientConnectionError } from '@cosyte/mllp';\n * client.on('error', (err) => {\n * if (isTransientConnectionError(err)) {\n * metrics.increment('mllp.transient_error');\n * } else {\n * metrics.increment('mllp.permanent_error');\n * }\n * });\n * ```\n */\nexport function isTransientConnectionError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return true;\n const code = (err as { code?: unknown }).code;\n if (typeof code !== \"string\") return true;\n switch (code) {\n case \"ENOTFOUND\":\n case \"EACCES\":\n return false;\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"ETIMEDOUT\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n case \"EPIPE\":\n return true;\n default:\n // TLS cert error codes (CERT_*) and *_VERIFY_* names → permanent.\n if (isTlsVerificationErrorCode(code)) return false;\n // Node TLS alert codes → permanent (Phase 8; recur on every attempt).\n if (code.startsWith(\"ERR_SSL_\")) return false;\n // A fatal MLLP framing error → permanent (MLLP-10). The peer is not speaking MLLP (an HTTP\n // probe, a health check, a wrong-port misconfiguration) or is emitting frames past the size\n // cap. Every reconnect meets the same bytes, so retrying is an unbounded reconnect storm\n // against a peer that is already misconfigured, the same reasoning that makes the TLS\n // classes permanent. A compatibility failure is not a network blip.\n if (code.startsWith(\"MLLP_\")) return false;\n // Default: transient (Postel's Law, be permissive about peer behavior).\n return true;\n }\n}\n\n/**\n * Thrown (or rejects the `send()` promise) when the in-flight queue exceeds\n * the configured high-water mark and `onBackpressure: 'reject'` is set.\n *\n * `highWaterMark` accepts a count cap, a byte cap, or both, when both are\n * present, the stricter-of-two trigger wins.\n *\n * @example\n * ```typescript\n * try {\n * await client.send(payload);\n * } catch (err) {\n * if (err instanceof MllpBackpressureError) {\n * logger.warn({\n * queueDepth: err.queueDepth,\n * queueBytes: err.queueBytes,\n * cap: err.highWaterMark,\n * });\n * }\n * }\n * ```\n */\nexport class MllpBackpressureError extends Error {\n override readonly name = \"MllpBackpressureError\" as const;\n\n /** Number of in-flight + queued sends at the moment of rejection. */\n readonly queueDepth: number;\n\n /** Total bytes of in-flight + queued frames at the moment of rejection. */\n readonly queueBytes: number;\n\n /** The high-water-mark configuration that was triggered. */\n readonly highWaterMark: { readonly count?: number; readonly bytes?: number };\n\n /**\n * Construct an MLLP backpressure error.\n *\n * @param message - Human-readable error message.\n * @param opts - Backpressure context (queue depth, queued bytes, the high-water-mark hit).\n */\n constructor(\n message: string,\n opts: {\n queueDepth: number;\n queueBytes: number;\n highWaterMark: { count?: number; bytes?: number };\n },\n ) {\n super(message);\n this.queueDepth = opts.queueDepth;\n this.queueBytes = opts.queueBytes;\n this.highWaterMark = opts.highWaterMark;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpBackpressureError);\n }\n }\n}\n","/**\n * MLLP Client, `createClient()` factory and `MllpClient` class.\n *\n * Provides the client-side MLLP transport: connect to an MLLP server over TCP,\n * encode and send framed messages, decode inbound ACKs, and surface lifecycle\n * events with frozen payloads. Supports `AbortSignal` cancellation on every\n * awaitable and `Symbol.asyncDispose` for `await using` ergonomics.\n *\n * The `correlateByControlId` option (MSH-10 → MSA-2 ACK matching) enables\n * out-of-order ACK handling.\n *\n * @example\n * ```typescript\n * import { createClient } from '@cosyte/mllp';\n *\n * const client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createConnection } from \"node:net\";\nimport type { Socket } from \"node:net\";\nimport { connect as tlsConnect } from \"node:tls\";\nimport type { TLSSocket } from \"node:tls\";\nimport { EventEmitter } from \"node:events\";\nimport { Connection } from \"../connection/index.js\";\nimport type { ConnectionState, StateChangeEvent } from \"../connection/index.js\";\nimport { MllpConnectionError } from \"../connection/index.js\";\nimport { safeEmit, safeEmitError } from \"../internal/safe-emit.js\";\nimport { NetTransport, TlsTransport } from \"../transport/index.js\";\nimport type { Transport } from \"../transport/index.js\";\nimport type { TlsOptions } from \"../transport/tls-options.js\";\nimport { MLLP_TLS_VERIFY_DISABLED, type SecurityWarning } from \"../transport/security-warnings.js\";\nimport { resolveTlsCipherPolicy } from \"../transport/tls-cipher-policy.js\";\nimport { readNegotiatedTlsParameters } from \"../transport/negotiated-tls.js\";\nimport { encodeFrame } from \"../framing/index.js\";\nimport { MllpFramingError } from \"../framing/index.js\";\nimport type { FrameReaderOptions, MllpWarning, WarningCode } from \"../framing/index.js\";\nimport { Correlator, extractMsaControlId } from \"./correlator.js\";\nimport type { AckModeWarningContext, PendingAck, TwoPhaseState } from \"./correlator.js\";\nimport { ackDiagnosticMessage } from \"./ack-diagnostics.js\";\nimport type { AckCorrelationCode } from \"./ack-diagnostics.js\";\nimport { controlIdFromMsh, readMshSegment } from \"../internal/control-id.js\";\nimport {\n classifyOutboundAckMode,\n type CommitAckReport,\n type OutboundAckMode,\n} from \"../internal/ack-mode.js\";\nimport { ackModeDiagnosticMessage, type AckModeCode } from \"../internal/ack-mode-diagnostics.js\";\nimport {\n MllpTimeoutError,\n MllpBackpressureError,\n MllpApplicationAckError,\n MllpCommitRejectedError,\n MllpNeverDeliveredError,\n MllpUnknownFateError,\n isTransientConnectionError,\n isTlsVerificationErrorCode,\n isTlsProtocolError,\n} from \"./error.js\";\n\n/**\n * A send parked INSIDE the client with no bytes on the wire: one waiting for the queue to fall\n * below the high-water mark, or for the single in-flight slot `pipeline: false` allows.\n *\n * Such a send is not in the correlator, so nothing else on the shutdown path can see it, and\n * before this register existed its promise was simply left pending forever when the client\n * closed. It is the clearest case of a message that never reached the transport.\n */\ninterface ParkedSend {\n /** Stop waiting and fail the send. Assigned once the waiting is wired. */\n cancel: () => void;\n}\n\n/**\n * Module-level \"never aborts\" sentinel for `RetryContext.signal`.\n *\n * When `connect()` is called WITHOUT a signal, `RetryContext.signal` must\n * still be a real `AbortSignal` (the type is non-optional). This sentinel\n * is constructed once and reused across all signal-less reconnect cycles\n * no new AbortController is allocated per cycle.\n *\n * The originating `AbortController` is held in module-private scope and\n * never exposed; hostile callers cannot abort the sentinel.\n */\nconst NEVER_ABORTING_SIGNAL: AbortSignal = new AbortController().signal;\n\n/**\n * Warning context for a diagnostic raised on an **outbound** send, where there is no\n * inbound frame to take an offset from and nothing to count.\n *\n * Every field is a fixed zero or `null` on purpose: the caller already holds the payload it\n * passed to `send()`, so a diagnostic about it has nothing to add beyond the code.\n */\nconst OUTBOUND_WARNING_CONTEXT: AckModeWarningContext = Object.freeze({\n msa1Bytes: null,\n ackCode: null,\n controlIdBytes: null,\n byteOffset: 0,\n elapsedSinceSendMs: 0,\n});\n\n/**\n * The `'warning'` payload the client emits for an ACK-correlation deviation\n * (`MLLP_ACK_UNMATCHED_CONTROL_ID`, `MLLP_ACK_AFTER_TIMEOUT`).\n *\n * A `MllpWarning` with two extra numeric fields. `message` is a **frozen\n * registry entry**, byte-for-byte identical for a given code no matter what\n * arrived on the wire, and everything input-derived is a number: the control\n * ID's byte length and the elapsed time. A warning is a log line, so it carries\n * no field content.\n *\n * @example\n * ```typescript\n * client.on('warning', (w: AckCorrelationWarning) => {\n * logger.warn({ code: w.code, idBytes: w.controlIdBytes, elapsedMs: w.elapsedSinceSendMs });\n * });\n * ```\n */\nexport interface AckCorrelationWarning extends MllpWarning {\n /** The correlation code this warning reports. */\n readonly code: AckCorrelationCode;\n /**\n * Byte length of the control ID involved, or `null` when there was none to\n * read. Control IDs are decoded `latin1`, so a code-unit count is a byte count.\n */\n readonly controlIdBytes: number | null;\n /**\n * Milliseconds between the send's write-flush (or its timeout, for a late\n * ACK) and this warning.\n */\n readonly elapsedSinceSendMs: number;\n}\n\n/**\n * The `'warning'` payload the client emits for an **acknowledgement-mode** deviation: an\n * enhanced-mode send on a client that cannot correlate two acknowledgements, a Table 0155\n * value it did not recognise, an acknowledgement it could not classify into a mode, and a\n * further acknowledgement for a send that is already settled or failed.\n *\n * It arrives on the same `'warning'` event as a framing warning and an\n * {@link AckCorrelationWarning}, so narrow on `code` before reading its fields. It is\n * **not** an `MllpWarning`: its `code` is an {@link AckModeCode}, deliberately a family of\n * its own rather than an addition to the decoder's registry, and it is not counted in\n * `getStats().warningsByCode`, whose keys are the decoder's codes.\n *\n * Everything input-derived on it is a number or a member of the closed six-code Table 0008\n * set. `message` is a **frozen registry entry**, byte-for-byte identical for a given code\n * no matter what arrived on the wire. A warning is a log line, so it carries no field\n * content: an MSA-1 nobody could classify is reported by its byte length alone.\n *\n * @example\n * ```typescript\n * client.on('warning', (w: AckModeWarning) => {\n * if (w.code === 'MLLP_ACK_MSA1_UNCLASSIFIABLE') {\n * logger.warn({ code: w.code, msa1Bytes: w.msa1Bytes, at: w.byteOffset });\n * }\n * });\n * ```\n */\nexport interface AckModeWarning {\n /** The acknowledgement-mode code this warning reports. */\n readonly code: AckModeCode;\n /** Frozen registry text for `code`. Never interpolated. */\n readonly message: string;\n /** Inbound frame's stream byte offset; `0` for a warning raised on an outbound send. */\n readonly byteOffset: number;\n /** Byte length of the MSA-1 field involved, or `null` when the code reports none. */\n readonly msa1Bytes: number | null;\n /** Table 0008 code involved, or `null` when the code reports none. */\n readonly ackCode: \"AA\" | \"AE\" | \"AR\" | \"CA\" | \"CE\" | \"CR\" | null;\n /** Byte length of the control ID involved, or `null` when there was none to read. */\n readonly controlIdBytes: number | null;\n /** Milliseconds since the send's write-flush, or since its disposal for a late ACK. */\n readonly elapsedSinceSendMs: number;\n /** Connection identifier, `undefined` before a connection is attached. */\n readonly connectionId: string | undefined;\n /** Wall-clock time at point of emission. */\n readonly timestamp: Date;\n}\n\n/**\n * Context passed to a custom `retryStrategy` hook on each reconnect attempt.\n *\n * Frozen via `Object.freeze` before invocation, handlers cannot mutate.\n *\n * @example\n * ```typescript\n * const retryStrategy: RetryStrategy = (ctx) => {\n * if (ctx.attempt >= 5) return null;\n * if (ctx.classifiedAs === 'permanent') return null;\n * return Math.min(30_000, 1000 * (ctx.attempt + 1));\n * };\n * ```\n */\nexport interface RetryContext {\n /** 0-indexed attempt counter for the current reconnect cycle. */\n readonly attempt: number;\n /** The error that triggered the disconnect. */\n readonly lastError: Error;\n /** Delay used for the previous attempt (ms). 0 on the first attempt. */\n readonly lastDelayMs: number;\n /** Total wall-clock ms elapsed since the disconnect that started this cycle. */\n readonly totalElapsedMs: number;\n /** Ms since the last successful ACK. `Infinity` if no success seen. */\n readonly sinceLastSuccessMs: number;\n /** How the failure that triggered this reconnect attempt was classified. */\n readonly classifiedAs: \"transient\" | \"permanent\";\n /**\n * The same `AbortSignal` passed into `connect()`. If no signal was\n * supplied, the module-level `NEVER_ABORTING_SIGNAL` sentinel is provided\n * so handlers always have a real `AbortSignal` to inspect.\n */\n readonly signal: AbortSignal;\n}\n\n/**\n * Custom reconnect-backoff hook. Return `null` to halt\n * reconnection, the FSM transitions to `CLOSED`.\n */\nexport type RetryStrategy = (ctx: RetryContext) => number | null;\n\n/**\n * Combined count + byte-based queue cap. Stricter-of-two wins.\n *\n * - `number`, count cap only (default 64).\n * - `{ bytes }`, byte cap only.\n * - `{ count, bytes }`, both caps; whichever trips first wins.\n *\n * @example\n * ```typescript\n * const opts: ClientOptions = {\n * host: 'localhost', port: 2575,\n * highWaterMark: { count: 100, bytes: 1_000_000 },\n * };\n * ```\n */\nexport type HighWaterMark = number | { readonly count?: number; readonly bytes?: number };\n\n/**\n * Options for {@link createClient} and the {@link MllpClient} constructor.\n *\n * @example\n * ```typescript\n * const opts: ClientOptions = { host: 'localhost', port: 2575, drainTimeoutMs: 10_000 };\n * ```\n */\nexport interface ClientOptions {\n /** Host to connect to (e.g. `'localhost'` or `'mllp.example.com'`). */\n readonly host: string;\n /** TCP port. */\n readonly port: number;\n /** FrameReader tolerance / size options. `onFrame` and `onWarning` are managed internally. */\n readonly framing?: Omit<FrameReaderOptions, \"onFrame\" | \"onWarning\">;\n /**\n * Bound on the acknowledgement wait {@link MllpClient.close} performs (default: `30_000`\n * ms). Sends already written to the transport are awaited until it elapses; the wait ends\n * early the moment the last of them is answered.\n */\n readonly drainTimeoutMs?: number;\n /**\n * Per-message ACK timeout in milliseconds. The clock starts at\n * the underlying `write()` flush callback, NOT at the `send()` call,\n * pre-flush queue time is not charged to the peer. Default: `30_000`.\n */\n readonly ackTimeoutMs?: number;\n /**\n * Bound, in milliseconds, on the **second** wait an enhanced-mode send can enter: the\n * one that starts when an accept acknowledgement (`CA`) reports that the peer has\n * committed the message, and ends when the application acknowledgement arrives.\n *\n * Measured from the moment that accept acknowledgement was received, not from the send,\n * so a peer that commits at 9 s and applies at 12 s settles the send successfully at\n * 12 s. Defaults to whatever `ackTimeoutMs` is in force for the send, and can be\n * overridden per send. It only ever applies to a send whose MSH-16 asks for an\n * application acknowledgement, so it changes nothing for an original-mode interface.\n *\n * @default the send's own `ackTimeoutMs`\n */\n readonly applicationAckTimeoutMs?: number;\n /**\n * If `true`, ACKs are matched against outgoing sends by MSH-10 → MSA-2.\n * Default `false` (FIFO mode).\n *\n * Out-of-order ACKs from the peer are supported in this mode. MSH-10 is\n * extracted from the outbound payload before send; MSA-2 is extracted from\n * the inbound ACK payload. An ACK whose MSA-2 matches no pending send\n * (and is not in the late-ACK graveyard) emits a frozen\n * `MllpFramingError('MLLP_ACK_UNMATCHED_CONTROL_ID')` to the `'error'` event.\n * A late ACK whose MSA-2 matches a graveyard entry emits a\n * `MLLP_ACK_AFTER_TIMEOUT` warning and is dropped.\n *\n * @default false\n */\n readonly correlateByControlId?: boolean;\n /**\n * Auto-reconnect on transient disconnect. Default `false`.\n *\n * When `true`, dropped connections caused by transient errors (per\n * {@link isTransientConnectionError}) trigger the FSM cycle\n * `CONNECTED → DISCONNECTED → RECONNECTING → CONNECTING → CONNECTED`\n * with exponential backoff unless overridden by\n * {@link ClientOptions.retryStrategy}. Permanent errors halt and\n * transition directly to `CLOSED`.\n */\n readonly autoReconnect?: boolean;\n /**\n * Custom reconnect-backoff hook. Return `null` to halt\n * reconnection. Receives a frozen {@link RetryContext}. Defaults\n * to the exponential strategy.\n */\n readonly retryStrategy?: RetryStrategy;\n /** First delay (ms) on auto-reconnect; default 100. */\n readonly initialDelayMs?: number;\n /** Maximum backoff cap (ms); default 30_000. */\n readonly maxDelayMs?: number;\n /** Backoff multiplier; default 2. */\n readonly multiplier?: number;\n /** Jitter fraction, e.g. 0.2 = ±20%; default 0.2. */\n readonly jitter?: number;\n /**\n * Application-level high-water mark on the in-flight + queued send set.\n * `number` configures a count cap (default 64);\n * `{ bytes }` configures a byte cap; `{ count, bytes }` configures\n * both, with the stricter-of-two trigger winning.\n *\n * When the cap is exceeded, behavior is governed by\n * {@link ClientOptions.onBackpressure}.\n *\n * @default 64\n */\n readonly highWaterMark?: HighWaterMark;\n /**\n * Behavior when the high-water mark is exceeded.\n *\n * - `'reject'` (default), `send()` rejects with `MllpBackpressureError`.\n * - `'wait'`, `send()` awaits the `'drain'` event OR the per-message\n * `ackTimeoutMs` OR `signal` abort, whichever fires first.\n *\n * @default 'reject'\n */\n readonly onBackpressure?: \"reject\" | \"wait\";\n /**\n * Strict serialization send → await-ACK → send.\n *\n * - `true` (default), concurrent in-flight sends up to\n * {@link ClientOptions.highWaterMark}.\n * - `false`, collapses the in-flight set to ≤1 (the unified Correlator's\n * `maxInFlight=1`); the next `send()` waits for the prior ACK before\n * reaching the wire.\n *\n * @default true\n */\n readonly pipeline?: boolean;\n /**\n * TCP keepalive interval (ms). Sets `socket.setKeepAlive(true, ms)` on\n * the underlying `net.Socket` BEFORE wrapping in `NetTransport`. OS-level\n * half-open detection (network partitions, NAT-table eviction). Independent\n * of {@link ClientOptions.deadPeerTimeoutMs}.\n *\n * @default undefined (off)\n */\n readonly keepaliveIntervalMs?: number;\n /**\n * Application-idle timeout (ms) keyed on last inbound bytes / ACK / warning.\n * On trip, calls `connection.destroy(new Error('dead\n * peer timeout'))` which surfaces as `MllpConnectionError({ phase: 'receive' })`.\n * Trip honors {@link ClientOptions.autoReconnect}. Independent of\n * {@link ClientOptions.keepaliveIntervalMs}.\n *\n * @default undefined (off)\n */\n readonly deadPeerTimeoutMs?: number;\n /**\n * Enable TLS (MLLPS) for this connection. `true` enables TLS with\n * all defaults, including certificate verification **on**. Pass a\n * {@link TlsOptions} object to customize (`ca`/`cert`/`key`, minimum\n * version, ciphers, `allowUnverified`, …).\n *\n * Spec anchor: IHE ATNA ITI-19 (https://profiles.ihe.net/ITI/TF/Volume2/ITI-19.html).\n *\n * @default undefined (plaintext TCP)\n */\n readonly tls?: TlsOptions | true;\n}\n\n/**\n * Observability snapshot returned by {@link MllpClient.getStats}.\n *\n * All fields are JSON-serializable, no Buffers, no class instances,\n * no Maps, no circular references. `lastConnectedAt` and `lastAckAt` are\n * **epoch milliseconds** (numbers), NOT `Date` instances, log-pipeline\n * friendly.\n *\n * `warningsByCode` keys are constrained to the public {@link WarningCode}\n * union, adding/removing a code is a breaking change (CLAUDE.md\n * stable-codes guardrail enforced at the type boundary).\n *\n * @example\n * ```typescript\n * const stats = client.getStats();\n * logger.info(JSON.stringify(stats));\n * // {\"state\":\"CONNECTED\",\"connectionId\":\"…\",\"queueDepth\":0, … }\n * ```\n */\nexport interface ClientStats {\n /** Current FSM state, mirrors `client.state`. */\n readonly state: ConnectionState;\n /** Live Connection's id, or `null` before the first connect (or post-CLOSED). */\n readonly connectionId: string | null;\n /** Total live correlator entries (in-flight + pre-flush + serialization-queued). */\n readonly queueDepth: number;\n /** Sum of `frame.length` across live correlator entries. */\n readonly queueBytes: number;\n /** Entries with `sentAt !== null`, actually written to the wire / awaiting ACK. */\n readonly inFlight: number;\n /**\n * Aggregated warning counts. Keys are constrained to the public\n * {@link WarningCode} union. Connection-level warnings + Correlator\n * `MLLP_ACK_*` warnings are merged.\n */\n readonly warningsByCode: Partial<Record<WarningCode, number>>;\n /** Bytes received from the peer (current Connection). */\n readonly totalBytesIn: number;\n /** Bytes written to the peer (current Connection). */\n readonly totalBytesOut: number;\n /** Total successful `connection.send()` calls since construction. */\n readonly sentTotal: number;\n /** Total ACKs matched + resolved since construction. */\n readonly ackedTotal: number;\n /** Total ACK timeouts since construction. */\n readonly timedOutTotal: number;\n /** Total reconnect attempts since construction. */\n readonly reconnectAttempts: number;\n /** Epoch ms of the last `CONNECTED` transition. `null` until first connect. */\n readonly lastConnectedAt: number | null;\n /** Epoch ms of the most recent successful ACK. `null` until first ACK. */\n readonly lastAckAt: number | null;\n /** Whether this client is configured for TLS. Mirrors `ClientOptions.tls` being set. */\n readonly tls: boolean;\n}\n\n/**\n * MLLP client, composes a single {@link Connection} over a {@link NetTransport}\n * (production) or any other `Transport` (testing via {@link InMemoryTransport}).\n *\n * Public events, every payload `Object.freeze`'d before emission:\n * - `'stateChange'`, `{ from, to, reason? }` from the underlying Connection FSM\n * - `'connect'`, `{ connectionId }` once the FSM enters `CONNECTED`\n * - `'disconnect'`, `{ connectionId }` once the FSM enters `DISCONNECTED`\n * - `'reconnecting'`, `{ connectionId, attempt?, delayMs? }`\n * - `'close'`, `{ connectionId }` once the FSM enters terminal `CLOSED`\n * - `'message'`, `{ payload, connectionId, byteOffset, warnings }` for every inbound frame\n * - `'warning'`, `MllpWarning | AckCorrelationWarning`. A framing deviation arrives as a\n * `MllpWarning` enriched with `connectionId` from the Connection layer; the two\n * ACK-correlation codes arrive as {@link AckCorrelationWarning}, which adds\n * `controlIdBytes` and `elapsedSinceSendMs`. Narrow on `code` before reading either.\n * - `'securityWarning'`, `SecurityWarning`. Emitted on every successful\n * `secureConnect` (initial + every reconnect) when `tls.allowUnverified` is `true`\n * with code `MLLP_TLS_VERIFY_DISABLED`. Also mirrored to `process.emitWarning`.\n * - `'tlsNegotiated'`, {@link NegotiatedTlsParameters}. Emitted once per completed TLS\n * handshake (initial + every reconnect) with the negotiated protocol version and\n * cipher suite. Never emitted on a plaintext connection.\n * - `'error'`, re-emitted from Connection. Guarded by `listenerCount('error') > 0` so\n * absence of a listener does NOT crash the process (server precedent).\n *\n * @example\n * ```typescript\n * const client = createClient({ host: 'localhost', port: 2575 });\n * client.on('stateChange', ({ from, to }) => logger.info({ from, to }));\n * client.on('message', ({ payload }) => logger.info({ bytes: payload.length }));\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n */\nexport class MllpClient extends EventEmitter {\n private readonly _opts: ClientOptions;\n private _connection: Connection | null = null;\n private _socket: Socket | null = null;\n /**\n * Initial state for `get state()` before `_connection` exists. Once a Connection\n * is attached, `state` mirrors `_connection.state`.\n */\n private _state: ConnectionState = \"DISCONNECTED\";\n\n /** Per-message ACK timeout in ms. Resolved at construction. */\n private readonly _ackTimeoutMs: number;\n /**\n * Default bound on the second (application-acknowledgement) wait, in ms.\n * `undefined` means \"whatever ACK timeout is in force for that send\".\n */\n private readonly _applicationAckTimeoutMs: number | undefined;\n /** controlId-mode flag. `false` → FIFO. */\n private readonly _correlateByControlId: boolean;\n /** Unified ACK correlator. Built during `_attachConnection`. */\n private _correlator: Correlator | null = null;\n /**\n * Periodic ACK-timeout sweep timer. Drives `_correlator.expireDue()` because\n * the Correlator is timer-free. Cleared on close/destroy.\n */\n private _ackSweepTimer: ReturnType<typeof setInterval> | null = null;\n\n // ── Reconnect state (Plan 04, CLIENT-05/06/12/17/18) ─────────────────────\n private readonly _autoReconnect: boolean;\n private readonly _initialDelayMs: number;\n private readonly _maxDelayMs: number;\n private readonly _multiplier: number;\n private readonly _jitter: number;\n private readonly _retryStrategy: RetryStrategy | undefined;\n\n /** 0-indexed attempt counter for the current reconnect cycle. */\n private _attempt = 0;\n /**\n * Total reconnect attempts since construction, surfaced as\n * `getStats().reconnectAttempts`. Incremented at the entry of every\n * `_handleDisconnect` invocation that proceeds to schedule a backoff.\n */\n private _reconnectAttempts = 0;\n /** Epoch ms of the last successful ACK. Drives backoff-reset. */\n private _lastSuccessAt: number | null = null;\n /** Epoch ms when the current reconnect cycle began. `null` outside a cycle. */\n private _reconnectCycleStartedAt: number | null = null;\n /** Active backoff `setTimeout` handle. `null` when no backoff is armed. */\n private _backoffTimer: ReturnType<typeof setTimeout> | null = null;\n /** Last delay (ms) used by the strategy. Surfaced via RetryContext.lastDelayMs. */\n private _lastDelayMs = 0;\n\n // ── Backpressure + pipeline state (Plan 05, CLIENT-07/CLIENT-19, D-23) ───\n /** Count cap; `Number.POSITIVE_INFINITY` when only bytes configured. */\n private readonly _hwmCount: number;\n /** Byte cap; `Number.POSITIVE_INFINITY` when only count configured. */\n private readonly _hwmBytes: number;\n /** Backpressure policy; default `'reject'`. */\n private readonly _onBackpressure: \"reject\" | \"wait\";\n /** Pipeline flag; default `true` (parallel up to highWaterMark). */\n private readonly _pipeline: boolean;\n /** Dead-peer idle timer. `null` when not armed. */\n private _deadPeerTimer: ReturnType<typeof setTimeout> | null = null;\n /** True once the self-'ack' listener that resets the dead-peer timer\n * has been attached. Guards against duplicate listeners on reconnect. */\n private _ackResetWired = false;\n /** Most recently bound `connect()` signal. Reread on every RetryContext build. */\n private _connectSignal: AbortSignal | undefined;\n /** Set when close()/destroy()/abort fires; reconnect handler short-circuits. */\n private _userClosed = false;\n /**\n * Sends parked inside the client with nothing written for them. See {@link ParkedSend}.\n * Every entry removes itself the moment its own wait ends, however it ends.\n */\n private readonly _parkedSends: Set<ParkedSend> = new Set();\n /**\n * Re-checks whether the shutdown drain is over, `null` when no `close()` is waiting.\n * Called from every point at which a pending send is settled.\n */\n private _drainWaiter: (() => void) | null = null;\n /**\n * Ends the shutdown drain wait now, whatever is still outstanding. `null` when no `close()`\n * is waiting. `destroy()` calls it, which is what makes a destroy during a drain prompt.\n */\n private _endDrainWait: (() => void) | null = null;\n /**\n * The `beforeClose` hook this client installed on its Connection, so a second `close()` call\n * composes nothing twice. Compared by identity, never by shape.\n */\n private _drainHook: ((drainTimeoutMs: number) => Promise<void>) | null = null;\n /** Captured Connection error feeding `RetryContext.lastError`. */\n private _lastError: Error | null = null;\n /** Listener-removal handle for the abort listener bound in connect(). */\n private _abortListener: { signal: AbortSignal; handler: () => void } | null = null;\n /**\n * Test-only seam, when set, `_beginReconnectAttempt` builds the new\n * Connection through this factory instead of opening a real net.Socket.\n *\n * @internal\n */\n private _reconnectFactory: (() => { conn: Connection; arm: () => void }) | null = null;\n\n // ── PLAN-06, observability counters for getStats (OBS-01, D-26) ─────────\n /** Total successful `conn.send()` flushes since construction. */\n private _sentTotal = 0;\n /** Total ACKs resolved since construction. */\n private _ackedTotal = 0;\n /** Total ACK timeouts since construction. */\n private _timedOutTotal = 0;\n /** Epoch ms of the most recent CONNECTED transition (null until first). */\n private _lastConnectedAt: number | null = null;\n /** Epoch ms of the most recent successful ACK (null until first). */\n private _lastAckAt: number | null = null;\n /**\n * Aggregated Correlator-emitted warning counts (MLLP_ACK_*). Connection-level\n * warnings are read directly from `_connection.getStats().warningsByCode`\n * at observation time and merged into the snapshot.\n */\n private _aggregatedWarningsByCode: Partial<Record<WarningCode, number>> = {};\n\n /**\n * Construct an MLLP client. Created idle; call `connect()` (or use\n * {@link createClient}/{@link createStarterClient}) to open the connection.\n *\n * @param opts - Client options (host/port, ACK timeout, reconnect/backpressure policy, …).\n */\n constructor(opts: ClientOptions) {\n super();\n this._opts = opts;\n this._ackTimeoutMs = opts.ackTimeoutMs ?? 30_000;\n this._applicationAckTimeoutMs = opts.applicationAckTimeoutMs;\n this._correlateByControlId = opts.correlateByControlId === true;\n this._autoReconnect = opts.autoReconnect === true;\n this._initialDelayMs = opts.initialDelayMs ?? 100;\n this._maxDelayMs = opts.maxDelayMs ?? 30_000;\n this._multiplier = opts.multiplier ?? 2;\n this._jitter = opts.jitter ?? 0.2;\n this._retryStrategy = opts.retryStrategy;\n\n // Plan 05, backpressure + pipeline (D-23, D-06).\n const hwm: HighWaterMark = opts.highWaterMark ?? 64;\n if (typeof hwm === \"number\") {\n this._hwmCount = hwm;\n this._hwmBytes = Number.POSITIVE_INFINITY;\n } else {\n this._hwmCount = hwm.count ?? Number.POSITIVE_INFINITY;\n this._hwmBytes = hwm.bytes ?? Number.POSITIVE_INFINITY;\n }\n this._onBackpressure = opts.onBackpressure ?? \"reject\";\n this._pipeline = opts.pipeline !== false;\n }\n\n /**\n * Current FSM state. Mirrors the underlying Connection's state once attached;\n * before `connect()` (or after a `CLOSED` Connection is dropped) reports\n * the client-level baseline (`'DISCONNECTED'`).\n */\n get state(): ConnectionState {\n return this._connection?.state ?? this._state;\n }\n\n /**\n * Open a TCP (or TLS) connection to the configured `host:port`\n * and attach a {@link Connection} to it. Resolves once the FSM\n * enters `CONNECTED`, for TLS, on `'secureConnect'` (handshake complete,\n * including certificate verification when it is on).\n *\n * Rejects with:\n * - `DOMException('Aborted', 'AbortError')` if `signal` is provided and aborts\n * before the connect resolves.\n * - `MllpConnectionError({ phase: 'connect' })` if the underlying socket emits\n * `error` before connecting, OR if the client is already connecting/connected.\n * TLS failures carry a `connectionCause`: `'tls-verify'` for certificate\n * verification failures, `'tls-handshake'` for TLS-protocol-shaped failures\n * ({@link isTlsProtocolError}); pure TCP failures carry none.\n * - `MllpTlsConfigurationError` if the configured cipher-suite list cannot be\n * honoured (the runtime rejects it, or `atnaTransportSecurity` and `ciphers`\n * both declare one). Raised **before** a socket is opened, so nothing is left\n * connected; identify it by `instanceof` plus its stable `code`.\n *\n * **Dual failure signal on initial connect:** the Connection's transport\n * error handler is attached before this promise's own error listener, so a\n * pre-connect socket error produces BOTH the promise rejection AND a\n * client `'error'` event (when an `'error'` listener is attached). Handle\n * whichever fits your flow; they describe the same underlying failure.\n *\n * **TLS 1.3 + mutual TLS caveat (RFC 8446 §4.4.2):** `connect()` resolving\n * does NOT guarantee that a `clientAuth: 'MUST'` server accepted your\n * client certificate. Under TLS 1.3 the client's handshake, and its\n * `'secureConnect'`, can complete before the server finishes validating\n * the certificate; a rejection then surfaces moments later as a typed\n * post-connect error (`'error'` event with an `ERR_SSL_*`/alert cause,\n * classified **permanent**, no auto-reconnect loop). ACK correlation via\n * {@link MllpClient.send} remains the delivery guarantee: no send resolves\n * without its ACK, so a rejected session can never silently \"deliver\".\n *\n * @example\n * ```typescript\n * const ac = new AbortController();\n * setTimeout(() => ac.abort(), 5_000);\n * await client.connect({ signal: ac.signal });\n * ```\n */\n connect(opts?: { signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Reject if we already hold a live Connection. Once a Connection has reached\n // CLOSED or DISCONNECTED we drop the reference and allow a fresh connect().\n if (\n this._connection !== null &&\n this._connection.state !== \"CLOSED\" &&\n this._connection.state !== \"DISCONNECTED\"\n ) {\n return Promise.reject(\n new MllpConnectionError(\"already connected or connecting\", {\n cause: new Error(\"already connected\"),\n phase: \"connect\",\n }),\n );\n }\n\n // Capture the connect signal for the reconnect cycle (W-07). Each call\n // overwrites the prior binding; `RetryContext.signal` reads `_connectSignal`\n // at the moment a RetryContext is built.\n if (signal !== undefined) {\n this._captureConnectSignal(signal);\n }\n\n return new Promise<void>((resolve, reject) => {\n let aborted = false;\n\n const { socket, transport } = this._createSocketAndTransport();\n // Plan 05, TCP keepalive set on the raw socket BEFORE NetTransport\n // wrap (CLIENT-08, D-11/A3, mirrors Phase 4 server). OS-level\n // half-open detection. No JS-side timer (W-03).\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n this._socket = socket;\n\n const connOpts =\n this._opts.framing !== undefined\n ? { transport, framing: this._opts.framing }\n : { transport };\n if (this._opts.drainTimeoutMs !== undefined) {\n (connOpts as { drainTimeoutMs?: number }).drainTimeoutMs = this._opts.drainTimeoutMs;\n }\n const conn = new Connection(connOpts);\n this._attachConnection(conn);\n\n const connectEventName = this._opts.tls !== undefined ? \"secureConnect\" : \"connect\";\n\n const cleanup = (): void => {\n if (signal !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n socket.removeListener(connectEventName, onSocketConnect);\n socket.removeListener(\"error\", onSocketError);\n };\n\n const abortHandler = (): void => {\n aborted = true;\n cleanup();\n // Tear down the in-flight attempt, also clears the correlator so\n // any sweep timer armed by _attachConnection is released.\n this._teardownCorrelator(\n new MllpConnectionError(\"connect aborted\", {\n cause: new Error(\"aborted\"),\n phase: \"connect\",\n }),\n );\n conn.destroy(new Error(\"aborted\"));\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n\n const onSocketConnect = (): void => {\n if (aborted) return;\n // TLS and plaintext behave identically here: 'secureConnect' (TLS)\n // or 'connect' (plaintext) immediately arms the Connection and\n // resolves. Deferring notifyConnect for TLS was tried and removed,\n // any delay leaves the Connection in CONNECTING while post-handshake\n // frames arrive, and Connection discards frames outside\n // CONNECTED/DRAINING (a silent inbound-frame drop). See the\n // TLS 1.3 note in the method JSDoc for what this means for mTLS.\n cleanup();\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n this._emitInsecureWarningIfNeeded();\n this._emitNegotiatedTlsParameters(socket);\n resolve();\n };\n\n const onSocketError = (err: Error): void => {\n if (aborted) return;\n cleanup();\n // Surface the OS error wrapped in MllpConnectionError (Connection's\n // _onTransportError handles the same wrap once attached, but the\n // socket's 'error' may arrive before NetTransport hands it off).\n reject(this._wrapConnectError(err, \"connect\"));\n };\n\n if (signal !== undefined) {\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n socket.once(connectEventName, onSocketConnect);\n socket.once(\"error\", onSocketError);\n });\n }\n\n /**\n * Build the raw socket + `Transport` pair for a connect / reconnect attempt.\n *\n * Plaintext (`ClientOptions.tls` unset): a `net.Socket` wrapped in\n * `NetTransport`. TLS: a `tls.TLSSocket` wrapped in\n * `TlsTransport`, verification defaults **on**\n * (`rejectUnauthorized: !allowUnverified`), floor `minVersion: 'TLSv1.2'`\n * (the IHE ATNA ITI-19 BCP195 floor), `servername` defaulting to\n * `ClientOptions.host`.\n */\n private _createSocketAndTransport(): {\n socket: Socket | TLSSocket;\n transport: Transport;\n } {\n const tlsOpt = this._opts.tls;\n if (tlsOpt === undefined) {\n const socket = createConnection({ host: this._opts.host, port: this._opts.port });\n return { socket, transport: new NetTransport(socket) };\n }\n const tlsOpts: TlsOptions = tlsOpt === true ? {} : tlsOpt;\n // Resolved and PROVEN acceptable to the runtime BEFORE any socket exists,\n // so a suite list that cannot be honoured rejects connect() with a typed\n // MllpTlsConfigurationError and leaves nothing connected behind. The throw\n // lands in connect()'s promise executor (a rejection) and in\n // _beginReconnectAttempt's catch (a permanent classification, MLLP_* code).\n const cipherPolicy = resolveTlsCipherPolicy(tlsOpts, \"client\");\n const socket = tlsConnect({\n host: this._opts.host,\n port: this._opts.port,\n servername: tlsOpts.servername ?? this._opts.host,\n ...(tlsOpts.ca !== undefined ? { ca: tlsOpts.ca } : {}),\n ...(tlsOpts.cert !== undefined ? { cert: tlsOpts.cert } : {}),\n ...(tlsOpts.key !== undefined ? { key: tlsOpts.key } : {}),\n ...(tlsOpts.passphrase !== undefined ? { passphrase: tlsOpts.passphrase } : {}),\n minVersion: tlsOpts.minVersion ?? \"TLSv1.2\",\n ...(tlsOpts.maxVersion !== undefined ? { maxVersion: tlsOpts.maxVersion } : {}),\n ...cipherPolicy,\n rejectUnauthorized: tlsOpts.allowUnverified !== true,\n });\n return { socket, transport: new TlsTransport(socket) };\n }\n\n /**\n * Wrap a connect-phase socket error as `MllpConnectionError`, classifying\n * TLS failures into the additive `connectionCause`:\n *\n * - `'tls-verify'`, certificate-verification failures\n * ({@link isTlsVerificationErrorCode}).\n * - `'tls-handshake'`, TLS-**protocol**-shaped failures only\n * ({@link isTlsProtocolError}): `ERR_SSL_*`, `EPROTO`, OpenSSL\n * alert-bearing errors.\n * - **No `connectionCause`**, pure TCP-level failures (`ECONNREFUSED`,\n * `ETIMEDOUT`, …) even on a TLS-configured connection; these carry the\n * same shape as plaintext connect failures.\n */\n private _wrapConnectError(err: Error, phase: \"connect\" | \"reconnect\"): MllpConnectionError {\n if (this._opts.tls === undefined) {\n return new MllpConnectionError(err.message, { cause: err, phase });\n }\n const code = (err as NodeJS.ErrnoException).code;\n if (typeof code === \"string\" && isTlsVerificationErrorCode(code)) {\n return new MllpConnectionError(err.message, {\n cause: err,\n phase,\n connectionCause: \"tls-verify\",\n });\n }\n if (isTlsProtocolError(err)) {\n return new MllpConnectionError(err.message, {\n cause: err,\n phase,\n connectionCause: \"tls-handshake\",\n });\n }\n // Pure TCP-level failure on a TLS-configured connection, same shape as\n // a plaintext connect failure; no TLS-specific connectionCause.\n return new MllpConnectionError(err.message, { cause: err, phase });\n }\n\n /**\n * Emit a client event, containing a throwing subscriber.\n *\n * Every event this client emits is reached from a callback we do not own, a socket's\n * `'connect'`/`'secureConnect'`/`'data'`/`'error'` listener, or a backoff timer. A throwing\n * subscriber would unwind into it and kill the process, and on several paths it would also skip\n * the work queued after the emit (the `resolve()` of `connect()`, the backoff scheduling).\n *\n * See `src/internal/safe-emit.ts`. The throw is re-surfaced on `'error'`.\n */\n private _emitContained(event: string, payload: unknown): void {\n safeEmit(this, event, payload, (err) => {\n safeEmitError(this, Object.freeze({ error: err }));\n });\n }\n\n /**\n * Emit a frozen {@link AckModeWarning} on the `'warning'` event.\n *\n * The message is a **registry lookup**, not an interpolation, and everything\n * input-derived on the payload is a number or a closed-set code. This holds in every\n * mode: an original-mode send emits nothing new, but when it does reach one of these\n * codes the same discipline applies.\n */\n private _emitAckModeWarning(\n code: AckModeCode,\n ctx: AckModeWarningContext,\n conn?: Connection,\n ): void {\n const warning: AckModeWarning = Object.freeze({\n code,\n message: ackModeDiagnosticMessage(code),\n byteOffset: ctx.byteOffset,\n msa1Bytes: ctx.msa1Bytes,\n ackCode: ctx.ackCode,\n controlIdBytes: ctx.controlIdBytes,\n elapsedSinceSendMs: ctx.elapsedSinceSendMs,\n connectionId: this._connection?.connectionId ?? conn?.connectionId,\n timestamp: new Date(),\n });\n this._emitContained(\"warning\", warning);\n }\n\n /**\n * Build the typed error for a send that was left waiting on its application\n * acknowledgement: the peer committed the message and then either fell silent past the\n * second window or lost the link.\n */\n private _applicationAckError(\n entry: PendingAck,\n reason: \"timeout\" | \"connection-lost\",\n elapsedMs: number,\n detail: string,\n ): MllpApplicationAckError {\n const commitReceivedAt = entry.twoPhase?.commitReceivedAt ?? 0;\n return new MllpApplicationAckError(\n `commit accepted (CA); no application acknowledgement ${detail}`,\n {\n reason,\n commitCode: \"CA\",\n messageControlIdBytes: entry.controlId?.length,\n elapsedMs,\n commitReceivedAt,\n },\n );\n }\n\n /**\n * Fail every pending send that is waiting on its application acknowledgement, with an\n * error naming the commit disposition already received. Used on the paths where the link\n * goes away: such a send must not be held for a resend (the peer already has the\n * message), nor left pending, nor reported as successful.\n *\n * Returns the entries it removed. Sends without a reported commit are untouched.\n */\n private _failCommitPendingSends(cause: string): PendingAck[] {\n const corr = this._correlator;\n if (corr === null) return [];\n const stranded: PendingAck[] = [];\n for (const entry of corr.liveEntries()) {\n if ((entry.twoPhase?.commitReceivedAt ?? null) !== null) stranded.push(entry);\n }\n const now = Date.now();\n for (const entry of stranded) {\n corr.dispose(entry.key);\n const commitReceivedAt = entry.twoPhase?.commitReceivedAt ?? now;\n entry.reject(\n this._applicationAckError(entry, \"connection-lost\", now - commitReceivedAt, cause),\n );\n }\n return stranded;\n }\n\n /**\n * Emit the per-connection insecure-TLS warning when\n * `tls.allowUnverified === true`, fires on EVERY successful\n * `secureConnect`, initial connect and every reconnect. Emits both a frozen\n * `'securityWarning'` event and `process.emitWarning`. No-op for plaintext\n * connections or when verification is on.\n */\n private _emitInsecureWarningIfNeeded(): void {\n const tlsOpt = this._opts.tls;\n if (tlsOpt === undefined || tlsOpt === true) return;\n if (tlsOpt.allowUnverified !== true) return;\n const message =\n \"MLLP TLS certificate verification is DISABLED (allowUnverified: true), \" +\n \"this connection does not authenticate the peer.\";\n const warning: SecurityWarning = Object.freeze({\n code: MLLP_TLS_VERIFY_DISABLED,\n message,\n host: this._opts.host,\n port: this._opts.port,\n timestamp: new Date(),\n });\n // Contained: this is reached from the socket's 'secureConnect' listener. A throwing\n // subscriber (a security-audit hook, exactly the kind of thing that subscribes here) would\n // both kill the process AND skip the resolve() that follows in onSocketConnect, hanging\n // connect() forever.\n this._emitContained(\"securityWarning\", warning);\n process.emitWarning(message, { code: MLLP_TLS_VERIFY_DISABLED });\n }\n\n /**\n * Emit the frozen negotiated-parameters record for a completed TLS\n * handshake, once per connection, initial connect and every reconnect\n * alike. No-op for a plaintext connection: there is nothing negotiated to\n * report, so no event is emitted at all.\n *\n * The payload is read off the TLS session, so it is a pair of registry names\n * plus this client's own host and port. It never sees a payload byte: it is\n * built at handshake-completion time, before any HL7 byte crosses the link.\n */\n private _emitNegotiatedTlsParameters(socket: Socket | TLSSocket): void {\n if (this._opts.tls === undefined) return;\n const params = readNegotiatedTlsParameters(socket, this._opts.host, this._opts.port);\n if (params === null) return;\n // Contained: reached from the socket's 'secureConnect' listener, exactly like the insecure\n // warning above. A throwing subscriber (a conformance auditor, the natural consumer of this\n // event) must not kill the process nor skip the resolve() that follows in onSocketConnect.\n this._emitContained(\"tlsNegotiated\", params);\n }\n\n /**\n * Wire a Connection's events through to this MllpClient. Every re-emitted\n * payload is `Object.freeze`'d before emission, even though the\n * Connection layer already freezes, defense-in-depth, harmless on\n * already-frozen objects.\n *\n * Builds the unified `Correlator` bound to this Connection and\n * arms the periodic ACK-timeout sweep. The Correlator is teardown-aware:\n * `close()` / `destroy()` clear the sweep timer and reject pending sends.\n *\n * @param conn - Connection to subscribe to.\n */\n private _attachConnection(conn: Connection): void {\n // Plan 04, preserve correlator state across reconnect cycles. In\n // controlId mode, in-flight sends are re-transmitted on the new\n // connection (D-08 / CLIENT-17), so the correlator must survive the\n // transition. The closures below dereference `this._connection`\n // lazily so they always see the CURRENT connection (not the dead\n // one captured at attach-time).\n if (this._correlator === null) {\n this._correlator = new Correlator({\n mode: this._correlateByControlId ? \"controlId\" : \"fifo\",\n ackTimeoutMs: this._ackTimeoutMs,\n // Plan 05, pipeline:false collapses the in-flight set to ≤1 (D-06).\n maxInFlight: this._pipeline ? Number.POSITIVE_INFINITY : 1,\n onWarning: (code, ctx) => {\n // PLAN-06 (OBS-01, D-26), aggregate Correlator-emitted warning counts.\n this._aggregatedWarningsByCode[code] = (this._aggregatedWarningsByCode[code] ?? 0) + 1;\n // The message is a REGISTRY LOOKUP, not an interpolation. Everything\n // input-derived is a number on its own field. See ack-diagnostics.ts.\n const warning: AckCorrelationWarning = Object.freeze({\n code,\n byteOffset: ctx.byteOffset,\n message: ackDiagnosticMessage(code),\n controlIdBytes: ctx.controlIdBytes,\n elapsedSinceSendMs: ctx.elapsedSinceSendMs,\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n timestamp: new Date(),\n });\n this._emitContained(\"warning\", warning);\n },\n onUnmatchedAck: (controlIdBytes) => {\n // CLIENT-15: unmatched ACK in controlId mode. Emit a frozen\n // MllpFramingError('MLLP_ACK_UNMATCHED_CONTROL_ID') to the 'error'\n // event. listenerCount-guarded so absent listeners don't crash the\n // process (T-05-03-02 mitigation).\n if (this.listenerCount(\"error\") === 0) return;\n const err = new MllpFramingError(\n \"MLLP_ACK_UNMATCHED_CONTROL_ID\",\n 0,\n Buffer.alloc(0),\n ackDiagnosticMessage(\"MLLP_ACK_UNMATCHED_CONTROL_ID\"),\n );\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n error: err,\n // The peer's MSA-2 byte LENGTH. The bytes themselves used to be\n // here and on the message above; a peer sending a one-megabyte\n // MSA-2 produced a one-megabyte Error.message, straight into a log.\n controlIdBytes,\n }),\n );\n },\n onTimeout: (entry, elapsedMs) => {\n // PLAN-06 (OBS-01, D-26), observability counter.\n this._timedOutTotal += 1;\n entry.reject(\n new MllpTimeoutError(`ACK timeout after ${elapsedMs}ms`, {\n messageControlIdBytes: entry.controlId?.length,\n elapsedMs,\n sentAt: entry.sentAt ?? 0,\n }),\n );\n // Plan 05, a timeout removes the entry from the live store too,\n // so emit 'drain' if the queue now sits below both caps. This is\n // critical for pipeline:false (D-06): an expired send must free\n // the in-flight slot so the next send can flush.\n this._maybeEmitDrain();\n },\n onApplicationAckTimeout: (entry, elapsedMs) => {\n this._timedOutTotal += 1;\n entry.reject(\n this._applicationAckError(entry, \"timeout\", elapsedMs, `after ${elapsedMs}ms`),\n );\n this._maybeEmitDrain();\n },\n onCommitRejected: (entry, code) => {\n entry.reject(\n new MllpCommitRejectedError(`peer answered a negative commit (${code})`, {\n commitCode: code,\n messageControlIdBytes: entry.controlId?.length,\n elapsedMs: entry.sentAt === null ? 0 : Date.now() - entry.sentAt,\n }),\n );\n this._maybeEmitDrain();\n },\n onCommitReported: (entry, ack, latencyMs) => {\n const report = entry.twoPhase?.onCommitReport ?? null;\n if (report === null) return;\n // Contained: this is the caller's own per-send hook, reached from the socket's\n // 'data' listener. A throwing hook must not unwind into the transport nor stop\n // the send from settling later on its application acknowledgement.\n try {\n report(Object.freeze({ code: \"CA\", payload: ack, latencyMs }));\n } catch (err) {\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n error: err instanceof Error ? err : new Error(String(err)),\n }),\n );\n }\n },\n onAckModeWarning: (code, ctx) => {\n this._emitAckModeWarning(code, ctx, conn);\n },\n });\n }\n\n // Periodic sweep: smaller of (ackTimeoutMs / 4) and 1000 ms; floor 50 ms.\n // A shorter application-acknowledgement window tightens the same cadence, so the\n // second wait is swept at the resolution it was configured with rather than the first\n // wait's. .unref() so this timer never keeps the process alive.\n if (this._ackSweepTimer === null) {\n const shortestWindowMs = Math.min(\n this._ackTimeoutMs,\n this._applicationAckTimeoutMs ?? this._ackTimeoutMs,\n );\n const sweepIntervalMs = Math.max(50, Math.min(1000, Math.floor(shortestWindowMs / 4)));\n this._ackSweepTimer = setInterval(() => {\n this._correlator?.expireDue();\n }, sweepIntervalMs);\n this._ackSweepTimer.unref();\n }\n\n // Single 'stateChange' listener delegates to _onStateChange (B-04 anchor).\n conn.on(\"stateChange\", (e: StateChangeEvent) => {\n this._onStateChange(e);\n });\n // Single 'message' listener: re-emit + delegate to _onAckPayload (B-04 anchor).\n conn.on(\n \"message\",\n (e: {\n payload: Buffer;\n connectionId: string;\n byteOffset: number;\n warnings: readonly MllpWarning[];\n }) => {\n // Contained: a throwing 'message' subscriber used to abort this handler BEFORE\n // _onAckPayload ran, so the ACK never reached the correlator and send() hung forever.\n // An observer must never be able to break ACK correlation.\n this._emitContained(\"message\", Object.freeze({ ...e }));\n // Plan 05, last-bytes-received signal resets dead-peer timer\n // (D-11 \"last bytes/ACK received\").\n this._armDeadPeerTimer();\n this._onAckPayload(e.payload, e.byteOffset);\n },\n );\n // PLAN-01 lifecycle re-emitters preserved unchanged.\n conn.on(\"connect\", (e: unknown) => {\n this._emitContained(\"connect\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"disconnect\", (e: unknown) => {\n this._emitContained(\"disconnect\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"reconnecting\", (e: unknown) => {\n this._emitContained(\"reconnecting\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"close\", (e: unknown) => {\n this._emitContained(\"close\", Object.freeze({ ...(e as object) }));\n });\n conn.on(\"warning\", (w: MllpWarning) => {\n this._emitContained(\"warning\", w);\n // Plan 05, Connection 'warning' is also a \"bytes received\" signal.\n this._armDeadPeerTimer();\n });\n conn.on(\"error\", (e: unknown) => {\n // Plan 04: capture the underlying Error for `RetryContext.lastError`.\n // The Connection emits frozen `{ connectionId, error: MllpConnectionError }`\n // payloads. We unwrap to the original transport error so the CLIENT-18\n // classifier (which inspects `err.code`) receives the OS-level code.\n const wrapper = e instanceof Error ? e : (e as { error?: unknown })?.error;\n if (wrapper instanceof Error) {\n // If the wrapper has a `.cause` Error (MllpConnectionError pattern),\n // prefer the inner cause for classification.\n const inner = (wrapper as { cause?: unknown }).cause;\n this._lastError = inner instanceof Error ? inner : wrapper;\n }\n // Guards the unlistened case (ERR_UNHANDLED_ERROR would crash the process, T-05-01-03)\n // AND contains a throwing 'error' subscriber. See src/internal/safe-emit.ts.\n safeEmitError(this, e);\n });\n this._connection = conn;\n\n // Plan 05, dead-peer timer self-listener on 'ack'. Connection emits\n // 'message' (already wired above); the MllpClient itself emits 'ack'\n // after matchAck succeeds. Both are \"last bytes/ACK received\" signals\n // (D-11). The 'ack' reset is effectively a no-op when 'message' just\n // armed it, but keeps the contract literal-true.\n if (!this._ackResetWired) {\n this._ackResetWired = true;\n this.on(\"ack\", () => {\n this._armDeadPeerTimer();\n });\n }\n\n // Arm the dead-peer timer if the connection is ALREADY in CONNECTED\n // (test seam path: _attachExistingConnection called after notifyConnect).\n // The state-change branch arms it for the normal path\n // (CONNECTING → CONNECTED transition).\n if (conn.state === \"CONNECTED\") {\n this._armDeadPeerTimer();\n }\n }\n\n /**\n * Arm (or re-arm) the dead-peer idle timer.\n * No-op when `deadPeerTimeoutMs` is unset.\n */\n private _armDeadPeerTimer(): void {\n if (this._opts.deadPeerTimeoutMs === undefined) return;\n if (this._deadPeerTimer !== null) {\n clearTimeout(this._deadPeerTimer);\n }\n this._deadPeerTimer = setTimeout(() => {\n this._connection?.destroy(new Error(\"dead peer timeout\"));\n }, this._opts.deadPeerTimeoutMs);\n this._deadPeerTimer.unref();\n }\n\n /**\n * Clear the dead-peer idle timer on every transition out of CONNECTED.\n */\n private _clearDeadPeerTimer(): void {\n if (this._deadPeerTimer !== null) {\n clearTimeout(this._deadPeerTimer);\n this._deadPeerTimer = null;\n }\n }\n\n /**\n * Disconnect handler, reconnect FSM core.\n *\n * Implements:\n * - Hybrid in-flight handling: controlId mode preserves\n * pending sends for resend; FIFO mode rejects in-flight with\n * `connectionCause: 'in-flight-orphan'` and queued with `'fifo-unsafe'`.\n * - Classifier-first: permanent errors\n * transition straight to CLOSED without invoking `retryStrategy`.\n * - Backoff-reset on recent success: first disconnect after a\n * successful ACK on the prior session resets `_attempt` to 0.\n * - `_reconnectAttempts` counter increment.\n * - Frozen RetryContext; a `null` return from the strategy halts reconnection.\n * - Default exponential strategy.\n *\n * Invoked from the SINGLE `_onStateChange` hook, no parallel listener.\n * Idempotent across same-cycle re-entry: cycle-start flag\n * coordinates first-disconnect vs subsequent-within-cycle behavior.\n */\n private _handleDisconnect(err: Error): void {\n if (this._userClosed) return;\n\n // CLIENT-17 hybrid: handle queued + in-flight sends per mode.\n if (this._correlator !== null) {\n if (this._correlateByControlId) {\n // Hold sends for resend after reconnect, DO NOT clear or reject.\n // The correlator's live store survives the FSM transition; the\n // entries are re-transmitted in `_beginReconnectAttempt` once the\n // new Connection enters CONNECTED.\n //\n // With ONE exception, and it is the whole point of the accept acknowledgement: a\n // send whose commit disposition has already been reported must not be re-sent. The\n // peer has said in writing that it holds the message, so putting it back on the\n // wire is how one clinical message becomes two. Such a send is failed here, with an\n // error naming the commit it did receive.\n this._failCommitPendingSends(\"before the link dropped\");\n } else {\n // FIFO: split between in-flight (sentAt set) and queued (sentAt null).\n // In-flight sends → 'in-flight-orphan'; queued sends → 'fifo-unsafe'.\n const orphans: PendingAck[] = [];\n const queued: PendingAck[] = [];\n for (const entry of this._correlator.liveEntries()) {\n if (entry.sentAt !== null) orphans.push(entry);\n else queued.push(entry);\n }\n for (const o of orphans) {\n o.reject(\n new MllpConnectionError(\"in-flight send orphaned by reconnect\", {\n cause: err,\n phase: \"reconnect\",\n connectionCause: \"in-flight-orphan\",\n }),\n );\n }\n for (const q of queued) {\n q.reject(\n new MllpConnectionError(\"queued send rejected by FIFO reconnect\", {\n cause: err,\n phase: \"reconnect\",\n connectionCause: \"fifo-unsafe\",\n }),\n );\n }\n for (const entry of [...orphans, ...queued]) {\n this._correlator.remove(entry.key);\n }\n }\n }\n\n // CLIENT-18 classification first (Composition A, D-16). Permanent\n // errors transition directly to CLOSED without invoking retryStrategy.\n // Phase 8: on a TLS-configured connection, TLS-protocol-shaped errors\n // (ERR_SSL_*, EPROTO, OpenSSL alert-bearing, see isTlsProtocolError)\n // are ALSO permanent: a clientAuth 'MUST' server that rejects this\n // client's certificate will reject every retry, never reconnect-loop\n // into it. Pure TCP-level errors (ECONNREFUSED, ETIMEDOUT, plain\n // ECONNRESET) stay transient so a network blip still auto-heals.\n const tlsProtocolShaped = this._opts.tls !== undefined && isTlsProtocolError(err);\n const classifiedAs: \"transient\" | \"permanent\" =\n !tlsProtocolShaped && isTransientConnectionError(err) ? \"transient\" : \"permanent\";\n if (classifiedAs === \"permanent\") {\n // Halt: force the dead Connection to CLOSED (terminal); future\n // connect() must be called explicitly.\n this._userClosed = true;\n this._connection?.destroy(err);\n return;\n }\n\n // W-02: bump the global reconnect-attempts counter once per disconnect\n // entering a cycle. PLAN-06 reads this for getStats().reconnectAttempts.\n this._reconnectAttempts += 1;\n\n // W-01: backoff reset on recent success.\n // First disconnect AFTER any successful ACK on the prior session\n // (`_reconnectCycleStartedAt === null` AND `_lastSuccessAt !== null`)\n // resets attempt to 0. Subsequent disconnects within the same cycle\n // do NOT re-reset, the cycle-start flag persists.\n if (this._reconnectCycleStartedAt === null && this._lastSuccessAt !== null) {\n this._attempt = 0;\n }\n if (this._reconnectCycleStartedAt === null) {\n this._reconnectCycleStartedAt = Date.now();\n }\n\n // Build RetryContext (W-07: NEVER_ABORTING_SIGNAL when no caller signal).\n const ctx: RetryContext = Object.freeze({\n attempt: this._attempt,\n lastError: err,\n lastDelayMs: this._lastDelayMs,\n totalElapsedMs: Date.now() - this._reconnectCycleStartedAt,\n sinceLastSuccessMs:\n this._lastSuccessAt !== null ? Date.now() - this._lastSuccessAt : Number.POSITIVE_INFINITY,\n classifiedAs,\n signal: this._connectSignal ?? NEVER_ABORTING_SIGNAL,\n });\n\n // Invoke strategy (T-05-04-05: defensive try/catch, caller-supplied hook).\n let delay: number | null;\n try {\n const strategy = this._retryStrategy ?? this._defaultRetryStrategy;\n delay = strategy(ctx);\n } catch (hookErr) {\n // Strategy threw, bail to CLOSED, surface error.\n this._lastError = hookErr instanceof Error ? hookErr : new Error(String(hookErr));\n {\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? \"<none>\",\n error: this._lastError,\n }),\n );\n }\n this._userClosed = true;\n this._connection?.destroy(this._lastError);\n return;\n }\n\n if (delay === null) {\n // D-17 null-return halt → CLOSED.\n this._userClosed = true;\n this._connection?.destroy(err);\n return;\n }\n\n // Emit 'reconnecting' with populated fields (Phase 3 D-CR-01 promise).\n //\n // Contained: a throwing subscriber here used to skip the backoff scheduling immediately below,\n // so auto-reconnect silently never fired again, the connection just stopped retrying, with no\n // error that named the cause.\n this._emitContained(\n \"reconnecting\",\n Object.freeze({\n connectionId: this._connection?.connectionId ?? \"<none>\",\n attempt: this._attempt,\n delayMs: delay,\n }),\n );\n\n // Schedule next CONNECTING attempt. .unref() so the timer never keeps\n // the process alive (test-suite ergonomics).\n this._lastDelayMs = delay;\n this._backoffTimer = setTimeout(() => {\n this._backoffTimer = null;\n this._attempt += 1;\n this._beginReconnectAttempt();\n }, delay);\n this._backoffTimer.unref();\n }\n\n /**\n * Default retry strategy: `min(maxDelay, initialDelay * multiplier^attempt)`\n * with ±jitter applied.\n */\n private _defaultRetryStrategy = (ctx: RetryContext): number => {\n const base = Math.min(\n this._maxDelayMs,\n this._initialDelayMs * Math.pow(this._multiplier, ctx.attempt),\n );\n const jitterFactor = 1 + (Math.random() * 2 - 1) * this._jitter;\n return Math.max(0, Math.floor(base * jitterFactor));\n };\n\n /**\n * Open a fresh Connection for the next reconnect attempt.\n *\n * In production, builds a new `net.Socket` + `NetTransport` + `Connection`.\n * In tests, the `_reconnectFactory` seam returns a pre-built Connection\n * over `InMemoryTransport.pair()`.\n *\n * On successful CONNECTED transition:\n * - controlId mode: re-transmits every live correlator entry via the\n * already-encoded `PendingAck.frame`, then `markFlushed`'s each one\n * so ACK timeouts restart from the new flush time.\n * - All modes: clears the cycle-start flag so the next disconnect\n * can re-enter `_handleDisconnect` cleanly.\n */\n private _beginReconnectAttempt(): void {\n if (this._userClosed) return;\n try {\n let conn: Connection;\n let arm: () => void;\n if (this._reconnectFactory !== null) {\n ({ conn, arm } = this._reconnectFactory());\n } else {\n const { socket, transport } = this._createSocketAndTransport();\n // Plan 05, TCP keepalive on every reconnect attempt too\n // (CLIENT-08, D-11/A3, mirror connect() site).\n if (this._opts.keepaliveIntervalMs !== undefined) {\n socket.setKeepAlive(true, this._opts.keepaliveIntervalMs);\n }\n this._socket = socket;\n const connOpts =\n this._opts.framing !== undefined\n ? { transport, framing: this._opts.framing }\n : { transport };\n if (this._opts.drainTimeoutMs !== undefined) {\n (connOpts as { drainTimeoutMs?: number }).drainTimeoutMs = this._opts.drainTimeoutMs;\n }\n conn = new Connection(connOpts);\n arm = (): void => {\n conn.notifyConnect(socket.remoteAddress ?? null, socket.remotePort ?? null);\n this._emitInsecureWarningIfNeeded();\n this._emitNegotiatedTlsParameters(socket);\n };\n const connectEventName = this._opts.tls !== undefined ? \"secureConnect\" : \"connect\";\n socket.once(\"error\", (sErr: Error) => {\n // Phase 8, the raw OS/TLS error (with its original `.code`) is\n // what the reconnect classifier inspects in _handleDisconnect;\n // keep it unwrapped here so TLS cert-verification codes (CERT_*,\n // UNABLE_TO_VERIFY_LEAF_SIGNATURE, …) and TLS-protocol-shaped\n // codes (ERR_SSL_*, EPROTO with an SSL alert) are still visible\n // to the classifier and correctly permanent (never reconnect-loop\n // into a misconfigured or MITM'd endpoint).\n if (this._lastError === null || this._lastError.message !== sErr.message) {\n this._lastError = sErr;\n }\n });\n socket.once(connectEventName, () => {\n // TLS and plaintext arm identically and immediately, see the\n // matching note in connect()'s onSocketConnect.\n arm();\n this._afterReconnectArmed();\n });\n // Replace prior Connection. Drop the dead reference; new Connection\n // is wired below.\n this._connection = null;\n this._attachConnection(conn);\n return;\n }\n // Test-seam path\n this._connection = null;\n this._attachConnection(conn);\n arm();\n this._afterReconnectArmed();\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err));\n this._handleDisconnect(this._lastError);\n }\n }\n\n /**\n * Post-CONNECTED steps for a reconnect attempt:\n * - controlId mode: resend every live correlator entry's frame, then\n * re-stamp `markFlushed` so ACK timers reset from the new flush.\n * - Clear the cycle-start flag so the next disconnect re-enters cleanly.\n */\n private _afterReconnectArmed(): void {\n if (this._correlateByControlId && this._correlator !== null && this._connection !== null) {\n const conn = this._connection;\n const corr = this._correlator;\n const entries = [...corr.liveEntries()];\n for (const entry of entries) conn.send(entry.frame);\n const now = Date.now();\n for (const entry of entries) corr.markFlushed(entry.key, now);\n }\n this._reconnectCycleStartedAt = null;\n }\n\n /**\n * Test seam, install a factory that produces the next reconnect Connection.\n *\n * @internal\n */\n _setReconnectFactory(factory: () => { conn: Connection; arm: () => void }): void {\n this._reconnectFactory = factory;\n }\n\n /**\n * Test seam, capture or rebind the connect-signal mid-flight.\n *\n * @internal\n */\n _captureConnectSignal(signal: AbortSignal): void {\n this._connectSignal = signal;\n if (this._abortListener !== null) {\n this._abortListener.signal.removeEventListener(\"abort\", this._abortListener.handler);\n this._abortListener = null;\n }\n const handler = (): void => {\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n this._connection?.destroy(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", handler, { once: true });\n this._abortListener = { signal, handler };\n }\n\n /**\n * Single source-of-truth for inbound ACK payload handling.\n *\n * - FIFO mode: passes `null` controlId so `matchAck` returns the head of\n * the live store.\n * - controlId mode: extracts MSA-2 and passes it to `matchAck` for keyed\n * lookup.\n *\n * Called from the SINGLE `'message'` listener registered in `_attachConnection`.\n * No parallel listener is registered.\n */\n private _onAckPayload(ackPayload: Buffer, byteOffset: number): void {\n if (this._correlator === null) return;\n // HOOK_EXTENSION_POINT: ack-payload\n const ackControlId: string | null = this._correlateByControlId\n ? extractMsaControlId(ackPayload)\n : null;\n const matched = this._correlator.matchAck(ackPayload, ackControlId, byteOffset);\n if (matched !== null) {\n this._onAckMatched(matched, ackPayload);\n }\n }\n\n /**\n * Single source-of-truth for a successfully matched ACK (live-store hit).\n *\n * Emits the frozen 'ack' event and resolves the pending send. Updates\n * `_lastSuccessAt`, `_ackedTotal` and `_lastAckAt`.\n *\n * Called from _onAckPayload when matchAck() returns a non-null PendingAck.\n */\n private _onAckMatched(matched: PendingAck, ackPayload: Buffer): void {\n const latencyMs = matched.sentAt !== null ? Date.now() - matched.sentAt : 0;\n this._emitContained(\n \"ack\",\n Object.freeze({\n payload: ackPayload,\n controlId: matched.controlId,\n latencyMs,\n }),\n );\n // HOOK_EXTENSION_POINT: ack-matched\n // Plan 04, backoff-reset signal (W-01): record the most recent successful\n // ACK so the next disconnect resets attempt counter to 0 if it's the\n // first disconnect AFTER a successful exchange on the prior session.\n this._lastSuccessAt = Date.now();\n // PLAN-06 (OBS-01, D-26), observability counters.\n this._ackedTotal += 1;\n this._lastAckAt = Date.now();\n matched.resolve(ackPayload);\n // Plan 05, emit 'drain' when queue depth crosses below high-water mark\n // (D-24). Fires once per ACK that brings the queue under both caps.\n this._maybeEmitDrain();\n }\n\n /**\n * Emit a frozen `'drain'` event when the queue depth and bytes fall below\n * both configured caps. Called from `_onAckMatched`\n * (every successful ACK) and from the Correlator's `onTimeout` callback\n * (every expired send), both code paths free a live-store slot.\n */\n private _maybeEmitDrain(): void {\n // Every path that settles a pending send comes through here, which makes this the one\n // place a shutdown drain has to be re-checked: it ends as soon as the last acknowledgement\n // it was waiting for lands, rather than sitting out the rest of the timeout.\n this._drainWaiter?.();\n const corr = this._correlator;\n if (corr === null) return;\n const belowCount = corr.size < this._hwmCount;\n const belowBytes = corr.queueBytes < this._hwmBytes;\n if (belowCount && belowBytes) {\n this._emitContained(\n \"drain\",\n Object.freeze({\n queueDepth: corr.size,\n queueBytes: corr.queueBytes,\n }),\n );\n }\n }\n\n /**\n * Single source-of-truth for Connection FSM transitions.\n *\n * Re-emits a frozen 'stateChange' event, detects\n * CONNECTED → DISCONNECTED|RECONNECTING to trigger `_handleDisconnect`, and\n * clears or arms the dead-peer timer on transitions out of / into CONNECTED.\n *\n * Called from the SINGLE 'stateChange' listener registered in _attachConnection.\n */\n private _onStateChange(e: StateChangeEvent): void {\n // Contained: everything below (dead-peer timer, the _handleDisconnect reconnect trigger) must\n // run even if a subscriber throws. A throwing 'stateChange' tap used to silently disable\n // auto-reconnect.\n this._emitContained(\"stateChange\", Object.freeze({ ...e }));\n // HOOK_EXTENSION_POINT: state-change\n // A shutdown drain waiting on acknowledgements ends the moment the connection leaves\n // DRAINING: the link failed, the peer went away, or the client was destroyed. Nothing\n // further can be acknowledged over a link that is gone, so close() returns and reports\n // what is left rather than hanging until the drain timeout.\n if (e.to !== \"DRAINING\") this._endDrainWait?.();\n // Plan 05, dead-peer timer arm/clear (D-14). Cleared on every\n // transition OUT of CONNECTED; re-armed on entry TO CONNECTED.\n if (e.to === \"CONNECTED\") {\n this._armDeadPeerTimer();\n // PLAN-06 (OBS-01, D-26), record CONNECTED epoch for getStats.\n this._lastConnectedAt = Date.now();\n }\n if (e.from === \"CONNECTED\" && e.to !== \"CONNECTED\") {\n this._clearDeadPeerTimer();\n }\n // Plan 04, disconnect detection (CLIENT-05/06/17). Trigger\n // `_handleDisconnect` on transitions out of CONNECTED into a\n // disconnect-leaning state, OR on a CONNECTING/RECONNECTING attempt\n // failing into CLOSED while we are inside a reconnect cycle (so the\n // cycle continues incrementing `_attempt`). The cycle-start flag plus\n // `_userClosed` guard against re-entry.\n const isPostConnectedDrop =\n e.from === \"CONNECTED\" &&\n (e.to === \"DISCONNECTED\" || e.to === \"RECONNECTING\" || e.to === \"CLOSED\");\n const isReconnectAttemptFailure =\n this._reconnectCycleStartedAt !== null &&\n (e.from === \"CONNECTING\" || e.from === \"RECONNECTING\") &&\n (e.to === \"CLOSED\" || e.to === \"DISCONNECTED\");\n if (isPostConnectedDrop || isReconnectAttemptFailure) {\n const cause = this._lastError ?? new Error(e.reason ?? \"disconnect\");\n if (this._userClosed) return;\n if (!this._autoReconnect) {\n // Reject pending sends, same teardown path as close() but with a\n // disconnect-flavored MllpConnectionError so callers see the cause.\n this._teardownCorrelator(\n new MllpConnectionError(\"disconnected; autoReconnect disabled\", {\n cause,\n phase: \"send\",\n }),\n );\n return;\n }\n this._handleDisconnect(cause);\n }\n }\n\n /**\n * Send an MLLP-framed payload and await the inbound ACK.\n *\n * Resolves with the ACK Buffer (framing stripped). Rejects with:\n * - `DOMException('Aborted', 'AbortError')` if `signal` aborts before the ACK.\n * - `MllpTimeoutError` if no ACK arrives within `ackTimeoutMs`.\n * The clock starts at the underlying `write()` flush callback, NOT at\n * the `send()` call.\n * - `MllpConnectionError({ phase: 'send' })` if the client is not connected.\n *\n * Emits a frozen `'ack'` event on every successful match.\n *\n * ## Enhanced acknowledgement mode\n *\n * A message whose MSH-15 or MSH-16 is not null asks for the two-part protocol of HL7\n * v2.5.1 §2.9, and on a client correlating by control ID this method delivers it: the\n * accept acknowledgement (`CA`) is reported through `onCommitAck` **without** settling\n * the send, and the later application acknowledgement (`AA`/`AE`/`AR`) is what resolves\n * it. All three application-mode codes resolve, because which of them the receiving\n * application chose is its clinical verdict and not this transport's to judge; read MSA-1\n * off the acknowledgement you are handed. A negative commit (`CE`/`CR`) rejects with\n * {@link MllpCommitRejectedError} at once, since no application acknowledgement follows a\n * refusal to take custody.\n *\n * A message with both fields empty is an original-mode send and behaves exactly as it\n * always has, one acknowledgement, settled by the first match.\n *\n * @example\n * ```typescript\n * const ack = await client.send(payloadBuffer);\n * logger.info({ ack: ack.toString('utf8') });\n * ```\n *\n * @param payload Raw bytes; MLLP framing is added internally via `encodeFrame`.\n * @param opts.signal AbortSignal, aborting cancels the ACK wait.\n * @param opts.ackTimeoutMs Per-send override of the backpressure wait budget.\n * @param opts.applicationAckTimeoutMs Per-send override of the second wait's bound.\n * @param opts.onCommitAck Called with the commit disposition when the peer commits the\n * message ahead of its application acknowledgement. See {@link CommitAckReport}.\n */\n send(\n payload: Buffer,\n opts?: {\n signal?: AbortSignal;\n ackTimeoutMs?: number;\n applicationAckTimeoutMs?: number;\n onCommitAck?: (report: CommitAckReport) => void;\n },\n ): Promise<Buffer> {\n const signal = opts?.signal;\n if (signal?.aborted === true) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n if (\n this._connection === null ||\n this._correlator === null ||\n this._connection.state !== \"CONNECTED\"\n ) {\n return Promise.reject(\n new MllpConnectionError(\"send before connect\", {\n cause: new Error(`client state is ${this.state}`),\n phase: \"send\",\n }),\n );\n }\n // One header scan feeds both reads: the correlation key the peer will echo back as\n // MSA-2, and what this message asks its peer for. Both are derived BEFORE the message\n // reaches the transport, so nothing about a send is decided after it is on the wire.\n const msh = readMshSegment(payload);\n // controlId mode: extract MSH-10 BEFORE enqueue so the live-store key\n // is the same string the peer will echo back as MSA-2.\n const controlId: string | null = this._correlateByControlId ? controlIdFromMsh(msh) : null;\n const ackMode = classifyOutboundAckMode(msh);\n const twoPhase = this._twoPhaseStateFor(ackMode, opts);\n const correlator = this._correlator;\n const conn = this._connection;\n // Frame once at enqueue time, same bytes go to the wire AND get held\n // for Plan 04 reconnect-resend (D-08 / CLIENT-17 controlId branch).\n const frame = encodeFrame(payload);\n\n // Plan 05, backpressure gate (CLIENT-07, D-23). Runs BEFORE enqueue\n // so a rejected send never touches the live store. The gate measures\n // the current `correlator.size` + `queueBytes` against the configured\n // high-water mark and applies the configured policy.\n const newQueueDepth = correlator.size + 1;\n const newQueueBytes = correlator.queueBytes + frame.length;\n const overCount = newQueueDepth > this._hwmCount;\n const overBytes = newQueueBytes > this._hwmBytes;\n if (overCount || overBytes) {\n const hwmDesc: { count?: number; bytes?: number } = {};\n if (this._hwmCount !== Number.POSITIVE_INFINITY) {\n hwmDesc.count = this._hwmCount;\n }\n if (this._hwmBytes !== Number.POSITIVE_INFINITY) {\n hwmDesc.bytes = this._hwmBytes;\n }\n if (this._onBackpressure === \"reject\") {\n return Promise.reject(\n new MllpBackpressureError(\n `queue at high-water mark (depth=${correlator.size}, bytes=${correlator.queueBytes})`,\n {\n queueDepth: correlator.size,\n queueBytes: correlator.queueBytes,\n highWaterMark: hwmDesc,\n },\n ),\n );\n }\n // 'wait' mode (CLIENT-07/CLIENT-11): defer until 'drain' fires OR\n // ackTimeoutMs elapses OR the caller's signal aborts (B-06).\n return this._waitThenSend(\n payload,\n { byteCount: frame.length, messageControlIdBytes: controlId?.length },\n opts,\n );\n }\n\n return new Promise<Buffer>((resolve, reject) => {\n let abortListener: (() => void) | null = null;\n const wrappedResolve = (ack: Buffer): void => {\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n resolve(ack);\n };\n const wrappedReject = (err: Error): void => {\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n reject(err);\n };\n const key = correlator.enqueue(frame, controlId, wrappedResolve, wrappedReject, twoPhase);\n if (key === null) {\n // pipeline:false (Plan 05, D-06). Correlator's maxInFlight=1 is\n // saturated. Wait for the next 'drain' event (the prior ACK\n // releases the slot) and then re-enter `send()`, the high-water\n // mark gate above has already approved this send.\n //\n // Nothing is on the wire for this send while it waits here, so it is registered as a\n // parked send: a shutdown must fail it with the never-delivered report rather than\n // leave its promise pending forever, which is what an unregistered park did.\n const parked: ParkedSend = { cancel: (): void => undefined };\n const neverDelivered = (): MllpNeverDeliveredError =>\n new MllpNeverDeliveredError({\n byteCount: frame.length,\n messageControlIdBytes: controlId?.length,\n });\n const onDrain = (): void => {\n this._parkedSends.delete(parked);\n this.off(\"drain\", onDrain);\n // The settlement that ENDS a shutdown drain emits 'drain' on its way out, so this\n // listener can run while the client is already going down. Re-entering send() then\n // would trade the truthful report for the generic not-connected one, for a message\n // whose bytes were never written. See `_shutdownBegun`.\n if (this._shutdownBegun()) {\n wrappedReject(neverDelivered());\n return;\n }\n this.send(payload, opts).then(wrappedResolve, wrappedReject);\n };\n parked.cancel = (): void => {\n this.off(\"drain\", onDrain);\n wrappedReject(neverDelivered());\n };\n this._parkedSends.add(parked);\n this.on(\"drain\", onDrain);\n if (signal !== undefined) {\n abortListener = (): void => {\n this._parkedSends.delete(parked);\n this.off(\"drain\", onDrain);\n wrappedReject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n return;\n }\n if (signal !== undefined) {\n abortListener = (): void => {\n correlator.remove(key);\n // An abort takes an entry out of the live store without going through the settlement\n // path, so a shutdown drain waiting on that entry is re-checked here rather than\n // left to wait out a timeout for an acknowledgement nobody wants any more.\n this._drainWaiter?.();\n wrappedReject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n // Connection.send returns boolean; `false` indicates socket-level\n // backpressure (the OS still buffers the bytes). The application-level\n // high-water mark is what the gate above enforces.\n conn.send(frame);\n correlator.markFlushed(key, Date.now());\n // PLAN-06 (OBS-01, D-26), count flushed sends. Synchronous post-send\n // increment per T-05-06-05 (counter race is bounded; observability is\n // \"good enough\" per D-26).\n this._sentTotal += 1;\n });\n }\n\n /**\n * Decide what a send's own MSH-15 and MSH-16 buy it, and report anything worth reporting\n * about them. Runs before the message reaches the transport.\n *\n * Three answers:\n *\n * * an **original-mode** message (both fields null, or a header this package cannot\n * scan) gets `null`, which is the state every send had before enhanced mode existed;\n * * an **enhanced-mode** message on a client that correlates in order rather than by\n * control ID also gets `null`, plus a warning saying so. MSA-2 is the only thing that\n * can attribute a second acknowledgement to a send, so without control-ID correlation\n * a two-acknowledgement conversation cannot be delivered. The alternative, leaving\n * such a send pending on an acknowledgement this client would otherwise have settled\n * it with, would break a working interface to signal a configuration gap;\n * * an **enhanced-mode** message on a control-ID client gets its two-phase state.\n *\n * An unrecognised Table 0155 value is warned and defaulted, never fatal: this is a\n * transport, and refusing to transmit a clinical message over a vendor value in MSH-15\n * would be HL7 content validation with the worst failure mode available. The message goes\n * out unaltered either way.\n */\n private _twoPhaseStateFor(\n ackMode: OutboundAckMode,\n opts?: {\n ackTimeoutMs?: number;\n applicationAckTimeoutMs?: number;\n onCommitAck?: (report: CommitAckReport) => void;\n },\n ): TwoPhaseState | null {\n if (!ackMode.enhanced) return null;\n if (ackMode.acceptFieldUnrecognised) {\n this._emitAckModeWarning(\"MLLP_ACK_ACCEPT_TYPE_UNRECOGNISED\", OUTBOUND_WARNING_CONTEXT);\n }\n if (ackMode.applicationFieldUnrecognised) {\n this._emitAckModeWarning(\"MLLP_ACK_APPLICATION_TYPE_UNRECOGNISED\", OUTBOUND_WARNING_CONTEXT);\n }\n if (!this._correlateByControlId) {\n this._emitAckModeWarning(\"MLLP_ACK_TWO_PHASE_UNAVAILABLE\", OUTBOUND_WARNING_CONTEXT);\n return null;\n }\n return {\n awaitsApplicationAck: ackMode.awaitsApplicationAck,\n applicationAckTimeoutMs:\n opts?.applicationAckTimeoutMs ??\n this._applicationAckTimeoutMs ??\n opts?.ackTimeoutMs ??\n this._ackTimeoutMs,\n onCommitReport: opts?.onCommitAck ?? null,\n commitCode: null,\n commitReceivedAt: null,\n };\n }\n\n /**\n * 'wait'-mode backpressure handler.\n *\n * Awaits one of four terminating signals, in order:\n * - `'drain'` event → re-enter `send()` (the gate will now pass).\n * - `ackTimeoutMs` elapses → reject with `MllpTimeoutError`.\n * - Caller's `signal` aborts → reject with `AbortError`. Cleanup\n * removes the drain listener AND the abort listener AND clears the\n * timer to prevent leaks.\n * - the client shuts down → reject with `MllpNeverDeliveredError`, because a send waiting\n * here has written nothing and the peer cannot have seen it. Registering the wait as a\n * {@link ParkedSend} is what makes that reachable at all.\n *\n * @param held - What this send would have written, for the never-delivered report. Counts\n * only: the caller still holds the payload it passed to `send()`.\n */\n private _waitThenSend(\n payload: Buffer,\n held: { byteCount: number; messageControlIdBytes: number | undefined },\n opts?: {\n signal?: AbortSignal;\n ackTimeoutMs?: number;\n applicationAckTimeoutMs?: number;\n onCommitAck?: (report: CommitAckReport) => void;\n },\n ): Promise<Buffer> {\n const signal = opts?.signal;\n return new Promise<Buffer>((resolve, reject) => {\n const ackTimeoutMs = opts?.ackTimeoutMs ?? this._ackTimeoutMs;\n let abortListener: (() => void) | null = null;\n const parked: ParkedSend = { cancel: (): void => undefined };\n const cleanup = (): void => {\n this._parkedSends.delete(parked);\n this.off(\"drain\", onDrain);\n clearTimeout(timer);\n if (signal !== undefined && abortListener !== null) {\n signal.removeEventListener(\"abort\", abortListener);\n }\n };\n const onDrain = (): void => {\n cleanup();\n // The settlement that ENDS a shutdown drain emits 'drain' on its way out, so this\n // listener can run while the client is already going down, and nothing was ever\n // written for this send. Same rule as the in-flight-slot park; see `_shutdownBegun`.\n if (this._shutdownBegun()) {\n reject(new MllpNeverDeliveredError(held));\n return;\n }\n // Re-enter send(): the gate will now pass because the queue\n // shrank. Forward both branches to our promise.\n this.send(payload, opts).then(resolve, reject);\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(\n new MllpTimeoutError(`waiting for drain timed out after ${ackTimeoutMs}ms`, {\n messageControlIdBytes: undefined,\n elapsedMs: ackTimeoutMs,\n sentAt: Date.now(),\n }),\n );\n }, ackTimeoutMs);\n timer.unref();\n parked.cancel = (): void => {\n cleanup();\n reject(new MllpNeverDeliveredError(held));\n };\n this._parkedSends.add(parked);\n this.on(\"drain\", onDrain);\n if (signal !== undefined) {\n // B-06: 'wait' mode MUST honor signal abort mid-wait. Cleanup\n // removes the drain listener so listenerCount('drain') returns to\n // its pre-send baseline, the timer is cleared, and the abort\n // listener removes itself.\n abortListener = (): void => {\n cleanup();\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n });\n }\n\n /**\n * Tear down per-connection state (sweep timer + correlator) when the\n * client is closing or destroying. Safe to call multiple times.\n *\n * A send that is waiting on its application acknowledgement is failed with an error\n * naming the commit disposition it did receive, rather than with the generic\n * connection error every other pending send gets. The two are genuinely different\n * outcomes: one send may never have reached the peer, the other is known to be in its\n * custody with only its application disposition unknown. That branch runs FIRST and is not\n * overridable by the caller, because the peer's custody of those bytes is a known fact and\n * no shutdown report may downgrade it to an unknown one.\n *\n * @param reason - Error for every entry the caller offers no better answer for.\n * @param unresolvedFor - Per-entry error for an entry whose commit disposition has NOT\n * arrived. This is where the shutdown path splits the two populations; omitted, every such\n * entry gets `reason`.\n */\n private _teardownCorrelator(reason: Error, unresolvedFor?: (entry: PendingAck) => Error): void {\n if (this._ackSweepTimer !== null) {\n clearInterval(this._ackSweepTimer);\n this._ackSweepTimer = null;\n }\n if (this._correlator !== null) {\n this._correlator.clear(reason, (entry, fallback) => {\n const commitReceivedAt = entry.twoPhase?.commitReceivedAt ?? null;\n if (commitReceivedAt !== null) {\n return this._applicationAckError(\n entry,\n \"connection-lost\",\n Date.now() - commitReceivedAt,\n \"before the connection closed\",\n );\n }\n return unresolvedFor === undefined ? fallback : unresolvedFor(entry);\n });\n // Drop the reference so subsequent send() calls reject via the\n // _connection / _correlator null check.\n this._correlator = null;\n }\n // Plan 05, dead-peer timer cleanup. Belt-and-suspenders for the\n // destroy() path that may bypass an explicit FSM transition.\n this._clearDeadPeerTimer();\n }\n\n /**\n * Number of pending sends whose bytes ARE on the transport and whose acknowledgement has not\n * arrived. This is what a shutdown drain waits for, and nothing else: a send still held\n * inside the client cannot be acknowledged, so holding the drain open for one would spend a\n * clinical shutdown's timeout waiting for an answer that cannot come.\n */\n private _outstandingAckCount(): number {\n return this._correlator?.getStats().inFlight ?? 0;\n }\n\n /**\n * The report a still-pending send gets when the client has finished shutting down, for the\n * one population `_teardownCorrelator` leaves to this method: entries whose commit\n * disposition never arrived.\n *\n * The split is the whole point of the shutdown contract. `sentAt` is stamped at the\n * transport write and is `null` until then, so it says exactly which side of the wire a\n * message was on when the client stopped:\n *\n * * written, unanswered: {@link MllpUnknownFateError}, ambiguous, and reported as\n * ambiguous, carrying the flush timestamp a replay decision reasons about;\n * * never written: {@link MllpNeverDeliveredError}, and a resend of one of those cannot\n * duplicate anything.\n *\n * **The unflushed branch is a structural guard, and today no caller can reach it.** `send()`\n * writes and stamps in the same synchronous run, so an entry that is in the live store is an\n * entry that went out, and the never-written population arrives instead as a\n * {@link ParkedSend}, which never enters the store at all. The branch stays because the\n * store MODELS the state, `sentAt` being nullable is its own claim about it, and the\n * reconnect path already splits the same two ways on the same field. Deleting it would make\n * this report say the opposite of the truth the day anything does produce one, which on this\n * surface means telling a consumer to treat an unwritten message as possibly committed.\n */\n private _shutdownErrorFor(entry: PendingAck): Error {\n const flushedAt = entry.sentAt;\n /* c8 ignore next 6 -- structural guard, unreachable today; see the note above */\n if (flushedAt === null) {\n return new MllpNeverDeliveredError({\n byteCount: entry.byteCount,\n messageControlIdBytes: entry.controlId?.length,\n });\n }\n return new MllpUnknownFateError({\n flushedAt,\n elapsedMs: Date.now() - flushedAt,\n byteCount: entry.byteCount,\n messageControlIdBytes: entry.controlId?.length,\n });\n }\n\n /**\n * Has this client begun shutting down? A send parked inside it must not be resumed once it\n * has: nothing was ever written for such a send, so its report is that delivery did not\n * occur, and re-entering `send()` would trade that report for the generic not-connected one.\n *\n * True from the moment `close()` or `destroy()` is called, true on every halt that has\n * already given up on the connection (a permanent connect failure, an exhausted retry\n * policy, an aborted connect), and true while the underlying connection is draining, which\n * is how a connection closed from outside this client is caught. Each of those is a state a\n * fresh `send()` cannot succeed from.\n *\n * Read on the `'drain'` listener of every {@link ParkedSend}, because a shutdown drain ends\n * when the last outstanding acknowledgement (or timeout, or abort) settles, and that same\n * settlement emits `'drain'`. A park woken by it unregisters itself, so the settle pass that\n * runs after the drain no longer sees it: the choice has to be made here or not at all.\n */\n private _shutdownBegun(): boolean {\n return this._userClosed || this._connection?.state === \"DRAINING\";\n }\n\n /**\n * Fail every send parked inside the client. See {@link ParkedSend}.\n *\n * Iterates a copy and clears the register first, because each `cancel()` removes its own\n * entry as it unwinds.\n */\n private _cancelParkedSends(): void {\n if (this._parkedSends.size === 0) return;\n const parked = [...this._parkedSends];\n this._parkedSends.clear();\n for (const send of parked) send.cancel();\n }\n\n /**\n * Settle everything still unresolved once the shutdown drain is over, under the\n * two-population rule. Runs on every exit from `close()`, including the aborted one, so no\n * send's promise is left pending by a shutdown.\n */\n private _settleUnresolvedSends(): void {\n this._endDrainWait?.();\n this._cancelParkedSends();\n this._teardownCorrelator(\n new MllpConnectionError(\"client closed\", {\n cause: new Error(\"closed\"),\n phase: \"close\",\n }),\n (entry) => this._shutdownErrorFor(entry),\n );\n }\n\n /**\n * Install this client's acknowledgement drain on a Connection's `beforeClose` seam, which is\n * what gives `drainTimeoutMs` a meaning on the client at all.\n *\n * Whatever hook is already there is **composed with**, not replaced: the seam is a public\n * instance property an owner may legitimately have assigned, and a shutdown that silently\n * dropped that work would be a worse defect than the one this fixes. Installing is\n * idempotent by hook identity, so a second `close()` composes nothing twice.\n */\n private _installDrainHook(conn: Connection): void {\n if (conn.beforeClose === this._drainHook) return;\n const inherited = conn.beforeClose;\n const hook = (drainTimeoutMs: number): Promise<void> =>\n Promise.all([inherited.call(conn, drainTimeoutMs), this._awaitOutstandingAcks()]).then(\n () => undefined,\n );\n this._drainHook = hook;\n conn.beforeClose = hook;\n }\n\n /**\n * Resolve once there is nothing left to wait for. Three ways that happens:\n *\n * * the last outstanding acknowledgement lands (or its send is otherwise settled), which\n * is what lets `close()` return in a fraction of the drain timeout;\n * * the connection leaves `DRAINING`, i.e. the link failed, the peer went away or the\n * client was destroyed. Nothing further can be acknowledged over a link that is gone, so\n * waiting out the rest of the timeout would buy nothing and hang the shutdown. That one\n * arrives through `_onStateChange`, the single delegating listener, rather than through a\n * second `'stateChange'` registration of its own;\n * * never, in which case `Connection.close` times the whole thing out at `drainTimeoutMs`\n * and the still-unanswered sends are reported with their fate unknown. The bound lives\n * there, in the drain race, rather than being duplicated here.\n */\n private _awaitOutstandingAcks(): Promise<void> {\n return new Promise<void>((resolve) => {\n let done = false;\n const finish = (): void => {\n if (done) return;\n done = true;\n this._drainWaiter = null;\n this._endDrainWait = null;\n resolve();\n };\n const recheck = (): void => {\n if (this._outstandingAckCount() === 0) finish();\n };\n this._drainWaiter = recheck;\n this._endDrainWait = finish;\n // Nothing outstanding is the common case: a caller that awaited its sends gets a close\n // that returns without touching the timeout at all.\n recheck();\n });\n }\n\n /**\n * **Test seam**, attach an externally-built {@link Connection} directly,\n * bypassing the `net.createConnection` + `NetTransport` path. Used by\n * lifecycle tests driving `InMemoryTransport.pair()` for determinism.\n *\n * @internal\n */\n _attachExistingConnection(conn: Connection): void {\n if (this._connection !== null) {\n throw new MllpConnectionError(\"connection already attached\", {\n cause: new Error(\"attach twice\"),\n phase: \"connect\",\n });\n }\n this._attachConnection(conn);\n }\n\n /**\n * Gracefully close the client, awaiting the acknowledgements of sends already written to\n * the transport.\n *\n * Delegates to {@link Connection.close}, which transitions `CONNECTED → DRAINING\n * → DISCONNECTED` (or `CLOSED` on drain timeout). No-op if no Connection is\n * attached, or if the close has already completed.\n *\n * The drain ends as soon as the last outstanding acknowledgement arrives, so a caller whose\n * peer answers promptly does not wait out `drainTimeoutMs`. Whatever is still unresolved\n * when it does end is reported to its own `send()` caller under one rule, and the whole\n * point of this method is that the two halves of that rule are told apart:\n *\n * * a send already written to the transport rejects with {@link MllpUnknownFateError},\n * carrying the timestamp at which its bytes went out. It may have been committed;\n * resending it may duplicate a clinical message. Nothing is retried here.\n * * a send still held inside the client rejects with {@link MllpNeverDeliveredError}.\n * The peer never saw those bytes, so resending them is safe.\n * * a send whose commit disposition the peer had already reported keeps its\n * {@link MllpApplicationAckError}, which names that disposition. The peer's custody of\n * those bytes is known, and a shutdown never downgrades a known fact to an unknown one.\n *\n * Rejects with `DOMException('Aborted', 'AbortError')` if `signal` aborts mid-drain;\n * on abort, the underlying Connection is force-destroyed and every still-pending send is\n * settled under the same rule rather than left pending.\n *\n * A drain cannot make delivery certain, and nothing here claims otherwise: an\n * acknowledgement lost in flight is indistinguishable from a message never received. That is\n * what the unknown-fate report says, and it is why the application still owns idempotency,\n * keyed on MSH-10 plus MSH-7.\n *\n * @example\n * ```typescript\n * await client.close({ drainTimeoutMs: 5_000 });\n * ```\n */\n async close(opts?: { drainTimeoutMs?: number; signal?: AbortSignal }): Promise<void> {\n const signal = opts?.signal;\n\n // AbortSignal: reject immediately if already aborted\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n\n // Plan 04: suppress reconnect for the rest of this client's lifetime.\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n\n const conn = this._connection;\n if (conn === null) {\n // No connection attached; still settle anything the client is holding\n // (defensive, this branch is unreachable in normal flow).\n this._settleUnresolvedSends();\n return;\n }\n\n // Pending sends are settled AFTER the drain, in the `finally` below, because the whole\n // question this method answers, which messages definitely never reached the wire, cannot\n // be answered before the wait it needs. A Connection already in a terminal state drains\n // nothing and returns at once, so an already-closed client is still prompt.\n this._installDrainHook(conn);\n const closeOpts =\n opts?.drainTimeoutMs !== undefined ? { drainTimeoutMs: opts.drainTimeoutMs } : undefined;\n\n if (signal === undefined) {\n try {\n await conn.close(closeOpts);\n } finally {\n this._settleUnresolvedSends();\n }\n return;\n }\n\n // Wire AbortSignal, abort during drain force-destroys the Connection\n let abortHandler: (() => void) | undefined;\n const abortPromise = new Promise<never>((_resolve, reject) => {\n abortHandler = (): void => {\n conn.destroy(new Error(\"aborted\"));\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n });\n\n try {\n await Promise.race([conn.close(closeOpts), abortPromise]);\n } finally {\n if (abortHandler !== undefined) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n this._settleUnresolvedSends();\n }\n }\n\n /**\n * Abruptly destroy the client, force-transitions the underlying Connection\n * to `CLOSED` immediately. No-op if no Connection is attached. Idempotent.\n *\n * Every pending send is settled at once: no acknowledgement is awaited and no drain timeout\n * is honoured, which is exactly what separates this from {@link MllpClient.close}. A\n * `close()` already waiting on acknowledgements stops waiting here and returns. A send still\n * held inside the client is failed with {@link MllpNeverDeliveredError}, because nothing was\n * ever written for it; that is a statement about the bytes, not about how the client was\n * shut down, so it holds on this path too.\n *\n * @example\n * ```typescript\n * client.destroy(new Error('shutting down'));\n * ```\n */\n destroy(reason?: Error): void {\n // Plan 04: suppress reconnect for the rest of this client's lifetime.\n this._userClosed = true;\n if (this._backoffTimer !== null) {\n clearTimeout(this._backoffTimer);\n this._backoffTimer = null;\n }\n const teardownReason =\n reason ??\n new MllpConnectionError(\"client destroyed\", {\n cause: new Error(\"destroyed\"),\n phase: \"close\",\n });\n // A drain in progress is abandoned rather than awaited, so a close() racing this destroy\n // returns instead of sitting out the rest of its timeout.\n this._endDrainWait?.();\n this._cancelParkedSends();\n this._teardownCorrelator(teardownReason);\n const conn = this._connection;\n if (conn === null) return;\n conn.destroy(reason);\n }\n\n /**\n * Returns a JSON-serializable observability snapshot.\n *\n * All fields are plain values, no Buffers, no class instances, no Maps,\n * no circular refs. Safe to `JSON.stringify` directly.\n *\n * `inFlight` is the count of correlator entries with `sentAt !== null`\n * (entries actually written to the wire and awaiting ACK), distinct from\n * `queueDepth` which counts ALL live correlator entries (including\n * pre-flush and serialization-queued sends).\n *\n * @example\n * ```typescript\n * setInterval(() => logger.info(JSON.stringify(client.getStats())), 60_000);\n * ```\n */\n getStats(): ClientStats {\n const connStats = this._connection?.getStats();\n const corrStats = this._correlator?.getStats();\n // Merge warningsByCode: Connection-level + Client-aggregated (Correlator-emitted).\n const merged: Partial<Record<WarningCode, number>> = {\n ...this._aggregatedWarningsByCode,\n };\n if (connStats !== undefined) {\n for (const [k, v] of Object.entries(connStats.warningsByCode)) {\n const code = k as WarningCode;\n merged[code] = (merged[code] ?? 0) + v;\n }\n }\n return {\n state: this.state,\n connectionId: connStats?.connectionId ?? null,\n queueDepth: corrStats?.size ?? 0,\n queueBytes: corrStats?.queueBytes ?? 0,\n inFlight: corrStats?.inFlight ?? 0,\n warningsByCode: merged,\n totalBytesIn: connStats?.bytesIn ?? 0,\n totalBytesOut: connStats?.bytesOut ?? 0,\n sentTotal: this._sentTotal,\n ackedTotal: this._ackedTotal,\n timedOutTotal: this._timedOutTotal,\n reconnectAttempts: this._reconnectAttempts,\n lastConnectedAt: this._lastConnectedAt,\n lastAckAt: this._lastAckAt,\n tls: this._opts.tls !== undefined,\n };\n }\n\n /**\n * Async disposal, delegates to {@link MllpClient.close} for `await using` support.\n *\n * @example\n * ```typescript\n * await using client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * // client.close() is called automatically at end of block\n * ```\n */\n async [Symbol.asyncDispose](): Promise<void> {\n return this.close();\n }\n}\n\n/**\n * Create an {@link MllpClient}. Equivalent to `new MllpClient(opts)`.\n *\n * @example\n * ```typescript\n * import { createClient } from '@cosyte/mllp';\n *\n * const client = createClient({ host: 'localhost', port: 2575 });\n * await client.connect();\n * const ack = await client.send(payloadBuffer);\n * await client.close();\n * ```\n */\nexport function createClient(opts: ClientOptions): MllpClient {\n return new MllpClient(opts);\n}\n\n/**\n * Options for {@link createStarterClient}.\n *\n * The starter applies opinionated defaults on top of `ClientOptions`,\n * so every override here is optional except `host` + `port`. The\n * starter-specific addition is `handleSignals` (mirrors `createStarterServer`).\n *\n * @example\n * ```typescript\n * const opts: StarterClientOptions = {\n * host: 'localhost',\n * port: 2575,\n * onMessage: (payload) => logger.info({ bytes: payload.length }),\n * handleSignals: true,\n * };\n * ```\n */\nexport interface StarterClientOptions {\n /** Host to connect to. */\n readonly host: string;\n /** TCP port. */\n readonly port: number;\n /**\n * Inbound-message callback (any framed payload from the peer, including\n * non-ACK messages on bidirectional channels). Mirrors the server-side\n * `onMessage` ergonomics.\n */\n readonly onMessage?: (payload: Buffer) => void;\n /** Override default `30_000`. */\n readonly ackTimeoutMs?: number;\n /** Override default `false` (FIFO mode). */\n readonly correlateByControlId?: boolean;\n /** Override default `true` (parallel up to highWaterMark). */\n readonly pipeline?: boolean;\n /** Override default `64`. */\n readonly highWaterMark?: HighWaterMark;\n /** Override default `'reject'`. */\n readonly onBackpressure?: \"reject\" | \"wait\";\n /** Override default `true` (auto-reconnect on transient errors). */\n readonly autoReconnect?: boolean;\n /** Custom reconnect-backoff hook. */\n readonly retryStrategy?: RetryStrategy;\n /** Drain timeout for `close()` (default `30_000`). */\n readonly drainTimeoutMs?: number;\n /** FrameReader options (passthrough). */\n readonly framing?: ClientOptions[\"framing\"];\n /** TCP keepalive interval ms. */\n readonly keepaliveIntervalMs?: number;\n /** Application-idle dead-peer timeout ms. */\n readonly deadPeerTimeoutMs?: number;\n /** Enable TLS (MLLPS) for this connection. Passthrough to `ClientOptions.tls`. */\n readonly tls?: TlsOptions | true;\n /**\n * Register process SIGTERM/SIGINT handlers that close the client. Default\n * `false`. When `true`, SIGTERM/SIGINT both call `client.close()`\n * and exit the process. Handlers self-deregister on `'close'`.\n */\n readonly handleSignals?: boolean;\n}\n\n/**\n * Three-line MLLP client with batteries-included defaults. The returned\n * client is already CONNECTED, `connect()` has been awaited.\n *\n * Defaults:\n * - `autoReconnect: true`\n * - `ackTimeoutMs: 30_000`\n * - `correlateByControlId: false` (FIFO mode, simplest mental model)\n * - `pipeline: true`\n * - `highWaterMark: 64`\n * - `onBackpressure: 'reject'`\n * - `handleSignals: false` (opt-in)\n *\n * The factory is **async**, so the literal three-line north-star snippet\n * has an explicit `await` BEFORE `createStarterClient(...)`, without it,\n * the `using` declaration would receive a `Promise`, not an `MllpClient`,\n * and `Symbol.asyncDispose` would not run at scope exit.\n *\n * @example\n * ```typescript\n * import { createStarterClient } from '@cosyte/mllp';\n * await using c = await createStarterClient({ host: 'localhost', port: 2575 });\n * const ack = await c.send(payloadBuffer);\n * ```\n */\nexport async function createStarterClient(opts: StarterClientOptions): Promise<MllpClient> {\n // Build ClientOptions, applying D-22 defaults only for unset fields.\n const clientOpts: ClientOptions = {\n host: opts.host,\n port: opts.port,\n autoReconnect: opts.autoReconnect ?? true,\n ackTimeoutMs: opts.ackTimeoutMs ?? 30_000,\n correlateByControlId: opts.correlateByControlId ?? false,\n pipeline: opts.pipeline ?? true,\n highWaterMark: opts.highWaterMark ?? 64,\n onBackpressure: opts.onBackpressure ?? \"reject\",\n ...(opts.drainTimeoutMs !== undefined ? { drainTimeoutMs: opts.drainTimeoutMs } : {}),\n ...(opts.framing !== undefined ? { framing: opts.framing } : {}),\n ...(opts.retryStrategy !== undefined ? { retryStrategy: opts.retryStrategy } : {}),\n ...(opts.keepaliveIntervalMs !== undefined\n ? { keepaliveIntervalMs: opts.keepaliveIntervalMs }\n : {}),\n ...(opts.deadPeerTimeoutMs !== undefined ? { deadPeerTimeoutMs: opts.deadPeerTimeoutMs } : {}),\n ...(opts.tls !== undefined ? { tls: opts.tls } : {}),\n };\n const client = createClient(clientOpts);\n\n if (opts.onMessage !== undefined) {\n const handler = opts.onMessage;\n client.on(\"message\", (e: { payload: Buffer }) => {\n handler(e.payload);\n });\n }\n\n if (opts.handleSignals === true) {\n // T-05-06-01: handler self-deregisters on 'close' to avoid per-process\n // listener accumulation (mirror createStarterServer pattern).\n const sigHandler = (): void => {\n void client\n .close()\n .then(() => process.exit(0))\n .catch(() => process.exit(1));\n };\n process.once(\"SIGTERM\", sigHandler);\n process.once(\"SIGINT\", sigHandler);\n client.once(\"close\", () => {\n process.removeListener(\"SIGTERM\", sigHandler);\n process.removeListener(\"SIGINT\", sigHandler);\n });\n }\n\n await client.connect();\n return client;\n}\n","/**\n * The canonical exchange corpus the differential harness sends, and the canonical\n * acknowledgement it compares a peer's answer against.\n *\n * Every message here is **synthetic**: the identifiers, names and dates are fabricated\n * and declared as such in this repository's PHI allow-list. Nothing in this file is, or\n * may become, a real patient. The corpus ships inside the published package so a consumer\n * who installed `@cosyte/mllp` and has no checkout of this repository can still run the\n * harness against their own interface engine.\n *\n * Each message is written as segment string literals joined by the HL7 segment separator\n * (`CR`) rather than as a byte blob, deliberately: the identifiers stay readable to a human\n * reviewer and to this repository's structured PHI scan, which is what keeps a planted\n * identifier from slipping through as opaque bytes.\n *\n * @example\n * ```typescript\n * import { canonicalExchanges } from '@cosyte/mllp';\n * for (const exchange of canonicalExchanges()) {\n * console.log(exchange.id, exchange.payload.length);\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { CONTROL_ID_ENCODING } from \"../internal/control-id.js\";\n\n/**\n * One send-and-await-response pair the harness runs against a peer.\n *\n * `payload` is the HL7 v2 message body with **no** MLLP framing on it. The harness frames\n * it canonically (`VT` + payload + `FS` + `CR`) with the package's own strict encoder\n * before it goes on the wire, so what a peer receives is the Release 1 block this package\n * emits for every message.\n *\n * @example\n * ```typescript\n * const [first] = canonicalExchanges();\n * // first?.id === 'adt-a01'; first?.controlId === 'MSG00001'\n * ```\n */\nexport interface CanonicalExchange {\n /**\n * Stable identifier for this exchange, and the identifier the report uses. It names the\n * message, never a patient, so it is safe to log.\n */\n readonly id: string;\n /** One-line description of what the message is, for a human reading the report. */\n readonly description: string;\n /**\n * The `MSH-10` message control ID this message carries. A conformant peer echoes it in\n * the `MSA-2` of its acknowledgement, which is the correlation the harness checks.\n */\n readonly controlId: string;\n /** The unframed HL7 v2 message bytes. A fresh copy per call; mutating it is harmless. */\n readonly payload: Buffer;\n}\n\n/** The HL7 v2 segment separator. Segments are joined with it to form a message body. */\nconst SEGMENT_SEPARATOR = \"\\r\";\n\n/**\n * An admit message. Patient identity is fabricated: the `900000000` block is a reserved\n * synthetic range that is never a real medical record number.\n */\nconst ADT_A01_SEGMENTS: readonly string[] = [\n \"MSH|^~\\\\&|SENDING_APP|SENDING_FAC|RECV_APP|RECV_FAC|20260709120000||ADT^A01|MSG00001|P|2.5\",\n \"EVN|A01|20260709120000\",\n \"PID|1||900000001^^^FAC^MR||DOE^JANE^Q||19700101|F\",\n \"PV1|1|I|WARD^101^1^FAC\",\n];\n\n/** An observation result message. Same reserved synthetic identifier range. */\nconst ORU_R01_SEGMENTS: readonly string[] = [\n \"MSH|^~\\\\&|LAB|LFAC|EHR|EFAC|20260709130000||ORU^R01|MSG00002|P|2.5\",\n \"PID|1||900000002^^^FAC^MR||ROE^RICHARD\",\n \"OBR|1||ORD123|CBC^COMPLETE BLOOD COUNT\",\n \"OBX|1|NM|WBC^WHITE BLOOD COUNT||7.2|10*9/L|4.0-11.0|N\",\n];\n\n/**\n * The canonical positive acknowledgement for the admit message above: its `MSA-2` echoes\n * that message's `MSH-10`. It is the reference answer, not a message the harness sends.\n */\nconst ACK_AA_SEGMENTS: readonly string[] = [\n \"MSH|^~\\\\&|RECV_APP|RECV_FAC|SENDING_APP|SENDING_FAC|20260709120001||ACK^A01|ACK00001|P|2.5\",\n \"MSA|AA|MSG00001\",\n];\n\n/**\n * Bytes of a message written as segments. `latin1` is the byte-faithful decode this\n * package uses everywhere a control ID crosses the boundary, so a corpus message means the\n * same bytes here as it does to the correlation scanners.\n */\nfunction messageBytes(segments: readonly string[]): Buffer {\n return Buffer.from(segments.join(SEGMENT_SEPARATOR), CONTROL_ID_ENCODING);\n}\n\n/**\n * The canonical exchange corpus, freshly built on every call so a caller cannot mutate the\n * corpus another caller will send.\n *\n * Two messages, both drawn from the framing goldens this package already pins itself\n * against, so what a consumer sends at their own engine is byte-identical to what this\n * package's own framing tests assert. The positive acknowledgement golden is deliberately\n * NOT one of them: it is the reference ANSWER (see {@link canonicalAcknowledgement}), and\n * an engine that correctly declines to acknowledge an unsolicited acknowledgement would\n * otherwise be reported as having failed to answer.\n *\n * @returns One entry per exchange, in the order the harness runs them.\n *\n * @example\n * ```typescript\n * const exchanges = canonicalExchanges();\n * // exchanges.map((e) => e.id) is ['adt-a01', 'oru-r01']\n * ```\n */\nexport function canonicalExchanges(): readonly CanonicalExchange[] {\n return Object.freeze([\n Object.freeze<CanonicalExchange>({\n id: \"adt-a01\",\n description: \"Patient admit, four segments, synthetic identity\",\n controlId: \"MSG00001\",\n payload: messageBytes(ADT_A01_SEGMENTS),\n }),\n Object.freeze<CanonicalExchange>({\n id: \"oru-r01\",\n description: \"Observation result, four segments, synthetic identity\",\n controlId: \"MSG00002\",\n payload: messageBytes(ORU_R01_SEGMENTS),\n }),\n ]);\n}\n\n/**\n * The canonical positive acknowledgement, unframed, freshly built on every call.\n *\n * This is what a conformant Release 1 peer's answer to the first canonical exchange looks\n * like on the wire once the framing is stripped. The harness never sends it; it is here so\n * a test double, or a consumer building one, can answer with the shape the package itself\n * emits.\n *\n * @returns The unframed acknowledgement bytes.\n *\n * @example\n * ```typescript\n * import { canonicalAcknowledgement, encodeFrame } from '@cosyte/mllp';\n * const wire = encodeFrame(canonicalAcknowledgement());\n * // wire[0] === 0x0b\n * ```\n */\nexport function canonicalAcknowledgement(): Buffer {\n return messageBytes(ACK_AA_SEGMENTS);\n}\n","/**\n * Typed **configuration** error for the differential harness, and its stable code.\n *\n * A configuration error is raised before any socket exists: the peer address the caller\n * supplied cannot be resolved into a host and a port, so the run is refused rather than\n * quietly skipped. Skipping it would be the worse answer by far, because a skip is what\n * \"no peer configured\" means, and a developer who mistyped an address would read the skip\n * as proof the harness ran.\n *\n * This code is a **public API**. It appears in caller `catch` blocks, log pipelines and\n * monitoring dashboards. Renaming or removing it is a breaking change, the same guardrail\n * the framing warning codes and the security-warning codes carry.\n *\n * @example\n * ```typescript\n * import { MllpDifferentialConfigurationError, runDifferential } from '@cosyte/mllp';\n * try {\n * await runDifferential({ peer: process.env['MLLP_DIFF_PEER'] });\n * } catch (err) {\n * if (err instanceof MllpDifferentialConfigurationError) {\n * logger.error({ code: err.code, value: err.value });\n * }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * A peer address was configured and could not be resolved into a host and a port.\n *\n * Distinct from no peer at all, which is not an error and skips the run.\n *\n * @example\n * ```typescript\n * import { MLLP_DIFF_PEER_UNPARSEABLE } from '@cosyte/mllp';\n * if (err.code === MLLP_DIFF_PEER_UNPARSEABLE) process.exit(78); // EX_CONFIG\n * ```\n */\nexport const MLLP_DIFF_PEER_UNPARSEABLE = \"MLLP_DIFF_PEER_UNPARSEABLE\";\n\n/**\n * Union of the stable differential-configuration error codes.\n *\n * @example\n * ```typescript\n * const code: DifferentialConfigurationErrorCode = 'MLLP_DIFF_PEER_UNPARSEABLE';\n * ```\n */\nexport type DifferentialConfigurationErrorCode = typeof MLLP_DIFF_PEER_UNPARSEABLE;\n\n/**\n * Frozen message registry for {@link DifferentialConfigurationErrorCode}.\n *\n * A lookup, never an interpolation of anything off the wire. The offending value the\n * factory appends is the address the CALLER configured, which is deployment configuration\n * in the same class as a hostname in a connection error, and the run is refused before a\n * byte of anyone's traffic exists.\n */\nconst DIFFERENTIAL_CONFIGURATION_MESSAGES: Readonly<\n Record<DifferentialConfigurationErrorCode, string>\n> = Object.freeze({\n [MLLP_DIFF_PEER_UNPARSEABLE]:\n \"the configured differential peer address could not be resolved into a host and a port, \" +\n \"so the run was refused rather than skipped; expected the form host:port\",\n});\n\n/**\n * Fixed, human-readable description for a differential-configuration code.\n *\n * Byte-for-byte identical for a given code, whatever was configured.\n *\n * @param code - The stable configuration-error code.\n * @returns The frozen registry text for `code`.\n *\n * @example\n * ```typescript\n * import { differentialConfigurationMessage, MLLP_DIFF_PEER_UNPARSEABLE } from '@cosyte/mllp';\n * logger.error(differentialConfigurationMessage(MLLP_DIFF_PEER_UNPARSEABLE));\n * ```\n */\nexport function differentialConfigurationMessage(code: DifferentialConfigurationErrorCode): string {\n return DIFFERENTIAL_CONFIGURATION_MESSAGES[code];\n}\n\n/**\n * Thrown when the differential harness is configured with a peer address it cannot use.\n *\n * Rejects the originating `runDifferential()` call. No socket is opened, no canonical\n * message is sent, and no report is produced: there is nothing to report about.\n *\n * Identify it by `instanceof` **and** by the stable\n * {@link MllpDifferentialConfigurationError.code}, never by matching on the message text.\n *\n * @example\n * ```typescript\n * import { MllpDifferentialConfigurationError } from '@cosyte/mllp';\n * try {\n * await runDifferential({ peer: 'not-an-address' });\n * } catch (err) {\n * if (err instanceof MllpDifferentialConfigurationError) logger.error({ value: err.value });\n * }\n * ```\n */\nexport class MllpDifferentialConfigurationError extends Error {\n override readonly name = \"MllpDifferentialConfigurationError\" as const;\n\n /**\n * The stable configuration-error code. Public API; branch on this rather than on the\n * message.\n */\n readonly code: DifferentialConfigurationErrorCode;\n\n /**\n * The offending value exactly as it was configured, so the operator can see which of\n * their settings is wrong without guessing. It is caller configuration, never anything\n * read off a peer.\n */\n readonly value: string;\n\n /**\n * Construct a differential configuration error.\n *\n * @param code - The stable configuration-error code; also selects the message.\n * @param value - The offending configured value, reported verbatim.\n */\n constructor(code: DifferentialConfigurationErrorCode, value: string) {\n super(`${differentialConfigurationMessage(code)}; got: ${JSON.stringify(value)}`);\n this.code = code;\n this.value = value;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MllpDifferentialConfigurationError);\n }\n }\n}\n","/**\n * Peer-address resolution for the differential harness.\n *\n * Two states are deliberately told apart, and conflating them is the defect this module\n * exists to prevent:\n *\n * * **No peer configured.** The address is absent or empty. There is nothing to point\n * the harness at, so the run skips cleanly and the default verification stays green.\n * * **A peer configured that cannot be read.** The address is present and is not a host\n * and a port. That is a mistake in someone's configuration, and it is refused by name.\n *\n * A single \"return undefined for both\" rule silently skips the live run while the operator\n * believes it ran, which is the one outcome a pre-go-live check must never produce.\n *\n * @packageDocumentation\n */\n\nimport { MLLP_DIFF_PEER_UNPARSEABLE, MllpDifferentialConfigurationError } from \"./error.js\";\n\n/** The highest port number a TCP endpoint can be bound to. */\nconst MAX_PORT = 65535;\n\n/**\n * A resolved peer endpoint: where the harness opens its connections.\n *\n * @example\n * ```typescript\n * const peer: DifferentialPeer = { host: '127.0.0.1', port: 2575 };\n * ```\n */\nexport interface DifferentialPeer {\n /** Host name or address literal, with any IPv6 brackets already removed. */\n readonly host: string;\n /** TCP port, an integer in 1..65535. */\n readonly port: number;\n}\n\n/**\n * Resolve a configured peer address into a host and a port.\n *\n * The address is split on its **last** colon, so an IPv6 literal resolves as well as a\n * host name: `[::1]:2575` and `::1:2575` both give host `::1`. A naive split on the first\n * colon mangles the host and produces a NaN port, which is precisely how a live run gets\n * skipped while the operator believes it happened.\n *\n * @param raw - The configured address, or `undefined`/`null`/empty for no peer at all.\n * @returns The resolved peer, or `undefined` when no peer is configured.\n * @throws MllpDifferentialConfigurationError when an address is present and unusable.\n *\n * @example\n * ```typescript\n * import { resolveDifferentialPeer } from '@cosyte/mllp';\n * const peer = resolveDifferentialPeer('127.0.0.1:2575');\n * // peer?.host === '127.0.0.1'; peer?.port === 2575\n * // resolveDifferentialPeer(undefined) === undefined\n * ```\n */\nexport function resolveDifferentialPeer(\n raw: string | undefined | null,\n): DifferentialPeer | undefined {\n if (raw === undefined || raw === null) return undefined;\n const trimmed = raw.trim();\n if (trimmed === \"\") return undefined;\n\n const refuse: () => never = () => {\n throw new MllpDifferentialConfigurationError(MLLP_DIFF_PEER_UNPARSEABLE, raw);\n };\n\n const lastColon = trimmed.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === trimmed.length - 1) refuse();\n\n const host = trimmed.slice(0, lastColon).replace(/^\\[|\\]$/g, \"\");\n if (host === \"\") refuse();\n\n const portText = trimmed.slice(lastColon + 1);\n // `Number` accepts leading signs, whitespace, hex and exponent forms, every one of which\n // would make a nonsense address look resolvable. Only plain digits are a port.\n if (!/^\\d+$/.test(portText)) refuse();\n const port = Number(portText);\n if (!Number.isInteger(port) || port <= 0 || port > MAX_PORT) refuse();\n\n return Object.freeze<DifferentialPeer>({ host, port });\n}\n","/**\n * The differential run: send each canonical message at a peer, read what comes back, and\n * report what was observed.\n *\n * ## What it does, exchange by exchange\n *\n * One connection per exchange, opened, used and closed. That is deliberate: a peer that\n * refuses or drops one connection then costs one exchange rather than the whole run, which\n * is what lets the report say \"this message was answered and that one was not\" instead of\n * \"the run failed\".\n *\n * The message goes out through this package's own strict encoder, so what the peer\n * receives is the canonical Release 1 block: `VT` + payload + `FS` + `CR`. The answer is\n * read back through this package's own decoder with **every tolerance enabled**, so a peer\n * that deviates from the block is described by the stable warning code for that deviation\n * rather than killing the run with an opaque error.\n *\n * ## What byte parity means here\n *\n * The MLLP **envelope** the peer emitted, compared against the canonical Release 1 block\n * this package emits for the same payload. It is not equality of message content, which\n * can never hold: an acknowledgement carries the peer's own control ID, its own timestamp\n * and its own sending application, and it is supposed to.\n *\n * ## Aim it with care\n *\n * A run sends real bytes into whatever engine it is pointed at. Those bytes are synthetic\n * patients, so an engine that accepts them stores synthetic patients. Point it at a test\n * or staging endpoint, never at a production interface carrying live traffic.\n *\n * @packageDocumentation\n */\n\nimport { createConnection } from \"node:net\";\n\nimport { FrameReader } from \"../framing/decoder.js\";\nimport { encodeFrame } from \"../framing/encoder.js\";\nimport { MllpFramingError } from \"../framing/error.js\";\nimport type { MllpWarning, WarningCode } from \"../framing/registry.js\";\nimport { extractMsaControlId } from \"../internal/control-id.js\";\nimport type { Transport } from \"../transport/index.js\";\nimport { NetTransport } from \"../transport/index.js\";\n\nimport { canonicalExchanges, type CanonicalExchange } from \"./corpus.js\";\nimport { resolveDifferentialPeer, type DifferentialPeer } from \"./peer.js\";\nimport type {\n DifferentialCorrelationOutcome,\n DifferentialDeviation,\n DifferentialExchangeOutcome,\n DifferentialExchangeReport,\n DifferentialParityOutcome,\n DifferentialReport,\n DifferentialRunResult,\n} from \"./report.js\";\n\n/** Default per-exchange response deadline. Generous, because an engine may be cold. */\nconst DEFAULT_DEADLINE_MS = 10_000;\n\n/**\n * The stable code that names a failure to correlate an acknowledgement to the message it\n * answered. It is a correlation failure and never a framing-parity one, and the report\n * keeps the two apart for exactly that reason.\n */\nconst ACK_CORRELATION_CODE: WarningCode = \"MLLP_ACK_UNMATCHED_CONTROL_ID\";\n\n/**\n * Opens a connection to the peer. Supply your own to run the harness over something other\n * than a plain TCP socket, a TLS transport for an MLLPS peer being the obvious case.\n *\n * The returned transport must not be connected yet: the harness registers its handlers\n * first and writes only once `onConnect` fires.\n *\n * @example\n * ```typescript\n * const connect: DifferentialConnect = (peer) =>\n * new TlsTransport(tls.connect({ host: peer.host, port: peer.port }));\n * ```\n */\nexport type DifferentialConnect = (peer: DifferentialPeer) => Transport;\n\n/**\n * Options for {@link runDifferential}.\n *\n * @example\n * ```typescript\n * const report = await runDifferential({ peer: '127.0.0.1:2575', deadlineMs: 5_000 });\n * ```\n */\nexport interface DifferentialRunOptions {\n /**\n * The peer to run against, as `host:port`. Absent or empty means no peer is configured,\n * and the run skips cleanly. A value that is present and cannot be resolved into a host\n * and a port is refused by name instead, because a silent skip there would read as\n * proof the harness ran.\n */\n readonly peer?: string | undefined;\n /** Per-exchange response deadline in milliseconds. Default 10000. */\n readonly deadlineMs?: number;\n /**\n * Largest response payload the decoder will accumulate before it refuses, in bytes.\n * Defaults to the package's own 16 MiB frame cap. A peer that exceeds it is reported as\n * a deviation named by the oversize warning code, not as an unhandled error.\n */\n readonly maxFrameSizeBytes?: number;\n /** The exchanges to run. Defaults to the shipped canonical corpus. */\n readonly exchanges?: readonly CanonicalExchange[];\n /** How to open each connection. Defaults to a plain TCP socket. */\n readonly connect?: DifferentialConnect;\n /** Aborts the run. The rejection carries the signal's own reason. */\n readonly signal?: AbortSignal;\n}\n\n/** How one exchange ended, before it is classified into a report entry. */\ntype ExchangeEnd =\n | { readonly kind: \"frame\"; readonly payload: Buffer; readonly frameStart: number }\n | { readonly kind: \"framing-error\"; readonly code: WarningCode; readonly byteOffset: number }\n | { readonly kind: \"timeout\" }\n | { readonly kind: \"closed\" }\n | { readonly kind: \"error\"; readonly error: Error }\n | { readonly kind: \"aborted\" };\n\n/** The default connection: a plain TCP socket wrapped as a transport. */\nfunction connectOverTcp(peer: DifferentialPeer): Transport {\n return new NetTransport(createConnection({ host: peer.host, port: peer.port }));\n}\n\n/** `true` when a socket error is the operating system refusing the connection outright. */\nfunction isConnectionRefused(err: Error): boolean {\n if (!(\"code\" in err)) return false;\n const { code } = err;\n return typeof code === \"string\" && code === \"ECONNREFUSED\";\n}\n\n/**\n * Which named failure a dead connection is. `connected` is what tells a peer that never\n * accepted the connection apart from one that accepted it and then went away, and those\n * are different findings for whoever reads the report.\n */\nfunction failureOutcome(err: Error | undefined, connected: boolean): DifferentialExchangeOutcome {\n if (err !== undefined && isConnectionRefused(err)) return \"connection-refused\";\n return connected ? \"connection-dropped\" : \"connection-failed\";\n}\n\n/** Throw the signal's own reason if the run has been aborted. */\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal !== undefined && signal.aborted) throw signal.reason;\n}\n\n/** Everything an exchange needs that is the same for every exchange in a run. */\ninterface ExchangeSettings {\n readonly peer: DifferentialPeer;\n readonly connect: DifferentialConnect;\n readonly deadlineMs: number;\n readonly maxFrameSizeBytes: number | undefined;\n readonly signal: AbortSignal | undefined;\n}\n\n/**\n * Run one canonical exchange to its conclusion. Never throws for anything the peer does;\n * the only rejection is an abort, which is the caller's own instruction.\n */\nasync function runExchange(\n exchange: CanonicalExchange,\n settings: ExchangeSettings,\n): Promise<DifferentialExchangeReport> {\n const { peer, connect, deadlineMs, maxFrameSizeBytes, signal } = settings;\n const framed = encodeFrame(exchange.payload);\n const startedAt = Date.now();\n const deviations: DifferentialDeviation[] = [];\n const chunks: Buffer[] = [];\n let responseByteCount = 0;\n let connected = false;\n\n const finish = (\n outcome: DifferentialExchangeOutcome,\n byteParity: DifferentialParityOutcome,\n correlation: DifferentialCorrelationOutcome,\n ): DifferentialExchangeReport =>\n Object.freeze<DifferentialExchangeReport>({\n exchangeId: exchange.id,\n outcome,\n byteParity,\n correlation,\n warningCodes: Object.freeze(deviations.map((d) => d.code)),\n deviations: Object.freeze([...deviations]),\n requestByteCount: framed.length,\n responseByteCount,\n deadlineMs,\n elapsedMs: Date.now() - startedAt,\n });\n\n let transport: Transport;\n try {\n transport = connect(peer);\n } catch {\n return finish(\"connection-failed\", \"not-observed\", \"not-observed\");\n }\n\n const end = await new Promise<ExchangeEnd>((resolve) => {\n let settled = false;\n let abortListener: (() => void) | undefined;\n const settle = (value: ExchangeEnd): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (abortListener !== undefined) signal?.removeEventListener(\"abort\", abortListener);\n resolve(value);\n };\n // Armed BEFORE any handler is registered, so nothing that settles synchronously during\n // registration can reach `settle` while this is still uninitialized.\n const timer = setTimeout(() => {\n settle({ kind: \"timeout\" });\n }, deadlineMs);\n\n const reader = new FrameReader({\n onFrame: (payload, frameStart, warnings) => {\n for (const w of warnings) recordWarning(deviations, w);\n settle({ kind: \"frame\", payload, frameStart });\n },\n allowFsOnly: true,\n allowLfAfterFs: true,\n allowMissingLeadingVt: true,\n allowLeadingWhitespace: true,\n ...(maxFrameSizeBytes === undefined ? {} : { maxFrameSizeBytes }),\n });\n\n transport.onData((chunk) => {\n if (settled) return;\n responseByteCount += chunk.length;\n chunks.push(Buffer.from(chunk));\n try {\n reader.push(chunk);\n } catch (err) {\n // A fatal decoder throw is still a deviation of the peer's block, so it is named\n // by its stable code rather than escaping as an opaque error.\n if (err instanceof MllpFramingError) {\n deviations.push(Object.freeze({ code: err.code, byteOffset: err.byteOffset }));\n settle({ kind: \"framing-error\", code: err.code, byteOffset: err.byteOffset });\n return;\n }\n settle({ kind: \"error\", error: err instanceof Error ? err : new Error(String(err)) });\n }\n });\n transport.onConnect(() => {\n connected = true;\n transport.write(framed);\n });\n transport.onError((err) => {\n settle({ kind: \"error\", error: err });\n });\n transport.onClose(() => {\n settle({ kind: \"closed\" });\n });\n\n if (signal !== undefined) {\n if (signal.aborted) {\n settle({ kind: \"aborted\" });\n return;\n }\n abortListener = (): void => {\n settle({ kind: \"aborted\" });\n };\n signal.addEventListener(\"abort\", abortListener, { once: true });\n }\n });\n\n transport.destroy();\n\n if (end.kind === \"aborted\") {\n throwIfAborted(signal);\n // Only a signal can produce this end, and `throwIfAborted` has already thrown its\n // reason. The arm exists so the compiler sees an exhaustive return, not as a path.\n return finish(\"connection-failed\", \"not-observed\", \"not-observed\");\n }\n\n switch (end.kind) {\n case \"frame\":\n return classifyFrame(exchange, end.payload, end.frameStart, chunks, deviations, finish);\n case \"framing-error\":\n return finish(\"undecodable-response\", \"deviation\", \"not-observed\");\n case \"timeout\":\n return finish(\"unanswered\", \"not-observed\", \"not-observed\");\n case \"closed\":\n return finish(failureOutcome(undefined, connected), \"not-observed\", \"not-observed\");\n case \"error\":\n return finish(failureOutcome(end.error, connected), \"not-observed\", \"not-observed\");\n }\n}\n\n/**\n * Record a decoder or encoder warning as a deviation, keeping the code and the offset and\n * dropping the message.\n *\n * The message is dropped on purpose, and this is not tidiness. Two codes render the hex of\n * the single byte found where a framing byte was expected, and on a stream that omits its\n * leading `VT` that byte is the first byte of the peer's unframed content. A report is a\n * file someone mails to a vendor, so it carries the code and the offset and nothing that\n * came off the wire.\n */\nfunction recordWarning(into: DifferentialDeviation[], warning: MllpWarning, offsetBase = 0): void {\n into.push(Object.freeze({ code: warning.code, byteOffset: offsetBase + warning.byteOffset }));\n}\n\n/** Classify a response frame into parity and correlation outcomes. */\nfunction classifyFrame(\n exchange: CanonicalExchange,\n payload: Buffer,\n frameStart: number,\n chunks: readonly Buffer[],\n deviations: DifferentialDeviation[],\n finish: (\n outcome: DifferentialExchangeOutcome,\n byteParity: DifferentialParityOutcome,\n correlation: DifferentialCorrelationOutcome,\n ) => DifferentialExchangeReport,\n): DifferentialExchangeReport {\n // Tolerant on purpose: a delivered payload can carry an `FS` byte, and the strict\n // encoder throws on one. Encoding tolerantly names that as its own deviation instead of\n // turning the peer's quirk into an exception here.\n const canonical = encodeFrame(payload, {\n allowDelimiterBytesInPayload: true,\n onWarning: (w) => {\n recordWarning(deviations, w, frameStart + 1);\n },\n });\n\n // Parity is the byte comparison, and every deviation recorded so far (decoder tolerances\n // first, then the encoder's delimiter warnings) is a reason it cannot hold. The\n // correlation code is appended below, AFTER this, so a mis-correlated acknowledgement is\n // never reported as a framing-parity failure.\n const received = Buffer.concat([...chunks]);\n const parityMatch =\n deviations.length === 0 &&\n frameStart === 0 &&\n received.length >= canonical.length &&\n received.subarray(0, canonical.length).equals(canonical);\n\n const acknowledged = extractMsaControlId(payload);\n let correlation: DifferentialCorrelationOutcome;\n if (acknowledged === null) correlation = \"absent\";\n else if (acknowledged === exchange.controlId) correlation = \"match\";\n else correlation = \"mismatch\";\n if (correlation !== \"match\") {\n deviations.push(Object.freeze({ code: ACK_CORRELATION_CODE, byteOffset: frameStart }));\n }\n\n return finish(\"answered\", parityMatch ? \"match\" : \"deviation\", correlation);\n}\n\n/** The run-level shape, derived from the exchanges rather than asserted over them. */\nfunction deriveResult(exchanges: readonly DifferentialExchangeReport[]): DifferentialRunResult {\n const answered = exchanges.filter((e) => e.outcome === \"answered\");\n if (answered.length === 0) return \"no-observation\";\n const clean =\n answered.length === exchanges.length &&\n answered.every(\n (e) => e.byteParity === \"match\" && e.correlation === \"match\" && e.deviations.length === 0,\n );\n return clean ? \"parity-observed\" : \"deviations-observed\";\n}\n\n/**\n * Run the canonical exchanges against a peer and report what was observed.\n *\n * With no peer configured the run skips cleanly and returns a report saying so, so a\n * default verification stays green on a machine that has no engine to point at. With a\n * peer configured that cannot be resolved into a host and a port, the run is refused by\n * name instead of skipped.\n *\n * The returned report is frozen and JSON-serializable, and carries no content read off the\n * peer: deviations are named by stable code and byte offset, never by quoting bytes.\n *\n * **The run sends synthetic patient messages into whatever engine it is aimed at.** Aim it\n * at a test or staging endpoint.\n *\n * @param options - Peer address, deadline, corpus, connection factory and abort signal.\n * @returns The report for the whole run, whatever each exchange did.\n * @throws MllpDifferentialConfigurationError when a peer address is configured and unusable.\n *\n * @example\n * ```typescript\n * import { runDifferential } from '@cosyte/mllp';\n * const report = await runDifferential({ peer: process.env['MLLP_DIFF_PEER'] });\n * console.log(JSON.stringify(report, null, 2));\n * ```\n */\nexport async function runDifferential(\n options: DifferentialRunOptions = {},\n): Promise<DifferentialReport> {\n const startedAt = new Date().toISOString();\n const deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;\n const exchanges = options.exchanges ?? canonicalExchanges();\n const connect = options.connect ?? connectOverTcp;\n const { signal } = options;\n\n throwIfAborted(signal);\n const peer = resolveDifferentialPeer(options.peer);\n\n if (peer === undefined) {\n return Object.freeze<DifferentialReport>({\n peer: undefined,\n result: \"skipped\",\n skipReason: \"no-peer-configured\",\n exchanges: Object.freeze([]),\n exchangesAttempted: 0,\n exchangesAnswered: 0,\n deadlineMs,\n startedAt,\n finishedAt: new Date().toISOString(),\n });\n }\n\n const settings: ExchangeSettings = {\n peer,\n connect,\n deadlineMs,\n maxFrameSizeBytes: options.maxFrameSizeBytes,\n signal,\n };\n const results: DifferentialExchangeReport[] = [];\n for (const exchange of exchanges) {\n throwIfAborted(signal);\n results.push(await runExchange(exchange, settings));\n }\n\n return Object.freeze<DifferentialReport>({\n peer: Object.freeze({ host: peer.host, port: peer.port }),\n result: deriveResult(results),\n skipReason: undefined,\n exchanges: Object.freeze([...results]),\n exchangesAttempted: results.length,\n exchangesAnswered: results.filter((e) => e.outcome === \"answered\").length,\n deadlineMs,\n startedAt,\n finishedAt: new Date().toISOString(),\n });\n}\n","/**\n * `@cosyte/mllp`, Production-grade MLLP client and server for Node.js.\n *\n * Transport-only sibling to `@cosyte/hl7`. Handles framing, ACKs, reconnects,\n * backpressure, and TLS without requiring knowledge of the MLLP spec.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n * const server = await createStarterServer({ port: 2575, onMessage: (buf) => buf });\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Package version marker exported from the `@cosyte/mllp` root.\n *\n * Kept in lockstep with `package.json` by `scripts/sync-version.mjs`, which the `version` script\n * runs immediately after `changeset version`. The `: string` annotation is deliberate, without it\n * TypeScript infers the *literal* type (`declare const VERSION = \"0.0.0\"`), which leaks the current\n * release into consumers' types and makes an equality check against any other version a compile\n * error.\n *\n * @example\n * ```typescript\n * import { VERSION } from '@cosyte/mllp';\n * console.log(VERSION);\n * ```\n */\nexport const VERSION: string = \"0.1.0\";\n\n// Phase 2: framing codec public surface\nexport type { WarningCode, MllpWarning } from \"./framing/index.js\";\nexport { MllpFramingError, encodeFrame, FrameReader, createWarning } from \"./framing/index.js\";\nexport type { FrameReaderOptions, EncoderOptions } from \"./framing/index.js\";\n\n// Phase 3: transport abstraction, connection FSM, and observability\nexport type { Transport } from \"./transport/index.js\";\nexport { NetTransport } from \"./transport/index.js\";\n\n// Phase 8: TLS / MLLPS hardening\nexport { TlsTransport } from \"./transport/index.js\";\nexport type { TlsOptions, ServerTlsOptions, ClientAuth, PemInput } from \"./transport/index.js\";\nexport {\n MLLP_TLS_VERIFY_DISABLED,\n MLLP_BIND_ALL_INTERFACES,\n type SecurityWarning,\n type SecurityWarningCode,\n} from \"./transport/index.js\";\n\n// The ATNA transport-security option: what it offers, what it reports, and how it\n// refuses a suite list it cannot honour.\nexport {\n ATNA_CIPHER_SUITES,\n TLS13_DEFAULT_CIPHER_SUITES,\n ATNA_CIPHER_LIST,\n resolveTlsCipherPolicy,\n type TlsCipherPolicyInput,\n type ResolvedTlsCipherPolicy,\n readNegotiatedTlsParameters,\n type NegotiatedTlsParameters,\n MLLP_TLS_CIPHER_LIST_REJECTED,\n MLLP_TLS_CIPHER_OPTION_CONFLICT,\n MLLP_TLS_DH_PARAMETERS_REJECTED,\n MllpTlsConfigurationError,\n tlsConfigurationMessage,\n type TlsConfigurationErrorCode,\n} from \"./transport/index.js\";\n\nexport {\n Connection,\n type ConnectionOptions,\n type ConnectionState,\n type ConnectionStats,\n type StateChangeEvent,\n type ReconnectingEvent,\n MllpConnectionError,\n type ConnectionErrorPhase,\n type ConnectionErrorCause,\n} from \"./connection/index.js\";\n\n// Phase 4: server\nexport {\n MllpServer,\n createServer,\n createStarterServer,\n type ServerOptions,\n type StarterServerOptions,\n type ServerStats,\n type MessageMeta,\n type NackEvent,\n type NackReason,\n type NackAckCode,\n} from \"./server/index.js\";\n\n// Phase 6: fail-safe ACK semantics & the commit contract\nexport {\n buildRawAck,\n rawAckUncorrelatable,\n resolveNackCode,\n MllpAckError,\n type AckCode,\n type NegativeAckCode,\n} from \"./server/index.js\";\n\n// Phase 5: client\nexport {\n MllpClient,\n createClient,\n createStarterClient,\n type ClientOptions,\n type ClientStats,\n type StarterClientOptions,\n type RetryContext,\n type RetryStrategy,\n MllpTimeoutError,\n MllpBackpressureError,\n isTransientConnectionError,\n isTlsVerificationErrorCode,\n isTlsProtocolError,\n ackDiagnosticMessage,\n type AckCorrelationCode,\n type AckCorrelationWarning,\n} from \"./client/index.js\";\n\n// Enhanced acknowledgement mode: two-phase correlation on the client, mode-correct code\n// selection on the server's auto-ACK path.\nexport {\n MllpApplicationAckError,\n MllpCommitRejectedError,\n ackModeDiagnosticMessage,\n type ApplicationAckFailure,\n type AckModeCode,\n type AckModeWarning,\n type CommitAckReport,\n} from \"./client/index.js\";\nexport type { AckModeWarningEvent } from \"./server/index.js\";\n\n// The two halves of a shutdown report: what was never written, and what was written and never\n// answered. A caller tells them apart to decide whether resending is safe.\nexport { MllpNeverDeliveredError, MllpUnknownFateError } from \"./client/index.js\";\n\n// Differential verification against a peer the caller names: the canonical exchange corpus, the\n// run that sends it, and the per-exchange parity / correlation report it returns.\nexport {\n MLLP_DIFF_PEER_UNPARSEABLE,\n MllpDifferentialConfigurationError,\n canonicalAcknowledgement,\n canonicalExchanges,\n differentialConfigurationMessage,\n resolveDifferentialPeer,\n runDifferential,\n} from \"./differential/index.js\";\nexport type {\n CanonicalExchange,\n DifferentialConfigurationErrorCode,\n DifferentialConnect,\n DifferentialCorrelationOutcome,\n DifferentialDeviation,\n DifferentialExchangeOutcome,\n DifferentialExchangeReport,\n DifferentialParityOutcome,\n DifferentialPeer,\n DifferentialReport,\n DifferentialReportPeer,\n DifferentialRunOptions,\n DifferentialRunResult,\n DifferentialSkipReason,\n} from \"./differential/index.js\";\n"]}