@pritiranjan/hl7v2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +434 -0
- package/dist/chunk-Q6NISXHR.js +130 -0
- package/dist/chunk-Q6NISXHR.js.map +1 -0
- package/dist/index.cjs +344 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +309 -0
- package/dist/index.d.ts +309 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/schema-qr-scwmL.d.cts +106 -0
- package/dist/schema-qr-scwmL.d.ts +106 -0
- package/dist/segments/index.cjs +636 -0
- package/dist/segments/index.cjs.map +1 -0
- package/dist/segments/index.d.cts +457 -0
- package/dist/segments/index.d.ts +457 -0
- package/dist/segments/index.js +532 -0
- package/dist/segments/index.js.map +1 -0
- package/package.json +100 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { InvalidHL7Error, parseHL7DateTime, SegmentNotFoundError, decodeEscapes, HL7ParseError } from './chunk-Q6NISXHR.js';
|
|
2
|
+
export { HL7ParseError, InvalidHL7Error, SegmentNotFoundError, decodeEscapes, encodeEscapes, formatHL7Date, formatHL7DateTime, parseHL7DateTime } from './chunk-Q6NISXHR.js';
|
|
3
|
+
|
|
4
|
+
// src/schema.ts
|
|
5
|
+
var DEFAULT_ENCODING = {
|
|
6
|
+
field: "|",
|
|
7
|
+
component: "^",
|
|
8
|
+
repetition: "~",
|
|
9
|
+
escape: "\\",
|
|
10
|
+
subComponent: "&"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/parser.ts
|
|
14
|
+
function parseEncodingChars(mshLine) {
|
|
15
|
+
const field = mshLine[3];
|
|
16
|
+
const component = mshLine[4];
|
|
17
|
+
const repetition = mshLine[5];
|
|
18
|
+
const escape = mshLine[6];
|
|
19
|
+
const subComponent = mshLine[7];
|
|
20
|
+
if (!field || !component || !repetition || !escape || !subComponent) {
|
|
21
|
+
throw new InvalidHL7Error(
|
|
22
|
+
`MSH encoding characters are incomplete \u2014 expected 5 chars at positions 3-7, got: "${mshLine.slice(3, 8)}"`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return { field, component, repetition, escape, subComponent };
|
|
26
|
+
}
|
|
27
|
+
function parseField(raw, enc) {
|
|
28
|
+
return raw.split(enc.repetition).map(
|
|
29
|
+
(rep) => rep.split(enc.component).map(
|
|
30
|
+
(comp) => comp.split(enc.subComponent)
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function parseMSHSegment(line, enc) {
|
|
35
|
+
const parts = line.split(enc.field);
|
|
36
|
+
const fields = [
|
|
37
|
+
// MSH.1 — the field separator character itself
|
|
38
|
+
[[[enc.field]]],
|
|
39
|
+
// MSH.2 — encoding characters verbatim; must NOT be split on component separator
|
|
40
|
+
[[[parts[1] ?? ""]]],
|
|
41
|
+
// MSH.3 onwards — normal fields
|
|
42
|
+
...parts.slice(2).map((f) => parseField(f, enc))
|
|
43
|
+
];
|
|
44
|
+
return { id: "MSH", fields, raw: line };
|
|
45
|
+
}
|
|
46
|
+
function parseGenericSegment(line, enc, lineNum) {
|
|
47
|
+
const sepIdx = line.indexOf(enc.field);
|
|
48
|
+
const id = sepIdx === -1 ? line : line.slice(0, sepIdx);
|
|
49
|
+
if (!/^[A-Z][A-Z0-9]{2}$/.test(id)) {
|
|
50
|
+
throw new HL7ParseError(
|
|
51
|
+
`Invalid segment identifier "${id}" at line ${String(lineNum)} \u2014 expected 3 alphanumeric characters (A-Z, 0-9) starting with a letter`,
|
|
52
|
+
{ line: lineNum, segmentId: id }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const fieldStrings = sepIdx === -1 ? [] : line.slice(sepIdx + 1).split(enc.field);
|
|
56
|
+
return { id, fields: fieldStrings.map((f) => parseField(f, enc)), raw: line };
|
|
57
|
+
}
|
|
58
|
+
function mshValue(seg, fieldNum) {
|
|
59
|
+
return seg.fields[fieldNum - 1]?.[0]?.[0]?.[0] ?? "";
|
|
60
|
+
}
|
|
61
|
+
function parseMessageType(field) {
|
|
62
|
+
const firstRep = field[0] ?? [];
|
|
63
|
+
const struct = firstRep[2]?.[0];
|
|
64
|
+
return {
|
|
65
|
+
type: firstRep[0]?.[0] ?? "",
|
|
66
|
+
event: firstRep[1]?.[0] ?? "",
|
|
67
|
+
structure: struct !== void 0 && struct !== "" ? struct : void 0
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function detectLineEnding(raw) {
|
|
71
|
+
if (raw.includes("\r\n")) return "\r\n";
|
|
72
|
+
if (raw.includes("\r")) return "\r";
|
|
73
|
+
return "\n";
|
|
74
|
+
}
|
|
75
|
+
function parse(raw, options = {}) {
|
|
76
|
+
if (!raw || !raw.trim()) {
|
|
77
|
+
throw new InvalidHL7Error("Input is empty");
|
|
78
|
+
}
|
|
79
|
+
const normalised = raw.replace(/\r\n/g, "\r").replace(/\n/g, "\r");
|
|
80
|
+
const lines = normalised.split("\r").filter((l) => l.trim().length > 0);
|
|
81
|
+
const firstLine = lines[0];
|
|
82
|
+
if (!firstLine?.startsWith("MSH")) {
|
|
83
|
+
throw new InvalidHL7Error(
|
|
84
|
+
`First non-blank line must be an MSH segment, got: "${(firstLine ?? "").slice(0, 30)}"`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (firstLine.length < 8) {
|
|
88
|
+
throw new InvalidHL7Error(
|
|
89
|
+
"MSH segment is too short to contain encoding characters (minimum length: 8)"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const encoding = parseEncodingChars(firstLine);
|
|
93
|
+
const segments2 = lines.map((line, idx) => {
|
|
94
|
+
if (line.startsWith("MSH")) return parseMSHSegment(line, encoding);
|
|
95
|
+
return parseGenericSegment(line, encoding, idx + 1);
|
|
96
|
+
});
|
|
97
|
+
const msh = segments2[0];
|
|
98
|
+
if (!msh) throw new InvalidHL7Error("No MSH segment could be parsed");
|
|
99
|
+
const shouldDecode = options.decodeEscapes === true;
|
|
100
|
+
const decode = (v) => shouldDecode ? decodeEscapes(v, encoding) : v;
|
|
101
|
+
const msgTypeField = msh.fields[8] ?? [];
|
|
102
|
+
return {
|
|
103
|
+
version: decode(mshValue(msh, 12)),
|
|
104
|
+
messageType: parseMessageType(msgTypeField),
|
|
105
|
+
messageControlId: decode(mshValue(msh, 10)),
|
|
106
|
+
timestamp: parseHL7DateTime(mshValue(msh, 7)),
|
|
107
|
+
sendingApplication: decode(mshValue(msh, 3)),
|
|
108
|
+
sendingFacility: decode(mshValue(msh, 4)),
|
|
109
|
+
receivingApplication: decode(mshValue(msh, 5)),
|
|
110
|
+
receivingFacility: decode(mshValue(msh, 6)),
|
|
111
|
+
processingId: mshValue(msh, 11),
|
|
112
|
+
segments: segments2,
|
|
113
|
+
encoding,
|
|
114
|
+
raw: raw.trim(),
|
|
115
|
+
lineEnding: detectLineEnding(raw)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function isHL7(input) {
|
|
119
|
+
return /^MSH[^A-Za-z0-9]/.test(input.trimStart());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/encoder.ts
|
|
123
|
+
function encodeField(field, enc) {
|
|
124
|
+
return field.map(
|
|
125
|
+
(rep) => rep.map((comp) => comp.join(enc.subComponent)).join(enc.component)
|
|
126
|
+
).join(enc.repetition);
|
|
127
|
+
}
|
|
128
|
+
function encodeMSHSegment(seg, enc) {
|
|
129
|
+
const encodingCharsStr = enc.component + enc.repetition + enc.escape + enc.subComponent;
|
|
130
|
+
const rest = seg.fields.slice(2).map((f) => encodeField(f, enc)).join(enc.field);
|
|
131
|
+
return `MSH${enc.field}${encodingCharsStr}${enc.field}${rest}`;
|
|
132
|
+
}
|
|
133
|
+
function encodeSegment(seg, enc) {
|
|
134
|
+
if (seg.id === "MSH") return encodeMSHSegment(seg, enc);
|
|
135
|
+
const fields = seg.fields.map((f) => encodeField(f, enc)).join(enc.field);
|
|
136
|
+
return `${seg.id}${enc.field}${fields}`;
|
|
137
|
+
}
|
|
138
|
+
function encode(msg, options = {}) {
|
|
139
|
+
const lineEnding = options.lineEnding ?? msg.lineEnding;
|
|
140
|
+
const enc = msg.encoding;
|
|
141
|
+
const lines = msg.segments.map((seg) => encodeSegment(seg, enc));
|
|
142
|
+
const result = lines.join(lineEnding);
|
|
143
|
+
return options.trailingNewline === true ? result + lineEnding : result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/query.ts
|
|
147
|
+
function segment(msg, id, options = {}) {
|
|
148
|
+
const upper = id.toUpperCase();
|
|
149
|
+
const idx = (options.segmentIndex ?? 1) - 1;
|
|
150
|
+
const matches = msg.segments.filter((s) => s.id === upper);
|
|
151
|
+
const found = matches[idx];
|
|
152
|
+
if (!found) throw new SegmentNotFoundError(upper);
|
|
153
|
+
return found;
|
|
154
|
+
}
|
|
155
|
+
function segments(msg, id) {
|
|
156
|
+
return msg.segments.filter((s) => s.id === id.toUpperCase());
|
|
157
|
+
}
|
|
158
|
+
function hasSegment(msg, id) {
|
|
159
|
+
return msg.segments.some((s) => s.id === id.toUpperCase());
|
|
160
|
+
}
|
|
161
|
+
function get(msg, segmentId, field, component, subComponent, options = {}) {
|
|
162
|
+
const segIdx = (options.segmentIndex ?? 1) - 1;
|
|
163
|
+
const repIdx = (options.repetition ?? 1) - 1;
|
|
164
|
+
const compIdx = (component ?? 1) - 1;
|
|
165
|
+
const subIdx = (subComponent ?? 1) - 1;
|
|
166
|
+
const segs = msg.segments.filter((s) => s.id === segmentId.toUpperCase());
|
|
167
|
+
const seg = segs[segIdx];
|
|
168
|
+
if (!seg) return "";
|
|
169
|
+
const fieldValue = seg.fields[field - 1];
|
|
170
|
+
if (!fieldValue) return "";
|
|
171
|
+
const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? "";
|
|
172
|
+
return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;
|
|
173
|
+
}
|
|
174
|
+
function getFromSegment(seg, msg, field, component, subComponent, options = {}) {
|
|
175
|
+
const repIdx = (options.repetition ?? 1) - 1;
|
|
176
|
+
const compIdx = (component ?? 1) - 1;
|
|
177
|
+
const subIdx = (subComponent ?? 1) - 1;
|
|
178
|
+
const fieldValue = seg.fields[field - 1];
|
|
179
|
+
if (!fieldValue) return "";
|
|
180
|
+
const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? "";
|
|
181
|
+
return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;
|
|
182
|
+
}
|
|
183
|
+
function getRepetitions(msg, segmentId, field, options = {}) {
|
|
184
|
+
const segIdx = (options.segmentIndex ?? 1) - 1;
|
|
185
|
+
const segs = msg.segments.filter((s) => s.id === segmentId.toUpperCase());
|
|
186
|
+
const seg = segs[segIdx];
|
|
187
|
+
if (!seg) return [];
|
|
188
|
+
const fieldValue = seg.fields[field - 1];
|
|
189
|
+
if (!fieldValue) return [];
|
|
190
|
+
if (options.decode !== true) return fieldValue;
|
|
191
|
+
return fieldValue.map(
|
|
192
|
+
(rep) => rep.map(
|
|
193
|
+
(comp) => comp.map((sub) => decodeEscapes(sub, msg.encoding))
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { DEFAULT_ENCODING, encode, encodeSegment, get, getFromSegment, getRepetitions, hasSegment, isHL7, parse, segment, segments };
|
|
199
|
+
//# sourceMappingURL=index.js.map
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts","../src/parser.ts","../src/encoder.ts","../src/query.ts"],"names":["segments"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The five special characters that govern HL7 v2 encoding.\n * Defined in MSH.1 and MSH.2 of every message.\n * Default values follow the HL7 v2 standard.\n */\nexport interface EncodingChars {\n /** Separates fields within a segment. Default: `|` */\n readonly field: string;\n /** Separates components within a field. Default: `^` */\n readonly component: string;\n /** Separates repetitions of a field. Default: `~` */\n readonly repetition: string;\n /** Introduces escape sequences. Default: `\\` */\n readonly escape: string;\n /** Separates sub-components. Default: `&` */\n readonly subComponent: string;\n}\n\n/** HL7 v2 standard encoding characters. */\nexport const DEFAULT_ENCODING: EncodingChars = {\n field: '|',\n component: '^',\n repetition: '~',\n escape: '\\\\',\n subComponent: '&',\n} as const;\n\n/**\n * A single HL7 v2 field value modelled as a 3-dimensional array.\n *\n * Dimensions (all 0-indexed internally):\n * `value[repetition][component][subComponent]`\n *\n * Most fields are simple strings: `[[['value']]]`\n * Composite fields: `[[['family', 'given', 'middle']]]`\n * Repeated fields: `[[['home phone']], [['work phone']]]`\n */\nexport type HL7Field = string[][][];\n\n/**\n * A single HL7 v2 segment — one line of a message.\n *\n * @example\n * // PID|1||MRN123^^^HOSP^MR||DOE^JOHN^A\n * segment.id // 'PID'\n * segment.fields // [[...], [...], ...]\n */\nexport interface HL7Segment {\n /** Three-letter segment identifier (e.g., `'MSH'`, `'PID'`, `'OBX'`). */\n readonly id: string;\n /**\n * Segment fields, 0-indexed.\n * `fields[n]` corresponds to field number `n+1` in the HL7 spec.\n *\n * MSH is special: `fields[0]` = MSH.1 (the field separator char itself).\n */\n readonly fields: HL7Field[];\n /** The original unparsed segment string — preserved for round-trip fidelity. */\n readonly raw: string;\n}\n\n/** Parsed message type and trigger event from MSH.9. */\nexport interface MessageType {\n /** Message type code (e.g., `'ADT'`, `'ORU'`, `'ORM'`). */\n readonly type: string;\n /** Trigger event code (e.g., `'A01'`, `'R01'`, `'O01'`). */\n readonly event: string;\n /** Optional message structure (e.g., `'ADT_A01'`). */\n readonly structure: string | undefined;\n}\n\n/** Processing ID — indicates the environment the message was sent from. */\nexport type ProcessingId = 'P' | 'D' | 'T' | string;\n\n/**\n * A fully parsed HL7 v2.x message.\n *\n * @example\n * const msg = parse(raw);\n * msg.version // '2.5.1'\n * msg.messageType // { type: 'ADT', event: 'A01', structure: 'ADT_A01' }\n * msg.messageControlId // 'MSG000001'\n * msg.segments // HL7Segment[]\n */\nexport interface HL7Message {\n /** HL7 version string extracted from MSH.12 (e.g., `'2.5'`, `'2.5.1'`). */\n readonly version: string;\n /** Message type and trigger event parsed from MSH.9. */\n readonly messageType: MessageType;\n /** Unique message control ID from MSH.10. Used for acknowledgement correlation. */\n readonly messageControlId: string;\n /** Message creation timestamp from MSH.7. `undefined` if absent or unparseable. */\n readonly timestamp: Date | undefined;\n /** Sending application name from MSH.3. */\n readonly sendingApplication: string;\n /** Sending facility name from MSH.4. */\n readonly sendingFacility: string;\n /** Receiving application name from MSH.5. */\n readonly receivingApplication: string;\n /** Receiving facility name from MSH.6. */\n readonly receivingFacility: string;\n /** Processing ID from MSH.11. `'P'` = Production, `'D'` = Debugging, `'T'` = Training. */\n readonly processingId: ProcessingId;\n /** All segments in document order. */\n readonly segments: HL7Segment[];\n /** Encoding characters extracted from MSH.1 and MSH.2. */\n readonly encoding: EncodingChars;\n /** The original raw input string — preserved without modification. */\n readonly raw: string;\n /**\n * The line ending character(s) detected from the original input.\n * Used by `encode()` to restore byte-identical output.\n * `'\\r'` (canonical HL7), `'\\n'` (Unix), or `'\\r\\n'` (Windows).\n */\n readonly lineEnding: '\\r' | '\\n' | '\\r\\n';\n}\n","import { InvalidHL7Error, HL7ParseError } from './errors.js';\nimport { parseHL7DateTime } from './datetime.js';\nimport { decodeEscapes } from './escape.js';\nimport type { EncodingChars, HL7Field, HL7Message, HL7Segment, MessageType } from './schema.js';\nimport { DEFAULT_ENCODING } from './schema.js';\n\nexport interface ParseOptions {\n /**\n * When `true`, HL7 escape sequences within field values are decoded to their\n * plain-text equivalents (e.g. `\\F\\` → `|`).\n *\n * Defaults to `false` so that `encode(parse(raw))` is byte-identical\n * to the original input — essential for ACK generation and message forwarding.\n */\n decodeEscapes?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction parseEncodingChars(mshLine: string): EncodingChars {\n const field = mshLine[3];\n const component = mshLine[4];\n const repetition = mshLine[5];\n const escape = mshLine[6];\n const subComponent = mshLine[7];\n\n if (!field || !component || !repetition || !escape || !subComponent) {\n throw new InvalidHL7Error(\n `MSH encoding characters are incomplete — ` +\n `expected 5 chars at positions 3-7, got: \"${mshLine.slice(3, 8)}\"`,\n );\n }\n\n return { field, component, repetition, escape, subComponent };\n}\n\n/**\n * Parse a raw field string into the canonical 3-D HL7Field structure:\n * `value[repetition][component][subComponent]`\n */\nfunction parseField(raw: string, enc: EncodingChars): HL7Field {\n return raw.split(enc.repetition).map(rep =>\n rep.split(enc.component).map(comp =>\n comp.split(enc.subComponent),\n ),\n );\n}\n\nfunction parseMSHSegment(line: string, enc: EncodingChars): HL7Segment {\n const parts = line.split(enc.field);\n const fields: HL7Field[] = [\n // MSH.1 — the field separator character itself\n [[[enc.field]]],\n // MSH.2 — encoding characters verbatim; must NOT be split on component separator\n [[[parts[1] ?? '']]],\n // MSH.3 onwards — normal fields\n ...parts.slice(2).map(f => parseField(f, enc)),\n ];\n return { id: 'MSH', fields, raw: line };\n}\n\nfunction parseGenericSegment(line: string, enc: EncodingChars, lineNum: number): HL7Segment {\n const sepIdx = line.indexOf(enc.field);\n const id = sepIdx === -1 ? line : line.slice(0, sepIdx);\n\n if (!/^[A-Z][A-Z0-9]{2}$/.test(id)) {\n throw new HL7ParseError(\n `Invalid segment identifier \"${id}\" at line ${String(lineNum)} — ` +\n `expected 3 alphanumeric characters (A-Z, 0-9) starting with a letter`,\n { line: lineNum, segmentId: id },\n );\n }\n\n const fieldStrings = sepIdx === -1 ? [] : line.slice(sepIdx + 1).split(enc.field);\n return { id, fields: fieldStrings.map(f => parseField(f, enc)), raw: line };\n}\n\n/** Extract a simple scalar value from an MSH field by 1-based field number. */\nfunction mshValue(seg: HL7Segment, fieldNum: number): string {\n return seg.fields[fieldNum - 1]?.[0]?.[0]?.[0] ?? '';\n}\n\nfunction parseMessageType(field: HL7Field): MessageType {\n const firstRep = field[0] ?? [];\n const struct = firstRep[2]?.[0];\n return {\n type: firstRep[0]?.[0] ?? '',\n event: firstRep[1]?.[0] ?? '',\n structure: struct !== undefined && struct !== '' ? struct : undefined,\n };\n}\n\nfunction detectLineEnding(raw: string): '\\r' | '\\n' | '\\r\\n' {\n if (raw.includes('\\r\\n')) return '\\r\\n';\n if (raw.includes('\\r')) return '\\r';\n return '\\n';\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse an HL7 v2.x message string into a structured {@link HL7Message}.\n *\n * ### Supported inputs\n * - HL7 versions 2.1 – 2.8\n * - Line endings: `\\r` (canonical), `\\n`, or `\\r\\n` — all normalised to `\\r`\n * - Custom encoding characters (non-default separators)\n * - Messages with blank lines (skipped silently)\n *\n * ### Round-trip fidelity\n * By default (`decodeEscapes: false`), field values retain their raw HL7\n * escape sequences, making `encode(parse(raw)) === raw` hold true.\n * Use `decodeEscapes: true` only when you need to display or process the\n * human-readable values and do not intend to re-encode the message.\n *\n * @throws {InvalidHL7Error} When the input is empty or does not start with MSH\n * @throws {HL7ParseError} When a segment identifier or field structure is invalid\n *\n * @example\n * import { parse } from 'hl7v2';\n *\n * const msg = parse(rawString);\n * console.log(msg.messageType); // { type: 'ADT', event: 'A01', structure: 'ADT_A01' }\n * console.log(msg.version); // '2.5.1'\n */\nexport function parse(raw: string, options: ParseOptions = {}): HL7Message {\n if (!raw || !raw.trim()) {\n throw new InvalidHL7Error('Input is empty');\n }\n\n // Normalise all line ending variants to the HL7 canonical segment terminator \\r\n const normalised = raw.replace(/\\r\\n/g, '\\r').replace(/\\n/g, '\\r');\n const lines = normalised.split('\\r').filter(l => l.trim().length > 0);\n\n const firstLine = lines[0];\n if (!firstLine?.startsWith('MSH')) {\n throw new InvalidHL7Error(\n `First non-blank line must be an MSH segment, got: \"${(firstLine ?? '').slice(0, 30)}\"`,\n );\n }\n if (firstLine.length < 8) {\n throw new InvalidHL7Error(\n 'MSH segment is too short to contain encoding characters (minimum length: 8)',\n );\n }\n\n const encoding = parseEncodingChars(firstLine);\n\n const segments: HL7Segment[] = lines.map((line, idx) => {\n if (line.startsWith('MSH')) return parseMSHSegment(line, encoding);\n return parseGenericSegment(line, encoding, idx + 1);\n });\n\n const msh = segments[0];\n if (!msh) throw new InvalidHL7Error('No MSH segment could be parsed');\n\n const shouldDecode = options.decodeEscapes === true;\n const decode = (v: string): string =>\n shouldDecode ? decodeEscapes(v, encoding) : v;\n\n // MSH.9 is at fields[8] (0-based). Pass the full HL7Field so all components\n // of the composite \"MSG_TYPE^TRIGGER_EVENT^MSG_STRUCTURE\" are accessible.\n const msgTypeField = msh.fields[8] ?? [];\n\n return {\n version: decode(mshValue(msh, 12)),\n messageType: parseMessageType(msgTypeField),\n messageControlId: decode(mshValue(msh, 10)),\n timestamp: parseHL7DateTime(mshValue(msh, 7)),\n sendingApplication: decode(mshValue(msh, 3)),\n sendingFacility: decode(mshValue(msh, 4)),\n receivingApplication: decode(mshValue(msh, 5)),\n receivingFacility: decode(mshValue(msh, 6)),\n processingId: mshValue(msh, 11),\n segments,\n encoding,\n raw: raw.trim(),\n lineEnding: detectLineEnding(raw),\n };\n}\n\n/**\n * Return `true` if the input looks like an HL7 v2 message.\n * This is a fast heuristic check — it does not fully parse the message.\n */\nexport function isHL7(input: string): boolean {\n return /^MSH[^A-Za-z0-9]/.test(input.trimStart());\n}\n\n// Re-export DEFAULT_ENCODING for convenience\nexport { DEFAULT_ENCODING };\n","import type { EncodingChars, HL7Field, HL7Message, HL7Segment } from './schema.js';\n\nexport interface EncodeOptions {\n /**\n * Line ending to use between segments.\n * HL7 v2 canonical is `'\\r'`. Some systems expect `'\\r\\n'` or `'\\n'`.\n * @default '\\r'\n */\n lineEnding?: '\\r' | '\\n' | '\\r\\n';\n /**\n * When `true`, a trailing line ending is appended after the last segment.\n * @default false\n */\n trailingNewline?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeField(field: HL7Field, enc: EncodingChars): string {\n return field\n .map(rep =>\n rep\n .map(comp => comp.join(enc.subComponent))\n .join(enc.component),\n )\n .join(enc.repetition);\n}\n\nfunction encodeMSHSegment(seg: HL7Segment, enc: EncodingChars): string {\n // Reconstruct MSH verbatim from its fields:\n // MSH + field_sep + encoding_chars + field_sep + MSH.3 + ...\n const encodingCharsStr =\n enc.component + enc.repetition + enc.escape + enc.subComponent;\n\n // fields[0] = MSH.1 (field sep, skip — already in prefix)\n // fields[1] = MSH.2 (encoding chars, always output verbatim)\n // fields[2..] = MSH.3 onwards\n const rest = seg.fields\n .slice(2)\n .map(f => encodeField(f, enc))\n .join(enc.field);\n\n return `MSH${enc.field}${encodingCharsStr}${enc.field}${rest}`;\n}\n\nfunction encodeSegment(seg: HL7Segment, enc: EncodingChars): string {\n if (seg.id === 'MSH') return encodeMSHSegment(seg, enc);\n const fields = seg.fields.map(f => encodeField(f, enc)).join(enc.field);\n return `${seg.id}${enc.field}${fields}`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Encode an {@link HL7Message} back into a raw HL7 v2 string.\n *\n * ### Round-trip guarantee\n * When called on a message produced by `parse(raw)` with default options\n * (i.e., `decodeEscapes` was **not** set to `true`), this function produces\n * output that is byte-identical to the original input, modulo line endings.\n *\n * ### Encoding characters\n * The message's own `encoding` field is used. Override via `options.encoding`\n * if you need to re-encode to a different separator set (rare).\n *\n * @param msg - The message to encode\n * @param options - Optional output configuration\n *\n * @example\n * import { parse, encode } from 'hl7v2';\n *\n * const msg = parse(rawString);\n * const back = encode(msg); // byte-identical to rawString (modulo line endings)\n *\n * // Modify a field and re-encode\n * const mutable = structuredClone(msg) as HL7Message;\n * // ... set fields ...\n * const updated = encode(mutable);\n */\nexport function encode(msg: HL7Message, options: EncodeOptions = {}): string {\n const lineEnding = options.lineEnding ?? msg.lineEnding;\n const enc = msg.encoding;\n\n const lines = msg.segments.map(seg => encodeSegment(seg, enc));\n const result = lines.join(lineEnding);\n return options.trailingNewline === true ? result + lineEnding : result;\n}\n\n/**\n * Encode a single {@link HL7Segment} to a string using the given encoding characters.\n *\n * Useful when constructing ACK messages or individual segments programmatically.\n *\n * @example\n * import { encodeSegment } from 'hl7v2';\n *\n * const mshString = encodeSegment(msg.segments[0], msg.encoding);\n */\nexport { encodeSegment };\n","import { SegmentNotFoundError } from './errors.js';\nimport { decodeEscapes } from './escape.js';\nimport type { HL7Field, HL7Message, HL7Segment } from './schema.js';\n\n// ---------------------------------------------------------------------------\n// Segment access\n// ---------------------------------------------------------------------------\n\n/**\n * Return the first segment with the given identifier.\n *\n * When a message contains multiple segments with the same identifier (e.g.\n * repeating OBX groups), pass `{ segmentIndex: N }` (1-based) to select which\n * occurrence to return. Defaults to the first occurrence (`segmentIndex: 1`).\n *\n * @throws {SegmentNotFoundError} When no segment with `id` exists at the given index.\n *\n * @example\n * const pid = segment(msg, 'PID');\n * const obx1 = segment(msg, 'OBX', { segmentIndex: 1 }); // first OBX\n * const obx2 = segment(msg, 'OBX', { segmentIndex: 2 }); // second OBX\n */\nexport function segment(\n msg: HL7Message,\n id: string,\n options: { segmentIndex?: number } = {},\n): HL7Segment {\n const upper = id.toUpperCase();\n const idx = (options.segmentIndex ?? 1) - 1; // convert 1-based to 0-based\n const matches = msg.segments.filter(s => s.id === upper);\n const found = matches[idx];\n if (!found) throw new SegmentNotFoundError(upper);\n return found;\n}\n\n/**\n * Return all segments with the given identifier.\n * Returns an empty array (never throws) when none are found.\n *\n * Useful for repeating segments such as OBX (lab results), NTE (notes),\n * or DG1 (diagnoses).\n *\n * @example\n * const allOBX = segments(msg, 'OBX');\n * for (const obx of allOBX) {\n * console.log(get(msg, obx, 5)); // OBX.5 — observation value\n * }\n */\nexport function segments(msg: HL7Message, id: string): HL7Segment[] {\n return msg.segments.filter(s => s.id === id.toUpperCase());\n}\n\n/**\n * Return `true` if the message contains at least one segment with the given identifier.\n *\n * @example\n * if (hasSegment(msg, 'PV1')) {\n * const pv1 = segment(msg, 'PV1');\n * }\n */\nexport function hasSegment(msg: HL7Message, id: string): boolean {\n return msg.segments.some(s => s.id === id.toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Field value access\n// ---------------------------------------------------------------------------\n\nexport interface GetOptions {\n /**\n * 1-based repetition number. Defaults to `1` (first repetition).\n *\n * @example\n * // PID.3 can repeat — get the second identifier\n * get(msg, 'PID', 3, undefined, undefined, { repetition: 2 });\n */\n repetition?: number;\n /**\n * When `true`, decode HL7 escape sequences in the returned value.\n * Defaults to `false`.\n */\n decode?: boolean;\n /**\n * 1-based index when the message contains multiple segments with the same\n * identifier. Defaults to `1` (first occurrence).\n *\n * @example\n * // Get OBX.5 from the third OBX segment\n * get(msg, 'OBX', 5, undefined, undefined, { segmentIndex: 3 });\n */\n segmentIndex?: number;\n}\n\n/**\n * Extract a scalar string value from a message field using 1-based HL7 addressing.\n *\n * Returns an empty string `''` rather than throwing when the field, component,\n * or sub-component does not exist — reflecting HL7's rule that absent elements\n * are equivalent to empty strings.\n *\n * HL7 addressing reference (1-based throughout):\n * - `get(msg, 'PID', 3)` → PID.3 (first component, first repetition)\n * - `get(msg, 'PID', 5, 1)` → PID.5.1 (family name component)\n * - `get(msg, 'PID', 5, 2)` → PID.5.2 (given name component)\n * - `get(msg, 'PID', 11, 1, 1)` → PID.11.1.1 (sub-component)\n * - `get(msg, 'OBX', 5, 1, 1, { repetition: 2 })` → OBX.5[2].1.1 (second repetition)\n *\n * @param msg - Parsed HL7 message\n * @param segmentId - Three-letter segment identifier (case-insensitive)\n * @param field - 1-based field number\n * @param component - 1-based component number (defaults to 1)\n * @param subComponent - 1-based sub-component number (defaults to 1)\n * @param options - See {@link GetOptions}\n *\n * @example\n * import { parse, get } from 'hl7v2';\n *\n * const msg = parse(raw);\n * const mrn = get(msg, 'PID', 3); // PID.3 — medical record number\n * const lastName = get(msg, 'PID', 5, 1); // PID.5.1 — family name\n * const firstName = get(msg, 'PID', 5, 2); // PID.5.2 — given name\n */\nexport function get(\n msg: HL7Message,\n segmentId: string,\n field: number,\n component?: number,\n subComponent?: number,\n options: GetOptions = {},\n): string {\n const segIdx = (options.segmentIndex ?? 1) - 1; // convert 1-based to 0-based\n const repIdx = (options.repetition ?? 1) - 1; // convert 1-based to 0-based\n const compIdx = (component ?? 1) - 1;\n const subIdx = (subComponent ?? 1) - 1;\n\n const segs = msg.segments.filter(s => s.id === segmentId.toUpperCase());\n const seg = segs[segIdx];\n if (!seg) return '';\n\n // fields are 0-indexed, HL7 field numbers are 1-based\n const fieldValue: HL7Field | undefined = seg.fields[field - 1];\n if (!fieldValue) return '';\n\n const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? '';\n return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;\n}\n\n/**\n * Extract a field value from a known segment reference (returned by {@link segment}\n * or {@link segments}).\n *\n * Equivalent to {@link get} but accepts an `HL7Segment` directly — useful when\n * iterating over repeating segments.\n *\n * @example\n * for (const obx of segments(msg, 'OBX')) {\n * const value = getFromSegment(obx, msg, 5); // OBX.5 for each OBX segment\n * }\n */\nexport function getFromSegment(\n seg: HL7Segment,\n msg: HL7Message,\n field: number,\n component?: number,\n subComponent?: number,\n options: Omit<GetOptions, 'segmentIndex'> = {},\n): string {\n const repIdx = (options.repetition ?? 1) - 1;\n const compIdx = (component ?? 1) - 1;\n const subIdx = (subComponent ?? 1) - 1;\n\n const fieldValue: HL7Field | undefined = seg.fields[field - 1];\n if (!fieldValue) return '';\n\n const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? '';\n return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;\n}\n\n/**\n * Return all repetitions of a field as a `string[][][]` structure (one entry\n * per repetition, preserving the `[component][subComponent]` shape).\n *\n * Useful for fields like PID.3 (patient identifier list) that carry multiple\n * typed identifiers — each with its own components — across repetitions.\n *\n * @example\n * const ids = getRepetitions(msg, 'PID', 3);\n * ids[0]?.[0]?.[0] // → first identifier value (PID.3[1].1)\n * ids[0]?.[3]?.[0] // → first identifier's assigning authority (PID.3[1].4)\n * ids[1]?.[0]?.[0] // → second identifier value (PID.3[2].1)\n */\nexport function getRepetitions(\n msg: HL7Message,\n segmentId: string,\n field: number,\n options: Omit<GetOptions, 'repetition'> = {},\n): string[][][] {\n const segIdx = (options.segmentIndex ?? 1) - 1;\n\n const segs = msg.segments.filter(s => s.id === segmentId.toUpperCase());\n const seg = segs[segIdx];\n if (!seg) return [];\n\n const fieldValue: HL7Field | undefined = seg.fields[field - 1];\n if (!fieldValue) return [];\n\n if (options.decode !== true) return fieldValue;\n\n return fieldValue.map(rep =>\n rep.map(comp =>\n comp.map(sub => decodeEscapes(sub, msg.encoding)),\n ),\n );\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five special characters that govern HL7 v2 encoding.
|
|
3
|
+
* Defined in MSH.1 and MSH.2 of every message.
|
|
4
|
+
* Default values follow the HL7 v2 standard.
|
|
5
|
+
*/
|
|
6
|
+
interface EncodingChars {
|
|
7
|
+
/** Separates fields within a segment. Default: `|` */
|
|
8
|
+
readonly field: string;
|
|
9
|
+
/** Separates components within a field. Default: `^` */
|
|
10
|
+
readonly component: string;
|
|
11
|
+
/** Separates repetitions of a field. Default: `~` */
|
|
12
|
+
readonly repetition: string;
|
|
13
|
+
/** Introduces escape sequences. Default: `\` */
|
|
14
|
+
readonly escape: string;
|
|
15
|
+
/** Separates sub-components. Default: `&` */
|
|
16
|
+
readonly subComponent: string;
|
|
17
|
+
}
|
|
18
|
+
/** HL7 v2 standard encoding characters. */
|
|
19
|
+
declare const DEFAULT_ENCODING: EncodingChars;
|
|
20
|
+
/**
|
|
21
|
+
* A single HL7 v2 field value modelled as a 3-dimensional array.
|
|
22
|
+
*
|
|
23
|
+
* Dimensions (all 0-indexed internally):
|
|
24
|
+
* `value[repetition][component][subComponent]`
|
|
25
|
+
*
|
|
26
|
+
* Most fields are simple strings: `[[['value']]]`
|
|
27
|
+
* Composite fields: `[[['family', 'given', 'middle']]]`
|
|
28
|
+
* Repeated fields: `[[['home phone']], [['work phone']]]`
|
|
29
|
+
*/
|
|
30
|
+
type HL7Field = string[][][];
|
|
31
|
+
/**
|
|
32
|
+
* A single HL7 v2 segment — one line of a message.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // PID|1||MRN123^^^HOSP^MR||DOE^JOHN^A
|
|
36
|
+
* segment.id // 'PID'
|
|
37
|
+
* segment.fields // [[...], [...], ...]
|
|
38
|
+
*/
|
|
39
|
+
interface HL7Segment {
|
|
40
|
+
/** Three-letter segment identifier (e.g., `'MSH'`, `'PID'`, `'OBX'`). */
|
|
41
|
+
readonly id: string;
|
|
42
|
+
/**
|
|
43
|
+
* Segment fields, 0-indexed.
|
|
44
|
+
* `fields[n]` corresponds to field number `n+1` in the HL7 spec.
|
|
45
|
+
*
|
|
46
|
+
* MSH is special: `fields[0]` = MSH.1 (the field separator char itself).
|
|
47
|
+
*/
|
|
48
|
+
readonly fields: HL7Field[];
|
|
49
|
+
/** The original unparsed segment string — preserved for round-trip fidelity. */
|
|
50
|
+
readonly raw: string;
|
|
51
|
+
}
|
|
52
|
+
/** Parsed message type and trigger event from MSH.9. */
|
|
53
|
+
interface MessageType {
|
|
54
|
+
/** Message type code (e.g., `'ADT'`, `'ORU'`, `'ORM'`). */
|
|
55
|
+
readonly type: string;
|
|
56
|
+
/** Trigger event code (e.g., `'A01'`, `'R01'`, `'O01'`). */
|
|
57
|
+
readonly event: string;
|
|
58
|
+
/** Optional message structure (e.g., `'ADT_A01'`). */
|
|
59
|
+
readonly structure: string | undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Processing ID — indicates the environment the message was sent from. */
|
|
62
|
+
type ProcessingId = 'P' | 'D' | 'T' | string;
|
|
63
|
+
/**
|
|
64
|
+
* A fully parsed HL7 v2.x message.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* const msg = parse(raw);
|
|
68
|
+
* msg.version // '2.5.1'
|
|
69
|
+
* msg.messageType // { type: 'ADT', event: 'A01', structure: 'ADT_A01' }
|
|
70
|
+
* msg.messageControlId // 'MSG000001'
|
|
71
|
+
* msg.segments // HL7Segment[]
|
|
72
|
+
*/
|
|
73
|
+
interface HL7Message {
|
|
74
|
+
/** HL7 version string extracted from MSH.12 (e.g., `'2.5'`, `'2.5.1'`). */
|
|
75
|
+
readonly version: string;
|
|
76
|
+
/** Message type and trigger event parsed from MSH.9. */
|
|
77
|
+
readonly messageType: MessageType;
|
|
78
|
+
/** Unique message control ID from MSH.10. Used for acknowledgement correlation. */
|
|
79
|
+
readonly messageControlId: string;
|
|
80
|
+
/** Message creation timestamp from MSH.7. `undefined` if absent or unparseable. */
|
|
81
|
+
readonly timestamp: Date | undefined;
|
|
82
|
+
/** Sending application name from MSH.3. */
|
|
83
|
+
readonly sendingApplication: string;
|
|
84
|
+
/** Sending facility name from MSH.4. */
|
|
85
|
+
readonly sendingFacility: string;
|
|
86
|
+
/** Receiving application name from MSH.5. */
|
|
87
|
+
readonly receivingApplication: string;
|
|
88
|
+
/** Receiving facility name from MSH.6. */
|
|
89
|
+
readonly receivingFacility: string;
|
|
90
|
+
/** Processing ID from MSH.11. `'P'` = Production, `'D'` = Debugging, `'T'` = Training. */
|
|
91
|
+
readonly processingId: ProcessingId;
|
|
92
|
+
/** All segments in document order. */
|
|
93
|
+
readonly segments: HL7Segment[];
|
|
94
|
+
/** Encoding characters extracted from MSH.1 and MSH.2. */
|
|
95
|
+
readonly encoding: EncodingChars;
|
|
96
|
+
/** The original raw input string — preserved without modification. */
|
|
97
|
+
readonly raw: string;
|
|
98
|
+
/**
|
|
99
|
+
* The line ending character(s) detected from the original input.
|
|
100
|
+
* Used by `encode()` to restore byte-identical output.
|
|
101
|
+
* `'\r'` (canonical HL7), `'\n'` (Unix), or `'\r\n'` (Windows).
|
|
102
|
+
*/
|
|
103
|
+
readonly lineEnding: '\r' | '\n' | '\r\n';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { DEFAULT_ENCODING as D, type EncodingChars as E, type HL7Message as H, type MessageType as M, type HL7Segment as a, type HL7Field as b };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five special characters that govern HL7 v2 encoding.
|
|
3
|
+
* Defined in MSH.1 and MSH.2 of every message.
|
|
4
|
+
* Default values follow the HL7 v2 standard.
|
|
5
|
+
*/
|
|
6
|
+
interface EncodingChars {
|
|
7
|
+
/** Separates fields within a segment. Default: `|` */
|
|
8
|
+
readonly field: string;
|
|
9
|
+
/** Separates components within a field. Default: `^` */
|
|
10
|
+
readonly component: string;
|
|
11
|
+
/** Separates repetitions of a field. Default: `~` */
|
|
12
|
+
readonly repetition: string;
|
|
13
|
+
/** Introduces escape sequences. Default: `\` */
|
|
14
|
+
readonly escape: string;
|
|
15
|
+
/** Separates sub-components. Default: `&` */
|
|
16
|
+
readonly subComponent: string;
|
|
17
|
+
}
|
|
18
|
+
/** HL7 v2 standard encoding characters. */
|
|
19
|
+
declare const DEFAULT_ENCODING: EncodingChars;
|
|
20
|
+
/**
|
|
21
|
+
* A single HL7 v2 field value modelled as a 3-dimensional array.
|
|
22
|
+
*
|
|
23
|
+
* Dimensions (all 0-indexed internally):
|
|
24
|
+
* `value[repetition][component][subComponent]`
|
|
25
|
+
*
|
|
26
|
+
* Most fields are simple strings: `[[['value']]]`
|
|
27
|
+
* Composite fields: `[[['family', 'given', 'middle']]]`
|
|
28
|
+
* Repeated fields: `[[['home phone']], [['work phone']]]`
|
|
29
|
+
*/
|
|
30
|
+
type HL7Field = string[][][];
|
|
31
|
+
/**
|
|
32
|
+
* A single HL7 v2 segment — one line of a message.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // PID|1||MRN123^^^HOSP^MR||DOE^JOHN^A
|
|
36
|
+
* segment.id // 'PID'
|
|
37
|
+
* segment.fields // [[...], [...], ...]
|
|
38
|
+
*/
|
|
39
|
+
interface HL7Segment {
|
|
40
|
+
/** Three-letter segment identifier (e.g., `'MSH'`, `'PID'`, `'OBX'`). */
|
|
41
|
+
readonly id: string;
|
|
42
|
+
/**
|
|
43
|
+
* Segment fields, 0-indexed.
|
|
44
|
+
* `fields[n]` corresponds to field number `n+1` in the HL7 spec.
|
|
45
|
+
*
|
|
46
|
+
* MSH is special: `fields[0]` = MSH.1 (the field separator char itself).
|
|
47
|
+
*/
|
|
48
|
+
readonly fields: HL7Field[];
|
|
49
|
+
/** The original unparsed segment string — preserved for round-trip fidelity. */
|
|
50
|
+
readonly raw: string;
|
|
51
|
+
}
|
|
52
|
+
/** Parsed message type and trigger event from MSH.9. */
|
|
53
|
+
interface MessageType {
|
|
54
|
+
/** Message type code (e.g., `'ADT'`, `'ORU'`, `'ORM'`). */
|
|
55
|
+
readonly type: string;
|
|
56
|
+
/** Trigger event code (e.g., `'A01'`, `'R01'`, `'O01'`). */
|
|
57
|
+
readonly event: string;
|
|
58
|
+
/** Optional message structure (e.g., `'ADT_A01'`). */
|
|
59
|
+
readonly structure: string | undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Processing ID — indicates the environment the message was sent from. */
|
|
62
|
+
type ProcessingId = 'P' | 'D' | 'T' | string;
|
|
63
|
+
/**
|
|
64
|
+
* A fully parsed HL7 v2.x message.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* const msg = parse(raw);
|
|
68
|
+
* msg.version // '2.5.1'
|
|
69
|
+
* msg.messageType // { type: 'ADT', event: 'A01', structure: 'ADT_A01' }
|
|
70
|
+
* msg.messageControlId // 'MSG000001'
|
|
71
|
+
* msg.segments // HL7Segment[]
|
|
72
|
+
*/
|
|
73
|
+
interface HL7Message {
|
|
74
|
+
/** HL7 version string extracted from MSH.12 (e.g., `'2.5'`, `'2.5.1'`). */
|
|
75
|
+
readonly version: string;
|
|
76
|
+
/** Message type and trigger event parsed from MSH.9. */
|
|
77
|
+
readonly messageType: MessageType;
|
|
78
|
+
/** Unique message control ID from MSH.10. Used for acknowledgement correlation. */
|
|
79
|
+
readonly messageControlId: string;
|
|
80
|
+
/** Message creation timestamp from MSH.7. `undefined` if absent or unparseable. */
|
|
81
|
+
readonly timestamp: Date | undefined;
|
|
82
|
+
/** Sending application name from MSH.3. */
|
|
83
|
+
readonly sendingApplication: string;
|
|
84
|
+
/** Sending facility name from MSH.4. */
|
|
85
|
+
readonly sendingFacility: string;
|
|
86
|
+
/** Receiving application name from MSH.5. */
|
|
87
|
+
readonly receivingApplication: string;
|
|
88
|
+
/** Receiving facility name from MSH.6. */
|
|
89
|
+
readonly receivingFacility: string;
|
|
90
|
+
/** Processing ID from MSH.11. `'P'` = Production, `'D'` = Debugging, `'T'` = Training. */
|
|
91
|
+
readonly processingId: ProcessingId;
|
|
92
|
+
/** All segments in document order. */
|
|
93
|
+
readonly segments: HL7Segment[];
|
|
94
|
+
/** Encoding characters extracted from MSH.1 and MSH.2. */
|
|
95
|
+
readonly encoding: EncodingChars;
|
|
96
|
+
/** The original raw input string — preserved without modification. */
|
|
97
|
+
readonly raw: string;
|
|
98
|
+
/**
|
|
99
|
+
* The line ending character(s) detected from the original input.
|
|
100
|
+
* Used by `encode()` to restore byte-identical output.
|
|
101
|
+
* `'\r'` (canonical HL7), `'\n'` (Unix), or `'\r\n'` (Windows).
|
|
102
|
+
*/
|
|
103
|
+
readonly lineEnding: '\r' | '\n' | '\r\n';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { DEFAULT_ENCODING as D, type EncodingChars as E, type HL7Message as H, type MessageType as M, type HL7Segment as a, type HL7Field as b };
|