Skip to main content
Version: v0.0.3

Class: Hl7Message

Parsed HL7 v2 message. Produced by parseHL7. Exposes the raw positional tree (rawSegments), delimiter metadata, warnings, and a typed traversal surface: get(path) for dot-paths, getAll(type) / segments(type) / allSegments() for wrapper-level iteration.

Remarks

The warnings array is frozen at the model boundary so downstream traversal and helpers cannot mutate parser output. The profile field is populated when a profile is passed, and is undefined otherwise. Segment/Field wrappers are cached per-message and invalidated wholesale by the mutation methods.

Example

import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
console.log(msg.get("PID.5.1")); // "Smith"
for (const obx of msg.segments("OBX")) {
console.log(obx.field(5).value);
}
for (const w of msg.warnings) console.warn(w.code);

Constructors

Constructor

new Hl7Message(init): Hl7Message

Internal

Construct a new Hl7Message. The constructor takes a plain init object and freezes the warnings array so callers cannot mutate parser output after handoff.

Parameters

init

Hl7MessageInit

Returns

Hl7Message

Properties

dateFormats

readonly dateFormats: readonly string[]

Merged dateFormats list: options.dateFormats ++ profile.dateFormats deduped first-occurrence per D-21. Empty array when neither source supplied any formats. Exposed publicly so helpers (msg.meta.timestamp) and advanced callers can introspect the active cascade.


encodingCharacters

readonly encodingCharacters: EncodingCharacters


profile

readonly profile: { lineage: readonly string[]; name: string; } | undefined


rawSegments

readonly rawSegments: readonly RawSegment[]

Raw positional tree produced by the parser. 1-indexed per HL7 convention (fields[0] is the segment-name / MSH separator placeholder slot). Use segments(type) / allSegments() for typed wrapper access: this field is exposed for advanced callers that need the raw tree directly.


version

readonly version: string


warnings

readonly warnings: readonly Hl7ParseWarning[]

Accessors

meta

Get Signature

get meta(): Meta

MSH-derived message metadata (type, controlId, timestamp, version, etc.). D-01: plain object. D-02: memoized: msg.meta === msg.meta across reads until mutation invalidates. D-03: always defined (MSH absence throws NO_MSH_SEGMENT at parse time).

Example
console.log(msg.meta.type); // "ADT^A01"
console.log(msg.meta.timestamp?.raw); // fidelity TS
console.log(msg.meta.controlId); // "MSG001"
Returns

Meta


patient

Get Signature

get patient(): Patient | undefined

PID-derived patient view, or undefined when no PID segment exists (D-04). D-02: memoized. HELPERS-07: never throws: absent fields surface as undefined on the returned Patient object.

Example
console.log(msg.patient?.mrn);
console.log(msg.patient?.fullName);
console.log(msg.patient?.dateOfBirth?.raw); // fidelity TS: e.g. "19800115"
Returns

Patient | undefined


structure

Get Signature

get structure(): MessageStructure

Structural-conformance summary for the common message types: a misroute/truncation safety net, NOT a conformance validator. Reports, per the message's recognized (MSH-9.1, MSH-9.2) type, which Required segment groups are present and which are entirely absent (missingGroups: the same set the parser flags as MISSING_EXPECTED_GROUP warnings). For an unmodelled type, recognized is false and missingGroups is empty. D-02: memoized.

Example
console.log(msg.structure.recognized); // true for ORU^R01, ADT^A01, …
console.log(msg.structure.missingGroups); // e.g. ["result"] if no OBR/OBX
Returns

MessageStructure


visit

Get Signature

get visit(): Visit | undefined

PV1-derived visit view, or undefined when no PV1 segment exists (HELPERS-03). D-02: memoized. HELPERS-07: never throws.

Example
console.log(msg.visit?.patientClass); // "I"
console.log(msg.visit?.admitDateTime?.raw); // fidelity TS
console.log(msg.visit?.attendingDoctor?.familyName);
Returns

