@cosyte/mllp 0.0.1 → 0.0.3
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 +329 -108
- package/README.md +23 -18
- package/TRADEMARKS.md +30 -0
- package/dist/ack-from-hl7/index.cjs +55 -15
- package/dist/ack-from-hl7/index.cjs.map +1 -1
- package/dist/ack-from-hl7/index.d.cts +112 -38
- package/dist/ack-from-hl7/index.d.ts +112 -38
- package/dist/ack-from-hl7/index.mjs +54 -15
- package/dist/ack-from-hl7/index.mjs.map +1 -1
- package/dist/{index-DjGQJtYE.d.cts → index-CD8V2Mtk.d.cts} +5 -5
- package/dist/{index-DjGQJtYE.d.ts → index-CD8V2Mtk.d.ts} +5 -5
- package/dist/index.cjs +157 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -420
- package/dist/index.d.ts +398 -420
- package/dist/index.mjs +157 -168
- 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 +16 -7
package/dist/index.mjs
CHANGED
|
@@ -25,13 +25,13 @@ var MllpFramingError = class _MllpFramingError extends Error {
|
|
|
25
25
|
/**
|
|
26
26
|
* Up to 64 bytes copied from around the anomaly.
|
|
27
27
|
*
|
|
28
|
-
* This is a **copied** Buffer
|
|
28
|
+
* This is a **copied** Buffer, isolated from the source buffer so it remains
|
|
29
29
|
* valid after the underlying buffer is reused or overwritten.
|
|
30
30
|
*
|
|
31
|
-
* **PHI contract
|
|
31
|
+
* **PHI contract:** the decoder only ever populates this with the single
|
|
32
32
|
* framing-boundary byte that violated the frame structure (whose hex value the
|
|
33
|
-
* `message` already discloses)
|
|
34
|
-
* whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE
|
|
33
|
+
* `message` already discloses), never a run of payload content bytes. For anomalies
|
|
34
|
+
* whose fault is not a specific byte (`MLLP_FRAME_TOO_LARGE`, the accumulated size),
|
|
35
35
|
* the snippet is **empty**: a payload slice on a public error field would leak a
|
|
36
36
|
* field-body slice of clinical content. Callers constructing this directly are
|
|
37
37
|
* responsible for the same discipline.
|
|
@@ -104,19 +104,19 @@ var FrameReader = class {
|
|
|
104
104
|
_opts;
|
|
105
105
|
_maxFrameSize;
|
|
106
106
|
_state = "SCANNING_FOR_VT";
|
|
107
|
-
/** Reusable accumulator buffer
|
|
107
|
+
/** Reusable accumulator buffer, grown by doubling as needed. */
|
|
108
108
|
_accumulator = Buffer.allocUnsafe(INITIAL_ACCUMULATOR_SIZE);
|
|
109
109
|
/** Number of payload bytes written to accumulator in current frame. */
|
|
110
110
|
_writePos = 0;
|
|
111
111
|
/** Monotonic absolute stream byte offset across the entire connection lifetime. */
|
|
112
112
|
_byteOffset = 0;
|
|
113
|
-
/** Byte offset where current leading-whitespace run started
|
|
113
|
+
/** Byte offset where current leading-whitespace run started. */
|
|
114
114
|
_wsStart = 0;
|
|
115
|
-
/** Count of leading whitespace bytes accumulated in current run
|
|
115
|
+
/** Count of leading whitespace bytes accumulated in current run. */
|
|
116
116
|
_wsCount = 0;
|
|
117
117
|
/** Byte offset of the VT byte that started the current frame. */
|
|
118
118
|
_frameStartOffset = 0;
|
|
119
|
-
/** Per-frame warning accumulator
|
|
119
|
+
/** Per-frame warning accumulator, cleared after each _deliverFrame(). */
|
|
120
120
|
_frameWarnings = [];
|
|
121
121
|
/**
|
|
122
122
|
* Construct a chunked MLLP frame reader.
|
|
@@ -147,7 +147,7 @@ var FrameReader = class {
|
|
|
147
147
|
/**
|
|
148
148
|
* Clear internal accumulator state and reset byte offset to 0.
|
|
149
149
|
*
|
|
150
|
-
* Call on reconnect / connection reuse to start fresh without allocating a new reader
|
|
150
|
+
* Call on reconnect / connection reuse to start fresh without allocating a new reader.
|
|
151
151
|
* Pending partial frame state is discarded silently.
|
|
152
152
|
*
|
|
153
153
|
* @example
|
|
@@ -240,7 +240,7 @@ var FrameReader = class {
|
|
|
240
240
|
this._emitWarning(
|
|
241
241
|
"MLLP_MISSING_LEADING_VT",
|
|
242
242
|
this._byteOffset,
|
|
243
|
-
`Missing leading VT (0x0B)
|
|
243
|
+
`Missing leading VT (0x0B), treating byte 0x${byte.toString(16).padStart(2, "0")} at offset ${this._byteOffset} as payload start`
|
|
244
244
|
);
|
|
245
245
|
this._frameStartOffset = this._byteOffset;
|
|
246
246
|
this._state = "READING_PAYLOAD";
|
|
@@ -388,7 +388,7 @@ var FrameReader = class {
|
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
390
|
* Copy accumulated payload bytes into a new Buffer and deliver via `onFrame`.
|
|
391
|
-
* The copy isolates callers from internal accumulator reuse
|
|
391
|
+
* The copy isolates callers from internal accumulator reuse.
|
|
392
392
|
*/
|
|
393
393
|
_deliverFrame() {
|
|
394
394
|
const payload = Buffer.from(this._accumulator.subarray(0, this._writePos));
|
|
@@ -398,7 +398,7 @@ var FrameReader = class {
|
|
|
398
398
|
this._frameWarnings = [];
|
|
399
399
|
this._opts.onFrame(payload, frameStart, frameWarnings);
|
|
400
400
|
}
|
|
401
|
-
/** Emit a warning via the `onWarning` callback, swallowing any handler exceptions
|
|
401
|
+
/** Emit a warning via the `onWarning` callback, swallowing any handler exceptions. */
|
|
402
402
|
_emitWarning(code, byteOffset, message) {
|
|
403
403
|
const warning = createWarning(code, byteOffset, message);
|
|
404
404
|
this._frameWarnings.push(warning);
|
|
@@ -485,8 +485,8 @@ var TlsTransport = class {
|
|
|
485
485
|
this._socket.on("data", fn);
|
|
486
486
|
}
|
|
487
487
|
/**
|
|
488
|
-
* See {@link Transport.onConnect}. Armed on `'secureConnect'
|
|
489
|
-
* handshake-complete event
|
|
488
|
+
* See {@link Transport.onConnect}. Armed on `'secureConnect'`, the TLS
|
|
489
|
+
* handshake-complete event, not the raw TCP `'connect'` event.
|
|
490
490
|
*/
|
|
491
491
|
onConnect(fn) {
|
|
492
492
|
this._socket.removeAllListeners("secureConnect");
|
|
@@ -552,7 +552,7 @@ var LEGAL_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
|
552
552
|
["CLOSED", /* @__PURE__ */ new Set()]
|
|
553
553
|
]);
|
|
554
554
|
var Connection = class extends EventEmitter {
|
|
555
|
-
/** Stable UUIDv4 identifier for this connection
|
|
555
|
+
/** Stable UUIDv4 identifier for this connection. */
|
|
556
556
|
connectionId;
|
|
557
557
|
_state = "CONNECTING";
|
|
558
558
|
_transport;
|
|
@@ -565,20 +565,19 @@ var Connection = class extends EventEmitter {
|
|
|
565
565
|
_bytesOut = 0;
|
|
566
566
|
_lastByteInAt = null;
|
|
567
567
|
_lastByteOutAt = null;
|
|
568
|
-
/** Ring buffer: last MAX_WARNINGS entries
|
|
568
|
+
/** Ring buffer: last MAX_WARNINGS entries. */
|
|
569
569
|
_warningBuffer = [];
|
|
570
|
-
/** Accurate total counts per code
|
|
570
|
+
/** Accurate total counts per code, unaffected by ring buffer truncation. */
|
|
571
571
|
_warningsByCode = /* @__PURE__ */ new Map();
|
|
572
|
-
/** Set to true once the ring buffer overflows
|
|
572
|
+
/** Set to true once the ring buffer overflows. */
|
|
573
573
|
_warningsTruncated = false;
|
|
574
574
|
_onWarningFn = null;
|
|
575
575
|
_drainPromise = null;
|
|
576
576
|
/**
|
|
577
|
-
* beforeClose hook
|
|
577
|
+
* beforeClose hook, no-op default that resolves immediately.
|
|
578
578
|
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
* Connection calls this during `close()`, racing against `drainTimeoutMs`.
|
|
579
|
+
* An owner may replace this instance property to run work before the socket
|
|
580
|
+
* closes. Connection calls it during `close()`, racing against `drainTimeoutMs`.
|
|
582
581
|
*/
|
|
583
582
|
beforeClose = () => Promise.resolve();
|
|
584
583
|
/**
|
|
@@ -625,7 +624,7 @@ var Connection = class extends EventEmitter {
|
|
|
625
624
|
});
|
|
626
625
|
}
|
|
627
626
|
/**
|
|
628
|
-
* Current FSM state. One of the 6 `ConnectionState` values
|
|
627
|
+
* Current FSM state. One of the 6 `ConnectionState` values.
|
|
629
628
|
*
|
|
630
629
|
* Subscribe to `'stateChange'` events for reactive state monitoring.
|
|
631
630
|
*/
|
|
@@ -633,10 +632,10 @@ var Connection = class extends EventEmitter {
|
|
|
633
632
|
return this._state;
|
|
634
633
|
}
|
|
635
634
|
/**
|
|
636
|
-
* Register or replace the per-connection warning subscriber
|
|
635
|
+
* Register or replace the per-connection warning subscriber.
|
|
637
636
|
*
|
|
638
637
|
* Subsequent calls replace the previous handler (set-once semantics prevent leaks).
|
|
639
|
-
* The handler is called synchronously and any exception it throws is swallowed
|
|
638
|
+
* The handler is called synchronously and any exception it throws is swallowed.
|
|
640
639
|
*
|
|
641
640
|
* @example
|
|
642
641
|
* ```typescript
|
|
@@ -674,7 +673,7 @@ var Connection = class extends EventEmitter {
|
|
|
674
673
|
/**
|
|
675
674
|
* Write raw bytes to the transport (no framing applied).
|
|
676
675
|
*
|
|
677
|
-
*
|
|
676
|
+
* The server and the client wrap this with `encodeFrame()` to add MLLP framing.
|
|
678
677
|
* Returns `false` if the connection is CLOSED or DISCONNECTED (no bytes written).
|
|
679
678
|
*
|
|
680
679
|
* @returns `true` if bytes flushed immediately; `false` if buffered (backpressure) or not writable.
|
|
@@ -696,7 +695,7 @@ var Connection = class extends EventEmitter {
|
|
|
696
695
|
* Initiate graceful close of the connection.
|
|
697
696
|
*
|
|
698
697
|
* - If `CONNECTING` or `RECONNECTING`: cancels the pending attempt, transitions
|
|
699
|
-
* directly to `CLOSED`, and calls `transport.destroy()
|
|
698
|
+
* directly to `CLOSED`, and calls `transport.destroy()`.
|
|
700
699
|
* - If `CONNECTED`: transitions to `DRAINING`, calls `beforeClose(drainTimeoutMs)`,
|
|
701
700
|
* then `DRAINING → DISCONNECTED` once the hook resolves, or `DRAINING → CLOSED`
|
|
702
701
|
* if the drain timeout elapses first.
|
|
@@ -731,7 +730,7 @@ var Connection = class extends EventEmitter {
|
|
|
731
730
|
* Race `beforeClose()` against the drain timeout.
|
|
732
731
|
*
|
|
733
732
|
* On normal completion: `DRAINING → DISCONNECTED` + `transport.close()`.
|
|
734
|
-
* On timeout: `DRAINING → CLOSED` + `transport.destroy()
|
|
733
|
+
* On timeout: `DRAINING → CLOSED` + `transport.destroy()`.
|
|
735
734
|
*/
|
|
736
735
|
async _drainWithTimeout(timeoutMs) {
|
|
737
736
|
const drainPromise = this.beforeClose(timeoutMs);
|
|
@@ -758,7 +757,7 @@ var Connection = class extends EventEmitter {
|
|
|
758
757
|
* Abruptly destroy the connection, discarding any pending writes.
|
|
759
758
|
*
|
|
760
759
|
* Transitions from any non-terminal state directly to `CLOSED` and calls
|
|
761
|
-
* `transport.destroy(reason)`. Emits `'close'` event. Idempotent
|
|
760
|
+
* `transport.destroy(reason)`. Emits `'close'` event. Idempotent, safe to
|
|
762
761
|
* call multiple times.
|
|
763
762
|
*
|
|
764
763
|
* @param reason - Optional error to propagate to the transport.
|
|
@@ -774,7 +773,7 @@ var Connection = class extends EventEmitter {
|
|
|
774
773
|
this._transport.destroy(reason);
|
|
775
774
|
}
|
|
776
775
|
/**
|
|
777
|
-
* Return a JSON-serializable observability snapshot
|
|
776
|
+
* Return a JSON-serializable observability snapshot.
|
|
778
777
|
*
|
|
779
778
|
* All timestamp fields are `Date | null` (not epoch milliseconds). `JSON.stringify()`
|
|
780
779
|
* serialises them to ISO 8601 strings with no information loss.
|
|
@@ -858,12 +857,12 @@ var Connection = class extends EventEmitter {
|
|
|
858
857
|
* Node throws `ERR_UNHANDLED_ERROR` when `'error'` is emitted on an `EventEmitter` with no
|
|
859
858
|
* listener. Every `'error'` emission in this class is reached from inside a transport callback
|
|
860
859
|
* (the socket's `'data'`/`'error'` listener), so an unguarded emit would become an **uncaught
|
|
861
|
-
* exception that kills the process
|
|
860
|
+
* exception that kills the process**, the exact failure the containment above exists to
|
|
862
861
|
* prevent, merely relocated one frame up the stack.
|
|
863
862
|
*
|
|
864
863
|
* `Connection` is a public export and can legitimately be driven with no `'error'` listener
|
|
865
864
|
* (`MllpServer` and `MllpClient` each attach one; a bare `Connection` need not). The connection
|
|
866
|
-
* is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind
|
|
865
|
+
* is still torn down and `'stateChange'`/`'close'` still fire, so a caller is never blind,
|
|
867
866
|
* they simply do not get the typed error object they never asked for. Mirrors
|
|
868
867
|
* `MllpServer._emitErrorIfListened`.
|
|
869
868
|
*/
|
|
@@ -882,7 +881,7 @@ var Connection = class extends EventEmitter {
|
|
|
882
881
|
* consumers keep the stable `code` and `byteOffset`), then **destroy this connection only**.
|
|
883
882
|
*
|
|
884
883
|
* Why destroy rather than resynchronize: once `push` has thrown, the reader's position within
|
|
885
|
-
* the byte stream is no longer trustworthy
|
|
884
|
+
* the byte stream is no longer trustworthy, the next bytes could be the middle of a message.
|
|
886
885
|
* Guessing where the next frame starts is how a clinical message gets silently mis-split, so
|
|
887
886
|
* the connection is dropped instead. A server drops that one peer and keeps serving everyone
|
|
888
887
|
* else.
|
|
@@ -894,7 +893,7 @@ var Connection = class extends EventEmitter {
|
|
|
894
893
|
* engine that is already unwell. A compatibility failure is not a network blip.
|
|
895
894
|
*
|
|
896
895
|
* If a peer's quirk is *expected* (trailing junk, keepalive bytes, a missing leading VT), the
|
|
897
|
-
* decoder's tolerance opt-ins are the supported answer
|
|
896
|
+
* decoder's tolerance opt-ins are the supported answer, they turn the throw into a warning.
|
|
898
897
|
*/
|
|
899
898
|
_onFatalFramingError(err) {
|
|
900
899
|
if (this._state === "CLOSED") return;
|
|
@@ -908,13 +907,13 @@ var Connection = class extends EventEmitter {
|
|
|
908
907
|
this.destroy(err);
|
|
909
908
|
}
|
|
910
909
|
/**
|
|
911
|
-
* Backstop for a NON-framing throw escaping `FrameReader.push
|
|
910
|
+
* Backstop for a NON-framing throw escaping `FrameReader.push`, i.e. our own bug, or a
|
|
912
911
|
* subscriber's, rather than the peer's bytes.
|
|
913
912
|
*
|
|
914
913
|
* `_onFrameDecoded` already contains subscriber throws at the dispatch site, so this should be
|
|
915
914
|
* unreachable. It exists because "unreachable" and "cannot kill the process from inside a
|
|
916
915
|
* socket `'data'` handler" are different claims, and only the second one is safe to bet a
|
|
917
|
-
* clinical interface on. Reported honestly as an internal receive error
|
|
916
|
+
* clinical interface on. Reported honestly as an internal receive error, never dressed up as
|
|
918
917
|
* a peer framing fault.
|
|
919
918
|
*/
|
|
920
919
|
_onInternalReceiveError(err) {
|
|
@@ -990,9 +989,9 @@ var UNSAFE_FIELD_SEPARATORS = /* @__PURE__ */ new Set([
|
|
|
990
989
|
SEGMENT_SEPARATOR_CR,
|
|
991
990
|
SEGMENT_SEPARATOR_LF,
|
|
992
991
|
11,
|
|
993
|
-
// VT
|
|
992
|
+
// VT, MLLP start block
|
|
994
993
|
28
|
|
995
|
-
// FS
|
|
994
|
+
// FS, MLLP end block
|
|
996
995
|
]);
|
|
997
996
|
function isMshSegmentAt(buf, i) {
|
|
998
997
|
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]);
|
|
@@ -1225,13 +1224,13 @@ var MllpServer = class extends EventEmitter {
|
|
|
1225
1224
|
_netServer;
|
|
1226
1225
|
_connections = /* @__PURE__ */ new Set();
|
|
1227
1226
|
_opts;
|
|
1228
|
-
/** `true` when `ServerOptions.tls` is set
|
|
1227
|
+
/** `true` when `ServerOptions.tls` is set, `_netServer` is actually a `tls.Server`. */
|
|
1229
1228
|
_isTls;
|
|
1230
1229
|
_listening = false;
|
|
1231
|
-
/**
|
|
1230
|
+
/** Single-flight guard: `true` while a `listen()` call is settling. */
|
|
1232
1231
|
_listenInFlight = false;
|
|
1233
1232
|
/**
|
|
1234
|
-
*
|
|
1233
|
+
* Settle hook for an in-flight `listen()`. Non-null only while a
|
|
1235
1234
|
* `listen()` is in flight; invoked by `close()` so a close racing startup
|
|
1236
1235
|
* rejects the pending `listen()` (typed) instead of leaving it hung with
|
|
1237
1236
|
* the single-flight guard stuck. Cleared on every settle path.
|
|
@@ -1241,7 +1240,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1241
1240
|
_host = null;
|
|
1242
1241
|
_acceptedTotal = 0;
|
|
1243
1242
|
_closedTotal = 0;
|
|
1244
|
-
/**
|
|
1243
|
+
/** Count of `'tlsClientError'` events since listen(). */
|
|
1245
1244
|
_tlsClientErrorsTotal = 0;
|
|
1246
1245
|
/**
|
|
1247
1246
|
* Construct an MLLP server. Created idle; call `listen()` (or use
|
|
@@ -1304,23 +1303,23 @@ var MllpServer = class extends EventEmitter {
|
|
|
1304
1303
|
* `Object.freeze({ port: actualPort, host: actualHost })`.
|
|
1305
1304
|
*
|
|
1306
1305
|
* **Single-flight:** one `listen()` per server lifecycle at a time. A call
|
|
1307
|
-
* while the server is already listening
|
|
1308
|
-
* still in flight
|
|
1306
|
+
* while the server is already listening, or while another `listen()` is
|
|
1307
|
+
* still in flight, rejects with a typed `MllpConnectionError` (concurrent
|
|
1309
1308
|
* binds raced each other's post-bind safety checks). Call `close()` before
|
|
1310
1309
|
* re-listening; sequential `listen()` → `close()` → `listen()` is fine.
|
|
1311
1310
|
*
|
|
1312
1311
|
* **`close()` during an in-flight `listen()`** rejects that `listen()` with
|
|
1313
1312
|
* a typed `MllpConnectionError` (never a hang) and clears the single-flight
|
|
1314
|
-
* guard
|
|
1313
|
+
* guard, a subsequent `listen()` on the same server works. This makes
|
|
1315
1314
|
* `Symbol.asyncDispose` (which delegates to `close()`) safe even before a
|
|
1316
1315
|
* `listen()` has settled. (Exception: `close({ signal })` with an
|
|
1317
|
-
* already-aborted signal is a no-op AbortError rejection
|
|
1316
|
+
* already-aborted signal is a no-op AbortError rejection, the in-flight
|
|
1318
1317
|
* `listen()` is left to settle on its own bind outcome; see `close()`.)
|
|
1319
1318
|
*
|
|
1320
1319
|
* @param port - TCP port to bind. Use `0` to let the OS assign an ephemeral port.
|
|
1321
1320
|
* @param hostOrOpts - Host string or object with `host` and optional `signal`.
|
|
1322
|
-
* Default host: `'127.0.0.1'` (
|
|
1323
|
-
* are rejected unless `ServerOptions.allowWildcardBind: true
|
|
1321
|
+
* Default host: `'127.0.0.1'` (bind-safety hardening). Wildcard hosts
|
|
1322
|
+
* are rejected unless `ServerOptions.allowWildcardBind: true`, literal
|
|
1324
1323
|
* spellings pre-bind, resolver-only shorthands post-bind via the
|
|
1325
1324
|
* OS-normalized bound address.
|
|
1326
1325
|
*
|
|
@@ -1340,7 +1339,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1340
1339
|
if (this._listening || this._listenInFlight) {
|
|
1341
1340
|
return Promise.reject(
|
|
1342
1341
|
new MllpConnectionError(
|
|
1343
|
-
this._listening ? "listen() rejected: server is already listening
|
|
1342
|
+
this._listening ? "listen() rejected: server is already listening, call close() before re-listening" : "listen() rejected: another listen() is already in flight on this server",
|
|
1344
1343
|
{
|
|
1345
1344
|
cause: new Error("listen already listening or in flight"),
|
|
1346
1345
|
phase: "connect"
|
|
@@ -1351,7 +1350,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1351
1350
|
if (isWildcardHost(host) && this._opts.allowWildcardBind !== true) {
|
|
1352
1351
|
return Promise.reject(
|
|
1353
1352
|
new MllpConnectionError(
|
|
1354
|
-
`refusing to bind wildcard host '${host}'
|
|
1353
|
+
`refusing to bind wildcard host '${host}', set ServerOptions.allowWildcardBind: true to bind all interfaces`,
|
|
1355
1354
|
{
|
|
1356
1355
|
cause: new Error("wildcard bind requires allowWildcardBind"),
|
|
1357
1356
|
phase: "connect"
|
|
@@ -1430,7 +1429,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1430
1429
|
{
|
|
1431
1430
|
ok: false,
|
|
1432
1431
|
error: new MllpConnectionError(
|
|
1433
|
-
`refusing to bind wildcard host '${host}' (bound address '${actualHost}')
|
|
1432
|
+
`refusing to bind wildcard host '${host}' (bound address '${actualHost}'), set ServerOptions.allowWildcardBind: true to bind all interfaces`,
|
|
1434
1433
|
{
|
|
1435
1434
|
cause: new Error("wildcard bind requires allowWildcardBind"),
|
|
1436
1435
|
phase: "connect"
|
|
@@ -1457,7 +1456,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1457
1456
|
}
|
|
1458
1457
|
};
|
|
1459
1458
|
const wildcardBound = isWildcardHost(actualHost);
|
|
1460
|
-
const wildcardMessage = `MLLP server is bound to ALL network interfaces (host='${actualHost}')
|
|
1459
|
+
const wildcardMessage = `MLLP server is bound to ALL network interfaces (host='${actualHost}'), set a specific bind address unless this is intentional.`;
|
|
1461
1460
|
if (wildcardBound) {
|
|
1462
1461
|
process.emitWarning(wildcardMessage, { code: MLLP_BIND_ALL_INTERFACES });
|
|
1463
1462
|
}
|
|
@@ -1485,19 +1484,19 @@ var MllpServer = class extends EventEmitter {
|
|
|
1485
1484
|
/**
|
|
1486
1485
|
* Stop accepting new connections and gracefully close all active connections.
|
|
1487
1486
|
*
|
|
1488
|
-
* Sequence
|
|
1489
|
-
* 1. `net.Server.close()
|
|
1487
|
+
* Sequence:
|
|
1488
|
+
* 1. `net.Server.close()`, stops accepting new connections immediately
|
|
1490
1489
|
* 2. If `_connections` is empty: resolves immediately (no drain needed)
|
|
1491
|
-
* 3. Calls `_drainAll(drainTimeoutMs)
|
|
1490
|
+
* 3. Calls `_drainAll(drainTimeoutMs)`, Promise.all + side-effect setTimeout
|
|
1492
1491
|
* that force-destroys stragglers after the drain window
|
|
1493
1492
|
*
|
|
1494
1493
|
* Calling `close()` while a `listen()` is still in flight proactively
|
|
1495
1494
|
* settles that `listen()` with a typed `MllpConnectionError` rejection
|
|
1496
|
-
* and clears the single-flight guard
|
|
1495
|
+
* and clears the single-flight guard, the server is immediately
|
|
1497
1496
|
* re-listenable. `Symbol.asyncDispose` (which delegates here) is
|
|
1498
1497
|
* therefore safe before `listen()` settles. **Qualification:** a
|
|
1499
1498
|
* `close({ signal })` whose signal is **already aborted** rejects
|
|
1500
|
-
* immediately with `AbortError` and performs no work
|
|
1499
|
+
* immediately with `AbortError` and performs no work, it does NOT close
|
|
1501
1500
|
* the server and does NOT settle the in-flight `listen()`, which simply
|
|
1502
1501
|
* continues and settles on its own bind outcome.
|
|
1503
1502
|
*
|
|
@@ -1549,7 +1548,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1549
1548
|
/**
|
|
1550
1549
|
* Drain all active connections with a shared timeout.
|
|
1551
1550
|
*
|
|
1552
|
-
* Uses Promise.all (not Promise.race) for coordination
|
|
1551
|
+
* Uses Promise.all (not Promise.race) for coordination, the timeout is a
|
|
1553
1552
|
* side effect that calls `conn.destroy()` on stragglers after `drainTimeoutMs`.
|
|
1554
1553
|
* Promise.all resolves when all `conn.close()` promises settle (which happens
|
|
1555
1554
|
* because `conn.destroy()` transitions connections to CLOSED).
|
|
@@ -1572,7 +1571,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1572
1571
|
}
|
|
1573
1572
|
}
|
|
1574
1573
|
/**
|
|
1575
|
-
* Async disposal
|
|
1574
|
+
* Async disposal, delegates to `close()` for `await using` support.
|
|
1576
1575
|
*
|
|
1577
1576
|
* @example
|
|
1578
1577
|
* ```typescript
|
|
@@ -1585,10 +1584,9 @@ var MllpServer = class extends EventEmitter {
|
|
|
1585
1584
|
return this.close();
|
|
1586
1585
|
}
|
|
1587
1586
|
/**
|
|
1588
|
-
* Return a JSON-serializable observability snapshot
|
|
1587
|
+
* Return a JSON-serializable observability snapshot.
|
|
1589
1588
|
*
|
|
1590
1589
|
* `totalBytesIn` and `totalBytesOut` aggregate from live connections at call time.
|
|
1591
|
-
* Plan 04 adds full aggregation; at Plan 01 scope they return 0.
|
|
1592
1590
|
*
|
|
1593
1591
|
* @example
|
|
1594
1592
|
* ```typescript
|
|
@@ -1619,7 +1617,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1619
1617
|
};
|
|
1620
1618
|
}
|
|
1621
1619
|
/**
|
|
1622
|
-
* Emit on `'error'` only when a listener is attached
|
|
1620
|
+
* Emit on `'error'` only when a listener is attached, an unlistened
|
|
1623
1621
|
* `EventEmitter` `'error'` emission throws (see the class JSDoc error
|
|
1624
1622
|
* contract). The single guard shared by the optional-tap forwarding sites
|
|
1625
1623
|
* in this class; the constructor's net.Server forwarder deliberately does
|
|
@@ -1629,7 +1627,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1629
1627
|
/**
|
|
1630
1628
|
* Emit a server event, containing a throwing subscriber.
|
|
1631
1629
|
*
|
|
1632
|
-
* Every event this server emits is reachable from a callback we do not own
|
|
1630
|
+
* Every event this server emits is reachable from a callback we do not own, the `net.Server`
|
|
1633
1631
|
* `'connection'` listener, the `tls.Server` `'tlsClientError'` listener, a socket's `'data'`
|
|
1634
1632
|
* listener (via `Connection`), or the `catch` block of a `void`-ed async ACK task. A throwing
|
|
1635
1633
|
* subscriber would therefore unwind into that callback and kill the process (or reject a void-ed
|
|
@@ -1734,8 +1732,8 @@ var MllpServer = class extends EventEmitter {
|
|
|
1734
1732
|
);
|
|
1735
1733
|
}
|
|
1736
1734
|
/**
|
|
1737
|
-
* Extract a minimal, content-free peer-certificate summary (
|
|
1738
|
-
*
|
|
1735
|
+
* Extract a minimal, content-free peer-certificate summary (ATNA ITI-19
|
|
1736
|
+
* mutual authentication), CN strings, expiry, and the verification
|
|
1739
1737
|
* outcome (`authorized`) only; never the full certificate object. Returns
|
|
1740
1738
|
* `null` when no peer certificate was presented (e.g. `clientAuth: 'WANT'`
|
|
1741
1739
|
* with no client cert).
|
|
@@ -1753,7 +1751,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1753
1751
|
subjectCN: asCn(cert.subject?.CN),
|
|
1754
1752
|
issuerCN: asCn(cert.issuer?.CN),
|
|
1755
1753
|
validTo: cert.valid_to ?? null,
|
|
1756
|
-
// Verification outcome
|
|
1754
|
+
// Verification outcome, under 'WANT' a certificate can be present yet
|
|
1757
1755
|
// UNVERIFIED (see PeerCertificateSummary.authorized JSDoc).
|
|
1758
1756
|
authorized: socket.authorized === true
|
|
1759
1757
|
});
|
|
@@ -1765,12 +1763,12 @@ var MllpServer = class extends EventEmitter {
|
|
|
1765
1763
|
* then dispatches: resolve ⇒ `AA`; throw/reject ⇒ a negative ACK (`AE` by default,
|
|
1766
1764
|
* `AR` via {@link MllpAckError}). A positive ACK can never precede a successful commit.
|
|
1767
1765
|
*
|
|
1768
|
-
* Without an `onMessage` handler, `'AA'` degrades to a **transport-accept
|
|
1766
|
+
* Without an `onMessage` handler, `'AA'` degrades to a **transport-accept**, `AA`
|
|
1769
1767
|
* meaning "bytes received and framed", not "application-processed".
|
|
1770
1768
|
*
|
|
1771
1769
|
* A handler failure is **expected flow**, not a server error: it produces a negative
|
|
1772
1770
|
* ACK and a `'nack'` event. The thrown error's message is never placed on the wire or
|
|
1773
|
-
* in the event (it may carry PHI)
|
|
1771
|
+
* in the event (it may carry PHI), only the static `ackCode` and the inbound control
|
|
1774
1772
|
* metadata reach the peer.
|
|
1775
1773
|
*/
|
|
1776
1774
|
async _sendCommitAck(payload, meta, conn) {
|
|
@@ -1802,18 +1800,18 @@ var MllpServer = class extends EventEmitter {
|
|
|
1802
1800
|
}
|
|
1803
1801
|
/**
|
|
1804
1802
|
* Resolve the auto-ACK code for a message the server would otherwise acknowledge
|
|
1805
|
-
* **positively** (`AA`)
|
|
1803
|
+
* **positively** (`AA`), the no-handler transport-accept and the post-commit success
|
|
1806
1804
|
* paths. Fail-safe: downgrade to `AE` and emit a PHI-safe `'nack'` whenever the message
|
|
1807
1805
|
* cannot carry a correlatable positive ACK, so `AA` never names a control ID the sender
|
|
1808
1806
|
* cannot match (→ timeout → resend → **duplicate clinical message**).
|
|
1809
1807
|
*
|
|
1810
1808
|
* Two independent disqualifiers, one from the bytes and one from the frame:
|
|
1811
1809
|
*
|
|
1812
|
-
* - **`rawAckUncorrelatable(payload)
|
|
1810
|
+
* - **`rawAckUncorrelatable(payload)`**, no readable `MSH`, an empty MSH-10, or a
|
|
1813
1811
|
* batch/concatenated-message shape. This is the SAME predicate `buildRawAck` enforces
|
|
1814
1812
|
* on the wire; it is re-checked here so the downgrade is **observable** (a `'nack'`
|
|
1815
1813
|
* event) rather than a silent code change inside the builder.
|
|
1816
|
-
* - **`MLLP_TRAILING_BYTES`** in the frame's warnings
|
|
1814
|
+
* - **`MLLP_TRAILING_BYTES`** in the frame's warnings, a mid-payload `VT` made the
|
|
1817
1815
|
* decoder discard accumulated bytes and deliver only the fragment after it. The
|
|
1818
1816
|
* clinical message was destroyed in transit, so even a fragment that happens to carry
|
|
1819
1817
|
* a readable MSH must not be positively acknowledged. This condition is invisible to
|
|
@@ -1822,7 +1820,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1822
1820
|
* accumulating the delivered frame, so it is frame-scoped: it names *this* payload's own
|
|
1823
1821
|
* discard, never a neighbouring frame's trailing junk.
|
|
1824
1822
|
*
|
|
1825
|
-
* `'discarded-bytes'` takes precedence in the reason when both hold
|
|
1823
|
+
* `'discarded-bytes'` takes precedence in the reason when both hold, it is the root cause.
|
|
1826
1824
|
*/
|
|
1827
1825
|
_resolveAutoAckPositive(payload, meta, conn) {
|
|
1828
1826
|
const discarded = meta.warnings.some((w) => w.code === "MLLP_TRAILING_BYTES");
|
|
@@ -1843,7 +1841,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1843
1841
|
* Run `onMessage` for its side effects only (custom-ACK and manual modes), guarding
|
|
1844
1842
|
* against a synchronous throw or a rejected promise. In these modes the handler does
|
|
1845
1843
|
* **not** gate the ACK, so a failure is re-emitted as a PHI-safe `'error'` on the
|
|
1846
|
-
* connection (
|
|
1844
|
+
* connection (a handler error never crashes the server) rather than escaping as
|
|
1847
1845
|
* an unhandled rejection. The error text is never placed on the wire.
|
|
1848
1846
|
*/
|
|
1849
1847
|
_observeMessage(payload, meta, conn) {
|
|
@@ -1857,7 +1855,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1857
1855
|
/**
|
|
1858
1856
|
* The custom-builder path for `autoAck: fn`. Awaits the builder and dispatches its
|
|
1859
1857
|
* Buffer as the ACK; the caller owns MSA-1. A builder error is re-emitted as `'error'`
|
|
1860
|
-
* on the connection
|
|
1858
|
+
* on the connection, the server never crashes.
|
|
1861
1859
|
*/
|
|
1862
1860
|
async _sendCustomAck(fn, payload, meta, conn) {
|
|
1863
1861
|
try {
|
|
@@ -1871,7 +1869,7 @@ var MllpServer = class extends EventEmitter {
|
|
|
1871
1869
|
/**
|
|
1872
1870
|
* Frame an ACK payload and write it to the connection. `Connection.send()` returns
|
|
1873
1871
|
* `false` under backpressure (socket write buffer full); that drops the ACK and emits
|
|
1874
|
-
* a `MllpConnectionError({ phase: 'send' })` on the connection
|
|
1872
|
+
* a `MllpConnectionError({ phase: 'send' })` on the connection, the peer will
|
|
1875
1873
|
* time out waiting and may retry. The server never crashes.
|
|
1876
1874
|
*/
|
|
1877
1875
|
_dispatchAck(conn, ackPayload) {
|
|
@@ -1936,7 +1934,7 @@ var Correlator = class {
|
|
|
1936
1934
|
_sendSeq = 0;
|
|
1937
1935
|
_queueBytes = 0;
|
|
1938
1936
|
/**
|
|
1939
|
-
*
|
|
1937
|
+
* Count of pending entries with `sentAt !== null`.
|
|
1940
1938
|
* Maintained at every site that mutates `entry.sentAt` or removes a
|
|
1941
1939
|
* flushed entry: `markFlushed`, `remove`, `matchAck`, `expireDue`, `clear`.
|
|
1942
1940
|
*/
|
|
@@ -1972,7 +1970,7 @@ var Correlator = class {
|
|
|
1972
1970
|
/**
|
|
1973
1971
|
* Enqueue a new send awaiting its ACK. Returns the assigned
|
|
1974
1972
|
* `correlationKey`, or `null` if `maxInFlight` is reached (caller awaits
|
|
1975
|
-
* drain
|
|
1973
|
+
* drain, `pipeline:false`).
|
|
1976
1974
|
*/
|
|
1977
1975
|
enqueue(frame, controlIdOrNull, resolve, reject) {
|
|
1978
1976
|
if (this._pending.size >= this._opts.maxInFlight) return null;
|
|
@@ -1991,7 +1989,7 @@ var Correlator = class {
|
|
|
1991
1989
|
return key;
|
|
1992
1990
|
}
|
|
1993
1991
|
/**
|
|
1994
|
-
* Record write-flush timestamp (
|
|
1992
|
+
* Record write-flush timestamp (the clock starts at flush, NOT
|
|
1995
1993
|
* at `send()` call). No-op if key is unknown (e.g. removed by abort).
|
|
1996
1994
|
*/
|
|
1997
1995
|
markFlushed(key, now) {
|
|
@@ -2007,16 +2005,16 @@ var Correlator = class {
|
|
|
2007
2005
|
* removed from live store. Caller calls `entry.resolve(ackPayload)`.
|
|
2008
2006
|
* - controlId: keyed lookup by `controlIdFromAck`. Live-store hit returns
|
|
2009
2007
|
* the entry; graveyard hit fires `MLLP_ACK_AFTER_TIMEOUT` warning
|
|
2010
|
-
*
|
|
2011
|
-
* `onUnmatchedAck(controlIdFromAck)`
|
|
2008
|
+
* and returns `null`; otherwise fires
|
|
2009
|
+
* `onUnmatchedAck(controlIdFromAck)` and returns `null`.
|
|
2012
2010
|
*
|
|
2013
|
-
* Triggers lazy graveyard eviction
|
|
2011
|
+
* Triggers lazy graveyard eviction.
|
|
2014
2012
|
*
|
|
2015
2013
|
* @param _payload Inbound ACK bytes (framing stripped). MSA-2 extraction
|
|
2016
2014
|
* happens at MllpClient's `_onAckPayload` hook; this method takes the
|
|
2017
2015
|
* already-extracted control ID as a parameter.
|
|
2018
2016
|
* @param controlIdFromAck MSA-2 extracted from ACK (controlId mode only).
|
|
2019
|
-
* @param byteOffsetFromAck Stream offset; forwarded to `onWarning
|
|
2017
|
+
* @param byteOffsetFromAck Stream offset; forwarded to `onWarning`.
|
|
2020
2018
|
*/
|
|
2021
2019
|
matchAck(_payload, controlIdFromAck = null, byteOffsetFromAck = 0) {
|
|
2022
2020
|
this._evictGraveyardDue(this._opts.now());
|
|
@@ -2060,9 +2058,9 @@ var Correlator = class {
|
|
|
2060
2058
|
}
|
|
2061
2059
|
/**
|
|
2062
2060
|
* Sweep live entries; expire those past `sentAt + ackTimeoutMs`.
|
|
2063
|
-
* Fires `onTimeout(entry, elapsedMs)`; entries move to graveyard
|
|
2064
|
-
* Driven externally by `MllpClient`'s periodic sweep tick
|
|
2065
|
-
* itself owns no timers
|
|
2061
|
+
* Fires `onTimeout(entry, elapsedMs)`; entries move to graveyard.
|
|
2062
|
+
* Driven externally by `MllpClient`'s periodic sweep tick, Correlator
|
|
2063
|
+
* itself owns no timers.
|
|
2066
2064
|
*/
|
|
2067
2065
|
expireDue(now) {
|
|
2068
2066
|
const t = now ?? this._opts.now();
|
|
@@ -2084,8 +2082,7 @@ var Correlator = class {
|
|
|
2084
2082
|
/**
|
|
2085
2083
|
* Reject every live entry with `reason` (insertion order) and clear
|
|
2086
2084
|
* the live store. Graveyard is left intact (ages out via lazy eviction).
|
|
2087
|
-
* Used by
|
|
2088
|
-
* and `MllpClient.close()` to cancel pending sends.
|
|
2085
|
+
* Used by `MllpClient`'s correlator teardown to cancel pending sends.
|
|
2089
2086
|
*/
|
|
2090
2087
|
clear(reason) {
|
|
2091
2088
|
for (const entry of this._pending.values()) entry.reject(reason);
|
|
@@ -2093,14 +2090,14 @@ var Correlator = class {
|
|
|
2093
2090
|
this._queueBytes = 0;
|
|
2094
2091
|
this._inFlight = 0;
|
|
2095
2092
|
}
|
|
2096
|
-
/** Iterate live entries in insertion order (
|
|
2093
|
+
/** Iterate live entries in insertion order (reconnect-resend). */
|
|
2097
2094
|
*liveEntries() {
|
|
2098
2095
|
for (const entry of this._pending.values()) yield entry;
|
|
2099
2096
|
}
|
|
2100
2097
|
/**
|
|
2101
2098
|
* Remove a live entry by key WITHOUT resolving/rejecting. Returns the
|
|
2102
2099
|
* removed entry, or `null` if no entry with that key exists.
|
|
2103
|
-
* Used by `MllpClient.send()` for AbortSignal cleanup and
|
|
2100
|
+
* Used by `MllpClient.send()` for AbortSignal cleanup and by the
|
|
2104
2101
|
* reconnect-reject FSM walk.
|
|
2105
2102
|
*/
|
|
2106
2103
|
remove(key) {
|
|
@@ -2121,7 +2118,7 @@ var Correlator = class {
|
|
|
2121
2118
|
inFlight: this._inFlight
|
|
2122
2119
|
};
|
|
2123
2120
|
}
|
|
2124
|
-
/** Lazy graveyard eviction
|
|
2121
|
+
/** Lazy graveyard eviction: drop entries past `timedOutAt + 2 * ackTimeoutMs`. */
|
|
2125
2122
|
_evictGraveyardDue(now) {
|
|
2126
2123
|
const threshold = 2 * this._opts.ackTimeoutMs;
|
|
2127
2124
|
for (const [key, entry] of this._graveyard) {
|
|
@@ -2214,7 +2211,7 @@ var MllpBackpressureError = class _MllpBackpressureError extends Error {
|
|
|
2214
2211
|
queueDepth;
|
|
2215
2212
|
/** Total bytes of in-flight + queued frames at the moment of rejection. */
|
|
2216
2213
|
queueBytes;
|
|
2217
|
-
/** The high-water-mark configuration that was triggered
|
|
2214
|
+
/** The high-water-mark configuration that was triggered. */
|
|
2218
2215
|
highWaterMark;
|
|
2219
2216
|
/**
|
|
2220
2217
|
* Construct an MLLP backpressure error.
|
|
@@ -2244,15 +2241,15 @@ var MllpClient = class extends EventEmitter {
|
|
|
2244
2241
|
* is attached, `state` mirrors `_connection.state`.
|
|
2245
2242
|
*/
|
|
2246
2243
|
_state = "DISCONNECTED";
|
|
2247
|
-
/** Per-message ACK timeout in ms
|
|
2244
|
+
/** Per-message ACK timeout in ms. Resolved at construction. */
|
|
2248
2245
|
_ackTimeoutMs;
|
|
2249
|
-
/** controlId-mode flag
|
|
2246
|
+
/** controlId-mode flag. `false` → FIFO. */
|
|
2250
2247
|
_correlateByControlId;
|
|
2251
|
-
/** Unified ACK correlator
|
|
2248
|
+
/** Unified ACK correlator. Built during `_attachConnection`. */
|
|
2252
2249
|
_correlator = null;
|
|
2253
2250
|
/**
|
|
2254
2251
|
* Periodic ACK-timeout sweep timer. Drives `_correlator.expireDue()` because
|
|
2255
|
-
* the Correlator is timer-free
|
|
2252
|
+
* the Correlator is timer-free. Cleared on close/destroy.
|
|
2256
2253
|
*/
|
|
2257
2254
|
_ackSweepTimer = null;
|
|
2258
2255
|
// ── Reconnect state (Plan 04, CLIENT-05/06/12/17/18) ─────────────────────
|
|
@@ -2265,12 +2262,12 @@ var MllpClient = class extends EventEmitter {
|
|
|
2265
2262
|
/** 0-indexed attempt counter for the current reconnect cycle. */
|
|
2266
2263
|
_attempt = 0;
|
|
2267
2264
|
/**
|
|
2268
|
-
*
|
|
2265
|
+
* Total reconnect attempts since construction, surfaced as
|
|
2269
2266
|
* `getStats().reconnectAttempts`. Incremented at the entry of every
|
|
2270
2267
|
* `_handleDisconnect` invocation that proceeds to schedule a backoff.
|
|
2271
2268
|
*/
|
|
2272
2269
|
_reconnectAttempts = 0;
|
|
2273
|
-
/** Epoch ms of the last successful ACK. Drives
|
|
2270
|
+
/** Epoch ms of the last successful ACK. Drives backoff-reset. */
|
|
2274
2271
|
_lastSuccessAt = null;
|
|
2275
2272
|
/** Epoch ms when the current reconnect cycle began. `null` outside a cycle. */
|
|
2276
2273
|
_reconnectCycleStartedAt = null;
|
|
@@ -2287,12 +2284,12 @@ var MllpClient = class extends EventEmitter {
|
|
|
2287
2284
|
_onBackpressure;
|
|
2288
2285
|
/** Pipeline flag; default `true` (parallel up to highWaterMark). */
|
|
2289
2286
|
_pipeline;
|
|
2290
|
-
/** Dead-peer idle timer
|
|
2287
|
+
/** Dead-peer idle timer. `null` when not armed. */
|
|
2291
2288
|
_deadPeerTimer = null;
|
|
2292
2289
|
/** True once the self-'ack' listener that resets the dead-peer timer
|
|
2293
2290
|
* has been attached. Guards against duplicate listeners on reconnect. */
|
|
2294
2291
|
_ackResetWired = false;
|
|
2295
|
-
/** Most recently bound `connect()` signal. Reread on every RetryContext build
|
|
2292
|
+
/** Most recently bound `connect()` signal. Reread on every RetryContext build. */
|
|
2296
2293
|
_connectSignal;
|
|
2297
2294
|
/** Set when close()/destroy()/abort fires; reconnect handler short-circuits. */
|
|
2298
2295
|
_userClosed = false;
|
|
@@ -2301,13 +2298,13 @@ var MllpClient = class extends EventEmitter {
|
|
|
2301
2298
|
/** Listener-removal handle for the abort listener bound in connect(). */
|
|
2302
2299
|
_abortListener = null;
|
|
2303
2300
|
/**
|
|
2304
|
-
* Test-only seam
|
|
2301
|
+
* Test-only seam, when set, `_beginReconnectAttempt` builds the new
|
|
2305
2302
|
* Connection through this factory instead of opening a real net.Socket.
|
|
2306
2303
|
*
|
|
2307
2304
|
* @internal
|
|
2308
2305
|
*/
|
|
2309
2306
|
_reconnectFactory = null;
|
|
2310
|
-
// ── PLAN-06
|
|
2307
|
+
// ── PLAN-06, observability counters for getStats (OBS-01, D-26) ─────────
|
|
2311
2308
|
/** Total successful `conn.send()` flushes since construction. */
|
|
2312
2309
|
_sentTotal = 0;
|
|
2313
2310
|
/** Total ACKs resolved since construction. */
|
|
@@ -2321,7 +2318,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2321
2318
|
/**
|
|
2322
2319
|
* Aggregated Correlator-emitted warning counts (MLLP_ACK_*). Connection-level
|
|
2323
2320
|
* warnings are read directly from `_connection.getStats().warningsByCode`
|
|
2324
|
-
* at observation time and merged into the snapshot
|
|
2321
|
+
* at observation time and merged into the snapshot.
|
|
2325
2322
|
*/
|
|
2326
2323
|
_aggregatedWarningsByCode = {};
|
|
2327
2324
|
/**
|
|
@@ -2361,9 +2358,9 @@ var MllpClient = class extends EventEmitter {
|
|
|
2361
2358
|
return this._connection?.state ?? this._state;
|
|
2362
2359
|
}
|
|
2363
2360
|
/**
|
|
2364
|
-
* Open a TCP (or TLS
|
|
2365
|
-
* and attach a
|
|
2366
|
-
* enters `CONNECTED
|
|
2361
|
+
* Open a TCP (or TLS) connection to the configured `host:port`
|
|
2362
|
+
* and attach a {@link Connection} to it. Resolves once the FSM
|
|
2363
|
+
* enters `CONNECTED`, for TLS, on `'secureConnect'` (handshake complete,
|
|
2367
2364
|
* including certificate verification when it is on).
|
|
2368
2365
|
*
|
|
2369
2366
|
* Rejects with:
|
|
@@ -2383,11 +2380,11 @@ var MllpClient = class extends EventEmitter {
|
|
|
2383
2380
|
*
|
|
2384
2381
|
* **TLS 1.3 + mutual TLS caveat (RFC 8446 §4.4.2):** `connect()` resolving
|
|
2385
2382
|
* does NOT guarantee that a `clientAuth: 'MUST'` server accepted your
|
|
2386
|
-
* client certificate. Under TLS 1.3 the client's handshake
|
|
2387
|
-
* `'secureConnect'
|
|
2383
|
+
* client certificate. Under TLS 1.3 the client's handshake, and its
|
|
2384
|
+
* `'secureConnect'`, can complete before the server finishes validating
|
|
2388
2385
|
* the certificate; a rejection then surfaces moments later as a typed
|
|
2389
2386
|
* post-connect error (`'error'` event with an `ERR_SSL_*`/alert cause,
|
|
2390
|
-
* classified **permanent
|
|
2387
|
+
* classified **permanent**, no auto-reconnect loop). ACK correlation via
|
|
2391
2388
|
* {@link MllpClient.send} remains the delivery guarantee: no send resolves
|
|
2392
2389
|
* without its ACK, so a rejected session can never silently "deliver".
|
|
2393
2390
|
*
|
|
@@ -2470,8 +2467,8 @@ var MllpClient = class extends EventEmitter {
|
|
|
2470
2467
|
* Build the raw socket + `Transport` pair for a connect / reconnect attempt.
|
|
2471
2468
|
*
|
|
2472
2469
|
* Plaintext (`ClientOptions.tls` unset): a `net.Socket` wrapped in
|
|
2473
|
-
* `NetTransport`. TLS
|
|
2474
|
-
* `TlsTransport
|
|
2470
|
+
* `NetTransport`. TLS: a `tls.TLSSocket` wrapped in
|
|
2471
|
+
* `TlsTransport`, verification defaults **on**
|
|
2475
2472
|
* (`rejectUnauthorized: !allowUnverified`), floor `minVersion: 'TLSv1.2'`
|
|
2476
2473
|
* (the IHE ATNA ITI-19 BCP195 floor), `servername` defaulting to
|
|
2477
2474
|
* `ClientOptions.host`.
|
|
@@ -2500,14 +2497,14 @@ var MllpClient = class extends EventEmitter {
|
|
|
2500
2497
|
}
|
|
2501
2498
|
/**
|
|
2502
2499
|
* Wrap a connect-phase socket error as `MllpConnectionError`, classifying
|
|
2503
|
-
* TLS failures
|
|
2500
|
+
* TLS failures into the additive `connectionCause`:
|
|
2504
2501
|
*
|
|
2505
|
-
* - `'tls-verify'
|
|
2502
|
+
* - `'tls-verify'`, certificate-verification failures
|
|
2506
2503
|
* ({@link isTlsVerificationErrorCode}).
|
|
2507
|
-
* - `'tls-handshake'
|
|
2504
|
+
* - `'tls-handshake'`, TLS-**protocol**-shaped failures only
|
|
2508
2505
|
* ({@link isTlsProtocolError}): `ERR_SSL_*`, `EPROTO`, OpenSSL
|
|
2509
2506
|
* alert-bearing errors.
|
|
2510
|
-
* - **No `connectionCause
|
|
2507
|
+
* - **No `connectionCause`**, pure TCP-level failures (`ECONNREFUSED`,
|
|
2511
2508
|
* `ETIMEDOUT`, …) even on a TLS-configured connection; these carry the
|
|
2512
2509
|
* same shape as plaintext connect failures.
|
|
2513
2510
|
*/
|
|
@@ -2535,7 +2532,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2535
2532
|
/**
|
|
2536
2533
|
* Emit a client event, containing a throwing subscriber.
|
|
2537
2534
|
*
|
|
2538
|
-
* Every event this client emits is reached from a callback we do not own
|
|
2535
|
+
* Every event this client emits is reached from a callback we do not own, a socket's
|
|
2539
2536
|
* `'connect'`/`'secureConnect'`/`'data'`/`'error'` listener, or a backoff timer. A throwing
|
|
2540
2537
|
* subscriber would unwind into it and kill the process, and on several paths it would also skip
|
|
2541
2538
|
* the work queued after the emit (the `resolve()` of `connect()`, the backoff scheduling).
|
|
@@ -2548,8 +2545,8 @@ var MllpClient = class extends EventEmitter {
|
|
|
2548
2545
|
});
|
|
2549
2546
|
}
|
|
2550
2547
|
/**
|
|
2551
|
-
* Emit the per-connection insecure-TLS warning
|
|
2552
|
-
* `tls.allowUnverified === true
|
|
2548
|
+
* Emit the per-connection insecure-TLS warning when
|
|
2549
|
+
* `tls.allowUnverified === true`, fires on EVERY successful
|
|
2553
2550
|
* `secureConnect`, initial connect and every reconnect. Emits both a frozen
|
|
2554
2551
|
* `'securityWarning'` event and `process.emitWarning`. No-op for plaintext
|
|
2555
2552
|
* connections or when verification is on.
|
|
@@ -2558,7 +2555,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2558
2555
|
const tlsOpt = this._opts.tls;
|
|
2559
2556
|
if (tlsOpt === void 0 || tlsOpt === true) return;
|
|
2560
2557
|
if (tlsOpt.allowUnverified !== true) return;
|
|
2561
|
-
const message = "MLLP TLS certificate verification is DISABLED (allowUnverified: true)
|
|
2558
|
+
const message = "MLLP TLS certificate verification is DISABLED (allowUnverified: true), this connection does not authenticate the peer.";
|
|
2562
2559
|
const warning = Object.freeze({
|
|
2563
2560
|
code: MLLP_TLS_VERIFY_DISABLED,
|
|
2564
2561
|
message,
|
|
@@ -2571,11 +2568,11 @@ var MllpClient = class extends EventEmitter {
|
|
|
2571
2568
|
}
|
|
2572
2569
|
/**
|
|
2573
2570
|
* Wire a Connection's events through to this MllpClient. Every re-emitted
|
|
2574
|
-
* payload is `Object.freeze`'d before emission
|
|
2575
|
-
* Connection layer already freezes
|
|
2571
|
+
* payload is `Object.freeze`'d before emission, even though the
|
|
2572
|
+
* Connection layer already freezes, defense-in-depth, harmless on
|
|
2576
2573
|
* already-frozen objects.
|
|
2577
2574
|
*
|
|
2578
|
-
* Builds the unified `Correlator`
|
|
2575
|
+
* Builds the unified `Correlator` bound to this Connection and
|
|
2579
2576
|
* arms the periodic ACK-timeout sweep. The Correlator is teardown-aware:
|
|
2580
2577
|
* `close()` / `destroy()` clear the sweep timer and reject pending sends.
|
|
2581
2578
|
*
|
|
@@ -2586,7 +2583,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2586
2583
|
this._correlator = new Correlator({
|
|
2587
2584
|
mode: this._correlateByControlId ? "controlId" : "fifo",
|
|
2588
2585
|
ackTimeoutMs: this._ackTimeoutMs,
|
|
2589
|
-
// Plan 05
|
|
2586
|
+
// Plan 05, pipeline:false collapses the in-flight set to ≤1 (D-06).
|
|
2590
2587
|
maxInFlight: this._pipeline ? Number.POSITIVE_INFINITY : 1,
|
|
2591
2588
|
onWarning: (code, ctx) => {
|
|
2592
2589
|
this._aggregatedWarningsByCode[code] = (this._aggregatedWarningsByCode[code] ?? 0) + 1;
|
|
@@ -2685,7 +2682,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2685
2682
|
}
|
|
2686
2683
|
}
|
|
2687
2684
|
/**
|
|
2688
|
-
* Arm (or re-arm) the dead-peer idle timer
|
|
2685
|
+
* Arm (or re-arm) the dead-peer idle timer.
|
|
2689
2686
|
* No-op when `deadPeerTimeoutMs` is unset.
|
|
2690
2687
|
*/
|
|
2691
2688
|
_armDeadPeerTimer() {
|
|
@@ -2699,8 +2696,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2699
2696
|
this._deadPeerTimer.unref();
|
|
2700
2697
|
}
|
|
2701
2698
|
/**
|
|
2702
|
-
* Clear the dead-peer idle timer
|
|
2703
|
-
* every transition out of CONNECTED).
|
|
2699
|
+
* Clear the dead-peer idle timer on every transition out of CONNECTED.
|
|
2704
2700
|
*/
|
|
2705
2701
|
_clearDeadPeerTimer() {
|
|
2706
2702
|
if (this._deadPeerTimer !== null) {
|
|
@@ -2709,22 +2705,22 @@ var MllpClient = class extends EventEmitter {
|
|
|
2709
2705
|
}
|
|
2710
2706
|
}
|
|
2711
2707
|
/**
|
|
2712
|
-
* Disconnect handler
|
|
2708
|
+
* Disconnect handler, reconnect FSM core.
|
|
2713
2709
|
*
|
|
2714
2710
|
* Implements:
|
|
2715
|
-
* -
|
|
2711
|
+
* - Hybrid in-flight handling: controlId mode preserves
|
|
2716
2712
|
* pending sends for resend; FIFO mode rejects in-flight with
|
|
2717
2713
|
* `connectionCause: 'in-flight-orphan'` and queued with `'fifo-unsafe'`.
|
|
2718
|
-
* -
|
|
2714
|
+
* - Classifier-first: permanent errors
|
|
2719
2715
|
* transition straight to CLOSED without invoking `retryStrategy`.
|
|
2720
|
-
* -
|
|
2716
|
+
* - Backoff-reset on recent success: first disconnect after a
|
|
2721
2717
|
* successful ACK on the prior session resets `_attempt` to 0.
|
|
2722
|
-
* -
|
|
2723
|
-
* -
|
|
2724
|
-
* -
|
|
2718
|
+
* - `_reconnectAttempts` counter increment.
|
|
2719
|
+
* - Frozen RetryContext; a `null` return from the strategy halts reconnection.
|
|
2720
|
+
* - Default exponential strategy.
|
|
2725
2721
|
*
|
|
2726
|
-
* Invoked from the SINGLE `_onStateChange` hook
|
|
2727
|
-
*
|
|
2722
|
+
* Invoked from the SINGLE `_onStateChange` hook, no parallel listener.
|
|
2723
|
+
* Idempotent across same-cycle re-entry: cycle-start flag
|
|
2728
2724
|
* coordinates first-disconnect vs subsequent-within-cycle behavior.
|
|
2729
2725
|
*/
|
|
2730
2726
|
_handleDisconnect(err) {
|
|
@@ -2824,7 +2820,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2824
2820
|
this._backoffTimer.unref();
|
|
2825
2821
|
}
|
|
2826
2822
|
/**
|
|
2827
|
-
* Default retry strategy
|
|
2823
|
+
* Default retry strategy: `min(maxDelay, initialDelay * multiplier^attempt)`
|
|
2828
2824
|
* with ±jitter applied.
|
|
2829
2825
|
*/
|
|
2830
2826
|
_defaultRetryStrategy = (ctx) => {
|
|
@@ -2912,7 +2908,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2912
2908
|
this._reconnectCycleStartedAt = null;
|
|
2913
2909
|
}
|
|
2914
2910
|
/**
|
|
2915
|
-
* Test seam
|
|
2911
|
+
* Test seam, install a factory that produces the next reconnect Connection.
|
|
2916
2912
|
*
|
|
2917
2913
|
* @internal
|
|
2918
2914
|
*/
|
|
@@ -2920,7 +2916,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
2920
2916
|
this._reconnectFactory = factory;
|
|
2921
2917
|
}
|
|
2922
2918
|
/**
|
|
2923
|
-
* Test seam
|
|
2919
|
+
* Test seam, capture or rebind the connect-signal mid-flight.
|
|
2924
2920
|
*
|
|
2925
2921
|
* @internal
|
|
2926
2922
|
*/
|
|
@@ -2946,12 +2942,11 @@ var MllpClient = class extends EventEmitter {
|
|
|
2946
2942
|
*
|
|
2947
2943
|
* - FIFO mode: passes `null` controlId so `matchAck` returns the head of
|
|
2948
2944
|
* the live store.
|
|
2949
|
-
* - controlId mode: extracts MSA-2
|
|
2950
|
-
*
|
|
2945
|
+
* - controlId mode: extracts MSA-2 and passes it to `matchAck` for keyed
|
|
2946
|
+
* lookup.
|
|
2951
2947
|
*
|
|
2952
2948
|
* Called from the SINGLE `'message'` listener registered in `_attachConnection`.
|
|
2953
|
-
* No parallel listener is registered
|
|
2954
|
-
* anchor (B-04).
|
|
2949
|
+
* No parallel listener is registered.
|
|
2955
2950
|
*/
|
|
2956
2951
|
_onAckPayload(ackPayload, byteOffset) {
|
|
2957
2952
|
if (this._correlator === null) return;
|
|
@@ -2964,10 +2959,8 @@ var MllpClient = class extends EventEmitter {
|
|
|
2964
2959
|
/**
|
|
2965
2960
|
* Single source-of-truth for a successfully matched ACK (live-store hit).
|
|
2966
2961
|
*
|
|
2967
|
-
*
|
|
2968
|
-
*
|
|
2969
|
-
* PLAN-06 extends at HOOK_EXTENSION_POINT: ack-matched to bump _ackedTotal and
|
|
2970
|
-
* set _lastAckAt.
|
|
2962
|
+
* Emits the frozen 'ack' event and resolves the pending send. Updates
|
|
2963
|
+
* `_lastSuccessAt`, `_ackedTotal` and `_lastAckAt`.
|
|
2971
2964
|
*
|
|
2972
2965
|
* Called from _onAckPayload when matchAck() returns a non-null PendingAck.
|
|
2973
2966
|
*/
|
|
@@ -2989,9 +2982,9 @@ var MllpClient = class extends EventEmitter {
|
|
|
2989
2982
|
}
|
|
2990
2983
|
/**
|
|
2991
2984
|
* Emit a frozen `'drain'` event when the queue depth and bytes fall below
|
|
2992
|
-
* both configured caps
|
|
2985
|
+
* both configured caps. Called from `_onAckMatched`
|
|
2993
2986
|
* (every successful ACK) and from the Correlator's `onTimeout` callback
|
|
2994
|
-
* (every expired send)
|
|
2987
|
+
* (every expired send), both code paths free a live-store slot.
|
|
2995
2988
|
*/
|
|
2996
2989
|
_maybeEmitDrain() {
|
|
2997
2990
|
const corr = this._correlator;
|
|
@@ -3011,12 +3004,9 @@ var MllpClient = class extends EventEmitter {
|
|
|
3011
3004
|
/**
|
|
3012
3005
|
* Single source-of-truth for Connection FSM transitions.
|
|
3013
3006
|
*
|
|
3014
|
-
*
|
|
3015
|
-
*
|
|
3016
|
-
*
|
|
3017
|
-
* CONNECTED → DISCONNECTED|RECONNECTING and trigger _handleDisconnect.
|
|
3018
|
-
* Plan 05 extends at HOOK_EXTENSION_POINT: state-change to clear/arm
|
|
3019
|
-
* dead-peer timer on transitions out of / into CONNECTED.
|
|
3007
|
+
* Re-emits a frozen 'stateChange' event, detects
|
|
3008
|
+
* CONNECTED → DISCONNECTED|RECONNECTING to trigger `_handleDisconnect`, and
|
|
3009
|
+
* clears or arms the dead-peer timer on transitions out of / into CONNECTED.
|
|
3020
3010
|
*
|
|
3021
3011
|
* Called from the SINGLE 'stateChange' listener registered in _attachConnection.
|
|
3022
3012
|
*/
|
|
@@ -3047,17 +3037,16 @@ var MllpClient = class extends EventEmitter {
|
|
|
3047
3037
|
}
|
|
3048
3038
|
}
|
|
3049
3039
|
/**
|
|
3050
|
-
* Send an MLLP-framed payload and await the inbound ACK
|
|
3040
|
+
* Send an MLLP-framed payload and await the inbound ACK.
|
|
3051
3041
|
*
|
|
3052
3042
|
* Resolves with the ACK Buffer (framing stripped). Rejects with:
|
|
3053
|
-
* - `DOMException('Aborted', 'AbortError')` if `signal` aborts before ACK
|
|
3054
|
-
*
|
|
3055
|
-
* - `MllpTimeoutError` if no ACK arrives within `ackTimeoutMs` (ERR-02).
|
|
3043
|
+
* - `DOMException('Aborted', 'AbortError')` if `signal` aborts before the ACK.
|
|
3044
|
+
* - `MllpTimeoutError` if no ACK arrives within `ackTimeoutMs`.
|
|
3056
3045
|
* The clock starts at the underlying `write()` flush callback, NOT at
|
|
3057
|
-
* the `send()` call
|
|
3046
|
+
* the `send()` call.
|
|
3058
3047
|
* - `MllpConnectionError({ phase: 'send' })` if the client is not connected.
|
|
3059
3048
|
*
|
|
3060
|
-
* Emits a frozen `'ack'` event on every successful match
|
|
3049
|
+
* Emits a frozen `'ack'` event on every successful match.
|
|
3061
3050
|
*
|
|
3062
3051
|
* @example
|
|
3063
3052
|
* ```typescript
|
|
@@ -3066,7 +3055,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
3066
3055
|
* ```
|
|
3067
3056
|
*
|
|
3068
3057
|
* @param payload Raw bytes; MLLP framing is added internally via `encodeFrame`.
|
|
3069
|
-
* @param opts.signal AbortSignal
|
|
3058
|
+
* @param opts.signal AbortSignal, aborting cancels the ACK wait.
|
|
3070
3059
|
*/
|
|
3071
3060
|
send(payload, opts) {
|
|
3072
3061
|
const signal = opts?.signal;
|
|
@@ -3154,12 +3143,12 @@ var MllpClient = class extends EventEmitter {
|
|
|
3154
3143
|
});
|
|
3155
3144
|
}
|
|
3156
3145
|
/**
|
|
3157
|
-
* 'wait'-mode backpressure handler
|
|
3146
|
+
* 'wait'-mode backpressure handler.
|
|
3158
3147
|
*
|
|
3159
3148
|
* Awaits one of three terminating signals, in order:
|
|
3160
3149
|
* - `'drain'` event → re-enter `send()` (the gate will now pass).
|
|
3161
3150
|
* - `ackTimeoutMs` elapses → reject with `MllpTimeoutError`.
|
|
3162
|
-
* - Caller's `signal` aborts → reject with `AbortError
|
|
3151
|
+
* - Caller's `signal` aborts → reject with `AbortError`. Cleanup
|
|
3163
3152
|
* removes the drain listener AND the abort listener AND clears the
|
|
3164
3153
|
* timer to prevent leaks.
|
|
3165
3154
|
*/
|
|
@@ -3216,7 +3205,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
3216
3205
|
this._clearDeadPeerTimer();
|
|
3217
3206
|
}
|
|
3218
3207
|
/**
|
|
3219
|
-
* **Test seam
|
|
3208
|
+
* **Test seam**, attach an externally-built {@link Connection} directly,
|
|
3220
3209
|
* bypassing the `net.createConnection` + `NetTransport` path. Used by
|
|
3221
3210
|
* lifecycle tests driving `InMemoryTransport.pair()` for determinism.
|
|
3222
3211
|
*
|
|
@@ -3295,7 +3284,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
3295
3284
|
}
|
|
3296
3285
|
}
|
|
3297
3286
|
/**
|
|
3298
|
-
* Abruptly destroy the client
|
|
3287
|
+
* Abruptly destroy the client, force-transitions the underlying Connection
|
|
3299
3288
|
* to `CLOSED` immediately. No-op if no Connection is attached. Idempotent.
|
|
3300
3289
|
*
|
|
3301
3290
|
* @example
|
|
@@ -3319,9 +3308,9 @@ var MllpClient = class extends EventEmitter {
|
|
|
3319
3308
|
conn.destroy(reason);
|
|
3320
3309
|
}
|
|
3321
3310
|
/**
|
|
3322
|
-
* Returns a JSON-serializable observability snapshot
|
|
3311
|
+
* Returns a JSON-serializable observability snapshot.
|
|
3323
3312
|
*
|
|
3324
|
-
* All fields are plain values
|
|
3313
|
+
* All fields are plain values, no Buffers, no class instances, no Maps,
|
|
3325
3314
|
* no circular refs. Safe to `JSON.stringify` directly.
|
|
3326
3315
|
*
|
|
3327
3316
|
* `inFlight` is the count of correlator entries with `sentAt !== null`
|
|
@@ -3365,7 +3354,7 @@ var MllpClient = class extends EventEmitter {
|
|
|
3365
3354
|
};
|
|
3366
3355
|
}
|
|
3367
3356
|
/**
|
|
3368
|
-
* Async disposal
|
|
3357
|
+
* Async disposal, delegates to {@link MllpClient.close} for `await using` support.
|
|
3369
3358
|
*
|
|
3370
3359
|
* @example
|
|
3371
3360
|
* ```typescript
|
|
@@ -3421,7 +3410,7 @@ async function createStarterClient(opts) {
|
|
|
3421
3410
|
}
|
|
3422
3411
|
|
|
3423
3412
|
// src/index.ts
|
|
3424
|
-
var VERSION = "0.0.
|
|
3413
|
+
var VERSION = "0.0.3";
|
|
3425
3414
|
|
|
3426
3415
|
export { Connection, FrameReader, MLLP_BIND_ALL_INTERFACES, MLLP_TLS_VERIFY_DISABLED, MllpAckError, MllpBackpressureError, MllpClient, MllpConnectionError, MllpFramingError, MllpServer, MllpTimeoutError, NetTransport, TlsTransport, VERSION, buildRawAck, createClient, createServer, createStarterClient, createStarterServer, createWarning, encodeFrame, isTlsProtocolError, isTlsVerificationErrorCode, isTransientConnectionError, rawAckUncorrelatable, resolveNackCode };
|
|
3427
3416
|
//# sourceMappingURL=index.mjs.map
|