hl7v2-net 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/cjs/constants.js +7 -0
- package/cjs/helpers/frame-stream.js +137 -0
- package/cjs/helpers/hl7-exchange-error.js +14 -0
- package/cjs/hl7-client.js +112 -0
- package/cjs/hl7-request.js +10 -0
- package/cjs/hl7-response.js +28 -0
- package/cjs/hl7-router.js +104 -0
- package/cjs/hl7-server.js +241 -0
- package/cjs/hl7-socket.js +154 -0
- package/cjs/index.js +11 -0
- package/cjs/package.json +3 -0
- package/cjs/types.js +2 -0
- package/esm/constants.js +4 -0
- package/esm/helpers/frame-stream.js +133 -0
- package/esm/helpers/hl7-exchange-error.js +10 -0
- package/esm/hl7-client.js +107 -0
- package/esm/hl7-request.js +6 -0
- package/esm/hl7-response.js +24 -0
- package/esm/hl7-router.js +100 -0
- package/esm/hl7-server.js +236 -0
- package/esm/hl7-socket.js +149 -0
- package/esm/index.js +8 -0
- package/esm/package.json +3 -0
- package/esm/types.js +1 -0
- package/package.json +66 -0
- package/types/constants.d.ts +4 -0
- package/types/helpers/frame-stream.d.ts +34 -0
- package/types/helpers/hl7-exchange-error.d.ts +12 -0
- package/types/hl7-client.d.ts +37 -0
- package/types/hl7-request.d.ts +7 -0
- package/types/hl7-response.d.ts +13 -0
- package/types/hl7-router.d.ts +12 -0
- package/types/hl7-server.d.ts +115 -0
- package/types/hl7-socket.d.ts +47 -0
- package/types/index.d.cts +8 -0
- package/types/index.d.ts +8 -0
- package/types/types.d.ts +5 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HL7Socket = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const hl7v2_1 = require("hl7v2");
|
|
6
|
+
const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
|
|
7
|
+
const node_events_async_1 = require("node-events-async");
|
|
8
|
+
const frame_stream_js_1 = require("./helpers/frame-stream.js");
|
|
9
|
+
const hl7_exchange_error_js_1 = require("./helpers/hl7-exchange-error.js");
|
|
10
|
+
class HL7Socket extends node_events_async_1.AsyncEventEmitter {
|
|
11
|
+
constructor(socket, options) {
|
|
12
|
+
super();
|
|
13
|
+
this._waitPromises = new Set();
|
|
14
|
+
this.socket = socket;
|
|
15
|
+
this._options = options;
|
|
16
|
+
const frameStream = new frame_stream_js_1.FrameStream({
|
|
17
|
+
frameStart: hl7v2_1.VT,
|
|
18
|
+
frameEnd: hl7v2_1.FS + hl7v2_1.CR,
|
|
19
|
+
maxBufferSize: options?.maxBufferSize,
|
|
20
|
+
});
|
|
21
|
+
this._frameStream = frameStream;
|
|
22
|
+
socket.on('error', err => this.emit('error', err));
|
|
23
|
+
socket.pipe(frameStream);
|
|
24
|
+
socket.on('connect', () => this.emit('connect'));
|
|
25
|
+
socket.on('ready', () => this.emit('ready'));
|
|
26
|
+
socket.on('lookup', listener => this.emit('lookup', listener));
|
|
27
|
+
socket.on('timeout', () => socket.destroy());
|
|
28
|
+
socket.on('close', () => {
|
|
29
|
+
this.emit('close');
|
|
30
|
+
});
|
|
31
|
+
frameStream.on('data', data => this._parseMessage(data));
|
|
32
|
+
}
|
|
33
|
+
get connected() {
|
|
34
|
+
return !this.socket.closed;
|
|
35
|
+
}
|
|
36
|
+
get closed() {
|
|
37
|
+
return this.socket.closed;
|
|
38
|
+
}
|
|
39
|
+
get readyState() {
|
|
40
|
+
return this.socket.readyState;
|
|
41
|
+
}
|
|
42
|
+
get maxBufferSize() {
|
|
43
|
+
return this._frameStream.maxBufferSize || 0;
|
|
44
|
+
}
|
|
45
|
+
set maxBufferSize(value) {
|
|
46
|
+
this._frameStream.maxBufferSize = value;
|
|
47
|
+
}
|
|
48
|
+
address() {
|
|
49
|
+
return this.socket.address();
|
|
50
|
+
}
|
|
51
|
+
async close(waitRunningHandlers) {
|
|
52
|
+
if (this.closed)
|
|
53
|
+
return;
|
|
54
|
+
/** Stop receiving data */
|
|
55
|
+
this.socket.unpipe(this._frameStream);
|
|
56
|
+
/** Wait for running handlers to finish */
|
|
57
|
+
if (waitRunningHandlers && this._waitPromises.size > 0) {
|
|
58
|
+
await new Promise(resolve => {
|
|
59
|
+
/** Timeout timer will resolve this promise to stop waiting */
|
|
60
|
+
const timer = setTimeout(() => {
|
|
61
|
+
resolve();
|
|
62
|
+
}, waitRunningHandlers).unref();
|
|
63
|
+
Promise.allSettled(Array.from(this._waitPromises)).then(() => {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
resolve();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return new Promise(resolve => {
|
|
70
|
+
this.socket.once('close', () => resolve());
|
|
71
|
+
this.socket.destroy();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
sendMessage(message) {
|
|
75
|
+
if (!this.connected)
|
|
76
|
+
throw new Error('Socket is not connected');
|
|
77
|
+
let encoding = message.header.field(hl7v2_1.MSHSegment.CharacterSet).value;
|
|
78
|
+
if (!encoding) {
|
|
79
|
+
encoding = 'UTF-8';
|
|
80
|
+
message.header.field(hl7v2_1.MSHSegment.CharacterSet).value = encoding;
|
|
81
|
+
}
|
|
82
|
+
const str = message.toHL7String();
|
|
83
|
+
const buf = iconv_lite_1.default.encode(str, encoding);
|
|
84
|
+
this.socket.write(hl7v2_1.VT);
|
|
85
|
+
this.socket.write(buf);
|
|
86
|
+
this.socket.end(hl7v2_1.FS + hl7v2_1.CR);
|
|
87
|
+
}
|
|
88
|
+
async sendMessageWaitAck(message) {
|
|
89
|
+
this.sendMessage(message);
|
|
90
|
+
const responseTimeout = this.responseTimeout;
|
|
91
|
+
const waitPromise = new Promise((resolve, reject) => {
|
|
92
|
+
let responseTimer;
|
|
93
|
+
const onMessage = (resp) => {
|
|
94
|
+
const msgType2 = resp.header.field(hl7v2_1.MSHSegment.MessageType).value;
|
|
95
|
+
if (msgType2 !== 'ACK')
|
|
96
|
+
return;
|
|
97
|
+
const msa = resp.getSegment('MSA');
|
|
98
|
+
if (!msa) {
|
|
99
|
+
const err = new hl7_exchange_error_js_1.HL7ExchangeError(`Invalid message returned from server. MSA segment not found.`, {
|
|
100
|
+
response: message,
|
|
101
|
+
request: resp,
|
|
102
|
+
segmentType: 'MSA',
|
|
103
|
+
hl7ErrorCode: 100,
|
|
104
|
+
});
|
|
105
|
+
onError(err);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const controlId2 = msa.field(hl7v2_1.MSASegment.MessageControlID).value;
|
|
109
|
+
if (controlId2 !== controlId)
|
|
110
|
+
return;
|
|
111
|
+
cleanup();
|
|
112
|
+
resolve(resp);
|
|
113
|
+
};
|
|
114
|
+
const onError = (error) => {
|
|
115
|
+
cleanup();
|
|
116
|
+
reject(error);
|
|
117
|
+
};
|
|
118
|
+
const cleanup = () => {
|
|
119
|
+
clearTimeout(responseTimer);
|
|
120
|
+
this.removeListener('message', onMessage);
|
|
121
|
+
this.socket.removeListener('error', onError);
|
|
122
|
+
};
|
|
123
|
+
const controlId = message.header.field(hl7v2_1.MSHSegment.MessageControlID).value;
|
|
124
|
+
this.socket.once('error', reject);
|
|
125
|
+
this.on('message', onMessage);
|
|
126
|
+
if (responseTimeout) {
|
|
127
|
+
responseTimer = setTimeout(() => {
|
|
128
|
+
onError(new Error('Response timeout'));
|
|
129
|
+
}, responseTimeout).unref();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
this._waitPromises.add(waitPromise);
|
|
133
|
+
waitPromise.finally(() => {
|
|
134
|
+
this._waitPromises.delete(waitPromise);
|
|
135
|
+
});
|
|
136
|
+
return waitPromise;
|
|
137
|
+
}
|
|
138
|
+
setKeepAlive(enable, initialDelay) {
|
|
139
|
+
this._options.keepAlive = enable;
|
|
140
|
+
this._options.keepAliveInitialDelay = initialDelay;
|
|
141
|
+
this.socket.setKeepAlive(enable, initialDelay);
|
|
142
|
+
}
|
|
143
|
+
_parseMessage(data) {
|
|
144
|
+
try {
|
|
145
|
+
const message = new hl7v2_1.HL7Message();
|
|
146
|
+
message.parse(data);
|
|
147
|
+
this.emit('message', message);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
this.emit('error', err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.HL7Socket = HL7Socket;
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./helpers/hl7-exchange-error.js"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./hl7-client.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./hl7-request.js"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./hl7-response.js"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./hl7-router.js"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./hl7-server.js"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./hl7-socket.js"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./types.js"), exports);
|
package/cjs/package.json
ADDED
package/cjs/types.js
ADDED
package/esm/constants.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { Transform } from 'node:stream';
|
|
3
|
+
export class FrameStream extends Transform {
|
|
4
|
+
constructor(opts) {
|
|
5
|
+
super({
|
|
6
|
+
...opts,
|
|
7
|
+
objectMode: true,
|
|
8
|
+
transform: undefined,
|
|
9
|
+
});
|
|
10
|
+
this._chunks = [];
|
|
11
|
+
this._bufferSize = 0;
|
|
12
|
+
this._customTransform = opts?.transform;
|
|
13
|
+
this._frameDelayMs = opts?.frameDelayMs || (opts?.frameEnd ? 0 : 500);
|
|
14
|
+
this.maxBufferSize = opts?.maxBufferSize;
|
|
15
|
+
if (opts?.frameStart) {
|
|
16
|
+
this._frameStart = Buffer.isBuffer(opts.frameStart)
|
|
17
|
+
? opts.frameStart
|
|
18
|
+
: Buffer.from(opts.frameStart);
|
|
19
|
+
}
|
|
20
|
+
if (opts?.frameEnd) {
|
|
21
|
+
this._frameEnd = Buffer.isBuffer(opts.frameEnd)
|
|
22
|
+
? opts.frameEnd
|
|
23
|
+
: Buffer.from(opts.frameEnd);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
_transform(chunk, _encoding, callback) {
|
|
27
|
+
// Reset flush timer
|
|
28
|
+
if (this._flushTimeout) {
|
|
29
|
+
clearTimeout(this._flushTimeout);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
// console.log(chunk);
|
|
33
|
+
if (!Buffer.isBuffer(chunk))
|
|
34
|
+
chunk = Buffer.from(chunk);
|
|
35
|
+
if (this._chunkBuffer) {
|
|
36
|
+
this._assertMaxBuffer(chunk.length);
|
|
37
|
+
chunk = Buffer.concat([this._chunkBuffer, chunk]);
|
|
38
|
+
this._chunkBuffer = undefined;
|
|
39
|
+
}
|
|
40
|
+
const frameStart = this._frameStart;
|
|
41
|
+
const frameEnd = this._frameEnd;
|
|
42
|
+
if (frameStart || frameEnd) {
|
|
43
|
+
const stxLen = frameStart?.length || 0;
|
|
44
|
+
const etxLen = frameEnd?.length || 0;
|
|
45
|
+
let stxPos = 0;
|
|
46
|
+
let etxPos = 0;
|
|
47
|
+
while (etxPos < chunk.length) {
|
|
48
|
+
/** Find the next stx position */
|
|
49
|
+
stxPos = etxPos;
|
|
50
|
+
if (frameStart) {
|
|
51
|
+
stxPos = chunk.indexOf(frameStart, etxPos);
|
|
52
|
+
if (stxPos < 0) {
|
|
53
|
+
this._chunkBuffer = chunk.subarray(etxPos);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (stxPos < 0)
|
|
58
|
+
stxPos = 0;
|
|
59
|
+
etxPos = frameEnd
|
|
60
|
+
? /** Find the next etx position if frameEnd defined */
|
|
61
|
+
chunk.indexOf(frameEnd, stxPos + stxLen)
|
|
62
|
+
: frameStart
|
|
63
|
+
? /** Find the next stx if frameStart not defined */
|
|
64
|
+
chunk.indexOf(frameStart, stxPos + stxLen)
|
|
65
|
+
: -1;
|
|
66
|
+
if (etxPos < 0) {
|
|
67
|
+
this._chunkBuffer = chunk.subarray(stxPos);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
etxPos += etxLen;
|
|
71
|
+
this._pushChunk(chunk.subarray(stxPos, etxPos));
|
|
72
|
+
this.flushBuffer();
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this._pushChunk(chunk);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (this._chunks.length && this._frameDelayMs)
|
|
80
|
+
this._flushTimeout = setTimeout(() => this.flushBuffer(), this._frameDelayMs).unref();
|
|
81
|
+
callback();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
_pushChunk(chunk) {
|
|
85
|
+
this._assertMaxBuffer(chunk.length);
|
|
86
|
+
this._chunks.push(chunk);
|
|
87
|
+
this._bufferSize += chunk.length;
|
|
88
|
+
}
|
|
89
|
+
_assertMaxBuffer(newChunkLen = 0) {
|
|
90
|
+
if (this.maxBufferSize &&
|
|
91
|
+
this._bufferSize + newChunkLen + (this._chunkBuffer?.length || 0) >
|
|
92
|
+
this.maxBufferSize) {
|
|
93
|
+
this.emit('error', new Error('Max buffer size exceeded'));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
_flush(callback) {
|
|
97
|
+
if (this._flushTimeout) {
|
|
98
|
+
clearTimeout(this._flushTimeout);
|
|
99
|
+
}
|
|
100
|
+
this.flushBuffer();
|
|
101
|
+
callback();
|
|
102
|
+
}
|
|
103
|
+
reset() {
|
|
104
|
+
this._chunks = [];
|
|
105
|
+
this._chunkBuffer = undefined;
|
|
106
|
+
if (this._flushTimeout) {
|
|
107
|
+
clearTimeout(this._flushTimeout);
|
|
108
|
+
this._flushTimeout = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
flushBuffer() {
|
|
112
|
+
if (this._chunkBuffer) {
|
|
113
|
+
this._chunks.push(this._chunkBuffer);
|
|
114
|
+
this._chunkBuffer = undefined;
|
|
115
|
+
}
|
|
116
|
+
/* c8 ignore next */
|
|
117
|
+
if (this._chunks.length === 0)
|
|
118
|
+
return;
|
|
119
|
+
const combined = this._chunks.length === 1 ? this._chunks[0] : Buffer.concat(this._chunks);
|
|
120
|
+
this._chunks = [];
|
|
121
|
+
this._bufferSize = 0;
|
|
122
|
+
if (this._customTransform) {
|
|
123
|
+
this._customTransform(combined, 'buffer', (err, data) => {
|
|
124
|
+
if (err)
|
|
125
|
+
this.emit('error', err);
|
|
126
|
+
else
|
|
127
|
+
this.push(data);
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.push(combined);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { AsyncEventEmitter } from 'node-events-async';
|
|
3
|
+
import { HL7Request } from './hl7-request.js';
|
|
4
|
+
import { HL7Response } from './hl7-response.js';
|
|
5
|
+
import { HL7Router } from './hl7-router.js';
|
|
6
|
+
import { HL7Socket } from './hl7-socket.js';
|
|
7
|
+
export class Hl7Client extends AsyncEventEmitter {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super();
|
|
10
|
+
this._router = new HL7Router();
|
|
11
|
+
this._options = options;
|
|
12
|
+
}
|
|
13
|
+
get connected() {
|
|
14
|
+
return this._socket?.connected ?? false;
|
|
15
|
+
}
|
|
16
|
+
get readyState() {
|
|
17
|
+
return this._socket?.readyState || 'closed';
|
|
18
|
+
}
|
|
19
|
+
get connectTimeout() {
|
|
20
|
+
return this._options.connectTimeout;
|
|
21
|
+
}
|
|
22
|
+
set connectTimeout(value) {
|
|
23
|
+
this._options.connectTimeout = value ?? undefined;
|
|
24
|
+
}
|
|
25
|
+
get responseTimeout() {
|
|
26
|
+
return this._options.responseTimeout;
|
|
27
|
+
}
|
|
28
|
+
set responseTimeout(value) {
|
|
29
|
+
this._options.responseTimeout = value ?? undefined;
|
|
30
|
+
}
|
|
31
|
+
get maxBufferSize() {
|
|
32
|
+
return this._options.maxBufferSize || 0;
|
|
33
|
+
}
|
|
34
|
+
set maxBufferSize(value) {
|
|
35
|
+
this._options.maxBufferSize = value;
|
|
36
|
+
if (this._socket)
|
|
37
|
+
this._socket.maxBufferSize = value;
|
|
38
|
+
}
|
|
39
|
+
connect() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
if (this.connected) {
|
|
42
|
+
resolve();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let timeoutTimer;
|
|
46
|
+
const tcpSocket = net.connect(this._options);
|
|
47
|
+
const socket = (this._socket = new HL7Socket(tcpSocket, this._options));
|
|
48
|
+
socket.on('connect', () => this.emit('connect'));
|
|
49
|
+
socket.on('ready', () => this.emit('ready'));
|
|
50
|
+
socket.on('lookup', listener => this.emit('lookup', listener));
|
|
51
|
+
socket.on('close', () => {
|
|
52
|
+
this._socket = undefined;
|
|
53
|
+
this.emit('close');
|
|
54
|
+
});
|
|
55
|
+
socket.on('error', err => this.emit('error', err));
|
|
56
|
+
socket.on('message', message => this._onMessage(message));
|
|
57
|
+
const onReady = () => {
|
|
58
|
+
clearTimeout(timeoutTimer);
|
|
59
|
+
tcpSocket.removeListener('error', onError);
|
|
60
|
+
resolve();
|
|
61
|
+
};
|
|
62
|
+
const onError = (error) => {
|
|
63
|
+
clearTimeout(timeoutTimer);
|
|
64
|
+
tcpSocket.removeListener('ready', onReady);
|
|
65
|
+
tcpSocket.destroy();
|
|
66
|
+
reject(error);
|
|
67
|
+
};
|
|
68
|
+
tcpSocket.once('ready', onReady);
|
|
69
|
+
tcpSocket.once('error', onError);
|
|
70
|
+
if (this.connectTimeout) {
|
|
71
|
+
timeoutTimer = setTimeout(() => {
|
|
72
|
+
this.emit('error', new Error('Connection timeout'));
|
|
73
|
+
tcpSocket.destroy();
|
|
74
|
+
}, this._options.connectTimeout).unref();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async close(waitRunningHandlers) {
|
|
79
|
+
await this._socket?.close(waitRunningHandlers);
|
|
80
|
+
}
|
|
81
|
+
async sendMessage(message) {
|
|
82
|
+
if (!this.connected)
|
|
83
|
+
await this.connect();
|
|
84
|
+
this._socket.sendMessage(message);
|
|
85
|
+
}
|
|
86
|
+
async sendMessageWaitAck(request) {
|
|
87
|
+
if (!this.connected)
|
|
88
|
+
await this.connect();
|
|
89
|
+
return this._socket.sendMessageWaitAck(request);
|
|
90
|
+
}
|
|
91
|
+
setKeepAlive(enable, initialDelay) {
|
|
92
|
+
this._options.keepAlive = enable;
|
|
93
|
+
this._options.keepAliveInitialDelay = initialDelay;
|
|
94
|
+
this._socket?.setKeepAlive(enable, initialDelay);
|
|
95
|
+
}
|
|
96
|
+
use(handler, priority = 0) {
|
|
97
|
+
this._router.use(handler, priority);
|
|
98
|
+
}
|
|
99
|
+
_onMessage(message) {
|
|
100
|
+
const req = new HL7Request(this._socket, message);
|
|
101
|
+
const res = new HL7Response(req);
|
|
102
|
+
this._router.handle(undefined, req, res, error => {
|
|
103
|
+
if (error)
|
|
104
|
+
this.emit('error', error);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AsyncEventEmitter } from 'node-events-async';
|
|
2
|
+
export class HL7Response extends AsyncEventEmitter {
|
|
3
|
+
constructor(req) {
|
|
4
|
+
super();
|
|
5
|
+
this._finished = false;
|
|
6
|
+
this._req = req;
|
|
7
|
+
}
|
|
8
|
+
get socket() {
|
|
9
|
+
return this._req.socket;
|
|
10
|
+
}
|
|
11
|
+
get request() {
|
|
12
|
+
return this._req;
|
|
13
|
+
}
|
|
14
|
+
get finished() {
|
|
15
|
+
return this._finished || !this.socket.connected;
|
|
16
|
+
}
|
|
17
|
+
send(message) {
|
|
18
|
+
if (this.finished || !this.socket.connected)
|
|
19
|
+
return;
|
|
20
|
+
this.socket.sendMessage(message);
|
|
21
|
+
this._finished = true;
|
|
22
|
+
this.emit('finish', message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export class HL7Router {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._allHandlers = {};
|
|
4
|
+
this._handlers = [];
|
|
5
|
+
this._errorHandlers = [];
|
|
6
|
+
}
|
|
7
|
+
use(handler, priority = 0) {
|
|
8
|
+
let list = this._allHandlers[priority];
|
|
9
|
+
if (!list) {
|
|
10
|
+
list = [];
|
|
11
|
+
this._allHandlers[priority] = list;
|
|
12
|
+
}
|
|
13
|
+
if (typeof handler === 'function')
|
|
14
|
+
list.push(handler);
|
|
15
|
+
else {
|
|
16
|
+
// noinspection SuspiciousTypeOfGuard
|
|
17
|
+
if (handler instanceof HL7Router) {
|
|
18
|
+
list.push((req, res, next) => {
|
|
19
|
+
handler.handle(undefined, req, res, error => {
|
|
20
|
+
if (!res.finished)
|
|
21
|
+
next(error);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
list.push((err, req, res, next) => {
|
|
25
|
+
handler.handle(err, req, res, error => {
|
|
26
|
+
if (!res.finished)
|
|
27
|
+
next(error);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
} /* c8 ignore else */
|
|
31
|
+
else {
|
|
32
|
+
throw new TypeError('Router handler must be a function or HL7Router');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this._needPrepare = true;
|
|
36
|
+
}
|
|
37
|
+
handle(error, req, res, callback) {
|
|
38
|
+
this._prepareStack();
|
|
39
|
+
let errIdx = -1;
|
|
40
|
+
let handlerIdx = -1;
|
|
41
|
+
let lastErr;
|
|
42
|
+
let callbackCalled = false;
|
|
43
|
+
const doCallback = (err) => {
|
|
44
|
+
if (callbackCalled)
|
|
45
|
+
return;
|
|
46
|
+
callbackCalled = true;
|
|
47
|
+
res.removeListener('finish', onFinish);
|
|
48
|
+
callback(err);
|
|
49
|
+
};
|
|
50
|
+
const onFinish = () => doCallback();
|
|
51
|
+
res.once('finish', onFinish);
|
|
52
|
+
const next = (err) => {
|
|
53
|
+
lastErr = err || lastErr;
|
|
54
|
+
if (res.finished) {
|
|
55
|
+
doCallback();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
if (err) {
|
|
60
|
+
errIdx++;
|
|
61
|
+
const handler = this._errorHandlers[errIdx];
|
|
62
|
+
if (!handler) {
|
|
63
|
+
doCallback(lastErr);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
handler(err, req, res, next);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
handlerIdx++;
|
|
71
|
+
const handler = this._handlers[handlerIdx];
|
|
72
|
+
if (!handler) {
|
|
73
|
+
doCallback(lastErr);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
handler(req, res, next);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
next(e);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
next(error);
|
|
85
|
+
}
|
|
86
|
+
_prepareStack() {
|
|
87
|
+
if (!this._needPrepare)
|
|
88
|
+
return;
|
|
89
|
+
delete this._needPrepare;
|
|
90
|
+
this._errorHandlers = [];
|
|
91
|
+
this._handlers = [];
|
|
92
|
+
Object.keys(this._allHandlers).forEach(p => {
|
|
93
|
+
const h = this._allHandlers[p];
|
|
94
|
+
if (h.length === 4)
|
|
95
|
+
this._errorHandlers.push(...h);
|
|
96
|
+
else
|
|
97
|
+
this._handlers.push(...h);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|