Visit | undefined

Methods

addSegment()

addSegment(name, fields): this

Append a new segment to the end of the message. name must match /^(?:[A-Z]{3}|Z[A-Z0-9]{2})$/u: throws TypeError otherwise (D-19).

fields is interpreted in HL7 1-indexed terms: addSegment("NTE", [a, b, c]) produces a segment whose NTE-1 = a, NTE-2 = b, NTE-3 = c. The internal RawSegment.fields[0] name/separator placeholder is synthesized by this method.

Each entry may be a plain string (treated as a single-subcomponent single-component single-repetition field) or a full RawField object for advanced callers who need structured content.

Invalidates caches on return; warnings untouched (D-16).

Parameters

name

string

fields

readonly (string | RawField)[]

Returns

this

Example

msg.addSegment("NTE", ["", "note text"]);
msg.get("NTE.2"); // "note text"

allergies()

allergies(): readonly Allergy[]

Every AL1 as an Allergy in document order. D-05: returns [] when no AL1 present.

Returns

readonly Allergy[]

Example

for (const al of msg.allergies()) console.log(al.code?.text, al.severity);

allSegments()

allSegments(): readonly Segment[]

Iterate every Segment in document order (MSH first, then every subsequent segment). Cached per-message; same array reference and same Segment instances on repeat calls (D-11). Invalidated wholesale by the mutation methods.

Returns

readonly Segment[]

Example

for (const seg of msg.allSegments()) {
console.log(seg.type);
}

appointments()

appointments(): readonly Appointment[]

Every SCH of an SIU message as a typed Appointment, with the AIS/AIG/AIL/AIP resource segments that follow it grouped positionally under that SCH. Surfaces the placer/filler appointment ids, SCH-25 filler status (Table 0278), SCH-11 start/end timing, and the resource groups (service / general / location / personnel). D-05: returns [] when no SCH is present. D-06: not memoized. Never throws (HELPERS-07). Not a scheduling-workflow state machine: see the package known-limitations.

Returns

readonly Appointment[]

Example

for (const appt of msg.appointments()) {
console.log(appt.fillerAppointmentId, appt.fillerStatusCode?.identifier);
for (const r of appt.resources) console.log(r.kind, r.code?.identifier);
}

charges()

charges(): readonly Charge[]

Every FT1 of a DFT message as a typed Charge, one per FT1 in document order. Surfaces billing-critical fields (FT1-6 transaction type, FT1-7 code, FT1-11/12 extended/unit amount, FT1-19 diagnosis linkage) with no billing logic and no money-as-float: amounts are the verbatim CP wire text. D-05: returns [] when no FT1 is present. D-06: not memoized. Never throws (HELPERS-07).

Returns

readonly Charge[]

Example

for (const charge of msg.charges()) {
console.log(charge.transactionType, charge.transactionCode?.identifier);
console.log(charge.amountExtended); // verbatim, never a number
}

diagnoses()

diagnoses(): readonly Diagnosis[]

Every DG1 as a Diagnosis in document order. D-05: returns [] when no DG1 present.

Returns

readonly Diagnosis[]

Example

for (const dg of msg.diagnoses()) console.log(dg.code?.identifier);

documents()

documents(): readonly ClinicalDocument[]

Every TXA of an MDM message as a typed ClinicalDocument, with the OBX narrative body grouped positionally under that TXA. The completion status (TXA-17) and availability status (TXA-19) are surfaced as distinct fields and never conflated: a document can be available before it is authenticated, and reading a preliminary document as final is the harm. D-05: returns [] when no TXA is present. D-06: not memoized. Never throws (HELPERS-07).

Returns

readonly ClinicalDocument[]

Example

for (const doc of msg.documents()) {
console.log(doc.documentType, doc.completionStatus, doc.availabilityStatus);
for (const obx of doc.observations) console.log(obx.value); // narrative body
}

