@glion/mllp-client 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,14 @@
1
1
  # @glion/mllp-client
2
2
 
3
- A simple HL7v2 MLLP client for Node.js and Cloudflare Workers.
3
+ An HL7v2 MLLP client for Node.js, Bun, Deno, and Cloudflare Workers.
4
4
 
5
5
  - 📦 **MLLP built in.** Framing, message boundaries and acknowledgment matching are handled. You send a parsed message and get one back.
6
6
  - 🔄 **Predictable connection lifecycle.** Timeouts on connecting and on waiting for a reply, connection attempts retried with backoff, TCP keepalive by default, and explicit states you can read.
7
- - ⚡ **Thin over TCP.** One socket, one message at a time. No queue, no worker threads, no polling.
7
+ - ⚡ **Thin over TCP.** One socket, one message on the wire at a time. Further sends queue in call order. No worker threads, no polling.
8
8
  - 🧯 **Errors you can act on.** Every failure carries a stable code and says whether the connection is still usable.
9
- - 🧩 **Any transport.** TCP included. TLS, Cloudflare Workers or an in-memory socket plug in behind it.
9
+ - 🧩 **Any transport.** TCP and TLS on Node.js, Bun, Deno, and Cloudflare Workers included. An in-memory or custom socket plugs in behind the same two-method interface.
10
10
  - 🔤 **Typed end to end.** TypeScript throughout, with parsed HL7v2 going in and coming out.
11
11
 
12
- > **Coming soon** — TLS and Cloudflare Workers adapters. Today the only bundled adapter is Node.js over TCP.
13
-
14
12
  ## Install
15
13
 
16
14
  ```bash
@@ -171,22 +169,23 @@ Sends one message and resolves with the acknowledgment that answers it. Connects
171
169
  - `AckException` — the receiver refused the message. The connection stays open.
172
170
  - `MllpInvalidMessageError` — no MSH-10, or the message could not be serialized. Nothing was sent.
173
171
  - `MllpInvalidOptionError` — `timeoutMs` is out of range. Nothing was sent.
174
- - `MllpAlreadySendingError` — another send is in flight.
175
- - `MllpClientClosedError` — the client is closed.
172
+ - `MllpClientClosedError` — the client is closed, or closed while this send was waiting its turn.
176
173
  - `MllpConnectionFailedError`, `MllpConnectionTimeoutError` — the connection could not be opened.
177
174
  - `MllpSendTimeoutError`, `MllpConnectionLostError`, `MllpInvalidResponseError` — the exchange failed. These close the connection; see [Errors](#errors).
175
+ - `MllpSendAbortedError` — `destroy()` cut the send off. Delivery is unknown.
178
176
 
179
177
  Every error carries `delivery`, `not-sent` or `unknown`, the one fact a retry needs; see [Errors](#errors).
180
178
 
181
- One message at a time. To send several, await each in turn:
179
+ One message is on the wire at a time. A `send()` arriving while another is in flight waits its turn, and sends go out in the order they were called, so a batch may be fired at once:
182
180
 
183
181
  ```ts
184
- for (const message of batch) {
185
- const ack = await client.send(message);
186
- record(ack.controlId, ack.code);
187
- }
182
+ const outcomes = await Promise.allSettled(
183
+ batch.map((message) => client.send(message))
184
+ );
188
185
  ```
189
186
 
187
+ The queue is in memory and has no bound. `timeoutMs` runs from the moment the write starts, not from the call. A send still waiting when `close()` or `destroy()` is called, or when a failure closes the client, rejects at once with `delivery: "not-sent"`: with `MllpClientClosedError`, or with the failure itself when the failure is one of not being sent, such as the dial's `MllpConnectionFailedError` or `MllpConnectionTimeoutError`, as `connect()` gets. Nothing behind a failed message goes out. See [Does `send()` queue?](#does-send-queue).
188
+
190
189
  ### `client.connect()`
191
190
 
192
191
  ```ts
