@medplum/hl7 4.3.9 → 4.3.11

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.
@@ -1,2 +1,2 @@
1
- "use strict";var S=Object.create;var m=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var T=Object.getPrototypeOf,O=Object.prototype.hasOwnProperty;var j=(s,e)=>{for(var t in e)m(s,t,{get:e[t],enumerable:!0})},C=(s,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of A(e))!O.call(s,n)&&n!==t&&m(s,n,{get:()=>e[n],enumerable:!(o=D(e,n))||o.enumerable});return s};var f=(s,e,t)=>(t=s!=null?S(T(s)):{},C(e||!s||!s.__esModule?m(t,"default",{value:s,enumerable:!0}):t,s)),B=s=>C(m({},"__esModule",{value:!0}),s);var K={};j(K,{CR:()=>H,DEFAULT_ENCODING:()=>g,FS:()=>M,Hl7Base:()=>d,Hl7Client:()=>E,Hl7CloseEvent:()=>a,Hl7Connection:()=>h,Hl7ErrorEvent:()=>c,Hl7MessageEvent:()=>l,Hl7Server:()=>k,VT:()=>I});module.exports=B(K);var d=class extends EventTarget{addEventListener(e,t,o){super.addEventListener(e,t,o)}removeEventListener(e,t,o){super.removeEventListener(e,t,o)}};var p=f(require("node:assert")),L=require("node:net");var P=require("@medplum/core"),u=f(require("iconv-lite"));var I=11,H=13,M=28;var l=class extends Event{constructor(e,t){super("message"),this.connection=e,this.message=t}},c=class extends Event{constructor(e){super("error"),this.error=e}},a=class extends Event{constructor(){super("close")}};var g="utf-8",h=class extends d{constructor(t,o=g,n=!1){super();this.chunks=[];this.messageQueue=[];this.socket=t,this.encoding=o,this.enhancedMode=n,t.on("data",r=>{try{if(this.appendData(r),r.at(-2)===28&&r.at(-1)===13){let i=Buffer.concat(this.chunks),v=i.subarray(1,i.length-2),b=u.default.decode(v,this.encoding),y=P.Hl7Message.parse(b);this.dispatchEvent(new l(this,y)),this.resetBuffer()}}catch(i){this.dispatchEvent(new c(i))}}),t.on("error",r=>{this.resetBuffer(),this.dispatchEvent(new c(r))}),t.on("close",()=>{this.dispatchEvent(new a)}),this.addEventListener("message",r=>{this.enhancedMode&&this.send(r.message.buildAck({ackCode:"CA"}));let i=this.messageQueue.shift();if(!i){this.dispatchEvent(new c(new Error(`Received a message when no pending messages were in the queue. Message: ${r.message}`)));return}i.resolve?.(r.message)})}isClosed(){return this.socket.closed}sendImpl(t,o){this.messageQueue.push(o);let n=t.toString(),r=u.default.encode(n,this.encoding),i=Buffer.alloc(r.length+3);i.writeInt8(11,0),r.copy(i,1),i.writeInt8(28,r.length+1),i.writeInt8(13,r.length+2),this.socket.write(i)}send(t){this.sendImpl(t,{message:t})}async sendAndWait(t){return new Promise((o,n)=>{let r={message:t,resolve:o,reject:n};this.sendImpl(t,r)})}async close(){return this.isClosed()?Promise.resolve():new Promise(t=>{this.socket.once("close",t),this.socket.end(),this.socket.destroy()})}appendData(t){this.chunks.push(t)}resetBuffer(){this.chunks=[]}setEncoding(t){this.encoding=t??g}getEncoding(){return this.encoding}setEnhancedMode(t){this.enhancedMode=t}getEnhancedMode(){return this.enhancedMode}};var E=class extends d{constructor(e){super(),this.options=e,this.host=this.options.host,this.port=this.options.port,this.encoding=this.options.encoding,this.keepAlive=this.options.keepAlive??!1,this.connectTimeout=this.options.connectTimeout??3e4}connect(){if(this.deferredConnectionPromise)return this.deferredConnectionPromise.promise;let e=this.deferredConnectionPromise=this.createDeferredConnectionPromise();return this.socket=(0,L.connect)({host:this.host,port:this.port,keepAlive:this.keepAlive}),this.connectTimeout>0&&(this.socket.setTimeout(this.connectTimeout),this.registerSocketTimeoutListener(e)),this.registerSocketConnectListener(e),this.registerSocketErrorListener(e),this.registerSocketCloseListener(e),e.promise}registerSocketTimeoutListener(e){(0,p.default)(this.socket);let t=this.socket,o=()=>{this.cleanupSocket(t);let n=new Error(`Connection timeout after ${this.connectTimeout}ms`);this.rejectDeferredPromise(e,n)};t.on("timeout",o)}registerSocketConnectListener(e){(0,p.default)(this.socket);let t=this.socket,o=()=>{if(t!==this.socket){this.cleanupSocket(t);return}let n;this.connection=n=new h(t,this.encoding),t.setTimeout(0),this.registerHl7ConnectionListeners(n),e.resolve(n)};t.on("connect",o)}registerSocketErrorListener(e){(0,p.default)(this.socket);let t=this.socket,o=n=>{this.cleanupSocket(t),n.constructor.name==="AggregateError"?this.rejectDeferredPromise(e,n.errors[0]):this.rejectDeferredPromise(e,n)};t.on("error",o)}registerSocketCloseListener(e){(0,p.default)(this.socket);let t=this.socket,o=()=>{this.cleanupSocket(t),this.rejectDeferredPromise(e,new Error("Socket closed before connection finished"))};t.on("close",o)}registerHl7ConnectionListeners(e){e.addEventListener("close",()=>{this.socket=void 0,this.connection=void 0,this.deferredConnectionPromise=void 0,this.dispatchEvent(new a)}),e.addEventListener("error",t=>{this.dispatchEvent(new c(t.error))})}createDeferredConnectionPromise(){let e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}rejectDeferredPromise(e,t){e.reject(t),this.deferredConnectionPromise===e&&(this.deferredConnectionPromise=void 0)}cleanupSocket(e){e.destroyed||e.destroy(),e===this.socket&&(this.socket=void 0)}async send(e){return(await this.connect()).send(e)}async sendAndWait(e){return(await this.connect()).sendAndWait(e)}async close(){if(this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=void 0),this.deferredConnectionPromise&&this.rejectDeferredPromise(this.deferredConnectionPromise,new Error("Client closed while connecting")),this.connection){let e=this.connection;delete this.connection,await e.close()}}};var x=require("@medplum/core"),w=f(require("node:net"));var k=class{constructor(e){this.encoding=void 0;this.enhancedMode=!1;this.handler=e}start(e,t,o){t&&this.setEncoding(t),o!==void 0&&this.setEnhancedMode(o);let n=w.default.createServer(i=>{let v=new h(i,this.encoding,this.enhancedMode);this.handler(v)}),r=async i=>{i?.code==="EADDRINUSE"&&(await(0,x.sleep)(50),n.close(),n.listen(e))};n.on("error",r),n.once("listening",()=>{n.off("error",r)}),n.listen(e),this.server=n}async stop(){return new Promise((e,t)=>{if(!this.server){t(new Error("Stop was called but there is no server running"));return}this.server.close(o=>{if(o){t(o);return}e()}),this.server=void 0})}setEnhancedMode(e){this.enhancedMode=e}getEnhancedMode(){return this.enhancedMode}setEncoding(e){this.encoding=e}getEncoding(){return this.encoding}};0&&(module.exports={CR,DEFAULT_ENCODING,FS,Hl7Base,Hl7Client,Hl7CloseEvent,Hl7Connection,Hl7ErrorEvent,Hl7MessageEvent,Hl7Server,VT});
1
+ "use strict";var T=Object.create;var v=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var Q=(o,t)=>{for(var e in t)v(o,e,{get:t[e],enumerable:!0})},w=(o,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of I(t))!B.call(o,s)&&s!==e&&v(o,s,{get:()=>t[s],enumerable:!(n=D(t,s))||n.enumerable});return o};var M=(o,t,e)=>(e=o!=null?T(R(o)):{},w(t||!o||!o.__esModule?v(e,"default",{value:o,enumerable:!0}):e,o)),j=o=>w(v({},"__esModule",{value:!0}),o);var K={};Q(K,{CR:()=>y,DEFAULT_ENCODING:()=>k,FS:()=>S,Hl7Base:()=>h,Hl7Client:()=>P,Hl7CloseEvent:()=>p,Hl7Connection:()=>u,Hl7ErrorEvent:()=>a,Hl7MessageEvent:()=>g,Hl7Server:()=>H,ReturnAckCategory:()=>C,VT:()=>N});module.exports=j(K);var h=class extends EventTarget{addEventListener(t,e,n){super.addEventListener(t,e,n)}removeEventListener(t,e,n){super.removeEventListener(t,e,n)}};var m=M(require("node:assert")),A=require("node:net");var d=require("@medplum/core"),E=M(require("iconv-lite"));var N=11,y=13,S=28;var g=class extends Event{constructor(t,e){super("message"),this.connection=t,this.message=e}},a=class extends Event{constructor(t){super("error"),this.error=t}},p=class extends Event{constructor(){super("close")}};var C={FIRST:"first",APPLICATION:"application"},k="utf-8",F=60*1e3,u=class extends h{constructor(e,n=k,s=!1,i={}){super();this.messagesPerMin=void 0;this.chunks=[];this.pendingMessages=new Map;this.responseQueue=[];this.lastMessageDispatchedTime=0;this.responseQueueProcessing=!1;this.socket=e,this.encoding=n,this.enhancedMode=s,this.messagesPerMin=i.messagesPerMin,e.on("data",r=>{try{if(this.appendData(r),r.at(-2)===28&&r.at(-1)===13){let c=Buffer.concat(this.chunks),l=c.subarray(1,c.length-2),f=E.default.decode(l,this.encoding),L=d.Hl7Message.parse(f);this.responseQueue.push(new g(this,L)),this.resetBuffer(),this.processResponseQueue().catch(O=>{this.dispatchEvent(new a(O))})}}catch(c){this.dispatchEvent(new a(c))}}),e.on("error",r=>{this.resetBuffer(),this.dispatchEvent(new a(r))}),e.on("close",()=>{this.dispatchEvent(new p)}),this.addEventListener("message",r=>{this.enhancedMode&&this.send(r.message.buildAck({ackCode:"CA"}));let c=r.message.getSegment("MSA")?.getField(2)?.toString();if(!c)return;let l=this.pendingMessages.get(c);if(!l){this.dispatchEvent(new a(new d.OperationOutcomeError({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"not-found",details:{text:"Response received for unknown message control ID"},diagnostics:`Received ACK for message control ID '${c}' but there was no pending message with this control ID`}]})));return}let f=r.message.getSegment("MSA")?.getField(1)?.toString()?.toUpperCase();f&&(l.returnAck===C.APPLICATION&&f==="CA"||(l.resolve(r.message),this.pendingMessages.delete(c)))})}isClosed(){return this.socket.closed}sendImpl(e){let n=e.toString(),s=E.default.encode(n,this.encoding),i=Buffer.alloc(s.length+3);i.writeInt8(11,0),s.copy(i,1),i.writeInt8(28,s.length+1),i.writeInt8(13,s.length+2),this.socket.write(i)}async processResponseQueue(){if(!this.responseQueueProcessing){for(this.responseQueueProcessing=!0;this.responseQueue.length;){if(this.messagesPerMin){let n=F/this.messagesPerMin,s=Date.now()-this.lastMessageDispatchedTime;n>s&&await(0,d.sleep)(n-s)}let e=this.responseQueue.shift();e&&this.dispatchEvent(e),this.lastMessageDispatchedTime=Date.now()}this.responseQueueProcessing=!1}}send(e){this.sendImpl(e)}async sendAndWait(e,n){return new Promise((s,i)=>{let r=e.getSegment("MSH")?.getField(10)?.toString();if(!r){i(new d.OperationOutcomeError((0,d.validationError)("Required field missing: MSH.10")));return}let c;n?.timeoutMs&&(c=setTimeout(()=>{this.pendingMessages.delete(r),i(new d.OperationOutcomeError({resourceType:"OperationOutcome",issue:[{severity:"error",code:"timeout",details:{text:"Client timeout"},diagnostics:`Request timed out after waiting ${n.timeoutMs} milliseconds for response`}]}))},n.timeoutMs)),this.pendingMessages.set(r,{message:e,resolve:s,reject:i,returnAck:n?.returnAck??C.FIRST,timer:c}),this.sendImpl(e)})}async close(){if(this.isClosed())return Promise.resolve();if(this.socket.end(),this.socket.destroy(),this.pendingMessages.size){for(let e of this.pendingMessages.values())e.timer&&clearTimeout(e.timer),e.reject(new d.OperationOutcomeError({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"incomplete",details:{text:"Message was still pending when connection closed"}}]}));this.dispatchEvent(new a(new d.OperationOutcomeError({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"incomplete",details:{text:"Messages were still pending when connection closed"},diagnostics:`Hl7Connection closed while ${this.pendingMessages.size} messages were pending`}]}))),this.pendingMessages.clear()}return new Promise(e=>{this.socket.once("close",e)})}appendData(e){this.chunks.push(e)}resetBuffer(){this.chunks=[]}setEncoding(e){this.encoding=e??k}getEncoding(){return this.encoding}setEnhancedMode(e){this.enhancedMode=e}getEnhancedMode(){return this.enhancedMode}setMessagesPerMin(e){this.messagesPerMin=e}getMessagesPerMin(){return this.messagesPerMin}};var P=class extends h{constructor(t){super(),this.options=t,this.host=this.options.host,this.port=this.options.port,this.encoding=this.options.encoding,this.keepAlive=this.options.keepAlive??!1,this.connectTimeout=this.options.connectTimeout??3e4}connect(){if(this.deferredConnectionPromise)return this.deferredConnectionPromise.promise;let t=this.deferredConnectionPromise=this.createDeferredConnectionPromise();return this.socket=(0,A.connect)({host:this.host,port:this.port,keepAlive:this.keepAlive}),this.connectTimeout>0&&(this.socket.setTimeout(this.connectTimeout),this.registerSocketTimeoutListener(t)),this.registerSocketConnectListener(t),this.registerSocketErrorListener(t),this.registerSocketCloseListener(t),t.promise}registerSocketTimeoutListener(t){(0,m.default)(this.socket);let e=this.socket,n=()=>{this.cleanupSocket(e);let s=new Error(`Connection timeout after ${this.connectTimeout}ms`);this.rejectDeferredPromise(t,s)};e.on("timeout",n)}registerSocketConnectListener(t){(0,m.default)(this.socket);let e=this.socket,n=()=>{if(e!==this.socket){this.cleanupSocket(e);return}let s;this.connection=s=new u(e,this.encoding),e.setTimeout(0),this.registerHl7ConnectionListeners(s),t.resolve(s)};e.on("connect",n)}registerSocketErrorListener(t){(0,m.default)(this.socket);let e=this.socket,n=s=>{this.cleanupSocket(e),s.constructor.name==="AggregateError"?this.rejectDeferredPromise(t,s.errors[0]):this.rejectDeferredPromise(t,s)};e.on("error",n)}registerSocketCloseListener(t){(0,m.default)(this.socket);let e=this.socket,n=()=>{this.cleanupSocket(e),this.rejectDeferredPromise(t,new Error("Socket closed before connection finished"))};e.on("close",n)}registerHl7ConnectionListeners(t){t.addEventListener("close",()=>{this.socket=void 0,this.connection=void 0,this.deferredConnectionPromise=void 0,this.dispatchEvent(new p)}),t.addEventListener("error",e=>{this.dispatchEvent(new a(e.error))})}createDeferredConnectionPromise(){let t,e;return{promise:new Promise((s,i)=>{t=s,e=i}),resolve:t,reject:e}}rejectDeferredPromise(t,e){t.reject(e),this.deferredConnectionPromise===t&&(this.deferredConnectionPromise=void 0)}cleanupSocket(t){t.destroyed||t.destroy(),t===this.socket&&(this.socket=void 0)}async send(t){return(await this.connect()).send(t)}async sendAndWait(t,e){return(await this.connect()).sendAndWait(t,e)}async close(){if(this.deferredConnectionPromise&&this.rejectDeferredPromise(this.deferredConnectionPromise,new Error("Client closed while connecting")),this.connection){let t=this.connection;delete this.connection,await t.close()}this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=void 0)}};var x=require("@medplum/core"),b=M(require("node:net"));var H=class{constructor(t){this.encoding=void 0;this.enhancedMode=!1;this.messagesPerMin=void 0;this.handler=t}start(t,e,n,s){e&&this.setEncoding(e),n!==void 0&&this.setEnhancedMode(n),s?.messagesPerMin!==void 0&&this.setMessagesPerMin(s.messagesPerMin);let i=b.default.createServer(c=>{let l=new u(c,this.encoding,this.enhancedMode,{messagesPerMin:this.messagesPerMin});this.handler(l)}),r=async c=>{c?.code==="EADDRINUSE"&&(await(0,x.sleep)(50),i.close(),i.listen(t))};i.on("error",r),i.once("listening",()=>{i.off("error",r)}),i.listen(t),this.server=i}async stop(){return new Promise((t,e)=>{if(!this.server){e(new Error("Stop was called but there is no server running"));return}this.server.close(n=>{if(n){e(n);return}t()}),this.server=void 0})}setEnhancedMode(t){this.enhancedMode=t}getEnhancedMode(){return this.enhancedMode}setEncoding(t){this.encoding=t}getEncoding(){return this.encoding}setMessagesPerMin(t){this.messagesPerMin=t}getMessagesPerMin(){return this.messagesPerMin}};0&&(module.exports={CR,DEFAULT_ENCODING,FS,Hl7Base,Hl7Client,Hl7CloseEvent,Hl7Connection,Hl7ErrorEvent,Hl7MessageEvent,Hl7Server,ReturnAckCategory,VT});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts", "../../src/base.ts", "../../src/client.ts", "../../src/connection.ts", "../../src/constants.ts", "../../src/events.ts", "../../src/server.ts"],
4
- "sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nexport * from './base';\nexport * from './client';\nexport * from './connection';\nexport * from './constants';\nexport * from './events';\nexport * from './server';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\nexport interface Hl7EventMap {\n message: Hl7MessageEvent;\n error: Hl7ErrorEvent;\n close: Hl7CloseEvent;\n}\n\nexport abstract class Hl7Base extends EventTarget {\n addEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.addEventListener(type, listener, options);\n }\n removeEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.removeEventListener(type, listener, options);\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport assert from 'node:assert';\nimport { connect, Socket } from 'node:net';\nimport { Hl7Base } from './base';\nimport { Hl7Connection } from './connection';\nimport { Hl7CloseEvent, Hl7ErrorEvent } from './events';\n\nexport interface Hl7ClientOptions {\n host: string;\n port: number;\n encoding?: string;\n keepAlive?: boolean;\n connectTimeout?: number; // Add timeout option\n}\n\nexport interface DeferredConnectionPromise {\n promise: Promise<Hl7Connection>;\n resolve: (connection: Hl7Connection) => void;\n reject: (err: Error) => void;\n}\n\nexport class Hl7Client extends Hl7Base {\n options: Hl7ClientOptions;\n host: string;\n port: number;\n encoding?: string;\n connection?: Hl7Connection;\n keepAlive: boolean;\n private socket?: Socket;\n private connectTimeout: number;\n private deferredConnectionPromise?: DeferredConnectionPromise;\n\n constructor(options: Hl7ClientOptions) {\n super();\n this.options = options;\n this.host = this.options.host;\n this.port = this.options.port;\n this.encoding = this.options.encoding;\n this.keepAlive = this.options.keepAlive ?? false;\n this.connectTimeout = this.options.connectTimeout ?? 30000; // Default 30 seconds\n }\n\n connect(): Promise<Hl7Connection> {\n // If we are already waiting for a pending connection attempt, just return the deferred promise to that\n // In the case that the promise is already resolve, we will also return a resolved connection\n if (this.deferredConnectionPromise) {\n return this.deferredConnectionPromise.promise;\n }\n\n const deferredPromise = (this.deferredConnectionPromise = this.createDeferredConnectionPromise());\n\n // Create the socket\n this.socket = connect({\n host: this.host,\n port: this.port,\n keepAlive: this.keepAlive,\n });\n\n if (this.connectTimeout > 0) {\n this.socket.setTimeout(this.connectTimeout);\n this.registerSocketTimeoutListener(deferredPromise);\n }\n\n this.registerSocketConnectListener(deferredPromise);\n this.registerSocketErrorListener(deferredPromise);\n this.registerSocketCloseListener(deferredPromise);\n\n return deferredPromise.promise;\n }\n\n private registerSocketTimeoutListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle timeout event\n const timeoutListener = (): void => {\n this.cleanupSocket(socket);\n const error = new Error(`Connection timeout after ${this.connectTimeout}ms`);\n this.rejectDeferredPromise(deferredPromise, error);\n };\n\n socket.on('timeout', timeoutListener);\n }\n\n private registerSocketConnectListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle successful connection\n const connectListener = (): void => {\n if (socket !== this.socket) {\n this.cleanupSocket(socket);\n return;\n }\n\n // Create the HL7 connection\n let connection: Hl7Connection;\n this.connection = connection = new Hl7Connection(socket, this.encoding);\n\n // Remove the timeout listener as we're now connected\n socket.setTimeout(0);\n\n this.registerHl7ConnectionListeners(connection);\n\n deferredPromise.resolve(connection);\n };\n\n socket.on('connect', connectListener);\n }\n\n private registerSocketErrorListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const errorListener = (err: Error | AggregateError): void => {\n this.cleanupSocket(socket);\n\n if (err.constructor.name === 'AggregateError') {\n this.rejectDeferredPromise(deferredPromise, (err as AggregateError).errors[0]);\n } else {\n this.rejectDeferredPromise(deferredPromise, err);\n }\n };\n\n socket.on('error', errorListener);\n }\n\n private registerSocketCloseListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const closeListener = (): void => {\n this.cleanupSocket(socket);\n this.rejectDeferredPromise(deferredPromise, new Error('Socket closed before connection finished'));\n };\n socket.on('close', closeListener);\n }\n\n private registerHl7ConnectionListeners(connection: Hl7Connection): void {\n // Set up event handlers\n connection.addEventListener('close', () => {\n this.socket = undefined;\n this.connection = undefined;\n this.deferredConnectionPromise = undefined;\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n connection.addEventListener('error', (event) => {\n this.dispatchEvent(new Hl7ErrorEvent(event.error));\n });\n }\n\n private createDeferredConnectionPromise(): DeferredConnectionPromise {\n // Setup our deferred connection promise\n let resolve!: (connection: Hl7Connection) => void;\n let reject!: (err: Error) => void;\n\n const promise = new Promise<Hl7Connection>((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n\n return {\n promise,\n resolve,\n reject,\n };\n }\n\n private rejectDeferredPromise(deferredPromise: DeferredConnectionPromise, err: Error): void {\n // Reject this deferred promise with the given error\n deferredPromise.reject(err);\n\n // If the currently tracked deferred promise is this deferred promise, remove it from the client\n if (this.deferredConnectionPromise === deferredPromise) {\n this.deferredConnectionPromise = undefined;\n }\n }\n\n private cleanupSocket(socket: Socket): void {\n if (!socket.destroyed) {\n socket.destroy();\n }\n if (socket === this.socket) {\n this.socket = undefined;\n }\n }\n\n async send(msg: Hl7Message): Promise<void> {\n return (await this.connect()).send(msg);\n }\n\n async sendAndWait(msg: Hl7Message): Promise<Hl7Message> {\n return (await this.connect()).sendAndWait(msg);\n }\n\n async close(): Promise<void> {\n // Close the socket if it exists\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.destroy();\n this.socket = undefined;\n }\n\n if (this.deferredConnectionPromise) {\n this.rejectDeferredPromise(this.deferredConnectionPromise, new Error('Client closed while connecting'));\n }\n\n // Close established connection if it exists\n if (this.connection) {\n const connection = this.connection;\n delete this.connection;\n await connection.close();\n }\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport iconv from 'iconv-lite';\nimport net from 'node:net';\nimport { Hl7Base } from './base';\nimport { CR, FS, VT } from './constants';\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\n// iconv-lite docs have great examples and explanations for how to use Buffers with iconv-lite:\n// See: https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding\n\nexport type Hl7MessageQueueItem = {\n message: Hl7Message;\n resolve?: (reply: Hl7Message) => void;\n reject?: (err: Error) => void;\n};\n\nexport const DEFAULT_ENCODING = 'utf-8';\n\nexport class Hl7Connection extends Hl7Base {\n readonly socket: net.Socket;\n encoding: string;\n enhancedMode: boolean;\n private chunks: Buffer[] = [];\n private readonly messageQueue: Hl7MessageQueueItem[] = [];\n\n constructor(socket: net.Socket, encoding: string = DEFAULT_ENCODING, enhancedMode = false) {\n super();\n\n this.socket = socket;\n this.encoding = encoding;\n this.enhancedMode = enhancedMode;\n\n socket.on('data', (data: Buffer) => {\n try {\n this.appendData(data);\n if (data.at(-2) === FS && data.at(-1) === CR) {\n const buffer = Buffer.concat(this.chunks);\n const contentBuffer = buffer.subarray(1, buffer.length - 2);\n const contentString = iconv.decode(contentBuffer, this.encoding);\n const message = Hl7Message.parse(contentString);\n this.dispatchEvent(new Hl7MessageEvent(this, message));\n this.resetBuffer();\n }\n } catch (err) {\n this.dispatchEvent(new Hl7ErrorEvent(err as Error));\n }\n });\n\n socket.on('error', (err) => {\n this.resetBuffer();\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n\n // The difference between \"end\" and \"close\", is that \"end\" is only emitted on half-close from the other side\n // If the connection from the other side does not close gracefully, but instead we destroy the socket, then the Hl7Connection will not emit close\n // if we listen only for \"end\"; \"close\" is always emitted, whether the close is graceful or forceful\n socket.on('close', () => {\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n this.addEventListener('message', (event) => {\n if (this.enhancedMode) {\n this.send(event.message.buildAck({ ackCode: 'CA' }));\n }\n // Get the queue item at the head of the queue\n const next = this.messageQueue.shift();\n // If there isn't an item, then throw an error\n if (!next) {\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new Error(`Received a message when no pending messages were in the queue. Message: ${event.message}`)\n )\n );\n return;\n }\n // Resolve the promise if there is one pending for this message\n next.resolve?.(event.message);\n });\n }\n\n /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */\n isClosed(): boolean {\n return this.socket.closed;\n }\n\n private sendImpl(reply: Hl7Message, queueItem: Hl7MessageQueueItem): void {\n this.messageQueue.push(queueItem);\n const replyString = reply.toString();\n const replyBuffer = iconv.encode(replyString, this.encoding);\n const outputBuffer = Buffer.alloc(replyBuffer.length + 3);\n outputBuffer.writeInt8(VT, 0);\n replyBuffer.copy(outputBuffer, 1);\n outputBuffer.writeInt8(FS, replyBuffer.length + 1);\n outputBuffer.writeInt8(CR, replyBuffer.length + 2);\n this.socket.write(outputBuffer);\n }\n\n send(reply: Hl7Message): void {\n this.sendImpl(reply, { message: reply });\n }\n\n async sendAndWait(msg: Hl7Message): Promise<Hl7Message> {\n return new Promise<Hl7Message>((resolve, reject) => {\n const queueItem = { message: msg, resolve, reject };\n this.sendImpl(msg, queueItem);\n });\n }\n\n async close(): Promise<void> {\n // If we have already received the close event, then we can just return immediately\n if (this.isClosed()) {\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n // Register a temporary listener to help resolve the promise once close has been emitted\n this.socket.once('close', resolve);\n this.socket.end();\n this.socket.destroy();\n });\n }\n\n private appendData(data: Buffer): void {\n this.chunks.push(data);\n }\n\n private resetBuffer(): void {\n this.chunks = [];\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding ?? DEFAULT_ENCODING;\n }\n\n getEncoding(): string {\n return this.encoding;\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * VT (Vertical Tab) character.\n *\n * In HL7 messages, this character is used to indicate the start of a message.\n */\nexport const VT = 0x0b;\n\n/**\n * CR (Carriage Return) character.\n *\n * In HL7 messages, this character is used to indicate the end of a message.\n */\nexport const CR = 0x0d;\n\n/**\n * FS (File Separator) character.\n *\n * In HL7 messages, this character is used to separate fields.\n */\nexport const FS = 0x1c;\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7MessageEvent extends Event {\n readonly connection: Hl7Connection;\n readonly message: Hl7Message;\n\n constructor(connection: Hl7Connection, message: Hl7Message) {\n super('message');\n this.connection = connection;\n this.message = message;\n }\n}\n\nexport class Hl7ErrorEvent extends Event {\n readonly error: Error;\n\n constructor(error: Error) {\n super('error');\n this.error = error;\n }\n}\n\nexport class Hl7CloseEvent extends Event {\n constructor() {\n super('close');\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { sleep } from '@medplum/core';\nimport net from 'node:net';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7Server {\n readonly handler: (connection: Hl7Connection) => void;\n server?: net.Server;\n private encoding: string | undefined = undefined;\n private enhancedMode = false;\n\n constructor(handler: (connection: Hl7Connection) => void) {\n this.handler = handler;\n }\n\n start(port: number, encoding?: string, enhancedMode?: boolean): void {\n if (encoding) {\n this.setEncoding(encoding);\n }\n if (enhancedMode !== undefined) {\n this.setEnhancedMode(enhancedMode);\n }\n\n const server = net.createServer((socket) => {\n const connection = new Hl7Connection(socket, this.encoding, this.enhancedMode);\n this.handler(connection);\n });\n\n // Node errors have a code\n const errorListener = async (e: Error & { code?: string }): Promise<void> => {\n if (e?.code === 'EADDRINUSE') {\n await sleep(50);\n server.close();\n server.listen(port);\n }\n };\n server.on('error', errorListener);\n\n server.once('listening', () => {\n server.off('error', errorListener);\n });\n\n server.listen(port);\n this.server = server;\n }\n\n async stop(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!this.server) {\n reject(new Error('Stop was called but there is no server running'));\n return;\n }\n this.server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n this.server = undefined;\n });\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding;\n }\n\n getEncoding(): string | undefined {\n return this.encoding;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["index_exports", "__export", "CR", "DEFAULT_ENCODING", "FS", "Hl7Base", "Hl7Client", "Hl7CloseEvent", "Hl7Connection", "Hl7ErrorEvent", "Hl7MessageEvent", "Hl7Server", "VT", "__toCommonJS", "Hl7Base", "type", "listener", "options", "import_node_assert", "import_node_net", "import_core", "import_iconv_lite", "VT", "CR", "FS", "Hl7MessageEvent", "connection", "message", "Hl7ErrorEvent", "error", "Hl7CloseEvent", "DEFAULT_ENCODING", "Hl7Connection", "Hl7Base", "socket", "encoding", "enhancedMode", "data", "buffer", "contentBuffer", "contentString", "iconv", "message", "Hl7MessageEvent", "err", "Hl7ErrorEvent", "Hl7CloseEvent", "event", "next", "reply", "queueItem", "replyString", "replyBuffer", "outputBuffer", "msg", "resolve", "reject", "Hl7Client", "Hl7Base", "options", "deferredPromise", "assert", "socket", "timeoutListener", "error", "connectListener", "connection", "Hl7Connection", "errorListener", "err", "closeListener", "Hl7CloseEvent", "event", "Hl7ErrorEvent", "resolve", "reject", "_resolve", "_reject", "msg", "import_core", "import_node_net", "Hl7Server", "handler", "port", "encoding", "enhancedMode", "server", "net", "socket", "connection", "Hl7Connection", "errorListener", "e", "resolve", "reject", "err"]
4
+ "sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nexport * from './base';\nexport * from './client';\nexport * from './connection';\nexport * from './constants';\nexport * from './events';\nexport * from './server';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\nexport interface Hl7EventMap {\n message: Hl7MessageEvent;\n error: Hl7ErrorEvent;\n close: Hl7CloseEvent;\n}\n\nexport abstract class Hl7Base extends EventTarget {\n addEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.addEventListener(type, listener, options);\n }\n removeEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.removeEventListener(type, listener, options);\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport assert from 'node:assert';\nimport { connect, Socket } from 'node:net';\nimport { Hl7Base } from './base';\nimport { Hl7Connection, SendAndWaitOptions } from './connection';\nimport { Hl7CloseEvent, Hl7ErrorEvent } from './events';\n\nexport interface Hl7ClientOptions {\n host: string;\n port: number;\n encoding?: string;\n keepAlive?: boolean;\n connectTimeout?: number; // Add timeout option\n}\n\nexport interface DeferredConnectionPromise {\n promise: Promise<Hl7Connection>;\n resolve: (connection: Hl7Connection) => void;\n reject: (err: Error) => void;\n}\n\nexport class Hl7Client extends Hl7Base {\n options: Hl7ClientOptions;\n host: string;\n port: number;\n encoding?: string;\n connection?: Hl7Connection;\n keepAlive: boolean;\n private socket?: Socket;\n private connectTimeout: number;\n private deferredConnectionPromise?: DeferredConnectionPromise;\n\n constructor(options: Hl7ClientOptions) {\n super();\n this.options = options;\n this.host = this.options.host;\n this.port = this.options.port;\n this.encoding = this.options.encoding;\n this.keepAlive = this.options.keepAlive ?? false;\n this.connectTimeout = this.options.connectTimeout ?? 30000; // Default 30 seconds\n }\n\n connect(): Promise<Hl7Connection> {\n // If we are already waiting for a pending connection attempt, just return the deferred promise to that\n // In the case that the promise is already resolve, we will also return a resolved connection\n if (this.deferredConnectionPromise) {\n return this.deferredConnectionPromise.promise;\n }\n\n const deferredPromise = (this.deferredConnectionPromise = this.createDeferredConnectionPromise());\n\n // Create the socket\n this.socket = connect({\n host: this.host,\n port: this.port,\n keepAlive: this.keepAlive,\n });\n\n if (this.connectTimeout > 0) {\n this.socket.setTimeout(this.connectTimeout);\n this.registerSocketTimeoutListener(deferredPromise);\n }\n\n this.registerSocketConnectListener(deferredPromise);\n this.registerSocketErrorListener(deferredPromise);\n this.registerSocketCloseListener(deferredPromise);\n\n return deferredPromise.promise;\n }\n\n private registerSocketTimeoutListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle timeout event\n const timeoutListener = (): void => {\n this.cleanupSocket(socket);\n const error = new Error(`Connection timeout after ${this.connectTimeout}ms`);\n this.rejectDeferredPromise(deferredPromise, error);\n };\n\n socket.on('timeout', timeoutListener);\n }\n\n private registerSocketConnectListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle successful connection\n const connectListener = (): void => {\n if (socket !== this.socket) {\n this.cleanupSocket(socket);\n return;\n }\n\n // Create the HL7 connection\n let connection: Hl7Connection;\n this.connection = connection = new Hl7Connection(socket, this.encoding);\n\n // Remove the timeout listener as we're now connected\n socket.setTimeout(0);\n\n this.registerHl7ConnectionListeners(connection);\n\n deferredPromise.resolve(connection);\n };\n\n socket.on('connect', connectListener);\n }\n\n private registerSocketErrorListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const errorListener = (err: Error | AggregateError): void => {\n this.cleanupSocket(socket);\n\n if (err.constructor.name === 'AggregateError') {\n this.rejectDeferredPromise(deferredPromise, (err as AggregateError).errors[0]);\n } else {\n this.rejectDeferredPromise(deferredPromise, err);\n }\n };\n\n socket.on('error', errorListener);\n }\n\n private registerSocketCloseListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const closeListener = (): void => {\n this.cleanupSocket(socket);\n this.rejectDeferredPromise(deferredPromise, new Error('Socket closed before connection finished'));\n };\n socket.on('close', closeListener);\n }\n\n private registerHl7ConnectionListeners(connection: Hl7Connection): void {\n // Set up event handlers\n connection.addEventListener('close', () => {\n this.socket = undefined;\n this.connection = undefined;\n this.deferredConnectionPromise = undefined;\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n connection.addEventListener('error', (event) => {\n this.dispatchEvent(new Hl7ErrorEvent(event.error));\n });\n }\n\n private createDeferredConnectionPromise(): DeferredConnectionPromise {\n // Setup our deferred connection promise\n let resolve!: (connection: Hl7Connection) => void;\n let reject!: (err: Error) => void;\n\n const promise = new Promise<Hl7Connection>((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n\n return {\n promise,\n resolve,\n reject,\n };\n }\n\n private rejectDeferredPromise(deferredPromise: DeferredConnectionPromise, err: Error): void {\n // Reject this deferred promise with the given error\n deferredPromise.reject(err);\n\n // If the currently tracked deferred promise is this deferred promise, remove it from the client\n if (this.deferredConnectionPromise === deferredPromise) {\n this.deferredConnectionPromise = undefined;\n }\n }\n\n private cleanupSocket(socket: Socket): void {\n if (!socket.destroyed) {\n socket.destroy();\n }\n if (socket === this.socket) {\n this.socket = undefined;\n }\n }\n\n async send(msg: Hl7Message): Promise<void> {\n return (await this.connect()).send(msg);\n }\n\n async sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message> {\n return (await this.connect()).sendAndWait(msg, options);\n }\n\n async close(): Promise<void> {\n if (this.deferredConnectionPromise) {\n this.rejectDeferredPromise(this.deferredConnectionPromise, new Error('Client closed while connecting'));\n }\n\n // Close established connection if it exists\n if (this.connection) {\n const connection = this.connection;\n delete this.connection;\n await connection.close();\n }\n // Close the socket if it exists\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.destroy();\n this.socket = undefined;\n }\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message, OperationOutcomeError, sleep, validationError } from '@medplum/core';\nimport iconv from 'iconv-lite';\nimport net from 'node:net';\nimport { Hl7Base } from './base';\nimport { CR, FS, VT } from './constants';\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\n// iconv-lite docs have great examples and explanations for how to use Buffers with iconv-lite:\n// See: https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding\n\nexport type Hl7MessageQueueItem = {\n message: Hl7Message;\n resolve: (reply: Hl7Message) => void;\n reject: (err: Error) => void;\n returnAck: ReturnAckCategory;\n timer?: NodeJS.Timeout;\n};\n\nexport const ReturnAckCategory = {\n /** The first ACK message received is the one returned */\n FIRST: 'first',\n /** Only return upon receiving a positive application-level ACK (AA), or if a commit-level error occurred */\n APPLICATION: 'application',\n} as const;\nexport type ReturnAckCategory = (typeof ReturnAckCategory)[keyof typeof ReturnAckCategory];\n\nexport interface SendAndWaitOptions {\n /** The ACK-level that the Promise should resolve on. The default is `ReturnAckCategory.ANY` (returns on the first ACK of any type). */\n returnAck?: ReturnAckCategory;\n /** The amount of milliseconds to wait before timing out when waiting for the response to a message. */\n timeoutMs?: number;\n}\n\nexport interface Hl7ConnectionOptions {\n messagesPerMin?: number;\n}\n\nexport const DEFAULT_ENCODING = 'utf-8';\nconst ONE_MINUTE = 60 * 1000;\n\nexport class Hl7Connection extends Hl7Base {\n readonly socket: net.Socket;\n encoding: string;\n enhancedMode: boolean;\n private messagesPerMin: number | undefined = undefined;\n private chunks: Buffer[] = [];\n private readonly pendingMessages: Map<string, Hl7MessageQueueItem> = new Map<string, Hl7MessageQueueItem>();\n private readonly responseQueue: Hl7MessageEvent[] = [];\n private lastMessageDispatchedTime = 0;\n private responseQueueProcessing = false;\n\n constructor(\n socket: net.Socket,\n encoding: string = DEFAULT_ENCODING,\n enhancedMode = false,\n options: Hl7ConnectionOptions = {}\n ) {\n super();\n\n this.socket = socket;\n this.encoding = encoding;\n this.enhancedMode = enhancedMode;\n this.messagesPerMin = options.messagesPerMin;\n\n socket.on('data', (data: Buffer) => {\n try {\n this.appendData(data);\n if (data.at(-2) === FS && data.at(-1) === CR) {\n const buffer = Buffer.concat(this.chunks);\n const contentBuffer = buffer.subarray(1, buffer.length - 2);\n const contentString = iconv.decode(contentBuffer, this.encoding);\n const message = Hl7Message.parse(contentString);\n this.responseQueue.push(new Hl7MessageEvent(this, message));\n this.resetBuffer();\n this.processResponseQueue().catch((err) => {\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n }\n } catch (err) {\n this.dispatchEvent(new Hl7ErrorEvent(err as Error));\n }\n });\n\n socket.on('error', (err) => {\n this.resetBuffer();\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n\n // The difference between \"end\" and \"close\", is that \"end\" is only emitted on half-close from the other side\n // If the connection from the other side does not close gracefully, but instead we destroy the socket, then the Hl7Connection will not emit close\n // if we listen only for \"end\"; \"close\" is always emitted, whether the close is graceful or forceful\n socket.on('close', () => {\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n this.addEventListener('message', (event) => {\n if (this.enhancedMode) {\n this.send(event.message.buildAck({ ackCode: 'CA' }));\n }\n const origMsgCtrlId = event.message.getSegment('MSA')?.getField(2)?.toString();\n // If there is no message control ID, just return\n if (!origMsgCtrlId) {\n return;\n }\n const queueItem = this.pendingMessages.get(origMsgCtrlId);\n if (!queueItem) {\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'not-found',\n details: {\n text: 'Response received for unknown message control ID',\n },\n diagnostics: `Received ACK for message control ID '${origMsgCtrlId}' but there was no pending message with this control ID`,\n },\n ],\n })\n )\n );\n return;\n }\n // Check the ACK type we should return on\n const ackCode = event.message.getSegment('MSA')?.getField(1)?.toString()?.toUpperCase();\n if (!ackCode) {\n return;\n }\n\n // Two modes:\n // Application-level or first ACK\n\n // First should always return on any ACK message, this is the default\n // The exception is APPLICATION, which should not resolve when the ACK is a CA, but should resolve on all other ACK types\n // On CA, we return early\n if (queueItem.returnAck === ReturnAckCategory.APPLICATION && ackCode === 'CA') {\n return;\n }\n\n // Resolve the promise if there is one pending for this message and we didn't exit already because the ACK type matches\n queueItem.resolve(event.message);\n this.pendingMessages.delete(origMsgCtrlId);\n });\n }\n\n /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */\n isClosed(): boolean {\n return this.socket.closed;\n }\n\n private sendImpl(reply: Hl7Message): void {\n const replyString = reply.toString();\n const replyBuffer = iconv.encode(replyString, this.encoding);\n const outputBuffer = Buffer.alloc(replyBuffer.length + 3);\n outputBuffer.writeInt8(VT, 0);\n replyBuffer.copy(outputBuffer, 1);\n outputBuffer.writeInt8(FS, replyBuffer.length + 1);\n outputBuffer.writeInt8(CR, replyBuffer.length + 2);\n this.socket.write(outputBuffer);\n }\n\n private async processResponseQueue(): Promise<void> {\n if (this.responseQueueProcessing) {\n return;\n }\n\n this.responseQueueProcessing = true;\n while (this.responseQueue.length) {\n if (this.messagesPerMin) {\n const millisBetweenMsgs = ONE_MINUTE / (this.messagesPerMin as number);\n const elapsedMillis = Date.now() - this.lastMessageDispatchedTime;\n if (millisBetweenMsgs > elapsedMillis) {\n await sleep(millisBetweenMsgs - elapsedMillis);\n }\n }\n const messageEvent = this.responseQueue.shift() as Hl7MessageEvent;\n if (messageEvent) {\n this.dispatchEvent(messageEvent);\n }\n this.lastMessageDispatchedTime = Date.now();\n }\n this.responseQueueProcessing = false;\n }\n\n send(reply: Hl7Message): void {\n this.sendImpl(reply);\n }\n\n async sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message> {\n return new Promise<Hl7Message>((resolve, reject) => {\n const msgCtrlId = msg.getSegment('MSH')?.getField(10)?.toString();\n if (!msgCtrlId) {\n reject(new OperationOutcomeError(validationError('Required field missing: MSH.10')));\n return;\n }\n\n let timer: NodeJS.Timeout | undefined;\n\n if (options?.timeoutMs) {\n timer = setTimeout(() => {\n this.pendingMessages.delete(msgCtrlId);\n reject(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'timeout',\n details: {\n text: 'Client timeout',\n },\n diagnostics: `Request timed out after waiting ${options.timeoutMs} milliseconds for response`,\n },\n ],\n })\n );\n }, options.timeoutMs);\n }\n\n this.pendingMessages.set(msgCtrlId, {\n message: msg,\n resolve,\n reject,\n returnAck: options?.returnAck ?? ReturnAckCategory.FIRST,\n timer,\n });\n this.sendImpl(msg);\n });\n }\n\n async close(): Promise<void> {\n // If we have already received the close event, then we can just return immediately\n if (this.isClosed()) {\n return Promise.resolve();\n }\n this.socket.end();\n this.socket.destroy();\n // Before clearing out messages, we should propagate a message to the consumer that we are closing the connection while some messages were still pending a response\n if (this.pendingMessages.size) {\n for (const queueItem of this.pendingMessages.values()) {\n if (queueItem.timer) {\n clearTimeout(queueItem.timer);\n }\n queueItem.reject(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'incomplete',\n details: {\n text: 'Message was still pending when connection closed',\n },\n },\n ],\n })\n );\n }\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'incomplete',\n details: {\n text: 'Messages were still pending when connection closed',\n },\n diagnostics: `Hl7Connection closed while ${this.pendingMessages.size} messages were pending`,\n },\n ],\n })\n )\n );\n // Clear out any pending messages\n this.pendingMessages.clear();\n }\n return new Promise((resolve) => {\n // Register a temporary listener to help resolve the promise once close has been emitted\n this.socket.once('close', resolve);\n });\n }\n\n private appendData(data: Buffer): void {\n this.chunks.push(data);\n }\n\n private resetBuffer(): void {\n this.chunks = [];\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding ?? DEFAULT_ENCODING;\n }\n\n getEncoding(): string {\n return this.encoding;\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setMessagesPerMin(messagesPerMin: number | undefined): void {\n this.messagesPerMin = messagesPerMin;\n }\n\n getMessagesPerMin(): number | undefined {\n return this.messagesPerMin;\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * VT (Vertical Tab) character.\n *\n * In HL7 messages, this character is used to indicate the start of a message.\n */\nexport const VT = 0x0b;\n\n/**\n * CR (Carriage Return) character.\n *\n * In HL7 messages, this character is used to indicate the end of a message.\n */\nexport const CR = 0x0d;\n\n/**\n * FS (File Separator) character.\n *\n * In HL7 messages, this character is used to separate fields.\n */\nexport const FS = 0x1c;\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7MessageEvent extends Event {\n readonly connection: Hl7Connection;\n readonly message: Hl7Message;\n\n constructor(connection: Hl7Connection, message: Hl7Message) {\n super('message');\n this.connection = connection;\n this.message = message;\n }\n}\n\nexport class Hl7ErrorEvent extends Event {\n readonly error: Error;\n\n constructor(error: Error) {\n super('error');\n this.error = error;\n }\n}\n\nexport class Hl7CloseEvent extends Event {\n constructor() {\n super('close');\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { sleep } from '@medplum/core';\nimport net from 'node:net';\nimport { Hl7Connection, Hl7ConnectionOptions } from './connection';\n\nexport class Hl7Server {\n readonly handler: (connection: Hl7Connection) => void;\n server?: net.Server;\n private encoding: string | undefined = undefined;\n private enhancedMode = false;\n private messagesPerMin: number | undefined = undefined;\n\n constructor(handler: (connection: Hl7Connection) => void) {\n this.handler = handler;\n }\n\n start(port: number, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions): void {\n if (encoding) {\n this.setEncoding(encoding);\n }\n if (enhancedMode !== undefined) {\n this.setEnhancedMode(enhancedMode);\n }\n if (options?.messagesPerMin !== undefined) {\n this.setMessagesPerMin(options.messagesPerMin);\n }\n\n const server = net.createServer((socket) => {\n const connection = new Hl7Connection(socket, this.encoding, this.enhancedMode, {\n messagesPerMin: this.messagesPerMin,\n });\n this.handler(connection);\n });\n\n // Node errors have a code\n const errorListener = async (e: Error & { code?: string }): Promise<void> => {\n if (e?.code === 'EADDRINUSE') {\n await sleep(50);\n server.close();\n server.listen(port);\n }\n };\n server.on('error', errorListener);\n\n server.once('listening', () => {\n server.off('error', errorListener);\n });\n\n server.listen(port);\n this.server = server;\n }\n\n async stop(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!this.server) {\n reject(new Error('Stop was called but there is no server running'));\n return;\n }\n this.server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n this.server = undefined;\n });\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding;\n }\n\n getEncoding(): string | undefined {\n return this.encoding;\n }\n\n setMessagesPerMin(messagesPerMin: number | undefined): void {\n this.messagesPerMin = messagesPerMin;\n }\n\n getMessagesPerMin(): number | undefined {\n return this.messagesPerMin;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["index_exports", "__export", "CR", "DEFAULT_ENCODING", "FS", "Hl7Base", "Hl7Client", "Hl7CloseEvent", "Hl7Connection", "Hl7ErrorEvent", "Hl7MessageEvent", "Hl7Server", "ReturnAckCategory", "VT", "__toCommonJS", "Hl7Base", "type", "listener", "options", "import_node_assert", "import_node_net", "import_core", "import_iconv_lite", "VT", "CR", "FS", "Hl7MessageEvent", "connection", "message", "Hl7ErrorEvent", "error", "Hl7CloseEvent", "ReturnAckCategory", "DEFAULT_ENCODING", "ONE_MINUTE", "Hl7Connection", "Hl7Base", "socket", "encoding", "enhancedMode", "options", "data", "buffer", "contentBuffer", "contentString", "iconv", "message", "Hl7MessageEvent", "err", "Hl7ErrorEvent", "Hl7CloseEvent", "event", "origMsgCtrlId", "queueItem", "ackCode", "reply", "replyString", "replyBuffer", "outputBuffer", "millisBetweenMsgs", "elapsedMillis", "messageEvent", "msg", "resolve", "reject", "msgCtrlId", "timer", "messagesPerMin", "Hl7Client", "Hl7Base", "options", "deferredPromise", "assert", "socket", "timeoutListener", "error", "connectListener", "connection", "Hl7Connection", "errorListener", "err", "closeListener", "Hl7CloseEvent", "event", "Hl7ErrorEvent", "resolve", "reject", "_resolve", "_reject", "msg", "import_core", "import_node_net", "Hl7Server", "handler", "port", "encoding", "enhancedMode", "options", "server", "net", "socket", "connection", "Hl7Connection", "errorListener", "e", "resolve", "reject", "err", "messagesPerMin"]
7
7
  }