get()

get(path): string | undefined

Resolve a dot-path (e.g. PID.5.1, OBX[2].5, PID.3[0].1) to its decoded leaf string (unescaped once at parse: never re-unescaped on read). Returns undefined when the path doesn't resolve: never throws on missing path (MODEL-05). Throws TypeError on malformed path syntax (e.g. "pid.5", empty string).

Parameters

path

string

Returns

string | undefined

Example

const msg = parseHL7(raw);
msg.get("PID.5.1"); // "Smith"
msg.get("OBX[2].5"); // third OBX's 5th field
msg.get("NOT.9.9"); // undefined
msg.get("MSH.12"); // "2.5": HL7 version string

getAll()

getAll(segmentType): readonly Segment[]

Return every Segment of segmentType in document order. Returns [] (empty array, NEVER undefined) when no segment of that type exists (MODEL-02). Alias for segments(segmentType): shares the same cache.

Parameters

segmentType

string

Returns

readonly Segment[]

Example

for (const obx of msg.getAll("OBX")) {
console.log(obx.field(5).value);
}

identityEvents()

identityEvents(): readonly IdentityEvent[]

Every recognized ADT patient-identity event (merge / move / link / unlink / person add/update), with the MRG-sourced prior and PID/PV1-sourced surviving parties labelled by role and the spec-constant direction: "MRG_TO_PID" on merge/move events. Returns [] when the trigger event is not in the identity family. D-06: not memoized. Never throws; incomplete merge pairs surface a MERGE_MISSING_PRIOR_OR_SURVIVOR warning on the event.

Returns

readonly IdentityEvent[]

Example

for (const ev of msg.identityEvents()) {
if (ev.kind === "merge" && ev.prior && ev.surviving) {
// retire ev.prior.identifiers in favour of ev.surviving.identifiers
}
}

immunizations()

immunizations(): readonly Immunization[]

Every RXA of a VXU^V04 as a typed Immunization, with RXR (route/site) and OBX children grouped positionally under the RXA and orderControl from the preceding ORC of the VXU order group. D-05: returns [] when no RXA present. D-06: not memoized. The vaccine code carries its own provenance; the action code (RXA-21) is surfaced verbatim and recordOrigin (administered vs historical) is derived only from the well-known NIP001 RXA-9.1 codes: never guessed.

Returns

readonly Immunization[]

Example

for (const imm of msg.immunizations()) {
console.log(imm.vaccineCode?.identifier, imm.doseAmount, imm.recordOrigin);
console.log(imm.actionCode, imm.completionStatus);
}

insurance()

insurance(): readonly Insurance[]

Every IN1 as an Insurance entry with positional IN2/IN3 presence flags. D-05: returns [] when no IN1 present.

Returns

readonly Insurance[]

Example

for (const ins of msg.insurance()) console.log(ins.planId?.text);

medications()

medications(): readonly Medication[]

Every RXO/RXE/RXD/RXA as a typed Medication, with RXR (route) and RXC (component) segments grouped positionally under their parent. D-05: returns [] when no RX* parent present. D-06: not memoized. The give amount and give strength are surfaced separately and never reconciled.

Each medication also carries its TQ1 / legacy embedded-TQ (RXE-1) timings (repeat pattern verbatim, never resolved to a schedule).

Returns

readonly Medication[]

Example

for (const med of msg.medications()) {
console.log(med.context, med.giveCode?.identifier, med.giveCode?.nameOfCodingSystem);
console.log(med.amount?.minimum, med.strength?.value);
for (const t of med.timings) console.log(t.repeatPattern?.code, t.totalOccurrences);
}

nextOfKin()

nextOfKin(): readonly NextOfKin[]

Every NK1 as a NextOfKin entry in document order. D-05: returns [] when no NK1 present.

Returns

readonly NextOfKin[]

Example

