diff --git a/src/fieldRepetition.ts b/src/fieldRepetition.ts index f36b069..2751518 100644 --- a/src/fieldRepetition.ts +++ b/src/fieldRepetition.ts @@ -1,9 +1,9 @@ -import {Component} from "./component"; -import {Delimiters} from "./decorators/enum/delimiters"; -import {Node} from "./decorators/interfaces/node" -import {HL7FatalError} from "./exception"; -import {NodeBase} from "./nodeBase"; -import {ValueNode} from "./valueNode"; +import {Component} from "./component.js"; +import {Delimiters} from "./decorators/enum/delimiters.js"; +import {Node} from "./decorators/interfaces/node.js" +import {HL7FatalError} from "./exception.js"; +import {NodeBase} from "./nodeBase.js"; +import {ValueNode} from "./valueNode.js"; /** * Create a Field Repetition in an HL7 message segment diff --git a/src/index.ts b/src/index.ts index 188f6a2..cf8b664 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,12 +10,13 @@ import { Message } from './message.js' import { NodeBase } from './nodeBase.js' import { Parser, ParserPlan } from './parser.js' import { Segment } from './segment.js' +import { SegmentList } from './segmentList.js' import { HL7_2_7 } from './specification/2.7.js' import { HL7_SPEC, HL7_SPEC_BASE } from './specification/specification.js' import { SubComponent } from './subComponent.js' export default Client -export { Client, Listener, Parser, ParserPlan, Message, Delimiters, Segment, Component, SubComponent, Field, FieldRepetition, EmptyNode, NodeBase, Node } +export { Client, Listener, Parser, ParserPlan, Message, Delimiters, Segment, SegmentList, Component, SubComponent, Field, FieldRepetition, EmptyNode, NodeBase, Node } /** HL7 Specs **/ export type { MSH } from './specification/specification.js' diff --git a/src/message.ts b/src/message.ts index 279ea1b..d57ac7c 100644 --- a/src/message.ts +++ b/src/message.ts @@ -1,31 +1,39 @@ -// @ts-nocheck - -import {HL7FatalError} from "./exception"; -import {NodeBase} from "./nodeBase"; -import {Segment} from "./segment"; +import {HL7FatalError} from "./exception.js"; +import {NodeBase} from "./nodeBase.js"; +import {Segment} from "./segment.js"; +import {SegmentList} from "./segmentList.js"; import * as Util from "./utils.js"; -import { Delimiters } from './decorators/enum/delimiters' +import { Delimiters } from './decorators/enum/delimiters.js' import { ClientBuilderOptions, normalizedClientBuilderOptions } from './normalize.js' -import { Node } from "./decorators/interfaces/node"; +import { Node } from "./decorators/interfaces/node.js"; /** * Message Class * @since 1.0.0 + * @extends NodeBase */ export class Message extends NodeBase { + /** @internal */ + private readonly _delimiters: string + /** @internal */ private readonly _opt: ReturnType - - private _delimiters: string - private _matchUnescape: RegExp; - private _matchEscape: RegExp; + /** @internal */ + private readonly _matchEscape: RegExp; + /** @internal */ + private readonly _matchUnescape: RegExp; + /** @internal */ private static _defaultDelimiters = "\r|^~\\&"; + /** @internal */ private static _defaultMatchUnescape = Message._makeMatchUnescape(Message._defaultDelimiters); + /** @internal */ private static _defaultMatchEscape = Message._makeMatchEscape(Message._defaultDelimiters); /** - * @param props Override default encoding characters. (@default |^~\\&) + * Build the Message or Parse It + * @since 1.0.0 + * @param props {@link ClientBuilderOptions} * @example * If you are processing a full message, do this: * @@ -38,7 +46,6 @@ export class Message extends NodeBase { * * which then you add segments with fields and values for your Hl7 message. * - * @since 1.0.0 */ constructor (props?: ClientBuilderOptions) { let opt = normalizedClientBuilderOptions(props) @@ -58,15 +65,13 @@ export class Message extends NodeBase { if (opt.text == "" && this._opt.specification.checkMSH(this._opt.mshHeader)) { - // set header this.set('MSH.1', `${this._opt.separatorField}`) this.set('MSH.2', `${this._opt.separatorComponent}${this._opt.separatorRepetition}${this._opt.separatorEscape}${this._opt.separatorSubComponent}`) - this.set('MSH.7', this.createDate()) + this.set('MSH.7', Util.createData(new Date())) this.set('MSH.9.1', this._opt.mshHeader?.msh_9.msh_9_1) this.set('MSH.9.2', this._opt.mshHeader?.msh_9.msh_9_2) - this.set('MSH.9.3', this._opt.mshHeader?.msh_9.msh_9_3) + this.set('MSH.9.3', `${this._opt.mshHeader?.msh_9.msh_9_1}_${this._opt.mshHeader?.msh_9.msh_9_2}`) this.set('MSH.10', this._opt.mshHeader?.msh_10) - // @todo Add MSH.11 this.set('MSH.12', this._opt.specification.name) } else { @@ -82,7 +87,6 @@ export class Message extends NodeBase { * @private */ private static _makeMatchEscape(delimiters: string): RegExp { - let sequences = [ Util.escapeForRegExp(delimiters[Delimiters.Escape]), Util.escapeForRegExp(delimiters[Delimiters.Field]), @@ -90,7 +94,6 @@ export class Message extends NodeBase { Util.escapeForRegExp(delimiters[Delimiters.Component]), Util.escapeForRegExp(delimiters[Delimiters.SubComponent]), ]; - return new RegExp(sequences.join("|"), "g"); } @@ -106,11 +109,19 @@ export class Message extends NodeBase { return new RegExp([matchEscape,"[^", matchEscape, "]*", matchEscape].join(""), "g"); } + /** + * Get Delimiters + * @since 1.0.0 + */ get delimiters(): string { - return this._delimiters; } + /** + * Unescape Text + * @since 1.0.0 + * @param text + */ unescape(text: string): string { if (text == null) { throw new HL7FatalError(500, "text must be passed in unescape function.") @@ -150,6 +161,11 @@ export class Message extends NodeBase { }); } + /** + * Escape String + * @since 1.0.0 + * @param text + */ escape(text: string): string { if (text == null) { throw new HL7FatalError(500, "text must be passed in escape function.") @@ -186,8 +202,26 @@ export class Message extends NodeBase { }); } + /** + * Add a new segment to a message. + * @since 1.0.0 + * @description Creating a new segment adds an empty segment to the message. + * It could be blank, or it could have values added into it. + * @param path + * @example + * + * const message = new Message({. the correct options here .}) + * + * const segment = message.addSegment('PV1') + * segment.set('PV1.1', 'Value Here'); + * + * // When doing this, it overall adds it to the 'message' object + * // when your output is the final result. + * + * const finalMessage = message.toString(); + * + */ addSegment(path: string): Segment { - if (!path) { throw new Error("Missing segment path."); } @@ -200,28 +234,44 @@ export class Message extends NodeBase { return this.addChild(preparedPath[0]); } + /** + * Read a path of a message. + * @description Could return {@link SegmentList} + * @since 1.0.0 + * @param path + */ read(path: string[]): Node { let segmentName = path.shift(); - if(path.length == 0) { + if (path.length == 0) { // only the segment name was in the path so return a SegmentList let segments = this.children.filter(x => (x).name == segmentName); if(segments.length > 0) { - return new SegmentList(this, segments); + return new SegmentList(this, segments) as Node; + } + } else { + if (typeof segmentName === 'undefined') { + throw new Error("We have an error Huston.") } - } - else { let segment = this._getFirstSegment(segmentName); - if(segment) { - return segment.read(path); + if (segment) { + return segment.read(path) as Node; } } - - return null; + throw new Error("Failure is not an option.") } + /** + * Write Core of the Message + * @since 1.0.0 + * @param path + * @param value + * @protected + */ protected writeCore(path: string[], value: string): Node { - let segmentName = path.shift(); + if (typeof segmentName == 'undefined') { + throw new Error("Danger, Will Robinson") + } let index = this._getFirstSegmentIndex(segmentName); if(index === undefined) { index = this.children.length; @@ -229,20 +279,29 @@ export class Message extends NodeBase { return this.writeAtIndex(path, value, index, segmentName); } - protected createChild(text: string, index: number): Node { - - // make sure to remove any \n that might follow the \r + /** + * Create a new child of a message which is a segment. + * @since + * @see {@link Segment} + * @param text Segment string. Must be 3 characters long. + * @param _index Not used to create a segment. + * @protected + */ + protected createChild(text: string, _index: number): Node { return new Segment(this, text.trim()); } + /** + * Path Core + * @since 1.0.0 + * @protected + */ protected pathCore(): string[] { - - // the message has an empty path return []; } + /** @internal */ private _getFirstSegment(name: string): Segment { - let children = this.children; for (let i = 0, l = children.length; i < l; i++) { let segment = children[i]; @@ -250,9 +309,11 @@ export class Message extends NodeBase { return segment; } } + throw new Error("We have a problem.") } - private _getFirstSegmentIndex(name: string): number { + /** @internal */ + private _getFirstSegmentIndex(name: string): number { let children = this.children; for (let i = 0, l = children.length; i < l; i++) { let segment = children[i]; @@ -260,6 +321,7 @@ export class Message extends NodeBase { return i; } } + return 0 } } diff --git a/src/nodeBase.ts b/src/nodeBase.ts index 3087d02..f9dff3b 100644 --- a/src/nodeBase.ts +++ b/src/nodeBase.ts @@ -1,17 +1,21 @@ -import {HL7FatalError} from "./exception"; -import {isNumber, isString} from "./utils"; -import * as Util from "./utils"; -import {Delimiters} from "./decorators/enum/delimiters"; -import { Node } from "./decorators/interfaces/node"; -import EmptyNode from "./emptyNode"; -import {Message} from "./message"; - -export default class NodeBase implements Node { - - protected parent: NodeBase; +import {EmptyNode} from "./emptyNode.js"; +import {HL7FatalError} from "./exception.js"; +import * as Util from "./utils.js"; +import {Delimiters} from "./decorators/enum/delimiters.js"; +import { Node } from "./decorators/interfaces/node.js"; +import {Message} from "./message.js"; + +/** + * Node Base + * @since 1.0.0 + * @internal + */ +export class NodeBase implements Node { + + protected parent: NodeBase | null; private _name: string; - private _text: string | null; + private _text: string; private readonly _delimiter: Delimiters | undefined; private _delimiterText: string; private _children: Node[]; @@ -19,7 +23,7 @@ export default class NodeBase implements Node { private _path: string[]; private _dirty: boolean; - constructor(parent: NodeBase, text: string | null = null, delimiter: Delimiters | undefined = undefined) { + constructor(parent: NodeBase | null, text: string = "", delimiter: Delimiters | undefined = undefined) { this.parent = parent @@ -71,8 +75,7 @@ export default class NodeBase implements Node { } return this; - } - else if (isNumber(path)) { + } else if (Util.isNumber(path)) { if (Array.isArray(value)) { /** If the value is an array, write each item in the array using the index of the item as an additional @@ -82,8 +85,7 @@ export default class NodeBase implements Node { child.set(i, value[i]); } return this; - } - else { + } else { this.setChild(this.createChild(this.prepareValue(value), path), path); } @@ -158,7 +160,7 @@ export default class NodeBase implements Node { } if (typeof path === "number") { return this.setChild(this.createChild("", path), path); - } else if (isString(path)) { + } else if (Util.isString(path)) { return this.write(this.preparePath(path), ""); } throw new HL7FatalError(500, "There seems to be a problem.") @@ -166,12 +168,12 @@ export default class NodeBase implements Node { protected preparePath(path: string): string[] { let parts = path.split("."); - if(parts[0] == "") { + if (parts[0] == "") { parts.shift(); parts = this.path.concat(parts); } - if(!this._isSubPath(parts)) { + if (!this._isSubPath(parts)) { throw new Error("'" + parts.toString() + "' is not a sub-path of '" + this.path.toString() + "'"); } @@ -183,7 +185,9 @@ export default class NodeBase implements Node { if(value == null) return ""; if(typeof value === "string") { - return this.message.escape(value); + if (typeof this.message !== 'undefined') { + return this.message.escape(value); + } } if(typeof value === "number") { @@ -201,10 +205,10 @@ export default class NodeBase implements Node { return value.toString(); } - protected get message(): Message { - + protected get message(): Message | undefined { if(this._message) return this._message; - return this._message = this.parent ? this.parent.message : this; + this._message = this.parent ? this.parent.message : this; + return this._message } read(_path: string[]): Node { @@ -224,15 +228,13 @@ export default class NodeBase implements Node { let child: Node; - if(path.length == 0) { + if (path.length == 0) { child = this.createChild(value || emptyValue, index); - } - else { + } else { // check if we already have a child at that index - if(index < this.children.length) { + if (index < this.children.length) { child = this.children[index]; - } - else { + } else { // if not, create a new one child = this.createChild(emptyValue, index); } @@ -240,7 +242,7 @@ export default class NodeBase implements Node { this.setChild(child, index); - if(path.length != 0) { + if (path.length != 0) { return child.write(path, value); } @@ -248,7 +250,6 @@ export default class NodeBase implements Node { } get path(): string[] { - if(this._path) return this._path; return this._path = this.pathCore(); } @@ -264,12 +265,17 @@ export default class NodeBase implements Node { if (typeof this._delimiter == 'undefined') { throw new HL7FatalError(500, "this._delimiter is somehow undefined.") } + + if (typeof this.message == "undefined") { + throw new Error("this.message is not defined.") + } + this._delimiterText = this.message.delimiters[this._delimiter]; return this._delimiterText } protected get children(): Node[] { - if (this._text !== null && !this._children) { + if (this._text !== '' && this._children.length === 0) { let parts = this._text.split(this.delimiter); let children = new Array(parts.length); for (let i = 0, l = parts.length; i < l; i++) { @@ -280,9 +286,9 @@ export default class NodeBase implements Node { return this._children; } - protected addChild(text: string): Node { + protected addChild(text: string): T { this.setDirty(); - let child = this.createChild(text, this.children.length); + let child = this.createChild(text, this.children.length); this.children.push(child); return child; } diff --git a/src/normalize.ts b/src/normalize.ts index a567aa7..b1e6710 100644 --- a/src/normalize.ts +++ b/src/normalize.ts @@ -1,7 +1,7 @@ import { TcpSocketConnectOpts } from 'node:net' import type { ConnectionOptions as TLSOptions } from 'node:tls' import { HL7_2_7 } from './specification/2.7.js' -import {MSH} from "./specification/specification"; +import {MSH} from "./specification/specification.js"; import * as Util from './utils.js' const DEFAULT_CLIENT_OPTS = { @@ -113,7 +113,7 @@ export interface ClientBuilderOptions { specification?: any /** The HL7 string that we are going to parse. * @default "" */ - text: string; + text?: string; } export interface ClientBuilderBatchOptions extends ClientBuilderOptions { @@ -187,9 +187,9 @@ export function normalizeClientOptions (raw?: ClientOptions): ValidatedClientOpt export function normalizedClientBuilderOptions (raw?: ClientBuilderOptions): ClientBuilderOptions { const props = { ...DEFAULT_CLIENT_BUILDER_OPTS, ...raw } - if (typeof props.mshHeader == 'undefined' && props.text !== '') { + if (typeof props.mshHeader == 'undefined' && props.text == '') { throw new Error('mshHeader must be set if no HL7 message is being passed.') - } else if (props.text.slice(0, 3) !== "MSH") { + } else if (typeof props.mshHeader == 'undefined' && props.text.slice(0, 3) !== "MSH") { throw new Error("text must begin with the MSH segment."); }