import EventEmitter from "node:events"; import { Socket, TcpSocketConnectOpts } from "node:net"; import { HL7Version, InboundResponse, MLLPCodec, MLLPCodec as MLLPCodec$1, Message } from "node-hl7-client"; import { ConnectionOptions } from "node:tls"; //#region src/utils/constants.d.ts /** @internal */ type validMSA1 = "AA" | "AE" | "AR" | "CA" | "CE" | "CR"; //#endregion //#region src/declaration/ISendRequest.d.ts /** * */ interface ISendRequest { /** */ getAckMessage: () => Message | undefined; /** */ getCodec: () => MLLPCodec$1; /** */ getSocket: () => Socket; /** Send a fully customized ACK message back to the client. */ sendCustomResponse: (message: Message | string, encoding?: BufferEncoding) => Promise; /** */ sendResponse: (type: validMSA1, encoding: BufferEncoding) => Promise; } //#endregion //#region src/utils/normalize.d.ts /** * @since 1.0.0 */ interface ListenerOptions { /** Accept any *known* HL7 version instead of pinning to a fixed set. Must be * set explicitly — omitting {@link version} alone is still an error. An * inbound message whose `MSH.12` is not a known HL7 version is still rejected * with an `AR` acknowledgement. Mutually exclusive with {@link version}. * @since 4.1.0 */ acceptAnyVersion?: boolean; /** Encoding of the messages we expect from the HL7 message. * @default "utf8" */ encoding?: BufferEncoding; /** Optional MSH segment overrides. See the readme for examples. * @since 2.5.0 */ mshOverrides?: Record string) | string>; /** Name of the Listener (e.g., IB_EPIC_ADT) * @default Randomized String */ name?: string; /** The network address to listen on expediently. * Must be set between 0 and 65,353 */ port: number; /** If you need to override the response class sent to the client, * you need to extend the base BaseSendResponse class from * **/ responseClass?: typeof BaseSendResponse; /** The HL7 version(s) this listener accepts — a single version or an * allow-list. Any inbound message whose `MSH.12` is not in the set is * rejected with an `AR` acknowledgement and the handler is not invoked. Each * value must be a known HL7 version (2.1, 2.2, 2.3, 2.3.1, 2.4, 2.5, 2.5.1, * 2.6, 2.7, 2.7.1, 2.8). * * Optional at the type level, but exactly one of `version` or * {@link acceptAnyVersion} must be provided. Mutually exclusive with * `acceptAnyVersion`. * @since 4.0.0 */ version?: HL7Version | HL7Version[]; } /** * Resolved server options (post-normalization). * @internal */ interface NormalizedServerOptions extends ServerOptions { bindAddress: string; encoding: BufferEncoding; ipv4: boolean; ipv6: boolean; /** Resolved value forwarded to `server.listen({ ipv6Only })`. `true` when * IPv6-only is requested, `false` when dual-stack listening is requested. * @since 4.0.0 */ ipv6Only: boolean; } /** * @since 1.0.0 */ interface ServerOptions { /** The network address to listen on. * * Defaults are picked automatically from `ipv4` / `ipv6`: * - IPv4 only (`ipv4: true, ipv6: false`, the default) → `"0.0.0.0"` * - IPv6 only (`ipv4: false, ipv6: true`) → `"::"` * - dual-stack (`ipv4: true, ipv6: true`) → `"::"` * * Pass an explicit IPv4 (e.g. `"10.1.2.3"`) or IPv6 (e.g. `"fe80::1"`) * literal — or `"localhost"` — when the host has multiple addresses * and you need to terminate on a specific one. * @default depends on ipv4/ipv6 (see above) */ bindAddress?: string; /** Encoding of the messages we expect from the HL7 message. * @default "utf8" */ encoding?: BufferEncoding; /** Accept IPv4 connections. * * Combine with {@link ipv6}` = true` to opt into dual-stack listening * (IPv4 + IPv6 on a single socket). Setting `ipv4: false` listens on * IPv6 only. * @default true */ ipv4?: boolean; /** Accept IPv6 connections. * * Combine with {@link ipv4}` = true` (also the default) to opt into * dual-stack listening. Setting `ipv6: true` alone listens on IPv6 only. * @default false */ ipv6?: boolean; /** Additional options when creating the TCP socket with net.connect(). */ socket?: TcpSocketConnectOpts; /** Enable TLS, or set TLS specific options like overriding the CA for * self-signed certificates. */ tls?: ConnectionOptions; } /** * @since 1.0.0 */ type ValidatedKeys = "port"; /** * @since 1.0.0 */ interface ValidatedOptions extends Pick, ValidatedKeys> { /** Resolved from {@link ListenerOptions.acceptAnyVersion}. When `true`, * `versions` is empty and any known HL7 version is accepted. */ acceptAnyVersion: boolean; mshOverrides?: Record string) | string>; name?: string; port: number; responseClass?: typeof BaseSendResponse; /** The resolved, deduped set of accepted versions. Empty when * `acceptAnyVersion` is `true`. */ versions: HL7Version[]; } /** @internal */ declare function normalizeListenerOptions(properties: ListenerOptions): ValidatedOptions; /** @internal */ declare function normalizeServerOptions(properties?: ServerOptions): NormalizedServerOptions; //#endregion //#region src/declaration/baseSendRequest.d.ts /** * base Send Response * @since 4.0.0 */ declare class BaseSendResponse extends EventEmitter implements ISendRequest { /** @internal */ protected _ack: Message | undefined; /** @internal */ protected readonly _codec: MLLPCodec$1; /** @internal */ protected readonly _message: Message; /** @internal */ protected readonly _mshOverrides: ListenerOptions["mshOverrides"]; /** @internal */ protected readonly _socket: Socket; constructor(socket: Socket, message: Message, mshOverrides?: ListenerOptions["mshOverrides"]); /** * Get the Ack Message * @since 2.2.0 * @remarks Get the acknowledged message that was sent to the client. * This could return undefined if accessed before sending the response */ getAckMessage(): Message | undefined; /** * Use the codec attached the to send request so you can send your custom message. You need to provide the client socket (@see getSocket()) * that you will need, the HL7 Message object as a string, and the encoding format you want. * * @example * * this._codec.sendMessage(socket, this.message.toString(), "utf8"); * */ getCodec(): MLLPCodec$1; /** * Get the socket used from the client which can be used to send a message back; * however, you should always use the Codec class (@see getCodec()) as it can handle * long messages to encode the message in a way that a remote side can * understand. * @since 4.0.0 * @return Socket */ getSocket(): Socket; /** * Send a fully customized acknowledgment back to the client. * @remarks Use this when the auto-generated ACK from {@link sendResponse} does * not match the format the receiving system expects (custom MSH/MSA fields, * extra segments, vendor-specific layouts, etc.). The provided message is * sent verbatim through the MLLP codec — no validation, swapping of * sender/receiver, or MSH overrides are applied. * * @since 4.0.0 * @param message The fully formed HL7 ACK to send. Accepts either a * {@link Message} instance or a raw HL7 string. * @param encoding Encoding to use when writing to the socket. * @example * ```ts * server.createInbound({ port, version: "2.7" }, async (req, res) => { * const ack = new Message({ * text: `MSH|^~\\&|MY_APP|MY_FAC|...|...|${createHL7Date(new Date())}||ACK|123|P|2.5\rMSA|AA|${req.getMessage().get("MSH.10").toString()}|All good|`, * }); * await res.sendCustomResponse(ack); * }); * ``` */ sendCustomResponse(message: Message | string, encoding?: BufferEncoding): Promise; /** * * @param _type * @param _encoding */ sendResponse(_type: validMSA1, _encoding?: BufferEncoding): Promise; /** @internal */ protected _validateMSA1(spec: string, type: validMSA1): void; } //#endregion //#region src/server/inboundRequest.d.ts /** * Inbound Request Props * @since 1.0.0 */ interface InboundRequestProps { /** The underlying socket of the connection that produced this message. * Available so handlers can read peer/local connection details (e.g. * `localAddress`, `localPort`, `remoteAddress`). * @since 4.0.0 */ socket?: Socket; type: string; } /** * Inbound Request * @since 1.0.0 */ declare class InboundRequest { /** @internal */ private readonly _fromType; /** @internal */ private readonly _message?; /** @internal */ private readonly _socket?; /** * @since 1.0.0 * @param message * @param props */ constructor(message: Message, properties: InboundRequestProps); /** ' * Get Stored Message * @since 1.0.0 */ getMessage(): Message; /** * Get the underlying socket associated with this inbound request. * @remarks Useful for advanced scenarios that need access to the connection * (for example reading `localAddress`, `localPort`, or `remoteAddress`). * Throws if the request was constructed without a socket reference. * @since 4.0.0 */ getSocket(): Socket; getType(): string; } //#endregion //#region src/server/sendResponse.d.ts /** * Send Response * @since 1.0.0 */ declare class SendResponse extends BaseSendResponse implements ISendRequest { /** * Send Response back to End User * @since 1.0.0 * @see {@link https://hl7-definition.caristix.com/v2/HL7v2.1/Tables/0008} * @param type * @param encoding * @example * If you are to confirm to the end user (client) that the message they sent was good and processed successfully. * you would send an "AA" style message (Application Accept). * Otherwise, send an "AR" (Application Reject) to tell the client the data was * not accepted/processed or send an "AE" * (Application Error) to tell the client your overall application had an error. * ```ts * const server = new Server({bindAddress: '0.0.0.0'}) * const IB_ADT = server.createInbound({port: LISTEN_PORT, version: '2.7'}, async (req, res) => { * const messageReq = req.getMessage() * await res.sendResponse("AA") * }) * * or * * const server = new Server({bindAddress: '0.0.0.0'}) * const IB_ADT = server.createInbound({port: LISTEN_PORT, version: '2.7'}, async (req, res) => { * const messageReq = req.getMessage() * await res.sendResponse("AR") * }) * * or * * const server = new Server({bindAddress: '0.0.0.0'}) * const IB_ADT = server.createInbound({port: LISTEN_PORT, version: '2.7'}, async (req, res) => { * const messageReq = req.getMessage() * await res.sendResponse("AE") * }) *``` * * "AE" (Application Error) will be automatically sent if there is a problem creating either an "AA" or "AR" * message from the original message sent because the original message structure sent wrong in the first place. * * If the specification of the Hl7 message you get from the client is 2.1, the value sent back can be "AA", "AR", "AE". * Anything above 2.1 and greater fields are valid "AA", "AR", "AE", "CA", "CR", and "CE". * If a CE is sent back with 2.1, the system will send an error in which * the server will have failed to respond properly back to the client. * */ sendResponse(type: validMSA1, encoding?: BufferEncoding): Promise; /** @internal */ private _createAckMessage; /** @internal */ private _createAEAckMessage; } //#endregion //#region src/server/server.d.ts /** * Server Class * @remarks Start Server listening on a network address. * {@link ServerOptions} Link to the options that can be passed into props. * @since 1.0.0 */ declare class Server extends EventEmitter { /** @internal */ _opt: ReturnType; /** * @since 1.0.0 * @param props {@link ServerOptions} * @example * * Non-TLS: * ``` * const server = new Server() * ``` * * TLS: * ``` * const server = new Server( * { * tls: * { * key: fs.readFileSync(path.join('certs/', 'server-key.pem')), // where your certs are * cert: fs.readFileSync(path.join('certs/', 'server-crt.pem')), // where your certs are * rejectUnauthorized: false * } * }) * ``` * */ constructor(properties?: ServerOptions); /** This creates an instance of a HL7 server. * @remarks You would specify your port and what it will do when it gets a message. * @since 1.0.0 * @example *``` * const server = new Server() * * const IB = server.createInbound({port: 3000, version: "2.7"}, async (req, res) => { * const messageReq = req.getMessage() * const messageRes = res.getAckMessage() * // do your code here * }) *``` * * */ createInbound(properties: ListenerOptions, callback: InboundHandler): Inbound; } //#endregion //#region src/server/inbound.d.ts interface IInbound extends EventEmitter { /** When the connection form the client is closed. We might have an error, we might not. */ on(name: "client.close", callback: (hasError: boolean) => void): this; /** When a connection from the client is made. */ on(name: "client.connect", callback: (socket: Socket) => void): this; /** When an error was sent by the connecting source. */ on(name: "client.error", callback: (error: any) => void): this; /** Something wrong happened during data parsing. */ on(name: "data.error", callback: (error: any) => void): this; /** The raw data received by this particular inbound connection. */ on(name: "data.raw", callback: (rawData: string) => void): this; /** When the socket itself has an error. */ on(name: "error", callback: (error: any) => void): this; /** When the socket is ready and listening on the port. */ on(name: "listen", callback: () => void): this; /** An HL7 response was sent */ on(name: "response.sent", callback: () => void): this; } /** * Inbound Handler * @remarks The handler that will handle the user parsing a received message by the client to the server. * @since 1.0.0 * @example * In this example, we are processing the results in an async handler. *```ts * const IB_ADT = server.createInbound({port: 3000, version: "2.7"}, async (req, res) => { * const messageReq = req.getMessage() * const messageRes = res.getAckMessage() * }) *``` */ type InboundHandler = (request: InboundRequest, res: BaseSendResponse | SendResponse) => void; /** * Inbound Listener Class * @since 1.0.0 */ declare class Inbound extends EventEmitter implements IInbound { /** @internal */ _main: Server; /** @internal */ _opt: ReturnType; /** @internal */ readonly stats: { /** Total message received to server. * @since 2.0.0 */ received: number; /** Total message parsed by the server. * @since 2.0.0 */ totalMessage: number; }; /** @internal */ private readonly _handler; /** @internal */ private _sendResponseClass; /** @internal */ private readonly _socket; /** @internal */ private readonly _sockets; /** * Build a Listener * @since 1.0.0 * @param server * @param props * @param handler */ constructor(server: Server, properties: ListenerOptions, handler: InboundHandler); /** Close Listener Instance. * This be called for each listener, but if the server instance is closed and shut down, this will also fire off. * @since 1.0.0 */ close(): Promise; /** @internal */ private _closeSocket; /** @internal */ private _handleMessages; /** Whether an inbound message's `MSH.12` is accepted by this listener. * When `acceptAnyVersion` is set, any known HL7 version passes; otherwise the * version must be a member of the resolved accepted set. * @internal */ private _isVersionAccepted; /** @internal */ private _listen; /** @internal */ private _onTcpClientConnected; /** Reject a message whose version is not accepted: send an `AR` * acknowledgement and surface the reason on the `data.error` event. * @internal */ private _rejectVersionMismatch; /** Build the `data.error` message for a rejected version, worded for the * listener's configuration (single version, allow-list, or accept-any). * @internal */ private _versionMismatchMessage; } //#endregion //#region src/utils/exception.d.ts /** Base Error Class * @since 1.0.0 */ declare class HL7Error extends Error { /** @internal */ code: number; /** @internal */ constructor(code: number, message: string); } /** Listener Error * @since 1.0.0 */ declare class HL7ListenerError extends Error { /** @internal */ name: string; } /** Server Error * @since 1.0.0 */ declare class HL7ServerError extends HL7Error { /** @internal */ name: string; constructor(message: string); } //#endregion export { HL7ListenerError, HL7ServerError, Inbound, type InboundHandler, InboundRequest, type InboundRequestProps, InboundResponse, type ListenerOptions, MLLPCodec, SendResponse, Server, Server as default, type ServerOptions, type validMSA1 };