@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/testing/index.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
40
40
|
/**
|
|
41
41
|
* Write `buf` to the peer transport.
|
|
42
42
|
*
|
|
43
|
-
* If the peer is paused, the buffer is copied and queued
|
|
43
|
+
* If the peer is paused, the buffer is copied and queued, returns `false`
|
|
44
44
|
* (backpressure signal). Otherwise, the peer's `onData` handler fires
|
|
45
45
|
* synchronously before this method returns.
|
|
46
46
|
*
|
|
@@ -58,7 +58,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
58
58
|
return true;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* Initiate graceful close
|
|
61
|
+
* Initiate graceful close, fires `onClose` on this end and on the peer,
|
|
62
62
|
* simulating a TCP FIN exchange.
|
|
63
63
|
*/
|
|
64
64
|
close() {
|
|
@@ -178,7 +178,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
178
178
|
this._paused = true;
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
181
|
-
* Resume delivery
|
|
181
|
+
* Resume delivery, flushes all queued chunks to the `onData` handler,
|
|
182
182
|
* synchronously, in the order they were written.
|
|
183
183
|
*/
|
|
184
184
|
resume() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/in-memory-transport.ts"],"names":[],"mappings":";;;AAuCO,IAAM,iBAAA,GAAN,MAAM,kBAAA,CAAuC;AAAA,EAC1C,KAAA,GAAkC,IAAA;AAAA,EAElC,SAAA,GAA8C,IAAA;AAAA,EAC9C,YAAA,GAAoC,IAAA;AAAA,EACpC,UAAA,GAAkC,IAAA;AAAA,EAClC,UAAA,GAA4C,IAAA;AAAA,EAE5C,UAAA,GAAa,KAAA;AAAA,EACb,OAAA,GAAU,KAAA;AAAA,EACV,WAAA,GAAc,CAAA;AAAA,EACd,iBAA2B,EAAC;AAAA,EAC5B,WAAA,GAAc,CAAA;AAAA,EAEd,WAAA,GAAc;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBvB,OAAO,IAAA,GAA+C;AACpD,IAAA,MAAM,CAAA,GAAI,IAAI,kBAAA,EAAkB;AAChC,IAAA,MAAM,CAAA,GAAI,IAAI,kBAAA,EAAkB;AAChC,IAAA,CAAA,CAAE,KAAA,GAAQ,CAAA;AACV,IAAA,CAAA,CAAE,KAAA,GAAQ,CAAA;AACV,IAAA,OAAO,CAAC,GAAG,CAAC,CAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,GAAA,EAAsB;AAC1B,IAAA,IAAI,IAAA,CAAK,YAAY,OAAO,KAAA;AAC5B,IAAA,MAAM,OAAO,IAAA,CAAK,KAAA;AAClB,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,CAAK,UAAA,EAAY,OAAO,KAAA;AAE7C,IAAA,IAAI,KAAK,OAAA,EAAS;AAEhB,MAAA,IAAA,CAAK,cAAA,CAAe,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AACzC,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,IAAA,CAAK,cAAc,GAAG,CAAA;AACtB,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAA,GAAc;AACZ,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAElB,IAAA,IAAI,IAAA,CAAK,eAAe,IAAA,EAAM;AAC5B,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,UAAA,EAAW;AAAA,MAClB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAEA,IAAA,MAAM,OAAO,IAAA,CAAK,KAAA;AAClB,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,CAAC,IAAA,CAAK,UAAA,EAAY;AACrC,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA,IAAI,IAAA,CAAK,eAAe,IAAA,EAAM;AAC5B,QAAA,IAAI;AACF,UAAA,IAAA,CAAK,UAAA,EAAW;AAAA,QAClB,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAQ,MAAA,EAAsB;AAC5B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,MAAM,GAAA,GAAM,MAAA,IAAU,IAAI,KAAA,CAAM,6BAA6B,CAAA;AAC7D,IAAA,IAAI,IAAA,CAAK,eAAe,IAAA,EAAM;AAC5B,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,WAAW,GAAG,CAAA;AAAA,MACrB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAI,IAAA,CAAK,eAAe,IAAA,EAAM;AAC5B,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,UAAA,EAAW;AAAA,MAClB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,EAAA,EAAmC;AACxC,IAAA,IAAA,CAAK,SAAA,GAAY,EAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,EAAA,EAAsB;AAC9B,IAAA,IAAA,CAAK,YAAA,GAAe,EAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,EAAA,EAAsB;AAC5B,IAAA,IAAA,CAAK,UAAA,GAAa,EAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,EAAA,EAAgC;AACtC,IAAA,IAAA,CAAK,UAAA,GAAa,EAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,aAAA,EAA6B;AACjC,IAAA,IAAA,CAAK,WAAA,GAAc,aAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAA,GAAe;AACb,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AACf,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,CAAe,MAAA,CAAO,CAAC,CAAA;AAC5C,IAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,MAAA,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,eAAA,GAAwB;AACtB,IAAA,IAAI,IAAA,CAAK,iBAAiB,IAAA,EAAM;AAC9B,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,YAAA,EAAa;AAAA,MACpB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,GAAA,EAAmB;AACvC,IAAA,IAAI,IAAA,CAAK,cAAc,IAAA,EAAM;AAI7B,IAAA,IAAI,IAAA,CAAK,cAAc,CAAA,EAAG;AACxB,MAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,IAChE;AAEA,IAAA,IAAI,IAAA,CAAK,cAAc,CAAA,EAAG;AACxB,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,OAAO,MAAA,GAAS,IAAI,MAAA,EAAQ;AAC1B,QAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,SAAS,IAAA,CAAK,WAAA,EAAa,IAAI,MAAM,CAAA;AAC1D,QAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,QAAA,CAAS,MAAA,EAAQ,GAAG,CAAA;AACtC,QAAA,IAAA,CAAK,WAAA,EAAA;AACL,QAAA,IAAI;AACF,UAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AAAA,QACtB,CAAA,SAAE;AACA,UAAA,IAAA,CAAK,WAAA,EAAA;AAAA,QACP;AACA,QAAA,MAAA,GAAS,GAAA;AAAA,MACX;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,WAAA,EAAA;AACL,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,UAAU,GAAG,CAAA;AAAA,MACpB,CAAA,SAAE;AACA,QAAA,IAAA,CAAK,WAAA,EAAA;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF","file":"index.cjs","sourcesContent":["/**\n * InMemoryTransport — deterministic, socket-free Transport for tests.\n *\n * Two connected ends are created via `InMemoryTransport.pair()`.\n * Writes to end A fire end B's registered `onData` handler **synchronously**\n * before `write()` returns — no async ceremony, no `await` in tests.\n *\n * @example\n * ```typescript\n * import { InMemoryTransport } from '@cosyte/mllp/testing';\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => console.log('received:', chunk));\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * // b's onData fired synchronously — log printed before this line\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Transport } from \"../transport/index.js\";\n\n/**\n * Deterministic in-memory transport for socket-free tests.\n *\n * Create connected pairs with `InMemoryTransport.pair()`. Writes to one end\n * deliver synchronously to the other end's `onData` handler — no async ceremony,\n * no `await` needed in tests.\n *\n * Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for\n * backpressure simulation, and `destroy(reason)` for abrupt-disconnect simulation.\n *\n * @example\n * ```typescript\n * const [client, server] = InMemoryTransport.pair();\n * server.onData((chunk) => server.write(chunk)); // echo\n * client.onData((echo) => console.log('echo:', echo));\n * client.write(Buffer.from('hello'));\n * ```\n */\nexport class InMemoryTransport implements Transport {\n private _peer: InMemoryTransport | null = null;\n\n private _onDataFn: ((chunk: Buffer) => void) | null = null;\n private _onConnectFn: (() => void) | null = null;\n private _onCloseFn: (() => void) | null = null;\n private _onErrorFn: ((err: Error) => void) | null = null;\n\n private _destroyed = false;\n private _paused = false;\n private _splitBytes = 0;\n private _pendingChunks: Buffer[] = [];\n private _writeDepth = 0;\n\n private constructor() {}\n\n /**\n * Create two connected InMemoryTransport ends.\n *\n * Writes to `a` deliver to `b`'s `onData` handler synchronously (and vice versa).\n * Call `a.simulateConnect()` after `pair()` to fire the `onConnect` handler,\n * simulating TCP connect completion.\n *\n * @returns Tuple `[a, b]` of two connected transports.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => b.write(chunk)); // echo server\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * ```\n */\n static pair(): [InMemoryTransport, InMemoryTransport] {\n const a = new InMemoryTransport();\n const b = new InMemoryTransport();\n a._peer = b;\n b._peer = a;\n return [a, b];\n }\n\n /**\n * Write `buf` to the peer transport.\n *\n * If the peer is paused, the buffer is copied and queued — returns `false`\n * (backpressure signal). Otherwise, the peer's `onData` handler fires\n * synchronously before this method returns.\n *\n * @returns `true` if delivered immediately; `false` if queued or transport is destroyed.\n */\n write(buf: Buffer): boolean {\n if (this._destroyed) return false;\n const peer = this._peer;\n if (peer === null || peer._destroyed) return false;\n\n if (peer._paused) {\n // Queue a copy — caller may reuse buf after write() returns (T-03-02-02)\n peer._pendingChunks.push(Buffer.from(buf));\n return false;\n }\n\n peer._deliverChunk(buf);\n return true;\n }\n\n /**\n * Initiate graceful close — fires `onClose` on this end and on the peer,\n * simulating a TCP FIN exchange.\n */\n close(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n // Fire onClose on this end\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow — test handlers must not break transport logic */\n }\n }\n // Simulate peer receiving FIN — fire peer's onClose\n const peer = this._peer;\n if (peer !== null && !peer._destroyed) {\n peer._destroyed = true;\n if (peer._onCloseFn !== null) {\n try {\n peer._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n }\n\n /**\n * Abruptly destroy this transport end.\n *\n * Fires `onError(reason)` then `onClose` on this end only. Does NOT affect\n * the peer (simulates one-sided abrupt disconnect, e.g., ECONNRESET on\n * just one socket).\n *\n * @param reason - Optional error; defaults to `new Error('InMemoryTransport destroyed')`.\n */\n destroy(reason?: Error): void {\n if (this._destroyed) return;\n this._destroyed = true;\n const err = reason ?? new Error(\"InMemoryTransport destroyed\");\n if (this._onErrorFn !== null) {\n try {\n this._onErrorFn(err);\n } catch {\n /* swallow */\n }\n }\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n /**\n * Register the data handler. Replaces any previously registered handler.\n *\n * @param fn - Called with each chunk delivered by the peer.\n */\n onData(fn: (chunk: Buffer) => void): void {\n this._onDataFn = fn;\n }\n\n /**\n * Register the connect handler. Replaces any previously registered handler.\n * Use `simulateConnect()` to trigger this in tests.\n *\n * @param fn - Called when `simulateConnect()` is invoked on this end.\n */\n onConnect(fn: () => void): void {\n this._onConnectFn = fn;\n }\n\n /**\n * Register the close handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `close()` or `destroy()` is invoked on this end,\n * or when the peer calls `close()`.\n */\n onClose(fn: () => void): void {\n this._onCloseFn = fn;\n }\n\n /**\n * Register the error handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `destroy(reason)` is invoked on this end.\n */\n onError(fn: (err: Error) => void): void {\n this._onErrorFn = fn;\n }\n\n /**\n * Simulate chunked TCP reads.\n *\n * After calling `split(n)`, any `write()` directed at this transport will be\n * delivered to the `onData` handler in chunks of `n` bytes rather than\n * all at once. Useful for verifying that a `FrameReader` reassembles frames\n * correctly across chunk boundaries.\n *\n * @param bytesPerChunk - Chunk size in bytes. Pass `0` to disable chunking.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.split(1); // b receives one byte at a time\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d])); // fires b.onData 4 times\n * ```\n */\n split(bytesPerChunk: number): void {\n this._splitBytes = bytesPerChunk;\n }\n\n /**\n * Pause delivery.\n *\n * While paused, writes directed at this transport are queued internally and\n * the `onData` handler is NOT called. The queued chunks are flushed on `resume()`.\n *\n * @example\n * ```typescript\n * b.pause();\n * a.write(buf); // queued, b.onData not called\n * b.resume(); // flushes queue synchronously\n * ```\n */\n pause(): void {\n this._paused = true;\n }\n\n /**\n * Resume delivery — flushes all queued chunks to the `onData` handler,\n * synchronously, in the order they were written.\n */\n resume(): void {\n this._paused = false;\n const pending = this._pendingChunks.splice(0);\n for (const chunk of pending) {\n this._deliverChunk(chunk);\n }\n }\n\n /**\n * Fire the `onConnect` handler on this end.\n *\n * Call after `pair()` to simulate TCP connect completion. In production code,\n * `NetTransport` fires the handler automatically when the socket emits `'connect'`.\n *\n * @example\n * ```typescript\n * const [a] = InMemoryTransport.pair();\n * a.onConnect(() => console.log('connected'));\n * a.simulateConnect(); // fires immediately\n * ```\n */\n simulateConnect(): void {\n if (this._onConnectFn !== null) {\n try {\n this._onConnectFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n private _deliverChunk(buf: Buffer): void {\n if (this._onDataFn === null) return;\n\n // Re-entrancy guard (D-03): writing from inside an onData handler would cause\n // infinite recursion or corrupt frame ordering. Detect and throw.\n if (this._writeDepth > 0) {\n throw new Error(\"InMemoryTransport: re-entrant write detected\");\n }\n\n if (this._splitBytes > 0) {\n let offset = 0;\n while (offset < buf.length) {\n const end = Math.min(offset + this._splitBytes, buf.length);\n const chunk = buf.subarray(offset, end);\n this._writeDepth++;\n try {\n this._onDataFn(chunk);\n } finally {\n this._writeDepth--;\n }\n offset = end;\n }\n } else {\n this._writeDepth++;\n try {\n this._onDataFn(buf);\n } finally {\n this._writeDepth--;\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/in-memory-transport.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * InMemoryTransport, deterministic, socket-free Transport for tests.\n *\n * Two connected ends are created via `InMemoryTransport.pair()`.\n * Writes to end A fire end B's registered `onData` handler **synchronously**\n * before `write()` returns, no async ceremony, no `await` in tests.\n *\n * @example\n * ```typescript\n * import { InMemoryTransport } from '@cosyte/mllp/testing';\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => console.log('received:', chunk));\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * // b's onData fired synchronously, log printed before this line\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Transport } from \"../transport/index.js\";\n\n/**\n * Deterministic in-memory transport for socket-free tests.\n *\n * Create connected pairs with `InMemoryTransport.pair()`. Writes to one end\n * deliver synchronously to the other end's `onData` handler, no async ceremony,\n * no `await` needed in tests.\n *\n * Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for\n * backpressure simulation, and `destroy(reason)` for abrupt-disconnect simulation.\n *\n * @example\n * ```typescript\n * const [client, server] = InMemoryTransport.pair();\n * server.onData((chunk) => server.write(chunk)); // echo\n * client.onData((echo) => console.log('echo:', echo));\n * client.write(Buffer.from('hello'));\n * ```\n */\nexport class InMemoryTransport implements Transport {\n private _peer: InMemoryTransport | null = null;\n\n private _onDataFn: ((chunk: Buffer) => void) | null = null;\n private _onConnectFn: (() => void) | null = null;\n private _onCloseFn: (() => void) | null = null;\n private _onErrorFn: ((err: Error) => void) | null = null;\n\n private _destroyed = false;\n private _paused = false;\n private _splitBytes = 0;\n private _pendingChunks: Buffer[] = [];\n private _writeDepth = 0;\n\n private constructor() {}\n\n /**\n * Create two connected InMemoryTransport ends.\n *\n * Writes to `a` deliver to `b`'s `onData` handler synchronously (and vice versa).\n * Call `a.simulateConnect()` after `pair()` to fire the `onConnect` handler,\n * simulating TCP connect completion.\n *\n * @returns Tuple `[a, b]` of two connected transports.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => b.write(chunk)); // echo server\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * ```\n */\n static pair(): [InMemoryTransport, InMemoryTransport] {\n const a = new InMemoryTransport();\n const b = new InMemoryTransport();\n a._peer = b;\n b._peer = a;\n return [a, b];\n }\n\n /**\n * Write `buf` to the peer transport.\n *\n * If the peer is paused, the buffer is copied and queued, returns `false`\n * (backpressure signal). Otherwise, the peer's `onData` handler fires\n * synchronously before this method returns.\n *\n * @returns `true` if delivered immediately; `false` if queued or transport is destroyed.\n */\n write(buf: Buffer): boolean {\n if (this._destroyed) return false;\n const peer = this._peer;\n if (peer === null || peer._destroyed) return false;\n\n if (peer._paused) {\n // Queue a copy, caller may reuse buf after write() returns (T-03-02-02)\n peer._pendingChunks.push(Buffer.from(buf));\n return false;\n }\n\n peer._deliverChunk(buf);\n return true;\n }\n\n /**\n * Initiate graceful close, fires `onClose` on this end and on the peer,\n * simulating a TCP FIN exchange.\n */\n close(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n // Fire onClose on this end\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow, test handlers must not break transport logic */\n }\n }\n // Simulate peer receiving FIN, fire peer's onClose\n const peer = this._peer;\n if (peer !== null && !peer._destroyed) {\n peer._destroyed = true;\n if (peer._onCloseFn !== null) {\n try {\n peer._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n }\n\n /**\n * Abruptly destroy this transport end.\n *\n * Fires `onError(reason)` then `onClose` on this end only. Does NOT affect\n * the peer (simulates one-sided abrupt disconnect, e.g., ECONNRESET on\n * just one socket).\n *\n * @param reason - Optional error; defaults to `new Error('InMemoryTransport destroyed')`.\n */\n destroy(reason?: Error): void {\n if (this._destroyed) return;\n this._destroyed = true;\n const err = reason ?? new Error(\"InMemoryTransport destroyed\");\n if (this._onErrorFn !== null) {\n try {\n this._onErrorFn(err);\n } catch {\n /* swallow */\n }\n }\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n /**\n * Register the data handler. Replaces any previously registered handler.\n *\n * @param fn - Called with each chunk delivered by the peer.\n */\n onData(fn: (chunk: Buffer) => void): void {\n this._onDataFn = fn;\n }\n\n /**\n * Register the connect handler. Replaces any previously registered handler.\n * Use `simulateConnect()` to trigger this in tests.\n *\n * @param fn - Called when `simulateConnect()` is invoked on this end.\n */\n onConnect(fn: () => void): void {\n this._onConnectFn = fn;\n }\n\n /**\n * Register the close handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `close()` or `destroy()` is invoked on this end,\n * or when the peer calls `close()`.\n */\n onClose(fn: () => void): void {\n this._onCloseFn = fn;\n }\n\n /**\n * Register the error handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `destroy(reason)` is invoked on this end.\n */\n onError(fn: (err: Error) => void): void {\n this._onErrorFn = fn;\n }\n\n /**\n * Simulate chunked TCP reads.\n *\n * After calling `split(n)`, any `write()` directed at this transport will be\n * delivered to the `onData` handler in chunks of `n` bytes rather than\n * all at once. Useful for verifying that a `FrameReader` reassembles frames\n * correctly across chunk boundaries.\n *\n * @param bytesPerChunk - Chunk size in bytes. Pass `0` to disable chunking.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.split(1); // b receives one byte at a time\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d])); // fires b.onData 4 times\n * ```\n */\n split(bytesPerChunk: number): void {\n this._splitBytes = bytesPerChunk;\n }\n\n /**\n * Pause delivery.\n *\n * While paused, writes directed at this transport are queued internally and\n * the `onData` handler is NOT called. The queued chunks are flushed on `resume()`.\n *\n * @example\n * ```typescript\n * b.pause();\n * a.write(buf); // queued, b.onData not called\n * b.resume(); // flushes queue synchronously\n * ```\n */\n pause(): void {\n this._paused = true;\n }\n\n /**\n * Resume delivery, flushes all queued chunks to the `onData` handler,\n * synchronously, in the order they were written.\n */\n resume(): void {\n this._paused = false;\n const pending = this._pendingChunks.splice(0);\n for (const chunk of pending) {\n this._deliverChunk(chunk);\n }\n }\n\n /**\n * Fire the `onConnect` handler on this end.\n *\n * Call after `pair()` to simulate TCP connect completion. In production code,\n * `NetTransport` fires the handler automatically when the socket emits `'connect'`.\n *\n * @example\n * ```typescript\n * const [a] = InMemoryTransport.pair();\n * a.onConnect(() => console.log('connected'));\n * a.simulateConnect(); // fires immediately\n * ```\n */\n simulateConnect(): void {\n if (this._onConnectFn !== null) {\n try {\n this._onConnectFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n private _deliverChunk(buf: Buffer): void {\n if (this._onDataFn === null) return;\n\n // Re-entrancy guard (D-03): writing from inside an onData handler would cause\n // infinite recursion or corrupt frame ordering. Detect and throw.\n if (this._writeDepth > 0) {\n throw new Error(\"InMemoryTransport: re-entrant write detected\");\n }\n\n if (this._splitBytes > 0) {\n let offset = 0;\n while (offset < buf.length) {\n const end = Math.min(offset + this._splitBytes, buf.length);\n const chunk = buf.subarray(offset, end);\n this._writeDepth++;\n try {\n this._onDataFn(chunk);\n } finally {\n this._writeDepth--;\n }\n offset = end;\n }\n } else {\n this._writeDepth++;\n try {\n this._onDataFn(buf);\n } finally {\n this._writeDepth--;\n }\n }\n }\n}\n"]}
|
package/dist/testing/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { T as Transport } from '../index-
|
|
1
|
+
import { T as Transport } from '../index-CD8V2Mtk.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* InMemoryTransport
|
|
4
|
+
* InMemoryTransport, deterministic, socket-free Transport for tests.
|
|
5
5
|
*
|
|
6
6
|
* Two connected ends are created via `InMemoryTransport.pair()`.
|
|
7
7
|
* Writes to end A fire end B's registered `onData` handler **synchronously**
|
|
8
|
-
* before `write()` returns
|
|
8
|
+
* before `write()` returns, no async ceremony, no `await` in tests.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```typescript
|
|
@@ -13,7 +13,7 @@ import { T as Transport } from '../index-DjGQJtYE.cjs';
|
|
|
13
13
|
* const [a, b] = InMemoryTransport.pair();
|
|
14
14
|
* b.onData((chunk) => console.log('received:', chunk));
|
|
15
15
|
* a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));
|
|
16
|
-
* // b's onData fired synchronously
|
|
16
|
+
* // b's onData fired synchronously, log printed before this line
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
19
|
* @packageDocumentation
|
|
@@ -23,7 +23,7 @@ import { T as Transport } from '../index-DjGQJtYE.cjs';
|
|
|
23
23
|
* Deterministic in-memory transport for socket-free tests.
|
|
24
24
|
*
|
|
25
25
|
* Create connected pairs with `InMemoryTransport.pair()`. Writes to one end
|
|
26
|
-
* deliver synchronously to the other end's `onData` handler
|
|
26
|
+
* deliver synchronously to the other end's `onData` handler, no async ceremony,
|
|
27
27
|
* no `await` needed in tests.
|
|
28
28
|
*
|
|
29
29
|
* Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for
|
|
@@ -69,7 +69,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
69
69
|
/**
|
|
70
70
|
* Write `buf` to the peer transport.
|
|
71
71
|
*
|
|
72
|
-
* If the peer is paused, the buffer is copied and queued
|
|
72
|
+
* If the peer is paused, the buffer is copied and queued, returns `false`
|
|
73
73
|
* (backpressure signal). Otherwise, the peer's `onData` handler fires
|
|
74
74
|
* synchronously before this method returns.
|
|
75
75
|
*
|
|
@@ -77,7 +77,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
77
77
|
*/
|
|
78
78
|
write(buf: Buffer): boolean;
|
|
79
79
|
/**
|
|
80
|
-
* Initiate graceful close
|
|
80
|
+
* Initiate graceful close, fires `onClose` on this end and on the peer,
|
|
81
81
|
* simulating a TCP FIN exchange.
|
|
82
82
|
*/
|
|
83
83
|
close(): void;
|
|
@@ -150,7 +150,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
150
150
|
*/
|
|
151
151
|
pause(): void;
|
|
152
152
|
/**
|
|
153
|
-
* Resume delivery
|
|
153
|
+
* Resume delivery, flushes all queued chunks to the `onData` handler,
|
|
154
154
|
* synchronously, in the order they were written.
|
|
155
155
|
*/
|
|
156
156
|
resume(): void;
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { T as Transport } from '../index-
|
|
1
|
+
import { T as Transport } from '../index-CD8V2Mtk.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* InMemoryTransport
|
|
4
|
+
* InMemoryTransport, deterministic, socket-free Transport for tests.
|
|
5
5
|
*
|
|
6
6
|
* Two connected ends are created via `InMemoryTransport.pair()`.
|
|
7
7
|
* Writes to end A fire end B's registered `onData` handler **synchronously**
|
|
8
|
-
* before `write()` returns
|
|
8
|
+
* before `write()` returns, no async ceremony, no `await` in tests.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```typescript
|
|
@@ -13,7 +13,7 @@ import { T as Transport } from '../index-DjGQJtYE.js';
|
|
|
13
13
|
* const [a, b] = InMemoryTransport.pair();
|
|
14
14
|
* b.onData((chunk) => console.log('received:', chunk));
|
|
15
15
|
* a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));
|
|
16
|
-
* // b's onData fired synchronously
|
|
16
|
+
* // b's onData fired synchronously, log printed before this line
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
19
|
* @packageDocumentation
|
|
@@ -23,7 +23,7 @@ import { T as Transport } from '../index-DjGQJtYE.js';
|
|
|
23
23
|
* Deterministic in-memory transport for socket-free tests.
|
|
24
24
|
*
|
|
25
25
|
* Create connected pairs with `InMemoryTransport.pair()`. Writes to one end
|
|
26
|
-
* deliver synchronously to the other end's `onData` handler
|
|
26
|
+
* deliver synchronously to the other end's `onData` handler, no async ceremony,
|
|
27
27
|
* no `await` needed in tests.
|
|
28
28
|
*
|
|
29
29
|
* Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for
|
|
@@ -69,7 +69,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
69
69
|
/**
|
|
70
70
|
* Write `buf` to the peer transport.
|
|
71
71
|
*
|
|
72
|
-
* If the peer is paused, the buffer is copied and queued
|
|
72
|
+
* If the peer is paused, the buffer is copied and queued, returns `false`
|
|
73
73
|
* (backpressure signal). Otherwise, the peer's `onData` handler fires
|
|
74
74
|
* synchronously before this method returns.
|
|
75
75
|
*
|
|
@@ -77,7 +77,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
77
77
|
*/
|
|
78
78
|
write(buf: Buffer): boolean;
|
|
79
79
|
/**
|
|
80
|
-
* Initiate graceful close
|
|
80
|
+
* Initiate graceful close, fires `onClose` on this end and on the peer,
|
|
81
81
|
* simulating a TCP FIN exchange.
|
|
82
82
|
*/
|
|
83
83
|
close(): void;
|
|
@@ -150,7 +150,7 @@ declare class InMemoryTransport implements Transport {
|
|
|
150
150
|
*/
|
|
151
151
|
pause(): void;
|
|
152
152
|
/**
|
|
153
|
-
* Resume delivery
|
|
153
|
+
* Resume delivery, flushes all queued chunks to the `onData` handler,
|
|
154
154
|
* synchronously, in the order they were written.
|
|
155
155
|
*/
|
|
156
156
|
resume(): void;
|
package/dist/testing/index.mjs
CHANGED
|
@@ -38,7 +38,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
38
38
|
/**
|
|
39
39
|
* Write `buf` to the peer transport.
|
|
40
40
|
*
|
|
41
|
-
* If the peer is paused, the buffer is copied and queued
|
|
41
|
+
* If the peer is paused, the buffer is copied and queued, returns `false`
|
|
42
42
|
* (backpressure signal). Otherwise, the peer's `onData` handler fires
|
|
43
43
|
* synchronously before this method returns.
|
|
44
44
|
*
|
|
@@ -56,7 +56,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
56
56
|
return true;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Initiate graceful close
|
|
59
|
+
* Initiate graceful close, fires `onClose` on this end and on the peer,
|
|
60
60
|
* simulating a TCP FIN exchange.
|
|
61
61
|
*/
|
|
62
62
|
close() {
|
|
@@ -176,7 +176,7 @@ var InMemoryTransport = class _InMemoryTransport {
|
|
|
176
176
|
this._paused = true;
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
|
-
* Resume delivery
|
|
179
|
+
* Resume delivery, flushes all queued chunks to the `onData` handler,
|
|
180
180
|
* synchronously, in the order they were written.
|
|
181
181
|
*/
|
|
182
182
|
resume() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/in-memory-transport.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * InMemoryTransport — deterministic, socket-free Transport for tests.\n *\n * Two connected ends are created via `InMemoryTransport.pair()`.\n * Writes to end A fire end B's registered `onData` handler **synchronously**\n * before `write()` returns — no async ceremony, no `await` in tests.\n *\n * @example\n * ```typescript\n * import { InMemoryTransport } from '@cosyte/mllp/testing';\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => console.log('received:', chunk));\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * // b's onData fired synchronously — log printed before this line\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Transport } from \"../transport/index.js\";\n\n/**\n * Deterministic in-memory transport for socket-free tests.\n *\n * Create connected pairs with `InMemoryTransport.pair()`. Writes to one end\n * deliver synchronously to the other end's `onData` handler — no async ceremony,\n * no `await` needed in tests.\n *\n * Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for\n * backpressure simulation, and `destroy(reason)` for abrupt-disconnect simulation.\n *\n * @example\n * ```typescript\n * const [client, server] = InMemoryTransport.pair();\n * server.onData((chunk) => server.write(chunk)); // echo\n * client.onData((echo) => console.log('echo:', echo));\n * client.write(Buffer.from('hello'));\n * ```\n */\nexport class InMemoryTransport implements Transport {\n private _peer: InMemoryTransport | null = null;\n\n private _onDataFn: ((chunk: Buffer) => void) | null = null;\n private _onConnectFn: (() => void) | null = null;\n private _onCloseFn: (() => void) | null = null;\n private _onErrorFn: ((err: Error) => void) | null = null;\n\n private _destroyed = false;\n private _paused = false;\n private _splitBytes = 0;\n private _pendingChunks: Buffer[] = [];\n private _writeDepth = 0;\n\n private constructor() {}\n\n /**\n * Create two connected InMemoryTransport ends.\n *\n * Writes to `a` deliver to `b`'s `onData` handler synchronously (and vice versa).\n * Call `a.simulateConnect()` after `pair()` to fire the `onConnect` handler,\n * simulating TCP connect completion.\n *\n * @returns Tuple `[a, b]` of two connected transports.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => b.write(chunk)); // echo server\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * ```\n */\n static pair(): [InMemoryTransport, InMemoryTransport] {\n const a = new InMemoryTransport();\n const b = new InMemoryTransport();\n a._peer = b;\n b._peer = a;\n return [a, b];\n }\n\n /**\n * Write `buf` to the peer transport.\n *\n * If the peer is paused, the buffer is copied and queued — returns `false`\n * (backpressure signal). Otherwise, the peer's `onData` handler fires\n * synchronously before this method returns.\n *\n * @returns `true` if delivered immediately; `false` if queued or transport is destroyed.\n */\n write(buf: Buffer): boolean {\n if (this._destroyed) return false;\n const peer = this._peer;\n if (peer === null || peer._destroyed) return false;\n\n if (peer._paused) {\n // Queue a copy — caller may reuse buf after write() returns (T-03-02-02)\n peer._pendingChunks.push(Buffer.from(buf));\n return false;\n }\n\n peer._deliverChunk(buf);\n return true;\n }\n\n /**\n * Initiate graceful close — fires `onClose` on this end and on the peer,\n * simulating a TCP FIN exchange.\n */\n close(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n // Fire onClose on this end\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow — test handlers must not break transport logic */\n }\n }\n // Simulate peer receiving FIN — fire peer's onClose\n const peer = this._peer;\n if (peer !== null && !peer._destroyed) {\n peer._destroyed = true;\n if (peer._onCloseFn !== null) {\n try {\n peer._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n }\n\n /**\n * Abruptly destroy this transport end.\n *\n * Fires `onError(reason)` then `onClose` on this end only. Does NOT affect\n * the peer (simulates one-sided abrupt disconnect, e.g., ECONNRESET on\n * just one socket).\n *\n * @param reason - Optional error; defaults to `new Error('InMemoryTransport destroyed')`.\n */\n destroy(reason?: Error): void {\n if (this._destroyed) return;\n this._destroyed = true;\n const err = reason ?? new Error(\"InMemoryTransport destroyed\");\n if (this._onErrorFn !== null) {\n try {\n this._onErrorFn(err);\n } catch {\n /* swallow */\n }\n }\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n /**\n * Register the data handler. Replaces any previously registered handler.\n *\n * @param fn - Called with each chunk delivered by the peer.\n */\n onData(fn: (chunk: Buffer) => void): void {\n this._onDataFn = fn;\n }\n\n /**\n * Register the connect handler. Replaces any previously registered handler.\n * Use `simulateConnect()` to trigger this in tests.\n *\n * @param fn - Called when `simulateConnect()` is invoked on this end.\n */\n onConnect(fn: () => void): void {\n this._onConnectFn = fn;\n }\n\n /**\n * Register the close handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `close()` or `destroy()` is invoked on this end,\n * or when the peer calls `close()`.\n */\n onClose(fn: () => void): void {\n this._onCloseFn = fn;\n }\n\n /**\n * Register the error handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `destroy(reason)` is invoked on this end.\n */\n onError(fn: (err: Error) => void): void {\n this._onErrorFn = fn;\n }\n\n /**\n * Simulate chunked TCP reads.\n *\n * After calling `split(n)`, any `write()` directed at this transport will be\n * delivered to the `onData` handler in chunks of `n` bytes rather than\n * all at once. Useful for verifying that a `FrameReader` reassembles frames\n * correctly across chunk boundaries.\n *\n * @param bytesPerChunk - Chunk size in bytes. Pass `0` to disable chunking.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.split(1); // b receives one byte at a time\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d])); // fires b.onData 4 times\n * ```\n */\n split(bytesPerChunk: number): void {\n this._splitBytes = bytesPerChunk;\n }\n\n /**\n * Pause delivery.\n *\n * While paused, writes directed at this transport are queued internally and\n * the `onData` handler is NOT called. The queued chunks are flushed on `resume()`.\n *\n * @example\n * ```typescript\n * b.pause();\n * a.write(buf); // queued, b.onData not called\n * b.resume(); // flushes queue synchronously\n * ```\n */\n pause(): void {\n this._paused = true;\n }\n\n /**\n * Resume delivery — flushes all queued chunks to the `onData` handler,\n * synchronously, in the order they were written.\n */\n resume(): void {\n this._paused = false;\n const pending = this._pendingChunks.splice(0);\n for (const chunk of pending) {\n this._deliverChunk(chunk);\n }\n }\n\n /**\n * Fire the `onConnect` handler on this end.\n *\n * Call after `pair()` to simulate TCP connect completion. In production code,\n * `NetTransport` fires the handler automatically when the socket emits `'connect'`.\n *\n * @example\n * ```typescript\n * const [a] = InMemoryTransport.pair();\n * a.onConnect(() => console.log('connected'));\n * a.simulateConnect(); // fires immediately\n * ```\n */\n simulateConnect(): void {\n if (this._onConnectFn !== null) {\n try {\n this._onConnectFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n private _deliverChunk(buf: Buffer): void {\n if (this._onDataFn === null) return;\n\n // Re-entrancy guard (D-03): writing from inside an onData handler would cause\n // infinite recursion or corrupt frame ordering. Detect and throw.\n if (this._writeDepth > 0) {\n throw new Error(\"InMemoryTransport: re-entrant write detected\");\n }\n\n if (this._splitBytes > 0) {\n let offset = 0;\n while (offset < buf.length) {\n const end = Math.min(offset + this._splitBytes, buf.length);\n const chunk = buf.subarray(offset, end);\n this._writeDepth++;\n try {\n this._onDataFn(chunk);\n } finally {\n this._writeDepth--;\n }\n offset = end;\n }\n } else {\n this._writeDepth++;\n try {\n this._onDataFn(buf);\n } finally {\n this._writeDepth--;\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/in-memory-transport.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * InMemoryTransport, deterministic, socket-free Transport for tests.\n *\n * Two connected ends are created via `InMemoryTransport.pair()`.\n * Writes to end A fire end B's registered `onData` handler **synchronously**\n * before `write()` returns, no async ceremony, no `await` in tests.\n *\n * @example\n * ```typescript\n * import { InMemoryTransport } from '@cosyte/mllp/testing';\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => console.log('received:', chunk));\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * // b's onData fired synchronously, log printed before this line\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Transport } from \"../transport/index.js\";\n\n/**\n * Deterministic in-memory transport for socket-free tests.\n *\n * Create connected pairs with `InMemoryTransport.pair()`. Writes to one end\n * deliver synchronously to the other end's `onData` handler, no async ceremony,\n * no `await` needed in tests.\n *\n * Supports `split(n)` for chunked-read simulation, `pause()`/`resume()` for\n * backpressure simulation, and `destroy(reason)` for abrupt-disconnect simulation.\n *\n * @example\n * ```typescript\n * const [client, server] = InMemoryTransport.pair();\n * server.onData((chunk) => server.write(chunk)); // echo\n * client.onData((echo) => console.log('echo:', echo));\n * client.write(Buffer.from('hello'));\n * ```\n */\nexport class InMemoryTransport implements Transport {\n private _peer: InMemoryTransport | null = null;\n\n private _onDataFn: ((chunk: Buffer) => void) | null = null;\n private _onConnectFn: (() => void) | null = null;\n private _onCloseFn: (() => void) | null = null;\n private _onErrorFn: ((err: Error) => void) | null = null;\n\n private _destroyed = false;\n private _paused = false;\n private _splitBytes = 0;\n private _pendingChunks: Buffer[] = [];\n private _writeDepth = 0;\n\n private constructor() {}\n\n /**\n * Create two connected InMemoryTransport ends.\n *\n * Writes to `a` deliver to `b`'s `onData` handler synchronously (and vice versa).\n * Call `a.simulateConnect()` after `pair()` to fire the `onConnect` handler,\n * simulating TCP connect completion.\n *\n * @returns Tuple `[a, b]` of two connected transports.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.onData((chunk) => b.write(chunk)); // echo server\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d]));\n * ```\n */\n static pair(): [InMemoryTransport, InMemoryTransport] {\n const a = new InMemoryTransport();\n const b = new InMemoryTransport();\n a._peer = b;\n b._peer = a;\n return [a, b];\n }\n\n /**\n * Write `buf` to the peer transport.\n *\n * If the peer is paused, the buffer is copied and queued, returns `false`\n * (backpressure signal). Otherwise, the peer's `onData` handler fires\n * synchronously before this method returns.\n *\n * @returns `true` if delivered immediately; `false` if queued or transport is destroyed.\n */\n write(buf: Buffer): boolean {\n if (this._destroyed) return false;\n const peer = this._peer;\n if (peer === null || peer._destroyed) return false;\n\n if (peer._paused) {\n // Queue a copy, caller may reuse buf after write() returns (T-03-02-02)\n peer._pendingChunks.push(Buffer.from(buf));\n return false;\n }\n\n peer._deliverChunk(buf);\n return true;\n }\n\n /**\n * Initiate graceful close, fires `onClose` on this end and on the peer,\n * simulating a TCP FIN exchange.\n */\n close(): void {\n if (this._destroyed) return;\n this._destroyed = true;\n // Fire onClose on this end\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow, test handlers must not break transport logic */\n }\n }\n // Simulate peer receiving FIN, fire peer's onClose\n const peer = this._peer;\n if (peer !== null && !peer._destroyed) {\n peer._destroyed = true;\n if (peer._onCloseFn !== null) {\n try {\n peer._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n }\n\n /**\n * Abruptly destroy this transport end.\n *\n * Fires `onError(reason)` then `onClose` on this end only. Does NOT affect\n * the peer (simulates one-sided abrupt disconnect, e.g., ECONNRESET on\n * just one socket).\n *\n * @param reason - Optional error; defaults to `new Error('InMemoryTransport destroyed')`.\n */\n destroy(reason?: Error): void {\n if (this._destroyed) return;\n this._destroyed = true;\n const err = reason ?? new Error(\"InMemoryTransport destroyed\");\n if (this._onErrorFn !== null) {\n try {\n this._onErrorFn(err);\n } catch {\n /* swallow */\n }\n }\n if (this._onCloseFn !== null) {\n try {\n this._onCloseFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n /**\n * Register the data handler. Replaces any previously registered handler.\n *\n * @param fn - Called with each chunk delivered by the peer.\n */\n onData(fn: (chunk: Buffer) => void): void {\n this._onDataFn = fn;\n }\n\n /**\n * Register the connect handler. Replaces any previously registered handler.\n * Use `simulateConnect()` to trigger this in tests.\n *\n * @param fn - Called when `simulateConnect()` is invoked on this end.\n */\n onConnect(fn: () => void): void {\n this._onConnectFn = fn;\n }\n\n /**\n * Register the close handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `close()` or `destroy()` is invoked on this end,\n * or when the peer calls `close()`.\n */\n onClose(fn: () => void): void {\n this._onCloseFn = fn;\n }\n\n /**\n * Register the error handler. Replaces any previously registered handler.\n *\n * @param fn - Called when `destroy(reason)` is invoked on this end.\n */\n onError(fn: (err: Error) => void): void {\n this._onErrorFn = fn;\n }\n\n /**\n * Simulate chunked TCP reads.\n *\n * After calling `split(n)`, any `write()` directed at this transport will be\n * delivered to the `onData` handler in chunks of `n` bytes rather than\n * all at once. Useful for verifying that a `FrameReader` reassembles frames\n * correctly across chunk boundaries.\n *\n * @param bytesPerChunk - Chunk size in bytes. Pass `0` to disable chunking.\n *\n * @example\n * ```typescript\n * const [a, b] = InMemoryTransport.pair();\n * b.split(1); // b receives one byte at a time\n * a.write(Buffer.from([0x0b, 0x41, 0x1c, 0x0d])); // fires b.onData 4 times\n * ```\n */\n split(bytesPerChunk: number): void {\n this._splitBytes = bytesPerChunk;\n }\n\n /**\n * Pause delivery.\n *\n * While paused, writes directed at this transport are queued internally and\n * the `onData` handler is NOT called. The queued chunks are flushed on `resume()`.\n *\n * @example\n * ```typescript\n * b.pause();\n * a.write(buf); // queued, b.onData not called\n * b.resume(); // flushes queue synchronously\n * ```\n */\n pause(): void {\n this._paused = true;\n }\n\n /**\n * Resume delivery, flushes all queued chunks to the `onData` handler,\n * synchronously, in the order they were written.\n */\n resume(): void {\n this._paused = false;\n const pending = this._pendingChunks.splice(0);\n for (const chunk of pending) {\n this._deliverChunk(chunk);\n }\n }\n\n /**\n * Fire the `onConnect` handler on this end.\n *\n * Call after `pair()` to simulate TCP connect completion. In production code,\n * `NetTransport` fires the handler automatically when the socket emits `'connect'`.\n *\n * @example\n * ```typescript\n * const [a] = InMemoryTransport.pair();\n * a.onConnect(() => console.log('connected'));\n * a.simulateConnect(); // fires immediately\n * ```\n */\n simulateConnect(): void {\n if (this._onConnectFn !== null) {\n try {\n this._onConnectFn();\n } catch {\n /* swallow */\n }\n }\n }\n\n private _deliverChunk(buf: Buffer): void {\n if (this._onDataFn === null) return;\n\n // Re-entrancy guard (D-03): writing from inside an onData handler would cause\n // infinite recursion or corrupt frame ordering. Detect and throw.\n if (this._writeDepth > 0) {\n throw new Error(\"InMemoryTransport: re-entrant write detected\");\n }\n\n if (this._splitBytes > 0) {\n let offset = 0;\n while (offset < buf.length) {\n const end = Math.min(offset + this._splitBytes, buf.length);\n const chunk = buf.subarray(offset, end);\n this._writeDepth++;\n try {\n this._onDataFn(chunk);\n } finally {\n this._writeDepth--;\n }\n offset = end;\n }\n } else {\n this._writeDepth++;\n try {\n this._onDataFn(buf);\n } finally {\n this._writeDepth--;\n }\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosyte/mllp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Production-grade MLLP client and server for Node.js
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Production-grade MLLP client and server for Node.js, transport-only sibling to @cosyte/hl7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.0.0"
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"dist",
|
|
28
28
|
"README.md",
|
|
29
29
|
"LICENSE",
|
|
30
|
+
"TRADEMARKS.md",
|
|
30
31
|
"CHANGELOG.md"
|
|
31
32
|
],
|
|
32
33
|
"sideEffects": false,
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
"@cosyte/test-utils": "^0.0.1",
|
|
89
90
|
"@cosyte/tsconfig": "^0.0.2",
|
|
90
91
|
"@cosyte/tsup-config": "^0.0.1",
|
|
91
|
-
"@cosyte/vitest-config": "^0.0.
|
|
92
|
+
"@cosyte/vitest-config": "^0.0.2",
|
|
92
93
|
"@types/node": "^22.0.0",
|
|
93
94
|
"@types/node-forge": "1.3.14",
|
|
94
95
|
"@vitest/coverage-v8": "4.1.4",
|
|
@@ -97,19 +98,27 @@
|
|
|
97
98
|
"node-forge": "1.4.0",
|
|
98
99
|
"prettier": "3.8.3",
|
|
99
100
|
"selfsigned": "2.4.1",
|
|
101
|
+
"simple-git-hooks": "2.13.0",
|
|
100
102
|
"tsup": "8.5.1",
|
|
103
|
+
"tsx": "4.21.0",
|
|
101
104
|
"typescript": "5.9.3",
|
|
102
105
|
"vite": "7.3.6",
|
|
103
106
|
"vitest": "4.1.4"
|
|
104
107
|
},
|
|
108
|
+
"simple-git-hooks": {
|
|
109
|
+
"pre-commit": "pnpm phi-scan --staged"
|
|
110
|
+
},
|
|
105
111
|
"scripts": {
|
|
106
112
|
"build": "tsup",
|
|
107
113
|
"changeset": "changeset",
|
|
108
114
|
"typecheck": "tsc --noEmit",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
115
|
+
"phi-scan": "tsx scripts/phi-scan.ts",
|
|
116
|
+
"check:no-emdash": "bash scripts/check-no-emdash.sh",
|
|
117
|
+
"check:no-internal-refs": "bash scripts/check-no-internal-refs.sh",
|
|
118
|
+
"lint": "eslint \"src/**/*.ts\" \"scripts/**/*.ts\" \"test/**/*.ts\" --max-warnings=0 --no-error-on-unmatched-pattern",
|
|
119
|
+
"lint:fix": "eslint \"src/**/*.ts\" \"scripts/**/*.ts\" \"test/**/*.ts\" --fix --no-error-on-unmatched-pattern",
|
|
120
|
+
"format": "prettier --write \"src/**/*.{ts,md}\" \"test/**/*.ts\" \"scripts/**/*.{ts,mjs}\" \"*.{json,md,yml}\"",
|
|
121
|
+
"format:check": "prettier --check \"src/**/*.{ts,md}\" \"test/**/*.ts\" \"scripts/**/*.{ts,mjs}\" \"*.{json,md,yml}\"",
|
|
113
122
|
"test": "vitest run",
|
|
114
123
|
"test:watch": "vitest",
|
|
115
124
|
"test:coverage": "vitest run --coverage",
|