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
readonlydateFormats: readonlystring[]
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
readonlyencodingCharacters:EncodingCharacters
profile
readonlyprofile: {lineage: readonlystring[];name:string; } |undefined
rawSegments
readonlyrawSegments: readonlyRawSegment[]
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
readonlyversion:string
warnings
readonlywarnings: readonlyHl7ParseWarning[]
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
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
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): readonlySegment[]
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): readonlySegment[]
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
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
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