@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.cjs
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var HL7ParseError = class extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "HL7ParseError";
|
|
8
|
+
this.line = options?.line;
|
|
9
|
+
this.segmentId = options?.segmentId;
|
|
10
|
+
if (options?.cause !== void 0) {
|
|
11
|
+
Object.defineProperty(this, "cause", {
|
|
12
|
+
value: options.cause,
|
|
13
|
+
writable: true,
|
|
14
|
+
configurable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var SegmentNotFoundError = class extends Error {
|
|
20
|
+
constructor(segmentId) {
|
|
21
|
+
super(
|
|
22
|
+
`Segment '${segmentId}' not found in message. Use hasSegment() to check before calling segment(), or segments() to get an empty array when absent.`
|
|
23
|
+
);
|
|
24
|
+
this.name = "SegmentNotFoundError";
|
|
25
|
+
this.segmentId = segmentId;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var InvalidHL7Error = class extends Error {
|
|
29
|
+
constructor(reason) {
|
|
30
|
+
super(`Input is not a valid HL7 v2 message: ${reason}`);
|
|
31
|
+
this.name = "InvalidHL7Error";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/datetime.ts
|
|
36
|
+
function parseHL7DateTime(raw) {
|
|
37
|
+
const s = raw.trim();
|
|
38
|
+
if (!s) return void 0;
|
|
39
|
+
if (s.length < 4) {
|
|
40
|
+
throw new HL7ParseError(`Cannot parse HL7 date/time: "${s}" \u2014 minimum format is YYYY`);
|
|
41
|
+
}
|
|
42
|
+
let base = s;
|
|
43
|
+
let offsetMin = 0;
|
|
44
|
+
const tzMatch = /([+-])(\d{2})(\d{2})$/.exec(s);
|
|
45
|
+
if (tzMatch) {
|
|
46
|
+
const sign = tzMatch[1] === "+" ? 1 : -1;
|
|
47
|
+
const tzHours = parseInt(tzMatch[2] ?? "0", 10);
|
|
48
|
+
const tzMins = parseInt(tzMatch[3] ?? "0", 10);
|
|
49
|
+
offsetMin = sign * (tzHours * 60 + tzMins);
|
|
50
|
+
base = s.slice(0, s.length - 5);
|
|
51
|
+
}
|
|
52
|
+
let fracMs = 0;
|
|
53
|
+
const dotIdx = base.indexOf(".");
|
|
54
|
+
if (dotIdx !== -1) {
|
|
55
|
+
const fracStr = base.slice(dotIdx);
|
|
56
|
+
fracMs = Math.round(parseFloat(fracStr) * 1e3);
|
|
57
|
+
base = base.slice(0, dotIdx);
|
|
58
|
+
}
|
|
59
|
+
const year = parseInt(base.slice(0, 4), 10);
|
|
60
|
+
const month = parseInt(base.slice(4, 6) || "01", 10) - 1;
|
|
61
|
+
const day = parseInt(base.slice(6, 8) || "01", 10);
|
|
62
|
+
const hour = parseInt(base.slice(8, 10) || "00", 10);
|
|
63
|
+
const min = parseInt(base.slice(10, 12) || "00", 10);
|
|
64
|
+
const sec = parseInt(base.slice(12, 14) || "00", 10);
|
|
65
|
+
if (isNaN(year) || isNaN(month) || isNaN(day)) {
|
|
66
|
+
throw new HL7ParseError(`Cannot parse HL7 date/time: "${s}"`);
|
|
67
|
+
}
|
|
68
|
+
const utcMs = Date.UTC(year, month, day, hour, min, sec, fracMs) - offsetMin * 6e4;
|
|
69
|
+
const result = new Date(utcMs);
|
|
70
|
+
if (isNaN(result.getTime())) {
|
|
71
|
+
throw new HL7ParseError(`Cannot parse HL7 date/time: "${s}"`);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function formatHL7DateTime(date) {
|
|
76
|
+
const pad = (n, len = 2) => String(n).padStart(len, "0");
|
|
77
|
+
return pad(date.getUTCFullYear(), 4) + pad(date.getUTCMonth() + 1) + pad(date.getUTCDate()) + pad(date.getUTCHours()) + pad(date.getUTCMinutes()) + pad(date.getUTCSeconds());
|
|
78
|
+
}
|
|
79
|
+
function formatHL7Date(date) {
|
|
80
|
+
const pad = (n, len = 2) => String(n).padStart(len, "0");
|
|
81
|
+
return pad(date.getUTCFullYear(), 4) + pad(date.getUTCMonth() + 1) + pad(date.getUTCDate());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/escape.ts
|
|
85
|
+
function escRx(s) {
|
|
86
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
87
|
+
}
|
|
88
|
+
function decodeEscapes(value, enc) {
|
|
89
|
+
const esc = enc.escape;
|
|
90
|
+
if (!value.includes(esc)) return value;
|
|
91
|
+
const pattern = new RegExp(`${escRx(esc)}([^${escRx(esc)}]*)${escRx(esc)}`, "g");
|
|
92
|
+
return value.replace(pattern, (match, seq) => {
|
|
93
|
+
switch (seq) {
|
|
94
|
+
case "F":
|
|
95
|
+
return enc.field;
|
|
96
|
+
case "S":
|
|
97
|
+
return enc.component;
|
|
98
|
+
case "T":
|
|
99
|
+
return enc.subComponent;
|
|
100
|
+
case "R":
|
|
101
|
+
return enc.repetition;
|
|
102
|
+
case "E":
|
|
103
|
+
return enc.escape;
|
|
104
|
+
case "H":
|
|
105
|
+
return "\x1B[1m";
|
|
106
|
+
case "N":
|
|
107
|
+
return "\x1B[0m";
|
|
108
|
+
case ".br":
|
|
109
|
+
return "\n";
|
|
110
|
+
default: {
|
|
111
|
+
if (seq.startsWith("X") && seq.length > 1) {
|
|
112
|
+
try {
|
|
113
|
+
const hex = seq.slice(1);
|
|
114
|
+
const bytes = new Uint8Array(hex.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? []);
|
|
115
|
+
return new TextDecoder().decode(bytes);
|
|
116
|
+
} catch {
|
|
117
|
+
return match;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return match;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function encodeEscapes(value, enc) {
|
|
126
|
+
const esc = enc.escape;
|
|
127
|
+
return value.replace(new RegExp(escRx(esc), "g"), `${esc}E${esc}`).replace(new RegExp(escRx(enc.field), "g"), `${esc}F${esc}`).replace(new RegExp(escRx(enc.component), "g"), `${esc}S${esc}`).replace(new RegExp(escRx(enc.repetition), "g"), `${esc}R${esc}`).replace(new RegExp(escRx(enc.subComponent), "g"), `${esc}T${esc}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/schema.ts
|
|
131
|
+
var DEFAULT_ENCODING = {
|
|
132
|
+
field: "|",
|
|
133
|
+
component: "^",
|
|
134
|
+
repetition: "~",
|
|
135
|
+
escape: "\\",
|
|
136
|
+
subComponent: "&"
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/parser.ts
|
|
140
|
+
function parseEncodingChars(mshLine) {
|
|
141
|
+
const field = mshLine[3];
|
|
142
|
+
const component = mshLine[4];
|
|
143
|
+
const repetition = mshLine[5];
|
|
144
|
+
const escape = mshLine[6];
|
|
145
|
+
const subComponent = mshLine[7];
|
|
146
|
+
if (!field || !component || !repetition || !escape || !subComponent) {
|
|
147
|
+
throw new InvalidHL7Error(
|
|
148
|
+
`MSH encoding characters are incomplete \u2014 expected 5 chars at positions 3-7, got: "${mshLine.slice(3, 8)}"`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return { field, component, repetition, escape, subComponent };
|
|
152
|
+
}
|
|
153
|
+
function parseField(raw, enc) {
|
|
154
|
+
return raw.split(enc.repetition).map(
|
|
155
|
+
(rep) => rep.split(enc.component).map(
|
|
156
|
+
(comp) => comp.split(enc.subComponent)
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function parseMSHSegment(line, enc) {
|
|
161
|
+
const parts = line.split(enc.field);
|
|
162
|
+
const fields = [
|
|
163
|
+
// MSH.1 — the field separator character itself
|
|
164
|
+
[[[enc.field]]],
|
|
165
|
+
// MSH.2 — encoding characters verbatim; must NOT be split on component separator
|
|
166
|
+
[[[parts[1] ?? ""]]],
|
|
167
|
+
// MSH.3 onwards — normal fields
|
|
168
|
+
...parts.slice(2).map((f) => parseField(f, enc))
|
|
169
|
+
];
|
|
170
|
+
return { id: "MSH", fields, raw: line };
|
|
171
|
+
}
|
|
172
|
+
function parseGenericSegment(line, enc, lineNum) {
|
|
173
|
+
const sepIdx = line.indexOf(enc.field);
|
|
174
|
+
const id = sepIdx === -1 ? line : line.slice(0, sepIdx);
|
|
175
|
+
if (!/^[A-Z][A-Z0-9]{2}$/.test(id)) {
|
|
176
|
+
throw new HL7ParseError(
|
|
177
|
+
`Invalid segment identifier "${id}" at line ${String(lineNum)} \u2014 expected 3 alphanumeric characters (A-Z, 0-9) starting with a letter`,
|
|
178
|
+
{ line: lineNum, segmentId: id }
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const fieldStrings = sepIdx === -1 ? [] : line.slice(sepIdx + 1).split(enc.field);
|
|
182
|
+
return { id, fields: fieldStrings.map((f) => parseField(f, enc)), raw: line };
|
|
183
|
+
}
|
|
184
|
+
function mshValue(seg, fieldNum) {
|
|
185
|
+
return seg.fields[fieldNum - 1]?.[0]?.[0]?.[0] ?? "";
|
|
186
|
+
}
|
|
187
|
+
function parseMessageType(field) {
|
|
188
|
+
const firstRep = field[0] ?? [];
|
|
189
|
+
const struct = firstRep[2]?.[0];
|
|
190
|
+
return {
|
|
191
|
+
type: firstRep[0]?.[0] ?? "",
|
|
192
|
+
event: firstRep[1]?.[0] ?? "",
|
|
193
|
+
structure: struct !== void 0 && struct !== "" ? struct : void 0
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function detectLineEnding(raw) {
|
|
197
|
+
if (raw.includes("\r\n")) return "\r\n";
|
|
198
|
+
if (raw.includes("\r")) return "\r";
|
|
199
|
+
return "\n";
|
|
200
|
+
}
|
|
201
|
+
function parse(raw, options = {}) {
|
|
202
|
+
if (!raw || !raw.trim()) {
|
|
203
|
+
throw new InvalidHL7Error("Input is empty");
|
|
204
|
+
}
|
|
205
|
+
const normalised = raw.replace(/\r\n/g, "\r").replace(/\n/g, "\r");
|
|
206
|
+
const lines = normalised.split("\r").filter((l) => l.trim().length > 0);
|
|
207
|
+
const firstLine = lines[0];
|
|
208
|
+
if (!firstLine?.startsWith("MSH")) {
|
|
209
|
+
throw new InvalidHL7Error(
|
|
210
|
+
`First non-blank line must be an MSH segment, got: "${(firstLine ?? "").slice(0, 30)}"`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (firstLine.length < 8) {
|
|
214
|
+
throw new InvalidHL7Error(
|
|
215
|
+
"MSH segment is too short to contain encoding characters (minimum length: 8)"
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
const encoding = parseEncodingChars(firstLine);
|
|
219
|
+
const segments2 = lines.map((line, idx) => {
|
|
220
|
+
if (line.startsWith("MSH")) return parseMSHSegment(line, encoding);
|
|
221
|
+
return parseGenericSegment(line, encoding, idx + 1);
|
|
222
|
+
});
|
|
223
|
+
const msh = segments2[0];
|
|
224
|
+
if (!msh) throw new InvalidHL7Error("No MSH segment could be parsed");
|
|
225
|
+
const shouldDecode = options.decodeEscapes === true;
|
|
226
|
+
const decode = (v) => shouldDecode ? decodeEscapes(v, encoding) : v;
|
|
227
|
+
const msgTypeField = msh.fields[8] ?? [];
|
|
228
|
+
return {
|
|
229
|
+
version: decode(mshValue(msh, 12)),
|
|
230
|
+
messageType: parseMessageType(msgTypeField),
|
|
231
|
+
messageControlId: decode(mshValue(msh, 10)),
|
|
232
|
+
timestamp: parseHL7DateTime(mshValue(msh, 7)),
|
|
233
|
+
sendingApplication: decode(mshValue(msh, 3)),
|
|
234
|
+
sendingFacility: decode(mshValue(msh, 4)),
|
|
235
|
+
receivingApplication: decode(mshValue(msh, 5)),
|
|
236
|
+
receivingFacility: decode(mshValue(msh, 6)),
|
|
237
|
+
processingId: mshValue(msh, 11),
|
|
238
|
+
segments: segments2,
|
|
239
|
+
encoding,
|
|
240
|
+
raw: raw.trim(),
|
|
241
|
+
lineEnding: detectLineEnding(raw)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function isHL7(input) {
|
|
245
|
+
return /^MSH[^A-Za-z0-9]/.test(input.trimStart());
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/encoder.ts
|
|
249
|
+
function encodeField(field, enc) {
|
|
250
|
+
return field.map(
|
|
251
|
+
(rep) => rep.map((comp) => comp.join(enc.subComponent)).join(enc.component)
|
|
252
|
+
).join(enc.repetition);
|
|
253
|
+
}
|
|
254
|
+
function encodeMSHSegment(seg, enc) {
|
|
255
|
+
const encodingCharsStr = enc.component + enc.repetition + enc.escape + enc.subComponent;
|
|
256
|
+
const rest = seg.fields.slice(2).map((f) => encodeField(f, enc)).join(enc.field);
|
|
257
|
+
return `MSH${enc.field}${encodingCharsStr}${enc.field}${rest}`;
|
|
258
|
+
}
|
|
259
|
+
function encodeSegment(seg, enc) {
|
|
260
|
+
if (seg.id === "MSH") return encodeMSHSegment(seg, enc);
|
|
261
|
+
const fields = seg.fields.map((f) => encodeField(f, enc)).join(enc.field);
|
|
262
|
+
return `${seg.id}${enc.field}${fields}`;
|
|
263
|
+
}
|
|
264
|
+
function encode(msg, options = {}) {
|
|
265
|
+
const lineEnding = options.lineEnding ?? msg.lineEnding;
|
|
266
|
+
const enc = msg.encoding;
|
|
267
|
+
const lines = msg.segments.map((seg) => encodeSegment(seg, enc));
|
|
268
|
+
const result = lines.join(lineEnding);
|
|
269
|
+
return options.trailingNewline === true ? result + lineEnding : result;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/query.ts
|
|
273
|
+
function segment(msg, id, options = {}) {
|
|
274
|
+
const upper = id.toUpperCase();
|
|
275
|
+
const idx = (options.segmentIndex ?? 1) - 1;
|
|
276
|
+
const matches = msg.segments.filter((s) => s.id === upper);
|
|
277
|
+
const found = matches[idx];
|
|
278
|
+
if (!found) throw new SegmentNotFoundError(upper);
|
|
279
|
+
return found;
|
|
280
|
+
}
|
|
281
|
+
function segments(msg, id) {
|
|
282
|
+
return msg.segments.filter((s) => s.id === id.toUpperCase());
|
|
283
|
+
}
|
|
284
|
+
function hasSegment(msg, id) {
|
|
285
|
+
return msg.segments.some((s) => s.id === id.toUpperCase());
|
|
286
|
+
}
|
|
287
|
+
function get(msg, segmentId, field, component, subComponent, options = {}) {
|
|
288
|
+
const segIdx = (options.segmentIndex ?? 1) - 1;
|
|
289
|
+
const repIdx = (options.repetition ?? 1) - 1;
|
|
290
|
+
const compIdx = (component ?? 1) - 1;
|
|
291
|
+
const subIdx = (subComponent ?? 1) - 1;
|
|
292
|
+
const segs = msg.segments.filter((s) => s.id === segmentId.toUpperCase());
|
|
293
|
+
const seg = segs[segIdx];
|
|
294
|
+
if (!seg) return "";
|
|
295
|
+
const fieldValue = seg.fields[field - 1];
|
|
296
|
+
if (!fieldValue) return "";
|
|
297
|
+
const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? "";
|
|
298
|
+
return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;
|
|
299
|
+
}
|
|
300
|
+
function getFromSegment(seg, msg, field, component, subComponent, options = {}) {
|
|
301
|
+
const repIdx = (options.repetition ?? 1) - 1;
|
|
302
|
+
const compIdx = (component ?? 1) - 1;
|
|
303
|
+
const subIdx = (subComponent ?? 1) - 1;
|
|
304
|
+
const fieldValue = seg.fields[field - 1];
|
|
305
|
+
if (!fieldValue) return "";
|
|
306
|
+
const raw = fieldValue[repIdx]?.[compIdx]?.[subIdx] ?? "";
|
|
307
|
+
return options.decode === true ? decodeEscapes(raw, msg.encoding) : raw;
|
|
308
|
+
}
|
|
309
|
+
function getRepetitions(msg, segmentId, field, options = {}) {
|
|
310
|
+
const segIdx = (options.segmentIndex ?? 1) - 1;
|
|
311
|
+
const segs = msg.segments.filter((s) => s.id === segmentId.toUpperCase());
|
|
312
|
+
const seg = segs[segIdx];
|
|
313
|
+
if (!seg) return [];
|
|
314
|
+
const fieldValue = seg.fields[field - 1];
|
|
315
|
+
if (!fieldValue) return [];
|
|
316
|
+
if (options.decode !== true) return fieldValue;
|
|
317
|
+
return fieldValue.map(
|
|
318
|
+
(rep) => rep.map(
|
|
319
|
+
(comp) => comp.map((sub) => decodeEscapes(sub, msg.encoding))
|
|
320
|
+
)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
exports.DEFAULT_ENCODING = DEFAULT_ENCODING;
|
|
325
|
+
exports.HL7ParseError = HL7ParseError;
|
|
326
|
+
exports.InvalidHL7Error = InvalidHL7Error;
|
|
327
|
+
exports.SegmentNotFoundError = SegmentNotFoundError;
|
|
328
|
+
exports.decodeEscapes = decodeEscapes;
|
|
329
|
+
exports.encode = encode;
|
|
330
|
+
exports.encodeEscapes = encodeEscapes;
|
|
331
|
+
exports.encodeSegment = encodeSegment;
|
|
332
|
+
exports.formatHL7Date = formatHL7Date;
|
|
333
|
+
exports.formatHL7DateTime = formatHL7DateTime;
|
|
334
|
+
exports.get = get;
|
|
335
|
+
exports.getFromSegment = getFromSegment;
|
|
336
|
+
exports.getRepetitions = getRepetitions;
|
|
337
|
+
exports.hasSegment = hasSegment;
|
|
338
|
+
exports.isHL7 = isHL7;
|
|
339
|
+
exports.parse = parse;
|
|
340
|
+
exports.parseHL7DateTime = parseHL7DateTime;
|
|
341
|
+
exports.segment = segment;
|
|
342
|
+
exports.segments = segments;
|
|
343
|
+
//# sourceMappingURL=index.cjs.map
|
|
344
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/datetime.ts","../src/escape.ts","../src/schema.ts","../src/parser.ts","../src/encoder.ts","../src/query.ts"],"names":["segments"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Thrown when an HL7 v2 message string cannot be parsed.\n *\n * The `line` and `segmentId` properties help pinpoint the exact failure location\n * when debugging multi-segment messages from production systems.\n */\nexport class HL7ParseError extends Error {\n /** 1-based line number in the source string where the error occurred. */\n readonly line: number | undefined;\n /** Segment identifier that was being parsed when the error occurred. */\n readonly segmentId: string | undefined;\n\n constructor(\n message: string,\n options?: { line?: number; segmentId?: string; cause?: unknown },\n ) {\n super(message);\n this.name = 'HL7ParseError';\n this.line = options?.line;\n this.segmentId = options?.segmentId;\n if (options?.cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: options.cause,\n writable: true,\n configurable: true,\n });\n }\n }\n}\n\n/**\n * Thrown when a required segment is not present in the message.\n *\n * @example\n * // Thrown by segment() when the segment is not found\n * const pid = segment(msg, 'PID'); // throws if no PID segment\n */\nexport class SegmentNotFoundError extends Error {\n readonly segmentId: string;\n\n constructor(segmentId: string) {\n super(\n `Segment '${segmentId}' not found in message. ` +\n `Use hasSegment() to check before calling segment(), ` +\n `or segments() to get an empty array when absent.`,\n );\n this.name = 'SegmentNotFoundError';\n this.segmentId = segmentId;\n }\n}\n\n/**\n * Thrown when an HL7 message cannot be identified as valid HL7 v2.\n * Usually means the input is empty, truncated, or a completely different format.\n */\nexport class InvalidHL7Error extends Error {\n constructor(reason: string) {\n super(`Input is not a valid HL7 v2 message: ${reason}`);\n this.name = 'InvalidHL7Error';\n }\n}\n","import { HL7ParseError } from './errors.js';\n\n/**\n * Parse an HL7 v2 Date/Time string into a JavaScript `Date`.\n *\n * Supported formats (per HL7 v2 spec section 2.8.21):\n * - `YYYY`\n * - `YYYYMM`\n * - `YYYYMMDD`\n * - `YYYYMMDDHHMM`\n * - `YYYYMMDDHHMMSS`\n * - `YYYYMMDDHHMMSS.S[SSS]`\n * - Any of the above with `+HHMM` or `-HHMM` timezone offset suffix\n *\n * @returns A `Date` object in UTC, or `undefined` if the value is empty.\n * @throws {HL7ParseError} If the string is non-empty but unparseable.\n */\nexport function parseHL7DateTime(raw: string): Date | undefined {\n const s = raw.trim();\n if (!s) return undefined;\n\n // Minimum valid length is 4 (year only)\n if (s.length < 4) {\n throw new HL7ParseError(`Cannot parse HL7 date/time: \"${s}\" — minimum format is YYYY`);\n }\n\n // Extract optional timezone offset: +HHMM or -HHMM at end\n let base = s;\n let offsetMin = 0;\n\n const tzMatch = /([+-])(\\d{2})(\\d{2})$/.exec(s);\n if (tzMatch) {\n const sign = tzMatch[1] === '+' ? 1 : -1;\n const tzHours = parseInt(tzMatch[2] ?? '0', 10);\n const tzMins = parseInt(tzMatch[3] ?? '0', 10);\n offsetMin = sign * (tzHours * 60 + tzMins);\n base = s.slice(0, s.length - 5);\n }\n\n // Parse fractional seconds: YYYYMMDDHHMMSS.SSSS\n let fracMs = 0;\n const dotIdx = base.indexOf('.');\n if (dotIdx !== -1) {\n const fracStr = base.slice(dotIdx);\n fracMs = Math.round(parseFloat(fracStr) * 1000);\n base = base.slice(0, dotIdx);\n }\n\n const year = parseInt(base.slice(0, 4), 10);\n const month = parseInt(base.slice(4, 6) || '01', 10) - 1; // 0-indexed\n const day = parseInt(base.slice(6, 8) || '01', 10);\n const hour = parseInt(base.slice(8, 10) || '00', 10);\n const min = parseInt(base.slice(10, 12)|| '00', 10);\n const sec = parseInt(base.slice(12, 14)|| '00', 10);\n\n if (isNaN(year) || isNaN(month) || isNaN(day)) {\n throw new HL7ParseError(`Cannot parse HL7 date/time: \"${s}\"`);\n }\n\n // Build UTC epoch, then subtract the timezone offset to normalise to UTC\n const utcMs = Date.UTC(year, month, day, hour, min, sec, fracMs) - offsetMin * 60_000;\n const result = new Date(utcMs);\n\n if (isNaN(result.getTime())) {\n throw new HL7ParseError(`Cannot parse HL7 date/time: \"${s}\"`);\n }\n\n return result;\n}\n\n/**\n * Format a JavaScript `Date` as an HL7 v2 timestamp string in UTC.\n * Output format: `YYYYMMDDHHMMSS`\n */\nexport function formatHL7DateTime(date: Date): string {\n const pad = (n: number, len = 2): string => String(n).padStart(len, '0');\n return (\n pad(date.getUTCFullYear(), 4) +\n pad(date.getUTCMonth() + 1) +\n pad(date.getUTCDate()) +\n pad(date.getUTCHours()) +\n pad(date.getUTCMinutes()) +\n pad(date.getUTCSeconds())\n );\n}\n\n/**\n * Format a JavaScript `Date` as an HL7 v2 date-only string in UTC.\n * Output format: `YYYYMMDD`\n */\nexport function formatHL7Date(date: Date): string {\n const pad = (n: number, len = 2): string => String(n).padStart(len, '0');\n return (\n pad(date.getUTCFullYear(), 4) +\n pad(date.getUTCMonth() + 1) +\n pad(date.getUTCDate())\n );\n}\n","import type { EncodingChars } from './schema.js';\n\n/** Escape a string for use in a regex character class. */\nfunction escRx(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Decode HL7 v2 escape sequences within a field value.\n *\n * Handles all standard sequences defined in the HL7 v2.x specification:\n * - `\\F\\` → field separator\n * - `\\S\\` → component separator\n * - `\\T\\` → sub-component separator\n * - `\\R\\` → repetition separator\n * - `\\E\\` → escape character\n * - `\\H\\` → start highlighting (stripped — presentation concern)\n * - `\\N\\` → normal text / end highlighting (stripped)\n * - `\\.br\\` → carriage return / line break → `\\n`\n * - `\\Xhh…\\` → hex-encoded bytes → UTF-8 string\n *\n * Unknown or locally-defined sequences (`\\Zxxx\\`) are preserved as-is.\n *\n * @param value - Raw field value that may contain escape sequences\n * @param enc - Encoding characters from the message's MSH segment\n */\nexport function decodeEscapes(value: string, enc: EncodingChars): string {\n const esc = enc.escape;\n if (!value.includes(esc)) return value;\n\n const pattern = new RegExp(`${escRx(esc)}([^${escRx(esc)}]*)${escRx(esc)}`, 'g');\n\n return value.replace(pattern, (match: string, seq: string): string => {\n switch (seq) {\n case 'F': return enc.field;\n case 'S': return enc.component;\n case 'T': return enc.subComponent;\n case 'R': return enc.repetition;\n case 'E': return enc.escape;\n case 'H': return '\\x1b[1m';\n case 'N': return '\\x1b[0m';\n case '.br': return '\\n';\n default: {\n if (seq.startsWith('X') && seq.length > 1) {\n try {\n const hex = seq.slice(1);\n const bytes = new Uint8Array(hex.match(/.{1,2}/g)?.map(b => parseInt(b, 16)) ?? []);\n return new TextDecoder().decode(bytes);\n } catch {\n return match;\n }\n }\n // Preserve unknown / locally-defined sequences unchanged\n return match;\n }\n }\n });\n}\n\n/**\n * Encode a plain string value for safe inclusion in an HL7 v2 field,\n * escaping any characters that have special meaning in the given encoding.\n *\n * Characters are escaped in this order to avoid double-escaping:\n * 1. Escape character itself (`\\E\\`)\n * 2. Field separator (`\\F\\`)\n * 3. Component separator (`\\S\\`)\n * 4. Repetition separator(`\\R\\`)\n * 5. Sub-component sep (`\\T\\`)\n *\n * @param value - Plain string to encode\n * @param enc - Encoding characters from the target message\n */\nexport function encodeEscapes(value: string, enc: EncodingChars): string {\n const esc = enc.escape;\n // Escape the escape character first to avoid double-escaping\n return value\n .replace(new RegExp(escRx(esc), 'g'), `${esc}E${esc}`)\n .replace(new RegExp(escRx(enc.field), 'g'), `${esc}F${esc}`)\n .replace(new RegExp(escRx(enc.component), 'g'), `${esc}S${esc}`)\n .replace(new RegExp(escRx(enc.repetition), 'g'), `${esc}R${esc}`)\n .replace(new RegExp(escRx(enc.subComponent), 'g'), `${esc}T${esc}`);\n}\n","/**\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"]}
|