hl7v2-net 1.4.4 → 1.6.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.
@@ -4,14 +4,21 @@ exports.FrameStream = void 0;
4
4
  const node_buffer_1 = require("node:buffer");
5
5
  const node_stream_1 = require("node:stream");
6
6
  class FrameStream extends node_stream_1.Transform {
7
+ _chunks = [];
8
+ _chunkBuffer;
9
+ _flushTimeout;
10
+ _frameDelayMs;
11
+ _frameStart;
12
+ _frameEnd;
13
+ _bufferSize = 0;
14
+ _customTransform;
15
+ maxBufferSize;
7
16
  constructor(opts) {
8
17
  super({
9
18
  ...opts,
10
19
  objectMode: true,
11
20
  transform: undefined,
12
21
  });
13
- this._chunks = [];
14
- this._bufferSize = 0;
15
22
  this._customTransform = opts?.transform;
16
23
  this._frameDelayMs = opts?.frameDelayMs || (opts?.frameEnd ? 0 : 500);
17
24
  this.maxBufferSize = opts?.maxBufferSize;
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HL7ExchangeError = void 0;
4
4
  const hl7v2_1 = require("hl7v2");
5
5
  class HL7ExchangeError extends hl7v2_1.HL7Error {
6
+ request;
7
+ response;
6
8
  constructor(message, args) {
7
9
  super(message, args);
8
10
  if (args?.request)
package/cjs/hl7-client.js CHANGED
@@ -5,11 +5,17 @@ const tslib_1 = require("tslib");
5
5
  const node_net_1 = tslib_1.__importDefault(require("node:net"));
6
6
  const node_tls_1 = tslib_1.__importDefault(require("node:tls"));
7
7
  const node_events_async_1 = require("node-events-async");
8
+ const reconnect_core_1 = tslib_1.__importDefault(require("reconnect-core"));
8
9
  const hl7_request_js_1 = require("./hl7-request.js");
9
10
  const hl7_response_js_1 = require("./hl7-response.js");
10
11
  const hl7_router_js_1 = require("./hl7-router.js");
11
12
  const hl7_socket_js_1 = require("./hl7-socket.js");
12
13
  class Hl7Client extends node_events_async_1.AsyncEventEmitter {
14
+ _connectionManager;
15
+ _router = new hl7_router_js_1.HL7Router();
16
+ _socket;
17
+ _tls;
18
+ _options;
13
19
  /**
14
20
  * Creates an HL7 TCP client
15
21
  * @static
@@ -35,8 +41,60 @@ class Hl7Client extends node_events_async_1.AsyncEventEmitter {
35
41
  */
36
42
  constructor(options) {
37
43
  super();
38
- this._router = new hl7_router_js_1.HL7Router();
39
44
  this._options = options;
45
+ let reconnecting = false;
46
+ const connectionManager = (this._connectionManager = socketReconnect({
47
+ ...this._options.reconnect,
48
+ timeout: this._options.connectTimeout,
49
+ })
50
+ .on('error', err => {
51
+ if (!connectionManager.reconnect)
52
+ return;
53
+ this.emit('error', err);
54
+ })
55
+ .on('connect', (tcpSocket) => {
56
+ if (!this._socket) {
57
+ const socket = (this._socket = new hl7_socket_js_1.HL7Socket(tcpSocket, this._options));
58
+ socket.on('connect', () => this.emit('connect'));
59
+ socket.on('ready', () => this.emit('ready'));
60
+ socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
61
+ socket.on('close', () => {
62
+ this._socket = undefined;
63
+ this.emit('close');
64
+ });
65
+ socket.on('error', err => this.emit('error', err));
66
+ socket.on('message', message => {
67
+ this.emit('message', message);
68
+ this._onMessage(message);
69
+ });
70
+ socket.on('send', message => this.emit('send', message));
71
+ socket.on('data', data => this.emit('data', data));
72
+ }
73
+ else
74
+ this._socket._bindSocket(tcpSocket);
75
+ if (reconnecting)
76
+ this.emit('reconnect', tcpSocket);
77
+ reconnecting = false;
78
+ this.emit('connect');
79
+ })
80
+ .on('reconnect', (n, delay) => {
81
+ if (n === 0 && delay === 0)
82
+ return;
83
+ reconnecting = true;
84
+ this.emit('reconnecting', n, delay);
85
+ })
86
+ .on('disconnect', err => {
87
+ this._socket = undefined;
88
+ if (connectionManager.reconnect) {
89
+ if (err)
90
+ err.message = `TCP socket connection lost: ${err.message}`;
91
+ else
92
+ err = new Error('TCP socket connection lost');
93
+ this.emit('disconnect', err);
94
+ }
95
+ else
96
+ this.emit('disconnect');
97
+ }));
40
98
  }
41
99
  get connected() {
42
100
  return this._socket?.connected ?? false;
@@ -81,57 +139,32 @@ class Hl7Client extends node_events_async_1.AsyncEventEmitter {
81
139
  if (this._socket)
82
140
  this._socket.maxBufferSize = value;
83
141
  }
84
- connect() {
142
+ async connect() {
143
+ if (this.connected)
144
+ return;
85
145
  return new Promise((resolve, reject) => {
86
- if (this.connected) {
87
- resolve();
88
- return;
89
- }
90
- let timeoutTimer;
91
- const tcpSocket = this._tls
92
- ? node_tls_1.default.connect(this._options)
93
- : node_net_1.default.connect(this._options);
94
- const socket = (this._socket = new hl7_socket_js_1.HL7Socket(tcpSocket, this._options));
95
- socket.on('connect', () => this.emit('connect'));
96
- socket.on('ready', () => this.emit('ready'));
97
- socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
98
- socket.on('close', () => {
99
- this._socket = undefined;
100
- this.emit('close');
101
- });
102
- socket.on('error', err => this.emit('error', err));
103
- socket.on('message', message => {
104
- this.emit('message', message);
105
- this._onMessage(message);
106
- });
107
- socket.on('send', message => this.emit('send', message));
108
- socket.on('data', data => this.emit('data', data));
109
146
  const onReady = () => {
110
- clearTimeout(timeoutTimer);
111
- tcpSocket.removeListener('error', onError);
112
- if (this._options.keepAlive) {
113
- tcpSocket.setKeepAlive(this._options.keepAlive, this._options.keepAliveInitialDelay);
114
- }
147
+ this.removeListener('error', onError);
115
148
  resolve();
116
149
  };
117
150
  const onError = (error) => {
118
- clearTimeout(timeoutTimer);
119
- tcpSocket.removeListener('ready', onReady);
120
- tcpSocket.destroy();
151
+ this.removeListener('ready', onReady);
121
152
  reject(error);
122
153
  };
123
- tcpSocket.once('ready', onReady);
124
- tcpSocket.once('error', onError);
125
- if (this.connectTimeout) {
126
- timeoutTimer = setTimeout(() => {
127
- this.emit('error', new Error('Connection timeout'));
128
- tcpSocket.destroy();
129
- }, this._options.connectTimeout).unref();
130
- }
154
+ this.once('ready', onReady);
155
+ this.once('error', onError);
156
+ this._connectionManager.reconnect = true;
157
+ this._connectionManager.connect({
158
+ tls: this._tls,
159
+ options: this._options,
160
+ });
131
161
  });
132
162
  }
133
163
  async close(waitRunningHandlers) {
164
+ this._connectionManager.reconnect = false;
134
165
  await this._socket?.close(waitRunningHandlers);
166
+ this._connectionManager.disconnect();
167
+ this._connectionManager.reset();
135
168
  }
136
169
  async sendMessage(message) {
137
170
  if (!this.connected)
@@ -161,3 +194,37 @@ class Hl7Client extends node_events_async_1.AsyncEventEmitter {
161
194
  }
162
195
  }
163
196
  exports.Hl7Client = Hl7Client;
197
+ const socketReconnect = (0, reconnect_core_1.default)((args) => {
198
+ const tcpSocket = args.tls
199
+ ? node_tls_1.default.connect({
200
+ ...args.options,
201
+ timeout: undefined,
202
+ })
203
+ : node_net_1.default.connect({
204
+ ...args.options,
205
+ timeout: undefined,
206
+ });
207
+ if (args.options.timeout) {
208
+ const timer = setTimeout(() => {
209
+ tcpSocket.destroy(new Error('Connection timed out'));
210
+ }, args.options.timeout).unref();
211
+ const onConnect = () => {
212
+ clearTimeout(timer);
213
+ tcpSocket.removeListener('error', onError);
214
+ };
215
+ const onError = () => {
216
+ clearTimeout(timer);
217
+ tcpSocket.removeListener('connect', onConnect);
218
+ tcpSocket.removeListener('ready', onReady);
219
+ };
220
+ const onReady = () => {
221
+ if (args.options.keepAlive) {
222
+ tcpSocket.setKeepAlive(args.options.keepAlive, args.options.keepAliveInitialDelay);
223
+ }
224
+ };
225
+ tcpSocket.once('connect', onConnect);
226
+ tcpSocket.once('error', onError);
227
+ tcpSocket.once('ready', onReady);
228
+ }
229
+ return tcpSocket;
230
+ });
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HL7Request = void 0;
4
4
  class HL7Request {
5
+ socket;
6
+ message;
5
7
  constructor(socket, message) {
6
8
  this.socket = socket;
7
9
  this.message = message;
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HL7Response = void 0;
4
4
  const hl7v2_1 = require("hl7v2");
5
5
  class HL7Response {
6
+ _finished = false;
7
+ request;
8
+ errors = [];
9
+ message;
6
10
  constructor(request) {
7
- this._finished = false;
8
- this.errors = [];
9
11
  this.request = request;
10
12
  this.message = request.message.createAck();
11
13
  request.socket.once('send', () => {
package/cjs/hl7-router.js CHANGED
@@ -2,11 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HL7Router = void 0;
4
4
  class HL7Router {
5
- constructor() {
6
- this._handlerStack = new Map();
7
- this._needPrepare = true;
8
- this._handlers = [];
9
- }
5
+ _handlerStack = new Map();
6
+ _needPrepare = true;
7
+ _handlers = [];
10
8
  use(handler, priority = 0) {
11
9
  let list = this._handlerStack[priority];
12
10
  if (!list) {
package/cjs/hl7-server.js CHANGED
@@ -13,6 +13,14 @@ const hl7_response_js_1 = require("./hl7-response.js");
13
13
  const hl7_router_js_1 = require("./hl7-router.js");
14
14
  const hl7_socket_js_1 = require("./hl7-socket.js");
15
15
  class HL7Server extends node_events_async_1.AsyncEventEmitter {
16
+ _server;
17
+ _sockets = new Set();
18
+ _router = new hl7_router_js_1.HL7Router();
19
+ _runningHandlers = new Set();
20
+ _closing;
21
+ parseStrict;
22
+ maxBufferPerSocket;
23
+ responseTimeout;
16
24
  /**
17
25
  * Creates a HL7 TCP server
18
26
  * @static
@@ -34,9 +42,6 @@ class HL7Server extends node_events_async_1.AsyncEventEmitter {
34
42
  */
35
43
  constructor(server, options) {
36
44
  super();
37
- this._sockets = new Set();
38
- this._router = new hl7_router_js_1.HL7Router();
39
- this._runningHandlers = new Set();
40
45
  this.maxBufferPerSocket = options?.maxBufferPerSocket;
41
46
  this.responseTimeout = options?.responseTimeout;
42
47
  this.parseStrict = options?.parseStrict;
@@ -197,7 +202,8 @@ class HL7Server extends node_events_async_1.AsyncEventEmitter {
197
202
  this._onMessage(message, socket);
198
203
  });
199
204
  socket.on('send', message => this.emit('send', message, socket));
200
- socket.on('data', data => this.emit('data', data));
205
+ socket.on('data', data => this.emit('data', data, socket));
206
+ socket.on('write', data => this.emit('write', data, socket));
201
207
  this.emit('connection', socket);
202
208
  }
203
209
  _onMessage(message, socket) {
package/cjs/hl7-socket.js CHANGED
@@ -2,41 +2,39 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HL7Socket = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const node_buffer_1 = require("node:buffer");
5
6
  const hl7v2_1 = require("hl7v2");
6
7
  const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
7
8
  const node_events_async_1 = require("node-events-async");
8
9
  const frame_stream_js_1 = require("./helpers/frame-stream.js");
9
10
  class HL7Socket extends node_events_async_1.AsyncEventEmitter {
11
+ _messageHooks = new Set();
12
+ _frameStream;
13
+ _waitPromises = new Set();
14
+ _options;
15
+ responseTimeout;
16
+ /**
17
+ * User defined property
18
+ * @protected
19
+ */
20
+ user;
10
21
  constructor(socket, options) {
11
22
  super();
12
- this._messageHooks = new Set();
13
- this._waitPromises = new Set();
14
- this.socket = socket;
15
23
  this._options = options;
16
24
  const frameStream = new frame_stream_js_1.FrameStream({
17
25
  frameStart: hl7v2_1.VT,
18
26
  frameEnd: hl7v2_1.FS + hl7v2_1.CR,
19
27
  maxBufferSize: options?.maxBufferSize,
20
28
  });
21
- this._frameStream = frameStream;
22
- socket.on('error', (err) => {
23
- if (err.code === 'ECONNRESET') {
24
- err.message = `Connection reset by peer`;
25
- }
26
- this.emit('error', err);
27
- });
28
- socket.pipe(frameStream);
29
- socket.on('connect', () => this.emit('connect'));
30
- socket.on('ready', () => this.emit('ready'));
31
- socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
32
- socket.on('timeout', () => socket.destroy());
33
- socket.on('close', () => {
34
- this.emit('close');
35
- });
36
29
  frameStream.on('data', data => {
37
30
  this.emit('data', data);
38
31
  this._onData(data);
39
32
  });
33
+ this._frameStream = frameStream;
34
+ this._bindSocket(socket);
35
+ }
36
+ get socket() {
37
+ return this._socket;
40
38
  }
41
39
  get connected() {
42
40
  return !this.socket.closed;
@@ -45,7 +43,7 @@ class HL7Socket extends node_events_async_1.AsyncEventEmitter {
45
43
  return this.socket.closed;
46
44
  }
47
45
  get readyState() {
48
- return this.socket.readyState;
46
+ return this.socket?.readyState || 'closed';
49
47
  }
50
48
  get maxBufferSize() {
51
49
  return this._frameStream.maxBufferSize || 0;
@@ -54,14 +52,14 @@ class HL7Socket extends node_events_async_1.AsyncEventEmitter {
54
52
  this._frameStream.maxBufferSize = value;
55
53
  }
56
54
  address() {
57
- return this.socket.address();
55
+ return this.socket?.address() || {};
58
56
  }
59
57
  remoteAddress() {
60
- const addr = this.socket.remoteAddress;
58
+ const addr = this.socket?.remoteAddress;
61
59
  return addr?.startsWith('::ffff:') ? addr.slice(7) : addr;
62
60
  }
63
61
  get writable() {
64
- return this.connected && this.socket.writable;
62
+ return this.connected && this.socket?.writable;
65
63
  }
66
64
  async close(waitRunningHandlers) {
67
65
  if (this.closed)
@@ -106,10 +104,14 @@ class HL7Socket extends node_events_async_1.AsyncEventEmitter {
106
104
  }
107
105
  const str = message.toHL7String();
108
106
  const buf = iconv_lite_1.default.encode(str, encoding);
109
- this.socket.write(hl7v2_1.VT);
110
- this.socket.write(buf);
111
- this.socket.write(hl7v2_1.FS + hl7v2_1.CR);
107
+ const outBuffer = node_buffer_1.Buffer.concat([
108
+ node_buffer_1.Buffer.from(hl7v2_1.VT),
109
+ buf,
110
+ node_buffer_1.Buffer.from(hl7v2_1.FS + hl7v2_1.CR),
111
+ ]);
112
+ this.socket.write(outBuffer);
112
113
  this.emit('send', message);
114
+ this.emit('write', outBuffer);
113
115
  }
114
116
  catch (err) {
115
117
  this.emit('error', err);
@@ -189,5 +191,44 @@ class HL7Socket extends node_events_async_1.AsyncEventEmitter {
189
191
  this.sendMessage(nak);
190
192
  }
191
193
  }
194
+ _bindSocket(socket) {
195
+ if (socket === this._socket)
196
+ return;
197
+ this._unBindSocket();
198
+ this._socket = socket;
199
+ const hl7EventListeners = (socket.__hl7EventListeners = {});
200
+ hl7EventListeners.onConnect = () => this.emit('connect');
201
+ hl7EventListeners.onReady = () => this.emit('ready');
202
+ hl7EventListeners.onLookup = (err, address, family, host) => this.emit('lookup', err, address, family, host);
203
+ hl7EventListeners.onTimeout = () => socket.destroy();
204
+ hl7EventListeners.onClose = () => this.emit('close');
205
+ hl7EventListeners.onError = (err) => {
206
+ if (err.code === 'ECONNRESET') {
207
+ err.message = `Connection reset by peer`;
208
+ }
209
+ this.emit('error', err);
210
+ };
211
+ socket.on('connect', hl7EventListeners.onConnect);
212
+ socket.on('ready', hl7EventListeners.onReady);
213
+ socket.on('lookup', hl7EventListeners.onLookup);
214
+ socket.on('timeout', hl7EventListeners.onTimeout);
215
+ socket.on('close', hl7EventListeners.onClose);
216
+ socket.on('error', hl7EventListeners.onError);
217
+ socket.pipe(this._frameStream);
218
+ }
219
+ _unBindSocket() {
220
+ if (!this.socket)
221
+ return;
222
+ this.socket.unpipe(this._frameStream);
223
+ const hl7EventListeners = this.socket.__hl7EventListeners;
224
+ if (hl7EventListeners) {
225
+ this.socket.removeListener('connect', hl7EventListeners.onConnect);
226
+ this.socket.removeListener('ready', hl7EventListeners.onReady);
227
+ this.socket.removeListener('lookup', hl7EventListeners.onLookup);
228
+ this.socket.removeListener('timeout', hl7EventListeners.onTimeout);
229
+ this.socket.removeListener('close', hl7EventListeners.onClose);
230
+ this.socket.removeListener('error', hl7EventListeners.onError);
231
+ }
232
+ }
192
233
  }
193
234
  exports.HL7Socket = HL7Socket;
@@ -1,14 +1,21 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import { Transform } from 'node:stream';
3
3
  export class FrameStream extends Transform {
4
+ _chunks = [];
5
+ _chunkBuffer;
6
+ _flushTimeout;
7
+ _frameDelayMs;
8
+ _frameStart;
9
+ _frameEnd;
10
+ _bufferSize = 0;
11
+ _customTransform;
12
+ maxBufferSize;
4
13
  constructor(opts) {
5
14
  super({
6
15
  ...opts,
7
16
  objectMode: true,
8
17
  transform: undefined,
9
18
  });
10
- this._chunks = [];
11
- this._bufferSize = 0;
12
19
  this._customTransform = opts?.transform;
13
20
  this._frameDelayMs = opts?.frameDelayMs || (opts?.frameEnd ? 0 : 500);
14
21
  this.maxBufferSize = opts?.maxBufferSize;
@@ -1,5 +1,7 @@
1
1
  import { HL7Error } from 'hl7v2';
2
2
  export class HL7ExchangeError extends HL7Error {
3
+ request;
4
+ response;
3
5
  constructor(message, args) {
4
6
  super(message, args);
5
7
  if (args?.request)
package/esm/hl7-client.js CHANGED
@@ -1,11 +1,17 @@
1
1
  import net from 'node:net';
2
2
  import tls from 'node:tls';
3
3
  import { AsyncEventEmitter } from 'node-events-async';
4
+ import reconnectCore from 'reconnect-core';
4
5
  import { HL7Request } from './hl7-request.js';
5
6
  import { HL7Response } from './hl7-response.js';
6
7
  import { HL7Router } from './hl7-router.js';
7
8
  import { HL7Socket } from './hl7-socket.js';
8
9
  export class Hl7Client extends AsyncEventEmitter {
10
+ _connectionManager;
11
+ _router = new HL7Router();
12
+ _socket;
13
+ _tls;
14
+ _options;
9
15
  /**
10
16
  * Creates an HL7 TCP client
11
17
  * @static
@@ -31,8 +37,60 @@ export class Hl7Client extends AsyncEventEmitter {
31
37
  */
32
38
  constructor(options) {
33
39
  super();
34
- this._router = new HL7Router();
35
40
  this._options = options;
41
+ let reconnecting = false;
42
+ const connectionManager = (this._connectionManager = socketReconnect({
43
+ ...this._options.reconnect,
44
+ timeout: this._options.connectTimeout,
45
+ })
46
+ .on('error', err => {
47
+ if (!connectionManager.reconnect)
48
+ return;
49
+ this.emit('error', err);
50
+ })
51
+ .on('connect', (tcpSocket) => {
52
+ if (!this._socket) {
53
+ const socket = (this._socket = new HL7Socket(tcpSocket, this._options));
54
+ socket.on('connect', () => this.emit('connect'));
55
+ socket.on('ready', () => this.emit('ready'));
56
+ socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
57
+ socket.on('close', () => {
58
+ this._socket = undefined;
59
+ this.emit('close');
60
+ });
61
+ socket.on('error', err => this.emit('error', err));
62
+ socket.on('message', message => {
63
+ this.emit('message', message);
64
+ this._onMessage(message);
65
+ });
66
+ socket.on('send', message => this.emit('send', message));
67
+ socket.on('data', data => this.emit('data', data));
68
+ }
69
+ else
70
+ this._socket._bindSocket(tcpSocket);
71
+ if (reconnecting)
72
+ this.emit('reconnect', tcpSocket);
73
+ reconnecting = false;
74
+ this.emit('connect');
75
+ })
76
+ .on('reconnect', (n, delay) => {
77
+ if (n === 0 && delay === 0)
78
+ return;
79
+ reconnecting = true;
80
+ this.emit('reconnecting', n, delay);
81
+ })
82
+ .on('disconnect', err => {
83
+ this._socket = undefined;
84
+ if (connectionManager.reconnect) {
85
+ if (err)
86
+ err.message = `TCP socket connection lost: ${err.message}`;
87
+ else
88
+ err = new Error('TCP socket connection lost');
89
+ this.emit('disconnect', err);
90
+ }
91
+ else
92
+ this.emit('disconnect');
93
+ }));
36
94
  }
37
95
  get connected() {
38
96
  return this._socket?.connected ?? false;
@@ -77,57 +135,32 @@ export class Hl7Client extends AsyncEventEmitter {
77
135
  if (this._socket)
78
136
  this._socket.maxBufferSize = value;
79
137
  }
80
- connect() {
138
+ async connect() {
139
+ if (this.connected)
140
+ return;
81
141
  return new Promise((resolve, reject) => {
82
- if (this.connected) {
83
- resolve();
84
- return;
85
- }
86
- let timeoutTimer;
87
- const tcpSocket = this._tls
88
- ? tls.connect(this._options)
89
- : net.connect(this._options);
90
- const socket = (this._socket = new HL7Socket(tcpSocket, this._options));
91
- socket.on('connect', () => this.emit('connect'));
92
- socket.on('ready', () => this.emit('ready'));
93
- socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
94
- socket.on('close', () => {
95
- this._socket = undefined;
96
- this.emit('close');
97
- });
98
- socket.on('error', err => this.emit('error', err));
99
- socket.on('message', message => {
100
- this.emit('message', message);
101
- this._onMessage(message);
102
- });
103
- socket.on('send', message => this.emit('send', message));
104
- socket.on('data', data => this.emit('data', data));
105
142
  const onReady = () => {
106
- clearTimeout(timeoutTimer);
107
- tcpSocket.removeListener('error', onError);
108
- if (this._options.keepAlive) {
109
- tcpSocket.setKeepAlive(this._options.keepAlive, this._options.keepAliveInitialDelay);
110
- }
143
+ this.removeListener('error', onError);
111
144
  resolve();
112
145
  };
113
146
  const onError = (error) => {
114
- clearTimeout(timeoutTimer);
115
- tcpSocket.removeListener('ready', onReady);
116
- tcpSocket.destroy();
147
+ this.removeListener('ready', onReady);
117
148
  reject(error);
118
149
  };
119
- tcpSocket.once('ready', onReady);
120
- tcpSocket.once('error', onError);
121
- if (this.connectTimeout) {
122
- timeoutTimer = setTimeout(() => {
123
- this.emit('error', new Error('Connection timeout'));
124
- tcpSocket.destroy();
125
- }, this._options.connectTimeout).unref();
126
- }
150
+ this.once('ready', onReady);
151
+ this.once('error', onError);
152
+ this._connectionManager.reconnect = true;
153
+ this._connectionManager.connect({
154
+ tls: this._tls,
155
+ options: this._options,
156
+ });
127
157
  });
128
158
  }
129
159
  async close(waitRunningHandlers) {
160
+ this._connectionManager.reconnect = false;
130
161
  await this._socket?.close(waitRunningHandlers);
162
+ this._connectionManager.disconnect();
163
+ this._connectionManager.reset();
131
164
  }
132
165
  async sendMessage(message) {
133
166
  if (!this.connected)
@@ -156,3 +189,37 @@ export class Hl7Client extends AsyncEventEmitter {
156
189
  });
157
190
  }
158
191
  }
192
+ const socketReconnect = reconnectCore((args) => {
193
+ const tcpSocket = args.tls
194
+ ? tls.connect({
195
+ ...args.options,
196
+ timeout: undefined,
197
+ })
198
+ : net.connect({
199
+ ...args.options,
200
+ timeout: undefined,
201
+ });
202
+ if (args.options.timeout) {
203
+ const timer = setTimeout(() => {
204
+ tcpSocket.destroy(new Error('Connection timed out'));
205
+ }, args.options.timeout).unref();
206
+ const onConnect = () => {
207
+ clearTimeout(timer);
208
+ tcpSocket.removeListener('error', onError);
209
+ };
210
+ const onError = () => {
211
+ clearTimeout(timer);
212
+ tcpSocket.removeListener('connect', onConnect);
213
+ tcpSocket.removeListener('ready', onReady);
214
+ };
215
+ const onReady = () => {
216
+ if (args.options.keepAlive) {
217
+ tcpSocket.setKeepAlive(args.options.keepAlive, args.options.keepAliveInitialDelay);
218
+ }
219
+ };
220
+ tcpSocket.once('connect', onConnect);
221
+ tcpSocket.once('error', onError);
222
+ tcpSocket.once('ready', onReady);
223
+ }
224
+ return tcpSocket;
225
+ });
@@ -1,4 +1,6 @@
1
1
  export class HL7Request {
2
+ socket;
3
+ message;
2
4
  constructor(socket, message) {
3
5
  this.socket = socket;
4
6
  this.message = message;
@@ -1,8 +1,10 @@
1
1
  import { MSASegment } from 'hl7v2';
2
2
  export class HL7Response {
3
+ _finished = false;
4
+ request;
5
+ errors = [];
6
+ message;
3
7
  constructor(request) {
4
- this._finished = false;
5
- this.errors = [];
6
8
  this.request = request;
7
9
  this.message = request.message.createAck();
8
10
  request.socket.once('send', () => {
package/esm/hl7-router.js CHANGED
@@ -1,9 +1,7 @@
1
1
  export class HL7Router {
2
- constructor() {
3
- this._handlerStack = new Map();
4
- this._needPrepare = true;
5
- this._handlers = [];
6
- }
2
+ _handlerStack = new Map();
3
+ _needPrepare = true;
4
+ _handlers = [];
7
5
  use(handler, priority = 0) {
8
6
  let list = this._handlerStack[priority];
9
7
  if (!list) {
package/esm/hl7-server.js CHANGED
@@ -9,6 +9,14 @@ import { HL7Response } from './hl7-response.js';
9
9
  import { HL7Router } from './hl7-router.js';
10
10
  import { HL7Socket } from './hl7-socket.js';
11
11
  export class HL7Server extends AsyncEventEmitter {
12
+ _server;
13
+ _sockets = new Set();
14
+ _router = new HL7Router();
15
+ _runningHandlers = new Set();
16
+ _closing;
17
+ parseStrict;
18
+ maxBufferPerSocket;
19
+ responseTimeout;
12
20
  /**
13
21
  * Creates a HL7 TCP server
14
22
  * @static
@@ -30,9 +38,6 @@ export class HL7Server extends AsyncEventEmitter {
30
38
  */
31
39
  constructor(server, options) {
32
40
  super();
33
- this._sockets = new Set();
34
- this._router = new HL7Router();
35
- this._runningHandlers = new Set();
36
41
  this.maxBufferPerSocket = options?.maxBufferPerSocket;
37
42
  this.responseTimeout = options?.responseTimeout;
38
43
  this.parseStrict = options?.parseStrict;
@@ -193,7 +198,8 @@ export class HL7Server extends AsyncEventEmitter {
193
198
  this._onMessage(message, socket);
194
199
  });
195
200
  socket.on('send', message => this.emit('send', message, socket));
196
- socket.on('data', data => this.emit('data', data));
201
+ socket.on('data', data => this.emit('data', data, socket));
202
+ socket.on('write', data => this.emit('write', data, socket));
197
203
  this.emit('connection', socket);
198
204
  }
199
205
  _onMessage(message, socket) {
package/esm/hl7-socket.js CHANGED
@@ -1,38 +1,36 @@
1
+ import { Buffer } from 'node:buffer';
1
2
  import { CR, FS, HL7Message, MSASegment, MSHSegment, VT, } from 'hl7v2';
2
3
  import iconv from 'iconv-lite';
3
4
  import { AsyncEventEmitter } from 'node-events-async';
4
5
  import { FrameStream } from './helpers/frame-stream.js';
5
6
  export class HL7Socket extends AsyncEventEmitter {
7
+ _messageHooks = new Set();
8
+ _frameStream;
9
+ _waitPromises = new Set();
10
+ _options;
11
+ responseTimeout;
12
+ /**
13
+ * User defined property
14
+ * @protected
15
+ */
16
+ user;
6
17
  constructor(socket, options) {
7
18
  super();
8
- this._messageHooks = new Set();
9
- this._waitPromises = new Set();
10
- this.socket = socket;
11
19
  this._options = options;
12
20
  const frameStream = new FrameStream({
13
21
  frameStart: VT,
14
22
  frameEnd: FS + CR,
15
23
  maxBufferSize: options?.maxBufferSize,
16
24
  });
17
- this._frameStream = frameStream;
18
- socket.on('error', (err) => {
19
- if (err.code === 'ECONNRESET') {
20
- err.message = `Connection reset by peer`;
21
- }
22
- this.emit('error', err);
23
- });
24
- socket.pipe(frameStream);
25
- socket.on('connect', () => this.emit('connect'));
26
- socket.on('ready', () => this.emit('ready'));
27
- socket.on('lookup', (err, address, family, host) => this.emit('lookup', err, address, family, host));
28
- socket.on('timeout', () => socket.destroy());
29
- socket.on('close', () => {
30
- this.emit('close');
31
- });
32
25
  frameStream.on('data', data => {
33
26
  this.emit('data', data);
34
27
  this._onData(data);
35
28
  });
29
+ this._frameStream = frameStream;
30
+ this._bindSocket(socket);
31
+ }
32
+ get socket() {
33
+ return this._socket;
36
34
  }
37
35
  get connected() {
38
36
  return !this.socket.closed;
@@ -41,7 +39,7 @@ export class HL7Socket extends AsyncEventEmitter {
41
39
  return this.socket.closed;
42
40
  }
43
41
  get readyState() {
44
- return this.socket.readyState;
42
+ return this.socket?.readyState || 'closed';
45
43
  }
46
44
  get maxBufferSize() {
47
45
  return this._frameStream.maxBufferSize || 0;
@@ -50,14 +48,14 @@ export class HL7Socket extends AsyncEventEmitter {
50
48
  this._frameStream.maxBufferSize = value;
51
49
  }
52
50
  address() {
53
- return this.socket.address();
51
+ return this.socket?.address() || {};
54
52
  }
55
53
  remoteAddress() {
56
- const addr = this.socket.remoteAddress;
54
+ const addr = this.socket?.remoteAddress;
57
55
  return addr?.startsWith('::ffff:') ? addr.slice(7) : addr;
58
56
  }
59
57
  get writable() {
60
- return this.connected && this.socket.writable;
58
+ return this.connected && this.socket?.writable;
61
59
  }
62
60
  async close(waitRunningHandlers) {
63
61
  if (this.closed)
@@ -102,10 +100,14 @@ export class HL7Socket extends AsyncEventEmitter {
102
100
  }
103
101
  const str = message.toHL7String();
104
102
  const buf = iconv.encode(str, encoding);
105
- this.socket.write(VT);
106
- this.socket.write(buf);
107
- this.socket.write(FS + CR);
103
+ const outBuffer = Buffer.concat([
104
+ Buffer.from(VT),
105
+ buf,
106
+ Buffer.from(FS + CR),
107
+ ]);
108
+ this.socket.write(outBuffer);
108
109
  this.emit('send', message);
110
+ this.emit('write', outBuffer);
109
111
  }
110
112
  catch (err) {
111
113
  this.emit('error', err);
@@ -185,4 +187,43 @@ export class HL7Socket extends AsyncEventEmitter {
185
187
  this.sendMessage(nak);
186
188
  }
187
189
  }
190
+ _bindSocket(socket) {
191
+ if (socket === this._socket)
192
+ return;
193
+ this._unBindSocket();
194
+ this._socket = socket;
195
+ const hl7EventListeners = (socket.__hl7EventListeners = {});
196
+ hl7EventListeners.onConnect = () => this.emit('connect');
197
+ hl7EventListeners.onReady = () => this.emit('ready');
198
+ hl7EventListeners.onLookup = (err, address, family, host) => this.emit('lookup', err, address, family, host);
199
+ hl7EventListeners.onTimeout = () => socket.destroy();
200
+ hl7EventListeners.onClose = () => this.emit('close');
201
+ hl7EventListeners.onError = (err) => {
202
+ if (err.code === 'ECONNRESET') {
203
+ err.message = `Connection reset by peer`;
204
+ }
205
+ this.emit('error', err);
206
+ };
207
+ socket.on('connect', hl7EventListeners.onConnect);
208
+ socket.on('ready', hl7EventListeners.onReady);
209
+ socket.on('lookup', hl7EventListeners.onLookup);
210
+ socket.on('timeout', hl7EventListeners.onTimeout);
211
+ socket.on('close', hl7EventListeners.onClose);
212
+ socket.on('error', hl7EventListeners.onError);
213
+ socket.pipe(this._frameStream);
214
+ }
215
+ _unBindSocket() {
216
+ if (!this.socket)
217
+ return;
218
+ this.socket.unpipe(this._frameStream);
219
+ const hl7EventListeners = this.socket.__hl7EventListeners;
220
+ if (hl7EventListeners) {
221
+ this.socket.removeListener('connect', hl7EventListeners.onConnect);
222
+ this.socket.removeListener('ready', hl7EventListeners.onReady);
223
+ this.socket.removeListener('lookup', hl7EventListeners.onLookup);
224
+ this.socket.removeListener('timeout', hl7EventListeners.onTimeout);
225
+ this.socket.removeListener('close', hl7EventListeners.onClose);
226
+ this.socket.removeListener('error', hl7EventListeners.onError);
227
+ }
228
+ }
188
229
  }
package/package.json CHANGED
@@ -1,22 +1,24 @@
1
1
  {
2
2
  "name": "hl7v2-net",
3
3
  "description": "HL7 v2 server/client for NodeJS",
4
- "version": "1.4.4",
4
+ "version": "1.6.0",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
8
  "@jsopen/objects": "^2.0.2",
9
- "iconv-lite": "^0.7.0",
9
+ "backoff": "^2.5.0",
10
+ "iconv-lite": "^0.7.1",
10
11
  "is-typedarray": "^1.0.0",
11
12
  "node-events-async": "^1.2.0",
13
+ "reconnect-core": "^1.3.0",
12
14
  "ts-gems": "^3.11.3",
13
15
  "tslib": "^2.8.1",
14
- "valgen": "^5.17.1",
16
+ "valgen": "^5.18.2",
15
17
  "uid": "^2.0.2"
16
18
  },
17
19
  "peerDependencies": {
18
- "hl7v2": "^1.4.4",
19
- "hl7v2-dictionary": "^1.4.4"
20
+ "hl7v2": "^1.6.0",
21
+ "hl7v2-dictionary": "^1.6.0"
20
22
  },
21
23
  "type": "module",
22
24
  "exports": {
@@ -1,13 +1,15 @@
1
1
  import net from 'node:net';
2
2
  import tls from 'node:tls';
3
+ import { Backoff } from 'backoff';
3
4
  import { HL7Message } from 'hl7v2';
4
- import { AddressInfo } from 'net';
5
5
  import { AsyncEventEmitter } from 'node-events-async';
6
+ import reconnectCore from 'reconnect-core';
6
7
  import { StrictOmit } from 'ts-gems';
7
8
  import { HL7Router } from './hl7-router.js';
8
9
  import { HL7Socket } from './hl7-socket.js';
9
10
  import { HL7Middleware } from './types.js';
10
11
  export declare class Hl7Client extends AsyncEventEmitter<Hl7Client.Events> {
12
+ protected _connectionManager: ReturnType<typeof socketReconnect>;
11
13
  protected _router: HL7Router;
12
14
  protected _socket?: HL7Socket;
13
15
  protected _tls?: boolean;
@@ -32,7 +34,7 @@ export declare class Hl7Client extends AsyncEventEmitter<Hl7Client.Events> {
32
34
  get closed(): boolean;
33
35
  get readyState(): net.SocketReadyState;
34
36
  get uri(): string;
35
- address(): AddressInfo;
37
+ address(): net.AddressInfo;
36
38
  get connectTimeout(): number | undefined;
37
39
  set connectTimeout(value: number | null);
38
40
  get responseTimeout(): number | undefined;
@@ -48,6 +50,10 @@ export declare class Hl7Client extends AsyncEventEmitter<Hl7Client.Events> {
48
50
  protected _onMessage(message: HL7Message): void;
49
51
  }
50
52
  export declare namespace Hl7Client {
53
+ export interface Events extends HL7Socket.Events {
54
+ reconnecting: [n: number, delay: number];
55
+ reconnect: [socket: net.Socket];
56
+ }
51
57
  interface CommonConnectOptions {
52
58
  connectTimeout?: number;
53
59
  maxBufferSize?: number;
@@ -55,10 +61,22 @@ export declare namespace Hl7Client {
55
61
  keepAlive?: boolean;
56
62
  keepAliveInitialDelay?: number;
57
63
  parseStrict?: boolean;
64
+ reconnect?: ReconnectOptions;
58
65
  }
59
66
  export type NetConnectOptions = StrictOmit<net.TcpNetConnectOpts, 'onread' | 'readable' | 'writable'> & CommonConnectOptions;
60
67
  export type TlsConnectOptions = StrictOmit<tls.ConnectionOptions, 'socket'> & CommonConnectOptions;
61
- export interface Events extends HL7Socket.Events {
68
+ export interface ReconnectOptions {
69
+ strategy?: 'fibonacci' | 'exponential' | Backoff;
70
+ immediate?: boolean | undefined;
71
+ failAfter?: number | undefined;
72
+ randomisationFactor?: number | undefined;
73
+ initialDelay?: number | undefined;
74
+ maxDelay?: number | undefined;
62
75
  }
63
76
  export {};
64
77
  }
78
+ declare const socketReconnect: reconnectCore.CustomModule<{
79
+ tls?: boolean;
80
+ options: Hl7Client.NetConnectOptions | Hl7Client.TlsConnectOptions;
81
+ }, net.Socket>;
82
+ export {};
@@ -108,7 +108,8 @@ export declare namespace HL7Server {
108
108
  error: [error: Error, HL7Socket | undefined];
109
109
  message: [message: HL7Message, socket: HL7Socket];
110
110
  send: [message: HL7Message, socket: HL7Socket];
111
- data: [Buffer];
111
+ data: [Buffer, socket: HL7Socket];
112
+ write: [buffer: Buffer, socket: HL7Socket];
112
113
  }
113
114
  interface Options {
114
115
  applicationName?: string;
@@ -4,7 +4,7 @@ import { Socket } from 'net';
4
4
  import { AsyncEventEmitter } from 'node-events-async';
5
5
  import { FrameStream } from './helpers/frame-stream.js';
6
6
  export declare class HL7Socket extends AsyncEventEmitter<HL7Socket.Events> {
7
- readonly socket: Socket;
7
+ protected _socket: Socket;
8
8
  protected _messageHooks: Set<(resp: HL7Message) => boolean>;
9
9
  protected _frameStream: FrameStream;
10
10
  protected _waitPromises: Set<Promise<any>>;
@@ -16,12 +16,13 @@ export declare class HL7Socket extends AsyncEventEmitter<HL7Socket.Events> {
16
16
  */
17
17
  protected user?: any;
18
18
  constructor(socket: Socket, options: HL7Socket.Options);
19
+ get socket(): Socket;
19
20
  get connected(): boolean;
20
21
  get closed(): boolean;
21
- get readyState(): import("net").SocketReadyState;
22
+ get readyState(): import("node:net").SocketReadyState;
22
23
  get maxBufferSize(): number;
23
24
  set maxBufferSize(value: number);
24
- address(): {} | import("net").AddressInfo;
25
+ address(): {};
25
26
  remoteAddress(): string | undefined;
26
27
  get writable(): boolean;
27
28
  close(waitRunningHandlers?: number): Promise<void>;
@@ -29,6 +30,8 @@ export declare class HL7Socket extends AsyncEventEmitter<HL7Socket.Events> {
29
30
  sendMessageWaitAck(message: HL7Message): Promise<HL7Message>;
30
31
  setKeepAlive(enable?: boolean, initialDelay?: number): void;
31
32
  protected _onData(data: Buffer): void;
33
+ _bindSocket(socket: Socket): void;
34
+ protected _unBindSocket(): void;
32
35
  }
33
36
  export declare namespace HL7Socket {
34
37
  interface Events {
@@ -37,14 +40,15 @@ export declare namespace HL7Socket {
37
40
  close: [];
38
41
  error: [error: Error];
39
42
  lookup: [
40
- err: Error,
43
+ err: Error | null,
41
44
  address: string,
42
- family: string | number,
45
+ family: number | null,
43
46
  host: string
44
47
  ];
45
48
  message: [message: HL7Message];
46
49
  send: [message: HL7Message];
47
- data: [Buffer];
50
+ data: [buffer: Buffer];
51
+ write: [buffer: Buffer];
48
52
  }
49
53
  interface Options {
50
54
  responseTimeout?: number;