@@ -197,7 +196,7 @@ Opens the connection without sending anything, dialing again under the [reconnec
197
196
 
198
197
  Idempotent. On a connected client it resolves at once; while an attempt is in flight it waits for the outcome and shares it.
199
198
 
200
- **Throws** the last attempt's `MllpConnectionFailedError` or `MllpConnectionTimeoutError`, or `MllpClientClosedError` when the client is already closed or `close()` cancelled the attempt.
199
+ **Throws** the last attempt's `MllpConnectionFailedError` or `MllpConnectionTimeoutError`, or `MllpClientClosedError` when the client is already closed or `close()` or `destroy()` cancelled the attempt.
201
200
 
202
201
  ### `client.close()`
203
202
 
@@ -205,9 +204,9 @@ Idempotent. On a connected client it resolves at once; while an attempt is in fl
205
204
  close(): Promise<void>
206
205
  ```
207
206
 
208
- Closes the connection once the message in flight has been acknowledged. New sends are refused from the moment it is called, so an in-flight message is never cut off. An attempt to connect stops at once.
207
+ Closes the connection once the message in flight has been acknowledged. New sends, and sends still waiting their turn, are refused from the moment it is called with `MllpClientClosedError`; the message in flight is never cut off. Await the sends you want delivered before calling it. An attempt to connect stops at once.
209
208
 
210
- Resolves when the connection is down, from any phase. Never throws. Idempotent. The wait is bounded by the in-flight send's own deadline.
209
+ Resolves when the connection is down, from any phase. Never throws. Idempotent. The wait is bounded by the in-flight send's own deadline. If the message it waits for fails, the client closes with that failure; otherwise with `null`, the owner's decision.
211
210
 
212
211
  ```ts
213
212
  process.on("SIGTERM", async () => {
@@ -216,13 +215,13 @@ process.on("SIGTERM", async () => {
216
215
  });
217
216
  ```
218
217
 
219
- ### `client.destroy()`
218
+ ### `client.destroy(reason?)`
220
219
 
221
220
  ```ts
222
- destroy(): Promise<void>
221
+ destroy(reason?: MllpClientError | null): Promise<void>
223
222
  ```
224
223
 
225
- Closes the connection now, without waiting for anything in flight. A message in flight rejects with `MllpSendAbortedError`. An attempt to connect stops at once.
224
+ Closes the connection now, without waiting for anything in flight. A message in flight rejects with `MllpSendAbortedError`. An attempt to connect stops at once. The client closes with `reason`: the `close` event carries it, and so does `cause` on every later call's `MllpClientClosedError`. Omitted or `null`, the client closed by its owner's decision, as `net.Socket.destroy(error)` does. Sends waiting their turn are rejected with `reason` itself when its delivery is `not-sent`, and with `MllpClientClosedError` carrying `reason` otherwise. Arriving while the client is already closing, it cuts the message in flight and joins the ending under way; `reason` is recorded when no failure is known yet.
226
225
 
227
226
  Resolves when the connection is down, from any phase. Never throws. Idempotent.
228
227
 
@@ -243,17 +242,25 @@ Calls `close()`. Lets a client be scoped with `await using`:
243
242
  readonly state: MllpClientState
244
243
  ```
245
244
 
246
- | Value | Meaning |
247
- | ------------ | --------------------------------------------------------------------------- |
248
- | `idle` | Nothing opened yet. The first `send()` or `connect()` opens the connection. |
249
- | `connecting` | The connection is being opened, further attempts included. |
250
- | `connected` | Open, with no message in flight. |
251
- | `sending` | A message is on the wire, waiting for its acknowledgment. |
252
- | `closing` | `close()` is waiting out the message in flight. |
253
- | `closed` | Done. |
245
+ | Value | Meaning |
246
+ | ------------ | -------------------------------------------------------------------------------- |
247
+ | `idle` | Nothing opened yet. The first `send()` or `connect()` opens the connection. |
248
+ | `connecting` | The connection is being opened, further attempts included. |
249
+ | `connected` | Open, with no message in flight. |
250
+ | `sending` | A message is on the wire, waiting for its acknowledgment. Further sends wait. |
251
+ | `closing` | The client is ending: a message being waited out, or the connection coming down. |
252
+ | `closed` | The connection is down. |
254
253
 
255
254
  A client closes once. After `close()`, `destroy()`, a connection the reconnect policy could not open, or a connection that was lost, every call throws `MllpClientClosedError`; construct a new client to send again.
256
255
 
256
+ ### `client.pending`
257
+
258
+ ```ts
259
+ readonly pending: number
260
+ ```
261
+
262
+ How many sends wait their turn, the one in flight excluded. The queue's backlog, for a metric or a backpressure decision; `0` whenever nothing waits.
263
+
257
264
  ### `client.connected`
258
265
 
259
266
  ```ts
@@ -271,10 +278,10 @@ off<E>(event: E, listener: MllpClientListener<E>): this
271
278
 
272
279
  Adds or removes a listener. Both return the client, so calls chain.
273
280
 
274
- | Event | Listener | Fires when |
275
- | --------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
276
- | `connect` | `() => void` | The connection opened. |
277
- | `close` | `(error: MllpClientError \| null) => void` | The client is done. Fires once, from any phase, even if it never connected. `error` is the failure it could not recover from, or `null` when you closed it. |
281
+ | Event | Listener | Fires when |
282
+ | --------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
283
+ | `connect` | `() => void` | The connection opened. |
284
+ | `close` | `(error: MllpClientError \| null) => void` | The connection is down. Fires once, from any phase, even if it never connected. `error` is the failure that closed the client, or `null` when you closed it. |
278
285
 
279
286
  A lost connection is not an event of its own: it closes the client, so `close` fires with the failure. Listeners are synchronous, and one that throws propagates to whatever triggered the event.
280
287
 
@@ -288,9 +295,16 @@ client
288
295
 
289
296
  ## Runtimes
290
297
 
291
- The client speaks MLLP over a pair of byte streams and knows nothing else about the transport. That whole dependency is [`MllpSocket`](#custom-socket) — two methods — so supporting a new runtime means writing an adapter, not forking the client.
298
+ The client speaks MLLP over a pair of byte streams and knows nothing else about the transport. That whole dependency is [`MllpSocket`](#custom-socket) — two methods — so supporting a runtime means an adapter, not a fork of the client.
292
299
 
293
- Node.js is the only adapter that ships today.
300
+ | Runtime | Adapter | Import | TLS | Proven by |
301
+ | ------------------ | -------------------------------------- | ---------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
302
+ | Node.js ≥ 22 | [`nodeSocket`](#nodejs) | `@glion/mllp-client/node` | [Trust store, CA, client certificate](#tls) | Conformance suite in CI, Node 22 and 24 |
303
+ | Bun | [`nodeSocket`](#nodejs) | `@glion/mllp-client/node` | [Trust store, CA, client certificate](#tls) | Conformance suite in CI, Bun 1.4 |
304
+ | Deno | [`nodeSocket`](#nodejs) | `@glion/mllp-client/node` | [Trust store, CA, client certificate](#tls) | Conformance suite in CI, Deno 2.9 |
305
+ | Cloudflare Workers | [`workersSocket`](#cloudflare-workers) | `@glion/mllp-client/workers` | Public trust store only | Integration suite in CI, inside `workerd`; TCP only, TLS handshake untested ([#626](https://github.com/rethinkhealth/glion/issues/626)) |
306
+
307
+ Every adapter runs the same conformance suite: the [`MllpSocket` contract](#custom-socket) case by case, and the client's behaviour over a real socket scenario by scenario. On Node.js, Bun, and Deno it runs twice, over TCP and over TLS. A custom socket can run it too; see `tests/integration/conformance/` in the package source.
294
308
 
295
309
  ### Node.js
296
310
 
@@ -300,16 +314,17 @@ import { nodeSocket } from "@glion/mllp-client/node";
300
314
  nodeSocket(options: NodeSocketOptions): MllpSocket
301
315
  ```
302
316
 
303
- Plain TCP over `net.Socket`.
317
+ TCP over `net.Socket`, or TLS over `tls.TLSSocket` when `tls` is set. The same adapter runs on Bun and Deno through their `node:net` and `node:tls` compatibility; no separate import is needed there.
304
318
 
305
- | Option | Type | Default | Description |
306
- | ----------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
307
- | `host` | `string` | required | Host name or address of the receiver. |
308
- | `port` | `number` | required | TCP port of the receiver. |
309
- | `gracefulCloseMs` | `number` | `1000` | How long a socket gets to end cleanly before it is destroyed. |
310
- | `keepAliveIdleMs` | `number` | `30000` | Idle time before the first keepalive probe, so a silent NAT or firewall drop surfaces before the next send. |
319
+ | Option | Type | Default | Description |
320
+ | ----------------- | --------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
321
+ | `host` | `string` | required | Host name or address of the receiver. |
322
+ | `port` | `number` | required | TCP port of the receiver. |
323
+ | `tls` | `boolean \| NodeTlsOptions` | `false` | TLS for the connection. See [TLS](#tls). |
324
+ | `gracefulCloseMs` | `number` | `1000` | How long a socket gets to end cleanly before it is destroyed. |
325
+ | `keepAliveIdleMs` | `number` | `30000` | Idle time before the first keepalive probe. Once the probes fail, the OS ends the socket, and the next send fails at once with `CONNECTION_LOST` instead of waiting out `sendTimeoutMs`. |
311
326
 
312
- `TCP_NODELAY` is set, so a message goes out immediately rather than waiting on Nagle's algorithm.
327
+ `TCP_NODELAY` is set, so a message goes out immediately rather than waiting on Nagle's algorithm. On Bun and Deno the keepalive delay is passed through to the runtime; whether the runtime honours it is not verified.
313
328
 
314
329
  ```ts
315
330
  const client = new MllpClient({
@@ -317,6 +332,91 @@ const client = new MllpClient({
317
332
  });
318
333
  ```
319
334
 
335
+ Deno needs `--allow-net` for the receiver's host and port.
336
+
337
+ #### TLS
338
+
339
+ `tls: true` verifies the receiver's certificate against the runtime's trusted CAs, for `host`, and needs no certificate files. `host` is sent as the server name (SNI) unless it is an IP address.
340
+
341
+ ```ts
342
+ nodeSocket({ host: "hl7.example.org", port: 2575, tls: true });
343
+ ```
344
+
345
+ An object passes Node's `tls.connect()` options:
346
+
347
+ | Option | Type | Default | Description |
348
+ | -------------------- | --------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------ |
349
+ | `ca` | `string \| Buffer \| Array` | Runtime CAs | CA certificates that issued the receiver's certificate, PEM. Replaces the runtime's trusted CAs. |
350
+ | `cert` | `string \| Buffer \| Array` | none | Client certificate chain for mutual TLS, PEM. |
351
+ | `key` | `string \| Buffer \| Array` | none | Private key for `cert`, PEM. |
352
+ | `passphrase` | `string` | none | Passphrase for an encrypted `key` or `pfx`. |
353
+ | `pfx` | `string \| Buffer \| Array` | none | Client certificate and key as PKCS#12. |
354
+ | `servername` | `string` | `host`, unless an IP address | Name the receiver's certificate is verified against, and the SNI name. |
355
+ | `rejectUnauthorized` | `boolean` | `true` | `false` accepts any certificate, including one presented by an attacker. |
356
+
357
+ ```ts
358
+ import { readFileSync } from "node:fs";
359
+
360
+ nodeSocket({
361
+ host: "10.20.0.15",
362
+ port: 2575,
363
+ tls: {
364
+ ca: readFileSync("hospital-ca.pem"),
365
+ cert: readFileSync("client.pem"),
366
+ key: readFileSync("client-key.pem"),
367
+ servername: "hl7.hospital.internal",
368
+ },
369
+ });
370
+ ```
371
+
372
+ A receiver dialed by IP address must present a certificate that lists that address, or `servername` must name one the certificate lists.
373
+
374
+ A handshake that fails rejects with [`CONNECTION_FAILED`](#connection_failed), with Node's TLS error on `cause`: for example `UNABLE_TO_VERIFY_LEAF_SIGNATURE` or `ERR_TLS_CERT_ALTNAME_INVALID`.
375
+
376
+ A receiver that refuses the client certificate over TLS 1.2 fails the handshake: [`CONNECTION_FAILED`](#connection_failed), nothing sent. Over TLS 1.3 it refuses after the handshake, so the connection opens and the first `send()` rejects with `CONNECTION_LOST`, delivery `unknown`.
377
+
378
+ On Deno, a wrong `passphrase` does not fail the handshake; the connection opens without the client certificate and fails as a refused client certificate.
379
+
380
+ ### Cloudflare Workers
381
+
382
+ ```ts
383
+ import { workersSocket } from "@glion/mllp-client/workers";
384
+
385
+ workersSocket(options: WorkersSocketOptions): MllpSocket
386
+ ```
387
+
388
+ TCP or TLS over `cloudflare:sockets`. The module resolves only inside the Workers runtime; import it from the `./workers` subpath, never from `.`.
389
+
390
+ | Option | Type | Default | Description |
391
+ | ------ | --------- | -------- | --------------------------------------------------------------------------------------------- |
392
+ | `host` | `string` | required | Host name or address of the receiver. |
393
+ | `port` | `number` | required | TCP port of the receiver. |
394
+ | `tls` | `boolean` | `false` | `true` verifies the receiver's certificate against the public CAs Workers trusts, for `host`. |
395
+
396
+ ```ts
397
+ import { MllpClient } from "@glion/mllp-client";
398
+ import { workersSocket } from "@glion/mllp-client/workers";
399
+
400
+ export default {
401
+ async fetch(request: Request): Promise<Response> {
402
+ await using client = new MllpClient({
403
+ socket: workersSocket({ host: "hl7.example.org", port: 2575 }),
404
+ });
405
+ const ack = await client.send(await messageFrom(request));
406
+ return Response.json({ code: ack.code, controlId: ack.controlId });
407
+ },
408
+ };
409
+ ```
410
+
411
+ What differs from Node:
412
+
413
+ - A Worker reaches only endpoints routable from Cloudflare's network. A receiver on a private network needs a publicly reachable endpoint in front of it.
414
+ - `close()` resolves as soon as the runtime has ended the socket; there is no grace window.
415
+ - Locally, under Miniflare (`wrangler dev` or the Vitest integration), connections go through a proxy, so `MllpConnectionFailedError.cause` carries the proxy's message rather than a socket error code. `code` is the same in both.
416
+ - Locally, under Miniflare, a `tls: true` connection fails with the proxy's message, whatever certificate the receiver presents. TLS to a receiver works only when the Worker runs on Cloudflare.
417
+ - Cloudflare blocks some destination ports.
418
+ - `tls` takes no CA, client certificate, or server name. The receiver must present a publicly trusted certificate for `host`, so dial a host name rather than an address. Any value other than a boolean throws `MllpInvalidOptionError`.
419
+
320
420
  ### Custom Socket
321
421
 
322
422
  You can expand `MllpSocket` to build a custom socket to one remote system, which the client opens, uses, and ends.
@@ -350,10 +450,10 @@ A rejection from the receiver is **not** an `MllpClientError` — see [`AckExcep
350
450
 
351
451
  `delivery` is the one fact a retry decision needs:
352
452
 
353
- | `delivery` | Meaning | Codes |
354
- | ---------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
355
- | `not-sent` | Nothing reached the wire. The message may be sent again as it is. | `INVALID_OPTION`, `INVALID_MESSAGE`, `ALREADY_SENDING`, `CLOSED`, `CONNECTION_FAILED`, `CONNECTION_TIMEOUT` |
356
- | `unknown` | The message may have reached the receiver. Sending it again may deliver it twice. | `SEND_TIMEOUT`, `CONNECTION_LOST`, `SEND_ABORTED`, `INVALID_RESPONSE` |
453
+ | `delivery` | Meaning | Codes |
454
+ | ---------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
455
+ | `not-sent` | Nothing reached the wire. The message may be sent again as it is. | `INVALID_OPTION`, `INVALID_MESSAGE`, `CLOSED`, `CONNECTION_FAILED`, `CONNECTION_TIMEOUT` |
456
+ | `unknown` | The message may have reached the receiver. Sending it again may deliver it twice. | `SEND_TIMEOUT`, `CONNECTION_LOST`, `SEND_ABORTED`, `INVALID_RESPONSE` |
357
457
 
358
458
  ```ts
359
459
  import { MllpClientError, MllpErrorCode } from "@glion/mllp-client";
@@ -381,7 +481,7 @@ The four failures of the wire — `CONNECTION_FAILED`, `CONNECTION_TIMEOUT`, `SE
381
481
 
382
482
  `MllpInvalidOptionError` · delivery `not-sent`
383
483
 
384
- A constructor option or a per-send `timeoutMs` is out of range — a timeout that is not a positive number of milliseconds, a `maxBufferedBytes` that is not a positive integer, or a `reconnect.attempts` that is not a non-negative integer or `Infinity`. The message names which one.
484
+ A constructor option or a per-send `timeoutMs` is out of range — a timeout that is not a positive number of milliseconds, a `maxBufferedBytes` that is not a positive integer, or a `reconnect.attempts` that is not a non-negative integer or `Infinity`. `workersSocket()` also throws it for a `tls` that is not a boolean. The message names which one.
385
485
 
386
486
  Thrown before anything is opened or sent. A configuration bug, not a runtime condition.
387
487
 
@@ -393,29 +493,21 @@ The message cannot be sent as it stands: no MSH-10 control ID, or it could not b
393
493
 
394
494
  **Nothing reached the wire and the connection is still in step**, so the next message can go out on it. Fix or quarantine the message; do not reconnect.
395
495
 
396
- ### `ALREADY_SENDING`
397
-
398
- `MllpAlreadySendingError` · delivery `not-sent` · field: `controlId`
399
-
400
- `send()` was called while another send was in flight. `controlId` identifies the message already on the wire.
401
-
402
- The client is lockstep by design. Await each send before starting the next, or give each concurrent stream its own client.
403
-
404
496
  ### `CLOSED`
405
497
 
406
498
  `MllpClientClosedError` · delivery `not-sent` · field: `cause`
407
499
 
408
- The client is closed, so the call cannot be served. Also the error a `connect()` gets when `close()` cancelled the attempt it was waiting for.
500
+ The client is closed or closing, so the call cannot be served. Also the error a `connect()` gets when `close()` or `destroy()` cancelled the attempt it was waiting for, and the error a `send()` gets when the client closed while it was waiting its turn; a send waiting behind a dial that failed gets the dial's error instead.
409
501
 
410
- A client closes once. Construct a new one to send again. When the client closed because the reconnect policy gave up, `cause` is the last attempt's failure.
502
+ A client closes once. Construct a new one to send again. When the client closed on a failure, `cause` is that failure: the last attempt's `CONNECTION_FAILED` or `CONNECTION_TIMEOUT` when the reconnect policy gave up, the `CONNECTION_LOST`, `SEND_TIMEOUT`, or `INVALID_RESPONSE` that ended an earlier message, including one `close()` was waiting out, or the `reason` given to `destroy()`.
411
503
 
412
504
  ### `CONNECTION_FAILED`
413
505
 
414
506
  `MllpConnectionFailedError` · delivery `not-sent` · field: `cause`
415
507
 
416
- The socket could not be opened. `cause` carries the underlying error — `ECONNREFUSED`, `ENOTFOUND`, `EHOSTUNREACH` and the like.
508
+ The socket could not be opened. `cause` carries the underlying error — `ECONNREFUSED`, `ENOTFOUND`, `EHOSTUNREACH`, or a TLS error such as `UNABLE_TO_VERIFY_LEAF_SIGNATURE`.
417
509
 
418
- Check host, port, and whether a firewall allows the route. Nothing was opened, so there is nothing to close.
510
+ Check host, port, whether a firewall allows the route, and, over TLS, the certificate the receiver presents. Nothing was opened, so there is nothing to close.
419
511
 
420
512
  ### `CONNECTION_TIMEOUT`
421
513
 
@@ -423,7 +515,7 @@ Check host, port, and whether a firewall allows the route. Nothing was opened, s
423
515
 
424
516
  The receiver did not accept the connection within `connectTimeoutMs`.
425
517
 
426
- Typically a packet-dropping firewall rather than a refused connection — a refusal arrives fast and surfaces as `CONNECTION_FAILED`.
518
+ Typically a packet-dropping firewall rather than a refused connection — a refusal arrives fast and surfaces as `CONNECTION_FAILED`. With `tls` set, also a receiver that speaks plain TCP and never answers the TLS handshake.
427
519
 
428
520
  ### `SEND_TIMEOUT`
429
521
 
@@ -489,6 +581,12 @@ Clients that pipeline can survive a timeout, because they keep a table of outsta
489
581
 
490
582
  The client closes with the failure, and never sends the failed message again — only you know whether the receiver already has it. Construct a new client to go on, as you would open a new socket.
491
583
 
584
+ ### Does `send()` queue?
585
+
586
+ Yes, in memory and without a bound. A `send()` arriving while a message is on the wire waits for that message's acknowledgment, then goes out; sends leave in the order they were called, one at a time, so an A01 fired before its A03 reaches the receiver first. Nothing goes out behind a message whose delivery is unknown: a failure closes the client, and every send still waiting rejects with `CLOSED` and `delivery: "not-sent"`. A send waiting behind a dial that fails rejects with the dial's error, as `connect()` does.
587
+
588
+ The queue exists only in this process. A message waiting in it is not on disk, so a crash loses it without a trace, and a producer faster than the receiver grows it without limit. An interface that must not lose events keeps its own persistent outbound queue and hands the client one message at a time; the client's line is for a batch whose outcomes the caller is awaiting.
589
+
492
590
  ### Why a socket rather than a host and port?
493
591
 
494
592
  The client handles MLLP and deliberately handles no transport. Putting the whole runtime dependency behind two methods means the same client runs over TCP, TLS, a Unix socket, or a pair of in-memory streams — and the test suite covers every lifecycle path without opening a port.
@@ -1,10 +1,17 @@
1
1
  /**
2
- * Option validation. Each `assert*` throws {@link MllpInvalidOptionError} for
3
- * an option a caller passed out of range, before anything happens.
2
+ * Option validation, and the client's invariants. Each option `assert*`
3
+ * throws {@link MllpInvalidOptionError} for an option a caller passed out of
4
+ * range, before anything happens. {@link assertPhase} throws for a phase the
5
+ * client cannot be in where it is asserted: a bug.
4
6
  *
5
7
  * @module
6
8
  */
9
+ import type { State } from "./state";
7
10
  export declare function assertTimeoutMs(option: string, ms: number): void;
8
11
  export declare function assertByteCap(bytes: number): void;
12
+ /** `state` is in `phase`; `where` names the site for the report. */
13
+ export declare function assertPhase<P extends State["phase"]>(state: State, phase: P, where: string): asserts state is Extract<State, {
14
+ phase: P;
15
+ }>;
9
16
  export declare function assertReconnectAttempts(attempts: number): void;
10
17
  //# sourceMappingURL=assertions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAMhE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAO9D"}
1
+ {"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAMhE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,EAClD,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,CAM/C;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAO9D"}
@@ -0,0 +1,32 @@
1
+ //#region src/errors/base.ts
2
+ /**
3
+ * The base every client error extends, and what each one carries: a fixed
4
+ * `code`, a fixed message, what became of the message, and the lower layer's
5
+ * failure on `cause`.
6
+ *
7
+ * @module
8
+ */
9
+ const MllpErrorCode = {
10
+ CLOSED: "CLOSED",
11
+ CONNECTION_FAILED: "CONNECTION_FAILED",
12
+ CONNECTION_LOST: "CONNECTION_LOST",
13
+ CONNECTION_TIMEOUT: "CONNECTION_TIMEOUT",
14
+ INVALID_MESSAGE: "INVALID_MESSAGE",
15
+ INVALID_OPTION: "INVALID_OPTION",
16
+ INVALID_RESPONSE: "INVALID_RESPONSE",
17
+ SEND_ABORTED: "SEND_ABORTED",
18
+ SEND_TIMEOUT: "SEND_TIMEOUT"
19
+ };
20
+ var MllpClientError = class extends Error {};
21
+ //#endregion
22
+ //#region src/errors/mllp-invalid-option-error.ts
23
+ /** An option is out of range. Raised before anything happens. */
24
+ var MllpInvalidOptionError = class extends MllpClientError {
25
+ name = "MllpInvalidOptionError";
26
+ code = MllpErrorCode.INVALID_OPTION;
27
+ delivery = "not-sent";
28
+ };
29
+ //#endregion
30
+ export { MllpClientError as n, MllpErrorCode as r, MllpInvalidOptionError as t };
31
+
32
+ //# sourceMappingURL=mllp-invalid-option-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mllp-invalid-option-error.js","names":[],"sources":["../../src/errors/base.ts","../../src/errors/mllp-invalid-option-error.ts"],"sourcesContent":["/**\n * The base every client error extends, and what each one carries: a fixed\n * `code`, a fixed message, what became of the message, and the lower layer's\n * failure on `cause`.\n *\n * @module\n */\n\nexport const MllpErrorCode = {\n CLOSED: \"CLOSED\",\n CONNECTION_FAILED: \"CONNECTION_FAILED\",\n CONNECTION_LOST: \"CONNECTION_LOST\",\n CONNECTION_TIMEOUT: \"CONNECTION_TIMEOUT\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n INVALID_OPTION: \"INVALID_OPTION\",\n INVALID_RESPONSE: \"INVALID_RESPONSE\",\n SEND_ABORTED: \"SEND_ABORTED\",\n SEND_TIMEOUT: \"SEND_TIMEOUT\",\n} as const;\n\nexport type MllpErrorCode = (typeof MllpErrorCode)[keyof typeof MllpErrorCode];\n\n/**\n * What became of the message. `not-sent`: nothing reached the wire, and the\n * message may be sent again as it is. `unknown`: it may have reached the\n * remote system, and sending it again may deliver it twice.\n */\nexport type MllpDelivery = \"not-sent\" | \"unknown\";\n\nexport abstract class MllpClientError extends Error {\n abstract readonly code: MllpErrorCode;\n abstract readonly delivery: MllpDelivery;\n}\n","import { MllpClientError, MllpErrorCode } from \"./base\";\n\n/** An option is out of range. Raised before anything happens. */\nexport class MllpInvalidOptionError extends MllpClientError {\n override readonly name = \"MllpInvalidOptionError\";\n readonly code = MllpErrorCode.INVALID_OPTION;\n readonly delivery = \"not-sent\";\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,gBAAgB;CAC3B,QAAQ;CACR,mBAAmB;CACnB,iBAAiB;CACjB,oBAAoB;CACpB,iBAAiB;CACjB,gBAAgB;CAChB,kBAAkB;CAClB,cAAc;CACd,cAAc;CACf;AAWD,IAAsB,kBAAtB,cAA8C,MAAM;;;;AC1BpD,IAAa,yBAAb,cAA4C,gBAAgB;CAC1D,OAAyB;CACzB,OAAgB,cAAc;CAC9B,WAAoB"}
package/dist/client.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import type { Root } from "@glion/ast";
2
+ import { MllpClientError } from "./errors";
2
3
  import { MllpClientEmitter } from "./events";
3
4
  import type { MllpClientOptions, MllpClientResponse, MllpClientState, MllpSendOptions } from "./types";
4
5
  /**
5
6
  * Sends HL7v2 messages to one remote system over MLLP and returns each
6
7
  * message's acknowledgment.
7
8
  *
8
- * One client owns one socket and sends one message at a time. `send()` writes
9
- * the message, waits for the acknowledgment, checks that it answers this
10
- * message, and resolves with it.
9
+ * One client owns one socket and puts one message on the wire at a time.
10
+ * `send()` writes the message, waits for the acknowledgment, checks that it
11
+ * answers this message, and resolves with it. A send arriving while another
12
+ * is in flight waits its turn; sends go out in the order they were called.
11
13
  *
12
14
  * ```ts
13
15
  * import { MllpClient } from "@glion/mllp-client";
@@ -47,6 +49,8 @@ export declare class MllpClient extends MllpClientEmitter {
47
49
  * `closing`, or `closed`.
48
50
  */
49
51
  get state(): MllpClientState;
52
+ /** How many sends wait their turn, the one in flight excluded. */
53
+ get pending(): number;
50
54
  /** Whether the connection is open. True in both `connected` and `sending`. */
51
55
  get connected(): boolean;
52
56
  /**
@@ -59,23 +63,30 @@ export declare class MllpClient extends MllpClientEmitter {
59
63
  * @throws {MllpConnectionFailedError} The last attempt could not open the
60
64
  * socket.
61
65
  * @throws {MllpConnectionTimeoutError} The last attempt did not open in time.
62
- * @throws {MllpClientClosedError} The client is closed, or `close()` arrived
63
- * while the connection was still being established.
66
+ * @throws {MllpClientClosedError} The client is closed, or `close()` or
67
+ * `destroy()` arrived while the connection was still being established.
64
68
  */
65
69
  connect(): Promise<void>;
66
70
  /**
67
71
  * Sends one message and returns its acknowledgment. Connects first when the
68
- * client is not connected yet.
72
+ * client is not connected yet, and waits for the send in flight, if any,
73
+ * before writing.
74
+ *
75
+ * Sends go out in the order `send()` was called. The queue has no bound.
76
+ * `timeoutMs` runs from the moment the write starts, not from the call. A
77
+ * send still waiting when the client closes rejects with
78
+ * {@link MllpClientClosedError}; one waiting behind a dial that fails
79
+ * rejects with the dial's error, as `connect()` does.
69
80
  *
70
81
  * Nothing is written when an option, phase, or message error is thrown.
71
- * Every other failure also closes the client.
82
+ * Every wire failure also closes the client; a NAK does not.
72
83
  *
73
84
  * @throws {AckException} The remote system answered with a NAK. The
74
85
  * connection stays open.
75
86
  * @throws {MllpInvalidOptionError} `timeoutMs` is out of range.
76
87
  * @throws {MllpInvalidMessageError} The message cannot be sent as-is.
77
- * @throws {MllpClientClosedError} The client is closed.
78
- * @throws {MllpAlreadySendingError} Another send is in flight.
88
+ * @throws {MllpClientClosedError} The client is closed, or closed while
89
+ * this send was waiting its turn.
79
90
  * @throws {MllpConnectionFailedError} The connection could not be opened.
80
91
  * @throws {MllpConnectionTimeoutError} The connection did not open in time.
81
92
  * @throws {MllpSendTimeoutError} No acknowledgment arrived in time.
@@ -87,21 +98,29 @@ export declare class MllpClient extends MllpClientEmitter {
87
98
  send(message: Root, opts?: MllpSendOptions): Promise<MllpClientResponse>;
88
99
  /**
89
100
  * Ends the connection once the message in flight is acknowledged, and
90
- * resolves when it is down. New sends are refused from the moment this is
91
- * called. An attempt to connect stops at once.
101
+ * resolves when it is down. New sends, and sends waiting their turn, are
102
+ * refused from the moment this is called. An attempt to connect stops at
103
+ * once.
92
104
  *
93
105
  * Resolves from any phase. Never rejects. Idempotent. The wait is bounded by
94
106
  * the in-flight send's own deadline; {@link destroy} does not wait at all.
107
+ * If the message it waits for fails, the client closes with that failure;
108
+ * otherwise with `null`.
95
109
  */
96
110
  close(): Promise<void>;
97
111
  /**
98
112
  * Ends the connection now, and resolves when it is down. A message in flight
99
113
  * rejects with {@link MllpSendAbortedError}. An attempt to connect stops at
100
- * once.
114
+ * once. The client closes with `reason`: the failure it reports on `close`
115
+ * and on every later call, or `null` for the owner's own decision.
101
116
  *
102
- * Resolves from any phase. Never rejects. Idempotent.
117
+ * Resolves from any phase. Never rejects. Idempotent. Sends waiting their
118
+ * turn are rejected with `reason` when its delivery is `not-sent`, and with
119
+ * {@link MllpClientClosedError} carrying `reason` otherwise. Arriving while
120
+ * the client is already closing, it cuts the message in flight and joins
121
+ * the ending under way; `reason` is recorded when no failure is known yet.
103
122
  */
104
- destroy(): Promise<void>;
123
+ destroy(reason?: MllpClientError | null): Promise<void>;
105
124
  /** Calls {@link close}. Enables `await using`. */
106
125
  [Symbol.asyncDispose](): Promise<void>;
107
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAsBvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAO7C,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,eAAe,EAEhB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,UAAW,SAAQ,iBAAiB;;IAO/C;;;OAGG;gBACS,IAAI,EAAE,iBAAiB;IA0BnC;;;OAGG;IACH,IAAI,KAAK,IAAI,eAAe,CAE3B;IAED,8EAA8E;IAC9E,IAAI,SAAS,IAAI,OAAO,CAGvB;IAED;;;;;;;;;;;;OAYG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B9B;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,IAAI,CACR,OAAO,EAAE,IAAI,EACb,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,kBAAkB,CAAC;IA2C9B;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB9B,kDAAkD;IAC5C,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAmK7C"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAevC,OAAO,EAEL,eAAe,EAIhB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAQ7C,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,eAAe,EAEhB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,UAAW,SAAQ,iBAAiB;;IAQ/C;;;OAGG;gBACS,IAAI,EAAE,iBAAiB;IA0BnC;;;OAGG;IACH,IAAI,KAAK,IAAI,eAAe,CAE3B;IAED,kEAAkE;IAClE,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,8EAA8E;IAC9E,IAAI,SAAS,IAAI,OAAO,CAGvB;IAED;;;;;;;;;;;;OAYG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC9B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,IAAI,CACR,OAAO,EAAE,IAAI,EACb,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,kBAAkB,CAAC;IA0B9B;;;;;;;;;;OAUG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B5B;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,MAAM,GAAE,eAAe,GAAG,IAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CnE,kDAAkD;IAC5C,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAkJ7C"}
@@ -6,7 +6,6 @@
6
6
  * @module
7
7
  */
8
8
  export declare const MllpErrorCode: {
9
- readonly ALREADY_SENDING: "ALREADY_SENDING";
10
9
  readonly CLOSED: "CLOSED";
11
10
  readonly CONNECTION_FAILED: "CONNECTION_FAILED";
12
11
  readonly CONNECTION_LOST: "CONNECTION_LOST";
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/errors/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;CAWhB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElD,8BAAsB,eAAgB,SAAQ,KAAK;IACjD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;CAC1C"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/errors/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;CAUhB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElD,8BAAsB,eAAgB,SAAQ,KAAK;IACjD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;CAC1C"}
@@ -1,4 +1,3 @@
1
- export { MllpAlreadySendingError } from "./mllp-already-sending-error";
2
1
  export { MllpClientError, MllpErrorCode } from "./base";
3
2
  export type { MllpDelivery } from "./base";
4
3
  export { MllpClientClosedError } from "./mllp-client-closed-error";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1,8 +1,7 @@
1
1
  import { MllpClientError } from "./base";
2
2
  /**
3
3
  * The call cannot be served: the client is closed, by `close()`, by
4
- * `destroy()`, or by a lost connection the reconnect policy did not restore.
5
- * That last failure is on `cause`.
4
+ * `destroy()`, or by a failure. The failure is on `cause`.
6
5
  */
7
6
  export declare class MllpClientClosedError extends MllpClientError {
8
7
  readonly name = "MllpClientClosedError";
@@ -1 +1 @@
1
- {"version":3,"file":"mllp-client-closed-error.d.ts","sourceRoot":"","sources":["../../src/errors/mllp-client-closed-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,QAAQ,CAAC;AAExD;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,eAAe;IACxD,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,IAAI,WAAwB;IACrC,QAAQ,CAAC,QAAQ,cAAc;gBAEnB,KAAK,GAAE,eAAe,GAAG,IAAW;CAQjD"}
1
+ {"version":3,"file":"mllp-client-closed-error.d.ts","sourceRoot":"","sources":["../../src/errors/mllp-client-closed-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,QAAQ,CAAC;AAExD;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,eAAe;IACxD,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,IAAI,WAAwB;IACrC,QAAQ,CAAC,QAAQ,cAAc;gBAEnB,KAAK,GAAE,eAAe,GAAG,IAAW;CAQjD"}
package/dist/events.d.ts CHANGED
@@ -8,8 +8,8 @@ export interface MllpClientEvents {
8
8
  /** The connection is open and ready for messages. */
9
9
  connect(): void;
10
10
  /**
11
- * The client is done and will not connect again. `error` is the failure the
12
- * reconnect policy did not recover from, or `null` when it closes normally.
11
+ * The connection is down and the client will not connect again. `error` is
12
+ * the failure that closed it, or `null` when the owner did.
13
13
  */
14
14
  close(error: MllpClientError | null): void;
15
15
  }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,6 @@ export { MllpClient } from "./client";
15
15
  export { MllpClientEmitter } from "./events";
16
16
  export type { MllpClientEvent, MllpClientEvents, MllpClientListener, } from "./events";
17
17
  export type { MllpClientOptions, MllpClientResponse, MllpClientState, MllpReconnectOptions, MllpSendOptions, MllpSocket, MllpStreams, } from "./types";
18
- export { MllpAlreadySendingError, MllpClientClosedError, MllpClientError, MllpConnectionError, MllpConnectionFailedError, MllpConnectionLostError, MllpConnectionTimeoutError, MllpErrorCode, MllpInvalidMessageError, MllpInvalidOptionError, MllpInvalidResponseError, MllpSendAbortedError, MllpSendTimeoutError, } from "./errors";
18
+ export { MllpClientClosedError, MllpClientError, MllpConnectionError, MllpConnectionFailedError, MllpConnectionLostError, MllpConnectionTimeoutError, MllpErrorCode, MllpInvalidMessageError, MllpInvalidOptionError, MllpInvalidResponseError, MllpSendAbortedError, MllpSendTimeoutError, } from "./errors";
19
19
  export type { MllpDelivery } from "./errors";
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}