for (const nk of msg.nextOfKin()) {
console.log(nk.name?.familyName, nk.relationship?.text);
}

notes()

notes(): readonly string[]

Message-level NTE notes: every NTE segment with no recognized preceding parent (not immediately following a PID, ORC, OBR, or OBX), surfaced verbatim in document order so nothing is dropped. Notes that DO attach to a specific patient / order / result are exposed on those helper outputs (msg.patient?.notes, order.notes, observation.notes), not here. D-05: returns [] when there are none. D-06: NOT memoized.

Returns

readonly string[]

Example

for (const note of msg.notes()) console.log(note); // message-level narrative

observations()

observations(): readonly Observation[]

Every OBX segment as a typed Observation in document order. D-05: returns [] when no OBX present. D-06: NOT memoized: each call re-walks rawSegments. Value type is discriminated per D-13.

Returns

readonly Observation[]

Example

for (const obs of msg.observations()) {
if (obs.valueType === "NM") console.log(obs.value); // number | undefined
}

orders()

orders(): readonly Order[]

Every OBR as an Order with its OBX children grouped positionally (D-12) and its TQ1 / legacy embedded-TQ (ORC-7) timings (the repeat pattern is surfaced verbatim, never resolved to a schedule). D-05: returns [] when no OBR present. D-06: not memoized.

Returns

readonly Order[]

Example

for (const order of msg.orders()) {
console.log(order.placerOrderNumber, order.observations.length);
for (const t of order.timings) console.log(t.repeatPattern?.code); // e.g. "Q6H": verbatim
}

prettyPrint()

prettyPrint(): string

Emit this message as a human-readable multi-line string for logs and debugging (SER-04). Single opinionated format (D-22 no options): header line with type / controlId / timestamp / segment count, then one line per segment with labeled [N]=value fields (D-23). Composite values render as their raw HL7 string: depth stops at field level (D-24). Pure: never warns, never throws (D-26).

Field values render as their raw HL7 string representation. Embedded delimiters in user data appear as escape sequences: e.g. a patient family name containing | renders as Smith\F\Jones (NOT Smith|Jones). This preserves round-trip fidelity: copy-pasting prettyPrint output into parseHL7 yields a structurally equivalent message. For un-escaped human display, parse the composite first via typed accessors (e.g. msg.patient?.familyName): those return already-decoded strings.

Returns

string

Example

import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
console.log(msg.prettyPrint());
// HL7 ADT^A01 controlId=MSG001 timestamp=2026-04-19T10:15:00Z (5 segments)
// MSH [3]=SENDAPP [4]=SENDFAC ...
// PID [1]=1 [3]=MRN123 [5]=Doe^John

removeSegment()

removeSegment(segmentType, occurrenceOrOptions?): this

Remove segments by type + occurrence or by type + all. Call shapes:

  • removeSegment("NTE"): remove the FIRST NTE (occurrence 0).
  • removeSegment("OBX", 1): remove the SECOND OBX (0-indexed per D-01).
  • removeSegment("OBX", { all: true }): remove ALL OBX segments.

MSH is protected: removeSegment("MSH") throws TypeError (every HL7 message must retain its MSH segment). Unknown segment types are a no-op (idempotent; no throw). Segment name must match the D-19 shape regex: invalid shapes throw TypeError for symmetry with addSegment.

Invalidates caches on return; warnings untouched (D-16).

Parameters

segmentType

string

occurrenceOrOptions?

number | { all?: boolean; }

Returns

this

Example

msg.removeSegment("NTE"); // remove first NTE
msg.removeSegment("OBX", 1); // remove second OBX
msg.removeSegment("OBX", { all: true }); // remove all remaining OBX

segments()

segments(segmentType): readonly Segment[]

Return the cached array of Segment wrappers for segmentType in document order. The returned array identity and the individual Segment instances are both stable across calls (D-11). Invalidated wholesale by the mutation methods.

Parameters

segmentType

