@cosyte/mllp 0.0.1
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.
- package/CHANGELOG.md +529 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/ack-from-hl7/index.cjs +417 -0
- package/dist/ack-from-hl7/index.cjs.map +1 -0
- package/dist/ack-from-hl7/index.d.cts +425 -0
- package/dist/ack-from-hl7/index.d.ts +425 -0
- package/dist/ack-from-hl7/index.mjs +400 -0
- package/dist/ack-from-hl7/index.mjs.map +1 -0
- package/dist/index-DjGQJtYE.d.cts +83 -0
- package/dist/index-DjGQJtYE.d.ts +83 -0
- package/dist/index.cjs +3455 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3112 -0
- package/dist/index.d.ts +3112 -0
- package/dist/index.mjs +3428 -0
- package/dist/index.mjs.map +1 -0
- package/dist/testing/index.cjs +243 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +174 -0
- package/dist/testing/index.d.ts +174 -0
- package/dist/testing/index.mjs +241 -0
- package/dist/testing/index.mjs.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +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/error.ts","../src/client/client.ts","../src/index.ts"],"names":["snippet","EventEmitter","randomUUID","s","isIP","SocketAddress","tlsCreateServer","netCreateServer","socket","createConnection","tlsConnect"],"mappings":"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","file":"index.cjs","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` (Phase 2).\n * Phase 3 `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 /** Stable human-readable description. Never contains payload bytes or secrets. */\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 Phase 3 `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 — Phase 3 enriches via\n * `{ ...w, connectionId: this.connectionId }` then re-freezes (D-08).\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 (MLLP-9):** 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 (FRAME-08). */\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 FRAME-11 DoS prevention requirement.\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 (WARN-06).\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 (MLLP-9 PHI audit).\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 Phase 4 when constructing readers per SERVER-12.\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 per WARN-06.\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 (FRAME-11 DoS prevention).\n */\n maxFrameSizeBytes?: number;\n /** FRAME-07: Tolerate FS without trailing CR; emits `MLLP_FS_WITHOUT_CR`. */\n allowFsOnly?: boolean;\n /** FRAME-08: Tolerate FS+LF instead of FS+CR; emits `MLLP_LF_AFTER_FS`. */\n allowLfAfterFs?: boolean;\n /** FRAME-09: Tolerate missing leading VT; emits `MLLP_MISSING_LEADING_VT`. */\n allowMissingLeadingVt?: boolean;\n /** FRAME-10: 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 (WARN-08):\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 (FRAME-10). */\n private _wsStart = 0;\n /** Count of leading whitespace bytes accumulated in current run (FRAME-10). */\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 (D-03).\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 (T-02-03-03).\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 (WARN-06). */\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` (Phase 8).\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 (Phase 8).\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 in Phase 3 even though `'reconnect'` is only\n * exercised in Phase 5 (CLIENT-17). Locking the full union now prevents a\n * breaking type change later (ERR-03).\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 (CLIENT-17).\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 (D-08, healthcare medication/orders semantics).\n * - `'tls-verify'` (Phase 8) — 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'` (Phase 8) — 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 from LIFE-01.\n *\n * Transitions are validated against the LIFE-02 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 * Phase 5 will populate `attempt` and `delayMs` once the reconnect backoff loop\n * is implemented. For now only `connectionId` is present at the point of emission.\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; // Phase 5 will populate\n readonly delayMs?: number; // Phase 5 will populate\n}\n\n/**\n * Return type of `connection.getStats()` — JSON-serializable per OBS-04.\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 (WARN-10). Replaces previous subscription. */\n onWarning?: (w: MllpWarning) => void;\n /** Drain timeout used by close() in Phase 4/5 (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 (OBS-05). */\nconst MAX_WARNINGS = 100;\n\n/**\n * Legal state transitions per LIFE-02.\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 (D-11). */\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 (OBS-05). */\n private _warningBuffer: MllpWarning[] = [];\n /** Accurate total counts per code — unaffected by ring buffer truncation (OBS-05). */\n private readonly _warningsByCode: Map<string, number> = new Map();\n /** Set to true once the ring buffer overflows (OBS-05). */\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 * Phase 4 (Server) and Phase 5 (Client) override this instance property to\n * register ACK-drain and send-queue drain logic respectively (D-07/D-08).\n * Connection calls this 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 (LIFE-01).\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 (WARN-10).\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 (WARN-06).\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 * Phase 4 (Server) and Phase 5 (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()` (LIFE-05).\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()` (T-03-04-01).\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 (OBS-03/04/05).\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 we hand to\n * `MLLP_ACK_UNMATCHED_CONTROL_ID` / `MLLP_ACK_AFTER_TIMEOUT` warnings and to\n * `MllpTimeoutError.messageControlId`. A masked ID is a control ID that never\n * existed on the wire — it misdirects exactly the operator who is trying to\n * trace a lost message.\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 (`MLLP-BATCH`); 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 `MllpTimeoutError.messageControlId` 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. A field that does not exist must read as absent, never as the\n * next segment's contents.\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 (`MLLP-BATCH`) —\n * until it is 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 Phase 8 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'` (Phase 8,\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 (SERVER-03).\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()` (OBS-02).\n *\n * All fields are JSON-serializable. `connections` and `activeConnections` both\n * reflect the current live connection count (ROADMAP SC-5 / OBS-02).\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 (OBS-02, ROADMAP SC-5).\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 (Phase 8). Mirrors `ServerOptions.tls` being set. */\n readonly tls: boolean;\n /** Total `'tlsClientError'` events (failed TLS handshakes, incl. rejected client certs) since listen() (Phase 8). */\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 (SERVER-12).\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 (D-03). 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 (D-10).\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 (D-11, ROADMAP SC-5).\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()` (D-06).\n * Default: 30 000 ms.\n */\n drainTimeoutMs?: number;\n\n /**\n * Enable TLS (MLLPS) for this server (Phase 8). 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 * (Phase 8). 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 (SERVER-08).\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: (buf) => buildAckBuffer(buf),\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'` (Phase 8 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)` (D-09). 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 (D-12).\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 (D-02).\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 * Phase 8 (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 (SERVER-10).\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 /** Phase 8 — single-flight guard: `true` while a `listen()` call is settling. */\n private _listenInFlight = false;\n /**\n * Phase 8 — 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 /** Phase 8 — 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'` (Phase 8 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 (D-06):\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 (OBS-02).\n *\n * `totalBytesIn` and `totalBytesOut` aggregate from live connections at call time.\n * Plan 04 adds full aggregation; at Plan 01 scope they return 0.\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 (Phase 8, ATNA\n * ITI-19 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 (D-04: 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 (D-04).\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 (D-04) — 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 * NOTE: Stub at Plan 01 scope — Plan 04 fills in the full implementation.\n *\n * @example\n * ```typescript\n * import { createStarterServer } from '@cosyte/mllp';\n *\n * const server = await createStarterServer({\n * port: 2575,\n * onMessage: (payload) => buildAck(payload),\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 (CONTEXT D-03/A1).\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 (PLAN-03) keys by MSH-10 `string`.\n * - `maxInFlight=1` (PLAN-05 `pipeline:false`) is enforced as a guard\n * on the same store; not a separate class (D-06).\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 (D-03).\n *\n * @packageDocumentation\n */\n\nimport type { WarningCode } from \"../framing/index.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 (D-03). */\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 (CLIENT-04). */\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 (D-04). */\nexport interface GraveyardEntry {\n readonly timedOutAt: number;\n readonly controlId: string | null;\n}\n\n/** JSON-serializable snapshot used by `client.getStats()` (D-26). */\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` (PLAN-06 / D-26).\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'` (PLAN-03 wires controlId). */\n readonly mode?: \"fifo\" | \"controlId\";\n /** Default `30_000` (CLIENT-04). */\n readonly ackTimeoutMs?: number;\n /** Default `Infinity`. PLAN-05 sets `1` for `pipeline:false`. */\n readonly maxInFlight?: number;\n /**\n * Emits `MLLP_ACK_AFTER_TIMEOUT` (D-04) and `MLLP_ACK_UNMATCHED_CONTROL_ID`\n * (D-05 — PLAN-03). `byteOffset` is forwarded from the inbound ACK frame\n * for observability (W-05).\n */\n readonly onWarning: (\n code: WarningCode,\n ctx: {\n controlId: string | null;\n elapsedSinceSendMs: number;\n byteOffset: number;\n },\n ) => void;\n /** Called by `matchAck()` in controlId mode (PLAN-03) on unmatched-ACK. */\n readonly onUnmatchedAck?: (controlId: string) => 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 * PLAN-06 — count of pending entries with `sentAt !== null` (D-26 / B-01).\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 — PLAN-05's `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 (CLIENT-04 — 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 * (CLIENT-16, D-04) and returns `null`; otherwise fires\n * `onUnmatchedAck(controlIdFromAck)` (CLIENT-15, D-05) and returns `null`.\n *\n * Triggers lazy graveyard eviction (D-04).\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` (W-05).\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(\"\");\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 controlId: grave.controlId,\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 if (this._opts.onUnmatchedAck !== undefined) {\n this._opts.onUnmatchedAck(controlIdFromAck);\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 (D-04).\n * Driven externally by `MllpClient`'s periodic sweep tick — Correlator\n * itself owns no timers (D-03).\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 FIFO reconnect-reject (D-07 — PLAN-04 wraps in MllpConnectionError)\n * and `MllpClient.close()` 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 (PLAN-04 reconnect-resend, D-07). */\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 PLAN-04's\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 (D-04): 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 * MLLP Client typed errors and classifiers.\n *\n * Exports:\n * - `MllpTimeoutError` (PLAN-02) — ACK timeout (ERR-02)\n * - `MllpBackpressureError` — high-water mark exceeded (ERR-04, plan 05)\n * - `isTransientConnectionError` (PLAN-04) — transient/permanent classifier (CLIENT-18)\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` (ERR-02).\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 * (CLIENT-04).\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, controlId: err.messageControlId });\n * }\n * }\n * ```\n */\nexport class MllpTimeoutError extends Error {\n override readonly name = \"MllpTimeoutError\" as const;\n\n /** MSH-10 control ID of the timed-out send (FIFO mode: `undefined`). */\n readonly messageControlId: string | 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.\n * @param opts - Timeout context (originating message control id, elapsed time, flush timestamp).\n */\n constructor(\n message: string,\n opts: {\n messageControlId: string | undefined;\n elapsedMs: number;\n sentAt: number;\n },\n ) {\n super(message);\n this.messageControlId = opts.messageControlId;\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` (Phase 8) 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 (Phase 8).\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` (Composition A\n * — see `RetryContext.classifiedAs`). Re-exported so callers can implement\n * their own retry policies.\n *\n * Classification table (CLIENT-18, D-16):\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 (Phase 8).\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 (Phase 8). 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`) (MLLP-10). 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 * (CLIENT-07, ERR-04).\n *\n * `highWaterMark` accepts a count cap, a byte cap, or both — when both are\n * present, the stricter-of-two trigger wins (D-23).\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 (D-23). */\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 * Phase 5 PLAN-01 shipped the lifecycle scaffolding — `connect()`, `close()`,\n * `destroy()`, event re-emission. PLAN-02 added `send()` + `MllpTimeoutError`.\n * The `correlateByControlId` option (MSH-10 → MSA-2 ACK matching) lights up\n * out-of-order ACK handling. Subsequent plans add auto-reconnect (Plan 04),\n * backpressure (Plan 05), and `createStarterClient` + `getStats()` (PLAN-06).\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 * // PLAN-02 will add: 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 {\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` (D-18, W-07).\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 (T-05-04-09).\n */\nconst NEVER_ABORTING_SIGNAL: AbortSignal = new AbortController().signal;\n\n/**\n * Context passed to a custom `retryStrategy` hook on each reconnect attempt\n * (CLIENT-12, D-15).\n *\n * Frozen via `Object.freeze` before invocation — handlers cannot mutate\n * (T-05-04-04 mitigation).\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 /** CLIENT-18 classification (Composition A — D-16). */\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. (D-18, W-07)\n */\n readonly signal: AbortSignal;\n}\n\n/**\n * Custom reconnect-backoff hook (CLIENT-12). Return `null` to halt\n * reconnection (D-17) — 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 (D-23).\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 * Phase 5 plans extend this incrementally — new fields are additive and optional.\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 (CLIENT-04). 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 * (CLIENT-03 controlId branch). 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 * (CLIENT-15). A late ACK whose MSA-2 matches a graveyard entry emits a\n * `MLLP_ACK_AFTER_TIMEOUT` warning (CLIENT-16) and is dropped.\n *\n * @default false\n */\n readonly correlateByControlId?: boolean;\n /**\n * Auto-reconnect on transient disconnect (CLIENT-05). 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 per D-19 unless overridden by\n * {@link ClientOptions.retryStrategy}. Permanent errors halt and\n * transition directly to `CLOSED` (Composition A — D-16).\n */\n readonly autoReconnect?: boolean;\n /**\n * Custom reconnect-backoff hook (CLIENT-12, D-15). Return `null` to halt\n * reconnection (D-17). Receives a frozen {@link RetryContext}. Defaults\n * to the exponential strategy described in D-19.\n */\n readonly retryStrategy?: RetryStrategy;\n /** First delay (ms) on auto-reconnect; default 100. (CLIENT-05, D-19) */\n readonly initialDelayMs?: number;\n /** Maximum backoff cap (ms); default 30_000. (CLIENT-05, D-19) */\n readonly maxDelayMs?: number;\n /** Backoff multiplier; default 2. (CLIENT-05, D-19) */\n readonly multiplier?: number;\n /** Jitter fraction, e.g. 0.2 = ±20%; default 0.2. (CLIENT-05, D-19) */\n readonly jitter?: number;\n /**\n * Application-level high-water mark on the in-flight + queued send set\n * (CLIENT-07, D-23). `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 (CLIENT-07).\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 (CLIENT-11).\n *\n * @default 'reject'\n */\n readonly onBackpressure?: \"reject\" | \"wait\";\n /**\n * Strict serialization send → await-ACK → send (CLIENT-19, D-06).\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} (CLIENT-08, D-11/A3).\n *\n * @default undefined (off)\n */\n readonly keepaliveIntervalMs?: number;\n /**\n * Application-idle timeout (ms) keyed on last inbound bytes / ACK / warning\n * (CLIENT-08, D-11). On trip, calls `connection.destroy(new Error('dead\n * peer timeout'))` which surfaces as `MllpConnectionError({ phase: 'receive' })`.\n * Trip honors {@link ClientOptions.autoReconnect} (D-13). Independent of\n * {@link ClientOptions.keepaliveIntervalMs} (D-11/A3).\n *\n * @default undefined (off)\n */\n readonly deadPeerTimeoutMs?: number;\n /**\n * Enable TLS (MLLPS) for this connection (Phase 8). `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} (OBS-01, D-26).\n *\n * All fields are JSON-serializable (OBS-04) — 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 per D-26.\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, B-05).\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 (B-05). 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 (W-02). */\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 (Phase 8). Mirrors `ClientOptions.tls` being set. */\n readonly tls: boolean;\n}\n\n/**\n * MLLP client — composes a single Phase 3 {@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 (D-25):\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? }` (Plan 04 populates)\n * - `'close'` — `{ connectionId }` once the FSM enters terminal `CLOSED`\n * - `'message'` — `{ payload, connectionId, byteOffset, warnings }` for every inbound frame\n * - `'warning'` — `MllpWarning` enriched with `connectionId` from the Connection layer\n * - `'securityWarning'` — `SecurityWarning` (Phase 8). Emitted on every successful\n * `secureConnect` (initial + every reconnect) when `tls.allowUnverified` is `true`\n * — 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 * // PLAN-02 will add: 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 (CLIENT-04). Resolved at construction. */\n private readonly _ackTimeoutMs: number;\n /** controlId-mode flag (CLIENT-03 branch). `false` → FIFO. */\n private readonly _correlateByControlId: boolean;\n /** Unified ACK correlator (D-03/A1). 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 per D-03. 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 * W-02 — total reconnect attempts since construction. Read by PLAN-06 for\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 W-01 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 (Plan 05 — D-11). `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 (W-07). */\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 (D-26).\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 — Phase 8) connection to the configured `host:port`\n * and attach a Phase 3 {@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 (Phase 8): 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 (Phase 8) 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 (Phase 8) 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 (D-25), even though the\n * Connection layer already freezes — defense-in-depth, harmless on\n * already-frozen objects.\n *\n * Builds the unified `Correlator` (D-03/A1) 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 this._emitContained(\n \"warning\",\n Object.freeze({\n code,\n byteOffset: ctx.byteOffset,\n message: `${code}: controlId=${ctx.controlId} elapsed=${ctx.elapsedSinceSendMs}ms`,\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n timestamp: new Date(),\n }),\n );\n },\n onUnmatchedAck: (controlId) => {\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 `Unmatched ACK control ID${controlId === \"\" ? \"\" : `: ${controlId}`}`,\n );\n safeEmitError(\n this,\n Object.freeze({\n connectionId: this._connection?.connectionId ?? conn.connectionId,\n error: err,\n controlId,\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 messageControlId: entry.controlId ?? undefined,\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 (Plan 05 — CLIENT-08, D-11).\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 (Plan 05 — D-14 timer cleanup on\n * 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 — Plan 04 reconnect FSM core.\n *\n * Implements:\n * - CLIENT-17 hybrid in-flight handling (D-08): 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 * - CLIENT-18 classifier-first (Composition A — D-16): permanent errors\n * transition straight to CLOSED without invoking `retryStrategy`.\n * - W-01 backoff-reset on recent success: first disconnect after a\n * successful ACK on the prior session resets `_attempt` to 0.\n * - W-02 `_reconnectAttempts` counter increment.\n * - D-15 frozen RetryContext + D-17 null-return halts.\n * - D-19 default exponential strategy.\n *\n * Invoked from the SINGLE `_onStateChange` hook — no parallel listener\n * (B-04). 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 (D-19): `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 (W-07).\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 at the `HOOK_EXTENSION_POINT: ack-payload`\n * anchor and passes it to `matchAck` for keyed lookup.\n *\n * Called from the SINGLE `'message'` listener registered in `_attachConnection`.\n * No parallel listener is registered — downstream plans extend at the named\n * anchor (B-04).\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 * PLAN-02: emit frozen 'ack' event, call matched.resolve().\n * Plan 04 extends at HOOK_EXTENSION_POINT: ack-matched to update _lastSuccessAt.\n * PLAN-06 extends at HOOK_EXTENSION_POINT: ack-matched to bump _ackedTotal and\n * set _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 (Plan 05 — D-24). 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 * PLAN-02: re-emit frozen 'stateChange' (was inline in PLAN-01; centralized\n * here so Plan 04 / Plan 05 can extend at named anchors).\n * Plan 04 extends at HOOK_EXTENSION_POINT: state-change to detect\n * CONNECTED → DISCONNECTED|RECONNECTING and trigger _handleDisconnect.\n * Plan 05 extends at HOOK_EXTENSION_POINT: state-change to clear/arm\n * 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 (CLIENT-02).\n *\n * Resolves with the ACK Buffer (framing stripped). Rejects with:\n * - `DOMException('Aborted', 'AbortError')` if `signal` aborts before ACK\n * (CLIENT-11 send branch).\n * - `MllpTimeoutError` if no ACK arrives within `ackTimeoutMs` (ERR-02).\n * The clock starts at the underlying `write()` flush callback, NOT at\n * the `send()` call (CLIENT-04, D-19).\n * - `MllpConnectionError({ phase: 'send' })` if the client is not connected.\n *\n * Emits a frozen `'ack'` event on every successful match (D-25 + Specifics).\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 (CLIENT-11).\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 (Plan 05 — CLIENT-07/CLIENT-11).\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` (B-06). 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 messageControlId: 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 (OBS-01, D-26).\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 * // PLAN-02 will add: 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} (PLAN-06, CLIENT-10).\n *\n * The starter applies opinionated D-22 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 (CLIENT-12). */\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 (CLIENT-08). */\n readonly keepaliveIntervalMs?: number;\n /** Application-idle dead-peer timeout ms (CLIENT-08). */\n readonly deadPeerTimeoutMs?: number;\n /** Enable TLS (MLLPS) for this connection (Phase 8). Passthrough to `ClientOptions.tls`. */\n readonly tls?: TlsOptions | true;\n /**\n * Register process SIGTERM/SIGINT handlers that close the client. Default\n * `false` (D-22). When `true`, SIGTERM/SIGINT both call `client.close()`\n * and exit the process. Handlers self-deregister on `'close'` (T-05-06-01).\n */\n readonly handleSignals?: boolean;\n}\n\n/**\n * Three-line MLLP client with batteries-included defaults (PLAN-06,\n * CLIENT-10, D-22). The returned client is already CONNECTED — `connect()`\n * has been awaited.\n *\n * D-22 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.1\";\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} from \"./client/index.js\";\n"]}
|