@cosyte/mllp 0.0.1 → 0.0.2
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 +269 -108
- package/README.md +23 -18
- package/TRADEMARKS.md +30 -0
- package/dist/ack-from-hl7/index.cjs +54 -14
- package/dist/ack-from-hl7/index.cjs.map +1 -1
- package/dist/ack-from-hl7/index.d.cts +113 -38
- package/dist/ack-from-hl7/index.d.ts +113 -38
- package/dist/ack-from-hl7/index.mjs +53 -14
- package/dist/ack-from-hl7/index.mjs.map +1 -1
- package/dist/{index-DjGQJtYE.d.cts → index-DAZ4BvJE.d.cts} +5 -5
- package/dist/{index-DjGQJtYE.d.ts → index-DAZ4BvJE.d.ts} +5 -5
- package/dist/index.cjs +90 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +239 -239
- package/dist/index.d.ts +239 -239
- package/dist/index.mjs +90 -90
- package/dist/index.mjs.map +1 -1
- package/dist/testing/index.cjs +3 -3
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +8 -8
- package/dist/testing/index.d.ts +8 -8
- package/dist/testing/index.mjs +3 -3
- package/dist/testing/index.mjs.map +1 -1
- package/package.json +15 -7
package/dist/index.cjs
CHANGED
|
@@ -27,13 +27,13 @@ var MllpFramingError = class _MllpFramingError extends Error {
|
|
|
27
27
|
/**
|
|
28
28
|
* Up to 64 bytes copied from around the anomaly.
|
|
29
29
|
*
|
|
30
|
-
* This is a **copied** Buffer
|
|
30
|
+
* This is a **copied** Buffer, isolated from the source buffer so it remains
|
|
31
31
|
* valid after the underlying buffer is reused or overwritten.
|
|
32
32
|
*
|
|
33
33
|
* **PHI contract (MLLP-9):** the decoder only ever populates this with the single
|
|
34
34
|
* framing-boundary byte that violated the frame structure (whose hex value the
|
|
35
|
-
* `message` already discloses)
|
|
36
|
-
* whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE
|
|
35
|
+
* `message` already discloses), never a run of payload content bytes. For anomalies
|
|
36
|
+
* whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE`, the accumulated size),
|
|
37
37
|
* the snippet is **empty**: a payload slice on a public error field would leak a
|
|
38
38
|
* field-body slice of clinical content. Callers constructing this directly are
|
|
39
39
|
* responsible for the same discipline.
|
|
@@ -106,7 +106,7 @@ var FrameReader = class {
|
|
|
106
106
|
_opts;
|
|
107
107
|
_maxFrameSize;
|
|
108
108
|
_state = "SCANNING_FOR_VT";
|
|
109
|
-
/** Reusable accumulator buffer
|
|
109
|
+
/** Reusable accumulator buffer, grown by doubling as needed. */
|
|
110
110
|
_accumulator = Buffer.allocUnsafe(INITIAL_ACCUMULATOR_SIZE);
|
|
111
111
|
/** Number of payload bytes written to accumulator in current frame. */
|
|
112
112
|
_writePos = 0;
|
|
@@ -118,7 +118,7 @@ var FrameReader = class {
|
|
|
118
118
|
_wsCount = 0;
|
|
119
119
|
/** Byte offset of the VT byte that started the current frame. */
|
|
120
120
|
_frameStartOffset = 0;
|
|
121
|
-
/** Per-frame warning accumulator
|
|
121
|
+
/** Per-frame warning accumulator, cleared after each _deliverFrame(). */
|
|
122
122
|
_frameWarnings = [];
|
|
123
123
|
/**
|
|
124
124
|
* Construct a chunked MLLP frame reader.
|
|
@@ -242,7 +242,7 @@ var FrameReader = class {
|
|
|
242
242
|
this._emitWarning(
|
|
243
243
|
"MLLP_MISSING_LEADING_VT",
|
|
244
244
|
this._byteOffset,
|
|
245
|
-
`Missing leading VT (0x0B)
|
|
245
|
+
`Missing leading VT (0x0B), treating byte 0x${byte.toString(16).padStart(2, "0")} at offset ${this._byteOffset} as payload start`
|
|
246
246
|
);
|
|
247
247
|
this._frameStartOffset = this._byteOffset;
|
|
248
248
|
this._state = "READING_PAYLOAD";
|
|
@@ -487,8 +487,8 @@ var TlsTransport = class {
|
|
|
487
487
|
this._socket.on("data", fn);
|
|
488
488
|
}
|
|
489
489
|
/**
|
|
490
|
-
* See {@link Transport.onConnect}. Armed on `'secureConnect'
|
|
491
|
-
* handshake-complete event
|
|
490
|
+
* See {@link Transport.onConnect}. Armed on `'secureConnect'`, the TLS
|
|
491
|
+
* handshake-complete event, not the raw TCP `'connect'` event.
|
|
492
492
|
*/
|
|
493
493
|
onConnect(fn) {
|
|
494
494
|
this._socket.removeAllListeners("secureConnect");
|
|
@@ -569,14 +569,14 @@ var Connection = class extends events.EventEmitter {
|
|
|
569
569
|
_lastByteOutAt = null;
|
|
570
570
|
/** Ring buffer: last MAX_WARNINGS entries (OBS-05). */
|
|
571
571
|
_warningBuffer = [];
|
|
572
|
-
/** Accurate total counts per code
|
|
572
|
+
/** Accurate total counts per code, unaffected by ring buffer truncation (OBS-05). */
|
|
573
573
|
_warningsByCode = /* @__PURE__ */ new Map();
|
|
574
574
|
/** Set to true once the ring buffer overflows (OBS-05). */
|
|
575
575
|
_warningsTruncated = false;
|
|
576
576
|
_onWarningFn = null;
|
|
577
577
|
_drainPromise = null;
|
|
578
578
|
/**
|
|
579
|
-
* beforeClose hook
|
|
579
|
+
* beforeClose hook, no-op default that resolves immediately.
|
|
580
580
|
*
|
|
581
581
|
* Phase 4 (Server) and Phase 5 (Client) override this instance property to
|
|
582
582
|
* register ACK-drain and send-queue drain logic respectively (D-07/D-08).
|
|
@@ -760,7 +760,7 @@ var Connection = class extends events.EventEmitter {
|
|
|
760
760
|
* Abruptly destroy the connection, discarding any pending writes.
|
|
761
761
|
*
|
|
762
762
|
* Transitions from any non-terminal state directly to `CLOSED` and calls
|
|
763
|
-
* `transport.destroy(reason)`. Emits `'close'` event. Idempotent
|
|
763
|
+
* `transport.destroy(reason)`. Emits `'close'` event. Idempotent, safe to
|
|
764
764
|
* call multiple times.
|
|
765
765
|
*
|
|
766
766
|
* @param reason - Optional error to propagate to the transport.
|
|
@@ -860,12 +860,12 @@ var Connection = class extends events.EventEmitter {
|
|
|
860
860
|
* Node throws `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an `EventEmitter` with no
|
|
861
861
|
* listener. Every `'error'` emission in this class is reached from inside a transport callback
|
|
862
862
|
* (the socket's `'data'`/`'error'` listener), so an unguarded emit would become an **uncaught
|
|
863
|
-
* exception that kills the process
|
|
863
|
+
* exception that kills the process**, the exact failure the containment above exists to
|
|
864
864
|
* prevent, merely relocated one frame up the stack.
|
|
865
865
|
*
|
|
866
866
|
* `Connection` is a public export and can legitimately be driven with no `'error'` listener
|
|
867
867
|
* (`MllpServer` and `MllpClient` each attach one; a bare `Connection` need not). The connection
|
|
868
|
-
* is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind
|
|
868
|
+
* is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind,
|
|
869
869
|
* they simply do not get the typed error object they never asked for. Mirrors
|
|
870
870
|
* `MllpServer._emitErrorIfListened`.
|
|
871
871
|
*/
|
|
@@ -884,7 +884,7 @@ var Connection = class extends events.EventEmitter {
|
|
|
884
884
|
* consumers keep the stable `code` and `byteOffset`), then **destroy this connection only**.
|
|
885
885
|
*
|
|
886
886
|
* Why destroy rather than resynchronize: once `push` has thrown, the reader's position within
|
|
887
|
-
* the byte stream is no longer trustworthy
|
|
887
|
+
* the byte stream is no longer trustworthy, the next bytes could be the middle of a message.
|
|
888
888
|
* Guessing where the next frame starts is how a clinical message gets silently mis-split, so
|
|
889
889
|
* the connection is dropped instead. A server drops that one peer and keeps serving everyone
|
|
890
890
|
* else.
|
|
@@ -896,7 +896,7 @@ var Connection = class extends events.EventEmitter {
|
|
|
896
896
|
* engine that is already unwell. A compatibility failure is not a network blip.
|
|
897
897
|
*
|
|
898
898
|
* If a peer's quirk is *expected* (trailing junk, keepalive bytes, a missing leading VT), the
|
|
899
|
-
* decoder's tolerance opt-ins are the supported answer
|
|
899
|
+
* decoder's tolerance opt-ins are the supported answer, they turn the throw into a warning.
|
|
900
900
|
*/
|
|
901
901
|
_onFatalFramingError(err) {
|
|
902
902
|
if (this._state === "CLOSED") return;
|
|
@@ -910,13 +910,13 @@ var Connection = class extends events.EventEmitter {
|
|
|
910
910
|
this.destroy(err);
|
|
911
911
|
}
|
|
912
912
|
/**
|
|
913
|
-
* Backstop for a NON-framing throw escaping `FrameReader.push
|
|
913
|
+
* Backstop for a NON-framing throw escaping `FrameReader.push`, i.e. our own bug, or a
|
|
914
914
|
* subscriber's, rather than the peer's bytes.
|
|
915
915
|
*
|
|
916
916
|
* `_onFrameDecoded` already contains subscriber throws at the dispatch site, so this should be
|
|
917
917
|
* unreachable. It exists because "unreachable" and "cannot kill the process from inside a
|
|
918
918
|
* socket `'data'` handler" are different claims, and only the second one is safe to bet a
|
|
919
|
-
* clinical interface on. Reported honestly as an internal receive error
|
|
919
|
+
* clinical interface on. Reported honestly as an internal receive error, never dressed up as
|
|
920
920
|
* a peer framing fault.
|
|
921
921
|
*/
|
|
922
922
|
_onInternalReceiveError(err) {
|
|
@@ -992,9 +992,9 @@ var UNSAFE_FIELD_SEPARATORS = /* @__PURE__ */ new Set([
|
|
|
992
992
|
SEGMENT_SEPARATOR_CR,
|
|
993
993
|
SEGMENT_SEPARATOR_LF,
|
|
994
994
|
11,
|
|
995
|
-
// VT
|
|
995
|
+
// VT, MLLP start block
|
|
996
996
|
28
|
|
997
|
-
// FS
|
|
997
|
+
// FS, MLLP end block
|
|
998
998
|
]);
|
|
999
999
|
function isMshSegmentAt(buf, i) {
|
|
1000
1000
|
return i + 3 < buf.length && buf[i] === ASCII_M && buf[i + 1] === ASCII_S && buf[i + 2] === ASCII_H && !UNSAFE_FIELD_SEPARATORS.has(buf[i + 3]);
|
|
@@ -1227,13 +1227,13 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1227
1227
|
_netServer;
|
|
1228
1228
|
_connections = /* @__PURE__ */ new Set();
|
|
1229
1229
|
_opts;
|
|
1230
|
-
/** `true` when `ServerOptions.tls` is set
|
|
1230
|
+
/** `true` when `ServerOptions.tls` is set, `_netServer` is actually a `tls.Server`. */
|
|
1231
1231
|
_isTls;
|
|
1232
1232
|
_listening = false;
|
|
1233
|
-
/** Phase 8
|
|
1233
|
+
/** Phase 8, single-flight guard: `true` while a `listen()` call is settling. */
|
|
1234
1234
|
_listenInFlight = false;
|
|
1235
1235
|
/**
|
|
1236
|
-
* Phase 8
|
|
1236
|
+
* Phase 8, settle hook for an in-flight `listen()`. Non-null only while a
|
|
1237
1237
|
* `listen()` is in flight; invoked by `close()` so a close racing startup
|
|
1238
1238
|
* rejects the pending `listen()` (typed) instead of leaving it hung with
|
|
1239
1239
|
* the single-flight guard stuck. Cleared on every settle path.
|
|
@@ -1243,7 +1243,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1243
1243
|
_host = null;
|
|
1244
1244
|
_acceptedTotal = 0;
|
|
1245
1245
|
_closedTotal = 0;
|
|
1246
|
-
/** Phase 8
|
|
1246
|
+
/** Phase 8, count of `'tlsClientError'` events since listen(). */
|
|
1247
1247
|
_tlsClientErrorsTotal = 0;
|
|
1248
1248
|
/**
|
|
1249
1249
|
* Construct an MLLP server. Created idle; call `listen()` (or use
|
|
@@ -1306,23 +1306,23 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1306
1306
|
* `Object.freeze({ port: actualPort, host: actualHost })`.
|
|
1307
1307
|
*
|
|
1308
1308
|
* **Single-flight:** one `listen()` per server lifecycle at a time. A call
|
|
1309
|
-
* while the server is already listening
|
|
1310
|
-
* still in flight
|
|
1309
|
+
* while the server is already listening, or while another `listen()` is
|
|
1310
|
+
* still in flight, rejects with a typed `MllpConnectionError` (concurrent
|
|
1311
1311
|
* binds raced each other's post-bind safety checks). Call `close()` before
|
|
1312
1312
|
* re-listening; sequential `listen()` → `close()` → `listen()` is fine.
|
|
1313
1313
|
*
|
|
1314
1314
|
* **`close()` during an in-flight `listen()`** rejects that `listen()` with
|
|
1315
1315
|
* a typed `MllpConnectionError` (never a hang) and clears the single-flight
|
|
1316
|
-
* guard
|
|
1316
|
+
* guard, a subsequent `listen()` on the same server works. This makes
|
|
1317
1317
|
* `Symbol.asyncDispose` (which delegates to `close()`) safe even before a
|
|
1318
1318
|
* `listen()` has settled. (Exception: `close({ signal })` with an
|
|
1319
|
-
* already-aborted signal is a no-op AbortError rejection
|
|
1319
|
+
* already-aborted signal is a no-op AbortError rejection, the in-flight
|
|
1320
1320
|
* `listen()` is left to settle on its own bind outcome; see `close()`.)
|
|
1321
1321
|
*
|
|
1322
1322
|
* @param port - TCP port to bind. Use `0` to let the OS assign an ephemeral port.
|
|
1323
1323
|
* @param hostOrOpts - Host string or object with `host` and optional `signal`.
|
|
1324
1324
|
* Default host: `'127.0.0.1'` (Phase 8 bind-safety hardening). Wildcard hosts
|
|
1325
|
-
* are rejected unless `ServerOptions.allowWildcardBind: true
|
|
1325
|
+
* are rejected unless `ServerOptions.allowWildcardBind: true`, literal
|
|
1326
1326
|
* spellings pre-bind, resolver-only shorthands post-bind via the
|
|
1327
1327
|
* OS-normalized bound address.
|
|
1328
1328
|
*
|
|
@@ -1342,7 +1342,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1342
1342
|
if (this._listening || this._listenInFlight) {
|
|
1343
1343
|
return Promise.reject(
|
|
1344
1344
|
new MllpConnectionError(
|
|
1345
|
-
this._listening ? "listen() rejected: server is already listening
|
|
1345
|
+
this._listening ? "listen() rejected: server is already listening, call close() before re-listening" : "listen() rejected: another listen() is already in flight on this server",
|
|
1346
1346
|
{
|
|
1347
1347
|
cause: new Error("listen already listening or in flight"),
|
|
1348
1348
|
phase: "connect"
|
|
@@ -1353,7 +1353,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1353
1353
|
if (isWildcardHost(host) && this._opts.allowWildcardBind !== true) {
|
|
1354
1354
|
return Promise.reject(
|
|
1355
1355
|
new MllpConnectionError(
|
|
1356
|
-
`refusing to bind wildcard host '${host}'
|
|
1356
|
+
`refusing to bind wildcard host '${host}', set ServerOptions.allowWildcardBind: true to bind all interfaces`,
|
|
1357
1357
|
{
|
|
1358
1358
|
cause: new Error("wildcard bind requires allowWildcardBind"),
|
|
1359
1359
|
phase: "connect"
|
|
@@ -1432,7 +1432,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1432
1432
|
{
|
|
1433
1433
|
ok: false,
|
|
1434
1434
|
error: new MllpConnectionError(
|
|
1435
|
-
`refusing to bind wildcard host '${host}' (bound address '${actualHost}')
|
|
1435
|
+
`refusing to bind wildcard host '${host}' (bound address '${actualHost}'), set ServerOptions.allowWildcardBind: true to bind all interfaces`,
|
|
1436
1436
|
{
|
|
1437
1437
|
cause: new Error("wildcard bind requires allowWildcardBind"),
|
|
1438
1438
|
phase: "connect"
|
|
@@ -1459,7 +1459,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1459
1459
|
}
|
|
1460
1460
|
};
|
|
1461
1461
|
const wildcardBound = isWildcardHost(actualHost);
|
|
1462
|
-
const wildcardMessage = `MLLP server is bound to ALL network interfaces (host='${actualHost}')
|
|
1462
|
+
const wildcardMessage = `MLLP server is bound to ALL network interfaces (host='${actualHost}'), set a specific bind address unless this is intentional.`;
|
|
1463
1463
|
if (wildcardBound) {
|
|
1464
1464
|
process.emitWarning(wildcardMessage, { code: MLLP_BIND_ALL_INTERFACES });
|
|
1465
1465
|
}
|
|
@@ -1488,18 +1488,18 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1488
1488
|
* Stop accepting new connections and gracefully close all active connections.
|
|
1489
1489
|
*
|
|
1490
1490
|
* Sequence (D-06):
|
|
1491
|
-
* 1. `net.Server.close()
|
|
1491
|
+
* 1. `net.Server.close()`, stops accepting new connections immediately
|
|
1492
1492
|
* 2. If `_connections` is empty: resolves immediately (no drain needed)
|
|
1493
|
-
* 3. Calls `_drainAll(drainTimeoutMs)
|
|
1493
|
+
* 3. Calls `_drainAll(drainTimeoutMs)`, Promise.all + side-effect setTimeout
|
|
1494
1494
|
* that force-destroys stragglers after the drain window
|
|
1495
1495
|
*
|
|
1496
1496
|
* Calling `close()` while a `listen()` is still in flight proactively
|
|
1497
1497
|
* settles that `listen()` with a typed `MllpConnectionError` rejection
|
|
1498
|
-
* and clears the single-flight guard
|
|
1498
|
+
* and clears the single-flight guard, the server is immediately
|
|
1499
1499
|
* re-listenable. `Symbol.asyncDispose` (which delegates here) is
|
|
1500
1500
|
* therefore safe before `listen()` settles. **Qualification:** a
|
|
1501
1501
|
* `close({ signal })` whose signal is **already aborted** rejects
|
|
1502
|
-
* immediately with `AbortError` and performs no work
|
|
1502
|
+
* immediately with `AbortError` and performs no work, it does NOT close
|
|
1503
1503
|
* the server and does NOT settle the in-flight `listen()`, which simply
|
|
1504
1504
|
* continues and settles on its own bind outcome.
|
|
1505
1505
|
*
|
|
@@ -1551,7 +1551,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1551
1551
|
/**
|
|
1552
1552
|
* Drain all active connections with a shared timeout.
|
|
1553
1553
|
*
|
|
1554
|
-
* Uses Promise.all (not Promise.race) for coordination
|
|
1554
|
+
* Uses Promise.all (not Promise.race) for coordination, the timeout is a
|
|
1555
1555
|
* side effect that calls `conn.destroy()` on stragglers after `drainTimeoutMs`.
|
|
1556
1556
|
* Promise.all resolves when all `conn.close()` promises settle (which happens
|
|
1557
1557
|
* because `conn.destroy()` transitions connections to CLOSED).
|
|
@@ -1574,7 +1574,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1574
1574
|
}
|
|
1575
1575
|
}
|
|
1576
1576
|
/**
|
|
1577
|
-
* Async disposal
|
|
1577
|
+
* Async disposal, delegates to `close()` for `await using` support.
|
|
1578
1578
|
*
|
|
1579
1579
|
* @example
|
|
1580
1580
|
* ```typescript
|
|
@@ -1621,7 +1621,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1621
1621
|
};
|
|
1622
1622
|
}
|
|
1623
1623
|
/**
|
|
1624
|
-
* Emit on `'error'` only when a listener is attached
|
|
1624
|
+
* Emit on `'error'` only when a listener is attached, an unlistened
|
|
1625
1625
|
* `EventEmitter` `'error'` emission throws (see the class JSDoc error
|
|
1626
1626
|
* contract). The single guard shared by the optional-tap forwarding sites
|
|
1627
1627
|
* in this class; the constructor's net.Server forwarder deliberately does
|
|
@@ -1631,7 +1631,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1631
1631
|
/**
|
|
1632
1632
|
* Emit a server event, containing a throwing subscriber.
|
|
1633
1633
|
*
|
|
1634
|
-
* Every event this server emits is reachable from a callback we do not own
|
|
1634
|
+
* Every event this server emits is reachable from a callback we do not own, the `net.Server`
|
|
1635
1635
|
* `'connection'` listener, the `tls.Server` `'tlsClientError'` listener, a socket's `'data'`
|
|
1636
1636
|
* listener (via `Connection`), or the `catch` block of a `void`-ed async ACK task. A throwing
|
|
1637
1637
|
* subscriber would therefore unwind into that callback and kill the process (or reject a void-ed
|
|
@@ -1737,7 +1737,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1737
1737
|
}
|
|
1738
1738
|
/**
|
|
1739
1739
|
* Extract a minimal, content-free peer-certificate summary (Phase 8, ATNA
|
|
1740
|
-
* ITI-19 mutual authentication)
|
|
1740
|
+
* ITI-19 mutual authentication), CN strings, expiry, and the verification
|
|
1741
1741
|
* outcome (`authorized`) only; never the full certificate object. Returns
|
|
1742
1742
|
* `null` when no peer certificate was presented (e.g. `clientAuth: 'WANT'`
|
|
1743
1743
|
* with no client cert).
|
|
@@ -1755,7 +1755,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1755
1755
|
subjectCN: asCn(cert.subject?.CN),
|
|
1756
1756
|
issuerCN: asCn(cert.issuer?.CN),
|
|
1757
1757
|
validTo: cert.valid_to ?? null,
|
|
1758
|
-
// Verification outcome
|
|
1758
|
+
// Verification outcome, under 'WANT' a certificate can be present yet
|
|
1759
1759
|
// UNVERIFIED (see PeerCertificateSummary.authorized JSDoc).
|
|
1760
1760
|
authorized: socket.authorized === true
|
|
1761
1761
|
});
|
|
@@ -1767,12 +1767,12 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1767
1767
|
* then dispatches: resolve ⇒ `AA`; throw/reject ⇒ a negative ACK (`AE` by default,
|
|
1768
1768
|
* `AR` via {@link MllpAckError}). A positive ACK can never precede a successful commit.
|
|
1769
1769
|
*
|
|
1770
|
-
* Without an `onMessage` handler, `'AA'` degrades to a **transport-accept
|
|
1770
|
+
* Without an `onMessage` handler, `'AA'` degrades to a **transport-accept**, `AA`
|
|
1771
1771
|
* meaning "bytes received and framed", not "application-processed".
|
|
1772
1772
|
*
|
|
1773
1773
|
* A handler failure is **expected flow**, not a server error: it produces a negative
|
|
1774
1774
|
* ACK and a `'nack'` event. The thrown error's message is never placed on the wire or
|
|
1775
|
-
* in the event (it may carry PHI)
|
|
1775
|
+
* in the event (it may carry PHI), only the static `ackCode` and the inbound control
|
|
1776
1776
|
* metadata reach the peer.
|
|
1777
1777
|
*/
|
|
1778
1778
|
async _sendCommitAck(payload, meta, conn) {
|
|
@@ -1804,18 +1804,18 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1804
1804
|
}
|
|
1805
1805
|
/**
|
|
1806
1806
|
* Resolve the auto-ACK code for a message the server would otherwise acknowledge
|
|
1807
|
-
* **positively** (`AA`)
|
|
1807
|
+
* **positively** (`AA`), the no-handler transport-accept and the post-commit success
|
|
1808
1808
|
* paths. Fail-safe: downgrade to `AE` and emit a PHI-safe `'nack'` whenever the message
|
|
1809
1809
|
* cannot carry a correlatable positive ACK, so `AA` never names a control ID the sender
|
|
1810
1810
|
* cannot match (→ timeout → resend → **duplicate clinical message**).
|
|
1811
1811
|
*
|
|
1812
1812
|
* Two independent disqualifiers, one from the bytes and one from the frame:
|
|
1813
1813
|
*
|
|
1814
|
-
* - **`rawAckUncorrelatable(payload)
|
|
1814
|
+
* - **`rawAckUncorrelatable(payload)`**, no readable `MSH`, an empty MSH-10, or a
|
|
1815
1815
|
* batch/concatenated-message shape. This is the SAME predicate `buildRawAck` enforces
|
|
1816
1816
|
* on the wire; it is re-checked here so the downgrade is **observable** (a `'nack'`
|
|
1817
1817
|
* event) rather than a silent code change inside the builder.
|
|
1818
|
-
* - **`MLLP_TRAILING_BYTES`** in the frame's warnings
|
|
1818
|
+
* - **`MLLP_TRAILING_BYTES`** in the frame's warnings, a mid-payload `VT` made the
|
|
1819
1819
|
* decoder discard accumulated bytes and deliver only the fragment after it. The
|
|
1820
1820
|
* clinical message was destroyed in transit, so even a fragment that happens to carry
|
|
1821
1821
|
* a readable MSH must not be positively acknowledged. This condition is invisible to
|
|
@@ -1824,7 +1824,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1824
1824
|
* accumulating the delivered frame, so it is frame-scoped: it names *this* payload's own
|
|
1825
1825
|
* discard, never a neighbouring frame's trailing junk.
|
|
1826
1826
|
*
|
|
1827
|
-
* `'discarded-bytes'` takes precedence in the reason when both hold
|
|
1827
|
+
* `'discarded-bytes'` takes precedence in the reason when both hold, it is the root cause.
|
|
1828
1828
|
*/
|
|
1829
1829
|
_resolveAutoAckPositive(payload, meta, conn) {
|
|
1830
1830
|
const discarded = meta.warnings.some((w) => w.code === "MLLP_TRAILING_BYTES");
|
|
@@ -1859,7 +1859,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1859
1859
|
/**
|
|
1860
1860
|
* The custom-builder path for `autoAck: fn`. Awaits the builder and dispatches its
|
|
1861
1861
|
* Buffer as the ACK; the caller owns MSA-1. A builder error is re-emitted as `'error'`
|
|
1862
|
-
* on the connection
|
|
1862
|
+
* on the connection, the server never crashes (D-04).
|
|
1863
1863
|
*/
|
|
1864
1864
|
async _sendCustomAck(fn, payload, meta, conn) {
|
|
1865
1865
|
try {
|
|
@@ -1873,7 +1873,7 @@ var MllpServer = class extends events.EventEmitter {
|
|
|
1873
1873
|
/**
|
|
1874
1874
|
* Frame an ACK payload and write it to the connection. `Connection.send()` returns
|
|
1875
1875
|
* `false` under backpressure (socket write buffer full); that drops the ACK and emits
|
|
1876
|
-
* a `MllpConnectionError({ phase: 'send' })` on the connection (D-04)
|
|
1876
|
+
* a `MllpConnectionError({ phase: 'send' })` on the connection (D-04), the peer will
|
|
1877
1877
|
* time out waiting and may retry. The server never crashes.
|
|
1878
1878
|
*/
|
|
1879
1879
|
_dispatchAck(conn, ackPayload) {
|
|
@@ -1938,7 +1938,7 @@ var Correlator = class {
|
|
|
1938
1938
|
_sendSeq = 0;
|
|
1939
1939
|
_queueBytes = 0;
|
|
1940
1940
|
/**
|
|
1941
|
-
* PLAN-06
|
|
1941
|
+
* PLAN-06, count of pending entries with `sentAt !== null` (D-26 / B-01).
|
|
1942
1942
|
* Maintained at every site that mutates `entry.sentAt` or removes a
|
|
1943
1943
|
* flushed entry: `markFlushed`, `remove`, `matchAck`, `expireDue`, `clear`.
|
|
1944
1944
|
*/
|
|
@@ -1974,7 +1974,7 @@ var Correlator = class {
|
|
|
1974
1974
|
/**
|
|
1975
1975
|
* Enqueue a new send awaiting its ACK. Returns the assigned
|
|
1976
1976
|
* `correlationKey`, or `null` if `maxInFlight` is reached (caller awaits
|
|
1977
|
-
* drain
|
|
1977
|
+
* drain, PLAN-05's `pipeline:false`).
|
|
1978
1978
|
*/
|
|
1979
1979
|
enqueue(frame, controlIdOrNull, resolve, reject) {
|
|
1980
1980
|
if (this._pending.size >= this._opts.maxInFlight) return null;
|
|
@@ -1993,7 +1993,7 @@ var Correlator = class {
|
|
|
1993
1993
|
return key;
|
|
1994
1994
|
}
|
|
1995
1995
|
/**
|
|
1996
|
-
* Record write-flush timestamp (CLIENT-04
|
|
1996
|
+
* Record write-flush timestamp (CLIENT-04, clock starts at flush, NOT
|
|
1997
1997
|
* at `send()` call). No-op if key is unknown (e.g. removed by abort).
|
|
1998
1998
|
*/
|
|
1999
1999
|
markFlushed(key, now) {
|
|
@@ -2063,7 +2063,7 @@ var Correlator = class {
|
|
|
2063
2063
|
/**
|
|
2064
2064
|
* Sweep live entries; expire those past `sentAt + ackTimeoutMs`.
|
|
2065
2065
|
* Fires `onTimeout(entry, elapsedMs)`; entries move to graveyard (D-04).
|
|
2066
|
-
* Driven externally by `MllpClient`'s periodic sweep tick
|
|
2066
|
+
* Driven externally by `MllpClient`'s periodic sweep tick, Correlator
|
|
2067
2067
|
* itself owns no timers (D-03).
|
|
2068
2068
|
*/
|
|
2069
2069
|
expireDue(now) {
|
|
@@ -2086,7 +2086,7 @@ var Correlator = class {
|
|
|
2086
2086
|
/**
|
|
2087
2087
|
* Reject every live entry with `reason` (insertion order) and clear
|
|
2088
2088
|
* the live store. Graveyard is left intact (ages out via lazy eviction).
|
|
2089
|
-
* Used by FIFO reconnect-reject (D-07
|
|
2089
|
+
* Used by FIFO reconnect-reject (D-07, PLAN-04 wraps in MllpConnectionError)
|
|
2090
2090
|
* and `MllpClient.close()` to cancel pending sends.
|
|
2091
2091
|
*/
|
|
2092
2092
|
clear(reason) {
|
|
@@ -2267,7 +2267,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2267
2267
|
/** 0-indexed attempt counter for the current reconnect cycle. */
|
|
2268
2268
|
_attempt = 0;
|
|
2269
2269
|
/**
|
|
2270
|
-
* W-02
|
|
2270
|
+
* W-02, total reconnect attempts since construction. Read by PLAN-06 for
|
|
2271
2271
|
* `getStats().reconnectAttempts`. Incremented at the entry of every
|
|
2272
2272
|
* `_handleDisconnect` invocation that proceeds to schedule a backoff.
|
|
2273
2273
|
*/
|
|
@@ -2289,7 +2289,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2289
2289
|
_onBackpressure;
|
|
2290
2290
|
/** Pipeline flag; default `true` (parallel up to highWaterMark). */
|
|
2291
2291
|
_pipeline;
|
|
2292
|
-
/** Dead-peer idle timer (Plan 05
|
|
2292
|
+
/** Dead-peer idle timer (Plan 05, D-11). `null` when not armed. */
|
|
2293
2293
|
_deadPeerTimer = null;
|
|
2294
2294
|
/** True once the self-'ack' listener that resets the dead-peer timer
|
|
2295
2295
|
* has been attached. Guards against duplicate listeners on reconnect. */
|
|
@@ -2303,13 +2303,13 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2303
2303
|
/** Listener-removal handle for the abort listener bound in connect(). */
|
|
2304
2304
|
_abortListener = null;
|
|
2305
2305
|
/**
|
|
2306
|
-
* Test-only seam
|
|
2306
|
+
* Test-only seam, when set, `_beginReconnectAttempt` builds the new
|
|
2307
2307
|
* Connection through this factory instead of opening a real net.Socket.
|
|
2308
2308
|
*
|
|
2309
2309
|
* @internal
|
|
2310
2310
|
*/
|
|
2311
2311
|
_reconnectFactory = null;
|
|
2312
|
-
// ── PLAN-06
|
|
2312
|
+
// ── PLAN-06, observability counters for getStats (OBS-01, D-26) ─────────
|
|
2313
2313
|
/** Total successful `conn.send()` flushes since construction. */
|
|
2314
2314
|
_sentTotal = 0;
|
|
2315
2315
|
/** Total ACKs resolved since construction. */
|
|
@@ -2363,9 +2363,9 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2363
2363
|
return this._connection?.state ?? this._state;
|
|
2364
2364
|
}
|
|
2365
2365
|
/**
|
|
2366
|
-
* Open a TCP (or TLS
|
|
2366
|
+
* Open a TCP (or TLS, Phase 8) connection to the configured `host:port`
|
|
2367
2367
|
* and attach a Phase 3 {@link Connection} to it. Resolves once the FSM
|
|
2368
|
-
* enters `CONNECTED
|
|
2368
|
+
* enters `CONNECTED`, for TLS, on `'secureConnect'` (handshake complete,
|
|
2369
2369
|
* including certificate verification when it is on).
|
|
2370
2370
|
*
|
|
2371
2371
|
* Rejects with:
|
|
@@ -2385,11 +2385,11 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2385
2385
|
*
|
|
2386
2386
|
* **TLS 1.3 + mutual TLS caveat (RFC 8446 §4.4.2):** `connect()` resolving
|
|
2387
2387
|
* does NOT guarantee that a `clientAuth: 'MUST'` server accepted your
|
|
2388
|
-
* client certificate. Under TLS 1.3 the client's handshake
|
|
2389
|
-
* `'secureConnect'
|
|
2388
|
+
* client certificate. Under TLS 1.3 the client's handshake, and its
|
|
2389
|
+
* `'secureConnect'`, can complete before the server finishes validating
|
|
2390
2390
|
* the certificate; a rejection then surfaces moments later as a typed
|
|
2391
2391
|
* post-connect error (`'error'` event with an `ERR_SSL_*`/alert cause,
|
|
2392
|
-
* classified **permanent
|
|
2392
|
+
* classified **permanent**, no auto-reconnect loop). ACK correlation via
|
|
2393
2393
|
* {@link MllpClient.send} remains the delivery guarantee: no send resolves
|
|
2394
2394
|
* without its ACK, so a rejected session can never silently "deliver".
|
|
2395
2395
|
*
|
|
@@ -2473,7 +2473,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2473
2473
|
*
|
|
2474
2474
|
* Plaintext (`ClientOptions.tls` unset): a `net.Socket` wrapped in
|
|
2475
2475
|
* `NetTransport`. TLS (Phase 8): a `tls.TLSSocket` wrapped in
|
|
2476
|
-
* `TlsTransport
|
|
2476
|
+
* `TlsTransport`, verification defaults **on**
|
|
2477
2477
|
* (`rejectUnauthorized: !allowUnverified`), floor `minVersion: 'TLSv1.2'`
|
|
2478
2478
|
* (the IHE ATNA ITI-19 BCP195 floor), `servername` defaulting to
|
|
2479
2479
|
* `ClientOptions.host`.
|
|
@@ -2504,12 +2504,12 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2504
2504
|
* Wrap a connect-phase socket error as `MllpConnectionError`, classifying
|
|
2505
2505
|
* TLS failures (Phase 8) into the additive `connectionCause`:
|
|
2506
2506
|
*
|
|
2507
|
-
* - `'tls-verify'
|
|
2507
|
+
* - `'tls-verify'`, certificate-verification failures
|
|
2508
2508
|
* ({@link isTlsVerificationErrorCode}).
|
|
2509
|
-
* - `'tls-handshake'
|
|
2509
|
+
* - `'tls-handshake'`, TLS-**protocol**-shaped failures only
|
|
2510
2510
|
* ({@link isTlsProtocolError}): `ERR_SSL_*`, `EPROTO`, OpenSSL
|
|
2511
2511
|
* alert-bearing errors.
|
|
2512
|
-
* - **No `connectionCause
|
|
2512
|
+
* - **No `connectionCause`**, pure TCP-level failures (`ECONNREFUSED`,
|
|
2513
2513
|
* `ETIMEDOUT`, …) even on a TLS-configured connection; these carry the
|
|
2514
2514
|
* same shape as plaintext connect failures.
|
|
2515
2515
|
*/
|
|
@@ -2537,7 +2537,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2537
2537
|
/**
|
|
2538
2538
|
* Emit a client event, containing a throwing subscriber.
|
|
2539
2539
|
*
|
|
2540
|
-
* Every event this client emits is reached from a callback we do not own
|
|
2540
|
+
* Every event this client emits is reached from a callback we do not own, a socket's
|
|
2541
2541
|
* `'connect'`/`'secureConnect'`/`'data'`/`'error'` listener, or a backoff timer. A throwing
|
|
2542
2542
|
* subscriber would unwind into it and kill the process, and on several paths it would also skip
|
|
2543
2543
|
* the work queued after the emit (the `resolve()` of `connect()`, the backoff scheduling).
|
|
@@ -2551,7 +2551,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2551
2551
|
}
|
|
2552
2552
|
/**
|
|
2553
2553
|
* Emit the per-connection insecure-TLS warning (Phase 8) when
|
|
2554
|
-
* `tls.allowUnverified === true
|
|
2554
|
+
* `tls.allowUnverified === true`, fires on EVERY successful
|
|
2555
2555
|
* `secureConnect`, initial connect and every reconnect. Emits both a frozen
|
|
2556
2556
|
* `'securityWarning'` event and `process.emitWarning`. No-op for plaintext
|
|
2557
2557
|
* connections or when verification is on.
|
|
@@ -2560,7 +2560,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2560
2560
|
const tlsOpt = this._opts.tls;
|
|
2561
2561
|
if (tlsOpt === void 0 || tlsOpt === true) return;
|
|
2562
2562
|
if (tlsOpt.allowUnverified !== true) return;
|
|
2563
|
-
const message = "MLLP TLS certificate verification is DISABLED (allowUnverified: true)
|
|
2563
|
+
const message = "MLLP TLS certificate verification is DISABLED (allowUnverified: true), this connection does not authenticate the peer.";
|
|
2564
2564
|
const warning = Object.freeze({
|
|
2565
2565
|
code: MLLP_TLS_VERIFY_DISABLED,
|
|
2566
2566
|
message,
|
|
@@ -2574,7 +2574,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2574
2574
|
/**
|
|
2575
2575
|
* Wire a Connection's events through to this MllpClient. Every re-emitted
|
|
2576
2576
|
* payload is `Object.freeze`'d before emission (D-25), even though the
|
|
2577
|
-
* Connection layer already freezes
|
|
2577
|
+
* Connection layer already freezes, defense-in-depth, harmless on
|
|
2578
2578
|
* already-frozen objects.
|
|
2579
2579
|
*
|
|
2580
2580
|
* Builds the unified `Correlator` (D-03/A1) bound to this Connection and
|
|
@@ -2588,7 +2588,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2588
2588
|
this._correlator = new Correlator({
|
|
2589
2589
|
mode: this._correlateByControlId ? "controlId" : "fifo",
|
|
2590
2590
|
ackTimeoutMs: this._ackTimeoutMs,
|
|
2591
|
-
// Plan 05
|
|
2591
|
+
// Plan 05, pipeline:false collapses the in-flight set to ≤1 (D-06).
|
|
2592
2592
|
maxInFlight: this._pipeline ? Number.POSITIVE_INFINITY : 1,
|
|
2593
2593
|
onWarning: (code, ctx) => {
|
|
2594
2594
|
this._aggregatedWarningsByCode[code] = (this._aggregatedWarningsByCode[code] ?? 0) + 1;
|
|
@@ -2687,7 +2687,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2687
2687
|
}
|
|
2688
2688
|
}
|
|
2689
2689
|
/**
|
|
2690
|
-
* Arm (or re-arm) the dead-peer idle timer (Plan 05
|
|
2690
|
+
* Arm (or re-arm) the dead-peer idle timer (Plan 05, CLIENT-08, D-11).
|
|
2691
2691
|
* No-op when `deadPeerTimeoutMs` is unset.
|
|
2692
2692
|
*/
|
|
2693
2693
|
_armDeadPeerTimer() {
|
|
@@ -2701,7 +2701,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2701
2701
|
this._deadPeerTimer.unref();
|
|
2702
2702
|
}
|
|
2703
2703
|
/**
|
|
2704
|
-
* Clear the dead-peer idle timer (Plan 05
|
|
2704
|
+
* Clear the dead-peer idle timer (Plan 05, D-14 timer cleanup on
|
|
2705
2705
|
* every transition out of CONNECTED).
|
|
2706
2706
|
*/
|
|
2707
2707
|
_clearDeadPeerTimer() {
|
|
@@ -2711,13 +2711,13 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2711
2711
|
}
|
|
2712
2712
|
}
|
|
2713
2713
|
/**
|
|
2714
|
-
* Disconnect handler
|
|
2714
|
+
* Disconnect handler, Plan 04 reconnect FSM core.
|
|
2715
2715
|
*
|
|
2716
2716
|
* Implements:
|
|
2717
2717
|
* - CLIENT-17 hybrid in-flight handling (D-08): controlId mode preserves
|
|
2718
2718
|
* pending sends for resend; FIFO mode rejects in-flight with
|
|
2719
2719
|
* `connectionCause: 'in-flight-orphan'` and queued with `'fifo-unsafe'`.
|
|
2720
|
-
* - CLIENT-18 classifier-first (Composition A
|
|
2720
|
+
* - CLIENT-18 classifier-first (Composition A, D-16): permanent errors
|
|
2721
2721
|
* transition straight to CLOSED without invoking `retryStrategy`.
|
|
2722
2722
|
* - W-01 backoff-reset on recent success: first disconnect after a
|
|
2723
2723
|
* successful ACK on the prior session resets `_attempt` to 0.
|
|
@@ -2725,7 +2725,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2725
2725
|
* - D-15 frozen RetryContext + D-17 null-return halts.
|
|
2726
2726
|
* - D-19 default exponential strategy.
|
|
2727
2727
|
*
|
|
2728
|
-
* Invoked from the SINGLE `_onStateChange` hook
|
|
2728
|
+
* Invoked from the SINGLE `_onStateChange` hook, no parallel listener
|
|
2729
2729
|
* (B-04). Idempotent across same-cycle re-entry: cycle-start flag
|
|
2730
2730
|
* coordinates first-disconnect vs subsequent-within-cycle behavior.
|
|
2731
2731
|
*/
|
|
@@ -2914,7 +2914,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2914
2914
|
this._reconnectCycleStartedAt = null;
|
|
2915
2915
|
}
|
|
2916
2916
|
/**
|
|
2917
|
-
* Test seam
|
|
2917
|
+
* Test seam, install a factory that produces the next reconnect Connection.
|
|
2918
2918
|
*
|
|
2919
2919
|
* @internal
|
|
2920
2920
|
*/
|
|
@@ -2922,7 +2922,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2922
2922
|
this._reconnectFactory = factory;
|
|
2923
2923
|
}
|
|
2924
2924
|
/**
|
|
2925
|
-
* Test seam
|
|
2925
|
+
* Test seam, capture or rebind the connect-signal mid-flight (W-07).
|
|
2926
2926
|
*
|
|
2927
2927
|
* @internal
|
|
2928
2928
|
*/
|
|
@@ -2952,7 +2952,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2952
2952
|
* anchor and passes it to `matchAck` for keyed lookup.
|
|
2953
2953
|
*
|
|
2954
2954
|
* Called from the SINGLE `'message'` listener registered in `_attachConnection`.
|
|
2955
|
-
* No parallel listener is registered
|
|
2955
|
+
* No parallel listener is registered, downstream plans extend at the named
|
|
2956
2956
|
* anchor (B-04).
|
|
2957
2957
|
*/
|
|
2958
2958
|
_onAckPayload(ackPayload, byteOffset) {
|
|
@@ -2991,9 +2991,9 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
2991
2991
|
}
|
|
2992
2992
|
/**
|
|
2993
2993
|
* Emit a frozen `'drain'` event when the queue depth and bytes fall below
|
|
2994
|
-
* both configured caps (Plan 05
|
|
2994
|
+
* both configured caps (Plan 05, D-24). Called from `_onAckMatched`
|
|
2995
2995
|
* (every successful ACK) and from the Correlator's `onTimeout` callback
|
|
2996
|
-
* (every expired send)
|
|
2996
|
+
* (every expired send), both code paths free a live-store slot.
|
|
2997
2997
|
*/
|
|
2998
2998
|
_maybeEmitDrain() {
|
|
2999
2999
|
const corr = this._correlator;
|
|
@@ -3068,7 +3068,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3068
3068
|
* ```
|
|
3069
3069
|
*
|
|
3070
3070
|
* @param payload Raw bytes; MLLP framing is added internally via `encodeFrame`.
|
|
3071
|
-
* @param opts.signal AbortSignal
|
|
3071
|
+
* @param opts.signal AbortSignal, aborting cancels the ACK wait (CLIENT-11).
|
|
3072
3072
|
*/
|
|
3073
3073
|
send(payload, opts) {
|
|
3074
3074
|
const signal = opts?.signal;
|
|
@@ -3156,7 +3156,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3156
3156
|
});
|
|
3157
3157
|
}
|
|
3158
3158
|
/**
|
|
3159
|
-
* 'wait'-mode backpressure handler (Plan 05
|
|
3159
|
+
* 'wait'-mode backpressure handler (Plan 05, CLIENT-07/CLIENT-11).
|
|
3160
3160
|
*
|
|
3161
3161
|
* Awaits one of three terminating signals, in order:
|
|
3162
3162
|
* - `'drain'` event → re-enter `send()` (the gate will now pass).
|
|
@@ -3218,7 +3218,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3218
3218
|
this._clearDeadPeerTimer();
|
|
3219
3219
|
}
|
|
3220
3220
|
/**
|
|
3221
|
-
* **Test seam
|
|
3221
|
+
* **Test seam**, attach an externally-built {@link Connection} directly,
|
|
3222
3222
|
* bypassing the `net.createConnection` + `NetTransport` path. Used by
|
|
3223
3223
|
* lifecycle tests driving `InMemoryTransport.pair()` for determinism.
|
|
3224
3224
|
*
|
|
@@ -3297,7 +3297,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3297
3297
|
}
|
|
3298
3298
|
}
|
|
3299
3299
|
/**
|
|
3300
|
-
* Abruptly destroy the client
|
|
3300
|
+
* Abruptly destroy the client, force-transitions the underlying Connection
|
|
3301
3301
|
* to `CLOSED` immediately. No-op if no Connection is attached. Idempotent.
|
|
3302
3302
|
*
|
|
3303
3303
|
* @example
|
|
@@ -3323,7 +3323,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3323
3323
|
/**
|
|
3324
3324
|
* Returns a JSON-serializable observability snapshot (OBS-01, D-26).
|
|
3325
3325
|
*
|
|
3326
|
-
* All fields are plain values
|
|
3326
|
+
* All fields are plain values, no Buffers, no class instances, no Maps,
|
|
3327
3327
|
* no circular refs. Safe to `JSON.stringify` directly.
|
|
3328
3328
|
*
|
|
3329
3329
|
* `inFlight` is the count of correlator entries with `sentAt !== null`
|
|
@@ -3367,7 +3367,7 @@ var MllpClient = class extends events.EventEmitter {
|
|
|
3367
3367
|
};
|
|
3368
3368
|
}
|
|
3369
3369
|
/**
|
|
3370
|
-
* Async disposal
|
|
3370
|
+
* Async disposal, delegates to {@link MllpClient.close} for `await using` support.
|
|
3371
3371
|
*
|
|
3372
3372
|
* @example
|
|
3373
3373
|
* ```typescript
|
|
@@ -3423,7 +3423,7 @@ async function createStarterClient(opts) {
|
|
|
3423
3423
|
}
|
|
3424
3424
|
|
|
3425
3425
|
// src/index.ts
|
|
3426
|
-
var VERSION = "0.0.
|
|
3426
|
+
var VERSION = "0.0.2";
|
|
3427
3427
|
|
|
3428
3428
|
exports.Connection = Connection;
|
|
3429
3429
|
exports.FrameReader = FrameReader;
|