string

Returns

readonly Segment[]

Example

const pid = msg.segments("PID")[0];
if (pid !== undefined) console.log(pid.field(5).value);

setComposite()

setComposite<K>(path, kind, value): this

Set a typed composite at a field (or field-repetition) dot-path: the conservative-emit mirror of the typed read accessors (asXpn/asCx/…). The caller passes a structured value (an XPN name, a CX identifier, a TS timestamp, …) by its CompositeKind, and the setter encodes it into a spec-clean field using the encode-safe path: any delimiter embedded in a component value is escaped, never injected, so a familyName of "Smith^Jr" re-parses to exactly that string rather than forging a component boundary. No hand-assembly of ^/&/~.

The path must resolve to a field ("PID.5") or a specific repetition of a field ("PID.11[1]"). A component/subcomponent-level path ("PID.5.1") is rejected with TypeError: a composite occupies a whole field, not a single component. Like setField, the target segment must already exist (addSegment first); the repetition defaults to index 0 and other repetitions of the field are preserved.

Never fabricates: an omitted optional composite field encodes to an empty/absent component, never a defaulted value; an all-empty composite clears the field. Segment/helper caches are invalidated on success; the frozen warnings array is untouched.

Type Parameters

K

K extends CompositeKind

Parameters

path

string

kind

K

value

CompositeValueByKind[K]

Returns

this

Example

const msg = buildMessage({ type: "ADT^A01" }).addSegment("PID", [""]);
msg.setComposite("PID.5", "XPN", { familyName: "Smith", givenName: "Ann" });
msg.setComposite("PID.3", "CX", { idNumber: "MRN001", identifierTypeCode: "MR" });
msg.setComposite("PID.7", "TS", "19880705");
msg.get("PID.5.1"); // "Smith"

setField()

setField(path, value): this

Set the string value at a dot-path. Mutates the underlying tree and returns this for chaining (D-15). Auto-creates missing repetitions, components, and subcomponents WITHIN an existing field, but does NOT auto-create segments: callers must addSegment first (throws TypeError with an actionable message otherwise).

The value is accepted verbatim: unescaped delimiter characters are NOT rejected on input (D-18). Re-escaping is the serializer's concern.

MSH-1 / MSH-2 follow the user-facing HL7 convention: setField("MSH.3", ...) targets MSH-3 (sending application), matching msg.get("MSH.3").

Segment/Field wrapper caches are invalidated wholesale on success (D-17). The frozen warnings array is never touched (D-16).

Parameters

path

string

value

string

Returns

this

Example

msg.setField("PID.8", "F"); // patient sex → F
msg.setField("PID.5.1", "Jones"); // family name
msg.setField("PID.4[2].1", "MRN2"); // create third repetition of PID-4

toJSON()

toJSON(): SerializedMessage

Emit this message as a structured SerializedMessage JSON projection (SER-03). Invoked automatically by JSON.stringify(msg) (D-18). Re-walks rawSegments on every call (D-30 no caching). Mirrors the raw tree one-for-one, preserves isNull, always includes warnings: [], and includes profile: { name, lineage } only when this.profile is truthy (D-19/D-20). Pure: never warns, never throws.

Returns

SerializedMessage

Example

import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
const snap = msg.toJSON();
console.log(snap.segments[0]?.name); // "MSH"
console.log(JSON.stringify(msg)); // same content, auto-invokes toJSON

toString()

toString(): string

Emit this message as spec-clean HL7 (SER-01). Re-walks rawSegments on every call (D-30 no caching). Segments are joined with \r per D-05; MSH-1 and MSH-2 are inlined verbatim from this.encodingCharacters per D-06; every field string passes through reescape per D-04. RawField.isNull === true is preserved as the HL7 literal "" (D-02). Pure: never warns, never throws (D-07).

Returns

string

Example

import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
console.log(msg.toString()); // spec-clean, CR-separated HL7