@@ -49,7 +49,7 @@ export declare class Hl7Client extends Hl7Base {
49
49
  private rejectDeferredPromise;
50
50
  private cleanupSocket;
51
51
  send(msg: Hl7Message): Promise<void>;
52
- sendAndWait(msg: Hl7Message): Promise<Hl7Message>;
52
+ sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message>;
53
53
  close(): Promise<void>;
54
54
  }
55
55
 
@@ -69,14 +69,19 @@ export declare class Hl7Connection extends Hl7Base {
69
69
  readonly socket: net.Socket;
70
70
  encoding: string;
71
71
  enhancedMode: boolean;
72
+ private messagesPerMin;
72
73
  private chunks;
73
- private readonly messageQueue;
74
- constructor(socket: net.Socket, encoding?: string, enhancedMode?: boolean);
74
+ private readonly pendingMessages;
75
+ private readonly responseQueue;
76
+ private lastMessageDispatchedTime;
77
+ private responseQueueProcessing;
78
+ constructor(socket: net.Socket, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions);
75
79
  /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */
76
80
  isClosed(): boolean;
77
81
  private sendImpl;
82
+ private processResponseQueue;
78
83
  send(reply: Hl7Message): void;
79
- sendAndWait(msg: Hl7Message): Promise<Hl7Message>;
84
+ sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message>;
80
85
  close(): Promise<void>;
81
86
  private appendData;
82
87
  private resetBuffer;
@@ -84,6 +89,12 @@ export declare class Hl7Connection extends Hl7Base {
84
89
  getEncoding(): string;
85
90
  setEnhancedMode(enhancedMode: boolean): void;
86
91
  getEnhancedMode(): boolean;
92
+ setMessagesPerMin(messagesPerMin: number | undefined): void;
93
+ getMessagesPerMin(): number | undefined;
94
+ }
95
+
96
+ export declare interface Hl7ConnectionOptions {
97
+ messagesPerMin?: number;
87
98
  }
88
99
 
89
100
  export declare class Hl7ErrorEvent extends Event {
@@ -105,8 +116,10 @@ export declare class Hl7MessageEvent extends Event {
105
116
 
106
117
  export declare type Hl7MessageQueueItem = {
107
118
  message: Hl7Message;
108
- resolve?: (reply: Hl7Message) => void;
109
- reject?: (err: Error) => void;
119
+ resolve: (reply: Hl7Message) => void;
120
+ reject: (err: Error) => void;
121
+ returnAck: ReturnAckCategory;
122
+ timer?: NodeJS.Timeout;
110
123
  };
111
124
 
112
125
  export declare class Hl7Server {
@@ -114,13 +127,32 @@ export declare class Hl7Server {
114
127
  server?: net.Server;
115
128
  private encoding;
116
129
  private enhancedMode;
130
+ private messagesPerMin;
117
131
  constructor(handler: (connection: Hl7Connection) => void);
118
- start(port: number, encoding?: string, enhancedMode?: boolean): void;
132
+ start(port: number, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions): void;
119
133
  stop(): Promise<void>;
120
134
  setEnhancedMode(enhancedMode: boolean): void;
121
135
  getEnhancedMode(): boolean;
122
136
  setEncoding(encoding: string | undefined): void;
123
137
  getEncoding(): string | undefined;
138
+ setMessagesPerMin(messagesPerMin: number | undefined): void;
139
+ getMessagesPerMin(): number | undefined;
140
+ }
141
+
142
+ export declare const ReturnAckCategory: {
143
+ /** The first ACK message received is the one returned */
144
+ readonly FIRST: "first";
145
+ /** Only return upon receiving a positive application-level ACK (AA), or if a commit-level error occurred */
146
+ readonly APPLICATION: "application";
147
+ };
148
+
149
+ export declare type ReturnAckCategory = (typeof ReturnAckCategory)[keyof typeof ReturnAckCategory];
150
+
151
+ export declare interface SendAndWaitOptions {
152
+ /** The ACK-level that the Promise should resolve on. The default is `ReturnAckCategory.ANY` (returns on the first ACK of any type). */
153
+ returnAck?: ReturnAckCategory;
154
+ /** The amount of milliseconds to wait before timing out when waiting for the response to a message. */
155
+ timeoutMs?: number;
124
156
  }
125
157
 
126
158
  /**
@@ -49,7 +49,7 @@ export declare class Hl7Client extends Hl7Base {
49
49
  private rejectDeferredPromise;
50
50
  private cleanupSocket;
51
51
  send(msg: Hl7Message): Promise<void>;
52
- sendAndWait(msg: Hl7Message): Promise<Hl7Message>;
52
+ sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message>;
53
53
  close(): Promise<void>;
54
54
  }
55
55
 
@@ -69,14 +69,19 @@ export declare class Hl7Connection extends Hl7Base {
69
69
  readonly socket: net.Socket;
70
70
  encoding: string;
71
71
  enhancedMode: boolean;
72
+ private messagesPerMin;
72
73
  private chunks;
73
- private readonly messageQueue;
74
- constructor(socket: net.Socket, encoding?: string, enhancedMode?: boolean);
74
+ private readonly pendingMessages;
75
+ private readonly responseQueue;
76
+ private lastMessageDispatchedTime;
77
+ private responseQueueProcessing;
78
+ constructor(socket: net.Socket, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions);
75
79
  /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */
76
80
  isClosed(): boolean;
77
81
  private sendImpl;
82
+ private processResponseQueue;
78
83
  send(reply: Hl7Message): void;
79
- sendAndWait(msg: Hl7Message): Promise<Hl7Message>;
84
+ sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message>;
80
85
  close(): Promise<void>;
81
86
  private appendData;
82
87
  private resetBuffer;
@@ -84,6 +89,12 @@ export declare class Hl7Connection extends Hl7Base {
84
89
  getEncoding(): string;
85
90
  setEnhancedMode(enhancedMode: boolean): void;
86
91
  getEnhancedMode(): boolean;
92
+ setMessagesPerMin(messagesPerMin: number | undefined): void;
93
+ getMessagesPerMin(): number | undefined;
94
+ }
95
+
96
+ export declare interface Hl7ConnectionOptions {
97
+ messagesPerMin?: number;
87
98
  }
88
99
 
89
100
  export declare class Hl7ErrorEvent extends Event {
@@ -105,8 +116,10 @@ export declare class Hl7MessageEvent extends Event {
105
116
 
106
117
  export declare type Hl7MessageQueueItem = {
107
118
  message: Hl7Message;
108
- resolve?: (reply: Hl7Message) => void;
109
- reject?: (err: Error) => void;
119
+ resolve: (reply: Hl7Message) => void;
120
+ reject: (err: Error) => void;
121
+ returnAck: ReturnAckCategory;
122
+ timer?: NodeJS.Timeout;
110
123
  };
111
124
 
112
125
  export declare class Hl7Server {
@@ -114,13 +127,32 @@ export declare class Hl7Server {
114
127
  server?: net.Server;
115
128
  private encoding;
116
129
  private enhancedMode;
130
+ private messagesPerMin;
117
131
  constructor(handler: (connection: Hl7Connection) => void);
118
- start(port: number, encoding?: string, enhancedMode?: boolean): void;
132
+ start(port: number, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions): void;
119
133
  stop(): Promise<void>;
120
134
  setEnhancedMode(enhancedMode: boolean): void;
121
135
  getEnhancedMode(): boolean;
122
136
  setEncoding(encoding: string | undefined): void;
123
137
  getEncoding(): string | undefined;
138
+ setMessagesPerMin(messagesPerMin: number | undefined): void;
139
+ getMessagesPerMin(): number | undefined;
140
+ }
141
+
142
+ export declare const ReturnAckCategory: {
143
+ /** The first ACK message received is the one returned */
144
+ readonly FIRST: "first";
145
+ /** Only return upon receiving a positive application-level ACK (AA), or if a commit-level error occurred */
146
+ readonly APPLICATION: "application";
147
+ };
148
+
149
+ export declare type ReturnAckCategory = (typeof ReturnAckCategory)[keyof typeof ReturnAckCategory];
150
+
151
+ export declare interface SendAndWaitOptions {
152
+ /** The ACK-level that the Promise should resolve on. The default is `ReturnAckCategory.ANY` (returns on the first ACK of any type). */
153
+ returnAck?: ReturnAckCategory;
154
+ /** The amount of milliseconds to wait before timing out when waiting for the response to a message. */
155
+ timeoutMs?: number;
124
156
  }
125
157
 
126
158
  /**
@@ -1,2 +1,2 @@
1
- var d=class extends EventTarget{addEventListener(e,t,o){super.addEventListener(e,t,o)}removeEventListener(e,t,o){super.removeEventListener(e,t,o)}};import p from"node:assert";import{connect as L}from"node:net";import{Hl7Message as P}from"@medplum/core";import u from"iconv-lite";var M=11,v=13,f=28;var l=class extends Event{constructor(e,t){super("message"),this.connection=e,this.message=t}},i=class extends Event{constructor(e){super("error"),this.error=e}},a=class extends Event{constructor(){super("close")}};var g="utf-8",h=class extends d{constructor(t,o=g,n=!1){super();this.chunks=[];this.messageQueue=[];this.socket=t,this.encoding=o,this.enhancedMode=n,t.on("data",s=>{try{if(this.appendData(s),s.at(-2)===28&&s.at(-1)===13){let r=Buffer.concat(this.chunks),m=r.subarray(1,r.length-2),C=u.decode(m,this.encoding),H=P.parse(C);this.dispatchEvent(new l(this,H)),this.resetBuffer()}}catch(r){this.dispatchEvent(new i(r))}}),t.on("error",s=>{this.resetBuffer(),this.dispatchEvent(new i(s))}),t.on("close",()=>{this.dispatchEvent(new a)}),this.addEventListener("message",s=>{this.enhancedMode&&this.send(s.message.buildAck({ackCode:"CA"}));let r=this.messageQueue.shift();if(!r){this.dispatchEvent(new i(new Error(`Received a message when no pending messages were in the queue. Message: ${s.message}`)));return}r.resolve?.(s.message)})}isClosed(){return this.socket.closed}sendImpl(t,o){this.messageQueue.push(o);let n=t.toString(),s=u.encode(n,this.encoding),r=Buffer.alloc(s.length+3);r.writeInt8(11,0),s.copy(r,1),r.writeInt8(28,s.length+1),r.writeInt8(13,s.length+2),this.socket.write(r)}send(t){this.sendImpl(t,{message:t})}async sendAndWait(t){return new Promise((o,n)=>{let s={message:t,resolve:o,reject:n};this.sendImpl(t,s)})}async close(){return this.isClosed()?Promise.resolve():new Promise(t=>{this.socket.once("close",t),this.socket.end(),this.socket.destroy()})}appendData(t){this.chunks.push(t)}resetBuffer(){this.chunks=[]}setEncoding(t){this.encoding=t??g}getEncoding(){return this.encoding}setEnhancedMode(t){this.enhancedMode=t}getEnhancedMode(){return this.enhancedMode}};var E=class extends d{constructor(e){super(),this.options=e,this.host=this.options.host,this.port=this.options.port,this.encoding=this.options.encoding,this.keepAlive=this.options.keepAlive??!1,this.connectTimeout=this.options.connectTimeout??3e4}connect(){if(this.deferredConnectionPromise)return this.deferredConnectionPromise.promise;let e=this.deferredConnectionPromise=this.createDeferredConnectionPromise();return this.socket=L({host:this.host,port:this.port,keepAlive:this.keepAlive}),this.connectTimeout>0&&(this.socket.setTimeout(this.connectTimeout),this.registerSocketTimeoutListener(e)),this.registerSocketConnectListener(e),this.registerSocketErrorListener(e),this.registerSocketCloseListener(e),e.promise}registerSocketTimeoutListener(e){p(this.socket);let t=this.socket,o=()=>{this.cleanupSocket(t);let n=new Error(`Connection timeout after ${this.connectTimeout}ms`);this.rejectDeferredPromise(e,n)};t.on("timeout",o)}registerSocketConnectListener(e){p(this.socket);let t=this.socket,o=()=>{if(t!==this.socket){this.cleanupSocket(t);return}let n;this.connection=n=new h(t,this.encoding),t.setTimeout(0),this.registerHl7ConnectionListeners(n),e.resolve(n)};t.on("connect",o)}registerSocketErrorListener(e){p(this.socket);let t=this.socket,o=n=>{this.cleanupSocket(t),n.constructor.name==="AggregateError"?this.rejectDeferredPromise(e,n.errors[0]):this.rejectDeferredPromise(e,n)};t.on("error",o)}registerSocketCloseListener(e){p(this.socket);let t=this.socket,o=()=>{this.cleanupSocket(t),this.rejectDeferredPromise(e,new Error("Socket closed before connection finished"))};t.on("close",o)}registerHl7ConnectionListeners(e){e.addEventListener("close",()=>{this.socket=void 0,this.connection=void 0,this.deferredConnectionPromise=void 0,this.dispatchEvent(new a)}),e.addEventListener("error",t=>{this.dispatchEvent(new i(t.error))})}createDeferredConnectionPromise(){let e,t;return{promise:new Promise((n,s)=>{e=n,t=s}),resolve:e,reject:t}}rejectDeferredPromise(e,t){e.reject(t),this.deferredConnectionPromise===e&&(this.deferredConnectionPromise=void 0)}cleanupSocket(e){e.destroyed||e.destroy(),e===this.socket&&(this.socket=void 0)}async send(e){return(await this.connect()).send(e)}async sendAndWait(e){return(await this.connect()).sendAndWait(e)}async close(){if(this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=void 0),this.deferredConnectionPromise&&this.rejectDeferredPromise(this.deferredConnectionPromise,new Error("Client closed while connecting")),this.connection){let e=this.connection;delete this.connection,await e.close()}}};import{sleep as x}from"@medplum/core";import w from"node:net";var k=class{constructor(e){this.encoding=void 0;this.enhancedMode=!1;this.handler=e}start(e,t,o){t&&this.setEncoding(t),o!==void 0&&this.setEnhancedMode(o);let n=w.createServer(r=>{let m=new h(r,this.encoding,this.enhancedMode);this.handler(m)}),s=async r=>{r?.code==="EADDRINUSE"&&(await x(50),n.close(),n.listen(e))};n.on("error",s),n.once("listening",()=>{n.off("error",s)}),n.listen(e),this.server=n}async stop(){return new Promise((e,t)=>{if(!this.server){t(new Error("Stop was called but there is no server running"));return}this.server.close(o=>{if(o){t(o);return}e()}),this.server=void 0})}setEnhancedMode(e){this.enhancedMode=e}getEnhancedMode(){return this.enhancedMode}setEncoding(e){this.encoding=e}getEncoding(){return this.encoding}};export{v as CR,g as DEFAULT_ENCODING,f as FS,d as Hl7Base,E as Hl7Client,a as Hl7CloseEvent,h as Hl7Connection,i as Hl7ErrorEvent,l as Hl7MessageEvent,k as Hl7Server,M as VT};
1
+ var l=class extends EventTarget{addEventListener(t,e,n){super.addEventListener(t,e,n)}removeEventListener(t,e,n){super.removeEventListener(t,e,n)}};import f from"node:assert";import{connect as O}from"node:net";import{Hl7Message as A,OperationOutcomeError as u,sleep as x,validationError as b}from"@medplum/core";import E from"iconv-lite";var S=11,v=13,M=28;var m=class extends Event{constructor(t,e){super("message"),this.connection=t,this.message=e}},c=class extends Event{constructor(t){super("error"),this.error=t}},h=class extends Event{constructor(){super("close")}};var C={FIRST:"first",APPLICATION:"application"},k="utf-8",L=60*1e3,p=class extends l{constructor(e,n=k,s=!1,o={}){super();this.messagesPerMin=void 0;this.chunks=[];this.pendingMessages=new Map;this.responseQueue=[];this.lastMessageDispatchedTime=0;this.responseQueueProcessing=!1;this.socket=e,this.encoding=n,this.enhancedMode=s,this.messagesPerMin=o.messagesPerMin,e.on("data",i=>{try{if(this.appendData(i),i.at(-2)===28&&i.at(-1)===13){let r=Buffer.concat(this.chunks),d=r.subarray(1,r.length-2),g=E.decode(d,this.encoding),w=A.parse(g);this.responseQueue.push(new m(this,w)),this.resetBuffer(),this.processResponseQueue().catch(y=>{this.dispatchEvent(new c(y))})}}catch(r){this.dispatchEvent(new c(r))}}),e.on("error",i=>{this.resetBuffer(),this.dispatchEvent(new c(i))}),e.on("close",()=>{this.dispatchEvent(new h)}),this.addEventListener("message",i=>{this.enhancedMode&&this.send(i.message.buildAck({ackCode:"CA"}));let r=i.message.getSegment("MSA")?.getField(2)?.toString();if(!r)return;let d=this.pendingMessages.get(r);if(!d){this.dispatchEvent(new c(new u({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"not-found",details:{text:"Response received for unknown message control ID"},diagnostics:`Received ACK for message control ID '${r}' but there was no pending message with this control ID`}]})));return}let g=i.message.getSegment("MSA")?.getField(1)?.toString()?.toUpperCase();g&&(d.returnAck===C.APPLICATION&&g==="CA"||(d.resolve(i.message),this.pendingMessages.delete(r)))})}isClosed(){return this.socket.closed}sendImpl(e){let n=e.toString(),s=E.encode(n,this.encoding),o=Buffer.alloc(s.length+3);o.writeInt8(11,0),s.copy(o,1),o.writeInt8(28,s.length+1),o.writeInt8(13,s.length+2),this.socket.write(o)}async processResponseQueue(){if(!this.responseQueueProcessing){for(this.responseQueueProcessing=!0;this.responseQueue.length;){if(this.messagesPerMin){let n=L/this.messagesPerMin,s=Date.now()-this.lastMessageDispatchedTime;n>s&&await x(n-s)}let e=this.responseQueue.shift();e&&this.dispatchEvent(e),this.lastMessageDispatchedTime=Date.now()}this.responseQueueProcessing=!1}}send(e){this.sendImpl(e)}async sendAndWait(e,n){return new Promise((s,o)=>{let i=e.getSegment("MSH")?.getField(10)?.toString();if(!i){o(new u(b("Required field missing: MSH.10")));return}let r;n?.timeoutMs&&(r=setTimeout(()=>{this.pendingMessages.delete(i),o(new u({resourceType:"OperationOutcome",issue:[{severity:"error",code:"timeout",details:{text:"Client timeout"},diagnostics:`Request timed out after waiting ${n.timeoutMs} milliseconds for response`}]}))},n.timeoutMs)),this.pendingMessages.set(i,{message:e,resolve:s,reject:o,returnAck:n?.returnAck??C.FIRST,timer:r}),this.sendImpl(e)})}async close(){if(this.isClosed())return Promise.resolve();if(this.socket.end(),this.socket.destroy(),this.pendingMessages.size){for(let e of this.pendingMessages.values())e.timer&&clearTimeout(e.timer),e.reject(new u({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"incomplete",details:{text:"Message was still pending when connection closed"}}]}));this.dispatchEvent(new c(new u({resourceType:"OperationOutcome",issue:[{severity:"warning",code:"incomplete",details:{text:"Messages were still pending when connection closed"},diagnostics:`Hl7Connection closed while ${this.pendingMessages.size} messages were pending`}]}))),this.pendingMessages.clear()}return new Promise(e=>{this.socket.once("close",e)})}appendData(e){this.chunks.push(e)}resetBuffer(){this.chunks=[]}setEncoding(e){this.encoding=e??k}getEncoding(){return this.encoding}setEnhancedMode(e){this.enhancedMode=e}getEnhancedMode(){return this.enhancedMode}setMessagesPerMin(e){this.messagesPerMin=e}getMessagesPerMin(){return this.messagesPerMin}};var P=class extends l{constructor(t){super(),this.options=t,this.host=this.options.host,this.port=this.options.port,this.encoding=this.options.encoding,this.keepAlive=this.options.keepAlive??!1,this.connectTimeout=this.options.connectTimeout??3e4}connect(){if(this.deferredConnectionPromise)return this.deferredConnectionPromise.promise;let t=this.deferredConnectionPromise=this.createDeferredConnectionPromise();return this.socket=O({host:this.host,port:this.port,keepAlive:this.keepAlive}),this.connectTimeout>0&&(this.socket.setTimeout(this.connectTimeout),this.registerSocketTimeoutListener(t)),this.registerSocketConnectListener(t),this.registerSocketErrorListener(t),this.registerSocketCloseListener(t),t.promise}registerSocketTimeoutListener(t){f(this.socket);let e=this.socket,n=()=>{this.cleanupSocket(e);let s=new Error(`Connection timeout after ${this.connectTimeout}ms`);this.rejectDeferredPromise(t,s)};e.on("timeout",n)}registerSocketConnectListener(t){f(this.socket);let e=this.socket,n=()=>{if(e!==this.socket){this.cleanupSocket(e);return}let s;this.connection=s=new p(e,this.encoding),e.setTimeout(0),this.registerHl7ConnectionListeners(s),t.resolve(s)};e.on("connect",n)}registerSocketErrorListener(t){f(this.socket);let e=this.socket,n=s=>{this.cleanupSocket(e),s.constructor.name==="AggregateError"?this.rejectDeferredPromise(t,s.errors[0]):this.rejectDeferredPromise(t,s)};e.on("error",n)}registerSocketCloseListener(t){f(this.socket);let e=this.socket,n=()=>{this.cleanupSocket(e),this.rejectDeferredPromise(t,new Error("Socket closed before connection finished"))};e.on("close",n)}registerHl7ConnectionListeners(t){t.addEventListener("close",()=>{this.socket=void 0,this.connection=void 0,this.deferredConnectionPromise=void 0,this.dispatchEvent(new h)}),t.addEventListener("error",e=>{this.dispatchEvent(new c(e.error))})}createDeferredConnectionPromise(){let t,e;return{promise:new Promise((s,o)=>{t=s,e=o}),resolve:t,reject:e}}rejectDeferredPromise(t,e){t.reject(e),this.deferredConnectionPromise===t&&(this.deferredConnectionPromise=void 0)}cleanupSocket(t){t.destroyed||t.destroy(),t===this.socket&&(this.socket=void 0)}async send(t){return(await this.connect()).send(t)}async sendAndWait(t,e){return(await this.connect()).sendAndWait(t,e)}async close(){if(this.deferredConnectionPromise&&this.rejectDeferredPromise(this.deferredConnectionPromise,new Error("Client closed while connecting")),this.connection){let t=this.connection;delete this.connection,await t.close()}this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=void 0)}};import{sleep as T}from"@medplum/core";import D from"node:net";var H=class{constructor(t){this.encoding=void 0;this.enhancedMode=!1;this.messagesPerMin=void 0;this.handler=t}start(t,e,n,s){e&&this.setEncoding(e),n!==void 0&&this.setEnhancedMode(n),s?.messagesPerMin!==void 0&&this.setMessagesPerMin(s.messagesPerMin);let o=D.createServer(r=>{let d=new p(r,this.encoding,this.enhancedMode,{messagesPerMin:this.messagesPerMin});this.handler(d)}),i=async r=>{r?.code==="EADDRINUSE"&&(await T(50),o.close(),o.listen(t))};o.on("error",i),o.once("listening",()=>{o.off("error",i)}),o.listen(t),this.server=o}async stop(){return new Promise((t,e)=>{if(!this.server){e(new Error("Stop was called but there is no server running"));return}this.server.close(n=>{if(n){e(n);return}t()}),this.server=void 0})}setEnhancedMode(t){this.enhancedMode=t}getEnhancedMode(){return this.enhancedMode}setEncoding(t){this.encoding=t}getEncoding(){return this.encoding}setMessagesPerMin(t){this.messagesPerMin=t}getMessagesPerMin(){return this.messagesPerMin}};export{v as CR,k as DEFAULT_ENCODING,M as FS,l as Hl7Base,P as Hl7Client,h as Hl7CloseEvent,p as Hl7Connection,c as Hl7ErrorEvent,m as Hl7MessageEvent,H as Hl7Server,C as ReturnAckCategory,S as VT};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/base.ts", "../../src/client.ts", "../../src/connection.ts", "../../src/constants.ts", "../../src/events.ts", "../../src/server.ts"],
4
- "sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\nexport interface Hl7EventMap {\n message: Hl7MessageEvent;\n error: Hl7ErrorEvent;\n close: Hl7CloseEvent;\n}\n\nexport abstract class Hl7Base extends EventTarget {\n addEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.addEventListener(type, listener, options);\n }\n removeEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.removeEventListener(type, listener, options);\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport assert from 'node:assert';\nimport { connect, Socket } from 'node:net';\nimport { Hl7Base } from './base';\nimport { Hl7Connection } from './connection';\nimport { Hl7CloseEvent, Hl7ErrorEvent } from './events';\n\nexport interface Hl7ClientOptions {\n host: string;\n port: number;\n encoding?: string;\n keepAlive?: boolean;\n connectTimeout?: number; // Add timeout option\n}\n\nexport interface DeferredConnectionPromise {\n promise: Promise<Hl7Connection>;\n resolve: (connection: Hl7Connection) => void;\n reject: (err: Error) => void;\n}\n\nexport class Hl7Client extends Hl7Base {\n options: Hl7ClientOptions;\n host: string;\n port: number;\n encoding?: string;\n connection?: Hl7Connection;\n keepAlive: boolean;\n private socket?: Socket;\n private connectTimeout: number;\n private deferredConnectionPromise?: DeferredConnectionPromise;\n\n constructor(options: Hl7ClientOptions) {\n super();\n this.options = options;\n this.host = this.options.host;\n this.port = this.options.port;\n this.encoding = this.options.encoding;\n this.keepAlive = this.options.keepAlive ?? false;\n this.connectTimeout = this.options.connectTimeout ?? 30000; // Default 30 seconds\n }\n\n connect(): Promise<Hl7Connection> {\n // If we are already waiting for a pending connection attempt, just return the deferred promise to that\n // In the case that the promise is already resolve, we will also return a resolved connection\n if (this.deferredConnectionPromise) {\n return this.deferredConnectionPromise.promise;\n }\n\n const deferredPromise = (this.deferredConnectionPromise = this.createDeferredConnectionPromise());\n\n // Create the socket\n this.socket = connect({\n host: this.host,\n port: this.port,\n keepAlive: this.keepAlive,\n });\n\n if (this.connectTimeout > 0) {\n this.socket.setTimeout(this.connectTimeout);\n this.registerSocketTimeoutListener(deferredPromise);\n }\n\n this.registerSocketConnectListener(deferredPromise);\n this.registerSocketErrorListener(deferredPromise);\n this.registerSocketCloseListener(deferredPromise);\n\n return deferredPromise.promise;\n }\n\n private registerSocketTimeoutListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle timeout event\n const timeoutListener = (): void => {\n this.cleanupSocket(socket);\n const error = new Error(`Connection timeout after ${this.connectTimeout}ms`);\n this.rejectDeferredPromise(deferredPromise, error);\n };\n\n socket.on('timeout', timeoutListener);\n }\n\n private registerSocketConnectListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle successful connection\n const connectListener = (): void => {\n if (socket !== this.socket) {\n this.cleanupSocket(socket);\n return;\n }\n\n // Create the HL7 connection\n let connection: Hl7Connection;\n this.connection = connection = new Hl7Connection(socket, this.encoding);\n\n // Remove the timeout listener as we're now connected\n socket.setTimeout(0);\n\n this.registerHl7ConnectionListeners(connection);\n\n deferredPromise.resolve(connection);\n };\n\n socket.on('connect', connectListener);\n }\n\n private registerSocketErrorListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const errorListener = (err: Error | AggregateError): void => {\n this.cleanupSocket(socket);\n\n if (err.constructor.name === 'AggregateError') {\n this.rejectDeferredPromise(deferredPromise, (err as AggregateError).errors[0]);\n } else {\n this.rejectDeferredPromise(deferredPromise, err);\n }\n };\n\n socket.on('error', errorListener);\n }\n\n private registerSocketCloseListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const closeListener = (): void => {\n this.cleanupSocket(socket);\n this.rejectDeferredPromise(deferredPromise, new Error('Socket closed before connection finished'));\n };\n socket.on('close', closeListener);\n }\n\n private registerHl7ConnectionListeners(connection: Hl7Connection): void {\n // Set up event handlers\n connection.addEventListener('close', () => {\n this.socket = undefined;\n this.connection = undefined;\n this.deferredConnectionPromise = undefined;\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n connection.addEventListener('error', (event) => {\n this.dispatchEvent(new Hl7ErrorEvent(event.error));\n });\n }\n\n private createDeferredConnectionPromise(): DeferredConnectionPromise {\n // Setup our deferred connection promise\n let resolve!: (connection: Hl7Connection) => void;\n let reject!: (err: Error) => void;\n\n const promise = new Promise<Hl7Connection>((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n\n return {\n promise,\n resolve,\n reject,\n };\n }\n\n private rejectDeferredPromise(deferredPromise: DeferredConnectionPromise, err: Error): void {\n // Reject this deferred promise with the given error\n deferredPromise.reject(err);\n\n // If the currently tracked deferred promise is this deferred promise, remove it from the client\n if (this.deferredConnectionPromise === deferredPromise) {\n this.deferredConnectionPromise = undefined;\n }\n }\n\n private cleanupSocket(socket: Socket): void {\n if (!socket.destroyed) {\n socket.destroy();\n }\n if (socket === this.socket) {\n this.socket = undefined;\n }\n }\n\n async send(msg: Hl7Message): Promise<void> {\n return (await this.connect()).send(msg);\n }\n\n async sendAndWait(msg: Hl7Message): Promise<Hl7Message> {\n return (await this.connect()).sendAndWait(msg);\n }\n\n async close(): Promise<void> {\n // Close the socket if it exists\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.destroy();\n this.socket = undefined;\n }\n\n if (this.deferredConnectionPromise) {\n this.rejectDeferredPromise(this.deferredConnectionPromise, new Error('Client closed while connecting'));\n }\n\n // Close established connection if it exists\n if (this.connection) {\n const connection = this.connection;\n delete this.connection;\n await connection.close();\n }\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport iconv from 'iconv-lite';\nimport net from 'node:net';\nimport { Hl7Base } from './base';\nimport { CR, FS, VT } from './constants';\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\n// iconv-lite docs have great examples and explanations for how to use Buffers with iconv-lite:\n// See: https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding\n\nexport type Hl7MessageQueueItem = {\n message: Hl7Message;\n resolve?: (reply: Hl7Message) => void;\n reject?: (err: Error) => void;\n};\n\nexport const DEFAULT_ENCODING = 'utf-8';\n\nexport class Hl7Connection extends Hl7Base {\n readonly socket: net.Socket;\n encoding: string;\n enhancedMode: boolean;\n private chunks: Buffer[] = [];\n private readonly messageQueue: Hl7MessageQueueItem[] = [];\n\n constructor(socket: net.Socket, encoding: string = DEFAULT_ENCODING, enhancedMode = false) {\n super();\n\n this.socket = socket;\n this.encoding = encoding;\n this.enhancedMode = enhancedMode;\n\n socket.on('data', (data: Buffer) => {\n try {\n this.appendData(data);\n if (data.at(-2) === FS && data.at(-1) === CR) {\n const buffer = Buffer.concat(this.chunks);\n const contentBuffer = buffer.subarray(1, buffer.length - 2);\n const contentString = iconv.decode(contentBuffer, this.encoding);\n const message = Hl7Message.parse(contentString);\n this.dispatchEvent(new Hl7MessageEvent(this, message));\n this.resetBuffer();\n }\n } catch (err) {\n this.dispatchEvent(new Hl7ErrorEvent(err as Error));\n }\n });\n\n socket.on('error', (err) => {\n this.resetBuffer();\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n\n // The difference between \"end\" and \"close\", is that \"end\" is only emitted on half-close from the other side\n // If the connection from the other side does not close gracefully, but instead we destroy the socket, then the Hl7Connection will not emit close\n // if we listen only for \"end\"; \"close\" is always emitted, whether the close is graceful or forceful\n socket.on('close', () => {\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n this.addEventListener('message', (event) => {\n if (this.enhancedMode) {\n this.send(event.message.buildAck({ ackCode: 'CA' }));\n }\n // Get the queue item at the head of the queue\n const next = this.messageQueue.shift();\n // If there isn't an item, then throw an error\n if (!next) {\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new Error(`Received a message when no pending messages were in the queue. Message: ${event.message}`)\n )\n );\n return;\n }\n // Resolve the promise if there is one pending for this message\n next.resolve?.(event.message);\n });\n }\n\n /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */\n isClosed(): boolean {\n return this.socket.closed;\n }\n\n private sendImpl(reply: Hl7Message, queueItem: Hl7MessageQueueItem): void {\n this.messageQueue.push(queueItem);\n const replyString = reply.toString();\n const replyBuffer = iconv.encode(replyString, this.encoding);\n const outputBuffer = Buffer.alloc(replyBuffer.length + 3);\n outputBuffer.writeInt8(VT, 0);\n replyBuffer.copy(outputBuffer, 1);\n outputBuffer.writeInt8(FS, replyBuffer.length + 1);\n outputBuffer.writeInt8(CR, replyBuffer.length + 2);\n this.socket.write(outputBuffer);\n }\n\n send(reply: Hl7Message): void {\n this.sendImpl(reply, { message: reply });\n }\n\n async sendAndWait(msg: Hl7Message): Promise<Hl7Message> {\n return new Promise<Hl7Message>((resolve, reject) => {\n const queueItem = { message: msg, resolve, reject };\n this.sendImpl(msg, queueItem);\n });\n }\n\n async close(): Promise<void> {\n // If we have already received the close event, then we can just return immediately\n if (this.isClosed()) {\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n // Register a temporary listener to help resolve the promise once close has been emitted\n this.socket.once('close', resolve);\n this.socket.end();\n this.socket.destroy();\n });\n }\n\n private appendData(data: Buffer): void {\n this.chunks.push(data);\n }\n\n private resetBuffer(): void {\n this.chunks = [];\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding ?? DEFAULT_ENCODING;\n }\n\n getEncoding(): string {\n return this.encoding;\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * VT (Vertical Tab) character.\n *\n * In HL7 messages, this character is used to indicate the start of a message.\n */\nexport const VT = 0x0b;\n\n/**\n * CR (Carriage Return) character.\n *\n * In HL7 messages, this character is used to indicate the end of a message.\n */\nexport const CR = 0x0d;\n\n/**\n * FS (File Separator) character.\n *\n * In HL7 messages, this character is used to separate fields.\n */\nexport const FS = 0x1c;\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7MessageEvent extends Event {\n readonly connection: Hl7Connection;\n readonly message: Hl7Message;\n\n constructor(connection: Hl7Connection, message: Hl7Message) {\n super('message');\n this.connection = connection;\n this.message = message;\n }\n}\n\nexport class Hl7ErrorEvent extends Event {\n readonly error: Error;\n\n constructor(error: Error) {\n super('error');\n this.error = error;\n }\n}\n\nexport class Hl7CloseEvent extends Event {\n constructor() {\n super('close');\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { sleep } from '@medplum/core';\nimport net from 'node:net';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7Server {\n readonly handler: (connection: Hl7Connection) => void;\n server?: net.Server;\n private encoding: string | undefined = undefined;\n private enhancedMode = false;\n\n constructor(handler: (connection: Hl7Connection) => void) {\n this.handler = handler;\n }\n\n start(port: number, encoding?: string, enhancedMode?: boolean): void {\n if (encoding) {\n this.setEncoding(encoding);\n }\n if (enhancedMode !== undefined) {\n this.setEnhancedMode(enhancedMode);\n }\n\n const server = net.createServer((socket) => {\n const connection = new Hl7Connection(socket, this.encoding, this.enhancedMode);\n this.handler(connection);\n });\n\n // Node errors have a code\n const errorListener = async (e: Error & { code?: string }): Promise<void> => {\n if (e?.code === 'EADDRINUSE') {\n await sleep(50);\n server.close();\n server.listen(port);\n }\n };\n server.on('error', errorListener);\n\n server.once('listening', () => {\n server.off('error', errorListener);\n });\n\n server.listen(port);\n this.server = server;\n }\n\n async stop(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!this.server) {\n reject(new Error('Stop was called but there is no server running'));\n return;\n }\n this.server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n this.server = undefined;\n });\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding;\n }\n\n getEncoding(): string | undefined {\n return this.encoding;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["Hl7Base", "type", "listener", "options", "assert", "connect", "Hl7Message", "iconv", "VT", "CR", "FS", "Hl7MessageEvent", "connection", "message", "Hl7ErrorEvent", "error", "Hl7CloseEvent", "DEFAULT_ENCODING", "Hl7Connection", "Hl7Base", "socket", "encoding", "enhancedMode", "data", "buffer", "contentBuffer", "contentString", "iconv", "message", "Hl7Message", "Hl7MessageEvent", "err", "Hl7ErrorEvent", "Hl7CloseEvent", "event", "next", "reply", "queueItem", "replyString", "replyBuffer", "outputBuffer", "msg", "resolve", "reject", "Hl7Client", "Hl7Base", "options", "deferredPromise", "connect", "assert", "socket", "timeoutListener", "error", "connectListener", "connection", "Hl7Connection", "errorListener", "err", "closeListener", "Hl7CloseEvent", "event", "Hl7ErrorEvent", "resolve", "reject", "_resolve", "_reject", "msg", "sleep", "net", "Hl7Server", "handler", "port", "encoding", "enhancedMode", "server", "net", "socket", "connection", "Hl7Connection", "errorListener", "e", "sleep", "resolve", "reject", "err"]
4
+ "sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\nexport interface Hl7EventMap {\n message: Hl7MessageEvent;\n error: Hl7ErrorEvent;\n close: Hl7CloseEvent;\n}\n\nexport abstract class Hl7Base extends EventTarget {\n addEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.addEventListener(type, listener, options);\n }\n removeEventListener<K extends keyof Hl7EventMap>(\n type: K,\n listener: ((event: Hl7EventMap[K]) => void) | EventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void;\n\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n super.removeEventListener(type, listener, options);\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport assert from 'node:assert';\nimport { connect, Socket } from 'node:net';\nimport { Hl7Base } from './base';\nimport { Hl7Connection, SendAndWaitOptions } from './connection';\nimport { Hl7CloseEvent, Hl7ErrorEvent } from './events';\n\nexport interface Hl7ClientOptions {\n host: string;\n port: number;\n encoding?: string;\n keepAlive?: boolean;\n connectTimeout?: number; // Add timeout option\n}\n\nexport interface DeferredConnectionPromise {\n promise: Promise<Hl7Connection>;\n resolve: (connection: Hl7Connection) => void;\n reject: (err: Error) => void;\n}\n\nexport class Hl7Client extends Hl7Base {\n options: Hl7ClientOptions;\n host: string;\n port: number;\n encoding?: string;\n connection?: Hl7Connection;\n keepAlive: boolean;\n private socket?: Socket;\n private connectTimeout: number;\n private deferredConnectionPromise?: DeferredConnectionPromise;\n\n constructor(options: Hl7ClientOptions) {\n super();\n this.options = options;\n this.host = this.options.host;\n this.port = this.options.port;\n this.encoding = this.options.encoding;\n this.keepAlive = this.options.keepAlive ?? false;\n this.connectTimeout = this.options.connectTimeout ?? 30000; // Default 30 seconds\n }\n\n connect(): Promise<Hl7Connection> {\n // If we are already waiting for a pending connection attempt, just return the deferred promise to that\n // In the case that the promise is already resolve, we will also return a resolved connection\n if (this.deferredConnectionPromise) {\n return this.deferredConnectionPromise.promise;\n }\n\n const deferredPromise = (this.deferredConnectionPromise = this.createDeferredConnectionPromise());\n\n // Create the socket\n this.socket = connect({\n host: this.host,\n port: this.port,\n keepAlive: this.keepAlive,\n });\n\n if (this.connectTimeout > 0) {\n this.socket.setTimeout(this.connectTimeout);\n this.registerSocketTimeoutListener(deferredPromise);\n }\n\n this.registerSocketConnectListener(deferredPromise);\n this.registerSocketErrorListener(deferredPromise);\n this.registerSocketCloseListener(deferredPromise);\n\n return deferredPromise.promise;\n }\n\n private registerSocketTimeoutListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle timeout event\n const timeoutListener = (): void => {\n this.cleanupSocket(socket);\n const error = new Error(`Connection timeout after ${this.connectTimeout}ms`);\n this.rejectDeferredPromise(deferredPromise, error);\n };\n\n socket.on('timeout', timeoutListener);\n }\n\n private registerSocketConnectListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle successful connection\n const connectListener = (): void => {\n if (socket !== this.socket) {\n this.cleanupSocket(socket);\n return;\n }\n\n // Create the HL7 connection\n let connection: Hl7Connection;\n this.connection = connection = new Hl7Connection(socket, this.encoding);\n\n // Remove the timeout listener as we're now connected\n socket.setTimeout(0);\n\n this.registerHl7ConnectionListeners(connection);\n\n deferredPromise.resolve(connection);\n };\n\n socket.on('connect', connectListener);\n }\n\n private registerSocketErrorListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const errorListener = (err: Error | AggregateError): void => {\n this.cleanupSocket(socket);\n\n if (err.constructor.name === 'AggregateError') {\n this.rejectDeferredPromise(deferredPromise, (err as AggregateError).errors[0]);\n } else {\n this.rejectDeferredPromise(deferredPromise, err);\n }\n };\n\n socket.on('error', errorListener);\n }\n\n private registerSocketCloseListener(deferredPromise: DeferredConnectionPromise): void {\n assert(this.socket);\n const socket = this.socket;\n\n // Handle connection errors\n const closeListener = (): void => {\n this.cleanupSocket(socket);\n this.rejectDeferredPromise(deferredPromise, new Error('Socket closed before connection finished'));\n };\n socket.on('close', closeListener);\n }\n\n private registerHl7ConnectionListeners(connection: Hl7Connection): void {\n // Set up event handlers\n connection.addEventListener('close', () => {\n this.socket = undefined;\n this.connection = undefined;\n this.deferredConnectionPromise = undefined;\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n connection.addEventListener('error', (event) => {\n this.dispatchEvent(new Hl7ErrorEvent(event.error));\n });\n }\n\n private createDeferredConnectionPromise(): DeferredConnectionPromise {\n // Setup our deferred connection promise\n let resolve!: (connection: Hl7Connection) => void;\n let reject!: (err: Error) => void;\n\n const promise = new Promise<Hl7Connection>((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n\n return {\n promise,\n resolve,\n reject,\n };\n }\n\n private rejectDeferredPromise(deferredPromise: DeferredConnectionPromise, err: Error): void {\n // Reject this deferred promise with the given error\n deferredPromise.reject(err);\n\n // If the currently tracked deferred promise is this deferred promise, remove it from the client\n if (this.deferredConnectionPromise === deferredPromise) {\n this.deferredConnectionPromise = undefined;\n }\n }\n\n private cleanupSocket(socket: Socket): void {\n if (!socket.destroyed) {\n socket.destroy();\n }\n if (socket === this.socket) {\n this.socket = undefined;\n }\n }\n\n async send(msg: Hl7Message): Promise<void> {\n return (await this.connect()).send(msg);\n }\n\n async sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message> {\n return (await this.connect()).sendAndWait(msg, options);\n }\n\n async close(): Promise<void> {\n if (this.deferredConnectionPromise) {\n this.rejectDeferredPromise(this.deferredConnectionPromise, new Error('Client closed while connecting'));\n }\n\n // Close established connection if it exists\n if (this.connection) {\n const connection = this.connection;\n delete this.connection;\n await connection.close();\n }\n // Close the socket if it exists\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.destroy();\n this.socket = undefined;\n }\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message, OperationOutcomeError, sleep, validationError } from '@medplum/core';\nimport iconv from 'iconv-lite';\nimport net from 'node:net';\nimport { Hl7Base } from './base';\nimport { CR, FS, VT } from './constants';\nimport { Hl7CloseEvent, Hl7ErrorEvent, Hl7MessageEvent } from './events';\n\n// iconv-lite docs have great examples and explanations for how to use Buffers with iconv-lite:\n// See: https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding\n\nexport type Hl7MessageQueueItem = {\n message: Hl7Message;\n resolve: (reply: Hl7Message) => void;\n reject: (err: Error) => void;\n returnAck: ReturnAckCategory;\n timer?: NodeJS.Timeout;\n};\n\nexport const ReturnAckCategory = {\n /** The first ACK message received is the one returned */\n FIRST: 'first',\n /** Only return upon receiving a positive application-level ACK (AA), or if a commit-level error occurred */\n APPLICATION: 'application',\n} as const;\nexport type ReturnAckCategory = (typeof ReturnAckCategory)[keyof typeof ReturnAckCategory];\n\nexport interface SendAndWaitOptions {\n /** The ACK-level that the Promise should resolve on. The default is `ReturnAckCategory.ANY` (returns on the first ACK of any type). */\n returnAck?: ReturnAckCategory;\n /** The amount of milliseconds to wait before timing out when waiting for the response to a message. */\n timeoutMs?: number;\n}\n\nexport interface Hl7ConnectionOptions {\n messagesPerMin?: number;\n}\n\nexport const DEFAULT_ENCODING = 'utf-8';\nconst ONE_MINUTE = 60 * 1000;\n\nexport class Hl7Connection extends Hl7Base {\n readonly socket: net.Socket;\n encoding: string;\n enhancedMode: boolean;\n private messagesPerMin: number | undefined = undefined;\n private chunks: Buffer[] = [];\n private readonly pendingMessages: Map<string, Hl7MessageQueueItem> = new Map<string, Hl7MessageQueueItem>();\n private readonly responseQueue: Hl7MessageEvent[] = [];\n private lastMessageDispatchedTime = 0;\n private responseQueueProcessing = false;\n\n constructor(\n socket: net.Socket,\n encoding: string = DEFAULT_ENCODING,\n enhancedMode = false,\n options: Hl7ConnectionOptions = {}\n ) {\n super();\n\n this.socket = socket;\n this.encoding = encoding;\n this.enhancedMode = enhancedMode;\n this.messagesPerMin = options.messagesPerMin;\n\n socket.on('data', (data: Buffer) => {\n try {\n this.appendData(data);\n if (data.at(-2) === FS && data.at(-1) === CR) {\n const buffer = Buffer.concat(this.chunks);\n const contentBuffer = buffer.subarray(1, buffer.length - 2);\n const contentString = iconv.decode(contentBuffer, this.encoding);\n const message = Hl7Message.parse(contentString);\n this.responseQueue.push(new Hl7MessageEvent(this, message));\n this.resetBuffer();\n this.processResponseQueue().catch((err) => {\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n }\n } catch (err) {\n this.dispatchEvent(new Hl7ErrorEvent(err as Error));\n }\n });\n\n socket.on('error', (err) => {\n this.resetBuffer();\n this.dispatchEvent(new Hl7ErrorEvent(err));\n });\n\n // The difference between \"end\" and \"close\", is that \"end\" is only emitted on half-close from the other side\n // If the connection from the other side does not close gracefully, but instead we destroy the socket, then the Hl7Connection will not emit close\n // if we listen only for \"end\"; \"close\" is always emitted, whether the close is graceful or forceful\n socket.on('close', () => {\n this.dispatchEvent(new Hl7CloseEvent());\n });\n\n this.addEventListener('message', (event) => {\n if (this.enhancedMode) {\n this.send(event.message.buildAck({ ackCode: 'CA' }));\n }\n const origMsgCtrlId = event.message.getSegment('MSA')?.getField(2)?.toString();\n // If there is no message control ID, just return\n if (!origMsgCtrlId) {\n return;\n }\n const queueItem = this.pendingMessages.get(origMsgCtrlId);\n if (!queueItem) {\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'not-found',\n details: {\n text: 'Response received for unknown message control ID',\n },\n diagnostics: `Received ACK for message control ID '${origMsgCtrlId}' but there was no pending message with this control ID`,\n },\n ],\n })\n )\n );\n return;\n }\n // Check the ACK type we should return on\n const ackCode = event.message.getSegment('MSA')?.getField(1)?.toString()?.toUpperCase();\n if (!ackCode) {\n return;\n }\n\n // Two modes:\n // Application-level or first ACK\n\n // First should always return on any ACK message, this is the default\n // The exception is APPLICATION, which should not resolve when the ACK is a CA, but should resolve on all other ACK types\n // On CA, we return early\n if (queueItem.returnAck === ReturnAckCategory.APPLICATION && ackCode === 'CA') {\n return;\n }\n\n // Resolve the promise if there is one pending for this message and we didn't exit already because the ACK type matches\n queueItem.resolve(event.message);\n this.pendingMessages.delete(origMsgCtrlId);\n });\n }\n\n /** @returns A boolean representing whether the socket attached to this Hl7Connection has emitted the close event already or not. */\n isClosed(): boolean {\n return this.socket.closed;\n }\n\n private sendImpl(reply: Hl7Message): void {\n const replyString = reply.toString();\n const replyBuffer = iconv.encode(replyString, this.encoding);\n const outputBuffer = Buffer.alloc(replyBuffer.length + 3);\n outputBuffer.writeInt8(VT, 0);\n replyBuffer.copy(outputBuffer, 1);\n outputBuffer.writeInt8(FS, replyBuffer.length + 1);\n outputBuffer.writeInt8(CR, replyBuffer.length + 2);\n this.socket.write(outputBuffer);\n }\n\n private async processResponseQueue(): Promise<void> {\n if (this.responseQueueProcessing) {\n return;\n }\n\n this.responseQueueProcessing = true;\n while (this.responseQueue.length) {\n if (this.messagesPerMin) {\n const millisBetweenMsgs = ONE_MINUTE / (this.messagesPerMin as number);\n const elapsedMillis = Date.now() - this.lastMessageDispatchedTime;\n if (millisBetweenMsgs > elapsedMillis) {\n await sleep(millisBetweenMsgs - elapsedMillis);\n }\n }\n const messageEvent = this.responseQueue.shift() as Hl7MessageEvent;\n if (messageEvent) {\n this.dispatchEvent(messageEvent);\n }\n this.lastMessageDispatchedTime = Date.now();\n }\n this.responseQueueProcessing = false;\n }\n\n send(reply: Hl7Message): void {\n this.sendImpl(reply);\n }\n\n async sendAndWait(msg: Hl7Message, options?: SendAndWaitOptions): Promise<Hl7Message> {\n return new Promise<Hl7Message>((resolve, reject) => {\n const msgCtrlId = msg.getSegment('MSH')?.getField(10)?.toString();\n if (!msgCtrlId) {\n reject(new OperationOutcomeError(validationError('Required field missing: MSH.10')));\n return;\n }\n\n let timer: NodeJS.Timeout | undefined;\n\n if (options?.timeoutMs) {\n timer = setTimeout(() => {\n this.pendingMessages.delete(msgCtrlId);\n reject(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'timeout',\n details: {\n text: 'Client timeout',\n },\n diagnostics: `Request timed out after waiting ${options.timeoutMs} milliseconds for response`,\n },\n ],\n })\n );\n }, options.timeoutMs);\n }\n\n this.pendingMessages.set(msgCtrlId, {\n message: msg,\n resolve,\n reject,\n returnAck: options?.returnAck ?? ReturnAckCategory.FIRST,\n timer,\n });\n this.sendImpl(msg);\n });\n }\n\n async close(): Promise<void> {\n // If we have already received the close event, then we can just return immediately\n if (this.isClosed()) {\n return Promise.resolve();\n }\n this.socket.end();\n this.socket.destroy();\n // Before clearing out messages, we should propagate a message to the consumer that we are closing the connection while some messages were still pending a response\n if (this.pendingMessages.size) {\n for (const queueItem of this.pendingMessages.values()) {\n if (queueItem.timer) {\n clearTimeout(queueItem.timer);\n }\n queueItem.reject(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'incomplete',\n details: {\n text: 'Message was still pending when connection closed',\n },\n },\n ],\n })\n );\n }\n this.dispatchEvent(\n new Hl7ErrorEvent(\n new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'warning',\n code: 'incomplete',\n details: {\n text: 'Messages were still pending when connection closed',\n },\n diagnostics: `Hl7Connection closed while ${this.pendingMessages.size} messages were pending`,\n },\n ],\n })\n )\n );\n // Clear out any pending messages\n this.pendingMessages.clear();\n }\n return new Promise((resolve) => {\n // Register a temporary listener to help resolve the promise once close has been emitted\n this.socket.once('close', resolve);\n });\n }\n\n private appendData(data: Buffer): void {\n this.chunks.push(data);\n }\n\n private resetBuffer(): void {\n this.chunks = [];\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding ?? DEFAULT_ENCODING;\n }\n\n getEncoding(): string {\n return this.encoding;\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setMessagesPerMin(messagesPerMin: number | undefined): void {\n this.messagesPerMin = messagesPerMin;\n }\n\n getMessagesPerMin(): number | undefined {\n return this.messagesPerMin;\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * VT (Vertical Tab) character.\n *\n * In HL7 messages, this character is used to indicate the start of a message.\n */\nexport const VT = 0x0b;\n\n/**\n * CR (Carriage Return) character.\n *\n * In HL7 messages, this character is used to indicate the end of a message.\n */\nexport const CR = 0x0d;\n\n/**\n * FS (File Separator) character.\n *\n * In HL7 messages, this character is used to separate fields.\n */\nexport const FS = 0x1c;\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { Hl7Message } from '@medplum/core';\nimport { Hl7Connection } from './connection';\n\nexport class Hl7MessageEvent extends Event {\n readonly connection: Hl7Connection;\n readonly message: Hl7Message;\n\n constructor(connection: Hl7Connection, message: Hl7Message) {\n super('message');\n this.connection = connection;\n this.message = message;\n }\n}\n\nexport class Hl7ErrorEvent extends Event {\n readonly error: Error;\n\n constructor(error: Error) {\n super('error');\n this.error = error;\n }\n}\n\nexport class Hl7CloseEvent extends Event {\n constructor() {\n super('close');\n }\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { sleep } from '@medplum/core';\nimport net from 'node:net';\nimport { Hl7Connection, Hl7ConnectionOptions } from './connection';\n\nexport class Hl7Server {\n readonly handler: (connection: Hl7Connection) => void;\n server?: net.Server;\n private encoding: string | undefined = undefined;\n private enhancedMode = false;\n private messagesPerMin: number | undefined = undefined;\n\n constructor(handler: (connection: Hl7Connection) => void) {\n this.handler = handler;\n }\n\n start(port: number, encoding?: string, enhancedMode?: boolean, options?: Hl7ConnectionOptions): void {\n if (encoding) {\n this.setEncoding(encoding);\n }\n if (enhancedMode !== undefined) {\n this.setEnhancedMode(enhancedMode);\n }\n if (options?.messagesPerMin !== undefined) {\n this.setMessagesPerMin(options.messagesPerMin);\n }\n\n const server = net.createServer((socket) => {\n const connection = new Hl7Connection(socket, this.encoding, this.enhancedMode, {\n messagesPerMin: this.messagesPerMin,\n });\n this.handler(connection);\n });\n\n // Node errors have a code\n const errorListener = async (e: Error & { code?: string }): Promise<void> => {\n if (e?.code === 'EADDRINUSE') {\n await sleep(50);\n server.close();\n server.listen(port);\n }\n };\n server.on('error', errorListener);\n\n server.once('listening', () => {\n server.off('error', errorListener);\n });\n\n server.listen(port);\n this.server = server;\n }\n\n async stop(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (!this.server) {\n reject(new Error('Stop was called but there is no server running'));\n return;\n }\n this.server.close((err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n this.server = undefined;\n });\n }\n\n setEnhancedMode(enhancedMode: boolean): void {\n this.enhancedMode = enhancedMode;\n }\n\n getEnhancedMode(): boolean {\n return this.enhancedMode;\n }\n\n setEncoding(encoding: string | undefined): void {\n this.encoding = encoding;\n }\n\n getEncoding(): string | undefined {\n return this.encoding;\n }\n\n setMessagesPerMin(messagesPerMin: number | undefined): void {\n this.messagesPerMin = messagesPerMin;\n }\n\n getMessagesPerMin(): number | undefined {\n return this.messagesPerMin;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["Hl7Base", "type", "listener", "options", "assert", "connect", "Hl7Message", "OperationOutcomeError", "sleep", "validationError", "iconv", "VT", "CR", "FS", "Hl7MessageEvent", "connection", "message", "Hl7ErrorEvent", "error", "Hl7CloseEvent", "ReturnAckCategory", "DEFAULT_ENCODING", "ONE_MINUTE", "Hl7Connection", "Hl7Base", "socket", "encoding", "enhancedMode", "options", "data", "buffer", "contentBuffer", "contentString", "iconv", "message", "Hl7Message", "Hl7MessageEvent", "err", "Hl7ErrorEvent", "Hl7CloseEvent", "event", "origMsgCtrlId", "queueItem", "OperationOutcomeError", "ackCode", "reply", "replyString", "replyBuffer", "outputBuffer", "millisBetweenMsgs", "elapsedMillis", "sleep", "messageEvent", "msg", "resolve", "reject", "msgCtrlId", "validationError", "timer", "messagesPerMin", "Hl7Client", "Hl7Base", "options", "deferredPromise", "connect", "assert", "socket", "timeoutListener", "error", "connectListener", "connection", "Hl7Connection", "errorListener", "err", "closeListener", "Hl7CloseEvent", "event", "Hl7ErrorEvent", "resolve", "reject", "_resolve", "_reject", "msg", "sleep", "net", "Hl7Server", "handler", "port", "encoding", "enhancedMode", "options", "server", "net", "socket", "connection", "Hl7Connection", "errorListener", "e", "sleep", "resolve", "reject", "err", "messagesPerMin"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medplum/hl7",
3
- "version": "4.3.9",
3
+ "version": "4.3.11",
4
4
  "description": "Medplum HL7 Utilities",
5
5
  "keywords": [
6
6
  "medplum",
@@ -55,11 +55,11 @@
55
55
  "test": "jest"
56
56
  },
57
57
  "dependencies": {
58
- "@medplum/core": "4.3.9",
58
+ "@medplum/core": "4.3.11",
59
59
  "iconv-lite": "0.6.3"
60
60
  },
61
61
  "devDependencies": {
62
- "@medplum/fhirtypes": "4.3.9"
62
+ "@medplum/fhirtypes": "4.3.11"
63
63
  },
64
64
  "engines": {
65
65
  "node": "^20.19.0 || >=22.12.0"