@cosyte/hl7
Namespaces
Classes
Field
Wrapper over a RawField exposing HL7 null/empty discrimination
(isNull), the repetitions tree, and a decoded value getter.
seg.field(3) === seg.field(3): Field instances are referentially stable
per segment position (D-12).
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
const pid5 = msg.segments("PID")[0]?.field(5);
console.log(pid5?.value); // "Smith": decoded at parse
console.log(pid5?.isNull); // false: HL7 explicit null is "", absent is not
console.log(pid5?.repetitions.length); // 1
Constructors
Constructor
new Field(
raw,enc,position,dateFormats?):Field
Internal
Construct a new Field. Called internally by Segment.field(n); user
code should obtain Field instances via msg.segments(type)[i].field(n).
Parameters
raw
enc
position
dateFormats?
readonly string[]
Returns
Properties
dateFormats
readonlydateFormats: readonlystring[]
Internal
The date formats the caller declared for this message
(ParseOptions.dateFormats ++ the applied profile's, deduplicated
first-occurrence-wins), threaded down from Hl7Message.dateFormats so a
typed TS coercion can honour them. Empty when neither route declared
any, which is the strict-only parse. Read by asTs() and by the two
helper sites that parse a datetime subcomponent directly.
enc
readonlyenc:EncodingCharacters
Internal
The 5 encoding characters for this message. Exposed for composite parsers.
isNull
readonlyisNull:boolean
HL7 null indicator: true iff the underlying field was the two-char literal "".
position
readonlyposition:Hl7Position
Internal
Position of this field in the parent message: used for position-aware error messages.
raw
readonlyraw:RawField
Internal
The full RawField this wrapper wraps. Exposed for composite parsers.
repetitions
readonlyrepetitions: readonlyRawRepetition[]
Reference to the underlying RawField.repetitions (no defensive copy).
Accessors
text
Get Signature
get text():
string
The field's canonical wire text: the full field re-serialized with the active delimiters and re-escaped content (repetitions, components, and subcomponents included). Contrast with value, which returns only the first subcomponent of the first component of the first repetition (decoded once at parse: never re-unescaped).
Use this when a field must be compared or echoed as a whole: e.g.
correlating an ACK's MSA-2 against the inbound MSH-10, where a
vendor-quirk control id containing an unescaped delimiter (ID^X) must
not be truncated to its first component.
Byte-verbatim for parsed content. The parse pipeline stores
decoded content, but also records the original wire bytes of any escape
whose decode is not byte-faithful (RawComponent.rawSubcomponents), so
re-serialization preserves the sender's exact escape bytes: hex escapes
stay hex (A\X41\B, casing intact), and recognize-and-preserve sequences
(\H\, \N, formatting/charset/vendor escapes) re-emit verbatim
rather than as escaped literal text. Delimiter/newline escapes and plain
content round-trip byte-exact via the re-escape path. The only remaining
canonicalization is structural: trailing insignificant empties are
stripped (D-02), and a field built by hand (not parsed) re-escapes its
decoded value since it has no overlay.
MSH-1/MSH-2 caveat. Like value, calling this on MSH-1 or
MSH-2 (the delimiter-definition fields) re-escapes the encoding
characters themselves and produces garbage: those two fields are only
meaningful through Hl7Message.encodingCharacters.
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7("MSH|^~\\&|A|B|C|D|20260101||ADT^A01|ID^X|P|2.5\r");
msg.segments("MSH")[0]?.field(10).value; // "ID" (first component only)
msg.segments("MSH")[0]?.field(10).text; // "ID^X" (verbatim wire text)
Returns
string
value
Get Signature
get value():
string
First-repetition, first-component, first-subcomponent value as a decoded
string: unescaped ONCE by the tokenizer on parse, returned verbatim here
(never re-unescaped). Returns "" when the field is
empty or HL7 null. Equivalent to msg.get('SEG.N') for a top-level access.
Example
const pid5 = msg.segments("PID")[0]?.field(5);
// wire "Smith\F\Jr" was decoded at parse → the field separator escape:
console.log(pid5?.value); // "Smith|Jr"
Returns
string
Methods
asCe()
asCe():
CE
Coerce this field's first repetition to a typed CE (Coded Element).
Returns
Example
const code = msg.segments("OBX")[0]?.field(3)?.asCe();
console.log(code?.identifier, code?.text);
asCwe()
asCwe():
CWE
Coerce this field's first repetition to a typed CWE (Coded With
Exceptions).
Returns
Example
const code = msg.segments("OBX")[0]?.field(3)?.asCwe();
console.log(code?.identifier, code?.text);
asCx()
asCx():
CX
Coerce this field's first repetition to a typed CX (Extended Composite
ID). assigningAuthority is a nested HD.
Returns
Example
const mrn = msg.segments("PID")[0]?.field(3)?.asCx();
console.log(mrn?.idNumber, mrn?.assigningAuthority?.namespaceId);
asHd()
asHd():
HD
Coerce this field's first repetition to a typed HD (Hierarchic
Designator).
Returns
Example
const sending = msg.segments("MSH")[0]?.field(3)?.asHd();
console.log(sending?.namespaceId);
asNm()
asNm():
NM
Coerce this field's first repetition to a typed NM (Numeric).
{ raw, value }: value is undefined on non-numeric input.
Returns
Example
const nm = msg.segments("OBX")[0]?.field(5)?.asNm();
console.log(nm?.value);
asPl()
asPl():
PL
Coerce this field's first repetition to a typed PL (Person Location).
facility is a nested HD.
Returns
Example
const loc = msg.segments("PV1")[0]?.field(3)?.asPl();
console.log(loc?.pointOfCare, loc?.room, loc?.facility?.namespaceId);
asSn()
asSn():
SN|undefined
Coerce this field's first repetition to a typed SN (Structured Numeric),
or undefined when the field carries no usable structured-numeric content.
Use for an OBX-5 whose OBX-2 value type is SN (a comparator like >90,
a range like 100-200, or a ratio like 1:128). num1/num2 are
number | undefined (never NaN); the comparator is surfaced only when
SN.1 is a recognized operator.
Returns
SN | undefined
Example
const sn = msg.segments("OBX")[0]?.field(5)?.asSn();
console.log(sn?.comparator, sn?.num1); // ">" 90
asTs()
asTs():
DtmParts
Coerce this field's first repetition to a typed TS (Time Stamp): the
fidelity DtmParts (raw + parts + precision + timezone). valid is
false on unparseable input (no throw). Build an absolute instant only on
explicit request via dtmToDate(ts).
The canonical HL7 shape is tried first. A value that is not canonical is
then matched against the dateFormats the caller declared for this
message, and a match reports which format won on matchedFormat. Nothing
beyond those declared formats is tried, so a vendor date the caller has
not described stays valid: false rather than becoming a plausible wrong
date.
Returns
Example
const ts = msg.segments("MSH")[0]?.field(7)?.asTs();
console.log(ts?.raw, ts?.precision, ts?.hasTimezone);
// parseHL7(raw, { dateFormats: ["MM/DD/YYYY"] }) on a PID-7 of "07/05/1988":
const dob = msg.segments("PID")[0]?.field(7)?.asTs();
console.log(dob?.month, dob?.matchedFormat); // 7 "MM/DD/YYYY"
asXad()
asXad():
XAD
Coerce this field's first repetition to a typed XAD (Extended Address).
Returns
Example
const addr = msg.segments("PID")[0]?.field(11)?.asXad();
console.log(addr?.street, addr?.city, addr?.stateOrProvince);
asXcn()
asXcn():
XCN
Coerce this field's first repetition to a typed XCN (Extended Composite
ID Number and Name for Persons). assigningAuthority is a nested HD.
Common on OBR-16 (ordering provider), PV1-7 (attending doctor), PV1-8
(referring doctor). Empty field → {} (never throws).
Returns
Example
const orderedBy = msg.segments("OBR")[0]?.field(16)?.asXcn();
console.log(orderedBy?.idNumber, orderedBy?.familyName, orderedBy?.identifierTypeCode);
asXpn()
asXpn():
XPN
Coerce this field's first repetition to a typed XPN (Extended Person
Name). Absent components are OMITTED from the result
(exactOptionalPropertyTypes). Not memoized in v1: each call re-parses
(D-09).
Returns
Example
const pid5 = msg.segments("PID")[0]?.field(5);
const name = pid5?.asXpn();
console.log(name?.familyName, name?.givenName);
asXtn()
asXtn():
XTN
Coerce this field's first repetition to a typed XTN (Extended
Telecommunication Number).
Returns
Example
const phone = msg.segments("PID")[0]?.field(13)?.asXtn();
console.log(phone?.telephoneNumber);
render()
render(
opts?):RenderedText
Render this field's formatted text (HL7 v2 §2.7 highlight + formatting
escapes) into a normalized RenderedText display model: plain text
plus highlight-aware runs. A read projection over the field's
byte-verbatim wire text: it never mutates the raw value, and it
never fabricates (an unrenderable escape is preserved + flagged). Use this
to surface a clinical narrative (an NTE / OBX-5 note) to a human without
the raw \.br\ / \H sentinels.
Parameters
opts?
see RenderTextOptions (e.g. a custom line-break string).
Returns
the normalized display model.
Example
const note = msg.segments("OBX")[0]?.field(5);
note?.render().text; // "Specimen received.\nGross exam normal."
empty()
staticempty(_enc):Field
Internal
Return a synthetic empty Field sentinel: used by Segment.field(n) to
honor MODEL-05's "never throws on missing" contract. The returned Field
has isNull === false, repetitions === [], and value === "".
Referentially stable across calls (same instance returned each time).
The enc argument is accepted for API symmetry but ignored: the
synthetic field carries no content, so unescape would be a no-op
regardless of the active encoding characters. For the same reason the
sentinel carries no declared dateFormats: there is no value to match one
against, and asTs() on it is the empty invalid TS either way.
Parameters
_enc
Returns
Example
const empty = Field.empty(msg.encodingCharacters);
console.log(empty.value); // ""
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);
Extended by
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
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. This is the list every datetime in the message
honours, from meta.timestamp to any field.asTs(), in exactly this
order; exposed so a caller can introspect what their options and profile
added up to.
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
HL7 version the model asserts for this message, from MSH-12.1.1, e.g.
"2.5" or "2.5.1".
Bounded, and it is "<withheld>" when MSH-12 does not hold a version.
Same reasoning as Segment.type: it presents as an identifier and
consumers label with it. meta.version keeps MSH-12 exactly as it
arrived, and is what version-scoped field selection reads, so this bound
changes no parsing behaviour.
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 and every IAM as an Allergy, one entry per segment in document
order, each naming the segment it was read from (source). An ADT^A60
carries its allergies in IAM, so it is read here too. D-05: returns []
when neither is present.
Limits: the IAR and NTE segments under an IAM are not read, nor IAM-1 or
IAM-8 onward (reach them with msg.segments("IAM")). IAM-6 is surfaced as
actionCode, and an exact D sets deleteRequested, but no action is ever
applied: a delete entry is still returned, an update never replaces an
earlier entry, and an AL1 and an IAM for the same allergen are two entries.
Returns
readonly Allergy[]
Example
for (const al of msg.allergies()) {
const kind = al.deleteRequested === true ? "delete request" : "allergy";
console.log(kind, al.source, al.code?.text, al.severity, al.actionCode);
}
allSegments()
allSegments(): readonly
Segment<string>[]
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<string>[]
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<string>[]
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, and
matches segment names ignoring case exactly as it does.
Parameters
segmentType
string
Returns
readonly Segment<string>[]
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 / change /
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/change events.
Returns [] when the trigger event is not in the identity family. The
recognized set is floored by the published message structures: every ADT
trigger event whose structure requires MRG is recognized. 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);
is()
Internal
Single runtime implementation behind both signatures.
Call Signature
is<
K>(key):this is TypedMessage<K>
Is this message of the type key names, and if so, narrow it for the
compiler. The check is on the (MSH-9.1, MSH-9.2) pair the parser already
extracted, so a message whose MSH-9 carries the three-component form
ADT^A01^ADT_A01 answers true to is("ADT^A01").
A key is "<MSH-9.1>^<MSH-9.2>" ("ADT^A01"), or "<MSH-9.1>" alone for a
message type the published structure registry matches on message code alone
("ACK", whose MSH-9.2 carries the acknowledged message's trigger event).
SUPPORTED_OVERLAY_MESSAGES enumerates every key.
Any other string is false, never a throw: an unrecognized type, the
three-component form as a string, an empty string, or a value computed at
run time. is does not parse its own argument, and a caller who wants a raw
comparison has msg.meta.type.
Read-only: it inspects meta and mutates nothing.
Type Parameters
K
K extends keyof OverlayStructures
Parameters
key
K
Returns
this is TypedMessage<K>
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.is("ORU^R01")) {
const code: "ORU" = msg.meta.messageCode; // literal, no cast
console.log(msg.part("OBR")?.field(4).value);
}
console.log(msg.is("ZZZ^Z99")); // false: not a published key
Call Signature
is(
key):boolean
Answer for a key that is not known at compile time. A runtime-computed
string cannot narrow anything, so this form returns a plain boolean.
Parameters
key
string
Returns
boolean
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).
Each medication also carries orderControl: ORC-1 of the ORC that opened
its order group (every RX* up to the next ORC shares it), exactly as sent.
It is never interpreted into an active, held or discontinued state, and it
is omitted when no ORC precedes the RX* or ORC-1 is empty, never carried
over from an earlier group.
Returns
readonly Medication[]
Example
for (const med of msg.medications()) {
console.log(med.orderControl); // e.g. "NW" (new) or "DC" (discontinue), verbatim
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
}
part()
part(
segmentType):Segment<string> |undefined
The FIRST Segment named segmentType in document order, or undefined
when the message carries none. Shorthand for segments(segmentType)[0]:
same cache, same case-insensitive matching on both sides, same referential
stability.
On a message narrowed by Hl7Message.is, segmentType is scoped at
compile time to the segment names that message type's published structure
marks required. It stays Segment | undefined there: the parser tolerates a
message that omits a required segment (and warns), so a required name is
never a presence guarantee.
Parameters
segmentType
string
Returns
Segment<string> | undefined
Example
const pid = msg.part("PID");
console.log(pid?.field(5).value);
parts()
parts(
segmentType): readonlySegment<string>[]
EVERY Segment named segmentType in document order, [] when there are
none. Alias for segments(segmentType): same cache, same array identity,
same case-insensitive matching.
On a message narrowed by Hl7Message.is, segmentType is scoped at
compile time to the segment names that message type's published structure
marks required; the returned list can still be empty, for the same reason
Hl7Message.part can still return undefined.
Parameters
segmentType
string
Returns
readonly Segment<string>[]
Example
for (const obx of msg.parts("OBX")) console.log(obx.field(5).value);
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<string>[]
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.
Segment names are matched ignoring ASCII case, on both sides. A sender
that ships obx is returned by segments("OBX"), and segments("obx")
returns the same array (one cache entry, so the D-11 identity guarantee
does not split across spellings). A SEGMENT_CASE warning records the
sender's deviation; Segment.raw.name is the spelling that arrived.
Parameters
segmentType
string
Returns
readonly Segment<string>[]
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
Hl7ParseError
Thrown by parseHL7 when the input violates one of the 4 unrecoverable
Tier-3 structural rules (missing MSH, truncated MSH, invalid encoding
characters, or empty input). Carries positional context plus a short
snippet of the offending input so consumers can log actionable errors.
Remarks
snippet may contain PHI when parsing real clinical messages, and the
library does not redact it: redact at the call site if required by your
compliance posture. It is the only field carrying input verbatim and
unfiltered (capped at 40 characters plus an ellipsis, but not shape-checked
in any way), so it is the one to redact first.
message is bounded but not absolutely content-free, and the difference is
worth stating because message is what a logger prints by default and what
stack embeds. A token lifted from the input is echoed only when it matches
the form the spec defines for it: a three-character segment identifier, an
MSH-9 type, an MSH-12 version, or a charset label the closed Table 0211
actually contains. Anything else becomes <withheld>. So a message cannot
carry a field's value, but it can carry a residue of up to three characters
when a malformed line happens to look like a segment identifier. The other
shapes are narrower in practice than their patterns allow, because the
library only ever feeds them registry-matched values, but the patterns
themselves admit more (a message type up to 26 characters, a version up to
14), which matters if you construct warnings yourself.
Under { strict: true } an escalated Tier-2 warning is thrown as this
error, carrying the warning's own bounded message and a snippet of the
first 40 characters of the input. That snippet is the head of the
message rather than the deviation's own segment, so it is usually the MSH
header; it is deliberately not re-pointed at the offending segment, since
doing so would move more clinical content into the unredacted field.
Example
import { parseHL7, Hl7ParseError } from "@cosyte/hl7";
try {
parseHL7("");
} catch (err) {
if (err instanceof Hl7ParseError && err.code === "EMPTY_INPUT") {
// handle empty input: err.position, err.snippet available
}
}
Extends
Error
Constructors
Constructor
new Hl7ParseError(
code,message,position,snippet):Hl7ParseError
Internal
Construct a new Hl7ParseError. All four fields are required so every
thrower populates full positional context per the TOL-02 requirement.
Parameters
code
message
string
position
snippet
string
Returns
Overrides
Error.constructor
Properties
code
readonlycode:FatalCode
position
readonlyposition:Hl7Position
snippet
readonlysnippet:string
ProfileDefinitionError
Thrown by defineProfile() and profile-validation code when a
profile definition is structurally invalid: e.g. references an undefined
parent, declares a malformed custom segment, or includes an unsupported
date format. Callers may optionally supply the offending profile name for
better diagnostics.
Example
import { ProfileDefinitionError } from "@cosyte/hl7";
throw new ProfileDefinitionError(
"Unknown parent profile: epic-v7",
"my-epic-extension",
);
Extends
Error
Constructors
Constructor
new ProfileDefinitionError(
message,profileName?):ProfileDefinitionError
Internal
Construct a new ProfileDefinitionError. profileName is optional so
callers may omit it when the offending profile cannot be named
(e.g. during initial validation before a name is parsed).
Parameters
message
string
profileName?
string
Returns
Overrides
Error.constructor
Properties
profileName
readonlyprofileName:string|undefined
SchemaTargetError
Thrown when emission is requested for a target this library does not support. Carries the requested name and the supported set as data as well as in the message, so a caller can react without parsing prose.
Example
import { emitMessageSchema, SchemaTargetError } from "@cosyte/hl7";
try {
emitMessageSchema("openapi-3.1");
} catch (error) {
if (error instanceof SchemaTargetError) {
error.requestedTarget; // "openapi-3.1"
error.supportedTargets; // ["json-schema-2020-12", "zod"]
}
}
Extends
Error
Constructors
Constructor
new SchemaTargetError(
requestedTarget):SchemaTargetError
Build the error for one unsupported target name.
Parameters
requestedTarget
string
Returns
Overrides
Error.constructor
Properties
requestedTarget
readonlyrequestedTarget:string
The target name the caller asked for, verbatim.
supportedTargets
readonlysupportedTargets: readonly ("json-schema-2020-12"|"zod")[]
Every target this library does support.
Segment
Wrapper over a RawSegment exposing typed per-position Field instances.
seg.field(3) === seg.field(3): referential stability is guaranteed per
segment instance.
FieldName is the set of names Segment.get accepts. It is string
for every segment whose profile-declared field names are not statically
known, which is the default and covers every segment reached without a
statically known profile; a segment obtained for a declared segment type from
a message parsed with one carries that type's declared names instead.
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
const pid = msg.segments("PID")[0];
if (pid !== undefined) console.log(pid.field(5).value);
Type Parameters
FieldName
FieldName extends string = string
the names get accepts; string when not known.
Constructors
Constructor
new Segment<
FieldName>(raw,enc,absoluteIndex,customFields?,dateFormats?):Segment<FieldName>
Internal
Construct a new Segment. Called internally by Hl7Message; user code
should obtain Segment instances via msg.segments(type) or
msg.allSegments().
The optional customFields parameter is the per-segment portion of the
applied profile's merged declarations for this segment type (PROF-07 /
D-16). When supplied, get(name) resolves names against it; otherwise
get(name) always returns undefined.
The optional dateFormats parameter is the message's merged date-format
list; it is passed straight through to each Field.
Parameters
raw
enc
absoluteIndex
number
customFields?
Readonly<Record<string, number>>
dateFormats?
readonly string[]
Returns
Segment<FieldName>
Properties
absoluteIndex
readonlyabsoluteIndex:number
Internal
Absolute index of this segment in Hl7Message.rawSegments[]. Used for position tracking.
customFields
readonlycustomFields:Readonly<Record<string,number>> |undefined
Internal
Lookup map from profile-declared field name → 1-indexed HL7 position.
Absent when no profile was applied to the parent message, or when the
applied profile declares no field names for this segment's type in either
of its declaration maps (customSegments for a Z-segment,
segmentOverrides for a standard one). Consumed by get(name) to resolve
named-field access (PROF-07).
dateFormats
readonlydateFormats: readonlystring[]
Internal
The message's merged dateFormats (D-21), handed to every Field this
segment builds so a typed TS coercion honours what the caller declared.
Empty when neither ParseOptions.dateFormats nor the applied profile
declared any.
enc
readonlyenc:EncodingCharacters
Internal
The 5 encoding characters for this message. Exposed for composite parsers.
fields
readonlyfields: readonlyRawField[]
Reference to the underlying RawSegment.fields: 1-indexed per HL7 convention.
raw
readonlyraw:RawSegment
Internal
The full RawSegment this wrapper wraps. Exposed for mutation methods.
type
readonlytype:string
Segment identifier: three characters with a leading letter, e.g. "PID",
"OBX", "ZPI".
Canonical ASCII uppercase. A sender that ships pid or Obx, which
no segment identifier in the HL7 v2 standard does but which real feeds do,
reports here as PID / OBX, so msg.segments("PID") matches it and a
SEGMENT_CASE warning records the deviation. Read
Segment.raw.name for the spelling that actually arrived.
Bounded, and it is "<withheld>" when the raw name is not that shape.
A line with no field separator has its whole content read as a segment
name, so an unescaped line break inside a narrative field forges a
"segment" whose name is clinical text. This field presents itself as a
structural identifier and consumers interpolate it into labels, loci and
reports, so it never carries that text. "" still means absent, which is
a different fact from withheld.
Use Segment.raw.name when you need the verbatim text: it is the
unbounded value, and it is what serialization emits, so the byte-verbatim
round-trip is unaffected. One consequence worth knowing: a segment whose
raw name fails the shape (a 4-character vendor Z-segment, which HL7 v2
Ch. 2 §2.5 does not permit) is not matched by msg.segments(name).
Methods
field()
field(
n):Field
Return the Field wrapper at HL7 position n. Indexing follows the HL7
1-indexed convention: seg.field(5) on a PID segment maps to PID-5.
MSH segments use the same user-facing convention: msh.field(1) returns
the field-separator (MSH-1), msh.field(2) returns encoding chars
(MSH-2), msh.field(3) returns MSH-3, and so on: the internal
fields[N-1] offset for MSH segments is applied here (mirrors the
dot-path resolver in dot-path.ts, keeping msg.segments('MSH')[0].field(3)
and msg.get('MSH.3') in agreement).
Returns a synthetic empty Field (.isNull === false, .value === "")
when n is out of range: never throws (MODEL-05). Successive calls with
the same n return the same Field instance (D-12).
Parameters
n
number
Returns
Example
const pid5 = msg.segments("PID")[0]?.field(5);
console.log(pid5?.value); // "Smith"
const msh3 = msg.segments("MSH")[0]?.field(3);
console.log(msh3?.value); // sending application (HL7 MSH-3)
get()
get(
name):Field|undefined
Return the Field at the profile-declared position for name, or
undefined when no custom mapping exists (PROF-07). Unlike field(n),
missing names return undefined, NOT a synthetic empty Field, so
typos surface instead of silently resolving to an empty string (D-14).
Two declaration maps feed it, and which one a name may come from depends
on the segment: customSegments names fields on a Z-segment,
segmentOverrides names fields on a standard one. Both are read-side
ALIASES. A name resolves to whatever field(n) returns for its declared
position and nothing else moves: the positional accessor, dot-paths, the
typed clinical accessors (msg.patient, msg.allergies(), ...), the
warning list and both serializations are unaffected by a declaration.
For segments the profile in force declares nothing for (and for every
segment when no profile was applied), this method always returns
undefined.
When the declared position is out of range for the underlying
RawSegment.fields, get(name) returns undefined (NOT a
synthetic-empty Field) so callers can distinguish "name not declared"
from "name declared but position missing in the raw message" only at
the presence level (both collapse to undefined per D-14).
name is checked at compile time when the declaration is statically
known. On a segment obtained by type from a message parsed with a
statically known profile, name is scoped to the names that profile
declares FOR THAT SEGMENT TYPE, so a typo is a type error rather than a
silent undefined. Everywhere the declaration is not statically known
(no profile, a value typed as the general Profile interface, the
process-wide default profile, an undeclared segment type, an empty field
map, or the undifferentiated allSegments() walk) name stays string.
The return type is unchanged either way: a declared name is not a promise
that the wire message carried it.
Parameters
name
FieldName
Returns
Field | undefined
Example
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const profile = defineProfile({
name: "vendor",
customSegments: { ZPI: { fields: { encounterId: 3 } } },
});
const msg = parseHL7(raw, profile);
console.log(msg.part("ZPI")?.get("encounterId")?.value); // narrowed, no cast
// msg.part("ZPI")?.get("encounterld"); // does not compile: not declared
const walked = msg.allSegments().find((s) => s.type === "ZPI");
console.log(walked?.get("encounterId")?.value); // a walk takes any name
Interfaces
AckErrorDetail
One ERR segment's worth of error detail for buildAck. Carries codes and
locations only: never echoed PHI values (the phi-redaction-review gate
binds this contract).
Example
import { buildAck } from "@cosyte/hl7";
const ack = buildAck(inbound, {
code: "AE",
error: { conditionCode: "101", severity: "E", location: "PID^1^5" },
});
Properties
conditionCode?
readonlyoptionalconditionCode?:string
HL7 Table 0357 message error condition code (ERR-3.1). Defaults to "207"
(Application internal error). The standard display text is looked up from
Table 0357 and emitted in ERR-3.2; unknown codes emit with empty text
(the code is preserved verbatim: never dropped).
location?
readonlyoptionallocation?:string
Error location (ERR-2, an HL7 ERL). A structural path such as "PID^1^5"
(segment id ^ segment sequence ^ field position). Must not contain a
patient data value: locations point at where, never what.
severity?
readonlyoptionalseverity?:ErrSeverity
HL7 Table 0516 severity (ERR-4). Defaults to "E" (Error).
AckErrorEntry
One ERR segment interpreted from an ACK. All fields are surfaced verbatim (no table lookup) and OMITTED when the underlying component is absent (exactOptionalPropertyTypes). Codes/locations only: never PHI.
Properties
conditionCode?
readonlyoptionalconditionCode?:string
ERR-3.1: HL7 Table 0357 condition code.
conditionCodeSystem?
readonlyoptionalconditionCodeSystem?:string
ERR-3.3: condition code system name (e.g. HL70357).
conditionText?
readonlyoptionalconditionText?:string
ERR-3.2: condition code display text.
location?
readonlyoptionallocation?:string
ERR-2: error location (an HL7 ERL), surfaced verbatim.
severity?
readonlyoptionalseverity?:string
ERR-4: HL7 Table 0516 severity (I/W/E).
Acknowledgment
Typed view of an inbound ACK. accepted / error / rejected are derived
from MSA-1 against HL7 Table 0008 and are mutually exclusive; all three
are false when MSA-1 is absent or not a recognized code (fail-safe).
Example
import { interpretAck, parseHL7 } from "@cosyte/hl7";
const ack = interpretAck(parseHL7(rawAck));
if (ack.accepted) {
// safe to consider the message acknowledged
} else if (ack.rejected) {
for (const e of ack.errors) console.error(e.conditionCode, e.severity);
}
Properties
accepted
readonlyaccepted:boolean
True iff MSA-1 is a positive accept (AA/CA).
code?
readonlyoptionalcode?:string
MSA-1 acknowledgment code (HL7 Table 0008), verbatim. Omitted when absent.
controlId?
readonlyoptionalcontrolId?:string
MSA-2 message control id (the correlated inbound MSH-10), surfaced as the
field's canonical wire text (Field.text): the whole field,
delimiters included, never truncated to the first component. Note this is
the re-escaped form: a properly-escaped id arrives as its wire bytes
(ID\S\X), and an HL7 explicit-null MSA-2 surfaces as the literal
two-character "". Omitted when absent/empty.
error
readonlyerror:boolean
True iff MSA-1 is an error acknowledgment (AE/CE).
errors
readonlyerrors: readonlyAckErrorEntry[]
Every ERR segment in document order ( [] when none ).
rejected
readonlyrejected:boolean
True iff MSA-1 is a reject acknowledgment (AR/CR).
AdtEvent
Typed EVN (event type) content for buildAdt.
Properties
eventOccurred?
readonlyoptionaleventOccurred?:string|DtmParts
EVN-6 Event Occurred.
recordedDateTime?
readonlyoptionalrecordedDateTime?:string|DtmParts
EVN-2 Recorded Date/Time.
AdtPatient
Typed PID (patient identification) content for buildAdt.
Properties
accountNumber?
readonlyoptionalaccountNumber?:CX
PID-18 Patient Account Number.
address?
PID-11 Patient Address: one or more XAD addresses.
administrativeSex?
readonlyoptionaladministrativeSex?:string
PID-8 Administrative Sex (e.g. "F", "M", "U").
birthDateTime?
readonlyoptionalbirthDateTime?:string|DtmParts
PID-7 Date/Time of Birth.
identifiers?
PID-3 Patient Identifier List: one or more CX identifiers (MRN, SSN, …).
mothersMaidenName?
readonlyoptionalmothersMaidenName?:XPN
PID-6 Mother's Maiden Name.
name?
PID-5 Patient Name.
phoneHome?
PID-13 Phone Number - Home: one or more XTN telecoms.
setId?
readonlyoptionalsetId?:string
PID-1 Set ID.
AdtPriorIdentity
Typed MRG (merge patient information) content for buildAdt: the
prior, non-surviving identity that a merge or move event retires in
favour of the patient identity. The direction is HL7's and is constant:
the identifiers here merge INTO the ones on PID, never the reverse.
Example
import type { AdtPriorIdentity } from "@cosyte/hl7";
const prior: AdtPriorIdentity = {
identifiers: { idNumber: "MRN-OLD", identifierTypeCode: "MR" },
accountNumber: { idNumber: "ACCT-OLD" },
};
Properties
accountNumber?
readonlyoptionalaccountNumber?:CX
MRG-3 Prior Patient Account Number: the merge key of an account merge.
identifiers?
MRG-1 Prior Patient Identifier List: one or more CX identifiers.
legacyPatientId?
readonlyoptionallegacyPatientId?:CX
MRG-4 Prior Patient ID. Backward compatibility only: withdrawn as of HL7 v2.7 in favour of MRG-1, and the read side stops reading it on a message whose MSH-12 declares v2.7 or later.
name?
readonlyoptionalname?:XPN
MRG-7 Prior Patient Name.
visitNumber?
readonlyoptionalvisitNumber?:CX
MRG-5 Prior Visit Number: the merge key of a visit merge.
AdtVisit
Typed PV1 (patient visit) content for buildAdt.
Properties
admitDateTime?
readonlyoptionaladmitDateTime?:string|DtmParts
PV1-44 Admit Date/Time.
assignedLocation?
readonlyoptionalassignedLocation?:PL
PV1-3 Assigned Patient Location.
attendingDoctor?
PV1-7 Attending Doctor.
patientClass?
readonlyoptionalpatientClass?:string
PV1-2 Patient Class (e.g. "I" inpatient, "O" outpatient, "E" emergency).
referringDoctor?
PV1-8 Referring Doctor.
setId?
readonlyoptionalsetId?:string
PV1-1 Set ID.
visitNumber?
readonlyoptionalvisitNumber?:CX
PV1-19 Visit Number.
Allergy
One allergy entry, read from an AL1 or an IAM segment (see source). Both
segments fill type, code, severity and reaction in the same shapes;
onsetDate comes from AL1 only, and actionCode, deleteRequested and
uniqueIdentifier from IAM only. Every key but source is OMITTED when the
field it reads is empty: none is defaulted.
An entry is a report of what one segment said, never the result of applying
it. An IAM entry whose action code is D is a request to delete an allergy
sent earlier, and it is still returned: it is marked by deleteRequested,
and nothing is removed, merged or updated on the caller's behalf.
Example
import type { Allergy } from "@cosyte/hl7";
const al: Allergy = {
source: "AL1",
type: "DA",
code: { identifier: "PEN", text: "Penicillin" },
severity: "SV",
reaction: "Hives",
};
Properties
actionCode?
readonlyoptionalactionCode?:string
IAM-6 allergy action code (Table 0206), component 1 exactly as sent: no
case-folding, no mapping, and an unlisted code surfaced as it arrived.
OMITTED when IAM-6 is empty, never defaulted to A. Absent on AL1 entries.
IAM-6 does not repeat: a malformed repeated IAM-6 surfaces every
repetition's component 1, joined by the message's repetition separator
(D~A), rather than its first piece.
Example
for (const al of msg.allergies()) {
if (al.actionCode === "U") console.log("update to", al.uniqueIdentifier?.entityIdentifier);
}
code?
readonlyoptionalcode?:CWE
AL1-3 or IAM-3 allergen code.
deleteRequested?
readonlyoptionaldeleteRequested?:true
true when actionCode is exactly D: this entry asks the receiver to
delete an allergy sent earlier, identified by uniqueIdentifier. OMITTED on
every other entry, including a lowercase d, an unknown or malformed code,
an empty IAM-6 and every AL1 entry. The entry itself is still returned, with
every field it carries; the delete is never applied here.
Example
const current = msg.allergies().filter((al) => al.deleteRequested !== true);
onsetDate?
readonlyoptionalonsetDate?:DtmParts
AL1-6 onset date as the fidelity TS.
reaction?
readonlyoptionalreaction?:string
AL1-5 or IAM-5 allergy reaction (first repetition).
severity?
readonlyoptionalseverity?:string
AL1-4 or IAM-4 severity, component 1 (SV=severe, MO=moderate, MI=mild).
source
readonlysource:AllergySource
The segment this entry was read from. Always present.
Example
const fromIam = msg.allergies().filter((al) => al.source === "IAM");
type?
readonlyoptionaltype?:string
AL1-2 or IAM-2 allergen type, component 1 (DA=drug, FA=food, EA=environmental, ...).
uniqueIdentifier?
readonlyoptionaluniqueIdentifier?:AllergyUniqueIdentifier
IAM-7 allergy unique identifier, components 1 and 2. OMITTED when IAM-7 is empty; never filled from IAM-3. Absent on AL1 entries.
Example
msg.allergies()[0]?.uniqueIdentifier?.entityIdentifier; // "ALG-0001"
AllergyUniqueIdentifier
IAM-7 allergy unique identifier (EI): the sender's identifier for one allergy, which an update or delete for that allergy repeats. Components 1 and 2 only; each key is OMITTED when its component is empty. It is never filled from IAM-3 or any other field when IAM-7 is absent.
Example
import type { AllergyUniqueIdentifier } from "@cosyte/hl7";
const id: AllergyUniqueIdentifier = { entityIdentifier: "ALG-0001", namespaceId: "LAB" };
Properties
entityIdentifier?
readonlyoptionalentityIdentifier?:string
EI-1 entity identifier, verbatim.
Example
msg.allergies()[0]?.uniqueIdentifier?.entityIdentifier; // "ALG-0001"
namespaceId?
readonlyoptionalnamespaceId?:string
EI-2 namespace ID of the application that assigned the identifier, verbatim.
Example
msg.allergies()[0]?.uniqueIdentifier?.namespaceId; // "LAB"
Appointment
SCH-derived appointment entry (SIU scheduling breadth). Surfaces the appointment identifiers, filler status (SCH-25, Table 0278), SCH-11 start/end timing, and the AI* resource groups. NOT a scheduling-workflow state machine.
Example
import type { Appointment } from "@cosyte/hl7";
const appt: Appointment = {
fillerAppointmentId: "A1001",
fillerStatusCode: { identifier: "Booked" },
resources: [],
};
Properties
endDateTime?
readonlyoptionalendDateTime?:DtmParts
Appointment end date/time: SCH-11 TQ.5 (fidelity TS).
fillerAppointmentId?
readonlyoptionalfillerAppointmentId?:string
SCH-2 filler appointment ID (EI first component, verbatim).
fillerStatusCode?
readonlyoptionalfillerStatusCode?:CWE
SCH-25 filler status code (HL7 Table 0278): the appointment status, verbatim/provenance-only.
placerAppointmentId?
readonlyoptionalplacerAppointmentId?:string
SCH-1 placer appointment ID (EI first component, verbatim).
resources
readonlyresources: readonlyAppointmentResource[]
AIS/AIG/AIL/AIP resources grouped under this SCH. Always present (possibly empty).
startDateTime?
readonlyoptionalstartDateTime?:DtmParts
Appointment start date/time: SCH-11 TQ.4 (fidelity TS).
AppointmentResource
One appointment resource grouped under a SCH: an AIS (service),
AIG (general resource), AIL (location), or AIP (personnel / provider) segment.
The resource identifier lives at position 3 of every AI* segment; for the
personnel resource (AIP) it is additionally surfaced as a typed person
(XCN), while the coded code (first component verbatim) is always available.
Example
import type { AppointmentResource } from "@cosyte/hl7";
const r: AppointmentResource = { kind: "location", code: { identifier: "OR-1" } };
Properties
code?
readonlyoptionalcode?:CWE
The AI*-3 resource identifier surfaced as a coded element: code.identifier
is the resource id (first component, verbatim). AIS-3 / AIG-3 are coded
elements, so code.text / code.nameOfCodingSystem are meaningful there;
AIL-3 is a PL (location) rather than a coded element, so only
code.identifier (the location id, PL.1) is meaningful and the other CWE
fields are positional provenance, not a coding system. Provenance-only.
kind
readonlykind:"service"|"general"|"location"|"personnel"
Which AI* segment sourced this resource: AIS→service, AIG→general, AIL→location, AIP→personnel.
person?
readonlyoptionalperson?:XCN
AIP-3 personnel resource as a typed XCN (personnel resources only): the appointment provider.
Batch
One batch within a stream: a run of messages delimited by a BHS header
and/or a BTS trailer (both optional in §2.10.3, so a BTS closes a
preceding run into a batch even with no BHS). A run of messages with
neither a header nor a trailer is not a batch: those messages live in
BatchSplitResult.messages only, so they never inflate a batch count.
declaredMessageCount is BTS-1 when the trailer declared a usable
non-negative integer (it is optional [0..1] in the spec, so it may be
absent); actualMessageCount is always the real count.
Example
import { splitBatch } from "@cosyte/hl7";
const [batch] = splitBatch(raw).batches;
batch?.header?.name; // "BHS" (or undefined for a headerless BTS-closed run)
batch?.actualMessageCount; // messages actually in the batch
Type Parameters
M
M extends Hl7Message = Hl7Message
the message type an ok entry carries.
Properties
actualMessageCount
readonlyactualMessageCount:number
The actual number of messages split out of this batch.
declaredMessageCount?
readonlyoptionaldeclaredMessageCount?:number
BTS-1 batch message count, when declared as a non-negative integer.
header?
readonlyoptionalheader?:BatchEnvelopeSegment
The BHS header, when this batch was opened by one.
messages
readonlymessages: readonlyBatchMessageEntry<M>[]
The messages in this batch, in stream order.
trailer?
readonlyoptionaltrailer?:BatchEnvelopeSegment
The BTS trailer, when this batch was closed by one.
BatchEnvelopeSegment
A raw batch-envelope segment (FHS/BHS/BTS/FTS) surfaced by
splitBatch. fields is the segment split on its own field separator
with fields[0] holding the segment name: deliberately the raw token
array, not a typed model: typed FHS/BHS field helpers beyond the raw
fields are not part of this surface. Note the
HL7 MSH-family indexing quirk: for FHS/BHS, fields[1] is the
encoding-characters field (FHS-2/BHS-2); for BTS/FTS, fields[1] is
field 1 (BTS-1 batch message count / FTS-1 file batch count).
Example
import { splitBatch } from "@cosyte/hl7";
const { fileHeader } = splitBatch("FHS|^~\\&|SENDER\r...");
fileHeader?.name; // "FHS"
fileHeader?.fields[2]; // "SENDER" (FHS-3, the File Sending Application)
Properties
fields
readonlyfields: readonlystring[]
Raw field tokens; fields[0] is the segment name.
name
readonlyname:BatchEnvelopeName
position
readonlyposition:Hl7Position
Position of this envelope segment in the split stream.
raw
readonlyraw:string
The verbatim segment string (line-ending normalized).
BatchSplitResult
The result of splitBatch: the flattened messages (every message
across every batch, in stream order: the primary surface), the nested
batches, the raw file envelope segments, and the batch-level warnings
(count-mismatch / missing-trailer). hadEnvelope is false for a bare
passthrough (no FHS/BHS/BTS/FTS seen).
Example
import { splitBatch } from "@cosyte/hl7";
const result = splitBatch(rawBatchFile);
result.messages.length; // every message, batched or not
result.batches.length; // === result.actualBatchCount
result.warnings; // BATCH_COUNT_MISMATCH / BATCH_MISSING_TRAILER (counts only)
Type Parameters
M
M extends Hl7Message = Hl7Message
the message type an ok entry carries.
Properties
actualBatchCount
readonlyactualBatchCount:number
The number of explicit batches split out of the stream.
batches
readonlybatches: readonlyBatch<M>[]
The explicit (BHS-delimited) batches, in stream order.
declaredBatchCount?
readonlyoptionaldeclaredBatchCount?:number
The last FTS-1 file batch count, when declared as a non-negative integer.
fileHeader?
readonlyoptionalfileHeader?:BatchEnvelopeSegment
The first FHS file header, when present.
fileTrailer?
readonlyoptionalfileTrailer?:BatchEnvelopeSegment
The last FTS file trailer, when present.
hadEnvelope
readonlyhadEnvelope:boolean
false when no envelope segment was seen (bare passthrough).
messages
readonlymessages: readonlyBatchMessageEntry<M>[]
Every message split out of the stream, in order: the primary surface. This
includes messages that belong to no explicit batch (a bare stream, or
content outside any BHS/BTS), so it is a superset of the messages
reachable via batches.
warnings
readonlywarnings: readonlyHl7ParseWarning[]
Batch-level warnings (count mismatch, missing trailer); counts/positions only, never PHI.
BuildAckOptions
Options for buildAck.
Example
buildAck(inbound, { code: "AA" }); // bare accept
buildAck(inbound, { code: "AR", error: { conditionCode: "200" } });
Properties
code
readonlycode:AckCode
The acknowledgment disposition to emit in MSA-1 (HL7 Table 0008). One of
AA/AE/AR (original) or CA/CE/CR (enhanced accept-level).
Required: buildAck builds the disposition it is told. An unknown code
is a programming error and throws TypeError.
error?
readonlyoptionalerror?:AckErrorDetail| readonlyAckErrorDetail[]
Optional error detail. A single AckErrorDetail or an array → one
ERR segment each. Typically supplied for AE/AR/CE/CR.
mode?
readonlyoptionalmode?:AckMode
Optional explicit acknowledgment mode. When omitted it is derived from the
inbound MSH-15/16 via detectAckMode. buildAck emits code
verbatim regardless of mode: this field is advisory metadata for adapters
(e.g. @cosyte/mllp's commit-policy layer) that need the detected mode.
BuildAdtInit
Input for buildAdt: the MSH envelope plus the typed segment bodies.
Extends
MessageEnvelope
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
event?
readonlyoptionalevent?:AdtEvent
EVN content (EVN-1 is the trigger event; EVN-2/6 optional).
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
priorIdentity?
readonlyoptionalpriorIdentity?:AdtPriorIdentity
MRG content: the prior identity a merge, move or identifier-change event retires. Optional, and emitted only when supplied. Required for a trigger event whose published structure requires MRG, where an init without it is a typed error rather than a message missing the identity it retires.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
visit?
readonlyoptionalvisit?:AdtVisit
PV1 content. Optional; an (empty) PV1 is emitted regardless so the visit group is present.
BuildDftInit
Input for buildDft: the MSH envelope plus the typed segment bodies.
Example
import type { BuildDftInit } from "@cosyte/hl7";
const init: BuildDftInit = {
sendingApp: "CLINIC",
receivingApp: "BILLING",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
charges: [{ transactionType: "CG", amountExtended: { price: "150.00", denomination: "USD" } }],
};
Extends
MessageEnvelope
Properties
charges
readonlycharges: readonlyDftCharge[]
FT1 content. Required, non-empty: a DFT with no transaction is a typed error.
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
event?
readonlyoptionalevent?:AdtEvent
EVN content (EVN-1 is the trigger event; EVN-2/6 optional).
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
visit?
readonlyoptionalvisit?:AdtVisit
PV1 content. Optional; a PV1 is emitted only when supplied.
BuildMdmInit
Input for buildMdm: the MSH envelope plus the typed segment bodies.
Example
import type { BuildMdmInit } from "@cosyte/hl7";
const init: BuildMdmInit = {
sendingApp: "TRANSCRIPTION",
receivingApp: "EHR",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
document: { documentType: "DS", body: [{ valueType: "TX", value: "Report text." }] },
};
Extends
MessageEnvelope
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
document
readonlydocument:MdmDocument
TXA content. Required: a document notification with no document is a typed error.
event?
readonlyoptionalevent?:AdtEvent
EVN content (EVN-1 is the trigger event; EVN-2/6 optional).
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
visit?
readonlyoptionalvisit?:AdtVisit
PV1 content. Optional; an (empty) PV1 is emitted regardless so the visit group is present.
BuildMessageInit
Input shape for buildMessage (SER-06). Mirrors msg.meta 1-for-1 so
read and write surfaces share field names (sendingApp, sendingFacility,
receivingApp, receivingFacility, controlId, timestamp, version,
processingId). type is the only required field.
Empty string vs. undefined semantics: omitting a field and passing an
empty string produce IDENTICAL wire output (both emit as an absent
positional field). To emit an HL7 explicit null ("") at a specific
position in an outbound message, build the message first and then call
.setField(path, '""'): the mutation method sets isNull=true
on the underlying RawField, and the emitter preserves that as the
literal two-char output per D-02.
Example
import { buildMessage } from "@cosyte/hl7";
const msg = buildMessage({
type: "ADT^A01",
sendingApp: "CLINIC",
sendingFacility: "MAIN",
receivingApp: "LAB",
receivingFacility: "REF",
timestamp: new Date("2026-04-19T10:15:00Z"),
})
.addSegment("PID", ["", "", "MRN123", "", "Doe^John"]);
console.log(msg.toString());
// To emit HL7 explicit null ("") instead of absent:
// msg.setField("PID.2", '""'); // distinct from empty/omitted
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted (D-12).
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
receivingApp?
readonlyoptionalreceivingApp?:string
receivingFacility?
readonlyoptionalreceivingFacility?:string
sendingApp?
readonlyoptionalsendingApp?:string
sendingFacility?
readonlyoptionalsendingFacility?:string
timestamp?
readonlyoptionaltimestamp?:string|Date
Date formatted to HL7 YYYYMMDDHHmmss (UTC, seconds) when supplied;
pre-formatted HL7 TS string passed through verbatim (D-13). Defaults to
new Date().
type
readonlytype:string
HL7 message type, e.g. "ADT^A01" (code + trigger) or
"ORU^R01^ORU_R01" (code + trigger + structure). Required (D-16).
The string is split on ^ into MSH-9 components; each component is
emitted verbatim. Literal ^ characters in a component are NOT
representable via this field: splitting is unconditional. Callers
needing that edge case should build the message and then use
.setField("MSH.9.1", ...) etc. after construction.
Rejected at runtime (D-16 / WR-04):
- empty string
""or whitespace-only" "; - strings whose every
^-split component is empty/whitespace (e.g."^","^^"," ^ ").
version?
readonlyoptionalversion?:string
Defaults to "2.5".
BuildOrmInit
Input for buildOrm: the MSH envelope plus the typed segment bodies.
Example
import type { BuildOrmInit } from "@cosyte/hl7";
const init: BuildOrmInit = {
sendingApp: "EHR",
receivingApp: "LAB",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
orders: [{ orderControl: "NW", universalServiceId: { identifier: "CBC" } }],
};
Extends
MessageEnvelope
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
orders
readonlyorders: readonlyOrmOrder[]
ORC/OBR content. Required, non-empty: an ORM with no order is a typed error.
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
BuildOruInit
Input for buildOru: the MSH envelope plus the typed segment bodies.
Extends
MessageEnvelope
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
observations
readonlyobservations: readonlyOruObservation[]
OBX content. Required, non-empty: an ORU with no result is a typed error.
order?
readonlyoptionalorder?:OruOrder
OBR content. Optional; an (empty) OBR is emitted regardless so the result group is well-formed.
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
BuildSiuInit
Input for buildSiu: the MSH envelope plus the typed segment bodies.
Example
import type { BuildSiuInit } from "@cosyte/hl7";
const init: BuildSiuInit = {
sendingApp: "SCHEDULING",
receivingApp: "EHR",
appointment: { fillerAppointmentId: "FL-2002", startDateTime: "20260801090000" },
resourceGroups: [{ setId: "1", resources: [{ kind: "location", code: { identifier: "OR-1" } }] }],
};
Extends
MessageEnvelope
Properties
appointment
readonlyappointment:SiuAppointment
SCH content. Required: never fabricated.
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
patient?
readonlyoptionalpatient?:AdtPatient
PID content. Optional: the patient group of the published SIU structure is optional, so a PID is emitted only when supplied.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
resourceGroups
readonlyresourceGroups: readonlySiuResourceGroup[]
RGS content. Required, non-empty: the published structure needs a resource group.
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
visit?
readonlyoptionalvisit?:AdtVisit
PV1 content. Optional; emitted only when supplied, and only alongside a patient.
BuildVxuInit
Input for buildVxu: the MSH envelope plus the typed segment bodies.
Example
import type { BuildVxuInit } from "@cosyte/hl7";
const init: BuildVxuInit = {
sendingApp: "CLINIC",
receivingApp: "IIS",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
immunizations: [{ vaccineCode: { identifier: "115", nameOfCodingSystem: "CVX" } }],
};
Extends
MessageEnvelope
Properties
controlId?
readonlyoptionalcontrolId?:string
Auto-generated via generateControlId() when omitted.
Inherited from
MessageEnvelope.controlId
immunizations
readonlyimmunizations: readonlyVxuImmunization[]
RXA content. Required, non-empty: a vaccination update with no dose is a typed error.
patient
readonlypatient:AdtPatient
PID content. Required: never fabricated.
processingId?
readonlyoptionalprocessingId?:string
Defaults to "P" (production).
Inherited from
MessageEnvelope.processingId
receivingApp?
readonlyoptionalreceivingApp?:string
Inherited from
MessageEnvelope.receivingApp
receivingFacility?
readonlyoptionalreceivingFacility?:string
Inherited from
MessageEnvelope.receivingFacility
sendingApp?
readonlyoptionalsendingApp?:string
Inherited from
MessageEnvelope.sendingApp
sendingFacility?
readonlyoptionalsendingFacility?:string
Inherited from
MessageEnvelope.sendingFacility
timestamp?
readonlyoptionaltimestamp?:string|Date
Date → HL7 YYYYMMDDHHmmss (UTC, seconds); a pre-formatted HL7 TS string
passes through verbatim. Defaults to new Date().
Inherited from
MessageEnvelope.timestamp
version?
readonlyoptionalversion?:string
Defaults to "2.5".
Inherited from
MessageEnvelope.version
Cardinality
A repetition-count constraint. min / max are inclusive bounds on the
number of repetitions (for a field rule) or occurrences (for a segment
rule). max may be the literal "*" for "unbounded". Omitted bounds are
unconstrained on that side.
Cardinality min is checked only when the element is present. An absent
Required element is reported as FINDING_CODES.PROFILE_REQUIRED_ABSENT
(a usage finding), not a cardinality finding: so a missing R field with
cardinality.min = 1 yields exactly one finding, never two.
Example
import type { Cardinality } from "@cosyte/hl7";
const once: Cardinality = { min: 1, max: 1 };
const many: Cardinality = { min: 1, max: "*" };
Properties
max?
readonlyoptionalmax?:number|"*"
min?
readonlyoptionalmin?:number
CE
HL7 v2 Coded Element (CE): coded element per HL7 Chapter 2. All 6 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- identifier (e.g. "GLU")
- text (human-readable, e.g. "Glucose")
- nameOfCodingSystem (e.g. "LN" for LOINC)
- alternateIdentifier
- alternateText
- nameOfAlternateCodingSystem
Components 7+ (present when a CWE-shaped value is read through the CE
accessor: e.g. version ids, originalText) are surfaced verbatim on
extraComponents rather than dropped.
Example
import type { CE } from "@cosyte/hl7";
const code: CE = { identifier: "GLU", text: "Glucose", nameOfCodingSystem: "LN" };
Properties
alternateIdentifier?
readonlyoptionalalternateIdentifier?:string
alternateText?
readonlyoptionalalternateText?:string
extraComponents?
readonlyoptionalextraComponents?: readonlystring[]
Components beyond the modeled 6 (HL7 component 7 onward), preserved
verbatim and in order. Non-empty only when a CWE-shaped value is read
through the CE accessor; OMITTED otherwise. An absent interior component
is preserved as "" so extraComponents[i] maps to HL7 component
7 + i.
identifier?
readonlyoptionalidentifier?:string
nameOfAlternateCodingSystem?
readonlyoptionalnameOfAlternateCodingSystem?:string
nameOfCodingSystem?
readonlyoptionalnameOfCodingSystem?:string
text?
readonlyoptionaltext?:string
Charge
FT1-derived charge entry (DFT financial breadth). Billing-critical fields surfaced with no billing logic and no money-as-float: the extended/unit amounts are the verbatim CP wire text, never parsed to a number.
Example
import type { Charge } from "@cosyte/hl7";
const c: Charge = {
transactionType: "CG",
transactionCode: { identifier: "80053", text: "Metabolic panel" },
amountExtended: "150.00^USD",
diagnoses: [{ identifier: "E11.9" }],
};
Properties
amountExtended?
readonlyoptionalamountExtended?:string
FT1-11 transaction amount, extended (CP): canonical wire text (e.g. 150.00^USD, byte-exact for a plain amount); never parsed to a number.
amountUnit?
readonlyoptionalamountUnit?:string
FT1-12 transaction amount, unit (CP): canonical wire text; never parsed to a number.
diagnoses
readonlydiagnoses: readonlyCWE[]
FT1-19 diagnosis code(s) linked to this charge (CE, repeating): billing diagnosis linkage. Always present (possibly empty).
quantity?
readonlyoptionalquantity?:number
FT1-10 transaction quantity (NM; strict-parsed, never NaN).
transactionCode?
readonlyoptionaltransactionCode?:CWE
FT1-7 transaction code: the institution charge/procedure code (CWE, provenance-only, never validated).
transactionDate?
readonlyoptionaltransactionDate?:DtmParts
FT1-4 transaction date (fidelity TS).
transactionType?
readonlyoptionaltransactionType?:string
FT1-6 transaction type (HL7 Table 0017: CG charge, CD credit, PY payment, AJ adjustment). Verbatim.
CharsetResolution
The outcome of resolving an MSH-18 label (or options.charset override)
against Table 0211.
Properties
canonical
readonlycanonical:string
The canonical Table-0211 code the label resolved to (e.g. "UTF-8",
"8859/1", "ISO IR87"). For an unrecognized label this is the input
trimmed + upper-cased, so it is still a stable key for comparison.
decoder
readonlydecoder:string
The WHATWG TextDecoder label to decode with when treatment === "decode".
Empty when treatment === "verbatim".
recognized
readonlyrecognized:boolean
true when the label is a recognized HL7 Table-0211 code (whether or not
the parser decodes it); false when the label is not in Table 0211.
Drives the warning code on the verbatim path: recognized-but-verbatim
→ UNSUPPORTED_CHARSET, unrecognized → UNKNOWN_CHARSET.
treatment
readonlytreatment:CharsetTreatment
Whether the parser decodes this set or preserves its bytes verbatim.
ClinicalDocument
TXA-derived clinical-document entry (MDM document breadth). The load-bearing safety property: completion status (TXA-17) and availability status (TXA-19) are DISTINCT fields and are never conflated: a document can be available before it is authenticated, and reading a preliminary document as final is the clinical harm. Both are verbatim / provenance-only.
Example
import type { ClinicalDocument } from "@cosyte/hl7";
const doc: ClinicalDocument = {
documentType: "DS",
completionStatus: "IP", // in progress: NOT yet authenticated
availabilityStatus: "AV", // available: a different axis
observations: [],
};
Properties
activityDateTime?
readonlyoptionalactivityDateTime?:DtmParts
TXA-4 activity date/time (fidelity TS).
availabilityStatus?
readonlyoptionalavailabilityStatus?:string
TXA-19 document availability status (HL7 Table 0273: AV available,
CA cancelled, OB obsolete, UN unavailable). Surfaced DISTINCT from
completionStatus; verbatim, never validated, never merged.
completionStatus?
readonlyoptionalcompletionStatus?:string
TXA-17 document completion status (HL7 Table 0271: e.g. DO documented,
IP in progress, AU authenticated, LA legally authenticated, IN
incomplete). Surfaced DISTINCT from availabilityStatus; verbatim,
never validated, never merged.
documentType?
readonlyoptionaldocumentType?:string
TXA-2 document type (HL7 Table 0270), verbatim.
observations
readonlyobservations: readonlyObservation[]
OBX narrative body grouped under this TXA. Always present (possibly empty).
parentDocumentNumber?
readonlyoptionalparentDocumentNumber?:string
TXA-13 parent document number (EI first component): addendum / replacement link.
uniqueDocumentNumber?
readonlyoptionaluniqueDocumentNumber?:string
TXA-12 unique document number (EI first component, verbatim).
CodedSystemFields
Structural shape of any coded element that carries a primary + alternate
coding system: both CWE and CE satisfy it. Kept structural
(not a union) so callers can pass a dg.code / obs.code directly.
Properties
nameOfAlternateCodingSystem?
readonlyoptionalnameOfAlternateCodingSystem?:string
nameOfCodingSystem?
readonlyoptionalnameOfCodingSystem?:string
CodingSystemInfo
A coding-system provenance answer: the system a code CLAIMS, never
validated. claimed is always present and verbatim (never dropped); the
resolved id / name are present only when the claim maps to a registered
Table 0396 entry.
Properties
claimed
readonlyclaimed:string
The coding-system id exactly as it appeared in CWE.3 / CE.3: preserved verbatim (original case and spelling), never altered, never dropped.
id?
readonlyoptionalid?:string
Registered Table 0396 acronym (alias-normalized). Present only when known.
known
readonlyknown:boolean
true when claimed resolved (directly or via alias) to a registered Table 0396 entry.
name?
readonlyoptionalname?:string
Canonical human-readable name. Present only when known.
ComparisonPredicate
A comparison statement: does the content at location stand in the verb
relation to one of the values?
The value read at a location is the subcomponent at the coordinate, with an
omitted component and subcomponent both defaulting to 1 (a coded
element's code), exactly as a field rule's own component default does.
values must carry at least one entry: a comparison with no value list is
PROFILE_MALFORMED, and is a compile error where the author writes
TypeScript.
A comparison against an element the message does not carry is unevaluatable, not false: there is no content to compare. It is reported as FINDING_CODES.PROFILE_CONDITION_UNEVALUATABLE rather than silently deciding the conditional.
The statement is quantified over repetitions and occurrences. Where the
location names a repeating field, or a segment that occurs more than once, the
comparison reads one value per repetition per occurrence and is true when ONE
OR MORE of them stands in the verb's relation to the value list. That is the
language's default occurrence semantics, and it covers the negative verbs too:
is not is true when SOME value is not a listed one, not when every value is
not. So at a repeating location a verb and its negative can both decide true
against the same value list. PredicateVerb works the case through.
Example
import type { ComparisonPredicate } from "@cosyte/hl7";
// "IF RXA-20 (Completion Status) contains one of the values in the list: {'CP', 'PA'}"
const predicate: ComparisonPredicate = {
location: { segment: "RXA", field: 20 },
verb: "contains",
values: ["CP", "PA"],
};
Properties
location
readonlylocation:PredicateLocation
The element whose content the statement compares.
values
readonlyvalues: readonlystring[]
The value list, at least one entry. For matches / does not match each
entry is a regular-expression source; anything the platform cannot compile
is PROFILE_MALFORMED.
verb
readonlyverb:PredicateVerb
The relation to test.
ComponentRule
A rule for one component position inside a field, declared on a field rule's FieldRule.components.
A component's cardinality is IMPLIED by its usage and may not be
declared. The HL7 conformance methodology (section 5.2, Cardinality) states
it outright: an explicit cardinality range is required for segment groups,
segments and field elements, while component and sub-component elements do
not explicitly include one, and the range implicitly associated with each
depends on its usage code: [1..1] for R, [0..1] for RE and O,
[0..0] for X. A component carries no repetition construct in HL7 v2, so
[1..1] reduces to "present with a value exactly once" and [0..0] to
"absent or empty": the observable is a presence finding, never a count
finding. Declaring a cardinality here is
FINDING_CODES.PROFILE_MALFORMED, not a silently-honoured extra
constraint.
Declaring component rules CLOSES the field's component set. A field rule that declares them says what that field's components are, so a present repetition carrying a non-empty value at an index the rule does not declare is FINDING_CODES.PROFILE_UNDECLARED_CONTENT. A field rule that declares NONE declares no depth and is checked exactly as it always was: the undeclared-content check can never fire for it. "None" means no component rule reaches the engine, so an EMPTY list says exactly what omitting the member says; it is never read as a declaration that the field has no components.
Example
import type { ComponentRule } from "@cosyte/hl7";
const identifier: ComponentRule = { component: 1, usage: "R" };
const checkDigit: ComponentRule = { component: 2, usage: "X" };
Properties
component
readonlycomponent:number
1-indexed component position within the field (e.g. 4 for PID-3.4).
name?
readonlyoptionalname?:string
Optional human label for the component (e.g. "Assigning Authority").
Structural documentation for the profile author only: findings identify a
component by its PHI-free structural locus (segment + field + index), never
by this label, so the label is never echoed into a finding message.
severity?
readonlyoptionalseverity?:FindingSeverity
Severity for findings this component rule produces. Defaults to the
severity of the field rule that carries it (itself "error" by default),
so downgrading a field rule downgrades its components with it.
usage?
readonlyoptionalusage?:UsageCode
Usage constraint for this component, from the same vocabulary a field rule declares (see UsageCode). Its cardinality follows from it and is never declared beside it.
R: a present repetition SHALL carry a non-empty value here. Absent ⇒ FINDING_CODES.PROFILE_REQUIRED_ABSENT.X: a present repetition SHALL NOT carry a value here. Present ⇒ FINDING_CODES.PROFILE_NOT_PERMITTED.RE/O: no presence constraint.C/CE/B/ a declared conditional: presence is not evaluated, on exactly the terms an undecided conditional field rule gets. A component rule declares no condition predicate and no caller resolution can name one, so nothing ever decides it.
Omitted ⇒ Optional, and an omitted usage is never refused. The component still counts as DECLARED for the undeclared-content check: declaring the index is what closes the set, not constraining it.
CompositeValueByKind
Maps each CompositeKind to the typed value encodeComposite
(and setComposite) accept for it. TS also accepts a pre-formatted HL7
timestamp string; NM also accepts a number or a raw numeric string,
both are emitted verbatim (the serializer never re-formats a claimed value).
Properties
CE
readonlyCE:CE
CWE
readonlyCWE:CWE
CX
readonlyCX:CX
HD
readonlyHD:HD
NM
readonlyNM:string|number|NM
PL
readonlyPL:PL
TS
readonlyTS:string|DtmParts
XAD
readonlyXAD:XAD
XCN
readonlyXCN:XCN
XPN
readonlyXPN:XPN
XTN
readonlyXTN:XTN
ConformanceFinding
One typed conformance finding. Carries the FindingCode, a
FindingSeverity, the structural FindingLocus, and a
human-readable message describing the rule that fired.
The message is PHI-safe by construction: it names the locus, the rule,
and (for a value-set miss) the SIZE of the value set, but never the
offending field value.
Example
import type { ConformanceFinding } from "@cosyte/hl7";
const f: ConformanceFinding = {
code: "PROFILE_VALUE_NOT_IN_SET",
severity: "error",
locus: { segment: "PID", field: 8, component: 1 },
message: 'PID-8 component 1 value is not in the profile value set (3 permitted codes).',
};
Properties
code
readonlycode:FindingCode
locus
readonlylocus:FindingLocus
message
readonlymessage:string
severity
readonlyseverity:FindingSeverity
ConformanceProfile
A user-authored, declarative conformance profile. The consumer supplies this; hl7 ships none. It is a bounded subset of the HL7 v2 Message-Profile model: usage / condition predicate / cardinality / length / consumer-supplied value set: with no bundled code set and no network binding (both deliberate scope boundaries). A condition predicate reads the message in front of it and nothing else.
Example
import type { ConformanceProfile } from "@cosyte/hl7";
// A minimal ADT profile the CONSUMER authors: example, NOT an attestation.
const profile: ConformanceProfile = {
name: "example-adt-min",
segments: [
{ segment: "MSH", usage: "R", fields: [{ field: 10, name: "Control ID", usage: "R" }] },
{ segment: "PID", usage: "R", cardinality: { min: 1, max: 1 }, fields: [
{ field: 3, name: "Patient Identifiers", usage: "R", cardinality: { min: 1, max: 1 } },
{ field: 8, name: "Administrative Sex", usage: "RE", valueSet: ["M", "F", "U"] },
] },
{ segment: "ZZZ", usage: "X" },
],
};
Properties
level?
readonlyoptionallevel?:ProfileLevel
The ProfileLevel this profile CLAIMS, echoed into ConformanceResult.level.
Omitting it claims constrainable, the weaker of the two claims a
working profile can make, so a profile that says nothing never reads as an
implementable one. Declaring standard or constrainable imposes no
level-derived constraint on any element: both levels are defined as leaving
optionality in place, so there is nothing for the engine to check.
Declaring implementable is a claim the engine CHECKS, because the
level asserts that all optionality and openness have been removed. Every
segment, field and component rule must declare a usage, and that usage must
be R, RE or X, or a declared conditional both of whose outcomes are
drawn from those three. A rule left at C, CE, O or B, a conditional
whose outcomes are not, and a rule that declares no usage at all each refuse
the claim as FINDING_CODES.PROFILE_MALFORMED rather than being
accepted on the author's word.
The level changes NO message check: it alters which findings can fire for a given message not at all. It is a declaration plus its gate.
name
readonlyname:string
A name for provenance: echoed into ConformanceResult.profileName.
segments
readonlysegments: readonlySegmentRule[]
The segment rules, evaluated in array order (stable finding order).
ConformanceResult
The result of validateAgainstProfile: the profile's name, the
ProfileLevel in force, and the ordered list of findings.
findings.length === 0 is NOT a conformance attestation. It means every
rule the profile declared was satisfied: nothing about the parts of the
message the profile did not cover, and nothing about clinical correctness.
Read it as "no declared rule was violated," never as "conformant." That holds
at every level, implementable included: the level says the profile removed
its own optionality, not that anyone accredited the result.
What the level DOES tell you is how to read an empty findings list. At
implementable level every element the profile declares was reduced to a
supported / not-supported decision, so the assessment against that profile is
complete. At standard or constrainable level it is not: elements the
profile left optional were never asserted either way, so an empty list is
consistent with parts of the message having gone unassessed.
Properties
findings
readonlyfindings: readonlyConformanceFinding[]
level
readonlylevel:ProfileLevel
The ProfileLevel the assessment was actually made at.
Present on EVERY result, including one for a profile so malformed its name
could not be read. A profile that declares no level is assessed and echoed
as constrainable; a profile whose declaration is not one of the three
levels is echoed the same way, alongside the
FINDING_CODES.PROFILE_MALFORMED finding that names it.
A REFUSED implementable claim is never echoed here. Where the engine
returned PROFILE_MALFORMED findings it did not complete the assessment
the claim asserts, so the level in force falls back to constrainable
rather than repeating a claim nothing verified.
profileName
readonlyprofileName:string
ConnectedPredicate
Two statements joined by a connector. Nests: either side may itself be a connected predicate.
A connected predicate is unevaluatable exactly when its result DEPENDS on an
unevaluatable operand. So AND is false as soon as one side is false however
unevaluatable the other is, OR is true as soon as one side is true, and
XOR needs both sides.
Example
import type { ConnectedPredicate } from "@cosyte/hl7";
// "IF RXA-9.1 contains '00' AND RXA-20 contains one of the values in the list: {'CP','PA'}"
const predicate: ConnectedPredicate = {
connector: "AND",
left: { location: { segment: "RXA", field: 9, component: 1 }, verb: "contains", values: ["00"] },
right: { location: { segment: "RXA", field: 20 }, verb: "contains", values: ["CP", "PA"] },
};
Properties
connector
readonlyconnector:PredicateConnector
Which connector joins the two operands.
left
readonlyleft:ConditionPredicate
The left-hand operand.
right
readonlyright:ConditionPredicate
The right-hand operand.
CustomSegmentDefinition
Shape of a single segment field-name declaration used by profile authoring
(defineProfile()). fields maps a caller-visible field NAME to its
1-indexed HL7 position within the segment. Declared here (alongside
Profile) to keep the parser's type module the single source of truth;
src/profiles/define.ts re-exports this type, so consumers can write
import type { CustomSegmentDefinition } from "@cosyte/hl7".
It is the value shape of BOTH declaration maps, and which map a declaration
sits in is what decides where the name may be used: customSegments names
fields on a custom Z-segment, segmentOverrides names fields on a standard
HL7 v2 segment. The shape is identical because the meaning of a position is:
a 1-indexed HL7 field position, read exactly as Segment.field(n) reads it.
Example
import type { CustomSegmentDefinition } from "@cosyte/hl7";
const zdp: CustomSegmentDefinition = {
fields: { departmentCode: 3, departmentName: 4 },
};
Properties
fields
readonlyfields:Readonly<Record<string,number>>
CWE
HL7 v2 Coded with Exceptions (CWE): coded element per HL7 Chapter 2. All 9 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- identifier (e.g. "GLU")
- text (human-readable, e.g. "Glucose")
- nameOfCodingSystem (e.g. "LN" for LOINC, "SCT" for SNOMED CT)
- alternateIdentifier
- alternateText
- nameOfAlternateCodingSystem
- codingSystemVersionId
- alternateCodingSystemVersionId
- originalText
Components 10+ (present only on v2.7+ senders) are surfaced verbatim, in
order, on extraComponents: never silently truncated.
Example
import type { CWE } from "@cosyte/hl7";
const code: CWE = { identifier: "GLU", text: "Glucose", nameOfCodingSystem: "LN" };
Properties
alternateCodingSystemVersionId?
readonlyoptionalalternateCodingSystemVersionId?:string
alternateIdentifier?
readonlyoptionalalternateIdentifier?:string
alternateText?
readonlyoptionalalternateText?:string
codingSystemVersionId?
readonlyoptionalcodingSystemVersionId?:string
extraComponents?
readonlyoptionalextraComponents?: readonlystring[]
Components beyond the modeled 9 (HL7 component 10 onward), preserved
verbatim and in order for forward-compatibility with v2.7+ senders that
carry the second-alternate triplet or coding-system / value-set OIDs.
OMITTED when the element has no components past the 9th. An absent interior
component is preserved as "" so extraComponents[i] maps to HL7
component 10 + i.
identifier?
readonlyoptionalidentifier?:string
nameOfAlternateCodingSystem?
readonlyoptionalnameOfAlternateCodingSystem?:string
nameOfCodingSystem?
readonlyoptionalnameOfCodingSystem?:string
originalText?
readonlyoptionaloriginalText?:string
text?
readonlyoptionaltext?:string
CX
HL7 v2 Extended Composite ID (CX): structured identifier per HL7 Chapter
2. All 10 components are optional. Fields are OMITTED when the underlying
component is absent (exactOptionalPropertyTypes). assigningAuthority
uses the nested HD shape; assigningFacility is flattened to a plain
string in v1.
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- idNumber
- checkDigit
- checkDigitScheme (ISO 7064, M10, M11, NPI)
- assigningAuthority (nested HD: 3 subcomponents form a HD composite)
- identifierTypeCode (MR, SSN, DL, MC, ...)
- assigningFacility (v1: flattened to string; spec is HD-shaped)
- effectiveDate (raw HL7 TS string)
- expirationDate (raw HL7 TS string)
- assigningJurisdiction (v1: flattened to string)
- assigningAgencyOrDepartment (v1: flattened to string)
Example
import type { CX } from "@cosyte/hl7";
const mrn: CX = {
idNumber: "123456",
assigningAuthority: { namespaceId: "EPIC", universalId: "1.2.840.114350", universalIdType: "ISO" },
identifierTypeCode: "MR",
};
Properties
assigningAgencyOrDepartment?
readonlyoptionalassigningAgencyOrDepartment?:string
assigningAuthority?
readonlyoptionalassigningAuthority?:HD
assigningFacility?
readonlyoptionalassigningFacility?:string
assigningJurisdiction?
readonlyoptionalassigningJurisdiction?:string
checkDigit?
readonlyoptionalcheckDigit?:string
checkDigitScheme?
readonlyoptionalcheckDigitScheme?:string
effectiveDate?
readonlyoptionaleffectiveDate?:string
expirationDate?
readonlyoptionalexpirationDate?:string
identifierTypeCode?
readonlyoptionalidentifierTypeCode?:string
idNumber?
readonlyoptionalidNumber?:string
DateParts
The calendar components a datetime actually STATED, as a frozen plain
object. A component the value did not state is ABSENT: the key is not
present at all rather than present with undefined, so Object.keys() is
exactly the set of stated components and the value's precision is
recoverable from it. There is no raw, valid or precision key here:
that fidelity metadata stays on DtmParts.
month is SPEC-NATIVE 1 to 12, never the JS Date 0 to 11. Delete
offsetMinutes and what is left is accepted as-is by
Temporal.PlainDateTime.from and luxon's DateTime.fromObject, with no key
rename and no value adjustment. That compatibility is the reason for the
shape; neither library is a dependency here, and neither needs to be.
Example
import { parseDtm, toObject } from "@cosyte/hl7";
toObject(parseDtm("19880705"));
// { year: 1988, month: 7, day: 5 }: three keys, no zero-fill
Object.keys(toObject(parseDtm("1970")) ?? {});
// ["year"]: a year-precision value states one component
Properties
day?
readonlyoptionalday?:number
Day of month, 1 to 31.
hour?
readonlyoptionalhour?:number
Hour, 0 to 23.
millisecond?
readonlyoptionalmillisecond?:number
Milliseconds, derived from the stated fractional second by taking its
first three digits VERBATIM and right-padding with zeroes ("5" is 500,
"0500" is 50, "123456" is 123). Never computed by multiplying a
floating-point fraction by 1000, which loses the last digit on values such
as 0.123. Absent when the value stated no fraction.
minute?
readonlyoptionalminute?:number
Minute, 0 to 59.
month?
readonlyoptionalmonth?:number
Month, 1 to 12 (spec-native, NOT the JS Date 0 to 11).
offsetMinutes?
readonlyoptionaloffsetMinutes?:number
Signed minutes east of UTC, present IF AND ONLY IF the value carried an
explicit offset. A stated zero offset (including HL7's -0000) is present
as 0. Never synthesised from the host machine's zone.
second?
readonlyoptionalsecond?:number
Second, 0 to 59.
year?
readonlyoptionalyear?:number
Four-digit year, exactly as stated. A year below 100 stays below 100.
DefinedProfile
The profile defineProfile produces for one options object: every
member of the general Profile interface, with customSegments at the
declarations that options object actually carries (its own, merged with every
parent's).
It stays assignable to Profile with no cast, so a profile built by
the factory still passes to every API that accepts the general interface, and
a caller who annotates a variable with Profile simply gives the narrowing
up rather than hitting a type error.
Example
import { defineProfile, type Profile } from "@cosyte/hl7";
const epic = defineProfile({
name: "epic",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
const asGeneral: Profile = epic; // no cast: the narrowing is additive
console.log(asGeneral.name); // "epic"
Extends
Omit<Profile,"customSegments">
Type Parameters
O
O
Properties
customSegments
readonlycustomSegments:DeclaredSegments<O>
The merged declarations, at the literal segment types and field names.
dateFormats?
readonlyoptionaldateFormats?: readonlystring[]
Date formats this vendor writes. Merged after ParseOptions.dateFormats
and honoured on every datetime the library returns, exactly as the option
form is.
Inherited from
describe?
readonlyoptionaldescribe?: () =>string
Returns
string
Inherited from
description?
readonlyoptionaldescription?:string
Inherited from
lineage?
readonlyoptionallineage?: readonlystring[]
Inherited from
name
readonlyname:string
Inherited from
onWarning?
readonlyoptionalonWarning?:OnWarningCallback
Inherited from
segmentOverrides?
readonlyoptionalsegmentOverrides?:Readonly<Record<string,CustomSegmentDefinition>>
Site-specific field NAMES bound to positions on STANDARD HL7 v2 segments,
keyed by canonical segment name (PID, AL1, MSH, ...). A separate map
from Profile.customSegments, which stays Z-segment-only: a
standard name appearing there would change which segments the parser
treats as profile-claimed, and an author skimming a profile literal can
see at a glance which declarations touch standard clinical fields.
Read-side aliases, and ADDITIVE ONLY. A declaration here creates a new
name for Segment.get(name) to resolve on segments of that type, and
changes no existing read: positional access, dot-paths, the typed clinical
accessors, the warning list and both serializations are what they were
without it.
Inherited from
DefineProfileOptions
Options accepted by defineProfile() (D-02). Mirrors the locked
Profile shape plus the extends input key. Every field except
name is optional.
Example
import { defineProfile, type DefineProfileOptions } from "@cosyte/hl7";
const opts: DefineProfileOptions = {
name: "my-lab",
dateFormats: ["MM/DD/YYYY"],
customSegments: { ZLB: { fields: { noteText: 3 } } },
};
const profile = defineProfile(opts);
Properties
customSegments?
readonlyoptionalcustomSegments?:Readonly<Record<string,CustomSegmentDefinition>>
dateFormats?
readonlyoptionaldateFormats?: readonlystring[]
description?
readonlyoptionaldescription?:string
extends?
name
readonlyname:string
onWarning?
readonlyoptionalonWarning?:OnWarningCallback
segmentOverrides?
readonlyoptionalsegmentOverrides?:Readonly<Record<string,CustomSegmentDefinition>>
Field names bound to positions on STANDARD HL7 v2 segments, keyed by
canonical segment name. Sibling of customSegments, never a relaxation of
it: a Z-segment key here is refused and pointed back at customSegments,
and a key that is not a standard segment name is refused outright.
Every binding is a NEW read. Declaring one changes nothing an existing caller sees; see Profile.segmentOverrides.
DftAmount
A transaction amount (CP, composite price) for buildDft, supplied as its typed parts. Every part is emitted verbatim: the price keeps the precision the caller wrote, and the denomination is never inferred.
Example
import type { DftAmount } from "@cosyte/hl7";
const amount: DftAmount = { price: "150.00", denomination: "USD" };
Properties
denomination?
readonlyoptionaldenomination?:string
CP.1.2 Price denomination, e.g. "USD" (ISO 4217). Never inferred.
price?
readonlyoptionalprice?:string
CP.1.1 Price quantity, e.g. "150.00". Emitted verbatim, never a float.
priceType?
readonlyoptionalpriceType?:string
CP.2 Price type (HL7 Table 0205, e.g. "AP" administrative price).
DftCharge
Typed FT1 (financial transaction) content for buildDft.
Example
import type { DftCharge } from "@cosyte/hl7";
const charge: DftCharge = {
setId: "1",
transactionDate: "20260801",
transactionType: "CG",
transactionCode: { identifier: "80053", text: "Metabolic panel" },
quantity: "1",
amountExtended: { price: "150.00", denomination: "USD" },
diagnoses: [{ identifier: "E11.9" }],
};
Properties
amountExtended?
readonlyoptionalamountExtended?:DftAmount
FT1-11 Transaction Amount, Extended.
amountUnit?
readonlyoptionalamountUnit?:DftAmount
FT1-12 Transaction Amount, Unit.
diagnoses?
FT1-19 Diagnosis Code(s): the billing diagnosis linkage, repeating.
postingDate?
readonlyoptionalpostingDate?:string|DtmParts
FT1-5 Transaction Posting Date.
quantity?
readonlyoptionalquantity?:string|number|NM
FT1-10 Transaction Quantity (emitted verbatim: the caller owns its precision).
setId?
readonlyoptionalsetId?:string
FT1-1 Set ID.
transactionCode?
readonlyoptionaltransactionCode?:CWE
FT1-7 Transaction Code: the institution charge or procedure code.
transactionDate?
readonlyoptionaltransactionDate?:string|DtmParts
FT1-4 Transaction Date.
transactionId?
readonlyoptionaltransactionId?:string
FT1-2 Transaction ID.
transactionType?
readonlyoptionaltransactionType?:string
FT1-6 Transaction Type (HL7 Table 0017: CG, CD, PY, AJ).
Diagnosis
DG1-derived diagnosis entry (HELPERS-06). dateTime is the fidelity TS.
Example
import type { Diagnosis } from "@cosyte/hl7";
const dg: Diagnosis = {
code: { identifier: "E11.9", text: "Type 2 diabetes" },
description: "Type 2 diabetes mellitus without complications",
type: "F",
};
Properties
code?
readonlyoptionalcode?:CWE
DG1-3 diagnosis code.
dateTime?
readonlyoptionaldateTime?:DtmParts
DG1-5 diagnosis date/time as the fidelity TS.
description?
readonlyoptionaldescription?:string
DG1-4 diagnosis description.
type?
readonlyoptionaltype?:string
DG1-6 diagnosis type (A=admitting, W=working, F=final).
DotPath
Parsed representation of a dot-path string. Produced by parsePath,
consumed by resolvePath. All numeric indices are normalized to the
internal convention (segmentIndex = 0-based occurrence, fieldIndex =
1-based HL7 field number, repetitionIndex = 0-based rep, componentIndex
and subcomponentIndex = 1-based HL7 positions).
Example
import { parsePath } from "@cosyte/hl7";
parsePath("OBX[2].5.1");
// { segmentType: "OBX", segmentIndex: 2, fieldIndex: 5,
// repetitionIndex: 0, componentIndex: 1 }
Properties
componentIndex?
readonlyoptionalcomponentIndex?:number
1-based HL7 component position (within a repetition).
fieldIndex
readonlyfieldIndex:number
1-based HL7 field number; maps to RawSegment.fields[fieldIndex].
repetitionIndex?
readonlyoptionalrepetitionIndex?:number
0-based repetition index; defaults to 0 when [N] is omitted.
segmentIndex
readonlysegmentIndex:number
0-based occurrence of this segment type in the message.
segmentType
readonlysegmentType:string
3-char segment identifier (e.g. "PID", "OBX", "ZPI").
subcomponentIndex?
readonlyoptionalsubcomponentIndex?:number
1-based HL7 subcomponent position (within a component).
DtmAmbiguity
Report attached to a timestamp the parser REFUSED to resolve because the value is order-ambiguous: a slash-separated numeric date whose first two components are both in 1-12, so a month-first and a day-first reading are each a legal calendar date and no evidence chooses between them.
The parser reports the two readings rather than picking one, because picking
one produces a plausible, wrong calendar date and nothing downstream can tell
that it was invented. Supply the vendor's order through
parseHL7(raw, { dateFormats: ["DD/MM/YYYY"] }) or a profile's
dateFormats, which are both tried ahead of the built-ins, and the value
resolves with no report at all.
Example
import { parseHL7 } from "@cosyte/hl7";
const ts = parseHL7(raw).meta.timestamp; // MSH-7 was "05/07/1988"
console.log(ts?.valid); // false: nothing was resolved
console.log(ts?.ambiguity?.raw); // "05/07/1988"
console.log(ts?.ambiguity?.candidates[0]); // MM/DD/YYYY -> 1988-05-07
console.log(ts?.ambiguity?.candidates[1]); // DD/MM/YYYY -> 1988-07-05
Properties
candidates
readonlycandidates: readonly [DtmAmbiguityCandidate,DtmAmbiguityCandidate]
Both readings, month-first then day-first. Exactly two, and they always disagree: a value whose readings coincide is resolved, not refused.
code
readonlycode:"AMBIGUOUS_DATE_ORDER"
Always AMBIGUOUS_DATE_ORDER: the stable identifier to compare on.
message
readonlymessage:string
A one-line explanation naming the value, both readings, and the remedy.
raw
readonlyraw:string
The value exactly as it arrived, so the report names what was declined.
DtmAmbiguityCandidate
One of the two calendar dates an order-ambiguous slash value could denote:
the format that would produce it, its spec-native month (1-12) and day,
and the ISO YYYY-MM-DD rendering of the date part. Declaring format
through parseHL7(raw, { dateFormats: [...] }) or a profile is what makes
the parser return that reading.
Example
import type { DtmAmbiguityCandidate } from "@cosyte/hl7";
const dayFirst: DtmAmbiguityCandidate = {
format: "DD/MM/YYYY", month: 7, day: 5, isoDate: "1988-07-05",
};
Properties
day
readonlyday:number
Day of month under this reading, 1-31.
format
readonlyformat:string
The date format that yields this reading, e.g. "DD/MM/YYYY".
isoDate
readonlyisoDate:string
The date part of this reading as ISO YYYY-MM-DD, e.g. "1988-07-05".
month
readonlymonth:number
Month under this reading, 1-12 (spec-native, NOT JS 0-11).
DtmParts
Parsed HL7 v2 TS/DTM value: the raw string plus its structural parts, with
the stated precision and timezone fidelity preserved. This is the
shape of the TS composite (field.asTs()) and every helper datetime.
The parts are only populated when valid is true. month/day/… are
spec-native (month is 1–12, NOT the JS Date 0–11). offsetMinutes
is present iff hasTimezone is true and is signed minutes east of UTC
(+0500 → 300, -0430 → -270). A missing offset is flagged
(hasTimezone: false), never resolved to UTC: the consumer decides how to
localize with dtmToDate.
Example
import type { DtmParts } from "@cosyte/hl7";
const dob: DtmParts = {
raw: "19880705", valid: true, precision: "day",
year: 1988, month: 7, day: 5, hasTimezone: false,
};
Properties
ambiguity?
readonlyoptionalambiguity?:DtmAmbiguity
Why an order-ambiguous slash date was refused, naming the raw value and
both readings it could have had. Present ONLY when valid is false
because the field order could not be established; absent on every other
result, including a plain malformed one. See DtmAmbiguity.
day?
readonlyoptionalday?:number
Day of month, 1–31.
fractionalSeconds?
readonlyoptionalfractionalSeconds?:string
Fractional-second digits exactly as populated (no leading dot), e.g.
"5" (0.5 s), "0500" (0.05 s). Preserved verbatim: never rounded.
hasTimezone
readonlyhasTimezone:boolean
true iff an explicit +/-ZZZZ offset was present.
hour?
readonlyoptionalhour?:number
Hour, 0–23.
matchedFormat?
readonlyoptionalmatchedFormat?:string
The non-canonical format that matched, e.g. "MM/DD/YYYY": normally one
the caller declared in dateFormats, or "ISO-8601" and the other
BUILTIN_DATE_FALLBACKS entries, which only msg.meta.timestamp
can reach. Absent for a strict HL7 DTM parse.
minute?
readonlyoptionalminute?:number
Minute, 0–59.
month?
readonlyoptionalmonth?:number
Month, 1–12 (spec-native, NOT JS 0–11).
offsetMinutes?
readonlyoptionaloffsetMinutes?:number
Signed minutes east of UTC; present iff hasTimezone is true.
precision?
readonlyoptionalprecision?:DtmPrecision
Stated precision; absent when valid is false.
raw
readonlyraw:string
The original HL7 string, exactly as it appeared (already unescaped).
second?
readonlyoptionalsecond?:number
Second, 0–59.
valid
readonlyvalid:boolean
true when raw is a well-formed, in-range HL7 DTM (or a matched
fallback format). false for empty, malformed, calendar-out-of-range, or
order-ambiguous input: in which case only raw, hasTimezone: false and
(for the ambiguous case) DtmParts.ambiguity are meaningful.
year?
readonlyoptionalyear?:number
Four-digit year.
DtmToDateOptions
Options controlling how dtmToDate resolves a missing timezone.
Example
import { parseDtm, dtmToDate } from "@cosyte/hl7";
// Treat an offset-less value as UTC (an explicit caller choice):
dtmToDate(parseDtm("20250102"), { assumeOffsetMinutes: 0 });
// ...or as US Eastern standard time (UTC-05:00):
dtmToDate(parseDtm("20250102"), { assumeOffsetMinutes: -300 });
Properties
assumeOffsetMinutes?
readonlyoptionalassumeOffsetMinutes?:number
Offset (signed minutes east of UTC) to assume when the value carries no
timezone. Without it, an offset-less value resolves to undefined,
dtmToDate never guesses a zone. Ignored when the value already has
an offset. Pass 0 to explicitly treat a naive value as UTC.
EncodingCharacters
The HL7 delimiter characters discovered from MSH-1 (field separator) and
MSH-2 (encoding characters). The first four: component, repetition,
escape, subcomponent: are mandatory across all HL7 v2 versions. The fifth,
truncation, is the v2.7+ truncation character (default # per spec
§2.5.5.2): only present when MSH-2 actually carries 5 encoding characters,
so messages that pre-date v2.7 round-trip with a 4-char MSH-2 unchanged.
Example
import type { EncodingCharacters } from "@cosyte/hl7";
const v25: EncodingCharacters = {
field: "|",
component: "^",
repetition: "~",
escape: "\\",
subcomponent: "&",
};
const v27: EncodingCharacters = { ...v25, truncation: "#" };
Properties
component
readonlycomponent:string
escape
readonlyescape:string
field
readonlyfield:string
repetition
readonlyrepetition:string
subcomponent
readonlysubcomponent:string
truncation?
readonlyoptionaltruncation?:string
ExpectedSegmentGroup
One expected segment for a recognized message type.
The published structure definition gives requiredSegment a minimum of one
along the whole path from the structure root, in every variant of the
referenced structure's family, so a structurally conformant message always
carries it. name and anchorSegments carry the same segment: they are the
long-standing shape of this type and a group is now exactly one required
segment, because the derivation works segment by segment rather than by
hand-picked bundle.
Properties
anchorSegments
readonlyanchorSegments: readonlystring[]
The segment name(s) whose presence satisfies this expectation.
name
readonlyname:string
The expectation's label, which is the required segment's name.
requiredSegment
readonlyrequiredSegment:string
The segment the published structure gives a minimum of one.
FieldRule
A rule for one field position within a segment. Every
constraint is optional; a rule with only a field index is a no-op.
Remarks
field is the 1-indexed HL7 position (MSH offset handled internally, exactly
like Segment.field(n): { field: 9 } on MSH targets MSH-9). Length and
value-set checks read the value at component (default 1), so a coded
field's code (CWE.1 / CE.1) is checked by default; set component to
check a different component. Both are applied per present repetition.
Properties
cardinality?
readonlyoptionalcardinality?:Cardinality
Repetition-count constraint for this field.
codingSystem?
readonlyoptionalcodingSystem?:string
Bind valueSet to the coding system its codes must be drawn from: a coding-system identifier this library recognizes, or any alias it recognizes for one. Declaring it turns "the code must be one of these" into "and it must be drawn from THIS system", so a feed sending a plausible code labelled with the wrong system stops validating clean.
The message's own coding-system component is compared by RESOLVED
IDENTITY, not by string equality: a feed sending loinc, LOINC or LN
all match a binding of LN, because each resolves to the same registered
acronym. A repetition whose coding-system component resolves to anything
else, including nothing at all, is
FINDING_CODES.PROFILE_CODING_SYSTEM_MISMATCH.
Opt-in and independent. Omitting it leaves a valueSet rule behaving
exactly as it always has. Declaring it adds a SECOND check rather than
changing the first: a permitted code carried under the wrong system yields
one coding-system finding and no value-set finding, and a code that is
wrong on both counts yields exactly one of each.
Recognition is this library's own frozen subset, not the whole
registry. An identifier it does not recognize is refused when the profile
is defined (FINDING_CODES.PROFILE_MALFORMED), rather than silently
compared against a system that cannot be resolved: so a local or
site-specific system cannot be bound, and a consumer with one keeps the
bare valueSet. A binding declared without a valueSet is refused the
same way: there is nothing there for it to bind.
This checks the sender's CLAIM, never the code. Nothing here verifies that the code exists in the system the message names: hl7 still ships no code set and makes no network call.
codingSystemComponent?
readonlyoptionalcodingSystemComponent?:number
1-indexed component carrying the coding-system identifier that a declared FieldRule.codingSystem is compared against.
Defaults to two positions after component, which is the coded
element's own code-to-system relationship: a code at component 1 puts its
system at component 3, and an alternate code at component 4 puts its
alternate system at component 6. So a rule checking the primary triplet
needs neither key, and a rule checking the alternate triplet sets
component alone.
Set it explicitly for a field whose layout does not follow that offset. Checking BOTH triplets from one rule is not supported: declare two rules on the same field.
component?
readonlyoptionalcomponent?:number
1-indexed component whose value the length / valueSet checks read.
Defaults to 1 (the first component: a coded element's code).
Not to be confused with FieldRule.components (plural), which declares per-component RULES and closes the field's component set. This one (singular) only says which component the length / value-set checks read; declaring it neither declares a component rule nor closes anything.
components?
readonlyoptionalcomponents?: readonlyComponentRule[]
Per-component rules for this field: what its components ARE and what each one's usage is. Applied per present repetition.
Declaring them closes the field's component set, which is what makes FINDING_CODES.PROFILE_UNDECLARED_CONTENT decidable: a present repetition carrying a non-empty value at an index no rule here declares is content the profile never specified, and the HL7 conformance methodology counts that as a conformance violation rather than harmless extra content (its own worked example is data in a fourth component where the profile defines three).
Omitting them changes nothing, and an empty list is omitting them. A
field rule that declares no component rules declares no depth: it is
checked exactly as it always has been, and the undeclared-content check can
never fire for it. components: [] carries no component rule, so it says
precisely that and is never read as "this field has no components" (which
would make every component of every repetition undeclared content). This is
additive and opt-in, per field rule.
Each component's cardinality is IMPLIED by its usage and may not be declared: see ComponentRule.
condition?
readonlyoptionalcondition?:ConditionPredicate
The ConditionPredicate that decides this rule's usage, evaluated
against the message. Only a rule whose usage is conditional (C, CE,
or a declared conditional) may declare one.
field
readonlyfield:number
1-indexed HL7 field position (e.g. 3 for PID-3, 9 for MSH-9).
length?
readonlyoptionallength?:number
Maximum character length of the checked component value (inclusive).
name?
readonlyoptionalname?:string
Optional human label for the field (e.g. "Patient Identifier List").
Structural documentation for the profile author only: findings identify a
field by its PHI-free structural locus (segment + index), never by this
label, so the label is never echoed into a finding message.
severity?
readonlyoptionalseverity?:FindingSeverity
Severity for findings this rule produces. Defaults to "error". A profile
author can downgrade a data-quality rule (e.g. a length or value-set check)
to "warning" or "info" without changing the check itself.
usage?
readonlyoptionalusage?:UsageCode
Usage constraint: a simple code, or a declared conditional such as
C(RE/X) (see UsageCode). Omitted ⇒ Optional, and an omitted
usage is never refused.
valueSet?
readonlyoptionalvalueSet?: readonlystring[]
Consumer-supplied permitted-value list. The checked component value must be a member (case-sensitive exact match). hl7 ships no code set: this is BYO terminology; membership is a literal string check, never a LOINC / SNOMED / ICD / RxNorm lookup and never a network call.
FindingLocus
The structural locus a finding refers to: segment name plus, where applicable, the field position, component, repetition, and segment occurrence. Every member is a name or an index: a locus is inherently PHI-free and never carries a field value.
A finding with no field is segment-level (presence / cardinality
of the segment itself); a finding with a field is field-level. For a
FINDING_CODES.PROFILE_MALFORMED diagnostic (a defect in the profile,
not the message) segment may be the sentinel "(profile)".
Properties
component?
readonlyoptionalcomponent?:number
1-indexed component, when a component-scoped check fired: length, value-set, a component rule's own usage, or undeclared content.
field?
readonlyoptionalfield?:number
1-indexed field position, when the finding is field-level.
occurrence?
readonlyoptionaloccurrence?:number
0-indexed segment occurrence, when the segment type repeats.
repetition?
readonlyoptionalrepetition?:number
0-indexed field repetition, when the finding is repetition-scoped.
segment
readonlysegment:string
Segment name (e.g. "PID"), or "(profile)" for a profile-shape defect.
HD
HL7 v2 Hierarchic Designator (HD): per HL7 Chapter 2 data type. All 3 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- namespaceId: application- or facility-scoped identifier (e.g. "EPIC").
- universalId: globally-unique id (e.g. an OID or UUID string).
- universalIdType: classifier for
universalId(ISO, GUID, UUID, DNS, URI, HL7, HCD, Random, etc.).
Example
import type { HD } from "@cosyte/hl7";
const authority: HD = { namespaceId: "EPIC", universalId: "1.2.840.114350", universalIdType: "ISO" };
Properties
namespaceId?
readonlyoptionalnamespaceId?:string
universalId?
readonlyoptionaluniversalId?:string
universalIdType?
readonlyoptionaluniversalIdType?:string
Hl7ParseWarning
Data shape for every Tier-2 warning emitted by the parser. Warnings are
plain data (distinct from Hl7ParseError, which is a thrown Error
subclass) so they can be safely accumulated into
Hl7Message.warnings and passed to onWarning callbacks.
Example
import type { Hl7ParseWarning } from "@cosyte/hl7";
const w: Hl7ParseWarning = {
code: "UNKNOWN_SEGMENT",
message: "Unknown segment: ZZZ",
position: { segmentIndex: 4 },
};
Properties
code
readonlycode:WarningCode
message
readonlymessage:string
position
readonlyposition:Hl7Position
Hl7Position
Positional context attached to every warning and fatal error. Fields are
1-indexed against the HL7 spec convention (see RawSegment.fields for the
index 0 slot convention). All fields past segmentIndex are optional,
for a top-level fatal like EMPTY_INPUT only segmentIndex: 0 is
populated; for a tokenizer warning deep inside a subcomponent all five
indices may be set.
Remarks
With exactOptionalPropertyTypes: true, do not pass fieldIndex: undefined
explicitly: omit the key instead.
Example
import type { Hl7Position } from "@cosyte/hl7";
const pos: Hl7Position = { segmentIndex: 2, fieldIndex: 5 };
Properties
componentIndex?
readonlyoptionalcomponentIndex?:number
fieldIndex?
readonlyoptionalfieldIndex?:number
repetitionIndex?
readonlyoptionalrepetitionIndex?:number
segmentIndex
readonlysegmentIndex:number
subcomponentIndex?
readonlyoptionalsubcomponentIndex?:number
IdentityEvent
One recognized patient-identity event. For merge / move / change kinds
the surviving (PID/PV1-sourced) and prior (MRG-sourced) parties are also
exposed directly, with the spec-constant direction: "MRG_TO_PID": the
prior identifiers are the ones being retired in favour of the surviving
ones, never the reverse (HL7 v2 Ch. 3, A18/A39/A40).
parties is the complete role-labelled surface in document order: nothing
present in the message is dropped, including a nonconforming MRG in a
link/add message (surfaced as a prior-role party).
warnings carries the event's own fail-safe warnings (currently
MERGE_MISSING_PRIOR_OR_SURVIVOR); they are scoped to the event and are
NOT appended to Hl7Message.warnings (a read-side helper never mutates
the message).
Example
import { parseHL7 } from "@cosyte/hl7";
for (const ev of parseHL7(raw).identityEvents()) {
if (ev.kind === "merge" && ev.prior && ev.surviving) {
// retire ev.prior.identifiers in favour of ev.surviving.identifiers
} else if (ev.warnings.length > 0) {
// incomplete pair: do NOT apply; route for review
}
}
Properties
direction?
readonlyoptionaldirection?:"MRG_TO_PID"
Spec-constant direction: the MRG (prior) identifiers are retired INTO the
PID (surviving) identifiers. Present on merge / move / change events
only; never inferred from content.
eventType
readonlyeventType:string
Trigger event code (MSH-9.2, falling back to EVN-1), e.g. "A40".
kind
readonlykind:IdentityEventKind
Classification of the trigger event.
parties
readonlyparties: readonlyIdentityParty[]
Every party in document order, role-labelled: the complete surface.
prior?
readonlyoptionalprior?:IdentityParty
The prior (non-surviving) party (merge/move/change): ONLY ever sourced from MRG.
surviving?
readonlyoptionalsurviving?:IdentityParty
The surviving party (merge/move/change): ONLY ever sourced from PID/PV1.
warnings
readonlywarnings: readonlyHl7ParseWarning[]
Event-scoped fail-safe warnings (never PHI-bearing).
IdentityParty
One party (one patient identity) in an identity event, labelled by role with its source segment recorded as provenance. Absent fields are OMITTED (exactOptionalPropertyTypes). All arrays and the object itself are frozen.
Field sources by sourceSegment:
"PID":identifiers= PID-3 repetitions,legacyPatientId= PID-2 (pre-v2.7 only),accountNumber= PID-18,visitNumber= PV1-19 (from the group's PV1, when present),name= PID-5 (first repetition)."MRG":identifiers= MRG-1 repetitions,legacyPatientId= MRG-4 (pre-v2.7 only),accountNumber= MRG-3,visitNumber= MRG-5,name= MRG-7 (first repetition).
Example
import { parseHL7 } from "@cosyte/hl7";
const ev = parseHL7(raw).identityEvents()[0];
if (ev?.prior) {
console.log(ev.prior.role); // "prior"
console.log(ev.prior.sourceSegment); // "MRG"
for (const cx of ev.prior.identifiers) console.log(cx.idNumber);
}
Properties
accountNumber?
readonlyoptionalaccountNumber?:CX
Patient account number (PID-18 / MRG-3).
identifiers
readonlyidentifiers: readonlyCX[]
Identifier list (PID-3 / MRG-1), every non-empty CX repetition.
legacyPatientId?
readonlyoptionallegacyPatientId?:CX
Legacy single patient ID (PID-2 / MRG-4). Backward-compat only; withdrawn as of HL7 v2.7: OMITTED (not read) when MSH-12 declares v2.7 or later.
name?
readonlyoptionalname?:XPN
Patient name (PID-5 / MRG-7, first repetition).
role
readonlyrole:IdentityRole
Role of this party in the event: the safety-critical label.
sourceSegment
readonlysourceSegment:"PID"|"MRG"
Segment this party was sourced from: provenance for the role label.
visitNumber?
readonlyoptionalvisitNumber?:CX
Visit number (PV1-19 for a PID party / MRG-5 for a prior party).
Immunization
A vaccine dose extracted from one RXA (Pharmacy/Treatment Administration)
segment of a VXU^V04 immunization message, with its RXR
(route/site) and OBX (e.g. VFC eligibility / funding source) children grouped
positionally under the RXA, and orderControl from the preceding ORC of the
VXU order group (ORC→RXA→[RXR]→[{OBX}]).
Safety contract. A wrong vaccine, dose, or mis-keyed action code can harm a patient or corrupt an IIS (Immunization Information System) registry, so this view is deliberately conservative:
vaccineCodecarries its own coding-system provenance via the CWE (vaccineCode.nameOfCodingSystem:CVXHL7 Table 0292; live IIS feeds frequently dual-code RXA-5 with an alternate CVX/NDC in CWE.4-6, surfaced asvaccineCode.alternateIdentifier/alternateText/nameOfAlternateCodingSystem). The helper reports the claim; it never validates or looks the code up.actionCode(RXA-21,A/D/U) is surfaced verbatim and never defaulted: mis-keying it corrupts a registry's add/delete/update dedup.doseAmountis strict-Number()parsed; the IIS "unknown dose" sentinel999is surfaced as the number999, never specially coerced.recordOrigin(administered vs historical) is derived only from the well-known NIP001 RXA-9.1 codes and OMITTED otherwise: see ImmunizationRecordOrigin.administrationStatustells a dose given ("completed") apart from a refused or not-administered record, a CVX998"no vaccine administered" placeholder and a delete request, and is"undetermined"rather than a guess: see ImmunizationAdministrationStatus. Every record is still returned, whatever it classifies as.- Malformed RXA segments never throw: absent fields are omitted keys.
routes, observations and administrationStatus are ALWAYS present
(routes and observations possibly empty). Deferred (not v1): IIS-specific
state profile constraints; CVX/MVX validity checks; the 2nd+ repetition of the
repeating RXA-15/16/17 lot/expiry/manufacturer fields (only the first
repetition is surfaced).
Example
import type { Immunization } from "@cosyte/hl7";
const imm: Immunization = {
vaccineCode: { identifier: "115", text: "Tdap", nameOfCodingSystem: "CVX" },
doseAmount: 0.5,
doseUnits: { identifier: "mL", nameOfCodingSystem: "UCUM" },
doseUnitsAreUcum: true,
recordOrigin: "administered",
manufacturer: { identifier: "PMC", text: "Sanofi Pasteur", nameOfCodingSystem: "MVX" },
completionStatus: "CP",
actionCode: "A",
administrationStatus: {
classification: "completed",
completionStatus: "CP",
actionCode: "A",
decidedBy: {
field: "RXA-20",
table: { name: "HL7 Table 0322", version: "3.0.0" },
map: {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70322-to-event-status",
version: "1.0.0",
},
},
},
routes: [{ route: { identifier: "IM", text: "Intramuscular" } }],
observations: [],
};
Properties
actionCode?
readonlyoptionalactionCode?:string
RXA-21 action code (A=add, D=delete, U=update): preserved verbatim, NEVER defaulted.
administeredDateTime?
readonlyoptionaladministeredDateTime?:DtmParts
RXA-3 date/time start of administration as the fidelity TS.
administrationStatus
readonlyadministrationStatus:ImmunizationAdministrationStatus
The record's administration status, derived from this RXA's own RXA-5,
RXA-20 and RXA-21, with the raw RXA-20 and RXA-21 codes beside it and the
field and table, map or code set that decided it. Always present:
"undetermined" when nothing classifies it. See
ImmunizationAdministrationStatus.
completionStatus?
readonlyoptionalcompletionStatus?:string
RXA-20 completion status (CP=complete, RE=refused, NA=not administered, PA=partially administered).
doseAmount?
readonlyoptionaldoseAmount?:number
RXA-6 administered dose amount (strict-parsed; never NaN). 999 = IIS "unknown", surfaced as-is.
doseUnits?
readonlyoptionaldoseUnits?:CWE
RXA-7 administered dose units (UCUM).
doseUnitsAreUcum?
readonlyoptionaldoseUnitsAreUcum?:boolean
true iff RXA-7's coding system (CWE.3) is exactly UCUM (HL7 Table 0396)
: i.e. the dose unit is declared UCUM and safe to interpret as computable.
false means a unit IS present but is NOT declared UCUM (surfaced as-is,
never coerced). OMITTED when RXA-7 is absent. A claim check only: UCUM
grammar is not validated here.
expirationDate?
readonlyoptionalexpirationDate?:DtmParts
RXA-16 substance expiration date (first repetition) as the fidelity TS.
informationSource?
readonlyoptionalinformationSource?:CWE
RXA-9 immunization information source (HL7 Table NIP001), preserved verbatim.
lotNumber?
readonlyoptionallotNumber?:string
RXA-15 substance lot number (first repetition).
manufacturer?
readonlyoptionalmanufacturer?:CWE
RXA-17 substance manufacturer (MVX, HL7 Table 0227; first repetition).
observations
readonlyobservations: readonlyObservation[]
OBX children grouped under this RXA (VFC eligibility, funding source, …). Always present (possibly empty).
orderControl?
readonlyoptionalorderControl?:string
ORC-1 order control when an ORC precedes this RXA in the VXU order group.
recordOrigin?
readonlyoptionalrecordOrigin?:ImmunizationRecordOrigin
Derived administered-vs-historical classification from RXA-9.1. See ImmunizationRecordOrigin.
refusalReason?
readonlyoptionalrefusalReason?:CWE
RXA-18 substance/treatment refusal reason (first repetition).
routes
readonlyroutes: readonlyMedicationRoute[]
RXR children grouped under this RXA (Table 0162 route / Table 0163 site). Always present (possibly empty).
vaccineCode?
readonlyoptionalvaccineCode?:CWE
RXA-5 administered vaccine code (CVX, HL7 Table 0292) with provenance + any alternate coding.
ImmunizationAdministrationStatus
An immunization record's administration status, derived from that RXA's own
RXA-5, RXA-20 and RXA-21: carried as administrationStatus on every
Immunization. It tells a dose given apart from a refused or
not-administered record, a CVX 998 "no vaccine administered" placeholder,
and a request to delete an administration sent earlier.
Which rule decides. Exactly one rule decides, the first that applies:
- RXA-21 exactly
D:"delete-requested". - RXA-21 present but not exactly one of
A,D,U,X(HL7 Table 0323):"undetermined". RXA-21A,U,Xor empty change nothing. - RXA-5 carries CVX
998in a triplet whose coding system resolves toCVX(or, in the primary triplet, that names no coding system):"no-vaccine-administered", or"undetermined"when the other triplet carries a different, non-empty identifier. - RXA-20 by HL7's Table 0322 to Event Status map:
CPandPA"completed",REandNA"not-done", anything else"undetermined".
Safety contract. "completed" only when RXA-20 is exactly CP or PA,
RXA-21 is empty or exactly A, U or X, and RXA-5 carries no CVX 998.
Each RXA classifies from its own fields alone: nothing is carried across
segments. A status is reported, never applied: a "delete-requested"
record is still returned, is never matched against an earlier message, and
removes nothing. The object is plain data, not a FHIR element.
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
const given = msg
.immunizations()
.filter((imm) => imm.administrationStatus.classification === "completed");
for (const imm of msg.immunizations()) {
const { classification, completionStatus, actionCode, decidedBy } = imm.administrationStatus;
if (classification === "undetermined") {
console.log("not classifiable:", decidedBy.field, completionStatus, actionCode);
}
}
Properties
actionCode?
readonlyoptionalactionCode?:string
The raw RXA-21 code, byte-identical to the actionCode of the same
immunization: the field's first value, decoded. OMITTED exactly when
actionCode is (the field is absent, empty or "").
classification
readonlyclassification:ImmunizationStatusClass
The classification the deciding rule gives the record.
completionStatus?
readonlyoptionalcompletionStatus?:string
The raw RXA-20 code, byte-identical to the completionStatus of the same
immunization: the field's first value, decoded. OMITTED exactly when
completionStatus is (the field is absent, empty or "").
decidedBy
readonlydecidedBy:ImmunizationStatusBasis
The field that decided, and the table, map or code set it was read against.
ImmunizationStatusCodeSet
The code set an RXA-5 classification read: the CDC's CVX (Vaccines
Administered) code set, and the one code of it this library reads, 998
"no vaccine administered". The CVX code set carries no version number of its
own; version is the date the CDC last updated the 998 entry, so a later
change to that entry shows up here as a changed version.
Example
import type { ImmunizationStatusCodeSet } from "@cosyte/hl7";
const codeSet: ImmunizationStatusCodeSet = {
name: "CDC CVX",
code: "998",
version: "2023-03-09",
};
Properties
code
readonlycode:"998"
The CVX code read: 998, "no vaccine administered".
name
readonlyname:"CDC CVX"
The code set: the CDC's CVX (Vaccines Administered) codes.
version
readonlyversion:"2023-03-09"
The date the CDC last updated the 998 entry, as YYYY-MM-DD.
ImmunizationStatusMap
The published HL7 v2-to-FHIR map an RXA-20 classification followed, by
canonical URL and version. The map comes from the HL7 Version 2 to FHIR
Implementation Guide (STU 1, standards status Informative); a later map
revision shows up here as a changed version.
Example
import type { ImmunizationStatusMap } from "@cosyte/hl7";
const map: ImmunizationStatusMap = {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70322-to-event-status",
version: "1.0.0",
};
Properties
url
readonlyurl:"http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70322-to-event-status"
Canonical URL of the ConceptMap whose rows the classification follows.
version
readonlyversion:"1.0.0"
The ConceptMap version the classification follows.
ImmunizationStatusTable
The HL7 v2 code table an ImmunizationAdministrationStatus read: Table 0322 (Completion Status, RXA-20) or Table 0323 (Action Code, RXA-21), each at the code-system version its codes were taken from.
Example
import type { ImmunizationStatusTable } from "@cosyte/hl7";
const table: ImmunizationStatusTable = { name: "HL7 Table 0322", version: "3.0.0" };
Properties
name
readonlyname:"HL7 Table 0322"|"HL7 Table 0323"
"HL7 Table 0322" for RXA-20, "HL7 Table 0323" for RXA-21.
version
readonlyversion:"3.0.0"
The code-system version the table's codes were taken from.
Insurance
IN1-derived insurance entry (HELPERS-06) with positional IN2/IN3 presence
flags (D-05 extension). hasIn2 / hasIn3 are ALWAYS present booleans;
callers who need the full IN2/IN3 surface can walk msg.segments("IN2").
Example
import type { Insurance } from "@cosyte/hl7";
const ins: Insurance = {
planId: { identifier: "PLAN1", text: "Aetna PPO" },
policyNumber: "POL123",
groupNumber: "GRP1",
hasIn2: false,
hasIn3: false,
};
Properties
companyId?
readonlyoptionalcompanyId?:CX
IN1-3 insurance company id.
companyName?
readonlyoptionalcompanyName?:string
IN1-4 insurance company name (first repetition, first component).
effectiveDate?
readonlyoptionaleffectiveDate?:DtmParts
IN1-12 plan effective date as the fidelity TS.
expirationDate?
readonlyoptionalexpirationDate?:DtmParts
IN1-13 plan expiration date as the fidelity TS.
groupNumber?
readonlyoptionalgroupNumber?:string
IN1-8 group number.
hasIn2
readonlyhasIn2:boolean
true iff an IN2 segment follows this IN1 before the next IN1.
hasIn3
readonlyhasIn3:boolean
true iff an IN3 segment follows this IN1 before the next IN1.
insuredName?
readonlyoptionalinsuredName?:XPN
IN1-16 insured's name.
planId?
readonlyoptionalplanId?:CWE
IN1-2 insurance plan id.
policyNumber?
readonlyoptionalpolicyNumber?:string
IN1-36 policy number.
JsonSchemaDocument
The emitted JSON Schema document: the root node of the projection, plus the reusable definitions its members reference.
Example
import { messageJsonSchema, type JsonSchemaDocument } from "@cosyte/hl7";
const doc: JsonSchemaDocument = messageJsonSchema();
Object.keys(doc.$defs).includes("SerializedWarning"); // true
Extends
Properties
$defs
readonly$defs:Readonly<Record<string,JsonSchemaNode>>
$ref?
readonlyoptional$ref?:string
Inherited from
$schema
readonly$schema:string
additionalProperties?
readonlyoptionaladditionalProperties?:false
Inherited from
JsonSchemaNode.additionalProperties
enum?
readonlyoptionalenum?: readonlystring[]
Inherited from
items?
readonlyoptionalitems?:JsonSchemaNode
Inherited from
properties?
readonlyoptionalproperties?:Readonly<Record<string,JsonSchemaNode>>
Inherited from
required?
readonlyoptionalrequired?: readonlystring[]
Inherited from
type?
readonlyoptionaltype?:"string"|"boolean"|"object"|"array"|"integer"
Inherited from
JsonSchemaNode
One node of the emitted JSON Schema. Every member is optional because a node
carries only the keywords its own shape needs: a reference carries $ref
alone, a scalar carries type, an object carries type, properties,
required and additionalProperties.
Example
import type { JsonSchemaNode } from "@cosyte/hl7";
const subcomponents: JsonSchemaNode = { type: "array", items: { type: "string" } };
Extended by
Properties
$ref?
readonlyoptional$ref?:string
additionalProperties?
readonlyoptionaladditionalProperties?:false
enum?
readonlyoptionalenum?: readonlystring[]
items?
readonlyoptionalitems?:JsonSchemaNode
properties?
readonlyoptionalproperties?:Readonly<Record<string,JsonSchemaNode>>
required?
readonlyoptionalrequired?: readonlystring[]
type?
readonlyoptionaltype?:"string"|"boolean"|"object"|"array"|"integer"
KnownCodingSystem
One recognized HL7 Table 0396 coding system: its registered acronym (the value expected in CWE.3 / CE.3), a canonical human-readable name, and the widely-used aliases tolerated for it. Aliases are matched case-insensitively.
Properties
aliases
readonlyaliases: readonlystring[]
Widely-used alternative spellings that claim the same system (matched
case-insensitively), e.g. "LOINC" for LN. The registered id itself
is always recognized and need not be repeated here.
id
readonlyid:string
Registered Table 0396 acronym, e.g. "LN".
name
readonlyname:string
Canonical human-readable name, e.g. "LOINC".
MdmDocument
Typed TXA (transcription document header) content for buildMdm.
Example
import type { MdmDocument } from "@cosyte/hl7";
const document: MdmDocument = {
documentType: "DS",
uniqueDocumentNumber: "DOC-1001",
completionStatus: "AU", // authenticated
availabilityStatus: "AV", // a different axis: available
body: [{ setId: "1", valueType: "TX", value: "Discharge summary text." }],
};
Properties
activityDateTime?
readonlyoptionalactivityDateTime?:string|DtmParts
TXA-4 Activity Date/Time.
availabilityStatus?
readonlyoptionalavailabilityStatus?:string
TXA-19 Document Availability Status (HL7 Table 0273: AV, CA, OB,
UN). A different axis from completionStatus: never merged, never
defaulted.
body?
readonlyoptionalbody?: readonlyOruObservation[]
The transcribed narrative body, one OBX per entry. Required for the
content events (T02, T04, T06, T08, T10), whose published
structure gives OBX a minimum of one.
completionStatus?
readonlyoptionalcompletionStatus?:string
TXA-17 Document Completion Status (HL7 Table 0271: DO, IP, AU,
LA, IN). A different axis from availabilityStatus: never
merged, never defaulted.
documentType?
readonlyoptionaldocumentType?:string
TXA-2 Document Type (HL7 Table 0270), verbatim.
parentDocumentNumber?
readonlyoptionalparentDocumentNumber?:string
TXA-13 Parent Document Number: the addendum or replacement link.
setId?
readonlyoptionalsetId?:string
TXA-1 Set ID.
uniqueDocumentNumber?
readonlyoptionaluniqueDocumentNumber?:string
TXA-12 Unique Document Number.
Medication
A medication extracted from one RXO/RXE/RXD/RXA segment, with its RXR
(route) and RXC (component) children grouped
positionally. context records which RX* segment this came from (give
vs dispense vs administered).
Safety contract. A wrong drug, strength, or route can harm a real patient, so this view is deliberately conservative:
giveCodecarries its own coding-system provenance via the CWE (giveCode.nameOfCodingSystem: e.g.RXNRxNorm,NDC). The helper surfaces the claim; it never validates or looks the code up.amount(how much) andstrength(concentration) are SEPARATE fields and are never reconciled: including against any strength a coded drug implies. A disagreement is preserved for the consumer to see.- Malformed RX* segments never throw: absent fields are omitted keys.
routes and components are ALWAYS present (possibly empty). So is
timings: empty when no TQ1 / legacy embedded TQ (RXE-1)
accompanies the medication; the repeat pattern is surfaced verbatim, never
normalized to a schedule. Deferred (not v1): sig/frequency interpretation,
dose-range or interaction checking, pharmacologic resolution of compounds.
orderControl is ORC-1 of the ORC that opened this medication's order group,
surfaced verbatim (NW, DC, HD, an unlisted code, any letter case)
and never classified into an active, held or ended state.
Example
import type { Medication } from "@cosyte/hl7";
const med: Medication = {
orderControl: "NW",
context: "encoded",
giveCode: { identifier: "1049630", text: "Acetaminophen 325 MG", nameOfCodingSystem: "RXN" },
amount: { minimum: 2, units: { identifier: "TAB" } },
strength: { value: 325, units: { identifier: "mg", nameOfCodingSystem: "UCUM" } },
routes: [{ route: { identifier: "PO", text: "Oral" } }],
components: [],
timings: [{ source: "TQ1", repeatPattern: { code: "Q6H", kind: "parametric", interval: { count: 6, unit: "H" } } }],
};
Properties
amount?
readonlyoptionalamount?:MedicationAmount
Give/dispense/administered amount (+ units). See MedicationAmount.
components
readonlycomponents: readonlyMedicationComponent[]
RXC children grouped under this RX* (compound components). Always present (possibly empty).
context
readonlycontext:MedicationContext
Which RX* segment this medication came from (give/dispense/administered).
dosageForm?
readonlyoptionaldosageForm?:CWE
RXO-5 requested dosage form (order context).
giveCode?
readonlyoptionalgiveCode?:CWE
RXO-1 / RXE-2 / RXD-2 / RXA-5 give/dispense/administered drug code, with provenance.
orderControl?
readonlyoptionalorderControl?:string
ORC-1 order control of the ORC that opened this medication's order group (the run of segments from that ORC to the next ORC), exactly as sent: never trimmed, case-folded, looked up, or classified into an active, held or ended state. Every RX* segment in one group carries the same value. Omitted when no ORC precedes the RX* segment in the message or that ORC's ORC-1 is empty; it is never carried over from an earlier group.
routes
readonlyroutes: readonlyMedicationRoute[]
RXR children grouped under this RX* (Table 0162 route). Always present (possibly empty).
strength?
readonlyoptionalstrength?:MedicationStrength
RXE-25/26 give strength: ENCODED context only; never reconciled with giveCode.
timings
readonlytimings: readonlyOrderTiming[]
TQ1 / legacy embedded-TQ (RXE-1) timing(s) grouped under this medication. Always present: empty when the medication carries no timing. See OrderTiming.
MedicationAmount
The give / dispense / administered amount of a Medication.
Carries the HL7 min/max amount pair and its units.
- For an order (RXO-2/3) or encoded order (RXE-3/4) the amount is a genuine min..max range: both keys may be present.
- For a dispense (RXD-4) or administration (RXA-6) there is a SINGLE
amount; it is surfaced as
minimumwithmaximumOMITTED. This is a single value, not a range: do not read the absentmaximumas "no upper bound on a range".
minimum/maximum are strict-Number() parsed (undefined, never NaN).
units is the give/dispense/administered units CWE (RXO-4 / RXE-5 / RXD-5 /
RXA-7); check units.nameOfCodingSystem === "UCUM" for computable units.
Example
import type { MedicationAmount } from "@cosyte/hl7";
const amount: MedicationAmount = { minimum: 250, units: { identifier: "mg", nameOfCodingSystem: "UCUM" } };
Properties
maximum?
readonlyoptionalmaximum?:number
RXO-3 / RXE-4 maximum. OMITTED for single-amount (dispense/administration) contexts.
minimum?
readonlyoptionalminimum?:number
RXO-2 / RXE-3 minimum, or the single dispense (RXD-4) / administered (RXA-6) amount.
units?
readonlyoptionalunits?:CWE
RXO-4 / RXE-5 / RXD-5 / RXA-7 give/dispense/administered units.
MedicationComponent
One RXC (Pharmacy/Treatment Component Order) grouped under its parent RX* segment: a component of a compound/IV. Surfaced STRUCTURALLY (the component list as authored), NOT pharmacologically resolved.
Example
import type { MedicationComponent } from "@cosyte/hl7";
const c: MedicationComponent = { type: "B", code: { identifier: "D5W", text: "Dextrose 5%" }, amount: 1000 };
Properties
amount?
readonlyoptionalamount?:number
RXC-3 component amount (strict-parsed; never NaN).
code?
readonlyoptionalcode?:CWE
RXC-2 component code.
type?
readonlyoptionaltype?:string
RXC-1 component type (e.g. "B"=base, "A"=additive: HL7 Table 0166).
units?
readonlyoptionalunits?:CWE
RXC-4 component units.
MedicationRoute
One RXR (Pharmacy/Treatment Route) grouped under its parent RX* segment.
route is HL7 Table 0162 (CWE); site is Table 0163 (CWE).
Provenance travels on the CWE (route.nameOfCodingSystem): a "PO" route is
only safe to act on when you know the system it was coded against.
Example
import type { MedicationRoute } from "@cosyte/hl7";
const r: MedicationRoute = { route: { identifier: "PO", text: "Oral" } };
Properties
route?
readonlyoptionalroute?:CWE
RXR-1 route of administration (HL7 Table 0162).
site?
readonlyoptionalsite?:CWE
RXR-2 administration site (HL7 Table 0163).
MedicationStrength
The give strength of an encoded Medication (RXE-25 value + RXE-26
units). Strength is the concentration of active ingredient (e.g.
"250 mg"), distinct from the give amount (how much is administered, e.g.
"2 tablets"). Only the "encoded" (RXE) context carries strength.
Fail-safe: strength is surfaced exactly as the explicit
RXE-25/26 fields declare it, and is NEVER reconciled against any strength
implied by the give code (e.g. an NDC that encodes "250 mg"). A consumer
that sees both an explicit strength here and a coded drug in giveCode must
treat a disagreement as a real signal: the library does not silently pick a
winner. value is strict-Number() parsed (undefined, never NaN).
Example
import type { MedicationStrength } from "@cosyte/hl7";
const strength: MedicationStrength = { value: 250, units: { identifier: "mg", nameOfCodingSystem: "UCUM" } };
Properties
units?
readonlyoptionalunits?:CWE
RXE-26 give strength units.
value?
readonlyoptionalvalue?:number
RXE-25 give strength numeric value (strict-parsed; never NaN).
MessageStructure
The structure summary for a parsed message: the data behind
Hl7Message.structure. For an unrecognized type, recognized is false and
every list is empty (the safety net is deliberately silent on types it does
not model).
Properties
expectedGroups
readonlyexpectedGroups: readonlyStructureGroup[]
Per-expected-segment presence verdicts (empty when unrecognized).
messageCode
readonlymessageCode:string
MSH-9.1 message code observed on the message. "" when absent, and
"<withheld>" when recognized is false and the observed value is not
identifier-shaped (MSH-9 can hold a data field on a malformed message).
missingGroups
readonlymissingGroups: readonlystring[]
Names of the expectations that are absent: the warnings.
missingSegments
readonlymissingSegments: readonlystring[]
The required segments the message does not carry.
recognized
readonlyrecognized:boolean
true when a MESSAGE_STRUCTURE_DEFINITIONS entry matched the type.
requiredSegments
readonlyrequiredSegments: readonlystring[]
Every segment the published structure requires (empty when unrecognized).
structureIds
readonlystructureIds: readonlystring[]
The published structure ids the verdict was derived from. Empty when the type is unrecognized or the entry is a retained transcription.
triggerEvent
readonlytriggerEvent:string
MSH-9.2 trigger event observed on the message. "" when absent, and
"<withheld>" when recognized is false and the observed value is not
identifier-shaped. Note that on the recognized branch this is echoed
verbatim, which for a definition matching on message code alone (ACK)
means an arbitrary MSH-9.2.
MessageStructureDefinition
The expected structure of one recognized message type.
An entry covers one message code and the trigger events the publication maps
to a single structure. An empty triggerEvents list means "match on message
code alone" (used for ACK, whose MSH-9.2 carries the acknowledged message's
trigger event rather than one of its own).
Properties
derivation
readonlyderivation:StructureDerivation
Whether the expectations were derived or retained.
expectedGroups
readonlyexpectedGroups: readonlyExpectedSegmentGroup[]
One entry per segment the published structure requires.
messageCode
readonlymessageCode:string
MSH-9.1 message code, e.g. "ORU".
requiredSegments
readonlyrequiredSegments: readonlystring[]
The required segment names, sorted: the same set, flattened.
retainedReason
readonlyretainedReason:string
Why a retained transcription could not be derived from the publication. Empty string for a derived entry.
structureId
readonlystructureId:string
The published structure id these expectations were derived from, e.g.
"ADT_A01". Empty for a retained transcription.
structureIds
readonlystructureIds: readonlystring[]
Every member of that structure's variant family that was actually read,
e.g. ["ADT_A01-A", "ADT_A01-B", "ADT_A01-C", "ADT_A01-D"]. Empty for a
retained transcription.
triggerEvents
readonlytriggerEvents: readonlystring[]
The MSH-9.2 trigger events this definition applies to, e.g. ["R01"].
An empty list means "match on message code alone" (e.g. ACK).
Meta
MSH-derived message metadata (HELPERS-01). D-03: always defined on
Hl7Message.meta (MSH absence throws NO_MSH_SEGMENT at parse time);
individual fields are optional because vendor-quirky messages routinely
omit pieces of MSH. timestamp is the fidelity TS (precision +
timezone preserved), not an eager UTC-assuming Date.
Example
import type { Meta } from "@cosyte/hl7";
const meta: Meta = {
type: "ADT^A01",
messageCode: "ADT",
triggerEvent: "A01",
controlId: "MSG001",
version: "2.5",
};
console.log(meta.timestamp?.raw, meta.timestamp?.precision);
Properties
controlId?
readonlyoptionalcontrolId?:string
MSH-10 message control ID: unique per message per sender.
messageCode?
readonlyoptionalmessageCode?:string
MSH-9.1 message code (e.g. "ADT", "ORU").
messageStructure?
readonlyoptionalmessageStructure?:string
MSH-9.3 message structure (e.g. "ADT_A01").
processingId?
readonlyoptionalprocessingId?:string
MSH-11.1 processing id (P=production, T=test, D=debug).
receivingApp?
readonlyoptionalreceivingApp?:string
MSH-5.1 receiving application namespace id.
receivingFacility?
readonlyoptionalreceivingFacility?:string
MSH-6.1 receiving facility namespace id.
sendingApp?
readonlyoptionalsendingApp?:string
MSH-3.1 sending application namespace id.
sendingFacility?
readonlyoptionalsendingFacility?:string
MSH-4.1 sending facility namespace id.
timestamp?
readonlyoptionaltimestamp?:DtmParts
MSH-7 message date/time as the fidelity TS. Absent when MSH-7 is empty
or unparseable. Present with valid: false and an ambiguity report when
the value is a slash date whose field order cannot be established: check
valid before reading the parts.
triggerEvent?
readonlyoptionaltriggerEvent?:string
MSH-9.2 trigger event (e.g. "A01", "R01").
type?
readonlyoptionaltype?:string
MSH-9 full message type string, e.g. "ADT^A01" or "ORU^R01^ORU_R01".
version?
readonlyoptionalversion?:string
MSH-12 HL7 version string (e.g. "2.5", "2.5.1").
NextOfKin
NK1-derived next-of-kin entry (HELPERS-06). Lean subset: callers can
reach for msg.segments("NK1") when they need the full NK1 surface.
Example
import type { NextOfKin } from "@cosyte/hl7";
const nk: NextOfKin = {
name: { familyName: "Doe", givenName: "John" },
relationship: { identifier: "FTH", text: "Father" },
};
Properties
address?
readonlyoptionaladdress?:XAD
NK1-4 address.
contactRole?
readonlyoptionalcontactRole?:CWE
NK1-7 contact role.
name?
readonlyoptionalname?:XPN
NK1-2 next-of-kin name.
phone?
readonlyoptionalphone?:XTN
NK1-5 phone (first repetition).
relationship?
readonlyoptionalrelationship?:CWE
NK1-3 relationship to patient (FTH=father, MTH=mother, SPO=spouse, ...).
NM
HL7 v2 Numeric (NM) composite. Carries both the raw HL7 numeric string
and the parsed JS number. .value is undefined when the raw string
is empty or not fully numeric: NEVER throws.
Unlike most composites in this phase, both raw and value are
ALWAYS-PRESENT keys (not optional): value is explicitly typed as
number | undefined so callers can destructure uniformly.
Example
import type { NM } from "@cosyte/hl7";
const glucose: NM = { raw: "120", value: 120 };
const bad: NM = { raw: "N/A", value: undefined };
Properties
raw
readonlyraw:string
value
readonlyvalue:number|undefined
ObservationBase
Fields shared by every Observation variant, regardless of the OBX-2
value type. Split from the discriminated union to keep the union
declaration readable (D-15 locked field list).
Example
import type { ObservationBase } from "@cosyte/hl7";
const base: ObservationBase = {
setId: "1",
identifier: { identifier: "GLU", text: "Glucose" },
resultStatus: {
classification: "undetermined",
table: { name: "HL7 Table 0085", version: "3.0.0" },
map: {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70085-to-observation-status",
version: "1.0.0",
},
},
};
Properties
abnormalFlags?
readonlyoptionalabnormalFlags?:string
OBX-8 abnormal flags (e.g. "H", "HH", "L", "LL").
identifier
readonlyidentifier:CWE
OBX-3 observation identifier. Always present (may be {} if OBX-3 absent).
notes?
readonlyoptionalnotes?: readonlystring[]
NTE note lines positionally attached to this OBX: each non-empty NTE-3 (Comment, FT) repetition of every NTE immediately following this observation, HL7-unescaped, in document order. OMITTED when the observation carries no notes. High-PHI-risk clinical narrative.
observedDateTime?
readonlyoptionalobservedDateTime?:DtmParts
OBX-14 date/time of observation as the fidelity TS.
referenceRange?
readonlyoptionalreferenceRange?:string
OBX-7 reference range (e.g. "80-110").
resultStatus
readonlyresultStatus:ResultStatusClassification
OBX-11 read against HL7 Table 0085 and classified by HL7's Table 0085 to
Observation Status map. Always present: "undetermined" when OBX-11 is
absent, unmapped or not exactly one code. See
ResultStatusClassification.
setId?
readonlyoptionalsetId?:string
OBX-1 set id (string: typically sequential "1", "2", ...).
status?
readonlyoptionalstatus?:string
OBX-11 observation result status (e.g. "F"=final, "P"=preliminary).
units?
readonlyoptionalunits?:CWE
OBX-6 units.
unitsAreUcum?
readonlyoptionalunitsAreUcum?:boolean
true iff OBX-6's coding system (CWE.3, "name of coding system") is
exactly UCUM (HL7 Table 0396): i.e. the unit is declared UCUM and is
safe to interpret as a computable unit. false means a unit IS present
but is NOT declared UCUM (e.g. a local code or free text) and is surfaced
as-is, never coerced. OMITTED when OBX-6 is absent. This is a claim check
only: the library does not validate UCUM grammar or check the alternate
coding system (CWE.6).
Order
OBR-derived order (HELPERS-05, D-16) with positionally-grouped OBX children
(D-12). observations is ALWAYS present: empty when no OBX follows this
OBR before the next OBR or end-of-message. timings is ALWAYS present,
empty when the order carries no TQ1 / legacy embedded TQ.
Example
import type { Order } from "@cosyte/hl7";
const order: Order = {
placerOrderNumber: "PLACER1",
fillerOrderNumber: "FILLER1",
universalServiceId: { identifier: "GLU", text: "Glucose" },
orderStatus: "F",
resultStatus: {
classification: "final",
code: "F",
table: { name: "HL7 Table 0123", version: "3.0.0" },
map: {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70123-queries-to-diagnostic-report-status",
version: "1.0.0",
},
},
observations: [],
timings: [],
};
Properties
fillerOrderNumber?
readonlyoptionalfillerOrderNumber?:string
OBR-3 filler order number.
notes?
readonlyoptionalnotes?: readonlystring[]
NTE note lines positionally attached to this order: the
ORC-region notes (before the OBR) followed by the OBR-region notes, in
document order. Several ORCs before one OBR all contribute here; nothing is
dropped. OMITTED when the order carries no notes. A note on a trailing or
dangling ORC that never opens an order is surfaced at message level
(msg.notes()), not here: still never dropped. High-PHI-risk clinical
narrative.
observations
readonlyobservations: readonlyObservation[]
OBX children grouped under this OBR (D-12 positional grouping). Always present.
orderControl?
readonlyoptionalorderControl?:string
ORC-1 order control when an ORC precedes this OBR.
orderedBy?
readonlyoptionalorderedBy?:XCN
OBR-16 ordering provider (D-24a XCN).
orderStatus?
readonlyoptionalorderStatus?:string
OBR-25 result status (HL7 Table 0123, e.g. "F" final, "P" preliminary).
placerOrderNumber?
readonlyoptionalplacerOrderNumber?:string
OBR-2 placer order number.
resultStatus
readonlyresultStatus:ResultStatusClassification
OBR-25 read against HL7 Table 0123 and classified by HL7's Table 0123 to
Diagnostic Report Status map. Always present: "undetermined" when OBR-25
is absent, unmapped or not exactly one code. Classified from OBR-25 alone,
never from the statuses of the order's observations, each of which carries
its own. See ResultStatusClassification.
timings
readonlytimings: readonlyOrderTiming[]
TQ1 / legacy embedded-TQ timing(s) grouped under this order. Always present: empty when the order carries no timing. See OrderTiming.
universalServiceId?
readonlyoptionaluniversalServiceId?:CWE
OBR-4 universal service identifier (test code + description).
OrderTiming
The order/medication timing structure: one TQ1 segment (v2.5+) or the legacy embedded TQ in ORC-7 / RXE-1 (pre-v2.5). Attached to Order.timings and Medication.timings.
Safety contract. hl7 surfaces the timing structure; it does not
compute administration schedules, resolve "institution-specified times" to
clock times, or interpret sig. The load-bearing repeatPattern and
totalOccurrences are preserved verbatim (see RepeatPattern);
startDateTime/endDateTime keep the TS precision + timezone
fidelity. A malformed timing never throws: absent pieces are omitted keys.
Example
import type { OrderTiming } from "@cosyte/hl7";
const t: OrderTiming = {
source: "TQ1",
quantity: { value: 1 },
repeatPattern: { code: "Q6H", kind: "parametric", interval: { count: 6, unit: "H" } },
totalOccurrences: 20,
};
Properties
endDateTime?
readonlyoptionalendDateTime?:DtmParts
TQ1-8 / legacy TQ.5 end date/time as the fidelity TS.
explicitTime?
readonlyoptionalexplicitTime?:string
TQ1-4 / legacy TQ.2 interval RI.2 explicit time(s): surfaced verbatim (first repetition/value).
priority?
readonlyoptionalpriority?:CWE
TQ1-9 priority (CWE) / legacy TQ.6 priority (surfaced as a CWE { identifier }).
quantity?
readonlyoptionalquantity?:TimingQuantity
TQ1-2 / legacy TQ.1 service quantity (CQ).
repeatPattern?
readonlyoptionalrepeatPattern?:RepeatPattern
TQ1-3 / legacy TQ.2 interval RI.1 repeat pattern (Table 0335): verbatim. See RepeatPattern.
serviceDuration?
readonlyoptionalserviceDuration?:string
TQ1-6 / legacy TQ.3 service duration: surfaced verbatim.
source
readonlysource:"TQ1"|"legacy"
Which structure this timing was read from: the dedicated TQ1 segment (v2.5+) or the legacy embedded TQ data type in ORC-7 (orders) / RXE-1 (encoded medications, pre-v2.5). The library treats the presence of a TQ1 segment as the v2.5+ signal: the legacy embedded TQ is surfaced only when no TQ1 accompanies the order, so the same timing is never double-counted and a legacy-only timing is never dropped.
startDateTime?
readonlyoptionalstartDateTime?:DtmParts
TQ1-7 / legacy TQ.4 start date/time as the fidelity TS.
totalOccurrences?
readonlyoptionaltotalOccurrences?:number
TQ1-14 / legacy TQ.12 total occurrences (NM): how many times the service
is to be performed (strict-parsed; never NaN). TQ1-14, not TQ1-11
(TQ1-11 is Text Instruction). Load-bearing: losing it drops the total
administered count.
OrmOrder
Typed order content for buildOrm: the OBR order detail every typed builder shares, plus the ORC control information and any OBX children.
Example
import type { OrmOrder } from "@cosyte/hl7";
const order: OrmOrder = {
orderControl: "NW",
setId: "1",
placerOrderNumber: "PL-1001",
universalServiceId: { identifier: "CBC", text: "Complete Blood Count" },
orderingProvider: { idNumber: "9990", familyName: "Welby" },
};
Extends
Properties
fillerOrderNumber?
readonlyoptionalfillerOrderNumber?:string
OBR-3 Filler Order Number.
Inherited from
observationDateTime?
readonlyoptionalobservationDateTime?:string|DtmParts
OBR-7 Observation Date/Time.
Inherited from
observations?
readonlyoptionalobservations?: readonlyOruObservation[]
OBX children of this order, in the order supplied.
orcFillerOrderNumber?
readonlyoptionalorcFillerOrderNumber?:string
ORC-3 Filler Order Number. Defaults to nothing: never copied from OBR-3.
orcPlacerOrderNumber?
readonlyoptionalorcPlacerOrderNumber?:string
ORC-2 Placer Order Number. Defaults to nothing: never copied from OBR-2.
orderControl?
readonlyoptionalorderControl?:string
ORC-1 Order Control (HL7 Table 0119: NW new order, CA cancel, XO
change, …). An ORC is emitted for every order regardless, because the
published expectation for this message type is exactly that segment; this
value fills ORC-1 when supplied and is never defaulted.
orderingProvider?
OBR-16 Ordering Provider.
Inherited from
placerOrderNumber?
readonlyoptionalplacerOrderNumber?:string
OBR-2 Placer Order Number.
Inherited from
resultStatus?
readonlyoptionalresultStatus?:string
OBR-25 Result Status (e.g. "F" final, "P" preliminary, "C" corrected).
Inherited from
setId?
readonlyoptionalsetId?:string
OBR-1 Set ID.
Inherited from
universalServiceId?
readonlyoptionaluniversalServiceId?:CWE
OBR-4 Universal Service Identifier.
Inherited from
OruObservation
Typed OBX (observation / result) content for buildOru.
Properties
abnormalFlags?
readonlyoptionalabnormalFlags?:string
OBX-8 Abnormal Flags (e.g. "H", "L", "N").
identifier?
readonlyoptionalidentifier?:CWE
OBX-3 Observation Identifier.
observationDateTime?
readonlyoptionalobservationDateTime?:string|DtmParts
OBX-14 Date/Time of the Observation.
observationResultStatus?
readonlyoptionalobservationResultStatus?:string
OBX-11 Observation Result Status (e.g. "F" final, "P" preliminary).
referenceRange?
readonlyoptionalreferenceRange?:string
OBX-7 References Range.
setId?
readonlyoptionalsetId?:string
OBX-1 Set ID.
units?
readonlyoptionalunits?:CWE
OBX-6 Units.
value?
readonlyoptionalvalue?:string
OBX-5 Observation Value (emitted verbatim: the caller owns its formatting).
valueType?
readonlyoptionalvalueType?:string
OBX-2 Value Type (e.g. "NM", "ST", "CE", "TX").
OruOrder
Typed OBR (observation request / order) content for buildOru.
Extended by
Properties
fillerOrderNumber?
readonlyoptionalfillerOrderNumber?:string
OBR-3 Filler Order Number.
observationDateTime?
readonlyoptionalobservationDateTime?:string|DtmParts
OBR-7 Observation Date/Time.
orderingProvider?
OBR-16 Ordering Provider.
placerOrderNumber?
readonlyoptionalplacerOrderNumber?:string
OBR-2 Placer Order Number.
resultStatus?
readonlyoptionalresultStatus?:string
OBR-25 Result Status (e.g. "F" final, "P" preliminary, "C" corrected).
setId?
readonlyoptionalsetId?:string
OBR-1 Set ID.
universalServiceId?
readonlyoptionaluniversalServiceId?:CWE
OBR-4 Universal Service Identifier.
OverlayStructures
Every published overlay key, mapped to the segment names the pair's published structure marks required. This is the compile-time face of the derived structure registry: the runtime support claim (SUPPORTED_OVERLAY_MESSAGES) is computed from that registry, and a test compares the two in both directions so this table can never claim a pair the registry does not recognize.
A key is "<MSH-9.1>^<MSH-9.2>" ("ADT^A01"), or "<MSH-9.1>" alone for a
registry entry that matches on message code alone ("ACK", whose MSH-9.2
carries the acknowledged message's trigger event rather than one of its own).
Example
import type { OverlayStructures } from "@cosyte/hl7";
// The required segments for one pair, at the type level:
type A01 = OverlayStructures["ADT^A01"]; // readonly ["EVN", "MSH", "PID", "PV1"]
Properties
ACK
readonlyACK: readonly ["MSA","MSH"]
ADT^A01
readonlyADT^A01: readonly ["EVN","MSH","PID","PV1"]
ADT^A02
readonlyADT^A02: readonly ["EVN","MSH","PID","PV1"]
ADT^A03
readonlyADT^A03: readonly ["EVN","MSH","PID","PV1"]
ADT^A04
readonlyADT^A04: readonly ["EVN","MSH","PID","PV1"]
ADT^A05
readonlyADT^A05: readonly ["EVN","MSH","PID","PV1"]
ADT^A06
readonlyADT^A06: readonly ["EVN","MSH","PID","PV1"]
ADT^A07
readonlyADT^A07: readonly ["EVN","MSH","PID","PV1"]
ADT^A08
readonlyADT^A08: readonly ["EVN","MSH","PID","PV1"]
ADT^A09
readonlyADT^A09: readonly ["EVN","MSH","PID","PV1"]
ADT^A10
readonlyADT^A10: readonly ["EVN","MSH","PID","PV1"]
ADT^A11
readonlyADT^A11: readonly ["EVN","MSH","PID","PV1"]
ADT^A12
readonlyADT^A12: readonly ["EVN","MSH","PID","PV1"]
ADT^A13
readonlyADT^A13: readonly ["EVN","MSH","PID","PV1"]
ADT^A14
readonlyADT^A14: readonly ["EVN","MSH","PID","PV1"]
ADT^A15
readonlyADT^A15: readonly ["EVN","MSH","PID","PRT","PV1"]
ADT^A16
readonlyADT^A16: readonly ["EVN","MSH","PID","PV1"]
ADT^A17
readonlyADT^A17: readonly ["EVN","MSH","PID","PV1"]
ADT^A20
readonlyADT^A20: readonly ["EVN","MSH","NPU"]
ADT^A21
readonlyADT^A21: readonly ["EVN","MSH","PID","PV1"]
ADT^A22
readonlyADT^A22: readonly ["EVN","MSH","PID","PV1"]
ADT^A23
readonlyADT^A23: readonly ["EVN","MSH","PID","PV1"]
ADT^A24
readonlyADT^A24: readonly ["EVN","MSH","PID"]
ADT^A25
readonlyADT^A25: readonly ["EVN","MSH","PID","PV1"]
ADT^A26
readonlyADT^A26: readonly ["EVN","MSH","PID","PV1"]
ADT^A27
readonlyADT^A27: readonly ["EVN","MSH","PID","PV1"]
ADT^A28
readonlyADT^A28: readonly ["EVN","MSH","PID","PV1"]
ADT^A29
readonlyADT^A29: readonly ["EVN","MSH","PID","PV1"]
ADT^A31
readonlyADT^A31: readonly ["EVN","MSH","PID","PV1"]
ADT^A32
readonlyADT^A32: readonly ["EVN","MSH","PID","PV1"]
ADT^A33
readonlyADT^A33: readonly ["EVN","MSH","PID","PV1"]
ADT^A37
readonlyADT^A37: readonly ["EVN","MSH","PID"]
ADT^A38
readonlyADT^A38: readonly ["EVN","MSH","PID","PV1"]
ADT^A40
readonlyADT^A40: readonly ["EVN","MRG","MSH","PID"]
ADT^A41
readonlyADT^A41: readonly ["EVN","MRG","MSH","PID"]
ADT^A42
readonlyADT^A42: readonly ["EVN","MRG","MSH","PID"]
ADT^A43
readonlyADT^A43: readonly ["EVN","MRG","MSH","PID"]
ADT^A44
readonlyADT^A44: readonly ["EVN","MRG","MSH","PID"]
ADT^A45
readonlyADT^A45: readonly ["EVN","MRG","MSH","PID","PV1"]
ADT^A47
readonlyADT^A47: readonly ["EVN","MRG","MSH","PID"]
ADT^A49
readonlyADT^A49: readonly ["EVN","MRG","MSH","PID"]
ADT^A50
readonlyADT^A50: readonly ["EVN","MRG","MSH","PID","PV1"]
ADT^A51
readonlyADT^A51: readonly ["EVN","MRG","MSH","PID","PV1"]
ADT^A52
readonlyADT^A52: readonly ["EVN","MSH","PID","PV1"]
ADT^A53
readonlyADT^A53: readonly ["EVN","MSH","PID","PV1"]
ADT^A54
readonlyADT^A54: readonly ["EVN","MSH","PID","PV1"]
ADT^A55
readonlyADT^A55: readonly ["EVN","MSH","PID","PV1"]
ADT^A60
readonlyADT^A60: readonly ["EVN","MSH","PID"]
ADT^A61
readonlyADT^A61: readonly ["EVN","MSH","PID","PV1"]
ADT^A62
readonlyADT^A62: readonly ["EVN","MSH","PID","PV1"]
DFT^P03
readonlyDFT^P03: readonly ["EVN","FT1","MSH","PID"]
DFT^P11
readonlyDFT^P11: readonly ["EVN","FT1","MSH","PID"]
MDM^T01
readonlyMDM^T01: readonly ["EVN","MSH","PID","PV1","TXA"]
MDM^T02
readonlyMDM^T02: readonly ["EVN","MSH","OBX","PID","PV1","TXA"]
MDM^T03
readonlyMDM^T03: readonly ["EVN","MSH","PID","PV1","TXA"]
MDM^T04
readonlyMDM^T04: readonly ["EVN","MSH","OBX","PID","PV1","TXA"]
MDM^T05
readonlyMDM^T05: readonly ["EVN","MSH","PID","PV1","TXA"]
MDM^T06
readonlyMDM^T06: readonly ["EVN","MSH","OBX","PID","PV1","TXA"]
MDM^T07
readonlyMDM^T07: readonly ["EVN","MSH","PID","PV1","TXA"]
MDM^T08
readonlyMDM^T08: readonly ["EVN","MSH","OBX","PID","PV1","TXA"]
MDM^T09
readonlyMDM^T09: readonly ["EVN","MSH","PID","PV1","TXA"]
MDM^T10
readonlyMDM^T10: readonly ["EVN","MSH","OBX","PID","PV1","TXA"]
MDM^T11
readonlyMDM^T11: readonly ["EVN","MSH","PID","PV1","TXA"]
OMG^O19
readonlyOMG^O19: readonly ["MSH","OBR","ORC"]
OMI^O23
readonlyOMI^O23: readonly ["IPC","MSH","OBR","ORC"]
OML^O21
readonlyOML^O21: readonly ["MSH","ORC"]
OML^O33
readonlyOML^O33: readonly ["MSH","ORC","SPM"]
OML^O35
readonlyOML^O35: readonly ["MSH","ORC","SAC","SPM"]
OML^O39
readonlyOML^O39: readonly ["MSH","ORC"]
OML^O59_A
readonlyOML^O59_A: readonly ["MSH","ORC"]
OMP^O09
readonlyOMP^O09: readonly ["MSH","ORC","RXO","RXR"]
ORM^O01
readonlyORM^O01: readonly ["ORC"]
ORU^R01
readonlyORU^R01: readonly ["MSH","OBR"]
ORU^R30
readonlyORU^R30: readonly ["MSH","OBR","OBX","ORC","PID"]
ORU^R31
readonlyORU^R31: readonly ["MSH","OBR","OBX","ORC","PID"]
ORU^R32
readonlyORU^R32: readonly ["MSH","OBR","OBX","ORC","PID"]
ORU^R40
readonlyORU^R40: readonly ["MSH","OBR"]
ORU^R42
readonlyORU^R42: readonly ["MSH","OBR"]
ORU^R43
readonlyORU^R43: readonly ["MSH","OBR"]
SIU^S12
readonlySIU^S12: readonly ["MSH","RGS","SCH"]
SIU^S13
readonlySIU^S13: readonly ["MSH","RGS","SCH"]
SIU^S14
readonlySIU^S14: readonly ["MSH","RGS","SCH"]
SIU^S15
readonlySIU^S15: readonly ["MSH","RGS","SCH"]
SIU^S16
readonlySIU^S16: readonly ["MSH","RGS","SCH"]
SIU^S17
readonlySIU^S17: readonly ["MSH","RGS","SCH"]
SIU^S18
readonlySIU^S18: readonly ["MSH","RGS","SCH"]
SIU^S19
readonlySIU^S19: readonly ["MSH","RGS","SCH"]
SIU^S20
readonlySIU^S20: readonly ["MSH","RGS","SCH"]
SIU^S21
readonlySIU^S21: readonly ["MSH","RGS","SCH"]
SIU^S22
readonlySIU^S22: readonly ["MSH","RGS","SCH"]
SIU^S23
readonlySIU^S23: readonly ["MSH","RGS","SCH"]
SIU^S24
readonlySIU^S24: readonly ["MSH","RGS","SCH"]
SIU^S26
readonlySIU^S26: readonly ["MSH","RGS","SCH"]
SIU^S27
readonlySIU^S27: readonly ["MSH","RGS","SCH"]
VXU^V04
readonlyVXU^V04: readonly ["MSH","PID"]
ParseOptions
Options accepted by parseHL7 to tune lenient/strict behaviour, inject a
profile, and configure optional preprocessing steps. Every field is
optional; parseHL7(raw, {}) is valid and produces the library defaults.
Remarks
With exactOptionalPropertyTypes: true, callers cannot pass
{ strict: undefined }: either omit the key or pass a boolean. The
profile: null form is the explicit opt-out from the process-scoped
default profile (PROF-08 semantics); profile omitted means "use the
default if one is registered".
Example
import { parseHL7, type ParseOptions } from "@cosyte/hl7";
const opts: ParseOptions = {
strict: true,
onWarning: (w) => console.warn(w.code),
dateFormats: ["YYYY-MM-DD"],
};
parseHL7(raw, opts);
Properties
charset?
readonlyoptionalcharset?:string
Override the character set used to decode Buffer input. When supplied
this wins over MSH-18 auto-discovery. When both are supplied and they
disagree (after alias normalization) the parser emits
ENCODING_MISMATCH and honours this override. Ignored for string
input.
Example
import { parseHL7 } from "@cosyte/hl7";
parseHL7(buf, { charset: "ISO-8859-1" });
dateFormats?
readonlyoptionaldateFormats?: readonlystring[]
Date formats your sender writes, tried in order after the canonical HL7
shape fails. They reach every datetime the library returns, from
msg.meta.timestamp to patient.dateOfBirth to an appointment's start
time, and a TS that matched one names it on matchedFormat.
These are tried ahead of an applied profile's own dateFormats, and they
are the ONLY non-canonical formats a typed datetime field accepts: a value
you have not described stays valid: false rather than being guessed at.
Tokens come from SUPPORTED_DATE_TOKENS; an entry with no supported token
matches nothing rather than throwing.
onWarning?
readonlyoptionalonWarning?:OnWarningCallback
profile?
readonlyoptionalprofile?:Profile|null
strict?
readonlyoptionalstrict?:boolean
stripMllpFraming?
readonlyoptionalstripMllpFraming?:boolean
trimFields?
readonlyoptionaltrimFields?:boolean
Patient
PID-derived patient view (HELPERS-02). msg.patient is undefined (D-04)
when no PID segment exists; this interface describes the shape when
present. identifiers and phoneNumbers are ALWAYS present as arrays
(D-09 / D-20): empty when the underlying field is absent. name is
ALWAYS present (D-19) even if {} when PID-5 is empty.
Example
import type { Patient } from "@cosyte/hl7";
const p: Patient = {
mrn: "MRN123",
identifiers: [{ idNumber: "MRN123", identifierTypeCode: "MR" }],
name: { familyName: "Smith", givenName: "Jane" },
familyName: "Smith",
givenName: "Jane",
fullName: "Jane Smith",
phoneNumbers: [],
};
console.log(p.dateOfBirth?.raw, p.dateOfBirth?.precision);
Properties
address?
readonlyoptionaladdress?:XAD
PID-11 home address parsed as XAD.
dateOfBirth?
readonlyoptionaldateOfBirth?:DtmParts
PID-7 date of birth as the fidelity TS. A day-only DOB keeps
precision: "day": never coerced to a UTC-midnight instant that would
read as the previous day in a negative-offset zone.
ethnicity?
readonlyoptionalethnicity?:CWE
PID-22 ethnic group.
familyName?
readonlyoptionalfamilyName?:string
PID-5.1 flat family name convenience (D-19).
fullName?
readonlyoptionalfullName?:string
Composed Western-order name "Given Middle Family, Suffix" (D-17).
givenName?
readonlyoptionalgivenName?:string
PID-5.2 flat given name convenience (D-19).
identifiers
readonlyidentifiers: readonlyCX[]
Full PID-3 identifier list, each parsed as a CX. Always present (D-09).
language?
readonlyoptionallanguage?:CE
PID-15 primary language.
middleName?
readonlyoptionalmiddleName?:string
PID-5.3 mapped from XPN.secondName (D-19).
mrn?
readonlyoptionalmrn?:string
Medical record number picked via pickMrn (D-07 / D-08).
name
readonlyname:XPN
Full PID-5 parsed name (first repetition). Always present as {} when empty (D-19).
notes?
readonlyoptionalnotes?: readonlystring[]
NTE note lines positionally attached to the (first) PID: notes immediately following the patient's PID segment, HL7-unescaped, in document order. OMITTED when the patient carries no notes. High-PHI-risk clinical narrative.
phoneNumbers
readonlyphoneNumbers: readonlyXTN[]
PID-13 (home) + PID-14 (business) repetitions concatenated. Always present (D-20).
race?
readonlyoptionalrace?:CWE
PID-10 race.
sex?
readonlyoptionalsex?:string
PID-8 administrative sex code.
PL
HL7 v2 Person Location (PL): structured location per HL7 Chapter 2. All
11 v1 components are optional. Fields are OMITTED when the underlying
component is absent (exactOptionalPropertyTypes). facility uses the
nested HD shape; assigningAuthorityForLocation is flattened to a
plain string in v1 (HL7 spec treats it as HD-shaped).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- pointOfCare: e.g. "ICU", "ED"
- room
- bed
- facility: nested HD (3 subcomponents form an HD composite)
- locationStatus: O=Occupied, U=Unoccupied, K=Contaminated, C=Closed, H=Housekeeping, I=Isolated
- personLocationType: C=Clinic, D=Department, H=Home, N=Nursing Unit, O=Office, R=Revenue Location
- building
- floor
- locationDescription: free-text
- comprehensiveLocationId
- assigningAuthorityForLocation (v1: flattened to string)
Example
import type { PL } from "@cosyte/hl7";
const bed: PL = {
pointOfCare: "ICU",
room: "101",
bed: "A",
facility: { namespaceId: "HOSP", universalId: "1.2.3", universalIdType: "UUID" },
};
Properties
assigningAuthorityForLocation?
readonlyoptionalassigningAuthorityForLocation?:string
bed?
readonlyoptionalbed?:string
building?
readonlyoptionalbuilding?:string
comprehensiveLocationId?
readonlyoptionalcomprehensiveLocationId?:string
facility?
readonlyoptionalfacility?:HD
floor?
readonlyoptionalfloor?:string
locationDescription?
readonlyoptionallocationDescription?:string
locationStatus?
readonlyoptionallocationStatus?:string
personLocationType?
readonlyoptionalpersonLocationType?:string
pointOfCare?
readonlyoptionalpointOfCare?:string
room?
readonlyoptionalroom?:string
PredicateLocation
Where in the message a condition predicate reads: a structural coordinate, a segment name plus the 1-indexed field, component and subcomponent positions that narrow it. Every member is a name or an index, so a location is inherently PHI-free and is safe to name in a finding.
The coordinate narrows from the outside in: a component needs a field,
and a subcomponent needs a component. A location that skips a level names
no addressable element and is refused as
FINDING_CODES.PROFILE_MALFORMED, as is a location whose segment is
not a valid segment name.
A comparison statement needs a field. A segment on its own has no
single content to compare, so a ComparisonPredicate whose location
stops at the segment is also PROFILE_MALFORMED. A
PresencePredicate may stop there: it asks whether the segment occurs.
Example
import type { PredicateLocation } from "@cosyte/hl7";
const completionStatus: PredicateLocation = { segment: "RXA", field: 20 };
const identifier: PredicateLocation = { segment: "RXA", field: 9, component: 1 };
Properties
component?
readonlyoptionalcomponent?:number
1-indexed component within the field. Requires field.
field?
readonlyoptionalfield?:number
1-indexed HL7 field position (e.g. 20 for RXA-20). Omitted targets the segment itself.
segment
readonlysegment:string
Segment name: 3 chars, [A-Z][A-Z0-9]{2} (standard or Z… segment).
subcomponent?
readonlyoptionalsubcomponent?:number
1-indexed subcomponent within the component. Requires component.
PresencePredicate
A presence statement: does the element at location carry content?
Content is read at or below the location, so a field-only location is valued
when any subcomponent of any component of any repetition is non-empty, and a
segment-only location is valued when the segment occurs at all. This is the
same reading of "present" the engine's R and X checks use.
Example
import type { PresencePredicate } from "@cosyte/hl7";
// "IF RXA-9.1 (Identifier) is valued"
const predicate: PresencePredicate = {
location: { segment: "RXA", field: 9, component: 1 },
presence: "is valued",
};
Properties
location
readonlylocation:PredicateLocation
The element the statement asks about.
presence
readonlypresence:PredicatePresence
Which of the two presence statements this is.
Profile
Structural placeholder for HL7 profiles. A profile bundles vendor-specific
tolerances, date formats, custom segment definitions, and optional
callbacks. Profiles are built with the defineProfile() factory.
customSegments is narrowed to the locked CustomSegmentDefinition shape,
and Profile carries an optional describe? method
so defineProfile()-produced profiles can be introspected without
consumers needing to narrow away from the Profile type. The describe
method is only populated by defineProfile(): hand-authored Profile
objects may omit it.
Example
import type { Profile } from "@cosyte/hl7";
const epic: Profile = {
name: "epic",
description: "Epic-specific quirks and date formats",
dateFormats: ["YYYYMMDDHHmmss", "YYYYMMDD"],
customSegments: {
ZDP: { fields: { departmentCode: 3, departmentName: 4 } },
},
segmentOverrides: {
PID: { fields: { siteMrn: 19 } },
},
};
Properties
customSegments?
readonlyoptionalcustomSegments?:Readonly<Record<string,CustomSegmentDefinition>>
dateFormats?
readonlyoptionaldateFormats?: readonlystring[]
Date formats this vendor writes. Merged after ParseOptions.dateFormats
and honoured on every datetime the library returns, exactly as the option
form is.
describe?
readonlyoptionaldescribe?: () =>string
Returns
string
description?
readonlyoptionaldescription?:string
lineage?
readonlyoptionallineage?: readonlystring[]
name
readonlyname:string
onWarning?
readonlyoptionalonWarning?:OnWarningCallback
segmentOverrides?
readonlyoptionalsegmentOverrides?:Readonly<Record<string,CustomSegmentDefinition>>
Site-specific field NAMES bound to positions on STANDARD HL7 v2 segments,
keyed by canonical segment name (PID, AL1, MSH, ...). A separate map
from Profile.customSegments, which stays Z-segment-only: a
standard name appearing there would change which segments the parser
treats as profile-claimed, and an author skimming a profile literal can
see at a glance which declarations touch standard clinical fields.
Read-side aliases, and ADDITIVE ONLY. A declaration here creates a new
name for Segment.get(name) to resolve on segments of that type, and
changes no existing read: positional access, dot-paths, the typed clinical
accessors, the warning list and both serializations are what they were
without it.
ProfiledMessage
A parsed message whose applied profile is statically known: every Hl7Message member unchanged, with the segment-type-keyed accessors scoped so a segment of a declared type exposes that type's declared field names to Segment.get.
It is a view, not a subclass: the parse allocates nothing extra and behaves
identically. Segments reached any other way (the undifferentiated
Hl7Message.allSegments walk) keep the string-accepting reader,
because the segment type is not knowable from a walk.
Example
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const epic = defineProfile({
name: "epic",
customSegments: { ZDP: { fields: { departmentCode: 3, departmentName: 4 } } },
});
const msg = parseHL7(raw, epic);
const zdp = msg.part("ZDP");
console.log(zdp?.get("departmentCode")?.value); // narrowed, no cast
// zdp?.get("departmentCod"); // does not compile: not a declared ZDP name
console.log(msg.part("PID")?.field(5).value); // undeclared type: any name compiles
Extends
Type Parameters
P
P
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. This is the list every datetime in the message
honours, from meta.timestamp to any field.asTs(), in exactly this
order; exposed so a caller can introspect what their options and profile
added up to.
Inherited from
encodingCharacters
readonlyencodingCharacters:EncodingCharacters
Inherited from
profile
readonlyprofile: {lineage: readonlystring[];name:string; } |undefined
Inherited from
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.
Inherited from
version
readonlyversion:string
HL7 version the model asserts for this message, from MSH-12.1.1, e.g.
"2.5" or "2.5.1".
Bounded, and it is "<withheld>" when MSH-12 does not hold a version.
Same reasoning as Segment.type: it presents as an identifier and
consumers label with it. meta.version keeps MSH-12 exactly as it
arrived, and is what version-scoped field selection reads, so this bound
changes no parsing behaviour.
Inherited from
warnings
readonlywarnings: readonlyHl7ParseWarning[]
Inherited from
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
Inherited from
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
Inherited from
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
Inherited from
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
Inherited from
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"
Inherited from
allergies()
allergies(): readonly
Allergy[]
Every AL1 and every IAM as an Allergy, one entry per segment in document
order, each naming the segment it was read from (source). An ADT^A60
carries its allergies in IAM, so it is read here too. D-05: returns []
when neither is present.
Limits: the IAR and NTE segments under an IAM are not read, nor IAM-1 or
IAM-8 onward (reach them with msg.segments("IAM")). IAM-6 is surfaced as
actionCode, and an exact D sets deleteRequested, but no action is ever
applied: a delete entry is still returned, an update never replaces an
earlier entry, and an AL1 and an IAM for the same allergen are two entries.
Returns
readonly Allergy[]
Example
for (const al of msg.allergies()) {
const kind = al.deleteRequested === true ? "delete request" : "allergy";
console.log(kind, al.source, al.code?.text, al.severity, al.actionCode);
}
Inherited from
allSegments()
allSegments(): readonly
Segment<string>[]
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<string>[]
Example
for (const seg of msg.allSegments()) {
console.log(seg.type);
}
Inherited from
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);
}
Inherited from
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
}
Inherited from
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);
Inherited from
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
}
Inherited from
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
Inherited from
getAll()
getAll<
S>(segmentType): readonlySegment<ProfileFieldName<P,S>>[]
Alias for ProfiledMessage.segments, with the same narrowing.
Type Parameters
S
S extends string
Parameters
segmentType
S
Returns
readonly Segment<ProfileFieldName<P, S>>[]
Overrides
identityEvents()
identityEvents(): readonly
IdentityEvent[]
Every recognized ADT patient-identity event (merge / move / change /
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/change events.
Returns [] when the trigger event is not in the identity family. The
recognized set is floored by the published message structures: every ADT
trigger event whose structure requires MRG is recognized. 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
}
}
Inherited from
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);
}
Inherited from
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);
Inherited from
is()
Internal
Single runtime implementation behind both signatures.
Call Signature
is<
K>(key):this is TypedMessage<K>
Is this message of the type key names, and if so, narrow it for the
compiler. The check is on the (MSH-9.1, MSH-9.2) pair the parser already
extracted, so a message whose MSH-9 carries the three-component form
ADT^A01^ADT_A01 answers true to is("ADT^A01").
A key is "<MSH-9.1>^<MSH-9.2>" ("ADT^A01"), or "<MSH-9.1>" alone for a
message type the published structure registry matches on message code alone
("ACK", whose MSH-9.2 carries the acknowledged message's trigger event).
SUPPORTED_OVERLAY_MESSAGES enumerates every key.
Any other string is false, never a throw: an unrecognized type, the
three-component form as a string, an empty string, or a value computed at
run time. is does not parse its own argument, and a caller who wants a raw
comparison has msg.meta.type.
Read-only: it inspects meta and mutates nothing.
Type Parameters
K
K extends keyof OverlayStructures
Parameters
key
K
Returns
this is TypedMessage<K>
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.is("ORU^R01")) {
const code: "ORU" = msg.meta.messageCode; // literal, no cast
console.log(msg.part("OBR")?.field(4).value);
}
console.log(msg.is("ZZZ^Z99")); // false: not a published key
Inherited from
Call Signature
is(
key):boolean
Answer for a key that is not known at compile time. A runtime-computed
string cannot narrow anything, so this form returns a plain boolean.
Parameters
key
string
Returns
boolean
Inherited from
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).
Each medication also carries orderControl: ORC-1 of the ORC that opened
its order group (every RX* up to the next ORC shares it), exactly as sent.
It is never interpreted into an active, held or discontinued state, and it
is omitted when no ORC precedes the RX* or ORC-1 is empty, never carried
over from an earlier group.
Returns
readonly Medication[]
Example
for (const med of msg.medications()) {
console.log(med.orderControl); // e.g. "NW" (new) or "DC" (discontinue), verbatim
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);
}
Inherited from
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);
}
Inherited from
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
Inherited from
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
}
Inherited from
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
}
Inherited from
part()
part<
S>(segmentType):Segment<ProfileFieldName<P,S>> |undefined
The first segment of segmentType, with that type's declared field names.
Type Parameters
S
S extends string
Parameters
segmentType
S
Returns
Segment<ProfileFieldName<P, S>> | undefined
Overrides
parts()
parts<
S>(segmentType): readonlySegment<ProfileFieldName<P,S>>[]
Every segment of segmentType, with that type's declared field names.
Type Parameters
S
S extends string
Parameters
segmentType
S
Returns
readonly Segment<ProfileFieldName<P, S>>[]
Overrides
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
Inherited from
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
Inherited from
segments()
segments<
S>(segmentType): readonlySegment<ProfileFieldName<P,S>>[]
Every segment of segmentType, with that type's declared field names.
Type Parameters
S
S extends string
Parameters
segmentType
S
Returns
readonly Segment<ProfileFieldName<P, S>>[]
Overrides
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"
Inherited from
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
Inherited from
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
Inherited from
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
Inherited from
RawComponent
A single component inside a repetition: the most deeply nested data
layer in the HL7 positional tree. A component is an ordered list of
subcomponent strings (subcomponent separator & by default).
Example
import type { RawComponent } from "@cosyte/hl7";
const comp: RawComponent = { subcomponents: ["Smith", "John"] };
Properties
rawSubcomponents?
readonlyoptionalrawSubcomponents?: readonly (string|undefined)[]
Internal
Escape-fidelity overlay, positionally aligned with subcomponents:
for each index, the subcomponent's original wire bytes to emit
verbatim instead of re-escaping the decoded form. An entry is present
(non-undefined) only when a subcomponent carried an escape whose decoded
form does not re-escape back to the exact wire bytes: i.e. a recognize-
and-preserve escape (\H\, \Z.., charset/formatting) or a hex escape
(\X41\, or non-canonical hex casing). Delimiter escapes (\F→|) round-
trip through reescape unchanged and get no overlay. The whole field is
absent when no subcomponent needs it (the overwhelming common case), so a
plain message's raw tree is byte-for-byte the shape it was before this
overlay existed.
Consumed only by the serializer ("src/serialize/emit-field.ts"); never read by the value/coercion surface, so it changes no decoded value.
subcomponents
readonlysubcomponents: readonlystring[]
The component's subcomponents, HL7-decoded: the tokenizer expands
escape sequences on parse so consumers read literal values (Smith|Jones,
not Smith\F\Jones). This is the value surface every reader uses
(Field.value, the composite parsers, dot-path, toJSON).
RawField
A positional field inside a segment. Carries its repetitions plus an
isNull discriminant that distinguishes the HL7 explicit null ("", a
two-character literal double quote) from an empty field (no content
between delimiters).
Example
import type { RawField } from "@cosyte/hl7";
const nullField: RawField = { repetitions: [], isNull: true };
const emptyField: RawField = { repetitions: [], isNull: false };
Properties
isNull
readonlyisNull:boolean
repetitions
readonlyrepetitions: readonlyRawRepetition[]
RawRepetition
A single repetition inside a field: HL7 fields may repeat using the
repetition separator (~ by default). Each repetition is an ordered list
of components.
Example
import type { RawRepetition } from "@cosyte/hl7";
const rep: RawRepetition = { components: [{ subcomponents: ["Smith"] }] };
Properties
components
readonlycomponents: readonlyRawComponent[]
RawSegment
A parsed HL7 segment: the top level of the positional tree. The name
is the three-character segment identifier (MSH, PID, ZPI, ...) and
fields is the 1-indexed positional field array (see the JSDoc on
fields for the index 0 slot convention).
Example
import type { RawSegment } from "@cosyte/hl7";
const pid: RawSegment = {
name: "PID",
fields: [
{ repetitions: [], isNull: false },
],
};
Properties
fields
readonlyfields: readonlyRawField[]
Positional fields array using HL7 1-indexed convention for ALL segments.
fields[0]is the segment name / separator placeholder slot (never a data field).fields[N]for N >= 1 is the HL7 N-th field.
Examples:
- MSH:
fields[0]= field-separator char,fields[1]= MSH-2 (encoding chars),fields[2]= MSH-3, ...,fields[11]= MSH-12. - PID:
fields[0]= "PID" name placeholder,fields[1]= PID-1,fields[2]= PID-2, ....
name
readonlyname:string
RenderedText
The normalized display model produced by renderText: a flat plain-text string plus the structured highlight-aware runs, plus an honesty list of the escape sequences that were preserved rather than rendered.
Properties
runs
readonlyruns: readonlyTextRun[]
The structured, highlight-aware form: { text, highlighted } runs in
document order. Empty runs are elided. Use this to preserve emphasis
(bold / reverse-video / etc.) that the flat text drops.
text
readonlytext:string
The full plain-text normalization: formatting commands become
whitespace / line breaks and highlight boundaries are dropped. This is
the string to show a human or feed a downstream .text. Equal to the
concatenation of every runs entry's text.
unrenderedSequences
readonlyunrenderedSequences: readonlystring[]
The escape sequences renderText preserved verbatim instead of
rendering: vendor \Zdddd…\, charset switches \Cxxyy/\Mxxyyzz,
and any malformed / unterminated sequence. Their literal characters ALSO
appear in text/runs (never silently dropped); this list
exists so a consumer can detect that a non-render decision was made and
surface or route those sequences deliberately. Empty when everything
rendered cleanly.
RenderTextOptions
Options for renderText.
Properties
newline?
readonlyoptionalnewline?:string
The string emitted for each line break (\.br\, \.sp, \.ce\, and a
raw CR/LF/CRLF in the input). Defaults to "\n". Set "\r\n" for
Windows-style display, or " " to flatten a note to a single line.
RepeatPattern
An order/medication timing repeat pattern (HL7 Table 0335): the frequency/SIG field (TQ1-3, or the legacy embedded TQ interval RI.1).
Safety contract. code is the decoded field value (HL7 escapes are
unescaped as with every field read) and is never resolved to clock times,
normalized, or mapped to a different frequency: reading Q6H as "daily"
or silently dropping a BID changes the administered dose count, a
transcription-class harm. kind/interval are convenience provenance ONLY;
code is the authoritative value.
Example
import type { RepeatPattern } from "@cosyte/hl7";
const q6h: RepeatPattern = { code: "Q6H", kind: "parametric", interval: { count: 6, unit: "H" } };
const bid: RepeatPattern = { code: "BID", kind: "named" };
Properties
code
readonlycode:string
The Table-0335 repeat-pattern code exactly as authored (e.g. "Q6H", "BID"). Never normalized.
interval?
readonlyoptionalinterval?:object
For a "parametric" Q<integer><unit> template only: the load-bearing
integer and its unit letter (S/M/H/D/W/L, or J for
day-of-week). OMITTED for "named"/"unknown" patterns. Informational,
code remains authoritative.
count
readonlycount:number
unit
readonlyunit:string
kind
readonlykind:RepeatPatternKind
Provenance classification of code: never used to resolve a schedule. See RepeatPatternKind.
ResultStatusClassification
A result status read against its HL7 table and classified by HL7's
published v2-to-FHIR status map: carried as resultStatus on every
Observation (from OBX-11) and every Order (from OBR-25).
Safety contract. Each OBX classifies from its own OBX-11 alone and each
order from its own OBR-25 alone: nothing is inherited, propagated or
reconciled across segments. A status is reported, never applied: an
"entered-in-error" observation is still returned, and replacing or
deleting an earlier result is the caller's decision. classification is
"final" only when the field is exactly F; every value HL7's map does not
map is "undetermined" (see ResultStatusClass), never a guess.
The object is plain data, not a FHIR element: no resource is built and no terminology service is consulted.
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
for (const obs of msg.observations()) {
const { classification, code } = obs.resultStatus;
if (classification === "entered-in-error") console.log("posted in error:", code);
else if (classification === "undetermined") console.log("not classifiable:", code);
}
Properties
classification
readonlyclassification:ResultStatusClass
The classification HL7's map gives the code, or "undetermined".
code?
readonlyoptionalcode?:string
The raw code, byte-identical to the status (OBX-11) or orderStatus
(OBR-25) the same observation or order surfaces: the field's first value,
decoded. OMITTED when that field is absent, empty or "", exactly when
status / orderStatus is. When the field carries more than one
repetition, component or subcomponent this is still only the first value,
and classification is "undetermined".
map
readonlymap:ResultStatusMap
The HL7 v2-to-FHIR map the classification followed.
table
readonlytable:ResultStatusTable
The HL7 table the code was read against.
ResultStatusMap
The published HL7 v2-to-FHIR map a ResultStatusClassification
followed, by canonical URL and version. The maps come from the HL7 Version 2
to FHIR Implementation Guide (STU 1, standards status Informative); a later
map revision shows up here as a changed version.
Example
import type { ResultStatusMap } from "@cosyte/hl7";
const map: ResultStatusMap = {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70085-to-observation-status",
version: "1.0.0",
};
Properties
url
readonlyurl:"http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70085-to-observation-status"|"http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70123-queries-to-diagnostic-report-status"
Canonical URL of the ConceptMap whose rows the classification follows.
version
readonlyversion:"1.0.0"
The ConceptMap version the classification follows.
ResultStatusTable
The HL7 v2 code table a ResultStatusClassification read: Table 0085 (Observation Result Status, OBX-11) or Table 0123 (Result Status, OBR-25), each at the code-system version the map was checked against.
Example
import type { ResultStatusTable } from "@cosyte/hl7";
const table: ResultStatusTable = { name: "HL7 Table 0085", version: "3.0.0" };
Properties
name
readonlyname:"HL7 Table 0085"|"HL7 Table 0123"
"HL7 Table 0085" for OBX-11, "HL7 Table 0123" for OBR-25.
version
readonlyversion:"3.0.0"
The code-system version the table's codes were taken from.
SegmentRule
A rule for one segment type. usage constrains whether
the segment must / must not appear; cardinality constrains how many times;
fields are the per-field rules, applied to every occurrence of the
segment.
Properties
cardinality?
readonlyoptionalcardinality?:Cardinality
Occurrence-count constraint for this segment across the message.
condition?
readonlyoptionalcondition?:ConditionPredicate
The ConditionPredicate that decides this rule's usage, evaluated
against the message. Only a rule whose usage is conditional (C, CE,
or a declared conditional) may declare one.
fields?
readonlyoptionalfields?: readonlyFieldRule[]
Per-field rules, applied to each occurrence of this segment.
segment
readonlysegment:string
Segment name: 3 chars, [A-Z][A-Z0-9]{2} (standard or Z… segment).
severity?
readonlyoptionalseverity?:FindingSeverity
Severity for the segment-level presence / cardinality findings. Default "error".
usage?
readonlyoptionalusage?:UsageCode
Usage for the segment as a whole. R ⇒ at least one occurrence required;
X ⇒ none permitted; RE / O ⇒ no presence constraint; C / CE /
B ⇒ presence not evaluated (no predicate language). A declared
conditional such as C(R/X) takes the outcome a caller resolves, and is
evaluated exactly as C when nothing resolves it. Omitted ⇒ Optional.
SerializedMessage
Snapshot-stable JSON projection of an Hl7Message (SER-03). Every field
is readonly; segment order is preserved; isNull flags are preserved.
Runtime immutability: the top-level object is Object.freezed
(boundary-frozen). Inner arrays are readonly at the TypeScript type
level but mutable at runtime: treat as immutable, do not mutate.
Example
import { parseHL7, type SerializedMessage } from "@cosyte/hl7";
const msg = parseHL7(raw);
const snap: SerializedMessage = msg.toJSON();
console.log(snap.segments[0]?.name); // "MSH"
console.log(snap.warnings.length); // 0 when clean
console.log(JSON.stringify(msg) === JSON.stringify(snap)); // true
console.log(Object.isFrozen(snap)); // true (D-30 boundary freeze)
Properties
encodingCharacters
readonlyencodingCharacters:EncodingCharacters
profile?
readonlyoptionalprofile?:object
lineage
readonlylineage: readonlystring[]
name
readonlyname:string
segments
readonlysegments: readonlyobject[]
warnings
readonlywarnings: readonlyHl7ParseWarning[]
SiuAppointment
Typed SCH (scheduling activity information) content for buildSiu.
Example
import type { SiuAppointment } from "@cosyte/hl7";
const appointment: SiuAppointment = {
placerAppointmentId: "PL-1001",
fillerAppointmentId: "FL-2002",
startDateTime: "20260801090000",
endDateTime: "20260801093000",
fillerStatusCode: { identifier: "Booked" },
};
Properties
endDateTime?
readonlyoptionalendDateTime?:string|DtmParts
SCH-11.5 End Date/Time of the appointment timing quantity.
fillerAppointmentId?
readonlyoptionalfillerAppointmentId?:string
SCH-2 Filler Appointment ID.
fillerStatusCode?
readonlyoptionalfillerStatusCode?:CWE
SCH-25 Filler Status Code (HL7 Table 0278). Verbatim; never defaulted.
placerAppointmentId?
readonlyoptionalplacerAppointmentId?:string
SCH-1 Placer Appointment ID.
startDateTime?
readonlyoptionalstartDateTime?:string|DtmParts
SCH-11.4 Start Date/Time of the appointment timing quantity.
SiuResource
Typed AI* (appointment resource) content for buildSiu.
Example
import type { SiuResource } from "@cosyte/hl7";
const theatre: SiuResource = { kind: "location", code: { identifier: "OR-1" } };
const surgeon: SiuResource = {
kind: "personnel",
person: { idNumber: "9990", familyName: "Welby", identifierTypeCode: "NPI" },
};
Properties
code?
readonlyoptionalcode?:CWE
AI*-3 resource identifier for a service, general resource or location
(AIS-3 / AIG-3 are coded elements; AIL-3 is a location, whose first
component is the location id). Ignored for a personnel resource, which
carries person instead.
kind
readonlykind:SiuResourceKind
Which resource segment to emit: AIS, AIG, AIL or AIP.
person?
readonlyoptionalperson?:XCN
AIP-3 personnel resource: the appointment provider. Personnel resources only.
setId?
readonlyoptionalsetId?:string
AI*-1 Set ID.
SiuResourceGroup
Typed RGS (resource group) content for buildSiu.
Example
import type { SiuResourceGroup } from "@cosyte/hl7";
const group: SiuResourceGroup = {
setId: "1",
resourceGroupId: { identifier: "RG-1", text: "Theatre" },
resources: [{ kind: "location", code: { identifier: "OR-1" } }],
};
Properties
actionCode?
readonlyoptionalactionCode?:string
RGS-2 Segment Action Code.
resourceGroupId?
readonlyoptionalresourceGroupId?:CWE
RGS-3 Resource Group ID.
resources?
readonlyoptionalresources?: readonlySiuResource[]
The AI* resources belonging to this group, in the order supplied.
setId?
readonlyoptionalsetId?:string
RGS-1 Set ID.
SN
HL7 v2 Structured Numeric (SN) composite. num1 and num2 are
ALWAYS-PRESENT keys (typed number | undefined) so callers can destructure
uniformly, mirroring NM; comparator and separatorOrSuffix are OMITTED
when absent (exactOptionalPropertyTypes).
An absent comparator means the default = relation per HL7 Chapter 2A.
This library surfaces the structure only: it does not evaluate the
inequality, validate the unit, or convert values.
Example
import type { SN } from "@cosyte/hl7";
const gfr: SN = { comparator: ">", num1: 90, num2: undefined }; // >90
const range: SN = { num1: 100, separatorOrSuffix: "-", num2: 200 }; // 100-200
Properties
comparator?
readonlyoptionalcomparator?:string
SN.1 comparator: one of > < >= <= = <>. Omitted ⇒ default =.
num1
readonlynum1:number|undefined
SN.2 first numeric value. undefined when absent or non-numeric (never NaN).
num2
readonlynum2:number|undefined
SN.4 second numeric value. undefined when absent or non-numeric (never NaN).
separatorOrSuffix?
readonlyoptionalseparatorOrSuffix?:string
SN.3 separator/suffix: - (range), :// (ratio), + (suffix), .. Omitted when absent.
StructureFamily
A referenced structure id and the variant family actually read for it.
Properties
members
readonlymembers: readonlystring[]
The family members present in the snapshot, sorted.
structureId
readonlystructureId:string
The structure id the publication's message list references.
StructureFinding
One typed published-structure finding: the StructureFindingCode, a
severity, the structural StructureFindingLocus, and a human-readable
message.
The message is PHI-safe by construction: it names the segment, the
occurrence, the published structure and the counts involved, and never a
value read out of the message.
Example
import type { StructureFinding } from "@cosyte/hl7";
const f: StructureFinding = {
code: "STRUCTURE_SEGMENT_CARDINALITY",
severity: "error",
locus: { segment: "EVN", structureId: "ADT_A01-A" },
message: 'Segment "EVN" occurs 0 times; published structure ADT_A01-A requires at least 1.',
};
Properties
code
readonlycode:StructureFindingCode
Which check fired.
locus
readonlylocus:StructureFindingLocus
Where in the message, structurally.
message
readonlymessage:string
Human-readable description of the rule that fired. Carries no field value.
severity
readonlyseverity:FindingSeverity
How hard a finding this is.
An ordering or cardinality finding is an error: the publication states a
constraint and the message breaks it. An unexpected segment is a warning,
because HL7 reserves Z segments for exactly this and a site-defined
segment in a live feed is normal traffic rather than a broken message. The
check still fires, under its own code, so a consumer that wants it to be an
error can make it one.
StructureFindingLocus
The structural locus a published-structure finding refers to: a segment name, the 0-indexed occurrence of that segment where one occurrence is responsible, and the published structure the finding was raised against.
Every member is a name or an index, so a locus is inherently PHI-free and never carries a field value. The occurrence index counts occurrences of that segment name, the same way a conformance finding's does.
Example
import type { StructureFindingLocus } from "@cosyte/hl7";
const locus: StructureFindingLocus = { segment: "PID", occurrence: 1, structureId: "ADT_A01-A" };
Properties
occurrence?
readonlyoptionaloccurrence?:number
0-indexed occurrence of that segment name, present when one occurrence is responsible. A minimum-cardinality finding names no occurrence: the responsible occurrence is the one that is not there.
segment
readonlysegment:string
Segment name (e.g. "PID"), bounded to the shape a segment id may take.
structureId
readonlystructureId:string
The published structure variant the finding was raised against.
StructureGroup
The presence verdict for one expected segment of a recognized message type.
Properties
anchorSegments
readonlyanchorSegments: readonlystring[]
The anchor segment name(s) whose presence would satisfy this group.
name
readonlyname:string
The expectation's label from its ExpectedSegmentGroup, e.g. "OBR".
present
readonlypresent:boolean
true when the required segment is present in the message.
requiredSegment
readonlyrequiredSegment:string
The segment the published structure gives a minimum of one.
StructurePublicationRef
The publication a snapshot was taken from.
Properties
commit
readonlycommit:string
The commit the snapshot was taken at.
commitDate
readonlycommitDate:string
That commit's date.
name
readonlyname:string
Human-readable name of the publication.
repository
readonlyrepository:string
The publishing repository, e.g. "HL7/v2ig".
repositoryUrl
readonlyrepositoryUrl:string
Its web URL.
tree
readonlytree:string
The subtree the structure definitions live in.
StructureRegistryProvenance
Everything needed to audit where a structural expectation came from: the publication, the exact bytes, and the structure behind every recognized pair.
Properties
families
readonlyfamilies: readonlyStructureFamily[]
Each referenced structure id and the variant family read for it.
files
readonlyfiles: readonlyStructureSnapshotFile[]
Every vendored file the registry was derived from, with its sha256.
pairs
readonlypairs: readonlyStructureSourcePair[]
Each recognized pair and the structure id it came from.
publication
readonlypublication:StructurePublicationRef
The publication the registry was derived from.
snapshotTakenAt
readonlysnapshotTakenAt:string
ISO date the snapshot was taken.
StructureSnapshotFile
One vendored publication file the shipped registry was derived from.
Properties
bytes
readonlybytes:number
Byte length of the vendored file.
path
readonlypath:string
Path relative to the vendored snapshot directory.
sha256
readonlysha256:string
Lowercase hex sha256 of the vendored bytes.
url
readonlyurl:string
The upstream URL the bytes were fetched from.
StructureSourcePair
One (message code, trigger event) pair and the structure it came from.
An empty triggerEvent marks an entry matched on message code alone, and an
empty structureId marks a retained transcription.
Properties
messageCode
readonlymessageCode:string
MSH-9.1 message code.
structureId
readonlystructureId:string
The published structure id, or "" for a retained transcription.
triggerEvent
readonlytriggerEvent:string
MSH-9.2 trigger event, or "" for a code-alone match.
StructureValidationResult
The result of validateMessageStructure.
Read validated first. false means the publication could not answer
for this message and reason says why; findings is empty and no structure
was selected. It is NOT "the message is fine".
validated: true with findings.length === 0 is not a conformance
attestation either. It means the message did not break the published
structure in the three ways checked here: segment order, segment occurrence
counts, and segments the publication does not name. Field content, datatypes,
value sets, tables and clinical correctness are all unchecked, and the
publication itself is vendored at a fixed commit.
Example
import { parseHL7, validateMessageStructure } from "@cosyte/hl7";
const result = validateMessageStructure(parseHL7(raw));
if (result.validated && result.findings.length === 0) {
// nothing checked here was violated, against result.structureId
}
Properties
findings
readonlyfindings: readonlyStructureFinding[]
The findings, in a stable order: the ordering finding first, then
cardinality findings by segment name, then unexpected segments in the order
they appear in the message. Always empty when validated is false.
reason
readonlyreason:""|StructureNotValidatedReason
Why validation did not run. Empty string when it did.
reasonMessage
readonlyreasonMessage:string
Human-readable form of reason. Empty string when validation ran.
structureId
readonlystructureId:string
The published structure variant the findings were raised against, e.g.
"ADT_A01-A". Empty string when validated is false.
structureIds
readonlystructureIds: readonlystring[]
Every variant of the family that was considered, sorted. Empty when
validated is false.
validated
readonlyvalidated:boolean
Whether a published structure was actually validated against.
SupportedBuilderMessage
One (message code, trigger event) pair the typed builders can author, with the builder that authors it and the segments the published structure requires for it.
triggerEvent is the empty string for a pair the registry matches on message
code alone (ACK, whose MSH-9.2 carries the acknowledged message's trigger
event); supportsBuilderMessage treats such an entry as matching any
trigger event for that code.
Example
import { SUPPORTED_BUILDER_MESSAGES } from "@cosyte/hl7";
const vxu = SUPPORTED_BUILDER_MESSAGES.find((m) => m.messageCode === "VXU");
console.log(vxu?.triggerEvent, vxu?.builder); // "V04" "buildVxu"
Properties
builder
readonlybuilder:string
The exported builder that authors this pair.
messageCode
readonlymessageCode:string
MSH-9.1 message code.
requiredSegments
readonlyrequiredSegments: readonlystring[]
The segments the derived structure registry marks required for this pair.
triggerEvent
readonlytriggerEvent:string
MSH-9.2 trigger event; "" when the registry matches on message code alone.
SupportedOverlayMessage
One published overlay key, with the message type it matches and the segments the published structure requires for it.
triggerEvent is the empty string for a key the registry matches on message
code alone (ACK), and Hl7Message.is then answers true for that key
whatever MSH-9.2 carries, in the same way supportsBuilderMessage treats a
code-only registry entry.
Example
import { SUPPORTED_OVERLAY_MESSAGES } from "@cosyte/hl7";
const oru = SUPPORTED_OVERLAY_MESSAGES.find((m) => m.key === "ORU^R01");
console.log(oru?.messageCode, oru?.triggerEvent); // "ORU" "R01"
console.log(oru?.requiredSegments); // ["MSH", "OBR"]
Properties
key
readonlykey:string
The overlay key, e.g. "ORU^R01" or the code-only "ACK".
messageCode
readonlymessageCode:string
MSH-9.1 message code.
requiredSegments
readonlyrequiredSegments: readonlystring[]
The segments the derived structure registry marks required for this key.
triggerEvent
readonlytriggerEvent:string
MSH-9.2 trigger event; "" when the registry matches on message code alone.
TextRun
One contiguous run of rendered display text, tagged with whether it fell
inside a \H…\N highlight span. Runs preserve emphasis boundaries that
the flat RenderedText.text intentionally drops.
Properties
highlighted
readonlyhighlighted:boolean
true when this run is inside a \H\…\N highlight span.
text
readonlytext:string
The literal display text of this run: escape sentinels already resolved (delimiter/hex decoded, formatting → whitespace/line breaks). Never contains a formatting sentinel; may contain the newline used for breaks.
TimingQuantity
A composite-quantity (CQ) value on an order/medication timing: the TQ1-2
service quantity. value is strict-Number() parsed (undefined,
never NaN); units carries any CQ.2 units. Both keys OMITTED when absent.
Example
import type { TimingQuantity } from "@cosyte/hl7";
const q: TimingQuantity = { value: 1, units: { identifier: "tablet" } };
Properties
units?
readonlyoptionalunits?:CWE
CQ.2 units.
value?
readonlyoptionalvalue?:number
CQ.1 quantity numeric value (strict-parsed; never NaN).
TypedBuilderCoverage
One typed builder's emit capability: the message code it authors and the segment names its typed init can populate. The published support set is the intersection of this with the derived structure registry.
Example
import { TYPED_BUILDER_COVERAGE } from "@cosyte/hl7";
const adt = TYPED_BUILDER_COVERAGE.find((c) => c.builder === "buildAdt");
console.log(adt?.segments.includes("MRG")); // true: prior identity is supported
Properties
builder
readonlybuilder:string
The exported builder function that authors this message code.
fixedTriggerEvents
readonlyfixedTriggerEvents: readonlystring[]
The trigger events this builder can author, when it authors a fixed set. Empty when the builder authors every trigger event the registry records for its message code (the builders that take a trigger-event argument).
messageCode
readonlymessageCode:string
MSH-9.1 message code this builder authors.
segments
readonlysegments: readonlystring[]
Every segment name the builder's typed init can populate.
TypedMessage
A parsed message narrowed to one overlay key: every Hl7Message member
unchanged, the message code and trigger event at literal types, and part /
parts scoped at compile time to the segment names that pair's published
structure marks required.
It is a view, not a subclass: msg.is(key) narrows the message you already
have, allocates nothing, and changes no runtime behaviour. Nothing here is
typed as guaranteed-present, because the parser tolerates a message that
omits a required segment (and warns); part returns Segment | undefined
and parts a possibly-empty list, exactly as segments does.
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.is("ORU^R01")) {
const obr = msg.part("OBR"); // Segment | undefined, no cast
console.log(obr?.field(4).value);
// msg.part("PID"); // does not compile: PID is not required for ORU^R01
console.log(msg.segments("PID").length); // the base accessor takes any name
}
Extends
Type Parameters
K
K extends OverlayKey
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. This is the list every datetime in the message
honours, from meta.timestamp to any field.asTs(), in exactly this
order; exposed so a caller can introspect what their options and profile
added up to.
Inherited from
encodingCharacters
readonlyencodingCharacters:EncodingCharacters
Inherited from
meta
readonlymeta:TypedMeta<K>
MSH-derived metadata, with the message type at the key's literal types.
Overrides
profile
readonlyprofile: {lineage: readonlystring[];name:string; } |undefined
Inherited from
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.
Inherited from
version
readonlyversion:string
HL7 version the model asserts for this message, from MSH-12.1.1, e.g.
"2.5" or "2.5.1".
Bounded, and it is "<withheld>" when MSH-12 does not hold a version.
Same reasoning as Segment.type: it presents as an identifier and
consumers label with it. meta.version keeps MSH-12 exactly as it
arrived, and is what version-scoped field selection reads, so this bound
changes no parsing behaviour.
Inherited from
warnings
readonlywarnings: readonlyHl7ParseWarning[]
Inherited from
Accessors
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
Inherited from
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
Inherited from
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
Inherited from
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"
Inherited from
allergies()
allergies(): readonly
Allergy[]
Every AL1 and every IAM as an Allergy, one entry per segment in document
order, each naming the segment it was read from (source). An ADT^A60
carries its allergies in IAM, so it is read here too. D-05: returns []
when neither is present.
Limits: the IAR and NTE segments under an IAM are not read, nor IAM-1 or
IAM-8 onward (reach them with msg.segments("IAM")). IAM-6 is surfaced as
actionCode, and an exact D sets deleteRequested, but no action is ever
applied: a delete entry is still returned, an update never replaces an
earlier entry, and an AL1 and an IAM for the same allergen are two entries.
Returns
readonly Allergy[]
Example
for (const al of msg.allergies()) {
const kind = al.deleteRequested === true ? "delete request" : "allergy";
console.log(kind, al.source, al.code?.text, al.severity, al.actionCode);
}
Inherited from
allSegments()
allSegments(): readonly
Segment<string>[]
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<string>[]
Example
for (const seg of msg.allSegments()) {
console.log(seg.type);
}
Inherited from
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);
}
Inherited from
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
}
Inherited from
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);
Inherited from
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
}
Inherited from
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
Inherited from
getAll()
getAll(
segmentType): readonlySegment<string>[]
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, and
matches segment names ignoring case exactly as it does.
Parameters
segmentType
string
Returns
readonly Segment<string>[]
Example
for (const obx of msg.getAll("OBX")) {
console.log(obx.field(5).value);
}
Inherited from
identityEvents()
identityEvents(): readonly
IdentityEvent[]
Every recognized ADT patient-identity event (merge / move / change /
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/change events.
Returns [] when the trigger event is not in the identity family. The
recognized set is floored by the published message structures: every ADT
trigger event whose structure requires MRG is recognized. 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
}
}
Inherited from
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);
}
Inherited from
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);
Inherited from
is()
Internal
Single runtime implementation behind both signatures.
Call Signature
is<
K>(key):this is TypedMessage<K>
Is this message of the type key names, and if so, narrow it for the
compiler. The check is on the (MSH-9.1, MSH-9.2) pair the parser already
extracted, so a message whose MSH-9 carries the three-component form
ADT^A01^ADT_A01 answers true to is("ADT^A01").
A key is "<MSH-9.1>^<MSH-9.2>" ("ADT^A01"), or "<MSH-9.1>" alone for a
message type the published structure registry matches on message code alone
("ACK", whose MSH-9.2 carries the acknowledged message's trigger event).
SUPPORTED_OVERLAY_MESSAGES enumerates every key.
Any other string is false, never a throw: an unrecognized type, the
three-component form as a string, an empty string, or a value computed at
run time. is does not parse its own argument, and a caller who wants a raw
comparison has msg.meta.type.
Read-only: it inspects meta and mutates nothing.
Type Parameters
K
K extends keyof OverlayStructures
Parameters
key
K
Returns
this is TypedMessage<K>
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.is("ORU^R01")) {
const code: "ORU" = msg.meta.messageCode; // literal, no cast
console.log(msg.part("OBR")?.field(4).value);
}
console.log(msg.is("ZZZ^Z99")); // false: not a published key
Inherited from
Call Signature
is(
key):boolean
Answer for a key that is not known at compile time. A runtime-computed
string cannot narrow anything, so this form returns a plain boolean.
Parameters
key
string
Returns
boolean
Inherited from
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).
Each medication also carries orderControl: ORC-1 of the ORC that opened
its order group (every RX* up to the next ORC shares it), exactly as sent.
It is never interpreted into an active, held or discontinued state, and it
is omitted when no ORC precedes the RX* or ORC-1 is empty, never carried
over from an earlier group.
Returns
readonly Medication[]
Example
for (const med of msg.medications()) {
console.log(med.orderControl); // e.g. "NW" (new) or "DC" (discontinue), verbatim
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);
}
Inherited from
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);
}
Inherited from
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
Inherited from
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
}
Inherited from
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
}
Inherited from
part()
part(
name):Segment<string> |undefined
The first segment of name, or undefined when the message carries none.
Parameters
name
Returns
Segment<string> | undefined
Overrides
parts()
parts(
name): readonlySegment<string>[]
Every segment of name in document order; empty when there are none.
Parameters
name
Returns
readonly Segment<string>[]
Overrides
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
Inherited from
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
Inherited from
segments()
segments(
segmentType): readonlySegment<string>[]
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.
Segment names are matched ignoring ASCII case, on both sides. A sender
that ships obx is returned by segments("OBX"), and segments("obx")
returns the same array (one cache entry, so the D-11 identity guarantee
does not split across spellings). A SEGMENT_CASE warning records the
sender's deviation; Segment.raw.name is the spelling that arrived.
Parameters
segmentType
string
Returns
readonly Segment<string>[]
Example
const pid = msg.segments("PID")[0];
if (pid !== undefined) console.log(pid.field(5).value);
Inherited from
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"
Inherited from
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
Inherited from
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
Inherited from
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
Inherited from
UsageOutcomes
The two usage outcomes a DeclaredConditionalUsage token records: the usage that applies when the condition is true, and the one that applies when it is false. Both are simple codes.
Example
import type { UsageOutcomes } from "@cosyte/hl7";
const outcomes: UsageOutcomes = { whenTrue: "RE", whenFalse: "X" };
Properties
whenFalse
readonlywhenFalse:SimpleUsageCode
The usage that applies when the condition is resolved false.
whenTrue
readonlywhenTrue:SimpleUsageCode
The usage that applies when the condition is resolved true.
UsageResolution
A caller-supplied resolution of one declared-conditional rule. The engine evaluates no condition predicate and reads nothing from the message to decide an outcome: the caller decides, and passes an ordered list of these alongside the message and the profile.
The segment name plus the 1-indexed field (absent when the target is a
segment rule) is the LOCUS the resolution applies to. It applies to the RULE
at that locus, hence to every occurrence of the segment and every repetition
of the field: there is no per-occurrence resolution.
A resolution whose locus matches no declared rule, matches more than one, or matches a rule whose usage is not a declared conditional is a profile defect (FINDING_CODES.PROFILE_MALFORMED), never a silent no-op.
Example
import type { UsageResolution } from "@cosyte/hl7";
// "PID-8's condition is true for this message."
const resolution: UsageResolution = { segment: "PID", field: 8, outcome: true };
Properties
field?
readonlyoptionalfield?:number
1-indexed field position when the target is a field rule; absent for a segment rule.
outcome
readonlyoutcome:boolean
true applies the token's true outcome, false its false outcome.
segment
readonlysegment:string
Segment name of the target rule (e.g. "PID").
Visit
PV1-derived visit view (HELPERS-03). msg.visit is undefined when no PV1
segment exists; this interface describes the shape when present. D-24a:
doctor fields use XCN (not flat strings). Date/time fields are the
fidelity TS (precision + timezone preserved).
Example
import type { Visit } from "@cosyte/hl7";
const v: Visit = {
patientClass: "I",
location: { pointOfCare: "ICU", room: "101" },
visitNumber: "VISIT001",
};
console.log(v.attendingDoctor?.familyName);
console.log(v.admitDateTime?.raw);
Properties
admitDateTime?
readonlyoptionaladmitDateTime?:DtmParts
PV1-44 admit date/time as the fidelity TS.
attendingDoctor?
readonlyoptionalattendingDoctor?:XCN
PV1-7 attending doctor (D-24a XCN).
dischargeDateTime?
readonlyoptionaldischargeDateTime?:DtmParts
PV1-45 discharge date/time as the fidelity TS.
location?
readonlyoptionallocation?:PL
PV1-3 assigned patient location (ward / room / bed) as PL.
patientClass?
readonlyoptionalpatientClass?:string
PV1-2 patient class ("I"=inpatient, "O"=outpatient, "E"=ER, ...).
referringDoctor?
readonlyoptionalreferringDoctor?:XCN
PV1-8 referring doctor (D-24a XCN).
visitNumber?
readonlyoptionalvisitNumber?:string
PV1-19 visit number.
VxuImmunization
Typed RXA (administered dose) content for buildVxu.
Example
import type { VxuImmunization } from "@cosyte/hl7";
const dose: VxuImmunization = {
orderControl: "RE",
administeredDateTime: "20260801",
vaccineCode: { identifier: "115", text: "Tdap", nameOfCodingSystem: "CVX" },
doseAmount: "0.5",
doseUnits: { identifier: "mL", nameOfCodingSystem: "UCUM" },
completionStatus: "CP",
actionCode: "A",
};
Properties
actionCode?
readonlyoptionalactionCode?:string
RXA-21 Action Code (A add, D delete, U update). Never defaulted.
administeredDateTime?
readonlyoptionaladministeredDateTime?:string|DtmParts
RXA-3 Date/Time Start of Administration.
administrationSubIdCounter?
readonlyoptionaladministrationSubIdCounter?:string
RXA-2 Administration Sub-ID Counter.
completionStatus?
readonlyoptionalcompletionStatus?:string
RXA-20 Completion Status (CP, RE, NA, PA).
doseAmount?
readonlyoptionaldoseAmount?:string|number|NM
RXA-6 Administered Amount (emitted verbatim: the caller owns its precision).
doseUnits?
readonlyoptionaldoseUnits?:CWE
RXA-7 Administered Units.
expirationDate?
readonlyoptionalexpirationDate?:string|DtmParts
RXA-16 Substance Expiration Date.
giveSubIdCounter?
readonlyoptionalgiveSubIdCounter?:string
RXA-1 Give Sub-ID Counter.
informationSource?
readonlyoptionalinformationSource?:CWE
RXA-9 Administration Notes: the immunization information source (HL7 Table NIP001).
lotNumber?
readonlyoptionallotNumber?:string
RXA-15 Substance Lot Number.
manufacturer?
readonlyoptionalmanufacturer?:CWE
RXA-17 Substance Manufacturer Name (MVX, HL7 Table 0227).
observations?
readonlyoptionalobservations?: readonlyOruObservation[]
OBX children of this dose (eligibility, funding source, …).
orderControl?
readonlyoptionalorderControl?:string
ORC-1 Order Control of the ORC that opens this dose's order group. An ORC
is emitted only when this is supplied; the read side surfaces it as
orderControl.
refusalReason?
readonlyoptionalrefusalReason?:CWE
RXA-18 Substance/Treatment Refusal Reason: a refused dose carries this.
routes?
readonlyoptionalroutes?: readonlyVxuRoute[]
RXR children of this dose (route / administration site).
vaccineCode?
readonlyoptionalvaccineCode?:CWE
RXA-5 Administered Code (CVX, HL7 Table 0292).
VxuRoute
Typed RXR (route/site) content grouped under one VxuImmunization.
Example
import type { VxuRoute } from "@cosyte/hl7";
const route: VxuRoute = {
route: { identifier: "IM", text: "Intramuscular" },
site: { identifier: "LD", text: "Left deltoid" },
};
Properties
route?
readonlyoptionalroute?:CWE
RXR-1 Route (HL7 Table 0162).
site?
readonlyoptionalsite?:CWE
RXR-2 Administration Site (HL7 Table 0163).
XAD
HL7 v2 Extended Address (XAD): structured postal address per HL7 Chapter 2. All 12 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- street: street address (house number + street name).
- otherDesignation: apartment number, suite, floor, etc.
- city
- stateOrProvince
- zipOrPostalCode
- country (ISO-3166 3-letter, e.g. "USA", "CAN")
- addressType (H=Home, B=Business, M=Mailing, O=Office, P=Permanent, ...)
- otherGeographicDesignation
- countyParishCode
- censusTract
- addressRepresentationCode
- addressValidityRange
Example
import type { XAD } from "@cosyte/hl7";
const addr: XAD = { street: "123 Main St", city: "Boston", stateOrProvince: "MA" };
Properties
addressRepresentationCode?
readonlyoptionaladdressRepresentationCode?:string
addressType?
readonlyoptionaladdressType?:string
addressValidityRange?
readonlyoptionaladdressValidityRange?:string
censusTract?
readonlyoptionalcensusTract?:string
city?
readonlyoptionalcity?:string
country?
readonlyoptionalcountry?:string
countyParishCode?
readonlyoptionalcountyParishCode?:string
otherDesignation?
readonlyoptionalotherDesignation?:string
otherGeographicDesignation?
readonlyoptionalotherGeographicDesignation?:string
stateOrProvince?
readonlyoptionalstateOrProvince?:string
street?
readonlyoptionalstreet?:string
zipOrPostalCode?
readonlyoptionalzipOrPostalCode?:string
XCN
HL7 v2 Extended Composite ID Number and Name for Persons (XCN): per HL7 Chapter 2.A.88. All 13 v1 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- idNumber: e.g. employee ID, NPI digits, DEA number (CX-1 analogue)
- familyName (XPN-1)
- givenName (XPN-2)
- secondName: second and further given names (XPN-3)
- suffix: Jr., III, etc. (XPN-4)
- prefix: Dr., Mrs., etc. (XPN-5)
- degree: MD, PhD, etc. (XPN-6)
- sourceTable
- assigningAuthority: nested HD (CX-4 analogue)
- nameTypeCode: L=Legal, M=Maiden, N=Nickname, ... (XPN-7)
- identifierCheckDigit
- checkDigitScheme: ISO 7064, M10, M11, NPI
- identifierTypeCode: "NPI", "DN" (DEA number), ... (CX-5 analogue)
Example
import type { XCN } from "@cosyte/hl7";
const orderingProvider: XCN = {
idNumber: "1234567890",
familyName: "Smith",
givenName: "Jane",
identifierTypeCode: "NPI",
};
Properties
assigningAuthority?
readonlyoptionalassigningAuthority?:HD
checkDigitScheme?
readonlyoptionalcheckDigitScheme?:string
degree?
readonlyoptionaldegree?:string
familyName?
readonlyoptionalfamilyName?:string
givenName?
readonlyoptionalgivenName?:string
identifierCheckDigit?
readonlyoptionalidentifierCheckDigit?:string
identifierTypeCode?
readonlyoptionalidentifierTypeCode?:string
idNumber?
readonlyoptionalidNumber?:string
nameTypeCode?
readonlyoptionalnameTypeCode?:string
prefix?
readonlyoptionalprefix?:string
secondName?
readonlyoptionalsecondName?:string
sourceTable?
readonlyoptionalsourceTable?:string
suffix?
readonlyoptionalsuffix?:string
XPN
HL7 v2 Extended Person Name (XPN): structured name per HL7 Chapter 2. All 14 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- familyName
- givenName
- secondName (or "second and further given names")
- suffix (e.g. Jr., III)
- prefix (e.g. Dr., Mrs.)
- degree (e.g. MD, PhD)
- nameTypeCode (L=Legal, M=Maiden, N=Nickname, S=Coded Pseudo-Name, ...)
- nameRepresentationCode
- nameContext (flattened to string in v1: CWE nesting is out of scope)
- nameValidityRange
- nameAssemblyOrder (F=family first, G=given first)
- effectiveDate (raw HL7 TS string: caller may parse via parseDtm)
- expirationDate
- professionalSuffix
Example
import type { XPN } from "@cosyte/hl7";
const name: XPN = { familyName: "Smith", givenName: "Jane", prefix: "Mrs." };
Properties
degree?
readonlyoptionaldegree?:string
effectiveDate?
readonlyoptionaleffectiveDate?:string
expirationDate?
readonlyoptionalexpirationDate?:string
familyName?
readonlyoptionalfamilyName?:string
givenName?
readonlyoptionalgivenName?:string
nameAssemblyOrder?
readonlyoptionalnameAssemblyOrder?:string
nameContext?
readonlyoptionalnameContext?:string
nameRepresentationCode?
readonlyoptionalnameRepresentationCode?:string
nameTypeCode?
readonlyoptionalnameTypeCode?:string
nameValidityRange?
readonlyoptionalnameValidityRange?:string
prefix?
readonlyoptionalprefix?:string
professionalSuffix?
readonlyoptionalprofessionalSuffix?:string
secondName?
readonlyoptionalsecondName?:string
suffix?
readonlyoptionalsuffix?:string
XTN
HL7 v2 Extended Telecommunication Number (XTN): structured telecom per HL7 Chapter 2. All 12 v1 components are optional. Fields are OMITTED when the underlying component is absent (exactOptionalPropertyTypes).
Component positions (HL7 1-indexed; this interface is 0-indexed by key):
- telephoneNumber: formatted or unformatted phone number
- telecommunicationUseCode: PRN=Primary Residence, WPN=Work, NET=Internet, ORN=Other Residence, BPN=Beeper, VHN=Vacation Home, ASN=Answering Service, EMR=Emergency, ...
- telecommunicationEquipmentType: PH=Phone, FX=Fax, MD=Modem, CP=Cellular Phone, BP=Beeper, Internet, X.400, TDD, TTY
- emailAddress
- countryCode (e.g. "+1")
- areaCityCode
- localNumber
- extension
- anyText: free-text note
- extensionPrefix (e.g. "x")
- speedDialCode
- unformattedTelephoneNumber
Example
import type { XTN } from "@cosyte/hl7";
const phone: XTN = {
telephoneNumber: "(555) 555-1234",
telecommunicationUseCode: "WPN",
telecommunicationEquipmentType: "PH",
};
Properties
anyText?
readonlyoptionalanyText?:string
areaCityCode?
readonlyoptionalareaCityCode?:string
countryCode?
readonlyoptionalcountryCode?:string
emailAddress?
readonlyoptionalemailAddress?:string
extension?
readonlyoptionalextension?:string
extensionPrefix?
readonlyoptionalextensionPrefix?:string
localNumber?
readonlyoptionallocalNumber?:string
speedDialCode?
readonlyoptionalspeedDialCode?:string
telecommunicationEquipmentType?
readonlyoptionaltelecommunicationEquipmentType?:string
telecommunicationUseCode?
readonlyoptionaltelecommunicationUseCode?:string
telephoneNumber?
readonlyoptionaltelephoneNumber?:string
unformattedTelephoneNumber?
readonlyoptionalunformattedTelephoneNumber?:string
Type Aliases
AckCode
Acknowledgment code union (HL7 Table 0008). Narrow on this to know whether a
disposition is accept (AA/CA), error (AE/CE), or reject (AR/CR).
AckCondition
AckCondition = typeof
ACK_CONDITIONS[keyof typeofACK_CONDITIONS]
Accept/application acknowledgment condition union (HL7 Table 0155).
AckMode
AckMode =
"original"|"enhanced"
The two HL7 acknowledgment modes. original = both MSH-15 and MSH-16 are absent/null; enhanced = either is present (HL7 v2 Chapter 2 §2.9).
AllergySource
AllergySource =
"AL1"|"IAM"
The segment an Allergy entry was read from: "AL1" (Patient Allergy
Information) or "IAM" (Patient Adverse Reaction Information, the segment an
ADT^A60 carries in place of AL1).
Example
import type { AllergySource } from "@cosyte/hl7";
const source: AllergySource = "IAM";
BatchEnvelopeName
BatchEnvelopeName =
"FHS"|"BHS"|"BTS"|"FTS"
The four HL7 batch-protocol envelope segment names (Ch. 2 §2.10.3).
Example
import type { BatchEnvelopeName } from "@cosyte/hl7";
const trailer: BatchEnvelopeName = "BTS";
BatchMessageEntry
BatchMessageEntry<
M> = {message:M;ok:true;position:Hl7Position;raw:string; } | {error:Hl7ParseError;ok:false;position:Hl7Position;raw:string; }
One message extracted from a batch stream. Discriminated on ok: a
successful parse carries the Hl7Message (whose own .warnings hold any
per-message Tier-2 deviations); a message that hit one of the four Tier-3
fatal conditions carries the Hl7ParseError instead: isolated, so the
rest of the batch still yields. raw is the message's verbatim source
(re-parseable by parseHL7); position is the message's MSH (or,
for pre-MSH stray content, its first) segment index in the stream.
M is the parsed-message type each ok entry carries. It is Hl7Message
unless the split was given a statically known profile, in which case it is
that profile's narrowed view and entry.message.part("ZDP")?.get(name) is
checked against the names the profile declares for ZDP.
Type Parameters
M
M extends Hl7Message = Hl7Message
the message type an ok entry carries.
Example
import { splitBatch } from "@cosyte/hl7";
for (const entry of splitBatch(raw).messages) {
if (entry.ok) handle(entry.message);
else quarantine(entry.raw, entry.error.code);
}
CharsetTreatment
CharsetTreatment =
"decode"|"verbatim"
How the parser treats a resolved MSH-18 character set.
decode: decode the byte stream to text with CharsetResolution.decoder.verbatim: read the raw bytes as a 1:1latin1mapping; do not decode (byte-recoverable for single-byte content: see the module header).
CompositeKind
CompositeKind =
"XPN"|"XAD"|"CX"|"CWE"|"CE"|"XTN"|"PL"|"TS"|"NM"|"HD"|"XCN"
The 11 typed composite kinds this module can encode. Mirrors the read-side
composite set (XPN, XAD, CX, CWE, CE, XTN, PL, TS, NM,
HD, XCN).
ConditionPredicate
ConditionPredicate =
PresencePredicate|ComparisonPredicate|ConnectedPredicate
A condition predicate: the computable test a conditionally-used element's usage is decided by, expressed in the published condition-predicate language as a presence statement, a comparison statement, or two of those joined by a connector.
A rule declares one on its condition, and only a rule whose usage is
conditional (C, CE, or a declared conditional such as C(RE/X)) may
carry one: there is nothing for a predicate to decide anywhere else, so a
predicate on any other rule is PROFILE_MALFORMED rather than a silent
no-op. A rule may not declare a predicate AND take a caller-supplied
resolution: that too is PROFILE_MALFORMED, so neither source is silently
preferred over the other.
The predicate reads the message only. It never makes a network call and never consults a bundled code set: every value it compares against is one the profile author wrote down.
Declare exactly one of presence, verb and connector, and OMIT the
others. A key set to undefined still declares it, so a statement
assembled from an options bag has to leave out the keys it does not use
rather than pass them through empty. A statement declaring two of them is
PROFILE_MALFORMED: the engine will not guess which question was meant,
because deciding a conditional element's usage from the wrong question turns
a required element into a permitted absence.
Example
import type { ConditionPredicate } from "@cosyte/hl7";
const simple: ConditionPredicate = {
location: { segment: "PID", field: 7 },
presence: "is valued",
};
const complex: ConditionPredicate = {
connector: "OR",
left: simple,
right: { location: { segment: "PID", field: 8 }, verb: "is", values: ["M", "F"] },
};
DeclaredConditionalUsage
DeclaredConditionalUsage =
`C(${SimpleUsageCode}/${SimpleUsageCode})`
A declared conditional usage token: C(t/f), where t is the usage that
applies when the condition is true and f the usage that applies when it is
false. Both outcomes are SimpleUsageCodes; the token is uppercase,
carries no whitespace, and does not nest (C(C(R/X)/O) is not one).
A rule declared C(R/X) is Required when the condition holds and
Not-permitted when it does not. Two sources can decide that, and a rule uses
exactly one of them: the ConditionPredicate on its condition, which
the engine evaluates against the message, or a caller-supplied
UsageResolution. Declaring both at one locus is
FINDING_CODES.PROFILE_MALFORMED, never a silent preference.
With neither supplied, the rule is evaluated exactly as a C rule with no
predicate is: presence not evaluated, every other constraint applied as
usual.
Example
import type { DeclaredConditionalUsage } from "@cosyte/hl7";
const usage: DeclaredConditionalUsage = "C(RE/X)";
DftPatient
DftPatient =
AdtPatient
Typed PID content for buildDft: the same shape every family shares.
Example
import type { DftPatient } from "@cosyte/hl7";
const patient: DftPatient = {
identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
accountNumber: { idNumber: "ACCT-1", identifierTypeCode: "AN" },
};
DtmPrecision
DtmPrecision =
"year"|"month"|"day"|"hour"|"minute"|"second"|"fraction"
Stated precision of a parsed HL7 DTM value: the number of populated
characters (excluding the timezone offset) determines which level applies.
A value's precision is preserved verbatim: |1970| is "year", never
silently promoted to a full timestamp.
Example
import { parseDtm } from "@cosyte/hl7";
console.log(parseDtm("1970").precision); // "year"
console.log(parseDtm("198807050000").precision); // "minute"
ErrSeverity
ErrSeverity = typeof
ERR_SEVERITIES[keyof typeofERR_SEVERITIES]
Error-severity union (HL7 Table 0516, ERR-4).
FatalCode
FatalCode = typeof
FATAL_CODES[keyof typeofFATAL_CODES]
Discriminant type for Hl7ParseError.code. Narrowing a caught error by
this code lets consumers write exhaustive switch blocks (enabled by the
switch-exhaustiveness-check lint rule) and guarantees a typo-free
comparison against the FATAL_CODES registry.
Example
import type { FatalCode } from "@cosyte/hl7";
function describe(code: FatalCode): string {
switch (code) {
case "EMPTY_INPUT":
return "input was empty";
case "NO_MSH_SEGMENT":
return "missing MSH";
case "MSH_TOO_SHORT":
return "MSH truncated";
case "INVALID_ENCODING_CHARACTERS":
return "bad MSH-1/MSH-2";
}
}
FindingCode
FindingCode = typeof
FINDING_CODES[keyof typeofFINDING_CODES]
Discriminant union of every ConformanceFinding code. Enables
exhaustive switch narrowing (the switch-exhaustiveness-check lint rule).
FindingSeverity
FindingSeverity =
"error"|"warning"|"info"
Severity of a ConformanceFinding. error is a constraint violation;
warning / info are author-downgraded advisories (via a rule's
severity). FINDING_CODES.PROFILE_MALFORMED is always error.
Hl7StreamSource
Hl7StreamSource =
AsyncIterable<string|Buffer|Uint8Array> |Iterable<string|Buffer|Uint8Array>
A chunked source parseStream can consume: a Node Readable, any
async-iterable, or any plain iterable of chunks. Chunks are string
(text stream) or Buffer/Uint8Array (binary stream). A real Node stream in
binary mode yields Buffers; in text mode it yields strings: a source is
expected to be homogeneous (all-text or all-binary), which every real
Readable is. A binary chunk is decoded 1:1 via latin1 (a lossless
byte↔codepoint mapping) so each message's own MSH-18 charset resolution runs
on its original bytes, exactly as splitBatch does.
Example
import { createReadStream } from "node:fs";
import { parseStream } from "@cosyte/hl7";
const src: Hl7StreamSource = createReadStream("feed.hl7");
for await (const entry of parseStream(src)) {
if (entry.ok) handle(entry.message);
else quarantine(entry.raw, entry.error.code);
}
IdentityEventKind
IdentityEventKind =
"merge"|"move"|"change"|"link"|"unlink"|"add"|"update"
Classification of a recognized identity trigger event.
merge: A18 / A34 / A35 / A36 / A39 / A40 / A41 / A42 (MRG expected). Two patient records become one; the prior record is retired.move: A43 / A44 / A45 (MRG expected). Information moves between identifier lists, accounts or visits that both go on existing.change: A47 / A49 / A50 / A51, the change-identifier family (MRG expected). One identifier is replaced by another on the SAME record: the direction is still MRG (prior) to PID (surviving), but no records are being conflated, so a consumer must not treat it as a patient merge.link/unlink: A24 / A37 (two PID groups, no MRG)add/update: A28 / A31 (person add/update, no MRG)
Example
import type { IdentityEventKind } from "@cosyte/hl7";
const kind: IdentityEventKind = "merge";
IdentityRole
IdentityRole =
"surviving"|"prior"|"linked"|"subject"
Role of one party in an identity event. surviving / subject / linked
parties are ONLY ever sourced from PID (+ PV1); prior parties are ONLY
ever sourced from MRG. That is the role-labelling invariant.
Example
import type { IdentityRole } from "@cosyte/hl7";
const role: IdentityRole = "surviving";
ImmunizationRecordOrigin
ImmunizationRecordOrigin =
"administered"|"historical"
Whether an Immunization records a dose that was administered by the
reporting system or is historical information sourced from elsewhere,
derived conservatively from RXA-9.1 against HL7 Table NIP001 (Immunization
Information Source, CDC v2.5.1 Immunization Messaging IG):
"administered": RXA-9.1 is exactly"00"(New immunization record)."historical": RXA-9.1 is"01".."08"(any "Historical information …" source).
Fail-safe: this is OMITTED (never guessed) when RXA-9 is absent or carries
a code outside the NIP001 administered/historical set. The raw RXA-9 claim is
always preserved verbatim on Immunization.informationSource, so a consumer
can inspect the original code even when recordOrigin is undefined. The
distinction matters because an IIS de-duplicates a historical report
differently from a dose it believes was administered: guessing corrupts
the registry.
ImmunizationStatusBasis
ImmunizationStatusBasis = {
field:"RXA-20";map:ImmunizationStatusMap;table:ImmunizationStatusTable&object; } | {field:"RXA-21";table:ImmunizationStatusTable&object; } | {codeSet:ImmunizationStatusCodeSet;field:"RXA-5"; }
What decided an ImmunizationAdministrationStatus: the RXA field the
deciding rule read, and the table, map or code set it read that field
against. Discriminated on field.
"RXA-21": the action code decided ("delete-requested", or"undetermined"for an RXA-21 outside Table 0323)."RXA-5": the vaccine code decided ("no-vaccine-administered", or"undetermined"for a CVX998contradicted by the other triplet)."RXA-20": the completion status decided, by the Table 0322 map.
Union Members
Type Literal
{ field: "RXA-20"; map: ImmunizationStatusMap; table: ImmunizationStatusTable & object; }
field
readonlyfield:"RXA-20"
RXA-20 Completion Status decided.
map
readonlymap:ImmunizationStatusMap
The Table 0322 to Event Status map the classification followed.
table
readonlytable:ImmunizationStatusTable&object
HL7 Table 0322, the table RXA-20 was read against.
Type Declaration
name
readonlyname:"HL7 Table 0322"
Type Literal
{ field: "RXA-21"; table: ImmunizationStatusTable & object; }
field
readonlyfield:"RXA-21"
RXA-21 Action Code decided.
table
readonlytable:ImmunizationStatusTable&object
HL7 Table 0323, the table RXA-21 was read against.
Type Declaration
name
readonlyname:"HL7 Table 0323"
Type Literal
{ codeSet: ImmunizationStatusCodeSet; field: "RXA-5"; }
codeSet
readonlycodeSet:ImmunizationStatusCodeSet
The CDC CVX code set, for code 998.
field
readonlyfield:"RXA-5"
RXA-5 Administered Code decided.
Example
import type { ImmunizationStatusBasis } from "@cosyte/hl7";
const basis: ImmunizationStatusBasis = {
field: "RXA-21",
table: { name: "HL7 Table 0323", version: "3.0.0" },
};
ImmunizationStatusClass
ImmunizationStatusClass =
"completed"|"not-done"|"no-vaccine-administered"|"delete-requested"|"undetermined"
What an immunization record's administration status classifies to.
"completed" and "not-done" are the target codes of HL7's published
Table 0322 to Event Status map (RXA-20 CP and PA are "completed",
RE and NA are "not-done").
"no-vaccine-administered": RXA-5 carries CDC CVX code998("no vaccine administered"), whatever RXA-20 says."delete-requested": RXA-21 is exactlyD. The record asks the receiver to delete an administration sent earlier; it is not a dose."undetermined": this library's fail-safe, not a map target. It covers an absent, empty or""(null) RXA-20, any RXA-20 or RXA-21 that is not exactly one code of its table (a lowercase code, whitespace around it, a code written as an escape sequence, a VT or FS byte inside the field, more than one repetition, component or subcomponent), and an RXA-5 that codes CVX998in one triplet and a different identifier in the other.
Only "completed" counts as a dose given. An "undetermined" record is
never a dose by default: read the raw codes and decide.
Example
import type { ImmunizationStatusClass } from "@cosyte/hl7";
const notADose: readonly ImmunizationStatusClass[] = [
"not-done",
"no-vaccine-administered",
"delete-requested",
"undetermined",
];
MdmObservation
MdmObservation =
OruObservation
Typed OBX content for buildMdm: one line of the transcribed body.
Example
import type { MdmObservation } from "@cosyte/hl7";
const line: MdmObservation = { setId: "1", valueType: "TX", value: "Discharge summary text." };
MdmPatient
MdmPatient =
AdtPatient
Typed PID content for buildMdm: the same shape every family shares.
Example
import type { MdmPatient } from "@cosyte/hl7";
const patient: MdmPatient = {
identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
name: { familyName: "Test", givenName: "Ann" },
};
MedicationContext
MedicationContext =
"order"|"encoded"|"dispense"|"administration"
Which pharmacy/treatment segment a Medication was extracted from: the
clinical "phase" of the medication. Each value maps 1:1 to one RX* parent
segment (Ch. 4A):
"order": RXO (Pharmacy/Treatment Order): the originally requested give code/amount/dosage-form, before pharmacy encoding."encoded": RXE (Encoded Order): the pharmacy-encoded give code/amount, plus the give strength (RXE-25/26): the only context that carries a separate strength."dispense": RXD (Dispense): what was actually dispensed."administration": RXA (Administration): what was actually given to the patient.
The context is preserved verbatim and never collapsed: an RDE order that
carries both an RXO request and an RXE encoded line surfaces as TWO
Medication entries with distinct contexts: the helper never reconciles
one against the other.
Observation
Observation =
ObservationBase& {value:number|undefined;valueType:"NM"; } | {value:SN|undefined;valueType:"SN"; } | {value:TS|undefined;valueType:"TS"|"DT"; } | {value:CWE|CE|undefined;valueType:"CWE"|"CE"; } | {value:string|undefined;valueType:string; }
One OBX segment as a typed Observation. value is discriminated by
valueType (OBX-2) per D-13:
"NM"→number | undefined"SN"→SN | undefined(structured numeric: comparator / range / ratio)"TS" | "DT"→TS | undefined(fidelity parts preserved)"CWE" | "CE"→CWE | CE | undefined(full composite per D-14)- other (
"ST","TX","FT","ID","IS","NA", unknown) →string | undefined(decoded, D-23)
D-22: value is undefined when OBX-5 is empty OR malformed for its
declared type (never throws, never NaN). A TS/DT value is always the
TS structure; check its .valid flag rather than expecting a Date.
Example
import type { Observation } from "@cosyte/hl7";
const glucose: Observation = {
setId: "1",
identifier: { identifier: "GLU", text: "Glucose" },
valueType: "NM",
value: 120,
units: { identifier: "mg/dL" },
referenceRange: "80-110",
abnormalFlags: "H",
status: "F",
resultStatus: {
classification: "final",
code: "F",
table: { name: "HL7 Table 0085", version: "3.0.0" },
map: {
url: "http://hl7.org/fhir/uv/v2mappings/ConceptMap/table-hl70085-to-observation-status",
version: "1.0.0",
},
},
};
OnWarningCallback
OnWarningCallback = (
warning) =>void
Callback invoked inline each time the parser emits a Tier-2 warning.
Always fires BEFORE the warning is appended to Hl7Message.warnings so
consumers observe warnings in the same order the parser discovered them.
Parameters
warning
Returns
void
Example
import { parseHL7, type OnWarningCallback } from "@cosyte/hl7";
const onWarning: OnWarningCallback = (w) => {
console.warn(w.code, w.message);
};
parseHL7(raw, { onWarning });
OrmObservation
OrmObservation =
OruObservation
Typed OBX content for buildOrm: an observation carried with the order.
Example
import type { OrmObservation } from "@cosyte/hl7";
const note: OrmObservation = { setId: "1", valueType: "ST", value: "Fasting" };
OrmPatient
OrmPatient =
AdtPatient
Typed PID content for buildOrm: the same shape every family shares.
Example
import type { OrmPatient } from "@cosyte/hl7";
const patient: OrmPatient = { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } };
OruPatient
OruPatient =
AdtPatient
Typed PID content for buildOru. Reuses AdtPatient: the patient-identification shape is identical across message families.
OverlayKey
OverlayKey = keyof
OverlayStructures
Every string Hl7Message.is narrows on: one key per (message code, trigger event) pair the structure registry recognizes, plus a code-only key for each registry entry that matches on message code alone.
Any other string is a documented false, never a throw: the three-component
MSH-9 form ("ADT^A01^ADT_A01") is not a key, because is compares the pair
the parser already extracted rather than parsing its own argument, and a
caller who wants a raw comparison has msg.meta.type.
Example
import type { OverlayKey } from "@cosyte/hl7";
const key: OverlayKey = "ORU^R01";
// const nope: OverlayKey = "ADT^A01^ADT_A01"; // not a key: does not compile
OverlayMessageCode
OverlayMessageCode<
K> =Kextends`${infer C}^${string}`?C:K
The MSH-9.1 message code one overlay key names, as a literal type.
Type Parameters
K
K extends OverlayKey
Example
import type { OverlayMessageCode } from "@cosyte/hl7";
type Code = OverlayMessageCode<"ORU^R01">; // "ORU"
OverlayRequiredSegment
OverlayRequiredSegment<
K> =OverlayStructures[K][number]
The segment names one overlay key's published structure marks required: the
names part and parts accept on a message narrowed to that key.
Type Parameters
K
K extends OverlayKey
Example
import type { OverlayRequiredSegment } from "@cosyte/hl7";
type A01Segments = OverlayRequiredSegment<"ADT^A01">; // "EVN" | "MSH" | "PID" | "PV1"
OverlayTriggerEvent
OverlayTriggerEvent<
K> =Kextends`${string}^${infer E}`?E:never
The MSH-9.2 trigger event one overlay key names, as a literal type, or
never for a code-only key. A code-only key ("ACK") constrains MSH-9.2 to
nothing at all, since it carries the acknowledged message's trigger event, so
a message narrowed to one keeps the base triggerEvent?: string.
Type Parameters
K
K extends OverlayKey
Example
import type { OverlayTriggerEvent } from "@cosyte/hl7";
type Event = OverlayTriggerEvent<"ORU^R01">; // "R01"
type None = OverlayTriggerEvent<"ACK">; // never: MSH-9.2 is unconstrained
PredicateConnector
PredicateConnector =
"AND"|"OR"|"XOR"
The connectors that join two condition-predicate statements, with the
published semantics: AND is true only when both operands are true, OR
when at least one is, XOR when exactly one is.
Example
import type { PredicateConnector } from "@cosyte/hl7";
const connector: PredicateConnector = "AND";
PredicatePresence
PredicatePresence =
"is valued"|"is not valued"
The two proposition statements that ask whether an element carries content,
from the published condition-predicate language (IF CWE.1 (Identifier) is valued).
A presence statement is always determinate: an element the message does not carry is simply not valued, which is an answer rather than a gap. That is what separates it from a comparison, which needs content to compare and has none when the element is absent.
Example
import type { PredicatePresence } from "@cosyte/hl7";
const statement: PredicatePresence = "is valued";
PredicateVerb
PredicateVerb =
"is"|"is not"|"contains"|"does not contain"|"matches"|"does not match"
The closed set of verbs a comparison statement may use, from the published condition-predicate language's verb table. Each pairs the element's content against the statement's value list:
is/is not: the value equals (does not equal) a listed value, whole and case-sensitive.contains/does not contain: the value carries (does not carry) a listed value as a substring, case-sensitive.matches/does not match: the value matches (does not match) a listed value read as a regular expression, anchored over the whole value.
A negative verb negates the COMPARISON, not the statement. Each pair is an exact complement value by value, and both halves are quantified the same way: existentially, over every repetition of the field and every occurrence of the segment the location names (see ComparisonPredicate). Three consequences, and the second is the one that surprises:
- Where the message carries exactly ONE value at that location, the pair behaves as an exact complement: one of the two decides true and the other false.
- Where the location REPEATS, both halves can decide true at once, because
both are existential.
PID-3.1 is 'MRN1'asks whether SOME identifier isMRN1, andPID-3.1 is not 'MRN1'asks whether SOME identifier is notMRN1; forMRN1~MRN2the answer to both is yes. So a negative verb is NOT a way to ask whether no repetition is a listed value: the language carries no negation connector, so that question cannot be written at all, andAL1-3.1 is not 'PENICILLIN'decides true for a patient who has that allergy and one other. - Where the message carries no value at that location, neither half decides. The comparison is unevaluatable whichever verb it uses.
Example
import type { PredicateVerb } from "@cosyte/hl7";
const verb: PredicateVerb = "contains";
ProfileFieldName
ProfileFieldName<
P,S> =Pextendsobject?DeclaredFieldName<C,S> :string
The field names profile P declares for segment type S, as a closed union
of string literals, or string when the compiler cannot know them.
string is the answer for every case where a declaration is not statically
visible, and there are more of those than there are narrowing ones: a value
typed as the general Profile interface, a segment type the profile
does not declare, a segment declared with an empty field map, and a
declaration map typed with an index signature. Narrowing is per segment type,
never a flat union of every name the profile declares.
Type Parameters
P
P
S
S extends string
Example
import { defineProfile, type ProfileFieldName } from "@cosyte/hl7";
const epic = defineProfile({
name: "epic",
customSegments: { ZDP: { fields: { departmentCode: 3, departmentName: 4 } } },
});
type Zdp = ProfileFieldName<typeof epic, "ZDP">; // "departmentCode" | "departmentName"
type Zzz = ProfileFieldName<typeof epic, "ZZZ">; // string: undeclared segment type
ProfileLevel
ProfileLevel =
"standard"|"constrainable"|"implementable"
The three profile levels the HL7 conformance methodology defines, in the order it defines them: from the least constrained to the most.
standard: the base standard definitions and constraints as-is. The overall structure is established, but the full declaration of requirements has yet to be specified, so considerable openness still exists.constrainable: further constrains a parent profile, but not all element attributes are fully constrained: optionality is still in place somewhere.implementable: defines all elements such that all optionality and openness have been removed. Every element is either supported (RorRE) or it is not (X), and a conditional usage's true and false outcomes are drawn from those same three codes.
Why a consumer cares, and it is the only reason this exists. The methodology draws the completeness line here: a complete assessment of an interface declaring conformance to an implementable profile can be determined, while for standard-level and constrainable-level profiles not all aspects can be. So an empty ConformanceResult.findings means two different things at two different levels, and ConformanceResult.level is what tells them apart.
The level is the author's CLAIM, checked for internal consistency. hl7
verifies that a profile claiming implementable has actually removed the
optionality the level asserts is gone, and refuses the claim as
FINDING_CODES.PROFILE_MALFORMED when it has not. Nothing here is an
external or accredited attestation, and zero findings at implementable
level is still not one: see ConformanceResult.
Example
import type { ProfileLevel } from "@cosyte/hl7";
const level: ProfileLevel = "implementable";
RepeatPatternKind
RepeatPatternKind =
"parametric"|"named"|"unknown"
How an order/medication RepeatPattern code (HL7 Table 0335) is classified,
provenance only, never used to resolve a schedule. The code
is always authoritative and surfaced verbatim; this flag only tells a
consumer what kind of pattern it is looking at so it can decide whether a
load-bearing integer is present.
"parametric": aQ<integer><unit>template (Q6H,Q30M,Q2D,Q1W,Q3J5) whose integer is load-bearing:Q6H(every 6 hours) is a different dose count fromQ8H. The parsed integer + unit ride on RepeatPattern.interval."named": a recognized fixed Table-0335 mnemonic scheduled at institution-specified times (BID,TID,QID,QOD,QHS,QAM,QPM,QSHIFT,PRN,AC,PC,HS,C). No numeric interval."unknown": anything else (a local code, free text, an unrecognized mnemonic). Surfaced verbatim, never mapped to a frequency.
ResultStatusClass
ResultStatusClass =
"final"|"corrected"|"amended"|"preliminary"|"entered-in-error"|"cancelled"|"registered"|"partial"|"undetermined"
What a result status classifies to. The first eight values are the target codes of HL7's published v2-to-FHIR status maps (Table 0085 to Observation Status for OBX-11, Table 0123 to Diagnostic Report Status for OBR-25).
"undetermined" is this library's fail-safe, not a map target. It covers
every code HL7's map leaves unmapped (OBX-11 B, I, N, O, R, S,
V, U; OBR-25 A, Y, Z, M, N), an absent, empty or "" (null)
field, and any field that is not exactly one code of its table: a lowercase
letter, a code with whitespace around it, a code written as an escape
sequence, a code outside the table, or a field with more than one
repetition, component or subcomponent. An "undetermined" value is never a
current result by default: read the raw code and decide.
Example
import type { ResultStatusClass } from "@cosyte/hl7";
const retracted: readonly ResultStatusClass[] = ["entered-in-error", "cancelled"];
SchemaEmitTarget
SchemaEmitTarget = typeof
SCHEMA_EMIT_TARGETS[number]
One of the emission targets this library supports.
Example
import { emitMessageSchema, type SchemaEmitTarget } from "@cosyte/hl7";
const target: SchemaEmitTarget = "json-schema-2020-12";
emitMessageSchema(target).startsWith("{");
SimpleUsageCode
SimpleUsageCode =
"R"|"RE"|"C"|"CE"|"O"|"X"|"B"
The seven simple HL7 v2 conformance usage codes (HL7 Conformance Methodology, Message Profiles; IHE ITI TF Vol.2 Appendix C). They constrain whether an element must, may, or must not appear:
R: Required. The element SHALL be present (with a value). Absent → FINDING_CODES.PROFILE_REQUIRED_ABSENT.RE: Required but may be Empty. The element is supported and SHALL be sent when the sender has the data; its absence is never a violation (this engine cannot know whether the sender had the data).C: Conditional. Presence depends on a condition predicate. A rule that declares one on itsconditionis evaluated by it: predicate true is Required, predicate false is Not-permitted, which are the outcomes IHE specifies forC. A rule that declares NO predicate has its presence not evaluated (treated as optional); its length / value-set / cardinality rules still apply when it IS present.CE: Conditional but may be Empty. With a predicate, true is Required-but-may-be-Empty and false is Not-permitted, again IHE's outcomes. With no predicate, the same non-evaluation asC.O: Optional. No presence constraint.X: Not supported / not permitted. The element SHALL NOT be present. Present → FINDING_CODES.PROFILE_NOT_PERMITTED.B: Backward Compatible. Presence is not evaluated, exactly as forC; the element's length / value-set / cardinality rules still apply when it IS present. See the note below on where that behaviour comes from.
Two bounded decisions this library makes rather than inherits
B's presence behaviour is this library's decision, not a sourced
requirement. The HL7 conformance methodology's table of usage indicators
allowable in a message profile lists B among them and carries no definition
text for it, so no source settles what a validator should do with a B
element. This library chooses the reading that imposes no presence obligation
(so B behaves as C does on presence) while preserving the author's
declared intent in the profile. A later, separate change may revisit it
against a source that defines the code.
A declared conditional's outcome domain is unrestricted BY THE TYPE, and
narrowed by the level a profile claims. DeclaredConditionalUsage
admits any simple code as either outcome, because the full indicator set is
the CONSTRAINABLE level's vocabulary and that is what a profile claims when it
declares no level. The methodology separately restricts an IMPLEMENTABLE
profile to R, RE or X per element, and its conditional outcomes
likewise. A profile that declares ConformanceProfile.level
implementable is held to exactly that: C, CE, O, B, a conditional
whose outcomes are not all drawn from R / RE / X, and a rule that
declares no usage at all are each
FINDING_CODES.PROFILE_MALFORMED there, and none of them is refused at
any other level.
Example
import type { SimpleUsageCode } from "@cosyte/hl7";
const usage: SimpleUsageCode = "R";
SiuPatient
SiuPatient =
AdtPatient
Typed PID content for buildSiu: the same shape every family shares.
Example
import type { SiuPatient } from "@cosyte/hl7";
const patient: SiuPatient = { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } };
SiuResourceKind
SiuResourceKind =
"service"|"general"|"location"|"personnel"
Which AI* segment carries one SiuResource: AIS, AIG, AIL or AIP.
Example
import type { SiuResourceKind } from "@cosyte/hl7";
const kind: SiuResourceKind = "location"; // emitted as AIL
StreamMessageEntry
StreamMessageEntry<
M> = {message:M;ok:true;position:Hl7Position;raw:string;streamWarnings: readonlyHl7ParseWarning[]; } | {error:Hl7ParseError;ok:false;position:Hl7Position;raw:string;streamWarnings: readonlyHl7ParseWarning[]; }
One message yielded by parseStream. Discriminated on ok, mirroring
splitBatch's per-message entry: a successful parse carries the
Hl7Message (whose own .warnings hold per-message Tier-2 deviations); a
message that hit one of the four Tier-3 fatals carries the Hl7ParseError
instead: isolated, so the rest of the stream still yields.
raw is the message's verbatim source (re-parseable by parseHL7);
position.segmentIndex is the message's MSH (or, for stray pre-MSH
content, its first) segment index in the overall stream: the streaming
analogue of splitBatch's message position. streamWarnings holds
stream-level diagnostics that are not per-message parse warnings: today
only unterminatedStreamMessage on a final message that lacked a
terminator. It is kept separate from message.warnings (which stays exactly
what a whole-buffer parseHL7 of the same bytes would produce) and is
empty for every message but a possibly-truncated final one. Both the entry
and streamWarnings are frozen.
M is the parsed-message type each ok entry carries. It is Hl7Message
unless the stream was given a statically known profile, in which case it is
that profile's narrowed view and entry.message.part("ZDP")?.get(name) is
checked against the names the profile declares for ZDP.
Type Parameters
M
M extends Hl7Message = Hl7Message
the message type an ok entry carries.
Example
import { parseStream, WARNING_CODES } from "@cosyte/hl7";
for await (const entry of parseStream(source)) {
for (const w of entry.streamWarnings) {
if (w.code === WARNING_CODES.UNTERMINATED_STREAM_MESSAGE) {
// the feed may have been cut off mid-message
}
}
}
StructureDerivation
StructureDerivation =
"published"|"retained-transcription"
How one registry entry's expectations were obtained.
"published" means every expectation was read off HL7's own machine-readable
structure definitions. "retained-transcription" means the publication
carries no structure for the pair at all and the previous hand transcription
was kept rather than dropped; such an entry always carries the reason.
StructureFindingCode
StructureFindingCode = typeof
STRUCTURE_FINDING_CODES[keyof typeofSTRUCTURE_FINDING_CODES]
Discriminant union of every StructureFinding code. Enables exhaustive
switch narrowing.
Example
import type { StructureFindingCode } from "@cosyte/hl7";
const code: StructureFindingCode = "STRUCTURE_SEGMENT_UNEXPECTED";
StructureNotValidatedReason
StructureNotValidatedReason = typeof
STRUCTURE_NOT_VALIDATED_REASONS[keyof typeofSTRUCTURE_NOT_VALIDATED_REASONS]
Discriminant union of every reason validation did not run.
Example
import type { StructureNotValidatedReason } from "@cosyte/hl7";
const reason: StructureNotValidatedReason = "RETAINED_TRANSCRIPTION";
ToDateOptions
ToDateOptions =
DtmToDateOptions
The options bag toDate accepts: { assumeOffsetMinutes?: number }
and nothing else. An ALIAS of the pre-existing DtmToDateOptions,
which is unchanged and still exported, so the two names denote one type and
a caller may use either.
It exists because every @cosyte parser spells this options type
ToDateOptions, and a consumer writing across two of them should be able to
spell it the same way in both. Adding the alias costs nothing at runtime:
it is erased at compile time and no value changes.
Example
import { parseDtm, toDate } from "@cosyte/hl7";
import type { ToDateOptions } from "@cosyte/hl7";
const assumeUtc: ToDateOptions = { assumeOffsetMinutes: 0 };
toDate(parseDtm("20250102"), assumeUtc)?.toISOString();
// "2025-01-02T00:00:00.000Z"
TS
TS =
DtmParts
HL7 v2 Time Stamp (TS) / Date Time (DTM) composite: the raw HL7 string plus
its parsed parts, preserving the stated precision and timezone
fidelity. This is a DtmParts: valid is false (with no parts)
for unparseable input: NEVER throws (TYPES-04 no-throw guarantee).
There is deliberately no date field: a day-only value coerced to a JS
Date at UTC midnight silently shifts the calendar day in negative-offset
zones. To obtain an absolute instant, call dtmToDate(ts) explicitly (and
supply assumeOffsetMinutes for an offset-less value).
Example
import type { TS } from "@cosyte/hl7";
import { dtmToDate } from "@cosyte/hl7";
const dob: TS = { raw: "19880705", valid: true, precision: "day",
year: 1988, month: 7, day: 5, hasTimezone: false };
console.log(dob.precision); // "day": not a full timestamp
console.log(dtmToDate(dob)); // undefined: refuses to guess the zone
TypedMeta
TypedMeta<
K> =Omit<Meta,"messageCode"|"triggerEvent"> &object& [OverlayTriggerEvent<K>] extends [never] ?Pick<Meta,"triggerEvent"> :object
Meta with the message code (and, for a key that names one, the trigger
event) at the literal type the key established. Every other member is the
Meta member unchanged: narrowing a message type says nothing about whether
the sender populated MSH-10 or MSH-7.
Type Declaration
messageCode
readonlymessageCode:OverlayMessageCode<K>
Type Parameters
K
K extends OverlayKey
Example
import { parseHL7 } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.is("ADT^A01")) {
const code: "ADT" = msg.meta.messageCode; // no cast, no optional chain
const event: "A01" = msg.meta.triggerEvent;
console.log(code, event, msg.meta.controlId); // controlId is still optional
}
UsageCode
UsageCode =
SimpleUsageCode|DeclaredConditionalUsage
Everything a rule's usage may be declared as: one of the seven
SimpleUsageCodes, or a DeclaredConditionalUsage token.
This is not the same type as UsageCodeRegistryEntry. The
registry publishes C(a/b) as the NOTATION for the declared-conditional
family, and a / b are placeholders rather than usage codes, so the
literal "C(a/b)" is deliberately NOT assignable here. Neither type widens
to a bare string: a typo such as "RQ" is a compile error, and
defineConformanceProfile is the runtime backstop rather than the
replacement.
Example
import type { UsageCode } from "@cosyte/hl7";
const simple: UsageCode = "R";
const conditional: UsageCode = "C(RE/X)";
UsageCodeRegistryEntry
UsageCodeRegistryEntry =
SimpleUsageCode|"C(a/b)"
One entry of the exported USAGE_CODES vocabulary listing: a
SimpleUsageCode, or the literal notation "C(a/b)" that stands for
the whole declared-conditional family.
Distinct from UsageCode on purpose, so the placeholder can be listed without ever becoming assignable where a rule's usage is expected.
Example
import type { UsageCodeRegistryEntry } from "@cosyte/hl7";
const entry: UsageCodeRegistryEntry = "C(a/b)";
VxuObservation
VxuObservation =
OruObservation
Typed OBX content for buildVxu (eligibility, funding source, …).
Example
import type { VxuObservation } from "@cosyte/hl7";
const eligibility: VxuObservation = {
setId: "1",
valueType: "CE",
identifier: { identifier: "64994-7", text: "Vaccine funding program eligibility" },
value: "V02",
};
VxuPatient
VxuPatient =
AdtPatient
Typed PID content for buildVxu: the same shape every family shares.
Example
import type { VxuPatient } from "@cosyte/hl7";
const patient: VxuPatient = {
identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
name: { familyName: "Test", givenName: "Ann" },
};
WarningCode
WarningCode = typeof
WARNING_CODES[keyof typeofWARNING_CODES]
Discriminant type for Hl7ParseWarning.code. Narrowing a warning by this
code lets consumers write exhaustive switch blocks (enabled by the
switch-exhaustiveness-check lint rule) and guarantees a typo-free
comparison against the WARNING_CODES registry.
Example
import type { Hl7ParseWarning, WarningCode } from "@cosyte/hl7";
function describe(w: Hl7ParseWarning): string {
const code: WarningCode = w.code;
switch (code) {
case "MLLP_FRAMING_STRIPPED":
return "stripped MLLP framing";
default:
return `warning: ${code}`;
}
}
Variables
ACK_CODES
constACK_CODES:object
HL7 Table 0008: Acknowledgment code (MSA-1). The two acknowledgment vocabularies:
- original mode:
AAApplication Accept ·AEApplication Error ·ARApplication Reject. - enhanced mode accept-level:
CACommit Accept ·CECommit Error ·CRCommit Reject (the application-level response in enhanced mode reusesAA/AE/AR).
Type Declaration
AA
readonlyAA:"AA"="AA"
AE
readonlyAE:"AE"="AE"
AR
readonlyAR:"AR"="AR"
CA
readonlyCA:"CA"="CA"
CE
readonlyCE:"CE"="CE"
CR
readonlyCR:"CR"="CR"
Example
import { ACK_CODES } from "@cosyte/hl7";
ACK_CODES.AA; // "AA"
ACK_CONDITIONS
constACK_CONDITIONS:object
HL7 Table 0155: Accept/application acknowledgment conditions (MSH-15 /
MSH-16): AL Always · NE Never · ER Error/reject conditions only ·
SU Successful completion only. Exposed read-only for adapters that
surface the inbound sender's stated acknowledgment expectations.
Type Declaration
AL
readonlyAL:"AL"="AL"
ER
readonlyER:"ER"="ER"
NE
readonlyNE:"NE"="NE"
SU
readonlySU:"SU"="SU"
Example
import { ACK_CONDITIONS } from "@cosyte/hl7";
ACK_CONDITIONS.AL; // "AL" (Always)
AMBIGUOUS_DATE_ORDER
constAMBIGUOUS_DATE_ORDER:"AMBIGUOUS_DATE_ORDER"="AMBIGUOUS_DATE_ORDER"
Stable identifier reported on DtmParts.ambiguity when a slash date's
field order cannot be established. Compare against it to tell "ambiguous,
refused" apart from "malformed" (no timestamp at all) and from "resolved via
a fallback" (valid: true with a matchedFormat). Distinct from the
TIMESTAMP_FALLBACK_FORMAT warning code, which reports the opposite
outcome: a value that DID resolve.
Example
import { parseHL7, AMBIGUOUS_DATE_ORDER } from "@cosyte/hl7";
const ts = parseHL7(raw).meta.timestamp;
if (ts?.ambiguity?.code === AMBIGUOUS_DATE_ORDER) {
console.log(ts.ambiguity.message);
}
BUILTIN_DATE_FALLBACKS
constBUILTIN_DATE_FALLBACKS: readonlystring[]
Ordered list of built-in timestamp formats parseDtmCascade falls
back to when neither the strict HL7 DTM match nor any user-supplied format
succeeds. ISO-8601 is tried first (most constrained); MM/DD/YYYY HH:mm:ss
last (it overlaps the date-only form).
The two MM/DD/YYYY entries resolve only what has a single reading. A
slash value whose first two components are both in 1-12 (05/07/1988) is
also a legal day-first date, and no built-in decides between them: such a
value is REFUSED with a DtmAmbiguity report instead of being read as
May 7. 07/25/1988 has one reading and still resolves; 05/05/1988 has two
that agree and still resolves. To read day-first values, declare the order:
parseHL7(raw, { dateFormats: ["DD/MM/YYYY"] }), or a profile's
dateFormats. Both are tried ahead of this list.
Example
import { BUILTIN_DATE_FALLBACKS } from "@cosyte/hl7";
console.log(BUILTIN_DATE_FALLBACKS);
// ["ISO-8601", "YYYY-MM-DD", "MM/DD/YYYY", "MM/DD/YYYY HH:mm:ss"]
DEFAULT_ENCODING_CHARACTERS
constDEFAULT_ENCODING_CHARACTERS:EncodingCharacters
The HL7 default 5-tuple of encoding characters used when a message does
not override them via MSH-1 / MSH-2. Re-used by downstream stages (the
escape map, parseHL7) as a synthetic-message fallback.
Example
import { DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
DEFAULT_ENCODING_CHARACTERS.field; // "|"
ERR_CONDITION_CODE_SYSTEM
constERR_CONDITION_CODE_SYSTEM:"HL70357"="HL70357"
Code-system name emitted in ERR-3.3 for Table 0357 condition codes.
ERR_CONDITION_CODES
constERR_CONDITION_CODES:Readonly<Record<string,string>>
HL7 Table 0357: Message error condition codes (ERR-3.1 → ERR-3.2 text).
Frozen read-only map of code → standard display text. The code system
name emitted in ERR-3.3 is ERR_CONDITION_CODE_SYSTEM.
Codes 104 (value too long) and 105 (table value not found) are v2.7+
additions; the rest are present from v2.5. buildAck emits whatever code it
is told (it never invents a condition) and looks up the display text here.
Example
import { ERR_CONDITION_CODES } from "@cosyte/hl7";
ERR_CONDITION_CODES["101"]; // "Required field missing"
ERR_SEVERITIES
constERR_SEVERITIES:object
HL7 Table 0516: Error severity (ERR-4). A v2.5+ construct (ERR was structured differently in v2.3.1).
Type Declaration
E
readonlyE:"E"="E"
Error.
I
readonlyI:"I"="I"
Information.
W
readonlyW:"W"="W"
Warning.
Example
import { ERR_SEVERITIES } from "@cosyte/hl7";
ERR_SEVERITIES.E; // "E" (Error)
FATAL_CODES
constFATAL_CODES:object
Stable string codes for every Tier-3 fatal the parser may throw. Locked
at four codes: anything else MUST be a Tier-2 warning. Consumers narrow
on err.code to react to specific structural failures.
Type Declaration
EMPTY_INPUT
readonlyEMPTY_INPUT:"EMPTY_INPUT"="EMPTY_INPUT"
INVALID_ENCODING_CHARACTERS
readonlyINVALID_ENCODING_CHARACTERS:"INVALID_ENCODING_CHARACTERS"="INVALID_ENCODING_CHARACTERS"
MSH_TOO_SHORT
readonlyMSH_TOO_SHORT:"MSH_TOO_SHORT"="MSH_TOO_SHORT"
NO_MSH_SEGMENT
readonlyNO_MSH_SEGMENT:"NO_MSH_SEGMENT"="NO_MSH_SEGMENT"
Example
import { parseHL7, FATAL_CODES, Hl7ParseError } from "@cosyte/hl7";
try {
parseHL7("");
} catch (err) {
if (err instanceof Hl7ParseError && err.code === FATAL_CODES.EMPTY_INPUT) {
// handle empty input
}
}
FINDING_CODES
constFINDING_CODES:object
The frozen registry of finding codes. Stable, additive string codes: a
consumer compares finding.code === FINDING_CODES.PROFILE_REQUIRED_ABSENT.
Segment-level vs field-level is disambiguated by whether the finding's
FindingLocus.field is present, not by separate codes.
Type Declaration
PROFILE_CARDINALITY
readonlyPROFILE_CARDINALITY:"PROFILE_CARDINALITY"="PROFILE_CARDINALITY"
A segment-occurrence or field-repetition count is outside its cardinality.
PROFILE_CODING_SYSTEM_MISMATCH
readonlyPROFILE_CODING_SYSTEM_MISMATCH:"PROFILE_CODING_SYSTEM_MISMATCH"="PROFILE_CODING_SYSTEM_MISMATCH"
A present repetition does not carry the coding system the rule binds its value set to: its coding-system component resolves to a different system, to none this library recognizes, or is blank or absent altogether.
A distinct code on purpose, because it is a different answer from FINDING_CODES.PROFILE_VALUE_NOT_IN_SET: "a code we do not accept" and "a code that looks right but claims the wrong system" are two problems with two remedies, and one code for both would hide the second inside the first. A rule that declares no FieldRule.codingSystem never emits it.
PROFILE_CONDITION_UNEVALUATABLE
readonlyPROFILE_CONDITION_UNEVALUATABLE:"PROFILE_CONDITION_UNEVALUATABLE"="PROFILE_CONDITION_UNEVALUATABLE"
A rule's declared ConditionPredicate could not be decided against this message, so the rule's usage was never selected and the element's presence was NOT assessed. A distinct code on purpose: an unassessed conditional is filterable by code, without matching a message string.
PROFILE_LENGTH
readonlyPROFILE_LENGTH:"PROFILE_LENGTH"="PROFILE_LENGTH"
A checked component value exceeds the declared maximum length.
PROFILE_MALFORMED
readonlyPROFILE_MALFORMED:"PROFILE_MALFORMED"="PROFILE_MALFORMED"
The profile ITSELF is structurally malformed (a diagnostic, not a message finding).
PROFILE_NOT_PERMITTED
readonlyPROFILE_NOT_PERMITTED:"PROFILE_NOT_PERMITTED"="PROFILE_NOT_PERMITTED"
A Not-permitted (X) segment or field is present.
PROFILE_REQUIRED_ABSENT
readonlyPROFILE_REQUIRED_ABSENT:"PROFILE_REQUIRED_ABSENT"="PROFILE_REQUIRED_ABSENT"
A Required (R) segment or field is absent (or present-but-empty).
PROFILE_UNDECLARED_CONTENT
readonlyPROFILE_UNDECLARED_CONTENT:"PROFILE_UNDECLARED_CONTENT"="PROFILE_UNDECLARED_CONTENT"
A present repetition carries a non-empty value at a component index the field rule does not declare.
A distinct code on purpose: "a value we do not accept" and "content in an element the profile never specified" are two problems with two remedies, and the second is the one an author cannot see from any other finding. It fires only for a field rule carrying at least one FieldRule.components entry, because declaring component rules is what closes the component set; a rule that declares none (the member omitted, or an empty list) can never emit it.
PROFILE_VALUE_NOT_IN_SET
readonlyPROFILE_VALUE_NOT_IN_SET:"PROFILE_VALUE_NOT_IN_SET"="PROFILE_VALUE_NOT_IN_SET"
A checked component value is not a member of the consumer-supplied value set.
Example
import { validateAgainstProfile, FINDING_CODES } from "@cosyte/hl7";
const { findings } = validateAgainstProfile(msg, profile);
const missing = findings.filter((f) => f.code === FINDING_CODES.PROFILE_REQUIRED_ABSENT);
JSON_SCHEMA_DIALECT
constJSON_SCHEMA_DIALECT:"https://json-schema.org/draft/2020-12/schema"="https://json-schema.org/draft/2020-12/schema"
The JSON Schema dialect the emitted document declares, and the only one this library emits.
Example
import { JSON_SCHEMA_DIALECT, messageJsonSchema } from "@cosyte/hl7";
messageJsonSchema().$schema === JSON_SCHEMA_DIALECT; // true
KNOWN_CODING_SYSTEMS
constKNOWN_CODING_SYSTEMS: readonlyKnownCodingSystem[]
The safety-relevant subset of HL7 Table 0396 this library recognizes for
provenance. Deliberately small and frozen: it is NOT the full Table 0396
registry. Each entry's source is recorded in
docs-content/spec-notes-coding-system.md.
Note on I10: Table 0396 registers I10 as ICD-10 (the WHO base
classification). US v2 feeds frequently send I10 when they mean
ICD-10-CM, but that clinical-modification specificity is the sender's
convention, not what the acronym registers: so this map reports the
registered claim ("ICD-10") and does not silently upgrade it to CM.
Example
import { KNOWN_CODING_SYSTEMS } from "@cosyte/hl7";
console.log(KNOWN_CODING_SYSTEMS.find((s) => s.id === "LN")?.name); // "LOINC"
KNOWN_SEGMENTS
constKNOWN_SEGMENTS:ReadonlySet<string>
Frozen set of every standard HL7 v2 segment name the library recognises.
Consumers parsing any segment whose name is neither in this set nor
declared by the active profile will see UNKNOWN_SEGMENT in
msg.warnings.
Example
import { KNOWN_SEGMENTS } from "@cosyte/hl7";
console.log(KNOWN_SEGMENTS.has("PID")); // true
console.log(KNOWN_SEGMENTS.has("ZPI")); // false
MESSAGE_STRUCTURE_DEFINITIONS
constMESSAGE_STRUCTURE_DEFINITIONS: readonlyMessageStructureDefinition[]
The derived expected-segment registry. Generated from HL7's published,
machine-readable message structures (see STRUCTURE_REGISTRY_PROVENANCE for
the publication, the commit and the per-file hashes) and frozen: data, not
config. Deliberately narrow in COVERAGE (twelve message codes) and not narrow
in CONTENT: within those codes it expects exactly what the publication marks
required. It is still a safety net, not a conformance validator.
Example
import { MESSAGE_STRUCTURE_DEFINITIONS } from "@cosyte/hl7";
const adt = MESSAGE_STRUCTURE_DEFINITIONS.find((d) =>
d.messageCode === "ADT" && d.triggerEvents.includes("A01"),
);
console.log(adt?.requiredSegments); // ["EVN", "MSH", "PID", "PV1"]
PREDICATE_CONNECTORS
constPREDICATE_CONNECTORS: readonlyPredicateConnector[]
The frozen, ordered listing of the connectors a condition predicate may use:
AND, OR, XOR. Like PREDICATE_VERBS this listing IS the
accept-set.
Example
import { PREDICATE_CONNECTORS } from "@cosyte/hl7";
PREDICATE_CONNECTORS.length; // 3
PREDICATE_CONNECTORS[2]; // "XOR"
PREDICATE_VERBS
constPREDICATE_VERBS: readonlyPredicateVerb[]
The frozen, ordered listing of the comparison verbs a condition predicate may
use: is, is not, contains, does not contain, matches,
does not match.
Unlike USAGE_CODES, this listing IS the accept-set: a verb outside it
is PROFILE_MALFORMED, and the PredicateVerb type refuses it at
compile time where the author writes TypeScript.
Example
import { PREDICATE_VERBS } from "@cosyte/hl7";
PREDICATE_VERBS.length; // 6
PREDICATE_VERBS[0]; // "is"
PROFILE_LEVELS
constPROFILE_LEVELS: readonlyProfileLevel[]
The frozen listing of the three ProfileLevels, ordered from the least
constrained to the most: standard, constrainable, implementable.
Unlike USAGE_CODES this listing IS the accept-set: a ConformanceProfile.level outside it is FINDING_CODES.PROFILE_MALFORMED, and the ProfileLevel type refuses it at compile time where the author writes TypeScript.
Example
import { PROFILE_LEVELS } from "@cosyte/hl7";
PROFILE_LEVELS.length; // 3
PROFILE_LEVELS[0]; // "standard"
profiles
constprofiles:object
Namespace object exposing the 8 shipped built-in vendor profiles (epic,
cerner, meditech, athena, genericLab, visage, philips, va). Each is authored
via the public defineProfile() API (BIP-01..09).
Type Declaration
athena
readonlyathena:DefinedProfile<{customSegments: {ZCA: {fields: {careTeamRole:number;providerId:number;providerName:number; }; }; };dateFormats:string[];description:string;name:string; }>
cerner
readonlycerner:DefinedProfile<{customSegments: {ZCO: {fields: {commentText:number;continuationFlag:number; }; };ZDS: {fields: {summaryText:number; }; }; };dateFormats:string[];description:string;name:string; }>
epic
readonlyepic:DefinedProfile<{customSegments: {ZDP: {fields: {departmentCode:number;departmentName:number; }; };ZRS: {fields: {resultStatus:number;statusDateTime:number; }; }; };dateFormats:string[];description:string;name:string; }>
genericLab
readonlygenericLab:DefinedProfile<{customSegments: {ZLB: {fields: {methodOverride:number;specimenOverride:number; }; };ZNT: {fields: {noteText:number; }; }; };dateFormats:string[];description:string;name:string; }>
meditech
readonlymeditech:DefinedProfile<{customSegments: {ZF1: {fields: {copayMaximum:number;copayMinimum:number;misServiceGroup:number;providerEncounter:number;serviceGroupCopay:number;visitCopay:number; }; };ZF2: {fields: {encounterDate:number;encounterProcedure:number;encounterProcedureCharge:number;encounterProcedureQuantity:number;providerEncounter:number;prvProcedureAmountDue:number;prvProcedureAmountPaid:number;setId:number; }; }; };dateFormats:string[];description:string;name:string; }>
philips
readonlyphilips:DefinedProfile<{customSegments: {ZAO: {fields: {acquisitionStatus:number;bodyPart:number;departmentId:number;device:number;modality:number;orderAdditionalDetails:number;orderCreatedBy:number;orderCustomDate1:number;orderCustomDate2:number;orderCustomDate3:number;orderCustomNumber1:number;orderCustomNumber2:number;orderCustomString1:number;orderCustomString2:number;orderCustomString3:number;orderCustomString4:number;orderCustomString5:number;orderCustomString6:number;orderCustomString7:number;orderUpdatedBy:number;orderWithNoImages:number;radiologistFamilyName:number;radiologistGivenName:number;radiologistId:number;radiologistMiddleName:number;resultTransferStatus:number;section:number;technicianFamilyName:number;technicianGivenName:number;technicianId:number;technicianMiddleName:number; }; };ZAP: {fields: {patientAdditionalDetails:number;patientCustomDate1:number;patientCustomDate2:number;patientCustomDate3:number;patientCustomNumber1:number;patientCustomNumber2:number;patientCustomString1:number;patientCustomString2:number;patientCustomString3:number;patientCustomString4:number;patientCustomString5:number;patientCustomString6:number;patientCustomString7:number; }; };ZAV: {fields: {visitAdditionalDetails:number; }; };ZDS: {fields: {studyInstanceUid:number; }; };ZEB: {fields: {encryptedPatientInfo:number; }; };ZLK: {fields: {externalWorkitemId:number;orderLinkId:number; }; }; };description:string;name:string; }>
va
readonlyva:DefinedProfile<{customSegments: {ZDS: {fields: {studyInstanceUid:number; }; }; };description:string;name:string; }>
visage
readonlyvisage:DefinedProfile<{customSegments: {ZDS: {fields: {studyInstanceUid:number; }; }; };description:string;name:string; }>
Example
import { parseHL7, profiles } from "@cosyte/hl7";
const msg = parseHL7(raw, profiles.epic);
console.log(msg.profile?.name); // "epic"
SCHEMA_EMIT_TARGETS
constSCHEMA_EMIT_TARGETS: readonly ["json-schema-2020-12","zod"]
Every emission target this library supports, as a caller names it.
Example
import { SCHEMA_EMIT_TARGETS } from "@cosyte/hl7";
SCHEMA_EMIT_TARGETS.includes("zod"); // true
STRUCTURE_FINDING_CODES
constSTRUCTURE_FINDING_CODES:object
The frozen registry of published-structure finding codes. Stable, additive
string codes: a consumer compares
finding.code === STRUCTURE_FINDING_CODES.STRUCTURE_SEGMENT_CARDINALITY.
Deliberately separate from the conformance engine's FINDING_CODES. Those
report a violation of a profile the CALLER wrote; these report a divergence
from the publication itself, and a consumer routing the two to the same place
is making a decision rather than inheriting one.
Type Declaration
STRUCTURE_SEGMENT_CARDINALITY
readonlySTRUCTURE_SEGMENT_CARDINALITY:"STRUCTURE_SEGMENT_CARDINALITY"="STRUCTURE_SEGMENT_CARDINALITY"
A segment occurs fewer times than the published structure's minimum along the path from the structure root, or more times than its maximum.
STRUCTURE_SEGMENT_OUT_OF_ORDER
readonlySTRUCTURE_SEGMENT_OUT_OF_ORDER:"STRUCTURE_SEGMENT_OUT_OF_ORDER"="STRUCTURE_SEGMENT_OUT_OF_ORDER"
A segment appears where the published structure does not allow it: the message's segment sequence stops being a beginning the published order allows.
The order is read against the publication's own bounds, at every locus: a node may occur as few and as many times as the publication says there, so a group's required leading segment cannot be skipped on one occurrence and consumed on the next, whether or not that group repeats. The one bound dropped for a segment name is the one the cardinality check already reported for it, so that too few or too many occurrences is one finding rather than two.
Where two segments arrive in an order the publication does not allow, the finding names the one that arrived late: the segment the published order puts first and the message delivered second, at the occurrence the message carries it. That is not always where the reading stopped, because the publication is often free to skip past the segment a message delayed, so an adjacent pair whose exchange the publication derives is what identifies the two. Every adjacent pair is asked, not a shortlist near the stop: in a message that repeats a group the pair can sit well in front of it. Where more than one pair would do, the earliest is named. Where no exchange derives, the defect is not two segments in the wrong order: the finding then names the segment that belonged where the reading stopped, and failing that the one that could not be placed.
STRUCTURE_SEGMENT_UNEXPECTED
readonlySTRUCTURE_SEGMENT_UNEXPECTED:"STRUCTURE_SEGMENT_UNEXPECTED"="STRUCTURE_SEGMENT_UNEXPECTED"
A segment the published structure does not name anywhere, a Z segment
included. Distinct from the other two on purpose: a segment the publication
never mentions has no position to be out of and no count to exceed.
A line carrying no segment name at all is not one of these. A message
ending with a segment terminator parses as a trailing segment whose type is
the empty string, and the publication names segments rather than blank
lines; the parse reports that line, under UNKNOWN_SEGMENT.
Example
import { validateMessageStructure, STRUCTURE_FINDING_CODES } from "@cosyte/hl7";
const { findings } = validateMessageStructure(msg);
const misordered = findings.filter(
(f) => f.code === STRUCTURE_FINDING_CODES.STRUCTURE_SEGMENT_OUT_OF_ORDER,
);
STRUCTURE_NOT_VALIDATED_REASONS
constSTRUCTURE_NOT_VALIDATED_REASONS:object
The frozen registry of reasons validation did not run. Every one of them is a question the publication cannot answer for this message, never a defect found in it.
Type Declaration
EMPTY_EXPECTATION
readonlyEMPTY_EXPECTATION:"EMPTY_EXPECTATION"="EMPTY_EXPECTATION"
The registry entry resolves to no ordered expectation at all. A published structure whose expectation is empty says nothing about the message, and treating "says nothing" as "allows nothing" would report every segment as unexpected.
NO_MESSAGE_TYPE
readonlyNO_MESSAGE_TYPE:"NO_MESSAGE_TYPE"="NO_MESSAGE_TYPE"
MSH-9.1 is absent or blank, so there is no message type to resolve.
RETAINED_TRANSCRIPTION
readonlyRETAINED_TRANSCRIPTION:"RETAINED_TRANSCRIPTION"="RETAINED_TRANSCRIPTION"
The message type is recognized only through a retained transcription: the publication carries no structure for it, so there is no published order and no published maximum to check against.
UNRECOGNIZED_MESSAGE_TYPE
readonlyUNRECOGNIZED_MESSAGE_TYPE:"UNRECOGNIZED_MESSAGE_TYPE"="UNRECOGNIZED_MESSAGE_TYPE"
The structure registry models no published structure for this message type.
Example
import { validateMessageStructure, STRUCTURE_NOT_VALIDATED_REASONS } from "@cosyte/hl7";
const result = validateMessageStructure(msg);
const unmodelled =
result.reason === STRUCTURE_NOT_VALIDATED_REASONS.UNRECOGNIZED_MESSAGE_TYPE;
STRUCTURE_REGISTRY_PROVENANCE
constSTRUCTURE_REGISTRY_PROVENANCE:Readonly<{families: readonlyReadonly<{members: readonlystring[];structureId:string; }>[];files: readonlyReadonly<{bytes:number;path:string;sha256:string;url:string; }>[];pairs: readonlyReadonly<{messageCode:string;structureId:string;triggerEvent:string; }>[];publication:Readonly<{commit:string;commitDate:string;name:string;repository:string;repositoryUrl:string;tree:string; }>;snapshotTakenAt:string; }>
Where every expectation in MESSAGE_STRUCTURE_DEFINITIONS came from: the publication and the commit it was read at, the sha256 and upstream URL of every vendored file, the variant family behind each referenced structure, and the structure id behind each recognized (message code, trigger event) pair.
A consumer auditing a warning can answer "says who?" without leaving the package and without a network call.
Example
import { STRUCTURE_REGISTRY_PROVENANCE } from "@cosyte/hl7";
console.log(STRUCTURE_REGISTRY_PROVENANCE.publication.repository); // "HL7/v2ig"
const a01 = STRUCTURE_REGISTRY_PROVENANCE.pairs.find(
(p) => p.messageCode === "ADT" && p.triggerEvent === "A01",
);
console.log(a01?.structureId); // "ADT_A01"
SUPPORTED_BUILDER_MESSAGES
constSUPPORTED_BUILDER_MESSAGES: readonlySupportedBuilderMessage[]
Every (message code, trigger event) pair the typed builders can author, with the builder that authors it. Frozen; derived from the structure registry, so it can never claim a pair the structure net would warn on.
Example
import { SUPPORTED_BUILDER_MESSAGES } from "@cosyte/hl7";
for (const m of SUPPORTED_BUILDER_MESSAGES) {
console.log(`${m.messageCode}^${m.triggerEvent}`, m.builder);
}
SUPPORTED_DATE_TOKENS
constSUPPORTED_DATE_TOKENS: readonlystring[] =TOKEN_ORDER
Every date-format token the library's format matcher and the
defineProfile() dateFormats validator recognize. Re-exported so profile
authors can introspect the valid token set.
Every token listed here is honoured by the matcher: none is silently accepted at definition time and then treated as literal characters. The vocabulary carries no two-digit-year token (no century window is applied, so a two-digit year cannot be resolved) and no timezone token (a value with no offset is flagged, never resolved).
Example
import { SUPPORTED_DATE_TOKENS } from "@cosyte/hl7";
console.log(SUPPORTED_DATE_TOKENS);
// ["YYYY", "MM", "M", "MMM", "MMMM", "DD", "D", "HH", "H", "hh", "h", "mm", "ss", "SSSS", "A"]
SUPPORTED_OVERLAY_MESSAGES
constSUPPORTED_OVERLAY_MESSAGES: readonlySupportedOverlayMessage[]
The published support claim for typed message overlays: every key Hl7Message.is narrows on, with the segments that pair's published structure requires. Frozen, and derived from the structure registry at module load rather than hand-listed, so it can never claim a pair the structure net does not recognize.
Example
import { SUPPORTED_OVERLAY_MESSAGES } from "@cosyte/hl7";
console.log(SUPPORTED_OVERLAY_MESSAGES.length); // one entry per published key
for (const m of SUPPORTED_OVERLAY_MESSAGES) console.log(m.key, m.requiredSegments);
TYPED_BUILDER_COVERAGE
constTYPED_BUILDER_COVERAGE: readonlyTypedBuilderCoverage[]
What each typed builder can emit. Frozen data, exposed so a consumer can
check the support claim's exclusions for themselves: a registry pair whose
required segments are not a subset of the matching entry's segments is
exactly the pair the support set leaves out.
Example
import { TYPED_BUILDER_COVERAGE, findMessageStructureDefinition } from "@cosyte/hl7";
const cov = TYPED_BUILDER_COVERAGE.find((c) => c.messageCode === "ADT");
const a20 = findMessageStructureDefinition("ADT", "A20");
// A20 requires NPU, which no typed init supplies, so A20 is not published:
console.log(a20?.requiredSegments.every((s) => cov?.segments.includes(s))); // false
USAGE_CODES
constUSAGE_CODES: readonlyUsageCodeRegistryEntry[]
The frozen, ordered registry of HL7 v2 usage indicators a message profile may
use: R, RE, C, CE, O, X, C(a/b), B.
It is a published vocabulary LISTING, not a membership oracle for what a rule may declare. The two questions are different and neither answers the other:
C(a/b)is listed here and is REFUSED on a rule, becauseaandbare placeholders for usage codes rather than usage codes.C(RE/X)is accepted on a rule and is NOT listed here, because the listing carries the family's notation once instead of all of its members.
What a rule may declare is answered by UsageCode at compile time and by defineConformanceProfile / validateAgainstProfile at run time. Do not re-derive this array as an accept-set.
Example
import { USAGE_CODES } from "@cosyte/hl7";
USAGE_CODES.length; // 8
USAGE_CODES[0]; // "R"
VERSION
constVERSION:string="0.1.0"
Library version string, synced with package.json#version at build time
by downstream phases. Exported now so consumers (and the type-check
pipeline) have at least one symbol to resolve through the exports map.
Example
import { VERSION } from "@cosyte/hl7";
console.log(VERSION);
WARNING_CODES
constWARNING_CODES:object
Stable string codes for every Tier-2 warning the parser may emit. The
registry is frozen via as const so TypeScript infers the exact string
literal union for WarningCode: there is zero runtime cost and no
magic-string comparisons for consumers.
Type Declaration
ACK_NO_CORRELATION_ID
readonlyACK_NO_CORRELATION_ID:"ACK_NO_CORRELATION_ID"="ACK_NO_CORRELATION_ID"
BATCH_COUNT_MISMATCH
readonlyBATCH_COUNT_MISMATCH:"BATCH_COUNT_MISMATCH"="BATCH_COUNT_MISMATCH"
BATCH_MISSING_TRAILER
readonlyBATCH_MISSING_TRAILER:"BATCH_MISSING_TRAILER"="BATCH_MISSING_TRAILER"
DUPLICATE_REQUIRED_SEGMENT
readonlyDUPLICATE_REQUIRED_SEGMENT:"DUPLICATE_REQUIRED_SEGMENT"="DUPLICATE_REQUIRED_SEGMENT"
ENCODING_MISMATCH
readonlyENCODING_MISMATCH:"ENCODING_MISMATCH"="ENCODING_MISMATCH"
EXTRA_FIELDS
readonlyEXTRA_FIELDS:"EXTRA_FIELDS"="EXTRA_FIELDS"
FIELD_WHITESPACE_TRIMMED
readonlyFIELD_WHITESPACE_TRIMMED:"FIELD_WHITESPACE_TRIMMED"="FIELD_WHITESPACE_TRIMMED"
MERGE_MISSING_PRIOR_OR_SURVIVOR
readonlyMERGE_MISSING_PRIOR_OR_SURVIVOR:"MERGE_MISSING_PRIOR_OR_SURVIVOR"="MERGE_MISSING_PRIOR_OR_SURVIVOR"
MISSING_EXPECTED_GROUP
readonlyMISSING_EXPECTED_GROUP:"MISSING_EXPECTED_GROUP"="MISSING_EXPECTED_GROUP"
MISSING_REQUIRED_FIELD
readonlyMISSING_REQUIRED_FIELD:"MISSING_REQUIRED_FIELD"="MISSING_REQUIRED_FIELD"
MLLP_FRAMING_STRIPPED
readonlyMLLP_FRAMING_STRIPPED:"MLLP_FRAMING_STRIPPED"="MLLP_FRAMING_STRIPPED"
OUT_OF_ORDER_SEGMENT
readonlyOUT_OF_ORDER_SEGMENT:"OUT_OF_ORDER_SEGMENT"="OUT_OF_ORDER_SEGMENT"
SEGMENT_CASE
readonlySEGMENT_CASE:"SEGMENT_CASE"="SEGMENT_CASE"
TIMESTAMP_FALLBACK_FORMAT
readonlyTIMESTAMP_FALLBACK_FORMAT:"TIMESTAMP_FALLBACK_FORMAT"="TIMESTAMP_FALLBACK_FORMAT"
UNKNOWN_CHARSET
readonlyUNKNOWN_CHARSET:"UNKNOWN_CHARSET"="UNKNOWN_CHARSET"
UNKNOWN_ESCAPE_SEQUENCE
readonlyUNKNOWN_ESCAPE_SEQUENCE:"UNKNOWN_ESCAPE_SEQUENCE"="UNKNOWN_ESCAPE_SEQUENCE"
UNKNOWN_SEGMENT
readonlyUNKNOWN_SEGMENT:"UNKNOWN_SEGMENT"="UNKNOWN_SEGMENT"
UNSUPPORTED_CHARSET
readonlyUNSUPPORTED_CHARSET:"UNSUPPORTED_CHARSET"="UNSUPPORTED_CHARSET"
UNTERMINATED_STREAM_MESSAGE
readonlyUNTERMINATED_STREAM_MESSAGE:"UNTERMINATED_STREAM_MESSAGE"="UNTERMINATED_STREAM_MESSAGE"
VERSION_MISMATCH
readonlyVERSION_MISMATCH:"VERSION_MISMATCH"="VERSION_MISMATCH"
Example
import { parseHL7, WARNING_CODES } from "@cosyte/hl7";
const msg = parseHL7(raw);
if (msg.warnings.some((w) => w.code === WARNING_CODES.MLLP_FRAMING_STRIPPED)) {
// handle MLLP-wrapped input
}
Functions
ackNoCorrelationId()
ackNoCorrelationId(
position):Hl7ParseWarning
Build an ACK_NO_CORRELATION_ID warning. Emitted by buildAck,
not by the parser: the inbound message carried no MSH-10 message control ID,
so the generated ACK leaves MSA-2 empty and, when a positive accept was
requested, downgrades it to an error code rather than fabricating an
unverifiable AA/CA. The position references the inbound MSH segment.
The message NEVER echoes a PHI value: only the structural fact.
Parameters
position
Returns
Example
import { buildAck, WARNING_CODES } from "@cosyte/hl7";
const ack = buildAck(inbound, { code: "AA" }); // inbound has no MSH-10
ack.warnings.some((w) => w.code === WARNING_CODES.ACK_NO_CORRELATION_ID); // true
alternateCodingSystemOf()
alternateCodingSystemOf(
coded):CodingSystemInfo|undefined
Provenance of a coded element's alternate coding system (CWE.6 / CE.6).
Returns undefined when the element claims no alternate system. Useful for
dual-coded fields (e.g. a problem carrying both SNOMED CT and ICD-10), where
assuming a single coding system would be unsafe.
Parameters
coded
Returns
CodingSystemInfo | undefined
Example
import { alternateCodingSystemOf } from "@cosyte/hl7";
const alt = alternateCodingSystemOf(dg.code);
if (alt) console.log("also coded in", alt.name ?? alt.claimed);
analyzeMessageStructure()
analyzeMessageStructure(
messageCode,triggerEvent,presentSegmentNames):MessageStructure
Analyze a parsed message's structure against the derived expected-segment
registry. Pure: it takes the message code, trigger event, and the set of
segment names actually present, and returns a MessageStructure summary. It
decides nothing about whether to warn: the caller (parser) emits one
MISSING_EXPECTED_GROUP warning per name in missingSegments.
Parameters
messageCode
string
MSH-9.1 (e.g. "ORU"); empty string if absent.
triggerEvent
string
MSH-9.2 (e.g. "R01"); empty string if absent.
presentSegmentNames
ReadonlySet<string>
the set of segment names present in the message.
Returns
Example
import { analyzeMessageStructure } from "@cosyte/hl7";
const s = analyzeMessageStructure("ORU", "R01", new Set(["MSH", "PID"]));
console.log(s.recognized); // true
console.log(s.missingSegments); // ["OBR"] (the published minimum of one)
batchCountMismatch()
batchCountMismatch(
position,unit,declared,actual):Hl7ParseWarning
Build a BATCH_COUNT_MISMATCH warning. Emitted by
splitBatch(): attached to the returned BatchSplitResult.warnings, never
to Hl7Message.warnings: when a declared envelope count does not equal the
count actually split out: a BTS-1 batch message count that differs from
the messages found in that batch, or an FTS-1 file batch count that
differs from the batches found in the file (HL7 v2 Ch. 2 §2.10.3; BTS-1 / FTS-1
per the BTS / FTS segment definitions in §2.15).
The splitter never drops the tail to make the numbers agree: the
mismatch is surfaced and every message is still returned; the caller decides
whether to reject.
The message carries only the declared-vs-actual integers and the unit
(message for BTS-1, batch for FTS-1): NEVER a field value, facility
identifier, or any other content, so no PHI is exposed. position references
the trailer segment (BTS/FTS) that declared the count.
Parameters
position
unit
"message" | "batch"
declared
number
actual
number
Returns
Example
import { batchCountMismatch } from "@cosyte/hl7";
const w = batchCountMismatch({ segmentIndex: 4 }, "message", 3, 2);
batchMissingTrailer()
batchMissingTrailer(
position,header,expectedTrailer):Hl7ParseWarning
Build a BATCH_MISSING_TRAILER warning. Emitted by
splitBatch() (attached to BatchSplitResult.warnings) when an envelope
header opens a scope that is never closed: a BHS batch header with no
matching BTS trailer, or an FHS file header with no matching FTS
trailer (HL7 v2 Ch. 2 §2.10.3: each envelope segment is optional, but a
profile such as an IIS file-submission spec may mandate the full frame).
splitBatch does not enforce such a rule: it splits, warns, and leaves
the accept/reject decision to the caller; the parse never throws for this.
The message carries only the header/trailer segment names: NEVER a field
value, so no PHI is exposed. position references the unmatched header
segment (FHS/BHS).
Parameters
position
header
"FHS" | "BHS"
expectedTrailer
"BTS" | "FTS"
Returns
Example
import { batchMissingTrailer } from "@cosyte/hl7";
const w = batchMissingTrailer({ segmentIndex: 0 }, "BHS", "BTS");
buildAck()
buildAck(
inbound,options):Hl7Message
Build a spec-clean ACK (MSH + MSA [+ ERR…]) responding to inbound.
Behavior:
- MSH: sender/receiver are swapped (inbound MSH-5/6 → ACK MSH-3/4;
inbound MSH-3/4 → ACK MSH-5/6); MSH-7 is the current UTC time; MSH-9 is
ACK(with the inbound trigger event echoed asACK^<trigger>^ACKwhen present); MSH-10 is a freshly generated control id; MSH-11 (processing id) and MSH-12 (version) echo the inbound values. - MSA: MSA-1 =
code; MSA-2 echoes the full inbound MSH-10 field (the raw field structure is carried over whole: a vendor-quirk id likeID^Xis never truncated to its first component). The echo carries the inbound field's escape-fidelity overlay, so an id bearing a hex escape (ID\X41\Q) or a preserved escape (\H\) echoes byte-verbatim, not canonicalized: exactly the bytes the sender put on the wire, which is what MSA-2 correlation compares. The only structural transform is trailing-empty canonicalization (D-02). (A sender that used custom encoding characters is re-delimited spec-cleanly: the overlay carries the sender's raw bytes in the sender's alphabet, so echoing them verbatim under the ACK's default alphabet would corrupt the field's structure and break correlation: the overlay is therefore bypassed and the decoded id is re-escaped under default, which re-parses back to the same control id. Default-delimiter senders, the norm, keep the byte-exact overlay echo.) - ERR: one segment per supplied
AckErrorDetail: ERR-2 location (when given), ERR-3 the Table 0357 condition code as a CWE (code^text^HL70357), ERR-4 the Table 0516 severity.
Fail-safe. If the inbound message has no MSH-10, the
ACK cannot be correlated. buildAck then leaves MSA-2 empty and, if a
positive accept (AA/CA) was requested, downgrades it to the matching
error code (AE/CE): it never fabricates an unverifiable positive ACK.
The returned message carries an ACK_NO_CORRELATION_ID warning. (This is the
inbound-side complement to @cosyte/mllp's "no commit ⇒ never AA".)
Pure aside from the generated control id + timestamp; never throws except on
a programming error (inbound not an Hl7Message, or an unknown code).
Parameters
inbound
options
Returns
Example
import { buildAck, parseHL7 } from "@cosyte/hl7";
const inbound = parseHL7(raw);
const ack = buildAck(inbound, { code: "AA" });
console.log(ack.toString()); // MSH|...\rMSA|AA|<inbound MSH-10>
parseHL7(ack.toString()).meta.type; // "ACK" (round-trips clean)
buildAdt()
buildAdt(
event,init):Hl7Message
Build a spec-clean ADT message for event (the MSH-9.2 trigger, e.g.
"A01", "A04", "A08") from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.patient / msg.visit read back the values supplied.
Parameters
event
string
the ADT trigger event (MSH-9.2). Required, non-empty.
init
the MSH envelope + typed PID/PV1/EVN content (patient
required), plus priorIdentity for a merge, move or identifier-change
event.
Returns
Throws
TypeError when event is empty, when init.patient is absent, or
when the requested trigger event's published structure requires MRG and no
prior identity carrying a merge key was supplied.
Examples
import { buildAdt, parseHL7 } from "@cosyte/hl7";
// A merge: the prior identifiers are retired in favour of the surviving ones.
const merge = buildAdt("A40", {
patient: { identifiers: { idNumber: "MRN-NEW", identifierTypeCode: "MR" } },
priorIdentity: { identifiers: { idNumber: "MRN-OLD", identifierTypeCode: "MR" } },
});
const ev = parseHL7(merge.toString()).identityEvents()[0];
ev?.prior?.identifiers[0]?.idNumber; // "MRN-OLD"
ev?.surviving?.identifiers[0]?.idNumber; // "MRN-NEW"
import { buildAdt, parseHL7 } from "@cosyte/hl7";
const msg = buildAdt("A01", {
sendingApp: "CLINIC",
receivingApp: "LAB",
patient: {
identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
name: { familyName: "Test", givenName: "Ann" },
birthDateTime: "19880705",
administrativeSex: "F",
},
visit: { patientClass: "I", assignedLocation: { pointOfCare: "ICU", room: "1", bed: "A" } },
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.patient?.familyName; // "Test"
round.structure.missingGroups; // []
buildDft()
buildDft(
event,init):Hl7Message
Build a spec-clean DFT message for event (the MSH-9.2 trigger, e.g.
"P03" post detail financial transaction, "P11" post detail financial
transactions with a new appointment) from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.charges() reads back the transactions supplied, in the order supplied.
Parameters
event
string
the DFT trigger event (MSH-9.2). Required, non-empty.
init
the MSH envelope + typed PID/FT1 content. patient and a
non-empty charges list are required.
Returns
Throws
TypeError when event is empty, when init is not an object, when
patient is absent, or when charges is absent or empty.
Example
import { buildDft, parseHL7 } from "@cosyte/hl7";
const msg = buildDft("P03", {
sendingApp: "CLINIC",
receivingApp: "BILLING",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
charges: [
{
setId: "1",
transactionDate: "20260801",
transactionType: "CG",
transactionCode: { identifier: "80053", text: "Metabolic panel" },
quantity: "1",
amountExtended: { price: "150.00", denomination: "USD" },
diagnoses: [{ identifier: "E11.9" }],
},
],
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.charges()[0]?.transactionCode?.identifier; // "80053"
round.charges()[0]?.amountExtended; // "150.00&USD": wire text, never a number
buildMdm()
buildMdm(
event,init):Hl7Message
Build a spec-clean MDM message for event (the MSH-9.2 trigger, e.g.
"T02" original document notification and content, "T06" document addendum
notification and content) from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.documents() reads back the document header and body supplied.
Parameters
event
string
the MDM trigger event (MSH-9.2). Required, non-empty.
init
the MSH envelope + typed PID/PV1/TXA content. patient and
document are required; document.body is required for the content events.
Returns
Throws
TypeError when event is empty, when init is not an object, when
patient or document is absent, or when a content event is asked for
with no body.
Example
import { buildMdm, parseHL7 } from "@cosyte/hl7";
const msg = buildMdm("T02", {
sendingApp: "TRANSCRIPTION",
receivingApp: "EHR",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
document: {
documentType: "DS",
uniqueDocumentNumber: "DOC-1001",
completionStatus: "AU",
availabilityStatus: "AV",
body: [{ setId: "1", valueType: "TX", value: "Discharge summary text." }],
},
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.documents()[0]?.completionStatus; // "AU"
round.documents()[0]?.observations[0]?.value; // "Discharge summary text."
buildMessage()
buildMessage(
init):Hl7Message
Construct an outbound Hl7Message from semantic MSH fields (SER-06).
Synthesises a complete MSH RawSegment per D-10/D-11 and hands to
new Hl7Message({...}). Callers chain .addSegment(name, fields)
(mutation method) to append PID, OBX, etc.
Defaults applied when fields are omitted (D-10):
controlId→generateControlId()(D-12)timestamp→formatHl7Timestamp(new Date())(D-13)version→"2.5"processingId→"P"sendingApp/sendingFacility/receivingApp/receivingFacility→ empty
Encoding characters are always DEFAULT_ENCODING_CHARACTERS (D-14); no
option to customise in v1.
Empty string vs. omitted field (W1): at the HL7 wire level, passing
sendingApp: "" and omitting sendingApp produce IDENTICAL output
(both emit as absent: || at the MSH-3 position). If you need to
emit an HL7 explicit null ("", the two-char literal) at a specific
position, build the message first, then use setField:
const msg = buildMessage({ type: "ADT^A01" });
msg.setField("MSH.3", '""'); // sets RawField.isNull = true
// msg.toString() now emits MSH-3 as `""` (2 chars), not as absent.
BuildMessageInit carries the same note on the input shape.
Parameters
init
Returns
Example
import { buildMessage, parseHL7 } from "@cosyte/hl7";
const msg = buildMessage({
type: "ADT^A01",
sendingApp: "CLINIC",
sendingFacility: "MAIN",
receivingApp: "LAB",
receivingFacility: "REF",
}).addSegment("PID", ["", "", "MRN123", "", "Doe^John"]);
// Spec-clean HL7 string round-trips through parseHL7:
const round = parseHL7(msg.toString());
console.log(round.meta.type); // "ADT^A01"
buildOrm()
buildOrm(
init):Hl7Message
Build a spec-clean ORM^O01 general-order message from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and msg.orders()
reads back the orders supplied, in the order supplied.
Parameters
init
the MSH envelope + typed PID/ORC/OBR content. patient and a
non-empty orders list are required.
Returns
Throws
TypeError when init is not an object, when patient is absent, or
when orders is absent or empty.
Example
import { buildOrm, parseHL7 } from "@cosyte/hl7";
const msg = buildOrm({
sendingApp: "EHR",
receivingApp: "LAB",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" } },
orders: [
{
orderControl: "NW",
setId: "1",
placerOrderNumber: "PL-1001",
universalServiceId: { identifier: "CBC", text: "Complete Blood Count" },
orderingProvider: { idNumber: "9990", familyName: "Welby" },
},
],
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.orders()[0]?.orderControl; // "NW"
round.orders()[0]?.universalServiceId?.identifier; // "CBC"
buildOru()
buildOru(
init):Hl7Message
Build a spec-clean ORU^R01 observation-result message from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.observations() reads back the results supplied.
Parameters
init
the MSH envelope + typed PID/OBR/OBX content. patient and a
non-empty observations list are required.
Returns
Throws
TypeError when patient is absent or observations is empty.
Example
import { buildOru, parseHL7 } from "@cosyte/hl7";
const msg = buildOru({
sendingApp: "LAB",
receivingApp: "EHR",
patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
name: { familyName: "Test", givenName: "Ann" } },
order: { universalServiceId: { identifier: "CBC", text: "Complete Blood Count", nameOfCodingSystem: "L" },
resultStatus: "F" },
observations: [
{ setId: "1", valueType: "NM",
identifier: { identifier: "WBC", text: "White Blood Cells", nameOfCodingSystem: "LN" },
value: "7.2", units: { identifier: "10*3/uL" }, observationResultStatus: "F" },
],
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.observations()[0]?.value; // "7.2"
buildSiu()
buildSiu(
event,init):Hl7Message
Build a spec-clean SIU message for event (the MSH-9.2 trigger, e.g.
"S12" new appointment, "S14" appointment modification, "S15"
appointment cancellation) from typed inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.appointments() reads back the identifiers, timing and resources
supplied.
Parameters
event
string
the SIU trigger event (MSH-9.2). Required, non-empty.
init
the MSH envelope + typed SCH/RGS/AI* content. appointment and
a non-empty resourceGroups list are required.
Returns
Throws
TypeError when event is empty, when init is not an object, when
appointment is absent, or when resourceGroups is absent or empty.
Example
import { buildSiu, parseHL7 } from "@cosyte/hl7";
const msg = buildSiu("S12", {
sendingApp: "SCHEDULING",
receivingApp: "EHR",
appointment: {
placerAppointmentId: "PL-1001",
fillerAppointmentId: "FL-2002",
startDateTime: "20260801090000",
endDateTime: "20260801093000",
fillerStatusCode: { identifier: "Booked" },
},
resourceGroups: [
{
setId: "1",
resources: [
{ kind: "location", code: { identifier: "OR-1" } },
{ kind: "personnel", person: { idNumber: "9990", familyName: "Welby" } },
],
},
],
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.appointments()[0]?.fillerAppointmentId; // "FL-2002"
round.appointments()[0]?.resources.map((r) => r.kind); // ["location", "personnel"]
buildVxu()
buildVxu(
init):Hl7Message
Build a spec-clean VXU^V04 vaccination-record-update message from typed
inputs.
The result is a real Hl7Message: msg.toString() serialises it,
parseHL7(msg.toString()) round-trips with zero warnings, and
msg.immunizations() reads back the doses supplied, in the order supplied.
Parameters
init
the MSH envelope + typed PID/RXA content. patient and a
non-empty immunizations list are required.
Returns
Throws
TypeError when init is not an object, when patient is absent, or
when immunizations is absent or empty.
Example
import { buildVxu, parseHL7 } from "@cosyte/hl7";
const msg = buildVxu({
sendingApp: "CLINIC",
receivingApp: "IIS",
patient: {
identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
name: { familyName: "Test", givenName: "Ann" },
},
immunizations: [
{
orderControl: "RE",
administeredDateTime: "20260801",
vaccineCode: { identifier: "115", text: "Tdap", nameOfCodingSystem: "CVX" },
doseAmount: "0.5",
doseUnits: { identifier: "mL", nameOfCodingSystem: "UCUM" },
completionStatus: "CP",
actionCode: "A",
routes: [{ route: { identifier: "IM", text: "Intramuscular" } }],
},
],
});
const round = parseHL7(msg.toString());
round.warnings.length; // 0
round.immunizations()[0]?.vaccineCode?.identifier; // "115"
canonicalCharset()
canonicalCharset(
raw):string
The canonical Table-0211 code for a label, for equality comparison (e.g. the
MSH-18-vs-options.charset ENCODING_MISMATCH check). Synonyms collapse to
one canonical string ("UNICODE UTF-8" and "UTF-8" → "UTF-8"), so a
synonym pair does not raise a false mismatch; an unrecognized label returns
its normalized form so two genuinely different labels still differ.
Parameters
raw
string
Returns
string
Example
import { canonicalCharset } from "@cosyte/hl7";
canonicalCharset("unicode utf-8") === canonicalCharset("UTF-8"); // true
codingSystem()
codingSystem(
id):CodingSystemInfo|undefined
Resolve a raw coding-system id (a CWE.3 / CE.3 "Name of Coding System"
value) to its provenance. Returns undefined when there is no claim to
resolve: id is undefined, empty, or whitespace-only.
Matching is case-insensitive and tolerant of surrounding whitespace, and
normalizes the well-known aliases in KNOWN_CODING_SYSTEMS (e.g.
"LOINC" → LN, "SNOMED" → SCT, "RxNorm" → RXN). An unrecognized
id is returned verbatim with known: false: never guessed.
Parameters
id
string | undefined
Returns
CodingSystemInfo | undefined
Example
import { codingSystem } from "@cosyte/hl7";
codingSystem("LN"); // { claimed: "LN", known: true, id: "LN", name: "LOINC" }
codingSystem("loinc"); // { claimed: "loinc", known: true, id: "LN", name: "LOINC" }
codingSystem("99zL"); // { claimed: "99zL", known: false }
codingSystem(undefined); // undefined
codingSystemOf()
codingSystemOf(
coded):CodingSystemInfo|undefined
Provenance of a coded element's primary coding system (CWE.3 / CE.3).
Returns undefined when the element claims no primary system.
Parameters
coded
Returns
CodingSystemInfo | undefined
Example
import { parseHL7, codingSystemOf } from "@cosyte/hl7";
const msg = parseHL7(raw);
for (const dg of msg.diagnoses()) {
const sys = dg.code && codingSystemOf(dg.code);
console.log(dg.code?.identifier, sys?.name ?? sys?.claimed ?? "(no system)");
}
decodeText()
decodeText(
input,enc?):string
Decode a field's escape-bearing HL7 text to its human value: the five
delimiter escapes (\F\ \S\ \T\ \R\ \E\), the truncation escape (\P),
\.br\ (→ newline), and hex (\Xdddd…) are resolved; presentational
escapes (\H\/\N, formatting, charset, vendor \Z..) are preserved
verbatim so nothing is lost. Never throws.
This is the one-call inverse of encodeText for delimiter-bearing content. It does not interpret formatting/highlight: for a normalized display string use renderText.
Parameters
input
string
the field's escape-bearing text.
enc?
EncodingCharacters = DEFAULT_ENCODING_CHARACTERS
encoding characters; defaults to the HL7 standard |^~\&.
Returns
string
the decoded value string.
Example
import { decodeText } from "@cosyte/hl7";
decodeText("Doe\\S\\John"); // "Doe^John": \S\ → component separator
decodeText("line1\\.br\\line2"); // "line1\nline2"
defineConformanceProfile()
defineConformanceProfile(
profile):ConformanceProfile
The fail-fast authoring gate for a conformance profile: a malformed
profile raises a typed ProfileDefinitionError at build time, before any
validation runs. Runs collectProfileDefects; on any defect, throws a
single ProfileDefinitionError listing every defect. On success,
returns the profile typed as ConformanceProfile.
This is optional: validateAgainstProfile tolerates a raw profile object and never throws: but it lets an author catch a typo when the profile is written rather than when it is run.
Parameters
profile
unknown
Returns
Throws
when the profile is structurally malformed.
Example
import { defineConformanceProfile } from "@cosyte/hl7";
const profile = defineConformanceProfile({
name: "example-adt-min",
segments: [{ segment: "PID", usage: "R" }],
});
// A typo throws at authoring time:
// defineConformanceProfile({ name: "x", segments: [{ segment: "pid" }] });
// → ProfileDefinitionError: segments[0].segment must be a valid segment name…
defineProfile()
defineProfile<
O>(opts):DefinedProfile<O>
Build a readonly Profile from a validated options object. Invalid
input throws ProfileDefinitionError with an actionable message
(PROF-02): bad Z-segment names (D-05), malformed date formats (D-08),
unknown top-level keys with typo hints (D-07), and missing/empty
name.
opts.extends accepts a single parent Profile or an array of them.
Parents are merged into the result: lineage is the parents' lineages
followed by this profile's own name, dateFormats, customSegments and
segmentOverrides are merged, description inherits when not supplied, and
onWarning handlers are composed. With no parent, lineage === [opts.name].
segmentOverrides names fields on STANDARD segments and is ADDITIVE ONLY: a
declaration there gives seg.get(name) a new name to resolve and changes no
existing read. Keys must be standard segment names, so a Z-segment key is
refused and pointed back at customSegments.
The returned profile CARRIES ITS DECLARED FIELD NAMES IN ITS TYPE: parse
with it and seg.get(name) on a declared segment type is checked against
the names declared for that type, a typo failing to compile instead of
silently reading undefined. That is additive, and the value stays
assignable to the general Profile interface with no cast; a caller who
annotates the variable Profile gives the narrowing up rather than hitting
an error, and one who passes an options object typed as
DefineProfileOptions never had it in the first place.
Type Parameters
O
O extends DefineProfileOptions
Parameters
opts
O
Returns
Example
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const epic = defineProfile({
name: "epic",
description: "Epic-specific quirks and ADT date formats",
dateFormats: ["MM/DD/YYYY HH:mm:ss", "MM/DD/YYYY"],
customSegments: {
ZDP: { fields: { departmentCode: 3, departmentName: 4 } },
ZRS: { fields: { resultStatus: 1, statusDateTime: 2 } },
},
});
console.log(epic.name); // "epic"
console.log(epic.lineage); // ["epic"]
console.log(epic.describe?.());
const msg = parseHL7(raw, epic);
console.log(msg.part("ZDP")?.get("departmentCode")?.value); // narrowed
// msg.part("ZDP")?.get("resultStatus"); // does not compile: that is a ZRS name
detectAckMode()
detectAckMode(
inbound):AckMode
Detect the HL7 acknowledgment mode of an inbound message from MSH-15 (accept acknowledgment type) and MSH-16 (application acknowledgment type), per HL7 v2 Chapter 2 §2.9: original when both are absent/empty; enhanced when either is present. This split is spec-exact (unlike the disposition mapping, which has no single correct model: see the package README).
Parameters
inbound
Returns
Example
import { detectAckMode, parseHL7 } from "@cosyte/hl7";
detectAckMode(parseHL7(raw)); // "original" | "enhanced"
downgradePositiveAck()
downgradePositiveAck(
code):AckCode
Downgrade a positive acknowledgment code to its matching error code,
AA → AE (original mode), CA → CE (enhanced mode). Every other code
passes through unchanged.
This is the single upstream source of truth for the fail-safe downgrade
pair: buildAck applies it when the inbound carries no MSH-10 correlation
id, and @cosyte/mllp's ack-from-hl7 adapter applies it when the inbound
cannot be parsed at all: neither ever fabricates an unverifiable positive
acknowledgment, and neither carries its own copy of the mapping.
Parameters
code
Returns
Example
import { downgradePositiveAck } from "@cosyte/hl7";
downgradePositiveAck("AA"); // "AE"
downgradePositiveAck("CA"); // "CE"
downgradePositiveAck("AR"); // "AR" (unchanged)
dtmToDate()
dtmToDate(
parts,options?):Date|undefined
Materialize an absolute-instant JS Date from DtmParts: only on
explicit caller request. Truncated fields fill to their lowest legal value
(month → January, day → 1, time → 0) for instant construction only; the
value's stated precision still tells the truth.
Timezone resolution is honest:
- has an offset → the exact instant, using that offset;
- no offset +
assumeOffsetMinutessupplied → that offset is applied; - no offset + nothing supplied →
undefined(never a silent UTC guess).
Returns undefined for an invalid value or an unresolvable zone; never
throws.
Parameters
parts
options?
DtmToDateOptions = {}
Returns
Date | undefined
Example
import { parseDtm, dtmToDate } from "@cosyte/hl7";
dtmToDate(parseDtm("20250102153045-0500"))?.toISOString();
// "2025-01-02T20:30:45.000Z": exact, offset-derived
dtmToDate(parseDtm("20250102")); // undefined: refuses to guess the zone
dtmToDate(parseDtm("20250102"), { assumeOffsetMinutes: 0 })?.toISOString();
// "2025-01-02T00:00:00.000Z": caller explicitly assumed UTC
duplicateRequiredSegment()
duplicateRequiredSegment(
position,segmentName):Hl7ParseWarning
Build a DUPLICATE_REQUIRED_SEGMENT warning. Emitted when a segment the
profile marks as singleton appears more than once (e.g. two MSH
segments). The parser keeps both; the warning alerts the consumer to
potential sender bugs.
Parameters
position
segmentName
string
Returns
Example
import { duplicateRequiredSegment } from "@cosyte/hl7";
const w = duplicateRequiredSegment({ segmentIndex: 1 }, "MSH");
emitMessageSchema()
emitMessageSchema(
target):string
Emit the named artifact for the serialized message projection, as text.
json-schema-2020-12 returns the JSON Schema document serialized with
two-space indentation and a trailing newline; zod returns the TypeScript
source text. Any other name throws SchemaTargetError and returns
nothing at all.
Pure and repeatable: no message is read, no network is touched, and two calls for the same target in one process return the identical string.
Parameters
target
string
Returns
string
Example
import { emitMessageSchema } from "@cosyte/hl7";
const schema = emitMessageSchema("json-schema-2020-12");
const zod = emitMessageSchema("zod");
schema === emitMessageSchema("json-schema-2020-12"); // true
zod.includes("import { z } from \"zod\";"); // true
encodeCe()
encodeCe(
v):RawField
Encode a CE (coded element, 6 modelled + preserved extraComponents).
Parameters
v
Returns
Example
import { encodeCe } from "@cosyte/hl7";
encodeCe({ identifier: "GLU", text: "Glucose", nameOfCodingSystem: "L" });
encodeComposite()
encodeComposite<
K>(kind,value):RawField
Encode any typed composite into a spec-clean RawField by its
CompositeKind. The single dispatcher setComposite and the typed
builders (buildAdt/buildOru) route through.
Encoding takes no encoding-characters argument on purpose: the field it produces carries the decoded component values, and the actual delimiter-escaping happens later in the serializer against the message's own encoding characters: so a composite is delimiter-independent to encode.
Type Parameters
K
K extends CompositeKind
Parameters
kind
K
value
Returns
Example
import { encodeComposite } from "@cosyte/hl7";
// A hostile family name cannot break framing on emit:
const f = encodeComposite("XPN", { familyName: "Smith^Jr", givenName: "Ann" });
encodeCompositeReps()
encodeCompositeReps<
K>(kind,values):RawField
Encode an array of typed composites into a single repeating RawField: one
HL7 repetition (~-joined on emit) per array element. Used for repeating
fields such as PID-3 (patient identifier list) and PID-11 (addresses). An
empty array yields an absent field.
Type Parameters
K
K extends CompositeKind
Parameters
kind
K
values
readonly CompositeValueByKind[K][]
Returns
Example
import { encodeCompositeReps } from "@cosyte/hl7";
const ids = encodeCompositeReps("CX", [
{ idNumber: "MRN001", identifierTypeCode: "MR" },
{ idNumber: "9990", identifierTypeCode: "SS" },
]);
encodeCwe()
encodeCwe(
v):RawField
Encode a CWE (coded element, 9 modelled + preserved extraComponents).
Parameters
v
Returns
Example
import { encodeCwe } from "@cosyte/hl7";
encodeCwe({ identifier: "GLU", text: "Glucose", nameOfCodingSystem: "LN" });
encodeCx()
encodeCx(
v):RawField
Encode a CX (identifier): component 4 is a nested HD (assigningAuthority).
Parameters
v
Returns
Example
import { encodeCx } from "@cosyte/hl7";
encodeCx({ idNumber: "MRN001", assigningAuthority: { namespaceId: "HOSP" }, identifierTypeCode: "MR" });
encodeHd()
encodeHd(
v):RawField
Encode an HD (hierarchic designator, 3 components) to a spec-clean field.
Parameters
v
Returns
Example
import { encodeHd } from "@cosyte/hl7";
encodeHd({ namespaceId: "EPIC", universalId: "1.2.840", universalIdType: "ISO" });
encodeNm()
encodeNm(
v):RawField
Encode an NM numeric. Accepts the typed NM (its raw string emitted
verbatim, preserving the sender's precision/formatting), a number, or a raw
string. A number is stringified with String(n); the value is never
reconciled or rounded.
Parameters
v
string | number | NM
Returns
Example
import { encodeNm } from "@cosyte/hl7";
encodeNm("120.50"); // precision preserved verbatim
encodePl()
encodePl(
v):RawField
Encode a PL (person location): component 4 is a nested HD (facility).
Parameters
v
Returns
Example
import { encodePl } from "@cosyte/hl7";
encodePl({ pointOfCare: "ICU", room: "101", bed: "A" });
encodeText()
encodeText(
input,enc?):string
Encode-safe direction: escape an arbitrary string so it can be placed in
an HL7 field as data without ever breaking framing. Every reserved character
: the escape char (escaped first, so decoding is unambiguous), the field,
component, subcomponent, and repetition separators, the declared truncation
char, and the framing-critical \n/\r: is replaced by its escape
sequence, so the value cannot inject a delimiter or forge a component /
subcomponent / repetition boundary.
The hard invariant, property-tested over arbitrary strings:
decodeText(encodeText(s, enc), enc) === s, and a message field carrying
encodeText(s) cannot forge a component / subcomponent / repetition
boundary or break framing: the value never escapes its field.
Two caveats are inherent to HL7 field encoding, not to this codec, and apply
to whole-field re-parse (they do not weaken the no-injection guarantee):
the two-character string "" is HL7's explicit-null token, so a field whose
entire value is "" re-parses as null; and the default parser trims field
whitespace, so a value with leading/trailing spaces re-parses trimmed. Encode
such values into a component/subcomponent position, or parse with trimming
off, to preserve them exactly.
Parameters
input
string
the arbitrary string to encode.
enc?
EncodingCharacters = DEFAULT_ENCODING_CHARACTERS
encoding characters; defaults to the HL7 standard |^~\&.
Returns
string
the spec-clean, delimiter-safe field body.
Example
import { encodeText, parseHL7 } from "@cosyte/hl7";
// A value full of delimiters cannot break out of its field:
const hostile = "a|b^c~d\\e&f";
const body = encodeText(hostile); // "a\\F\\b\\S\\c\\R\\d\\E\\e\\T\\f"
const msg = parseHL7(`MSH|^~\\&|A|B|C|D|20260101||ADT^A01|1|P|2.5\rNTE|1||${body}`);
msg.segments("NTE")[0]?.field(3).value === hostile; // true: round-trips exactly
encodeTs()
encodeTs(
v):RawField
Encode a TS/DTM timestamp. Accepts either the typed TS (its raw
string is emitted verbatim: the serializer never re-derives a timestamp
from parts) or a pre-formatted HL7 timestamp string.
Parameters
v
string | DtmParts
Returns
Example
import { encodeTs } from "@cosyte/hl7";
encodeTs("20260721101500");
encodeXad()
encodeXad(
v):RawField
Encode an XAD (12 components) to a spec-clean field.
Parameters
v
Returns
Example
import { encodeXad } from "@cosyte/hl7";
encodeXad({ street: "123 Main St", city: "Boston", stateOrProvince: "MA" });
encodeXcn()
encodeXcn(
v):RawField
Encode an XCN: component 9 is a nested HD (assigningAuthority).
Parameters
v
Returns
Example
import { encodeXcn } from "@cosyte/hl7";
encodeXcn({ idNumber: "1234567890", familyName: "Welby", identifierTypeCode: "NPI" });
encodeXpn()
encodeXpn(
v):RawField
Encode an XPN (14 components) to a spec-clean field.
Parameters
v
Returns
Example
import { encodeXpn } from "@cosyte/hl7";
encodeXpn({ familyName: "Doe", givenName: "Jane", prefix: "Dr" });
encodeXtn()
encodeXtn(
v):RawField
Encode an XTN (telecom, 12 components) to a spec-clean field.
Parameters
v
Returns
Example
import { encodeXtn } from "@cosyte/hl7";
encodeXtn({ telephoneNumber: "555-1234", telecommunicationUseCode: "PRN" });
encodingMismatch()
encodingMismatch(
position,detail):Hl7ParseWarning
Build an ENCODING_MISMATCH warning. Emitted when the MSH-2 encoding
characters declared by the sender do not match what the parser observed
downstream (e.g. the sender declares ^~\& but uses !@#$ as actual
separators in later segments).
Parameters
position
detail
string
Returns
Example
import { encodingMismatch } from "@cosyte/hl7";
const w = encodingMismatch({ segmentIndex: 0 }, "MSH-2 declares ^~\\& but segment used !@#$");
extraFields()
extraFields(
position,segmentName,extraCount):Hl7ParseWarning
Build an EXTRA_FIELDS warning. Emitted when a segment contains more
fields than the profile definition (or HL7 spec) declares: the extras
are preserved on RawSegment.fields but flagged for consumers.
Parameters
position
segmentName
string
extraCount
number
Returns
Example
import { extraFields } from "@cosyte/hl7";
const w = extraFields({ segmentIndex: 4 }, "PID", 3);
fieldWhitespaceTrimmed()
fieldWhitespaceTrimmed(
position,leadingCount,trailingCount):Hl7ParseWarning
Build a FIELD_WHITESPACE_TRIMMED warning. Emitted when the parser trims
leading or trailing whitespace from a field value (the trimFields
option, on by default). The message carries only the leading/trailing
character counts: NEVER the field value itself (before or after
trimming): so no PHI is exposed; the trimmed value is still preserved
verbatim in the parsed output.
Parameters
position
leadingCount
number
trailingCount
number
Returns
Example
import { fieldWhitespaceTrimmed } from "@cosyte/hl7";
const w = fieldWhitespaceTrimmed({ segmentIndex: 1, fieldIndex: 5 }, 2, 1);
findMessageStructureDefinition()
findMessageStructureDefinition(
messageCode,triggerEvent):MessageStructureDefinition|undefined
The registry entry behind a recognized (message code, trigger event) pair, or
undefined when the pair is not modelled. Exposed so a consumer reading a
structural warning can reach the published structure it came from.
Parameters
messageCode
string
triggerEvent
string
Returns
MessageStructureDefinition | undefined
Example
import { findMessageStructureDefinition } from "@cosyte/hl7";
const def = findMessageStructureDefinition("ORU", "R01");
console.log(def?.structureId); // "ORU_R01"
formatDtm()
formatDtm(
parts):string
Reconstruct the HL7 DTM string from DtmParts. The inverse of
parseDtm for a strict HL7 parse: formatDtm(parseDtm(s)) === s for
any well-formed s, including the byte-preserving -0000 (which HL7, unlike
RFC 3339, treats as UTC but whose sign we retain for exact round-trip).
Returns the raw string unchanged when parts.valid is false.
Emits exactly the populated precision, no zero-fill, so a year-only value
re-serializes to four characters, never YYYY0101.
Parameters
parts
Returns
string
Example
import { parseDtm, formatDtm } from "@cosyte/hl7";
formatDtm(parseDtm("198807050000")); // "198807050000"
formatDtm(parseDtm("1970")); // "1970"
getDefaultProfile()
getDefaultProfile():
Profile|undefined
Return the current default profile, or undefined if none is
registered. Consistent with msg.profile convention: undefined
rather than null.
Returns
Profile | undefined
Example
import { getDefaultProfile } from "@cosyte/hl7";
const p = getDefaultProfile();
if (p !== undefined) console.log("default profile:", p.name);
interpretAck()
interpretAck(
msg):Acknowledgment
Interpret an Hl7Message as an acknowledgment. Never throws; a message with
no MSA segment yields an all-false, empty-errors view. The result is
deeply frozen.
Parameters
msg
Returns
Example
import { interpretAck, parseHL7 } from "@cosyte/hl7";
const view = interpretAck(parseHL7("MSH|^~\\&|...\rMSA|AA|MSG001"));
view.accepted; // true
view.controlId; // "MSG001"
isPositiveAck()
isPositiveAck(
code):boolean
True iff code is a positive accept (AA/CA). Unknown/absent → false.
Parameters
code
string | undefined
Returns
boolean
Example
isPositiveAck("AA"); // true
isPositiveAck("AE"); // false
isSchemaEmitTarget()
isSchemaEmitTarget(
name): name is "json-schema-2020-12" | "zod"
Whether a name is one of the supported emission targets.
Parameters
name
string
Returns
name is "json-schema-2020-12" | "zod"
Example
import { isSchemaEmitTarget } from "@cosyte/hl7";
isSchemaEmitTarget("zod"); // true
isSchemaEmitTarget("json-schema"); // false
mergeMissingPriorOrSurvivor()
mergeMissingPriorOrSurvivor(
position,eventType,missing):Hl7ParseWarning
Build a MERGE_MISSING_PRIOR_OR_SURVIVOR warning. Emitted
by identityEvents() (a read-side helper: it attaches to the returned
IdentityEvent.warnings, never to Hl7Message.warnings) when a merge/move
trigger event (A18/A34/A35/A36/A39/A40/A41/A42/A43/A44) is missing one side
of the spec-mandated MRG (prior) → PID (surviving) pair: or when that side
carries no usable identity field: no MRG segment in the patient group, no
PID for an orphaned MRG, or a PID/MRG whose identifier, account, and visit
fields are all empty or version-gated (a v2.7+ MRG whose only content was
the withdrawn MRG-4 must not read as "nothing to retire"). The helper
surfaces whatever IS present and never
guesses the merge direction: this warning is the signal that the pair is
incomplete.
The message carries only the structural facts (trigger event code + which role is missing): NEVER an identifier, name, or any other field value, so no PHI is exposed (HL7 v2 Ch. 3: the PID carries surviving and the MRG carries non-surviving identity information).
Parameters
position
eventType
string
missing
"prior" | "survivor"
Returns
Example
import { mergeMissingPriorOrSurvivor } from "@cosyte/hl7";
const w = mergeMissingPriorOrSurvivor({ segmentIndex: 1 }, "A40", "prior");
messageJsonSchema()
messageJsonSchema():
JsonSchemaDocument
Build the JSON Schema document for the serialized message projection.
Pure and repeatable: it reads the measured shape and the warning-code registry, nothing else. It parses no message, reads no network and returns a fresh document on every call, so two calls in one process produce equal documents whatever happened between them.
Returns
Example
import { messageJsonSchema } from "@cosyte/hl7";
const schema = messageJsonSchema();
schema.$schema; // "https://json-schema.org/draft/2020-12/schema"
schema.required; // ["encodingCharacters", "segments", "warnings"]
schema.$defs["SerializedProfile"]?.additionalProperties; // false
messageZodSource()
messageZodSource():
string
Render the whole Zod module for the serialized message projection.
Pure and repeatable: it reads the measured shape and the warning-code registry, nothing else. It parses no message, reads no network, and two calls in one process return the identical string whatever happened between them.
The output declares exactly one import, whose module specifier is zod, one
const per named object in an order where a definition always precedes the
definition that references it, and one inferred type alias for the
projection.
Returns
string
Example
import { messageZodSource } from "@cosyte/hl7";
const source = messageZodSource();
source.startsWith("//"); // a header comment
source.includes('import { z } from "zod";'); // the single import
source.includes("export const SerializedMessageSchema");
missingExpectedGroup()
missingExpectedGroup(
position,messageType,groupName,anchorSegments):Hl7ParseWarning
Build a MISSING_EXPECTED_GROUP warning. Emitted once per
absent Required segment group when the message's (MSH-9.1, MSH-9.2) type is
one the structure safety net recognizes and an expected group is entirely
missing: e.g. an ORU^R01 carrying no OBR/OBX result group, the
signature of a truncated or misrouted feed. Tier-2 and additive: lenient
parse never throws on it, strict mode may promote it. The message carries
only the structural fact (message type, group name, anchor segment names),
never a field value: the message type is shape-checked before it is
interpolated, and withheld if it does not match. position references MSH-9.
Parameters
position
messageType
string
groupName
string
anchorSegments
readonly string[]
Returns
Example
import { missingExpectedGroup } from "@cosyte/hl7";
const w = missingExpectedGroup(
{ segmentIndex: 0, fieldIndex: 9 },
"ORU^R01",
"result",
["OBR", "OBX"],
);
missingRequiredField()
missingRequiredField(
position,segmentName,fieldIndex):Hl7ParseWarning
Build a MISSING_REQUIRED_FIELD warning. Emitted when a field the active
profile marks as required is empty or missing. Distinct from the
NO_MSH_SEGMENT fatal, which escalates a missing MSH altogether.
Parameters
position
segmentName
string
fieldIndex
number
Returns
Example
import { missingRequiredField } from "@cosyte/hl7";
const w = missingRequiredField({ segmentIndex: 0, fieldIndex: 3 }, "MSH", 3);
missingRequiredSegment()
missingRequiredSegment(
position,messageType,segmentName,structureId):Hl7ParseWarning
Build a MISSING_EXPECTED_GROUP warning naming one absent required SEGMENT.
This is what the parser emits: once per segment the published structure
definition gives a minimum of one and the message does not carry, e.g. an
ADT^A01 with no EVN. It shares the code with
missingExpectedGroup on purpose. The code is public contract that
consumers narrow on, and the change here is which structural fact the
message names, not which class of finding it is.
Tier-2 and additive: lenient parse never throws on it, strict mode may
promote it. The message carries only structural identifiers (the message
type, the segment name, the published structure id), never a field value:
the message type is shape-checked before it is interpolated and withheld if
it does not match, and the other two are registry data rather than anything
read off the message. position references MSH-9.
Parameters
position
messageType
string
segmentName
string
structureId
string
Returns
Example
import { missingRequiredSegment } from "@cosyte/hl7";
const w = missingRequiredSegment(
{ segmentIndex: 0, fieldIndex: 9 },
"ADT^A01",
"EVN",
"ADT_A01",
);
mllpFramingStripped()
mllpFramingStripped(
position):Hl7ParseWarning
Build a MLLP_FRAMING_STRIPPED warning. Emitted once per parse when the
preprocessor detects and removes MLLP framing bytes (0x0B / 0x1C /
trailing 0x0D) from the input.
Parameters
position
Returns
Example
import { mllpFramingStripped } from "@cosyte/hl7";
const w = mllpFramingStripped({ segmentIndex: 0 });
outOfOrderSegment()
outOfOrderSegment(
position,segmentName):Hl7ParseWarning
Build an OUT_OF_ORDER_SEGMENT warning. Emitted when a segment appears
outside the order the active profile declares (e.g. EVN appearing
before MSH in a typical ADT message).
Parameters
position
segmentName
string
Returns
Example
import { outOfOrderSegment } from "@cosyte/hl7";
const w = outOfOrderSegment({ segmentIndex: 2 }, "EVN");
parseCe()
parseCe(
rep,enc):CE
Parse an HL7 v2 CE repetition into a structured CE object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics).
Parameters
rep
enc
Returns
Example
import { parseCe, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["GLU"] },
{ subcomponents: ["Glucose"] },
{ subcomponents: ["LN"] },
] };
const ce = parseCe(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(ce.identifier); // "GLU"
parseCwe()
parseCwe(
rep,enc):CWE
Parse an HL7 v2 CWE repetition into a structured CWE object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics).
Parameters
rep
enc
Returns
Example
import { parseCwe, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["GLU"] },
{ subcomponents: ["Glucose"] },
{ subcomponents: ["LN"] },
] };
const cwe = parseCwe(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(cwe.identifier); // "GLU"
parseCx()
parseCx(
rep,enc):CX
Parse an HL7 v2 CX repetition into a structured CX object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics). Component 4
(assigningAuthority) is parsed as a nested HD; see component table in
the CX interface JSDoc for the v1 simplifications on components 6/9/10.
Parameters
rep
enc
Returns
Example
import { parseCx, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["123"] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: ["EPIC", "1.2.840.114350", "ISO"] },
{ subcomponents: ["MR"] },
] };
const cx = parseCx(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(cx.idNumber); // "123"
console.log(cx.assigningAuthority?.namespaceId); // "EPIC"
parseDtm()
parseDtm(
raw):DtmParts
Parse an HL7 v2 TS/DTM string into typed DtmParts, preserving the
stated precision and timezone fidelity. Never zero-fills a truncation,
never coerces to a Date, and never assumes UTC for a missing offset.
Returns { raw, valid: false, hasTimezone: false } (no parts) for empty,
malformed, or calendar-out-of-range input: never throws. A fractional
component is only accepted at full second precision.
Parameters
raw
string
Returns
Example
import { parseDtm } from "@cosyte/hl7";
parseDtm("1970");
// { raw: "1970", valid: true, precision: "year", year: 1970, hasTimezone: false }
parseDtm("20250102153045.5-0500");
// precision "fraction", fractionalSeconds "5", hasTimezone true, offsetMinutes -300
parseDtm("not-a-date");
// { raw: "not-a-date", valid: false, hasTimezone: false }
parseHd()
parseHd(
rep,enc):HD
Parse an HL7 v2 HD repetition into a structured HD object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics).
Parameters
rep
enc
Returns
Example
import { parseHd, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["EPIC"] },
{ subcomponents: ["1.2.840.114350"] },
{ subcomponents: ["ISO"] },
] };
const hd = parseHd(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(hd.namespaceId); // "EPIC"
console.log(hd.universalIdType); // "ISO"
parseHL7()
Internal
implementation signature; overload signatures above carry the public JSDoc + @example.
Call Signature
parseHL7(
raw):Hl7Message
Parse a raw HL7 v2 message (string or Buffer) into an Hl7Message.
The parser is lenient by default: recoverable deviations from the HL7
spec are reported via msg.warnings and (optionally)
options.onWarning but do not throw. Four unrecoverable structural
errors throw Hl7ParseError: NO_MSH_SEGMENT, MSH_TOO_SHORT,
INVALID_ENCODING_CHARACTERS, EMPTY_INPUT. Opt into strict mode
with { strict: true } to escalate every Tier-2 warning into an
Hl7ParseError.
Parameters
raw
string | Buffer<ArrayBufferLike>
Returns
Examples
import { parseHL7, WARNING_CODES } from "@cosyte/hl7";
const msg = parseHL7(
"MSH|^~\\&|APP|FAC|APP|FAC|20250101||ADT^A01|1|P|2.5\rPID|||123",
);
console.log(msg.version); // "2.5"
console.log(msg.warnings.length); // 0
for (const w of msg.warnings) {
if (w.code === WARNING_CODES.MLLP_FRAMING_STRIPPED) {
// handle MLLP-framed sender
}
}
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// A statically known profile narrows the field-name reader per segment type:
const msg = parseHL7(raw, vendor);
console.log(msg.part("ZDP")?.get("departmentCode")?.value);
// msg.part("ZDP")?.get("departmentCod"); // does not compile: not declared
Call Signature
parseHL7<
P>(raw,profile):ProfiledMessage<P>
Parse a raw HL7 v2 message (string or Buffer) into an Hl7Message.
The parser is lenient by default: recoverable deviations from the HL7
spec are reported via msg.warnings and (optionally)
options.onWarning but do not throw. Four unrecoverable structural
errors throw Hl7ParseError: NO_MSH_SEGMENT, MSH_TOO_SHORT,
INVALID_ENCODING_CHARACTERS, EMPTY_INPUT. Opt into strict mode
with { strict: true } to escalate every Tier-2 warning into an
Hl7ParseError.
Type Parameters
P
P extends Profile
Parameters
raw
string | Buffer<ArrayBufferLike>
profile
P
Returns
Examples
import { parseHL7, WARNING_CODES } from "@cosyte/hl7";
const msg = parseHL7(
"MSH|^~\\&|APP|FAC|APP|FAC|20250101||ADT^A01|1|P|2.5\rPID|||123",
);
console.log(msg.version); // "2.5"
console.log(msg.warnings.length); // 0
for (const w of msg.warnings) {
if (w.code === WARNING_CODES.MLLP_FRAMING_STRIPPED) {
// handle MLLP-framed sender
}
}
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// A statically known profile narrows the field-name reader per segment type:
const msg = parseHL7(raw, vendor);
console.log(msg.part("ZDP")?.get("departmentCode")?.value);
// msg.part("ZDP")?.get("departmentCod"); // does not compile: not declared
Call Signature
parseHL7<
P>(raw,options):ProfiledMessage<P>
Parse a raw HL7 v2 message (string or Buffer) into an Hl7Message.
The parser is lenient by default: recoverable deviations from the HL7
spec are reported via msg.warnings and (optionally)
options.onWarning but do not throw. Four unrecoverable structural
errors throw Hl7ParseError: NO_MSH_SEGMENT, MSH_TOO_SHORT,
INVALID_ENCODING_CHARACTERS, EMPTY_INPUT. Opt into strict mode
with { strict: true } to escalate every Tier-2 warning into an
Hl7ParseError.
Type Parameters
P
P extends Profile
Parameters
raw
string | Buffer<ArrayBufferLike>
options
ParseOptions & object
Returns
Examples
import { parseHL7, WARNING_CODES } from "@cosyte/hl7";
const msg = parseHL7(
"MSH|^~\\&|APP|FAC|APP|FAC|20250101||ADT^A01|1|P|2.5\rPID|||123",
);
console.log(msg.version); // "2.5"
console.log(msg.warnings.length); // 0
for (const w of msg.warnings) {
if (w.code === WARNING_CODES.MLLP_FRAMING_STRIPPED) {
// handle MLLP-framed sender
}
}
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// A statically known profile narrows the field-name reader per segment type:
const msg = parseHL7(raw, vendor);
console.log(msg.part("ZDP")?.get("departmentCode")?.value);
// msg.part("ZDP")?.get("departmentCod"); // does not compile: not declared
Call Signature
parseHL7(
raw,options):Hl7Message
Parse a raw HL7 v2 message (string or Buffer) into an Hl7Message.
The parser is lenient by default: recoverable deviations from the HL7
spec are reported via msg.warnings and (optionally)
options.onWarning but do not throw. Four unrecoverable structural
errors throw Hl7ParseError: NO_MSH_SEGMENT, MSH_TOO_SHORT,
INVALID_ENCODING_CHARACTERS, EMPTY_INPUT. Opt into strict mode
with { strict: true } to escalate every Tier-2 warning into an
Hl7ParseError.
Parameters
raw
string | Buffer<ArrayBufferLike>
options
Returns
Examples
import { parseHL7, WARNING_CODES } from "@cosyte/hl7";
const msg = parseHL7(
"MSH|^~\\&|APP|FAC|APP|FAC|20250101||ADT^A01|1|P|2.5\rPID|||123",
);
console.log(msg.version); // "2.5"
console.log(msg.warnings.length); // 0
for (const w of msg.warnings) {
if (w.code === WARNING_CODES.MLLP_FRAMING_STRIPPED) {
// handle MLLP-framed sender
}
}
import { defineProfile, parseHL7 } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// A statically known profile narrows the field-name reader per segment type:
const msg = parseHL7(raw, vendor);
console.log(msg.part("ZDP")?.get("departmentCode")?.value);
// msg.part("ZDP")?.get("departmentCod"); // does not compile: not declared
parseNm()
parseNm(
rep,_enc):NM
Parse an HL7 v2 NM repetition into { raw, value }. Uses Number(raw)
for strict numeric parsing: trailing non-numeric characters produce
NaN, normalized to undefined. Empty raw also produces undefined.
Number("") is 0 in JS: which is the wrong answer for an empty HL7
numeric field. The explicit empty-string check below returns
{ raw: "", value: undefined } so empty inputs match missing inputs.
Parameters
rep
_enc
Returns
Example
import { parseNm, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [{ subcomponents: ["120.5"] }] };
const nm = parseNm(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(nm.value); // 120.5
parsePath()
parsePath(
path):DotPath
Parse an HL7 dot-path string into a DotPath descriptor. Accepts shapes
like SEG, SEG[n], SEG.N, SEG.N[r], SEG.N.C, SEG.N.C.S, and all
combinations up to SEG[n].N[r].C.S. Throws TypeError with the offending
path string on any malformed input.
Parameters
path
string
Returns
Example
import { parsePath } from "@cosyte/hl7";
parsePath("PID.5.1"); // { segmentType: "PID", segmentIndex: 0, fieldIndex: 5, componentIndex: 1 }
parsePath("OBX[2].5"); // { segmentType: "OBX", segmentIndex: 2, fieldIndex: 5 }
parsePath("PID.3[1].1"); // { segmentType: "PID", segmentIndex: 0, fieldIndex: 3, repetitionIndex: 1, componentIndex: 1 }
parsePl()
parsePl(
rep,enc):PL
Parse an HL7 v2 PL repetition into a structured PL object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics). Component 4
(facility) is parsed as a nested HD; see component table in the PL
interface JSDoc for the v1 simplifications.
Parameters
rep
enc
Returns
Example
import { parsePl, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["ICU"] },
{ subcomponents: ["101"] },
{ subcomponents: ["A"] },
{ subcomponents: ["HOSP", "1.2.3", "UUID"] },
] };
const pl = parsePl(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(pl.pointOfCare); // "ICU"
console.log(pl.facility?.namespaceId); // "HOSP"
parseSn()
parseSn(
rep,enc):SN|undefined
Parse an HL7 v2 SN repetition into a structured SN, or undefined when the
field carries no usable structured-numeric content (empty, or so malformed
that no comparator, number, or separator can be recovered). Components are
returned verbatim (already decoded by the tokenizer); num1/num2 use strict Number() parsing.
Fail-safe: a non-operator value in the comparator slot (SN.1) is dropped
rather than surfaced as a relation, and a non-numeric SN.2/SN.4 becomes
undefined: the parser never emits a confident wrong comparator or number.
Parameters
rep
enc
Returns
SN | undefined
Example
import { parseSn, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: [">"] },
{ subcomponents: ["90"] },
] };
const sn = parseSn(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(sn?.comparator, sn?.num1); // ">" 90
parseStream()
Internal
implementation signature; overloads above carry the public JSDoc.
Call Signature
parseStream(
source):AsyncGenerator<StreamMessageEntry<Hl7Message>,void,void>
Incrementally parse a chunked HL7 v2 byte / text stream, yielding one
StreamMessageEntry per MSH-delimited message as its boundary
completes, with O(one-message) memory: the whole stream is never
retained. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] batch frame (HL7 v2 Ch. 2 §2.10.3):
- a message split across chunk boundaries (mid-segment, mid-field, even
mid-
MSH|^~\&) is reassembled correctly: feeding the same bytes in 1-byte chunks vs. one chunk yields identical messages; \r,\r\n, and\nsegment terminators are all tolerated (a\r\nsplit across a chunk boundary is not mistaken for a bare\r);- each message is parsed by the shipped parseHL7 (no second grammar),
okentries carry theHl7Message, a Tier-3 fatal is an isolated failure entry; a malformed message never suppresses later messages; - batch-envelope segments (
FHS/BHS/BTS/FTS) are treated as boundaries and never yielded as messages, soyielded count == MSH count(envelope count reconciliation is splitBatch's job); - a final message with no trailing terminator is still yielded, flagged with a stream-level unterminatedStreamMessage warning: never a throw, the tail is never dropped.
The second argument, when given, is forwarded verbatim to parseHL7 for
each message (profile, strict, charset, dateFormats, …), exactly as
splitBatch forwards it.
Parameters
source
Returns
AsyncGenerator<StreamMessageEntry<Hl7Message>, void, void>
Examples
import { parseStream } from "@cosyte/hl7";
async function* chunks() {
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r";
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r";
}
let count = 0;
for await (const entry of parseStream(chunks())) {
if (entry.ok) count += 1; // 2: one per MSH, streamed, released each time
}
import { defineProfile, parseStream } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for await (const entry of parseStream(source, vendor)) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
parseStream<
P>(source,profile):AsyncGenerator<StreamMessageEntry<ProfiledMessage<P>>,void,void>
Incrementally parse a chunked HL7 v2 byte / text stream, yielding one
StreamMessageEntry per MSH-delimited message as its boundary
completes, with O(one-message) memory: the whole stream is never
retained. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] batch frame (HL7 v2 Ch. 2 §2.10.3):
- a message split across chunk boundaries (mid-segment, mid-field, even
mid-
MSH|^~\&) is reassembled correctly: feeding the same bytes in 1-byte chunks vs. one chunk yields identical messages; \r,\r\n, and\nsegment terminators are all tolerated (a\r\nsplit across a chunk boundary is not mistaken for a bare\r);- each message is parsed by the shipped parseHL7 (no second grammar),
okentries carry theHl7Message, a Tier-3 fatal is an isolated failure entry; a malformed message never suppresses later messages; - batch-envelope segments (
FHS/BHS/BTS/FTS) are treated as boundaries and never yielded as messages, soyielded count == MSH count(envelope count reconciliation is splitBatch's job); - a final message with no trailing terminator is still yielded, flagged with a stream-level unterminatedStreamMessage warning: never a throw, the tail is never dropped.
The second argument, when given, is forwarded verbatim to parseHL7 for
each message (profile, strict, charset, dateFormats, …), exactly as
splitBatch forwards it.
Type Parameters
P
P extends Profile
Parameters
source
profile
P
Returns
AsyncGenerator<StreamMessageEntry<ProfiledMessage<P>>, void, void>
Examples
import { parseStream } from "@cosyte/hl7";
async function* chunks() {
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r";
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r";
}
let count = 0;
for await (const entry of parseStream(chunks())) {
if (entry.ok) count += 1; // 2: one per MSH, streamed, released each time
}
import { defineProfile, parseStream } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for await (const entry of parseStream(source, vendor)) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
parseStream<
P>(source,options):AsyncGenerator<StreamMessageEntry<ProfiledMessage<P>>,void,void>
Incrementally parse a chunked HL7 v2 byte / text stream, yielding one
StreamMessageEntry per MSH-delimited message as its boundary
completes, with O(one-message) memory: the whole stream is never
retained. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] batch frame (HL7 v2 Ch. 2 §2.10.3):
- a message split across chunk boundaries (mid-segment, mid-field, even
mid-
MSH|^~\&) is reassembled correctly: feeding the same bytes in 1-byte chunks vs. one chunk yields identical messages; \r,\r\n, and\nsegment terminators are all tolerated (a\r\nsplit across a chunk boundary is not mistaken for a bare\r);- each message is parsed by the shipped parseHL7 (no second grammar),
okentries carry theHl7Message, a Tier-3 fatal is an isolated failure entry; a malformed message never suppresses later messages; - batch-envelope segments (
FHS/BHS/BTS/FTS) are treated as boundaries and never yielded as messages, soyielded count == MSH count(envelope count reconciliation is splitBatch's job); - a final message with no trailing terminator is still yielded, flagged with a stream-level unterminatedStreamMessage warning: never a throw, the tail is never dropped.
The second argument, when given, is forwarded verbatim to parseHL7 for
each message (profile, strict, charset, dateFormats, …), exactly as
splitBatch forwards it.
Type Parameters
P
P extends Profile
Parameters
source
options
ParseOptions & object
Returns
AsyncGenerator<StreamMessageEntry<ProfiledMessage<P>>, void, void>
Examples
import { parseStream } from "@cosyte/hl7";
async function* chunks() {
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r";
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r";
}
let count = 0;
for await (const entry of parseStream(chunks())) {
if (entry.ok) count += 1; // 2: one per MSH, streamed, released each time
}
import { defineProfile, parseStream } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for await (const entry of parseStream(source, vendor)) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
parseStream(
source,options):AsyncGenerator<StreamMessageEntry<Hl7Message>,void,void>
Incrementally parse a chunked HL7 v2 byte / text stream, yielding one
StreamMessageEntry per MSH-delimited message as its boundary
completes, with O(one-message) memory: the whole stream is never
retained. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] batch frame (HL7 v2 Ch. 2 §2.10.3):
- a message split across chunk boundaries (mid-segment, mid-field, even
mid-
MSH|^~\&) is reassembled correctly: feeding the same bytes in 1-byte chunks vs. one chunk yields identical messages; \r,\r\n, and\nsegment terminators are all tolerated (a\r\nsplit across a chunk boundary is not mistaken for a bare\r);- each message is parsed by the shipped parseHL7 (no second grammar),
okentries carry theHl7Message, a Tier-3 fatal is an isolated failure entry; a malformed message never suppresses later messages; - batch-envelope segments (
FHS/BHS/BTS/FTS) are treated as boundaries and never yielded as messages, soyielded count == MSH count(envelope count reconciliation is splitBatch's job); - a final message with no trailing terminator is still yielded, flagged with a stream-level unterminatedStreamMessage warning: never a throw, the tail is never dropped.
The second argument, when given, is forwarded verbatim to parseHL7 for
each message (profile, strict, charset, dateFormats, …), exactly as
splitBatch forwards it.
Parameters
source
options
Returns
AsyncGenerator<StreamMessageEntry<Hl7Message>, void, void>
Examples
import { parseStream } from "@cosyte/hl7";
async function* chunks() {
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r";
yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r";
}
let count = 0;
for await (const entry of parseStream(chunks())) {
if (entry.ok) count += 1; // 2: one per MSH, streamed, released each time
}
import { defineProfile, parseStream } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for await (const entry of parseStream(source, vendor)) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
parseTs()
parseTs(
rep,_enc,dateFormats?):DtmParts
Parse an HL7 v2 TS/DTM repetition into fidelity TS parts. The
canonical HL7 shape is tried first; dateFormats, when the caller declared
any, is then tried in order and a match reports itself on matchedFormat.
Nothing else is tried: the library's built-in fallback list is reserved for
the non-composite msg.meta.timestamp path, because guessing MM/DD/YYYY
on a date of birth a vendor wrote day-first produces a plausible wrong date
rather than a visible failure.
dateFormats reaches this parser from Hl7Message.dateFormats, which is
ParseOptions.dateFormats followed by the applied profile's, deduplicated
first-occurrence-wins. Omit it (or pass an empty list) and the parse is
exactly the strict HL7 one.
The result is frozen so the immutability guarantee holds for callers that destructure or retain it.
Parameters
rep
_enc
dateFormats?
readonly string[]
Returns
Example
import { parseTs, DEFAULT_ENCODING_CHARACTERS, dtmToDate } from "@cosyte/hl7";
const rep = { components: [{ subcomponents: ["20250102153045-0500"] }] };
const ts = parseTs(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(ts.raw); // "20250102153045-0500"
console.log(ts.precision, ts.hasTimezone); // "second" true
console.log(dtmToDate(ts)?.toISOString()); // "2025-01-02T20:30:45.000Z"
const vendor = { components: [{ subcomponents: ["07/05/1988"] }] };
const dob = parseTs(vendor, DEFAULT_ENCODING_CHARACTERS, ["MM/DD/YYYY"]);
console.log(dob.month, dob.matchedFormat); // 7 "MM/DD/YYYY"
parseXad()
parseXad(
rep,enc):XAD
Parse an HL7 v2 XAD repetition into a structured XAD object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics).
Parameters
rep
enc
Returns
Example
import { parseXad, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["123 Main St"] },
{ subcomponents: ["Apt 4"] },
{ subcomponents: ["Boston"] },
{ subcomponents: ["MA"] },
{ subcomponents: ["02101"] },
] };
const addr = parseXad(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(addr.city); // "Boston"
parseXcn()
parseXcn(
rep,enc):XCN
Parse an HL7 v2 XCN repetition into a structured XCN object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics). Component 9
(assigningAuthority) is parsed as a nested HD; see component table in
the XCN interface JSDoc for the v1 trimming.
Parameters
rep
enc
Returns
Example
import { parseXcn, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["1234567890"] },
{ subcomponents: ["Smith"] },
{ subcomponents: ["Jane"] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: ["HOSP", "1.2.3", "ISO"] },
{ subcomponents: ["L"] },
{ subcomponents: [""] },
{ subcomponents: [""] },
{ subcomponents: ["NPI"] },
] };
const xcn = parseXcn(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(xcn.idNumber); // "1234567890"
console.log(xcn.familyName); // "Smith"
console.log(xcn.assigningAuthority?.namespaceId); // "HOSP"
console.log(xcn.identifierTypeCode); // "NPI"
parseXpn()
parseXpn(
rep,enc):XPN
Parse an HL7 v2 XPN repetition into a structured XPN object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics).
Parameters
rep
enc
Returns
Example
import { parseXpn, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["Smith"] },
{ subcomponents: ["Jane"] },
] };
const xpn = parseXpn(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(xpn.familyName); // "Smith"
console.log(xpn.givenName); // "Jane"
parseXtn()
parseXtn(
rep,enc):XTN
Parse an HL7 v2 XTN repetition into a structured XTN object. Components
are returned verbatim (already decoded once by the tokenizer: never
re-unescaped). Absent / empty components are OMITTED
from the result (exactOptionalPropertyTypes semantics). Components past
position 12 are silently ignored in v1.
Parameters
rep
enc
Returns
Example
import { parseXtn, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const rep = { components: [
{ subcomponents: ["555-1234"] },
{ subcomponents: ["PRN"] },
{ subcomponents: ["PH"] },
] };
const xtn = parseXtn(rep, DEFAULT_ENCODING_CHARACTERS);
console.log(xtn.telephoneNumber); // "555-1234"
pickMrn()
pickMrn(
identifiers):string|undefined
Pick the Medical Record Number string from a list of PID-3 CX identifiers.
D-07: prefer the first CX whose identifierTypeCode === "MR" (HL7 v2.5+
canonical MRN marker). D-10: the match is case-SENSITIVE: lowercase
"mr" does NOT match; spec mandates uppercase.
D-08: when no MR-typed identifier is found, fall back to the first CX's
idNumber. Returns undefined when the first CX has no idNumber (even
if later CXs do: the fallback is strictly "first CX", not "first CX with
idNumber", because we need a deterministic answer).
D-21: no warning emitted. Callers who need strict MR resolution can walk
patient.identifiers themselves.
Parameters
identifiers
readonly CX[]
Returns
string | undefined
Example
import { pickMrn } from "@cosyte/hl7";
pickMrn([
{ idNumber: "X1" },
{ idNumber: "MRN001", identifierTypeCode: "MR" },
]);
// → "MRN001"
pickMrn([{ idNumber: "X1" }]);
// → "X1" (fallback: no MR entry)
pickMrn([]);
// → undefined
reescape()
reescape(
input,enc):string
Re-escape reserved characters back into their \X\ forms so the serializer
can emit spec-clean HL7. This is the inverse of unescape for every
delimiter-bearing character; round-trip cleanliness
(unescape(reescape(x, enc), enc, emit, pos) === x) is a documented
property covered by tests.
The characters re-escaped:
enc.escape → \E
enc.field → \F
enc.component → \S
enc.subcomponent → \T
enc.repetition → \R
enc.truncation → \P\ (only when MSH-2 declared one, v2.7+)
"\n" (LF) → .br
"\r" (CR) → \X0D\ (a decoded CR is the HL7 segment separator;
emitting it raw would corrupt wire framing, so
it re-encodes to its hex escape: see below)
Lossy by construction for the non-delimiter escape families. reescape
only knows about the reserved characters above: it cannot reconstruct a
recognize-and-preserve escape (\H\, formatting, charset, \Z..) or the
original bytes of a hex escape (\X41\ decoded to A; casing of \X0d),
because those decode to ordinary characters that carry no "I was an escape"
marker. Byte-verbatim emit for those families is the serializer's job via
the RawComponent.rawSubcomponents overlay (see escapeFidelityRaw);
reescape is the fallback for content that has no overlay (constructed
values, Field-level re-escapes).
Iteration uses for..of, which walks Unicode code points (not UTF-16 code
units), so user-supplied content containing non-BMP characters round-trips
correctly without special surrogate-pair handling.
Parameters
input
string
enc
Returns
string
Example
import { reescape, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
reescape("Smith|John", DEFAULT_ENCODING_CHARACTERS); // "Smith\\F\\John"
reescape("line1\nline2", DEFAULT_ENCODING_CHARACTERS); // "line1\\.br\\line2"
renderText()
renderText(
input,enc?,opts?):RenderedText
Render an HL7 v2 escape/formatting-bearing string into a normalized display model (plain text + highlight-aware runs). A read projection: it never mutates the underlying field, and it never fabricates: an escape it cannot render is preserved as literal characters and flagged in RenderedText.unrenderedSequences.
input is the field's escape-bearing text. Pass the wire form (e.g.
Field.text, byte-verbatim for parsed content) for the most faithful
result: a wire \E\H\E\ (an escaped literal backslash-H-backslash) then
renders as the three literal characters \H, never as a highlight. Passing
an already-decoded Field.value also works: its \.br\ is already a
newline (rendered as one line break) and its \H/formatting sentinels are
still recognized.
Never throws for any input.
Parameters
input
string
the escape-bearing field text to render.
enc?
EncodingCharacters = DEFAULT_ENCODING_CHARACTERS
the message's encoding characters (for \F\/\S/… targets);
defaults to the HL7 standard |^~\&.
opts?
see RenderTextOptions.
Returns
the normalized RenderedText.
Example
import { renderText } from "@cosyte/hl7";
const r = renderText("Specimen received.\\.br\\Gross exam \\H\\normal\\N\\.");
r.text;
// "Specimen received.\nGross exam normal."
r.runs;
// [ { text: "Specimen received.\nGross exam ", highlighted: false },
// { text: "normal", highlighted: true },
// { text: ".", highlighted: false } ]
r.unrenderedSequences; // []
resolveCharset()
resolveCharset(
raw):CharsetResolution
Resolve an MSH-18 label (or an options.charset override) to a
CharsetResolution. A blank / undefined label resolves to the ASCII
default. A recognized Table-0211 code returns its registry entry. An
unrecognized label resolves to a verbatim, recognized: false outcome so
the caller preserves bytes and emits UNKNOWN_CHARSET.
Parameters
raw
string | undefined
Returns
Example
import { resolveCharset } from "@cosyte/hl7";
resolveCharset("UNICODE UTF-8"); // { canonical: "UTF-8", treatment: "decode", ... }
resolveCharset("ISO IR87"); // { canonical: "ISO IR87", treatment: "verbatim", recognized: true }
resolveCharset("WINDOWS-1252"); // { canonical: "WINDOWS-1252", treatment: "verbatim", recognized: false }
resolvePath()
resolvePath(
path,segments,_enc):string|undefined
Resolve a dot-path string against a raw segment tree to its decoded
leaf value. Returns undefined whenever the path does not resolve (missing
segment, out-of-range field/component/subcomponent/repetition), and never
throws on a missing value. Throws TypeError only when path itself is
malformed: callers relying on "never throws" should pre-validate or wrap
in try/catch.
Parameters
path
string
segments
readonly RawSegment[]
_enc
Returns
string | undefined
Example
import { parseHL7, resolvePath } from "@cosyte/hl7";
const msg = parseHL7(raw);
resolvePath("PID.5.1", msg.rawSegments, msg.encodingCharacters); // "Smith"
resolvePath("NOT.9", msg.rawSegments, msg.encodingCharacters); // undefined
segmentCase()
segmentCase(
position,observed):Hl7ParseWarning
Build a SEGMENT_CASE warning. Emitted when a segment identifier carries an
ASCII lowercase letter (e.g. pid instead of PID, or Obx instead of
OBX), which no segment identifier in the HL7 v2 standard does. The parser accepts the
segment and resolves it as the segment it names, so msg.segments("PID"),
msg.patient and observations() all see it; the warning is how a consumer
learns the sender is non-conforming.
"Resolved as", not "rewritten to": the original spelling stays on
RawSegment.name and is what serialization re-emits, so the byte-verbatim
round-trip is unaffected.
Parameters
position
observed
string
Returns
Example
import { segmentCase } from "@cosyte/hl7";
const w = segmentCase({ segmentIndex: 3 }, "pid");
setDefaultProfile()
setDefaultProfile(
profile):void
Register a process-scoped default profile. parseHL7(raw) (with no
explicit profile arg) consults getDefaultProfile() and applies the
returned profile if any. Pass null (or undefined) to clear.
Effects are IDENTICAL to passing the profile explicitly as the second
arg of parseHL7 (D-20): customSegments, dateFormats, onWarning chain,
and profile attribution all apply the same way.
Explicit args ALWAYS win: parseHL7(raw, myProfile) uses myProfile
regardless of the default; parseHL7(raw, { profile: null }) opts out
of the default for a single call without changing the registered
default.
Test hygiene: This is the ONLY mutable module-scoped state in the
library. Tests that call setDefaultProfile MUST clean up in
afterEach (setDefaultProfile(null)) or default-profile bleed will
infect subsequent tests.
Parameters
profile
Profile | null
Returns
void
Example
import { setDefaultProfile, profiles, parseHL7 } from "@cosyte/hl7";
// Set once at app startup
setDefaultProfile(profiles.epic);
// Every parseHL7 call in the app now uses profiles.epic unless
// another profile is passed explicitly.
const msg = parseHL7(raw);
console.log(msg.profile?.name); // "epic"
// Clear when done (or in test teardown):
setDefaultProfile(null);
splitBatch()
Internal
implementation signature; overloads above carry the public JSDoc.
Call Signature
splitBatch(
raw):BatchSplitResult
Split a raw HL7 v2 batch / file stream into its individual messages plus
the envelope metadata. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] frame (HL7 v2 Ch. 2 §2.10.3):
- handles a file with multiple batches and a batch with multiple messages;
- a bare single message (no envelope) passes straight through as one entry;
- a malformed message mid-batch is isolated (returned as a typed failure entry): its siblings are still returned, the tail is never dropped;
- reconciles BTS-1 (batch message count) and FTS-1 (file batch count) and emits batchCountMismatch on a mismatch: counts only, never PHI;
- emits batchMissingTrailer when a
BHS/FHSheader opens a scope noBTS/FTScloses: a warning, never a throw (the caller decides to reject).
The second argument, when given, is forwarded verbatim to parseHL7
for each message (profile, strict, charset, dateFormats, …). Under
strict, a message that would warn surfaces as a failure entry (still
isolated). splitBatch itself never throws: an empty stream yields an empty
result.
Parameters
raw
string | Buffer<ArrayBufferLike>
Returns
Examples
import { splitBatch } from "@cosyte/hl7";
const { messages, warnings } = splitBatch(
"FHS|^~\\&|SENDER\r" +
"BHS|^~\\&|SENDER\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r" +
"BTS|2\rFTS|1\r",
);
console.log(messages.length); // 2
for (const entry of messages) {
if (entry.ok) console.log(entry.message.version); // "2.5"
}
console.log(warnings.length); // 0: declared counts match
import { defineProfile, splitBatch } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for (const entry of splitBatch(rawBatchFile, vendor).messages) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
splitBatch<
P>(raw,profile):BatchSplitResult<ProfiledMessage<P>>
Split a raw HL7 v2 batch / file stream into its individual messages plus
the envelope metadata. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] frame (HL7 v2 Ch. 2 §2.10.3):
- handles a file with multiple batches and a batch with multiple messages;
- a bare single message (no envelope) passes straight through as one entry;
- a malformed message mid-batch is isolated (returned as a typed failure entry): its siblings are still returned, the tail is never dropped;
- reconciles BTS-1 (batch message count) and FTS-1 (file batch count) and emits batchCountMismatch on a mismatch: counts only, never PHI;
- emits batchMissingTrailer when a
BHS/FHSheader opens a scope noBTS/FTScloses: a warning, never a throw (the caller decides to reject).
The second argument, when given, is forwarded verbatim to parseHL7
for each message (profile, strict, charset, dateFormats, …). Under
strict, a message that would warn surfaces as a failure entry (still
isolated). splitBatch itself never throws: an empty stream yields an empty
result.
Type Parameters
P
P extends Profile
Parameters
raw
string | Buffer<ArrayBufferLike>
profile
P
Returns
BatchSplitResult<ProfiledMessage<P>>
Examples
import { splitBatch } from "@cosyte/hl7";
const { messages, warnings } = splitBatch(
"FHS|^~\\&|SENDER\r" +
"BHS|^~\\&|SENDER\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r" +
"BTS|2\rFTS|1\r",
);
console.log(messages.length); // 2
for (const entry of messages) {
if (entry.ok) console.log(entry.message.version); // "2.5"
}
console.log(warnings.length); // 0: declared counts match
import { defineProfile, splitBatch } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for (const entry of splitBatch(rawBatchFile, vendor).messages) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
splitBatch<
P>(raw,options):BatchSplitResult<ProfiledMessage<P>>
Split a raw HL7 v2 batch / file stream into its individual messages plus
the envelope metadata. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] frame (HL7 v2 Ch. 2 §2.10.3):
- handles a file with multiple batches and a batch with multiple messages;
- a bare single message (no envelope) passes straight through as one entry;
- a malformed message mid-batch is isolated (returned as a typed failure entry): its siblings are still returned, the tail is never dropped;
- reconciles BTS-1 (batch message count) and FTS-1 (file batch count) and emits batchCountMismatch on a mismatch: counts only, never PHI;
- emits batchMissingTrailer when a
BHS/FHSheader opens a scope noBTS/FTScloses: a warning, never a throw (the caller decides to reject).
The second argument, when given, is forwarded verbatim to parseHL7
for each message (profile, strict, charset, dateFormats, …). Under
strict, a message that would warn surfaces as a failure entry (still
isolated). splitBatch itself never throws: an empty stream yields an empty
result.
Type Parameters
P
P extends Profile
Parameters
raw
string | Buffer<ArrayBufferLike>
options
ParseOptions & object
Returns
BatchSplitResult<ProfiledMessage<P>>
Examples
import { splitBatch } from "@cosyte/hl7";
const { messages, warnings } = splitBatch(
"FHS|^~\\&|SENDER\r" +
"BHS|^~\\&|SENDER\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r" +
"BTS|2\rFTS|1\r",
);
console.log(messages.length); // 2
for (const entry of messages) {
if (entry.ok) console.log(entry.message.version); // "2.5"
}
console.log(warnings.length); // 0: declared counts match
import { defineProfile, splitBatch } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for (const entry of splitBatch(rawBatchFile, vendor).messages) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
Call Signature
splitBatch(
raw,options):BatchSplitResult
Split a raw HL7 v2 batch / file stream into its individual messages plus
the envelope metadata. Demarcates by MSH boundaries inside the optional
[FHS] { [BHS] { MSH… } [BTS] } [FTS] frame (HL7 v2 Ch. 2 §2.10.3):
- handles a file with multiple batches and a batch with multiple messages;
- a bare single message (no envelope) passes straight through as one entry;
- a malformed message mid-batch is isolated (returned as a typed failure entry): its siblings are still returned, the tail is never dropped;
- reconciles BTS-1 (batch message count) and FTS-1 (file batch count) and emits batchCountMismatch on a mismatch: counts only, never PHI;
- emits batchMissingTrailer when a
BHS/FHSheader opens a scope noBTS/FTScloses: a warning, never a throw (the caller decides to reject).
The second argument, when given, is forwarded verbatim to parseHL7
for each message (profile, strict, charset, dateFormats, …). Under
strict, a message that would warn surfaces as a failure entry (still
isolated). splitBatch itself never throws: an empty stream yields an empty
result.
Parameters
raw
string | Buffer<ArrayBufferLike>
options
Returns
Examples
import { splitBatch } from "@cosyte/hl7";
const { messages, warnings } = splitBatch(
"FHS|^~\\&|SENDER\r" +
"BHS|^~\\&|SENDER\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r" +
"MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r" +
"BTS|2\rFTS|1\r",
);
console.log(messages.length); // 2
for (const entry of messages) {
if (entry.ok) console.log(entry.message.version); // "2.5"
}
console.log(warnings.length); // 0: declared counts match
import { defineProfile, splitBatch } from "@cosyte/hl7";
const vendor = defineProfile({
name: "vendor",
customSegments: { ZDP: { fields: { departmentCode: 3 } } },
});
// The profile is forwarded to every message, narrowing each one's reader:
for (const entry of splitBatch(rawBatchFile, vendor).messages) {
if (entry.ok) console.log(entry.message.part("ZDP")?.get("departmentCode")?.value);
}
supportsBuilderMessage()
supportsBuilderMessage(
messageCode,triggerEvent):boolean
Can a typed builder author this (message code, trigger event) pair as a spec-clean, zero-warning message? A published entry whose trigger event is the empty string matches any trigger event for that message code.
A false answer does not mean the builder refuses the event: a builder that
takes a trigger event still emits the content supplied. It means the library
does not claim the result is structurally complete.
Parameters
messageCode
string
MSH-9.1, e.g. "SIU".
triggerEvent
string
MSH-9.2, e.g. "S12".
Returns
boolean
Example
import { supportsBuilderMessage } from "@cosyte/hl7";
supportsBuilderMessage("SIU", "S12"); // true
supportsBuilderMessage("ADT", "A20"); // false: the structure requires NPU
timestampFallbackFormat()
timestampFallbackFormat(
position,matchedFormat):Hl7ParseWarning
Build a TIMESTAMP_FALLBACK_FORMAT warning: a date/time value that did not
parse as strict HL7 but did match a fallback format.
This is the warning's constructor, not its emit site. The only code path
that can raise it is the lenient cascade behind msg.meta.timestamp, and
only when a caller hands that cascade an emit callback, so a parse does not
put this warning on msg.warnings today. Read matchedFormat on the TS
to see which format answered a value; a typed datetime field never consults
the built-in fallbacks at all.
Parameters
position
matchedFormat
string
Returns
Example
import { timestampFallbackFormat } from "@cosyte/hl7";
const w = timestampFallbackFormat(
{ segmentIndex: 1, fieldIndex: 7 },
"YYYY-MM-DD",
);
toDate()
toDate(
value,options?):Date|undefined
Materialize an absolute-instant JS Date from a parsed HL7 datetime, ONLY
when the zone is determinate. Delegates to dtmToDate, so the rule is
the one this parser has always applied:
- the value carries an explicit offset: the exact instant from THAT offset,
and
options.assumeOffsetMinutesis ignored; - no offset and
assumeOffsetMinutessupplied as a finite number: that offset is applied, including an explicit0meaning "treat this naive value as UTC"; - no offset and no usable option:
undefined. The host timezone is NEVER read and UTC is NEVER assumed.
"Usable" is checked rather than assumed, because a published package is
called from JavaScript as well as from TypeScript. An options bag that is
null, or an assumeOffsetMinutes that is not a finite number, names no
zone: it answers undefined rather than coercing to one. A string "0", a
true and an [] all multiply to a number in JavaScript, so a converter
that simply arithmetics them hands back an instant the caller never asked
for, and 0 in particular is silent UTC.
The offset the VALUE states is held to the same account, one step earlier:
it wins outright over any assumption, so it is checked to be a zone before
it is allowed to win. A hasTimezone value whose offsetMinutes is a
string, a boolean, an array, NaN or a count of minutes a whole day or
more from UTC states no zone either, and coercing it would fabricate exactly
the confident instant this function exists to refuse.
Components below the stated precision fill to their lowest legal value
(month to 1, day to 1, time to 0) FOR INSTANT CONSTRUCTION ONLY; the value's
stated precision is unchanged, and a later toObject or toISO on the same
value returns exactly what it returned before. A four-digit year below 100
stays that year: 0050 is year 50, never 1950.
Returns undefined for an invalid value, an unresolvable zone, a value
whose stated components do not name a real calendar date or a real zone,
and for undefined / null. Never throws, for any input, on either
parameter. An impossible day is refused rather than rolled into the
following month, so no instant this returns is a day away from the value the
sender wrote.
Parameters
value
DtmParts | null | undefined
options?
DtmToDateOptions | null
Returns
Date | undefined
Example
import { parseDtm, toDate } from "@cosyte/hl7";
toDate(parseDtm("20250102")); // undefined: refuses to guess
toDate(parseDtm("20250102"), { assumeOffsetMinutes: 0 })?.toISOString();
// "2025-01-02T00:00:00.000Z": the caller chose UTC
toDate(parseDtm("20250102153045-0500"))?.toISOString();
// "2025-01-02T20:30:45.000Z": exact, offset-derived
toDate(parseDtm("20240230"), { assumeOffsetMinutes: 0 });
// undefined: February has no 30th, and 1 March is not what was written
toISO()
toISO(
value):string|undefined
Render a parsed HL7 datetime as ISO-8601 TRUNCATED TO ITS STATED PRECISION, never padded out: a year-precision value renders four characters, a day-precision value renders ten. Fractional digits are rendered VERBATIM as stated, neither padded to three nor rounded.
An explicit offset is appended as Z when it is exactly zero (including
HL7's -0000), otherwise as +HH:MM / -HH:MM. When the value carried NO
offset, NOTHING is appended: the string is deliberately zone-less and no Z
is fabricated. Because a zero offset renders Z, this is NOT a byte round
trip of the wire value and is not meant to be; formatDtm is the
round-trip route and is unchanged.
Returns undefined for a value the parser marked invalid, for a value with
no stated year (the HL7 DTM datatype mandates a leading four-digit year, so
this parser produces no time-only value), for a value whose stated
components do not name a real calendar date or a real zone, and for
undefined / null. Never throws. A string this returns is always one an
ISO-8601 reader reads back: "2024-02-30" is never rendered, because every
reader silently moves it to 1 March, and neither is an offset past
+23:59, because every reader answers an Invalid Date for it.
Parameters
value
DtmParts | null | undefined
Returns
string | undefined
Example
import { parseDtm, toISO } from "@cosyte/hl7";
toISO(parseDtm("1970")); // "1970"
toISO(parseDtm("19700705")); // "1970-07-05": no fabricated Z
toISO(parseDtm("20250102153045.5-0500")); // "2025-01-02T15:30:45.5-05:00"
toISO(parseDtm("20250102153045-0000")); // "2025-01-02T15:30:45Z"
toISO(parseDtm("20230229")); // undefined: 2023 is not a leap year
toISO(parseDtm("20250102153045+2400")); // undefined: no zone is 24 hours east
toObject()
toObject(
value):DateParts|undefined
Project a parsed HL7 datetime onto the shared DateParts shape: a frozen plain object carrying ONLY the calendar components the value stated.
Returns undefined for a value the parser marked invalid, for a value
stating no components at all, for a value whose stated components do not
name a real calendar date (20240230 names no day: February has no 30th)
or a real zone (an offset has to be a whole number of minutes within a day
of UTC), and for undefined / null. Never throws. Every value in the
object it returns is therefore a finite number, which is what makes
offsetMinutes safe to read as minutes east of UTC.
The value's own stated precision is untouched by the call: this is a projection, not a conversion of the parsed value.
Parameters
value
DtmParts | null | undefined
Returns
DateParts | undefined
Example
import { parseDtm, toObject } from "@cosyte/hl7";
toObject(parseDtm("19880705"));
// { year: 1988, month: 7, day: 5 }
toObject(parseDtm("20250102153045.0500-0430"));
// { year: 2025, month: 1, day: 2, hour: 15, minute: 30, second: 45,
// millisecond: 50, offsetMinutes: -270 }
toObject(parseDtm("not-a-date")); // undefined
toObject(parseDtm("20240230")); // undefined: February has no 30th
toObject(parseDtm("20240229")); // { year: 2024, month: 2, day: 29 }: 2024 is a leap year
unescape()
unescape(
input,enc,emit,position):string
Expand HL7 escape sequences (\F\, \S, \T\, \R, \E\, \.br,
\X..\, vendor-specific \Z..) inside a field, component, or
subcomponent string. The escape delimiter comes from enc.escape (default
``), so senders using a non-default escape character are handled
transparently.
Unknown or malformed sequences are preserved VERBATIM in the output and
emit an UNKNOWN_ESCAPE_SEQUENCE warning via emit. Unterminated escapes
(an escape character with no closing partner before end-of-input) are also
preserved in full and warn once: the scan is strictly O(n) and cannot
infinite-loop on malformed input.
Parameters
input
string
enc
emit
(w) => void
position
Returns
string
Example
import { unescape, DEFAULT_ENCODING_CHARACTERS } from "@cosyte/hl7";
const warnings: Array<unknown> = [];
const out = unescape(
"patient\\F\\name\\.br\\DOB",
DEFAULT_ENCODING_CHARACTERS,
(w) => warnings.push(w),
{ segmentIndex: 1, fieldIndex: 5 },
);
// out === "patient|name\nDOB"
unknownCharset()
unknownCharset(
position,requested):Hl7ParseWarning
Build an UNKNOWN_CHARSET warning. Emitted when MSH-18 (or an
options.charset override) declares a value that is not a recognized HL7
Table-0211 character set. The parser never guesses an encoding: it reads the
raw bytes as latin1 (a 1:1 byte mapping) so single-byte content stays
recoverable, rather than corrupting it with replacement characters. The
message carries the charset code only, never a decoded field value: the code
is shape-checked before it is interpolated, and withheld if it does not match.
Parameters
position
requested
string
Returns
Example
import { unknownCharset } from "@cosyte/hl7";
const w = unknownCharset({ segmentIndex: 0, fieldIndex: 18 }, "ISO IR 999");
unknownEscapeSequence()
unknownEscapeSequence(
position,body):Hl7ParseWarning
Build an UNKNOWN_ESCAPE_SEQUENCE warning for a terminated escape
sequence (\..\) whose body is not a recognized HL7 escape. The message
NEVER embeds the escape body: only its length and, when the body's first
character is a recognized HL7 escape-identifier letter (structural HL7
grammar, not PHI: e.g. Z for a vendor escape, . for a formatting
escape), that single letter. A body that doesn't start with a known escape
letter is untrusted field text and names no character at all. The sequence
itself is still preserved verbatim in the parsed output: only the WARNING
message is PHI-safe.
Parameters
position
body
string
Returns
Example
import { unknownEscapeSequence } from "@cosyte/hl7";
const w = unknownEscapeSequence({ segmentIndex: 2, fieldIndex: 3 }, "Z99");
// message: `Unknown HL7 escape sequence of type "Z" (3 chars) preserved verbatim.`
unknownSegment()
unknownSegment(
position,segmentName):Hl7ParseWarning
Build an UNKNOWN_SEGMENT warning. Emitted when a segment identifier is
not in the HL7 spec's standard set and not registered in the active
profile's customSegments.
The message states that the comparison ignored case, because the parser
folds a segment name to its canonical ASCII-uppercase spelling before
looking it up. An earlier wording read "not in HL7 spec and no profile
claim" while the lookup was case-SENSITIVE, so a sender shipping obx was
told its segment was absent from the standard. OBX is in the standard;
the real fault was the case, and the message sent the integrator looking
for a missing spec entry that was never missing.
Parameters
position
segmentName
string
Returns
Example
import { unknownSegment } from "@cosyte/hl7";
const w = unknownSegment({ segmentIndex: 7 }, "ZZZ");
unsupportedCharset()
unsupportedCharset(
position,code):Hl7ParseWarning
Build an UNSUPPORTED_CHARSET warning. Emitted when a recognized
Table-0211 character set is not decoded into text: either because this parser
does not decode it (the multibyte / ISO-2022-switched East-Asian sets: JIS,
GB 18030, KS X 1001, CNS 11643, BIG-5: and the wide Unicode transforms
UTF-16 / UTF-32), or because a strict decode of a decodable set failed (a
byte invalid / undefined for the declared set, or an ICU build lacking the
label). In every case the raw bytes are read as latin1, never guessed at, so
the parser does not emit replacement-char-corrupted text. Single-byte content
stays byte-recoverable; multibyte content is best-effort (a content byte can
coincide with a structural delimiter: see the parser's known-limitations).
The switch escapes (\Cxxyy\ / \Mxxyyzz) are recognized and preserved by
the escape layer; full stateful decoding is a documented non-goal. The message
carries the charset code only, never a decoded field value: the code is
shape-checked before it is interpolated, and withheld if it does not match.
Parameters
position
code
string
Returns
Example
import { unsupportedCharset } from "@cosyte/hl7";
const w = unsupportedCharset({ segmentIndex: 0, fieldIndex: 18 }, "ISO IR87");
unterminatedEscapeSequence()
unterminatedEscapeSequence(
position):Hl7ParseWarning
Build an UNKNOWN_ESCAPE_SEQUENCE warning for an unterminated escape
(an escape character with no closing partner before end-of-input). The
"body" in this case is the entire remainder of the field, so the message
carries NEITHER the body NOR its length (the length of a truncated tail is
itself derivable field-shape information): just the fact that an
unterminated escape was found, and the position. The remainder is still
preserved verbatim in the parsed output.
Parameters
position
Returns
Example
import { unterminatedEscapeSequence } from "@cosyte/hl7";
const w = unterminatedEscapeSequence({ segmentIndex: 2, fieldIndex: 3 });
// message: `Unterminated HL7 escape sequence preserved verbatim.`
unterminatedStreamMessage()
unterminatedStreamMessage(
position):Hl7ParseWarning
Build an UNTERMINATED_STREAM_MESSAGE warning. Emitted by
parseStream(): attached to a StreamMessageEntry's streamWarnings,
NOT to Hl7Message.warnings: when the final message in a stream ends
without a segment terminator (its last segment ran to end-of-stream with no
trailing \r/\r\n/\n). The message is still parsed and yielded in
full; this is the fail-safe signal that the tail may have been truncated
mid-message (a cut-off feed), never a reason to drop it and never a throw.
Only the last message can be unterminated: every earlier message is closed by
the terminator that precedes the next MSH/envelope boundary.
The message carries only the structural fact, NEVER a field value, so no PHI
is exposed. position references the message's MSH segment index.
Parameters
position
Returns
Example
import { unterminatedStreamMessage } from "@cosyte/hl7";
const w = unterminatedStreamMessage({ segmentIndex: 6 });
usageOutcomes()
usageOutcomes(
rule):UsageOutcomes|undefined
The true and false usage outcomes a rule's declared conditional records, as
two separate simple codes: for C(RE/X), { whenTrue: "RE", whenFalse: "X" }.
Returns undefined when the rule's usage is a simple code or is absent,
which is how it reports "this rule has no outcomes". It never throws,
including for a value forced through any.
Parameters
rule
a segment rule or a field rule from a conformance profile.
Returns
UsageOutcomes | undefined
the two outcomes, or undefined when the rule declares none.
Example
import { usageOutcomes, type FieldRule } from "@cosyte/hl7";
const conditional: FieldRule = { field: 8, usage: "C(RE/X)" };
usageOutcomes(conditional); // { whenTrue: "RE", whenFalse: "X" }
const simple: FieldRule = { field: 8, usage: "R" };
usageOutcomes(simple); // undefined (a simple usage records no outcomes)
validateAgainstProfile()
validateAgainstProfile(
message,profile,resolutions?):ConformanceResult
Validate a parsed HL7 v2 message against a user-authored declarative conformance profile and return typed findings.
Never throws. A well-formed profile is evaluated rule by rule; a
malformed profile is reported as PROFILE_MALFORMED findings (the engine
does not validate against a profile it cannot trust: never a silent pass).
Either way you get a ConformanceResult. For fail-fast authoring, run
defineConformanceProfile first: it throws on a malformed profile.
findings.length === 0 is NOT a conformance attestation. It means every
rule the profile declared was satisfied: nothing about the parts of the
message the profile did not cover, and nothing about clinical correctness.
No PHI in findings: each finding names the structural locus (segment / field / component / repetition) and the rule, never the offending value.
Read-only: the message is never mutated.
A conditional usage takes its outcome from one source, never two. A rule
that declares a ConditionPredicate on its condition has it
evaluated against the message, and the true or false outcome selects the
usage the rest of the rule is checked under: the outcomes a declared
conditional such as C(R/X) writes down, or the ones IHE states for the bare
C and CE codes. A rule that declares none takes the outcome of a matching
UsageResolution in resolutions instead. Declaring both at one locus
is PROFILE_MALFORMED. With neither, the rule is evaluated exactly as a C
rule is: presence not evaluated, every other constraint applied as usual.
A predicate the message cannot decide is reported, never guessed. It
yields a PROFILE_CONDITION_UNEVALUATABLE finding and the element's presence
is not assessed, so an unassessed conditional can never hide inside an empty
findings list.
A value set may be bound to the coding system its codes come from. A rule
that declares FieldRule.codingSystem has the message's own
coding-system component compared against it by resolved identity, per present
repetition, and a mismatch is PROFILE_CODING_SYSTEM_MISMATCH: a distinct
code, so "a code we do not accept" and "a code that looks right but claims
the wrong system" stop being one answer. The check is additive and opt-in: a
rule that declares no binding behaves exactly as it always has. Still no code
set and still no network call, because this compares the sender's CLAIM
against the profile and never the code against a terminology.
A field rule may declare what its COMPONENTS are, and doing so closes the
set. Each entry of components carries a 1-indexed index and an optional
usage code whose cardinality is IMPLIED rather than declared (R is
[1..1], RE and O are [0..1], X is [0..0]; a component has no
repetition construct, so each reduces to presence). Per present repetition,
R with nothing at that component is PROFILE_REQUIRED_ABSENT and X with
something there is PROFILE_NOT_PERMITTED, both at a locus naming the
component. Content at an index the rule does NOT declare is
PROFILE_UNDECLARED_CONTENT, one finding per undeclared index per
repetition: the methodology counts content in an element the profile never
specified as a conformance violation rather than harmless extra content. A
field rule that declares no components declares no depth and is checked
exactly as it always was, so this is additive and opt-in per rule.
Every result carries the ProfileLevel in force, beside the
profile name, because an empty findings list means two different things at
two different levels. A profile may declare
ConformanceProfile.level; one that declares none is assessed and
echoed as constrainable, the weaker claim, so silence never reads as
implementable. A profile that DOES claim implementable is held to the
level's own terminus at profile-shape time: every segment, field and
component rule must declare a usage, and that usage must be R, RE or X
or a declared conditional whose outcomes are drawn from those three. A claim
that fails is PROFILE_MALFORMED, and a refused claim is never the level
echoed on the result. The level changes no message check at all: it decides
which findings the PROFILE is refused for, never which findings a MESSAGE
produces.
Omitting resolutions (or passing undefined) is the same as passing an
empty list. Any OTHER value that is not a list of well-formed resolutions is
a PROFILE_MALFORMED finding, never read as "no resolutions": a mis-typed
argument must not return a clean result for elements nothing checked.
Parameters
message
a parsed message from parseHL7.
profile
the consumer's declarative ConformanceProfile.
resolutions?
readonly UsageResolution[]
optional caller-supplied outcomes for declared-conditional rules that declare no predicate.
Returns
the profile name, the level in force, and the ordered findings (empty ⇒ no declared rule violated).
Example
import { parseHL7, validateAgainstProfile, type ConformanceProfile } from "@cosyte/hl7";
const profile: ConformanceProfile = {
name: "example-adt-min",
segments: [
{ segment: "PID", usage: "R", fields: [
{ field: 3, name: "Patient Identifiers", usage: "R" },
{
field: 8,
name: "Administrative Sex",
usage: "C(RE/X)",
// "Required but may be empty when a birth date was sent, else not permitted."
condition: { location: { segment: "PID", field: 7 }, presence: "is valued" },
valueSet: ["M", "F", "U"],
},
] },
],
};
const { findings } = validateAgainstProfile(parseHL7(raw), profile);
for (const f of findings) console.log(f.severity, f.code, f.message);
// findings.length === 0 ⇒ no declared rule violated (NOT an attestation)
validateMessageStructure()
validateMessageStructure(
message):StructureValidationResult
Validate a parsed message against HL7's own published structure for its trigger event: segment order, segment occurrence counts, and segments the published structure does not name.
Opt-in and read-only. Nothing calls it for you, it never throws, and it leaves the message exactly as it found it: same segments, same warnings, same serialization.
Check validated before you read findings. A message type the registry
does not model, one recognized only through a retained transcription, and one
carrying no readable message type at all each come back with validated: false, a reason, and no findings: that is "the publication cannot answer",
not "the message is fine".
Zero findings is not a conformance attestation. It means this message did not break the published structure in the three ways checked here. Field content, datatypes, value sets and HL7 tables are all unchecked, the covered message codes are twelve rather than the whole standard, and the publication behind it is vendored at a fixed commit.
Parameters
message
a parsed message. Input the parser rejects never reaches here: the parse fails first, with the fatal error it always raised.
Returns
Example
import { parseHL7, validateMessageStructure } from "@cosyte/hl7";
const result = validateMessageStructure(parseHL7(raw));
result.validated; // false for a message type the registry does not model
result.structureId; // e.g. "ADT_A01-A": the variant the findings are against
result.findings.map((f) => f.code);
versionMismatch()
versionMismatch(
position,declared,expected):Hl7ParseWarning
Build a VERSION_MISMATCH warning. Emitted when MSH-12 declares an HL7
version that does not match what the active profile or ParseOptions
expected.
Parameters
position
declared
string
expected
string
Returns
Example
import { versionMismatch } from "@cosyte/hl7";
const w = versionMismatch({ segmentIndex: 0, fieldIndex: 12 }, "2.9", "2.5");