@cosyte/hl7 0.0.1 → 0.0.2
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/CHANGELOG.md +436 -147
- package/README.md +154 -91
- package/TRADEMARKS.md +37 -0
- package/dist/index.cjs +1504 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2060 -477
- package/dist/index.d.ts +2060 -477
- package/dist/index.mjs +1479 -112
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -6
package/dist/index.d.cts
CHANGED
|
@@ -11,7 +11,7 @@ import { Buffer } from 'node:buffer';
|
|
|
11
11
|
/**
|
|
12
12
|
* Stable string codes for every Tier-2 warning the parser may emit. The
|
|
13
13
|
* registry is frozen via `as const` so TypeScript infers the exact string
|
|
14
|
-
* literal union for `WarningCode
|
|
14
|
+
* literal union for `WarningCode`: there is zero runtime cost and no
|
|
15
15
|
* magic-string comparisons for consumers.
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
@@ -43,6 +43,7 @@ declare const WARNING_CODES: {
|
|
|
43
43
|
readonly MERGE_MISSING_PRIOR_OR_SURVIVOR: "MERGE_MISSING_PRIOR_OR_SURVIVOR";
|
|
44
44
|
readonly BATCH_COUNT_MISMATCH: "BATCH_COUNT_MISMATCH";
|
|
45
45
|
readonly BATCH_MISSING_TRAILER: "BATCH_MISSING_TRAILER";
|
|
46
|
+
readonly UNTERMINATED_STREAM_MESSAGE: "UNTERMINATED_STREAM_MESSAGE";
|
|
46
47
|
};
|
|
47
48
|
/**
|
|
48
49
|
* Discriminant type for `Hl7ParseWarning.code`. Narrowing a warning by this
|
|
@@ -102,8 +103,8 @@ declare function mllpFramingStripped(position: Hl7Position): Hl7ParseWarning;
|
|
|
102
103
|
* Build a `FIELD_WHITESPACE_TRIMMED` warning. Emitted when the parser trims
|
|
103
104
|
* leading or trailing whitespace from a field value (the `trimFields`
|
|
104
105
|
* option, on by default). The message carries only the leading/trailing
|
|
105
|
-
* character **counts
|
|
106
|
-
* trimming)
|
|
106
|
+
* character **counts**: NEVER the field value itself (before or after
|
|
107
|
+
* trimming): so no PHI is exposed; the trimmed value is still preserved
|
|
107
108
|
* verbatim in the parsed output.
|
|
108
109
|
*
|
|
109
110
|
* @example
|
|
@@ -116,12 +117,12 @@ declare function fieldWhitespaceTrimmed(position: Hl7Position, leadingCount: num
|
|
|
116
117
|
/**
|
|
117
118
|
* Build an `UNKNOWN_ESCAPE_SEQUENCE` warning for a **terminated** escape
|
|
118
119
|
* sequence (`\..\`) whose body is not a recognized HL7 escape. The message
|
|
119
|
-
* NEVER embeds the escape body
|
|
120
|
+
* NEVER embeds the escape body: only its length and, when the body's first
|
|
120
121
|
* character is a recognized HL7 escape-identifier letter (structural HL7
|
|
121
|
-
* grammar, not PHI
|
|
122
|
+
* grammar, not PHI: e.g. `Z` for a vendor escape, `.` for a formatting
|
|
122
123
|
* escape), that single letter. A body that doesn't start with a known escape
|
|
123
124
|
* letter is untrusted field text and names no character at all. The sequence
|
|
124
|
-
* itself is still preserved verbatim in the parsed output
|
|
125
|
+
* itself is still preserved verbatim in the parsed output: only the WARNING
|
|
125
126
|
* message is PHI-safe.
|
|
126
127
|
*
|
|
127
128
|
* @example
|
|
@@ -137,7 +138,7 @@ declare function unknownEscapeSequence(position: Hl7Position, body: string): Hl7
|
|
|
137
138
|
* (an escape character with no closing partner before end-of-input). The
|
|
138
139
|
* "body" in this case is the entire remainder of the field, so the message
|
|
139
140
|
* carries NEITHER the body NOR its length (the length of a truncated tail is
|
|
140
|
-
* itself derivable field-shape information)
|
|
141
|
+
* itself derivable field-shape information): just the fact that an
|
|
141
142
|
* unterminated escape was found, and the position. The remainder is still
|
|
142
143
|
* preserved verbatim in the parsed output.
|
|
143
144
|
*
|
|
@@ -179,7 +180,7 @@ declare function timestampFallbackFormat(position: Hl7Position, matchedFormat: s
|
|
|
179
180
|
declare function segmentCase(position: Hl7Position, observed: string): Hl7ParseWarning;
|
|
180
181
|
/**
|
|
181
182
|
* Build an `EXTRA_FIELDS` warning. Emitted when a segment contains more
|
|
182
|
-
* fields than the profile definition (or HL7 spec) declares
|
|
183
|
+
* fields than the profile definition (or HL7 spec) declares: the extras
|
|
183
184
|
* are preserved on `RawSegment.fields` but flagged for consumers.
|
|
184
185
|
*
|
|
185
186
|
* @example
|
|
@@ -243,10 +244,10 @@ declare function missingRequiredField(position: Hl7Position, segmentName: string
|
|
|
243
244
|
* Build a `MISSING_EXPECTED_GROUP` warning (roadmap Phase G). Emitted once per
|
|
244
245
|
* absent Required segment group when the message's (MSH-9.1, MSH-9.2) type is
|
|
245
246
|
* one the structure safety net recognizes and an expected group is entirely
|
|
246
|
-
* missing
|
|
247
|
+
* missing: e.g. an `ORU^R01` carrying no `OBR`/`OBX` result group, the
|
|
247
248
|
* signature of a truncated or misrouted feed. Tier-2 and additive: lenient
|
|
248
249
|
* parse never throws on it, `strict` mode may promote it. The message carries
|
|
249
|
-
* only the structural fact (message type, group name, anchor segment names)
|
|
250
|
+
* only the structural fact (message type, group name, anchor segment names),
|
|
250
251
|
* NEVER a field value, so no PHI is exposed. `position` references MSH-9.
|
|
251
252
|
*
|
|
252
253
|
* @example
|
|
@@ -291,7 +292,7 @@ declare function versionMismatch(position: Hl7Position, declared: string, expect
|
|
|
291
292
|
* Table-0211 character set**. The parser never guesses an encoding: it reads the
|
|
292
293
|
* raw bytes as `latin1` (a 1:1 byte mapping) so single-byte content stays
|
|
293
294
|
* recoverable, rather than corrupting it with replacement characters. The
|
|
294
|
-
* message carries the charset code only
|
|
295
|
+
* message carries the charset code only, never a decoded field value, so no
|
|
295
296
|
* PHI is exposed.
|
|
296
297
|
*
|
|
297
298
|
* @example
|
|
@@ -303,18 +304,18 @@ declare function versionMismatch(position: Hl7Position, declared: string, expect
|
|
|
303
304
|
declare function unknownCharset(position: Hl7Position, requested: string): Hl7ParseWarning;
|
|
304
305
|
/**
|
|
305
306
|
* Build an `UNSUPPORTED_CHARSET` warning. Emitted when a **recognized**
|
|
306
|
-
* Table-0211 character set is not decoded into text
|
|
307
|
-
* does not decode it (the multibyte / ISO-2022-switched East-Asian sets
|
|
308
|
-
* GB 18030, KS X 1001, CNS 11643, BIG-5
|
|
307
|
+
* Table-0211 character set is not decoded into text: either because this parser
|
|
308
|
+
* does not decode it (the multibyte / ISO-2022-switched East-Asian sets: JIS,
|
|
309
|
+
* GB 18030, KS X 1001, CNS 11643, BIG-5: and the wide Unicode transforms
|
|
309
310
|
* UTF-16 / UTF-32), or because a strict decode of a decodable set **failed** (a
|
|
310
311
|
* byte invalid / undefined for the declared set, or an ICU build lacking the
|
|
311
312
|
* label). In every case the raw bytes are read as `latin1`, never guessed at, so
|
|
312
313
|
* the parser does not emit replacement-char-corrupted text. Single-byte content
|
|
313
314
|
* stays byte-recoverable; multibyte content is best-effort (a content byte can
|
|
314
|
-
* coincide with a structural delimiter
|
|
315
|
+
* coincide with a structural delimiter: see the parser's known-limitations).
|
|
315
316
|
* The switch escapes (`\Cxxyy\` / `\Mxxyyzz\`) are recognized and preserved by
|
|
316
317
|
* the escape layer; full stateful decoding is a documented non-goal. The message
|
|
317
|
-
* carries the charset code only
|
|
318
|
+
* carries the charset code only, never a decoded field value, so no PHI is
|
|
318
319
|
* exposed.
|
|
319
320
|
*
|
|
320
321
|
* @example
|
|
@@ -330,7 +331,7 @@ declare function unsupportedCharset(position: Hl7Position, code: string): Hl7Par
|
|
|
330
331
|
* so the generated ACK leaves MSA-2 empty and, when a positive accept was
|
|
331
332
|
* requested, downgrades it to an error code rather than fabricating an
|
|
332
333
|
* unverifiable `AA`/`CA`. The `position` references the inbound MSH segment.
|
|
333
|
-
* The message NEVER echoes a PHI value
|
|
334
|
+
* The message NEVER echoes a PHI value: only the structural fact.
|
|
334
335
|
*
|
|
335
336
|
* @example
|
|
336
337
|
* ```ts
|
|
@@ -342,20 +343,20 @@ declare function unsupportedCharset(position: Hl7Position, code: string): Hl7Par
|
|
|
342
343
|
declare function ackNoCorrelationId(position: Hl7Position): Hl7ParseWarning;
|
|
343
344
|
/**
|
|
344
345
|
* Build a `MERGE_MISSING_PRIOR_OR_SURVIVOR` warning (roadmap Phase K). Emitted
|
|
345
|
-
* by `identityEvents()` (a read-side helper
|
|
346
|
+
* by `identityEvents()` (a read-side helper: it attaches to the returned
|
|
346
347
|
* `IdentityEvent.warnings`, never to `Hl7Message.warnings`) when a merge/move
|
|
347
348
|
* trigger event (A18/A34/A35/A36/A39/A40/A41/A42/A43/A44) is missing one side
|
|
348
|
-
* of the spec-mandated MRG (prior) → PID (surviving) pair
|
|
349
|
+
* of the spec-mandated MRG (prior) → PID (surviving) pair: or when that side
|
|
349
350
|
* carries no usable identity field: no MRG segment in the patient group, no
|
|
350
351
|
* PID for an orphaned MRG, or a PID/MRG whose identifier, account, and visit
|
|
351
352
|
* fields are all empty or version-gated (a v2.7+ MRG whose only content was
|
|
352
353
|
* the withdrawn MRG-4 must not read as "nothing to retire"). The helper
|
|
353
354
|
* surfaces whatever IS present and never
|
|
354
|
-
* guesses the merge direction
|
|
355
|
+
* guesses the merge direction: this warning is the signal that the pair is
|
|
355
356
|
* incomplete.
|
|
356
357
|
*
|
|
357
358
|
* The message carries only the structural facts (trigger event code + which
|
|
358
|
-
* role is missing)
|
|
359
|
+
* role is missing): NEVER an identifier, name, or any other field value, so
|
|
359
360
|
* no PHI is exposed (HL7 v2 Ch. 3: the PID carries surviving and the MRG
|
|
360
361
|
* carries non-surviving identity information).
|
|
361
362
|
*
|
|
@@ -368,18 +369,18 @@ declare function ackNoCorrelationId(position: Hl7Position): Hl7ParseWarning;
|
|
|
368
369
|
declare function mergeMissingPriorOrSurvivor(position: Hl7Position, eventType: string, missing: "prior" | "survivor"): Hl7ParseWarning;
|
|
369
370
|
/**
|
|
370
371
|
* Build a `BATCH_COUNT_MISMATCH` warning (roadmap Phase L). Emitted by
|
|
371
|
-
* `splitBatch()
|
|
372
|
-
* to `Hl7Message.warnings
|
|
372
|
+
* `splitBatch()`: attached to the returned `BatchSplitResult.warnings`, never
|
|
373
|
+
* to `Hl7Message.warnings`: when a declared envelope count does not equal the
|
|
373
374
|
* count actually split out: a **BTS-1** batch message count that differs from
|
|
374
375
|
* the messages found in that batch, or an **FTS-1** file batch count that
|
|
375
376
|
* differs from the batches found in the file (HL7 v2 Ch. 2 §2.10.3; BTS-1 / FTS-1
|
|
376
377
|
* per the BTS / FTS segment definitions in §2.15).
|
|
377
|
-
* The splitter **never drops the tail** to make the numbers agree
|
|
378
|
+
* The splitter **never drops the tail** to make the numbers agree: the
|
|
378
379
|
* mismatch is surfaced and every message is still returned; the caller decides
|
|
379
380
|
* whether to reject.
|
|
380
381
|
*
|
|
381
382
|
* The message carries only the declared-vs-actual **integers** and the unit
|
|
382
|
-
* (`message` for BTS-1, `batch` for FTS-1)
|
|
383
|
+
* (`message` for BTS-1, `batch` for FTS-1): NEVER a field value, facility
|
|
383
384
|
* identifier, or any other content, so no PHI is exposed. `position` references
|
|
384
385
|
* the trailer segment (`BTS`/`FTS`) that declared the count.
|
|
385
386
|
*
|
|
@@ -392,15 +393,15 @@ declare function mergeMissingPriorOrSurvivor(position: Hl7Position, eventType: s
|
|
|
392
393
|
declare function batchCountMismatch(position: Hl7Position, unit: "message" | "batch", declared: number, actual: number): Hl7ParseWarning;
|
|
393
394
|
/**
|
|
394
395
|
* Build a `BATCH_MISSING_TRAILER` warning (roadmap Phase L). Emitted by
|
|
395
|
-
* `splitBatch()`
|
|
396
|
+
* `splitBatch()` (attached to `BatchSplitResult.warnings`) when an envelope
|
|
396
397
|
* header opens a scope that is never closed: a **BHS** batch header with no
|
|
397
398
|
* matching **BTS** trailer, or an **FHS** file header with no matching **FTS**
|
|
398
|
-
* trailer (HL7 v2 Ch. 2 §2.10.3
|
|
399
|
+
* trailer (HL7 v2 Ch. 2 §2.10.3: each envelope segment is optional, but a
|
|
399
400
|
* profile such as an IIS file-submission spec may mandate the full frame).
|
|
400
|
-
* `splitBatch` **does not enforce** such a rule
|
|
401
|
+
* `splitBatch` **does not enforce** such a rule: it splits, warns, and leaves
|
|
401
402
|
* the accept/reject decision to the caller; the parse never throws for this.
|
|
402
403
|
*
|
|
403
|
-
* The message carries only the header/trailer segment names
|
|
404
|
+
* The message carries only the header/trailer segment names: NEVER a field
|
|
404
405
|
* value, so no PHI is exposed. `position` references the unmatched header
|
|
405
406
|
* segment (`FHS`/`BHS`).
|
|
406
407
|
*
|
|
@@ -411,13 +412,34 @@ declare function batchCountMismatch(position: Hl7Position, unit: "message" | "ba
|
|
|
411
412
|
* ```
|
|
412
413
|
*/
|
|
413
414
|
declare function batchMissingTrailer(position: Hl7Position, header: "FHS" | "BHS", expectedTrailer: "FTS" | "BTS"): Hl7ParseWarning;
|
|
415
|
+
/**
|
|
416
|
+
* Build an `UNTERMINATED_STREAM_MESSAGE` warning (roadmap Phase S). Emitted by
|
|
417
|
+
* `parseStream()`: attached to a {@link StreamMessageEntry}'s `streamWarnings`,
|
|
418
|
+
* NOT to `Hl7Message.warnings`: when the **final** message in a stream ends
|
|
419
|
+
* without a segment terminator (its last segment ran to end-of-stream with no
|
|
420
|
+
* trailing `\r`/`\r\n`/`\n`). The message is still parsed and yielded **in
|
|
421
|
+
* full**; this is the fail-safe signal that the tail may have been truncated
|
|
422
|
+
* mid-message (a cut-off feed), never a reason to drop it and never a throw.
|
|
423
|
+
* Only the last message can be unterminated: every earlier message is closed by
|
|
424
|
+
* the terminator that precedes the next `MSH`/envelope boundary.
|
|
425
|
+
*
|
|
426
|
+
* The message carries only the structural fact, NEVER a field value, so no PHI
|
|
427
|
+
* is exposed. `position` references the message's `MSH` segment index.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* import { unterminatedStreamMessage } from "@cosyte/hl7";
|
|
432
|
+
* const w = unterminatedStreamMessage({ segmentIndex: 6 });
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
declare function unterminatedStreamMessage(position: Hl7Position): Hl7ParseWarning;
|
|
414
436
|
|
|
415
437
|
/**
|
|
416
438
|
* Shared type definitions consumed across the `@cosyte/hl7` parser
|
|
417
439
|
* pipeline. These types are contracts between parser stages (normalize,
|
|
418
440
|
* mllp, segments, delimiters, tokenize) and the `Hl7Message` model shell.
|
|
419
441
|
*
|
|
420
|
-
* Every type here is deliberately readonly
|
|
442
|
+
* Every type here is deliberately readonly: the parser produces immutable
|
|
421
443
|
* data structures and consumers must not mutate them. Narrowing is done via
|
|
422
444
|
* the `Hl7ParseWarning.code` and `Hl7ParseError.code` discriminants defined
|
|
423
445
|
* in sibling files.
|
|
@@ -426,14 +448,14 @@ declare function batchMissingTrailer(position: Hl7Position, header: "FHS" | "BHS
|
|
|
426
448
|
/**
|
|
427
449
|
* Positional context attached to every warning and fatal error. Fields are
|
|
428
450
|
* 1-indexed against the HL7 spec convention (see `RawSegment.fields` for the
|
|
429
|
-
* index 0 slot convention). All fields past `segmentIndex` are optional
|
|
451
|
+
* index 0 slot convention). All fields past `segmentIndex` are optional,
|
|
430
452
|
* for a top-level fatal like `EMPTY_INPUT` only `segmentIndex: 0` is
|
|
431
453
|
* populated; for a tokenizer warning deep inside a subcomponent all five
|
|
432
454
|
* indices may be set.
|
|
433
455
|
*
|
|
434
456
|
* @remarks
|
|
435
457
|
* With `exactOptionalPropertyTypes: true`, do not pass `fieldIndex: undefined`
|
|
436
|
-
* explicitly
|
|
458
|
+
* explicitly: omit the key instead.
|
|
437
459
|
*
|
|
438
460
|
* @example
|
|
439
461
|
* ```ts
|
|
@@ -470,7 +492,7 @@ type OnWarningCallback = (warning: Hl7ParseWarning) => void;
|
|
|
470
492
|
*
|
|
471
493
|
* @remarks
|
|
472
494
|
* With `exactOptionalPropertyTypes: true`, callers cannot pass
|
|
473
|
-
* `{ strict: undefined }
|
|
495
|
+
* `{ strict: undefined }`: either omit the key or pass a boolean. The
|
|
474
496
|
* `profile: null` form is the explicit opt-out from the process-scoped
|
|
475
497
|
* default profile (PROF-08 semantics); `profile` omitted means "use the
|
|
476
498
|
* default if one is registered".
|
|
@@ -531,14 +553,14 @@ interface CustomSegmentDefinition {
|
|
|
531
553
|
/**
|
|
532
554
|
* Structural placeholder for HL7 profiles. A profile bundles vendor-specific
|
|
533
555
|
* tolerances, date formats, custom segment definitions, and optional
|
|
534
|
-
* callbacks. Phase 2 ships only the type
|
|
556
|
+
* callbacks. Phase 2 ships only the type: the `defineProfile()` builder
|
|
535
557
|
* and runtime effects land in Phase 6.
|
|
536
558
|
*
|
|
537
559
|
* Phase 6 Plan 01 tightens `customSegments` to the locked
|
|
538
560
|
* `CustomSegmentDefinition` shape and adds an optional `describe?` method
|
|
539
561
|
* so `defineProfile()`-produced profiles can be introspected without
|
|
540
562
|
* consumers needing to narrow away from the `Profile` type. The `describe`
|
|
541
|
-
* method is only populated by `defineProfile()
|
|
563
|
+
* method is only populated by `defineProfile()`: hand-authored `Profile`
|
|
542
564
|
* objects may omit it.
|
|
543
565
|
*
|
|
544
566
|
* @example
|
|
@@ -565,8 +587,8 @@ interface Profile {
|
|
|
565
587
|
}
|
|
566
588
|
/**
|
|
567
589
|
* The HL7 delimiter characters discovered from MSH-1 (field separator) and
|
|
568
|
-
* MSH-2 (encoding characters). The first four
|
|
569
|
-
* escape, subcomponent
|
|
590
|
+
* MSH-2 (encoding characters). The first four: component, repetition,
|
|
591
|
+
* escape, subcomponent: are mandatory across all HL7 v2 versions. The fifth,
|
|
570
592
|
* `truncation`, is the v2.7+ truncation character (default `#` per spec
|
|
571
593
|
* §2.5.5.2): only present when MSH-2 actually carries 5 encoding characters,
|
|
572
594
|
* so messages that pre-date v2.7 round-trip with a 4-char MSH-2 unchanged.
|
|
@@ -593,7 +615,7 @@ interface EncodingCharacters {
|
|
|
593
615
|
readonly truncation?: string;
|
|
594
616
|
}
|
|
595
617
|
/**
|
|
596
|
-
* A single component inside a repetition
|
|
618
|
+
* A single component inside a repetition: the most deeply nested data
|
|
597
619
|
* layer in the HL7 positional tree. A component is an ordered list of
|
|
598
620
|
* subcomponent strings (subcomponent separator `&` by default).
|
|
599
621
|
*
|
|
@@ -605,7 +627,7 @@ interface EncodingCharacters {
|
|
|
605
627
|
*/
|
|
606
628
|
interface RawComponent {
|
|
607
629
|
/**
|
|
608
|
-
* The component's subcomponents, **HL7-decoded
|
|
630
|
+
* The component's subcomponents, **HL7-decoded**: the tokenizer expands
|
|
609
631
|
* escape sequences on parse so consumers read literal values (`Smith|Jones`,
|
|
610
632
|
* not `Smith\F\Jones`). This is the value surface every reader uses
|
|
611
633
|
* (`Field.value`, the composite parsers, dot-path, `toJSON`).
|
|
@@ -616,7 +638,7 @@ interface RawComponent {
|
|
|
616
638
|
* for each index, the subcomponent's **original wire bytes** to emit
|
|
617
639
|
* verbatim instead of re-escaping the decoded form. An entry is present
|
|
618
640
|
* (non-`undefined`) only when a subcomponent carried an escape whose decoded
|
|
619
|
-
* form does not re-escape back to the exact wire bytes
|
|
641
|
+
* form does not re-escape back to the exact wire bytes: i.e. a recognize-
|
|
620
642
|
* and-preserve escape (`\H\`, `\Z..\`, charset/formatting) or a hex escape
|
|
621
643
|
* (`\X41\`, or non-canonical hex casing). Delimiter escapes (`\F\`→`|`) round-
|
|
622
644
|
* trip through `reescape` unchanged and get no overlay. The whole field is
|
|
@@ -632,7 +654,7 @@ interface RawComponent {
|
|
|
632
654
|
readonly rawSubcomponents?: readonly (string | undefined)[];
|
|
633
655
|
}
|
|
634
656
|
/**
|
|
635
|
-
* A single repetition inside a field
|
|
657
|
+
* A single repetition inside a field: HL7 fields may repeat using the
|
|
636
658
|
* repetition separator (`~` by default). Each repetition is an ordered list
|
|
637
659
|
* of components.
|
|
638
660
|
*
|
|
@@ -663,7 +685,7 @@ interface RawField {
|
|
|
663
685
|
readonly isNull: boolean;
|
|
664
686
|
}
|
|
665
687
|
/**
|
|
666
|
-
* A parsed HL7 segment
|
|
688
|
+
* A parsed HL7 segment: the top level of the positional tree. The `name`
|
|
667
689
|
* is the three-character segment identifier (`MSH`, `PID`, `ZPI`, ...) and
|
|
668
690
|
* `fields` is the 1-indexed positional field array (see the JSDoc on
|
|
669
691
|
* `fields` for the index 0 slot convention).
|
|
@@ -708,7 +730,7 @@ interface RawSegment {
|
|
|
708
730
|
* them, which keeps custom-delimiter behaviour (PARSE-02) consistent across
|
|
709
731
|
* the pipeline.
|
|
710
732
|
*
|
|
711
|
-
* Only three of the four Tier-3 fatal codes originate here
|
|
733
|
+
* Only three of the four Tier-3 fatal codes originate here,
|
|
712
734
|
* `NO_MSH_SEGMENT`, `MSH_TOO_SHORT`, `INVALID_ENCODING_CHARACTERS`. The
|
|
713
735
|
* fourth, `EMPTY_INPUT`, is owned by the `normalize.ts` preprocessing stage.
|
|
714
736
|
*/
|
|
@@ -727,23 +749,182 @@ interface RawSegment {
|
|
|
727
749
|
declare const DEFAULT_ENCODING_CHARACTERS: EncodingCharacters;
|
|
728
750
|
|
|
729
751
|
/**
|
|
730
|
-
*
|
|
752
|
+
* Formatted-text rendering for `@cosyte/hl7`: turns HL7 v2 §2.7
|
|
753
|
+
* escape/formatting-bearing field content into a **normalized display model**
|
|
754
|
+
* a human (or a downstream display / FHIR `.text`) can read, instead of the
|
|
755
|
+
* raw `\H\`/`\.br\` sentinels the lenient parser preserves.
|
|
756
|
+
*
|
|
757
|
+
* This is the read-projection half of Phase R: `renderText` never mutates the
|
|
758
|
+
* raw field value (rendering is opt-in on top of the tolerant parse), and it
|
|
759
|
+
* **never fabricates**: an escape it cannot render is preserved as its literal
|
|
760
|
+
* characters AND flagged in {@link RenderedText.unrenderedSequences}, never
|
|
761
|
+
* dropped and never replaced with a guessed glyph.
|
|
762
|
+
*
|
|
763
|
+
* **Clinical-safety angle.** A lab/clinical narrative surfaced with raw
|
|
764
|
+
* `\.br\` / `\H\` sentinels is *misread* by a human; that is the harm this
|
|
765
|
+
* prevents. Formatting commands become real whitespace/line-breaks and
|
|
766
|
+
* highlight boundaries are dropped from the plain text (and exposed as
|
|
767
|
+
* `highlighted` runs for consumers that want emphasis), so what a reader sees
|
|
768
|
+
* is the intended text: never a sentinel and never an invented character.
|
|
769
|
+
*
|
|
770
|
+
* ## Spec traceability (HL7 v2 Chapter 2, §2.7: confirmed firsthand
|
|
771
|
+
* 2026-07-21 against the v2.5.1 and v2.8.2 chapter text; section numbers shift
|
|
772
|
+
* by version)
|
|
773
|
+
*
|
|
774
|
+
* - **§2.7.1 formatting codes**: the delimiter/escape substitutions
|
|
775
|
+
* `\F\ \S\ \T\ \R\ \E\` (→ the message's own field/component/subcomponent/
|
|
776
|
+
* repetition/escape characters), the highlight pair `\H\` (start) / `\N\`
|
|
777
|
+
* (normal, i.e. end), and (v2.7+) `\P\` (the truncation character).
|
|
778
|
+
* - **§2.7.2/2.7.3 charset switch**: `\Cxxyy\` (single-byte) / `\Mxxyyzz\`
|
|
779
|
+
* (multi-byte, `zz` optional): selects an alternate MSH-18 character set for
|
|
780
|
+
* the following run. Decoding requires stateful charset context this module
|
|
781
|
+
* does not have, so a switch is **preserved + flagged**, never guessed.
|
|
782
|
+
* - **§2.7.5/2.7.6 hex**: `\Xdddd…\`: consecutive **pairs** of hex digits,
|
|
783
|
+
* each pair one 8-bit byte.
|
|
784
|
+
* - **§2.7.6/2.7.7 formatting commands** (FT data type): `\.br\` `\.sp\`
|
|
785
|
+
* `\.in\` `\.ti\` `\.fi\` `\.nf\` `\.ce\` `\.sk\`; `.sp` takes an optional
|
|
786
|
+
* positive count, `.in`/`.ti` a signed indent, `.sk` a space count. Rendered
|
|
787
|
+
* to a conservative plain-text normalization (see the table below).
|
|
788
|
+
* - **§2.7.7/2.7.8 locally-defined**: `\Zdddd…\`: opaque, meaning is a
|
|
789
|
+
* sender/receiver agreement, so **preserved + flagged**, never interpreted.
|
|
790
|
+
*
|
|
791
|
+
* ## Normalization policy (conservative, documented: not a page-layout engine)
|
|
792
|
+
*
|
|
793
|
+
* | Escape | Rendered as |
|
|
794
|
+
* | --------------------------- | --------------------------------------------- |
|
|
795
|
+
* | `\F\ \S\ \T\ \R\ \E\ \P\` | the literal delimiter / escape / trunc char |
|
|
796
|
+
* | `\Xdddd…\` | the decoded byte(s) |
|
|
797
|
+
* | `\H\` / `\N\` | dropped from text; toggles a `highlighted` run |
|
|
798
|
+
* | `\.br\` | one line break |
|
|
799
|
+
* | `\.sp\` / `\.sp <n>\` | `n` line breaks (default 1, clamped to 100) |
|
|
800
|
+
* | `\.ce\` | one line break (centering dropped) |
|
|
801
|
+
* | `\.in <n>\` / `\.ti <n>\` | dropped (indentation: exact column math is a defer) |
|
|
802
|
+
* | `\.fi\` / `\.nf\` | dropped (word-wrap mode: no content) |
|
|
803
|
+
* | `\.sk <n>\` | `n` spaces (default 1, clamped to 100) |
|
|
804
|
+
* | `\Cxxyy\` `\Mxxyyzz\` `\Zdddd…\`, malformed / unterminated | literal chars, **preserved + flagged** |
|
|
805
|
+
*
|
|
806
|
+
* A raw CR / LF / CRLF already present in the input (e.g. a `\.br\` the parser
|
|
807
|
+
* already decoded to `\n`, or a hex-encoded `\X0D\`/`\X0A\`) is normalized to
|
|
808
|
+
* one line break too, so rendering a pre-decoded `Field.value` and rendering
|
|
809
|
+
* the wire text agree. A `\.sp <n>\`/`\.sk <n>\` count is clamped to a small
|
|
810
|
+
* maximum (a conservative renderer need not honor a pathological skip, and an
|
|
811
|
+
* unbounded count is a memory / `RangeError` foot-gun on a hostile feed).
|
|
812
|
+
*
|
|
813
|
+
* @packageDocumentation
|
|
814
|
+
*/
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* One contiguous run of rendered display text, tagged with whether it fell
|
|
818
|
+
* inside a `\H\`…\`\N\` highlight span. Runs preserve emphasis boundaries that
|
|
819
|
+
* the flat {@link RenderedText.text} intentionally drops.
|
|
820
|
+
*/
|
|
821
|
+
interface TextRun {
|
|
822
|
+
/**
|
|
823
|
+
* The literal display text of this run: escape sentinels already resolved
|
|
824
|
+
* (delimiter/hex decoded, formatting → whitespace/line breaks). Never
|
|
825
|
+
* contains a formatting sentinel; may contain the newline used for breaks.
|
|
826
|
+
*/
|
|
827
|
+
readonly text: string;
|
|
828
|
+
/** `true` when this run is inside a `\H\`…`\N\` highlight span. */
|
|
829
|
+
readonly highlighted: boolean;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* The normalized display model produced by {@link renderText}: a flat
|
|
833
|
+
* plain-text string plus the structured highlight-aware runs, plus an
|
|
834
|
+
* honesty list of the escape sequences that were preserved rather than
|
|
835
|
+
* rendered.
|
|
836
|
+
*/
|
|
837
|
+
interface RenderedText {
|
|
838
|
+
/**
|
|
839
|
+
* The full plain-text normalization: formatting commands become
|
|
840
|
+
* whitespace / line breaks and highlight boundaries are dropped. This is
|
|
841
|
+
* the string to show a human or feed a downstream `.text`. Equal to the
|
|
842
|
+
* concatenation of every {@link runs} entry's `text`.
|
|
843
|
+
*/
|
|
844
|
+
readonly text: string;
|
|
845
|
+
/**
|
|
846
|
+
* The structured, highlight-aware form: `{ text, highlighted }` runs in
|
|
847
|
+
* document order. Empty runs are elided. Use this to preserve emphasis
|
|
848
|
+
* (bold / reverse-video / etc.) that the flat {@link text} drops.
|
|
849
|
+
*/
|
|
850
|
+
readonly runs: readonly TextRun[];
|
|
851
|
+
/**
|
|
852
|
+
* The escape sequences `renderText` **preserved verbatim instead of
|
|
853
|
+
* rendering**: vendor `\Zdddd…\`, charset switches `\Cxxyy\`/`\Mxxyyzz\`,
|
|
854
|
+
* and any malformed / unterminated sequence. Their literal characters ALSO
|
|
855
|
+
* appear in {@link text}/{@link runs} (never silently dropped); this list
|
|
856
|
+
* exists so a consumer can detect that a non-render decision was made and
|
|
857
|
+
* surface or route those sequences deliberately. Empty when everything
|
|
858
|
+
* rendered cleanly.
|
|
859
|
+
*/
|
|
860
|
+
readonly unrenderedSequences: readonly string[];
|
|
861
|
+
}
|
|
862
|
+
/** Options for {@link renderText}. */
|
|
863
|
+
interface RenderTextOptions {
|
|
864
|
+
/**
|
|
865
|
+
* The string emitted for each line break (`\.br\`, `\.sp\`, `\.ce\`, and a
|
|
866
|
+
* raw CR/LF/CRLF in the input). Defaults to `"\n"`. Set `"\r\n"` for
|
|
867
|
+
* Windows-style display, or `" "` to flatten a note to a single line.
|
|
868
|
+
*/
|
|
869
|
+
readonly newline?: string;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Render an HL7 v2 escape/formatting-bearing string into a normalized display
|
|
873
|
+
* model (plain text + highlight-aware runs). A **read projection**: it never
|
|
874
|
+
* mutates the underlying field, and it never fabricates: an escape it cannot
|
|
875
|
+
* render is preserved as literal characters and flagged in
|
|
876
|
+
* {@link RenderedText.unrenderedSequences}.
|
|
877
|
+
*
|
|
878
|
+
* `input` is the field's escape-bearing text. Pass the **wire form** (e.g.
|
|
879
|
+
* `Field.text`, byte-verbatim for parsed content) for the most faithful
|
|
880
|
+
* result: a wire `\E\H\E\` (an escaped literal backslash-H-backslash) then
|
|
881
|
+
* renders as the three literal characters `\H\`, never as a highlight. Passing
|
|
882
|
+
* an already-decoded `Field.value` also works: its `\.br\` is already a
|
|
883
|
+
* newline (rendered as one line break) and its `\H\`/formatting sentinels are
|
|
884
|
+
* still recognized.
|
|
885
|
+
*
|
|
886
|
+
* Never throws for any input.
|
|
887
|
+
*
|
|
888
|
+
* @param input - the escape-bearing field text to render.
|
|
889
|
+
* @param enc - the message's encoding characters (for `\F\`/`\S\`/… targets);
|
|
890
|
+
* defaults to the HL7 standard `|^~\&`.
|
|
891
|
+
* @param opts - see {@link RenderTextOptions}.
|
|
892
|
+
* @returns the normalized {@link RenderedText}.
|
|
893
|
+
*
|
|
894
|
+
* @example
|
|
895
|
+
* ```ts
|
|
896
|
+
* import { renderText } from "@cosyte/hl7";
|
|
897
|
+
*
|
|
898
|
+
* const r = renderText("Specimen received.\\.br\\Gross exam \\H\\normal\\N\\.");
|
|
899
|
+
* r.text;
|
|
900
|
+
* // "Specimen received.\nGross exam normal."
|
|
901
|
+
* r.runs;
|
|
902
|
+
* // [ { text: "Specimen received.\nGross exam ", highlighted: false },
|
|
903
|
+
* // { text: "normal", highlighted: true },
|
|
904
|
+
* // { text: ".", highlighted: false } ]
|
|
905
|
+
* r.unrenderedSequences; // []
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
declare function renderText(input: string, enc?: EncodingCharacters, opts?: RenderTextOptions): RenderedText;
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* CE: HL7 v2 Coded Element composite. 6-component coded-element shape
|
|
731
912
|
* parsed from a `RawRepetition` on demand by `Field.asCe()` (wired in Plan
|
|
732
|
-
* 04). Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
913
|
+
* 04). Fields are OMITTED when absent (exactOptionalPropertyTypes): NEVER
|
|
733
914
|
* set to `undefined`.
|
|
734
915
|
*
|
|
735
|
-
* CE is the older, simpler sibling of CWE
|
|
916
|
+
* CE is the older, simpler sibling of CWE: 6 components, no version ids,
|
|
736
917
|
* no originalText. Still common in OBX.3 and similar observation-identifier
|
|
737
918
|
* slots on older messages. CE was deprecated at HL7 v2.5 and withdrawn at
|
|
738
|
-
* v2.6 in favor of CWE; the two are read uniformly here
|
|
919
|
+
* v2.6 in favor of CWE; the two are read uniformly here: reading a
|
|
739
920
|
* CWE-shaped value through `asCe()` preserves components 7+ on
|
|
740
921
|
* `extraComponents` rather than dropping them, so neither accessor is lossy.
|
|
741
922
|
*
|
|
742
|
-
* Zero runtime deps
|
|
923
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
743
924
|
*/
|
|
744
925
|
|
|
745
926
|
/**
|
|
746
|
-
* HL7 v2 Coded Element (CE)
|
|
927
|
+
* HL7 v2 Coded Element (CE): coded element per HL7 Chapter 2. All 6
|
|
747
928
|
* components are optional. Fields are OMITTED when the underlying component
|
|
748
929
|
* is absent (exactOptionalPropertyTypes).
|
|
749
930
|
*
|
|
@@ -756,7 +937,7 @@ declare const DEFAULT_ENCODING_CHARACTERS: EncodingCharacters;
|
|
|
756
937
|
* 6. nameOfAlternateCodingSystem
|
|
757
938
|
*
|
|
758
939
|
* Components 7+ (present when a CWE-shaped value is read through the CE
|
|
759
|
-
* accessor
|
|
940
|
+
* accessor: e.g. version ids, originalText) are surfaced verbatim on
|
|
760
941
|
* `extraComponents` rather than dropped.
|
|
761
942
|
*
|
|
762
943
|
* @example
|
|
@@ -783,7 +964,7 @@ interface CE {
|
|
|
783
964
|
}
|
|
784
965
|
/**
|
|
785
966
|
* Parse an HL7 v2 CE repetition into a structured `CE` object. Components
|
|
786
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
967
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
787
968
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
788
969
|
* from the result (exactOptionalPropertyTypes semantics).
|
|
789
970
|
*
|
|
@@ -802,24 +983,24 @@ interface CE {
|
|
|
802
983
|
declare function parseCe(rep: RawRepetition, enc: EncodingCharacters): CE;
|
|
803
984
|
|
|
804
985
|
/**
|
|
805
|
-
* CWE
|
|
986
|
+
* CWE: HL7 v2 Coded with Exceptions composite. 9-component coded-element
|
|
806
987
|
* shape parsed from a `RawRepetition` on demand by `Field.asCwe()` (wired in
|
|
807
|
-
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
988
|
+
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes),
|
|
808
989
|
* NEVER set to `undefined`.
|
|
809
990
|
*
|
|
810
991
|
* Note: full HL7 v2.6+ CWE has 22 components. v1 of this library ships the 9
|
|
811
|
-
* core components
|
|
812
|
-
* originalText
|
|
813
|
-
* the full shape. Components 10+ that a newer (v2.7+) sender supplies
|
|
814
|
-
* second-alternate triplet and the coding-system / value-set OIDs
|
|
992
|
+
* core components: identifier, text, coding-system trio + version ids, and
|
|
993
|
+
* originalText: which cover the common HL7 v2.5 use cases. v2 may restore
|
|
994
|
+
* the full shape. Components 10+ that a newer (v2.7+) sender supplies: the
|
|
995
|
+
* second-alternate triplet and the coding-system / value-set OIDs: are NOT
|
|
815
996
|
* dropped: they are preserved verbatim on `extraComponents` so the typed view
|
|
816
997
|
* is lossless across versions.
|
|
817
998
|
*
|
|
818
|
-
* Zero runtime deps
|
|
999
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
819
1000
|
*/
|
|
820
1001
|
|
|
821
1002
|
/**
|
|
822
|
-
* HL7 v2 Coded with Exceptions (CWE)
|
|
1003
|
+
* HL7 v2 Coded with Exceptions (CWE): coded element per HL7 Chapter 2. All
|
|
823
1004
|
* 9 components are optional. Fields are OMITTED when the underlying
|
|
824
1005
|
* component is absent (exactOptionalPropertyTypes).
|
|
825
1006
|
*
|
|
@@ -835,7 +1016,7 @@ declare function parseCe(rep: RawRepetition, enc: EncodingCharacters): CE;
|
|
|
835
1016
|
* 9. originalText
|
|
836
1017
|
*
|
|
837
1018
|
* Components 10+ (present only on v2.7+ senders) are surfaced verbatim, in
|
|
838
|
-
* order, on `extraComponents
|
|
1019
|
+
* order, on `extraComponents`: never silently truncated.
|
|
839
1020
|
*
|
|
840
1021
|
* @example
|
|
841
1022
|
* ```ts
|
|
@@ -865,7 +1046,7 @@ interface CWE {
|
|
|
865
1046
|
}
|
|
866
1047
|
/**
|
|
867
1048
|
* Parse an HL7 v2 CWE repetition into a structured `CWE` object. Components
|
|
868
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1049
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
869
1050
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
870
1051
|
* from the result (exactOptionalPropertyTypes semantics).
|
|
871
1052
|
*
|
|
@@ -884,23 +1065,23 @@ interface CWE {
|
|
|
884
1065
|
declare function parseCwe(rep: RawRepetition, enc: EncodingCharacters): CWE;
|
|
885
1066
|
|
|
886
1067
|
/**
|
|
887
|
-
* HD
|
|
1068
|
+
* HD: HL7 v2 Hierarchic Designator composite. 3-component structured
|
|
888
1069
|
* identifier (namespace + universal id + universal id type) parsed from a
|
|
889
1070
|
* `RawRepetition` on demand. Fields are OMITTED when absent
|
|
890
|
-
* (exactOptionalPropertyTypes)
|
|
1071
|
+
* (exactOptionalPropertyTypes): NEVER set to `undefined`.
|
|
891
1072
|
*
|
|
892
|
-
* Zero runtime deps
|
|
1073
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
893
1074
|
*/
|
|
894
1075
|
|
|
895
1076
|
/**
|
|
896
|
-
* HL7 v2 Hierarchic Designator (HD)
|
|
1077
|
+
* HL7 v2 Hierarchic Designator (HD): per HL7 Chapter 2 data type. All 3
|
|
897
1078
|
* components are optional. Fields are OMITTED when the underlying component
|
|
898
1079
|
* is absent (exactOptionalPropertyTypes).
|
|
899
1080
|
*
|
|
900
1081
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
901
|
-
* 1. namespaceId
|
|
902
|
-
* 2. universalId
|
|
903
|
-
* 3. universalIdType
|
|
1082
|
+
* 1. namespaceId: application- or facility-scoped identifier (e.g. "EPIC").
|
|
1083
|
+
* 2. universalId: globally-unique id (e.g. an OID or UUID string).
|
|
1084
|
+
* 3. universalIdType: classifier for `universalId` (ISO, GUID, UUID, DNS,
|
|
904
1085
|
* URI, HL7, HCD, Random, etc.).
|
|
905
1086
|
*
|
|
906
1087
|
* @example
|
|
@@ -916,7 +1097,7 @@ interface HD {
|
|
|
916
1097
|
}
|
|
917
1098
|
/**
|
|
918
1099
|
* Parse an HL7 v2 HD repetition into a structured `HD` object. Components
|
|
919
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1100
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
920
1101
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
921
1102
|
* from the result (exactOptionalPropertyTypes semantics).
|
|
922
1103
|
*
|
|
@@ -936,10 +1117,10 @@ interface HD {
|
|
|
936
1117
|
declare function parseHd(rep: RawRepetition, enc: EncodingCharacters): HD;
|
|
937
1118
|
|
|
938
1119
|
/**
|
|
939
|
-
* CX
|
|
1120
|
+
* CX: HL7 v2 Extended Composite ID (with check digit) composite.
|
|
940
1121
|
* 10-component structured-identifier shape parsed from a `RawRepetition` on
|
|
941
1122
|
* demand by `Field.asCx()` (wired in Plan 04). Fields are OMITTED when
|
|
942
|
-
* absent (exactOptionalPropertyTypes)
|
|
1123
|
+
* absent (exactOptionalPropertyTypes): NEVER set to `undefined`.
|
|
943
1124
|
*
|
|
944
1125
|
* Component 4 (`assigningAuthority`) is the ONE nested-composite field in
|
|
945
1126
|
* this plan: its subcomponents form a 3-field HD (namespaceId, universalId,
|
|
@@ -948,14 +1129,14 @@ declare function parseHd(rep: RawRepetition, enc: EncodingCharacters): HD;
|
|
|
948
1129
|
* CX component 4.
|
|
949
1130
|
*
|
|
950
1131
|
* Component 6 (`assigningFacility`) is simplified to a flat `string` in v1
|
|
951
|
-
|
|
1132
|
+
*: the HL7 spec treats it as HD-shaped, but v1 favours simplicity. Callers
|
|
952
1133
|
* who need the full HD can parse the raw string separately.
|
|
953
1134
|
*
|
|
954
|
-
* Zero runtime deps
|
|
1135
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
955
1136
|
*/
|
|
956
1137
|
|
|
957
1138
|
/**
|
|
958
|
-
* HL7 v2 Extended Composite ID (CX)
|
|
1139
|
+
* HL7 v2 Extended Composite ID (CX): structured identifier per HL7 Chapter
|
|
959
1140
|
* 2. All 10 components are optional. Fields are OMITTED when the underlying
|
|
960
1141
|
* component is absent (exactOptionalPropertyTypes). `assigningAuthority`
|
|
961
1142
|
* uses the nested `HD` shape; `assigningFacility` is flattened to a plain
|
|
@@ -965,7 +1146,7 @@ declare function parseHd(rep: RawRepetition, enc: EncodingCharacters): HD;
|
|
|
965
1146
|
* 1. idNumber
|
|
966
1147
|
* 2. checkDigit
|
|
967
1148
|
* 3. checkDigitScheme (ISO 7064, M10, M11, NPI)
|
|
968
|
-
* 4. assigningAuthority (nested HD
|
|
1149
|
+
* 4. assigningAuthority (nested HD: 3 subcomponents form a HD composite)
|
|
969
1150
|
* 5. identifierTypeCode (MR, SSN, DL, MC, ...)
|
|
970
1151
|
* 6. assigningFacility (v1: flattened to string; spec is HD-shaped)
|
|
971
1152
|
* 7. effectiveDate (raw HL7 TS string)
|
|
@@ -997,7 +1178,7 @@ interface CX {
|
|
|
997
1178
|
}
|
|
998
1179
|
/**
|
|
999
1180
|
* Parse an HL7 v2 CX repetition into a structured `CX` object. Components
|
|
1000
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1181
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1001
1182
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1002
1183
|
* from the result (exactOptionalPropertyTypes semantics). Component 4
|
|
1003
1184
|
* (`assigningAuthority`) is parsed as a nested `HD`; see component table in
|
|
@@ -1021,7 +1202,7 @@ interface CX {
|
|
|
1021
1202
|
declare function parseCx(rep: RawRepetition, enc: EncodingCharacters): CX;
|
|
1022
1203
|
|
|
1023
1204
|
/**
|
|
1024
|
-
* NM
|
|
1205
|
+
* NM: HL7 v2 Numeric composite. Parses the raw HL7 numeric string into a
|
|
1025
1206
|
* `number` via the stricter `Number(raw)` (not `parseFloat`, which tolerates
|
|
1026
1207
|
* trailing garbage like "12abc" → 12). `NaN` is normalized to `undefined`
|
|
1027
1208
|
* per the same no-throw discipline as TS/DTM (D-24 analogous).
|
|
@@ -1029,17 +1210,17 @@ declare function parseCx(rep: RawRepetition, enc: EncodingCharacters): CX;
|
|
|
1029
1210
|
* The raw string is preserved verbatim on the composite so callers needing
|
|
1030
1211
|
* to round-trip or render with the original precision/formatting can.
|
|
1031
1212
|
*
|
|
1032
|
-
* Zero runtime deps
|
|
1213
|
+
* Zero runtime deps: pure function over the raw tree + `unescape` + stdlib
|
|
1033
1214
|
* `Number`.
|
|
1034
1215
|
*/
|
|
1035
1216
|
|
|
1036
1217
|
/**
|
|
1037
1218
|
* HL7 v2 Numeric (NM) composite. Carries both the raw HL7 numeric string
|
|
1038
1219
|
* and the parsed JS `number`. `.value` is `undefined` when the raw string
|
|
1039
|
-
* is empty or not fully numeric
|
|
1220
|
+
* is empty or not fully numeric: NEVER throws.
|
|
1040
1221
|
*
|
|
1041
1222
|
* Unlike most composites in this phase, both `raw` and `value` are
|
|
1042
|
-
* ALWAYS-PRESENT keys (not optional)
|
|
1223
|
+
* ALWAYS-PRESENT keys (not optional): `value` is explicitly typed as
|
|
1043
1224
|
* `number | undefined` so callers can destructure uniformly.
|
|
1044
1225
|
*
|
|
1045
1226
|
* @example
|
|
@@ -1055,10 +1236,10 @@ interface NM {
|
|
|
1055
1236
|
}
|
|
1056
1237
|
/**
|
|
1057
1238
|
* Parse an HL7 v2 NM repetition into `{ raw, value }`. Uses `Number(raw)`
|
|
1058
|
-
* for strict numeric parsing
|
|
1239
|
+
* for strict numeric parsing: trailing non-numeric characters produce
|
|
1059
1240
|
* `NaN`, normalized to `undefined`. Empty raw also produces `undefined`.
|
|
1060
1241
|
*
|
|
1061
|
-
* `Number("")` is `0` in JS
|
|
1242
|
+
* `Number("")` is `0` in JS: which is the wrong answer for an empty HL7
|
|
1062
1243
|
* numeric field. The explicit empty-string check below returns
|
|
1063
1244
|
* `{ raw: "", value: undefined }` so empty inputs match missing inputs.
|
|
1064
1245
|
*
|
|
@@ -1073,42 +1254,42 @@ interface NM {
|
|
|
1073
1254
|
declare function parseNm(rep: RawRepetition, _enc: EncodingCharacters): NM;
|
|
1074
1255
|
|
|
1075
1256
|
/**
|
|
1076
|
-
* PL
|
|
1077
|
-
* shape (v1 trimmed from the HL7 v2.5 full 12-component PL
|
|
1257
|
+
* PL: HL7 v2 Person Location composite. 11-component structured-location
|
|
1258
|
+
* shape (v1 trimmed from the HL7 v2.5 full 12-component PL: slot 12
|
|
1078
1259
|
* `entityIdentifier` is rarely used; v2 may restore the full shape) parsed
|
|
1079
1260
|
* from a `RawRepetition` on demand by `Field.asPl()` (wired in Plan 04).
|
|
1080
|
-
* Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
1261
|
+
* Fields are OMITTED when absent (exactOptionalPropertyTypes): NEVER set
|
|
1081
1262
|
* to `undefined`.
|
|
1082
1263
|
*
|
|
1083
1264
|
* Component 4 (`facility`) is the ONE nested-composite field in this
|
|
1084
1265
|
* parser: its subcomponents form a 3-field HD (namespaceId, universalId,
|
|
1085
|
-
* universalIdType)
|
|
1266
|
+
* universalIdType): same synthesis pattern used in `parseCx` for
|
|
1086
1267
|
* `assigningAuthority`. To reuse `parseHd` without duplicating logic, the
|
|
1087
1268
|
* parser synthesises a `RawRepetition` whose components wrap the
|
|
1088
1269
|
* subcomponents of PL component 4.
|
|
1089
1270
|
*
|
|
1090
|
-
* Zero runtime deps
|
|
1271
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1091
1272
|
*/
|
|
1092
1273
|
|
|
1093
1274
|
/**
|
|
1094
|
-
* HL7 v2 Person Location (PL)
|
|
1275
|
+
* HL7 v2 Person Location (PL): structured location per HL7 Chapter 2. All
|
|
1095
1276
|
* 11 v1 components are optional. Fields are OMITTED when the underlying
|
|
1096
1277
|
* component is absent (exactOptionalPropertyTypes). `facility` uses the
|
|
1097
1278
|
* nested `HD` shape; `assigningAuthorityForLocation` is flattened to a
|
|
1098
1279
|
* plain string in v1 (HL7 spec treats it as HD-shaped).
|
|
1099
1280
|
*
|
|
1100
1281
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
1101
|
-
* 1. pointOfCare
|
|
1282
|
+
* 1. pointOfCare: e.g. "ICU", "ED"
|
|
1102
1283
|
* 2. room
|
|
1103
1284
|
* 3. bed
|
|
1104
|
-
* 4. facility
|
|
1105
|
-
* 5. locationStatus
|
|
1285
|
+
* 4. facility: nested HD (3 subcomponents form an HD composite)
|
|
1286
|
+
* 5. locationStatus: O=Occupied, U=Unoccupied, K=Contaminated, C=Closed,
|
|
1106
1287
|
* H=Housekeeping, I=Isolated
|
|
1107
|
-
* 6. personLocationType
|
|
1288
|
+
* 6. personLocationType: C=Clinic, D=Department, H=Home, N=Nursing Unit,
|
|
1108
1289
|
* O=Office, R=Revenue Location
|
|
1109
1290
|
* 7. building
|
|
1110
1291
|
* 8. floor
|
|
1111
|
-
* 9. locationDescription
|
|
1292
|
+
* 9. locationDescription: free-text
|
|
1112
1293
|
* 10. comprehensiveLocationId
|
|
1113
1294
|
* 11. assigningAuthorityForLocation (v1: flattened to string)
|
|
1114
1295
|
*
|
|
@@ -1138,7 +1319,7 @@ interface PL {
|
|
|
1138
1319
|
}
|
|
1139
1320
|
/**
|
|
1140
1321
|
* Parse an HL7 v2 PL repetition into a structured `PL` object. Components
|
|
1141
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1322
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1142
1323
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1143
1324
|
* from the result (exactOptionalPropertyTypes semantics). Component 4
|
|
1144
1325
|
* (`facility`) is parsed as a nested `HD`; see component table in the `PL`
|
|
@@ -1161,15 +1342,15 @@ interface PL {
|
|
|
1161
1342
|
declare function parsePl(rep: RawRepetition, enc: EncodingCharacters): PL;
|
|
1162
1343
|
|
|
1163
1344
|
/**
|
|
1164
|
-
* SN
|
|
1345
|
+
* SN: HL7 v2 Structured Numeric composite (HL7 Chapter 2A). Parses an OBX-5
|
|
1165
1346
|
* value declared as `OBX-2 = SN` into its four positional components so a
|
|
1166
1347
|
* comparator or a range/ratio is preserved as TYPED data instead of being
|
|
1167
1348
|
* flattened to a string (where everything past the first component is lost).
|
|
1168
1349
|
*
|
|
1169
1350
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
1170
|
-
* 1. comparator (ST)
|
|
1351
|
+
* 1. comparator (ST): `>` `<` `>=` `<=` `=` `<>`; absent ⇒ `=`
|
|
1171
1352
|
* 2. num1 (NM)
|
|
1172
|
-
* 3. separatorOrSuffix (ST)
|
|
1353
|
+
* 3. separatorOrSuffix (ST): `-` (range), `:` / `/` (ratio), `+` (suffix), `.`
|
|
1173
1354
|
* 4. num2 (NM)
|
|
1174
1355
|
*
|
|
1175
1356
|
* Usage examples (the literal OBX-5 string ⇒ what this parses):
|
|
@@ -1179,12 +1360,12 @@ declare function parsePl(rep: RawRepetition, enc: EncodingCharacters): PL;
|
|
|
1179
1360
|
* `^1^:^128` ⇒ titer/ratio 1:128 → { num1: 1, separatorOrSuffix: ":", num2: 128 }
|
|
1180
1361
|
*
|
|
1181
1362
|
* Safety: `num1`/`num2` are parsed with the same strict `Number()` discipline
|
|
1182
|
-
* as NM
|
|
1363
|
+
* as NM: non-numeric input becomes `undefined`, NEVER `NaN` and never a
|
|
1183
1364
|
* silently wrong number. The comparator is surfaced ONLY when SN.1 is a
|
|
1184
1365
|
* recognized operator, so a non-operator value in the comparator slot is never
|
|
1185
1366
|
* passed off as a real relation.
|
|
1186
1367
|
*
|
|
1187
|
-
* Zero runtime deps
|
|
1368
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1188
1369
|
*/
|
|
1189
1370
|
|
|
1190
1371
|
/**
|
|
@@ -1194,7 +1375,7 @@ declare function parsePl(rep: RawRepetition, enc: EncodingCharacters): PL;
|
|
|
1194
1375
|
* when absent (`exactOptionalPropertyTypes`).
|
|
1195
1376
|
*
|
|
1196
1377
|
* An absent `comparator` means the default `=` relation per HL7 Chapter 2A.
|
|
1197
|
-
* This library surfaces the structure only
|
|
1378
|
+
* This library surfaces the structure only: it does not evaluate the
|
|
1198
1379
|
* inequality, validate the unit, or convert values.
|
|
1199
1380
|
*
|
|
1200
1381
|
* @example
|
|
@@ -1205,11 +1386,11 @@ declare function parsePl(rep: RawRepetition, enc: EncodingCharacters): PL;
|
|
|
1205
1386
|
* ```
|
|
1206
1387
|
*/
|
|
1207
1388
|
interface SN {
|
|
1208
|
-
/** SN.1 comparator
|
|
1389
|
+
/** SN.1 comparator: one of `>` `<` `>=` `<=` `=` `<>`. Omitted ⇒ default `=`. */
|
|
1209
1390
|
readonly comparator?: string;
|
|
1210
1391
|
/** SN.2 first numeric value. `undefined` when absent or non-numeric (never `NaN`). */
|
|
1211
1392
|
readonly num1: number | undefined;
|
|
1212
|
-
/** SN.3 separator/suffix
|
|
1393
|
+
/** SN.3 separator/suffix: `-` (range), `:`/`/` (ratio), `+` (suffix), `.`. Omitted when absent. */
|
|
1213
1394
|
readonly separatorOrSuffix?: string;
|
|
1214
1395
|
/** SN.4 second numeric value. `undefined` when absent or non-numeric (never `NaN`). */
|
|
1215
1396
|
readonly num2: number | undefined;
|
|
@@ -1222,7 +1403,7 @@ interface SN {
|
|
|
1222
1403
|
*
|
|
1223
1404
|
* Fail-safe: a non-operator value in the comparator slot (SN.1) is dropped
|
|
1224
1405
|
* rather than surfaced as a relation, and a non-numeric SN.2/SN.4 becomes
|
|
1225
|
-
* `undefined
|
|
1406
|
+
* `undefined`: the parser never emits a confident wrong comparator or number.
|
|
1226
1407
|
*
|
|
1227
1408
|
* @example
|
|
1228
1409
|
* ```ts
|
|
@@ -1244,14 +1425,14 @@ declare function parseSn(rep: RawRepetition, enc: EncodingCharacters): SN | unde
|
|
|
1244
1425
|
* `Date`-coercing helper into a **fidelity-first** parser. The HL7 v2 Ch. 2A
|
|
1245
1426
|
* DTM datatype is `YYYY[MM[DD[HH[MM[SS[.S[S[S[S]]]]]]]]][+/-ZZZZ]`, where the
|
|
1246
1427
|
* number of populated characters (excluding the offset) sets the precision and
|
|
1247
|
-
* a missing offset "defaults to that of the local time zone of the sender"
|
|
1428
|
+
* a missing offset "defaults to that of the local time zone of the sender",
|
|
1248
1429
|
* NOT UTC and NOT the parser's zone. The old behavior (zero-fill truncations,
|
|
1249
1430
|
* assume-UTC on a missing offset, eager `Date`) is an architectural defect
|
|
1250
1431
|
* [S-DTM-IMPL]: it silently shifts a day-only birth date (`|19880705|`) by a
|
|
1251
1432
|
* day in any negative-offset zone.
|
|
1252
1433
|
*
|
|
1253
1434
|
* This module therefore:
|
|
1254
|
-
* - `parseDtm(raw)` decodes the DTM into typed **parts** (`DtmParts`)
|
|
1435
|
+
* - `parseDtm(raw)` decodes the DTM into typed **parts** (`DtmParts`),
|
|
1255
1436
|
* precision preserved, no zero-fill, no `Date`, no UTC assumption.
|
|
1256
1437
|
* - `formatDtm(parts)` reconstructs the DTM string from parts (round-trip).
|
|
1257
1438
|
* - `dtmToDate(parts, opts)` materializes an absolute `Date` **only on
|
|
@@ -1261,12 +1442,12 @@ declare function parseSn(rep: RawRepetition, enc: EncodingCharacters): SN | unde
|
|
|
1261
1442
|
* formats → built-in fallbacks) used by non-composite callers such as
|
|
1262
1443
|
* `msg.meta.timestamp`; it emits `TIMESTAMP_FALLBACK_FORMAT` on a fallback.
|
|
1263
1444
|
*
|
|
1264
|
-
* Zero runtime deps
|
|
1445
|
+
* Zero runtime deps: only the JS stdlib `Date` / `Date.UTC` APIs and
|
|
1265
1446
|
* hand-rolled regex / token matchers. No date-fns, no luxon, no moment.
|
|
1266
1447
|
*/
|
|
1267
1448
|
|
|
1268
1449
|
/**
|
|
1269
|
-
* Stated precision of a parsed HL7 DTM value
|
|
1450
|
+
* Stated precision of a parsed HL7 DTM value: the number of populated
|
|
1270
1451
|
* characters (excluding the timezone offset) determines which level applies.
|
|
1271
1452
|
* A value's precision is preserved verbatim: `|1970|` is `"year"`, never
|
|
1272
1453
|
* silently promoted to a full timestamp.
|
|
@@ -1280,7 +1461,7 @@ declare function parseSn(rep: RawRepetition, enc: EncodingCharacters): SN | unde
|
|
|
1280
1461
|
*/
|
|
1281
1462
|
type DtmPrecision = "year" | "month" | "day" | "hour" | "minute" | "second" | "fraction";
|
|
1282
1463
|
/**
|
|
1283
|
-
* Parsed HL7 v2 TS/DTM value
|
|
1464
|
+
* Parsed HL7 v2 TS/DTM value: the raw string plus its structural parts, with
|
|
1284
1465
|
* the stated **precision** and **timezone fidelity** preserved. This is the
|
|
1285
1466
|
* shape of the `TS` composite (`field.asTs()`) and every helper datetime.
|
|
1286
1467
|
*
|
|
@@ -1288,7 +1469,7 @@ type DtmPrecision = "year" | "month" | "day" | "hour" | "minute" | "second" | "f
|
|
|
1288
1469
|
* **spec-native** (`month` is 1–12, NOT the JS `Date` 0–11). `offsetMinutes`
|
|
1289
1470
|
* is present iff `hasTimezone` is `true` and is signed minutes east of UTC
|
|
1290
1471
|
* (`+0500` → `300`, `-0430` → `-270`). A missing offset is **flagged**
|
|
1291
|
-
* (`hasTimezone: false`), never resolved to UTC
|
|
1472
|
+
* (`hasTimezone: false`), never resolved to UTC: the consumer decides how to
|
|
1292
1473
|
* localize with {@link dtmToDate}.
|
|
1293
1474
|
*
|
|
1294
1475
|
* @example
|
|
@@ -1306,7 +1487,7 @@ interface DtmParts {
|
|
|
1306
1487
|
/**
|
|
1307
1488
|
* `true` when `raw` is a well-formed, in-range HL7 DTM (or a matched
|
|
1308
1489
|
* fallback format). `false` for empty, malformed, or calendar-out-of-range
|
|
1309
|
-
* input
|
|
1490
|
+
* input: in which case only `raw` and `hasTimezone: false` are meaningful.
|
|
1310
1491
|
*/
|
|
1311
1492
|
readonly valid: boolean;
|
|
1312
1493
|
/** Stated precision; absent when `valid` is `false`. */
|
|
@@ -1325,7 +1506,7 @@ interface DtmParts {
|
|
|
1325
1506
|
readonly second?: number;
|
|
1326
1507
|
/**
|
|
1327
1508
|
* Fractional-second digits exactly as populated (no leading dot), e.g.
|
|
1328
|
-
* `"5"` (0.5 s), `"0500"` (0.05 s). Preserved verbatim
|
|
1509
|
+
* `"5"` (0.5 s), `"0500"` (0.05 s). Preserved verbatim: never rounded.
|
|
1329
1510
|
*/
|
|
1330
1511
|
readonly fractionalSeconds?: string;
|
|
1331
1512
|
/** `true` iff an explicit `+/-ZZZZ` offset was present. */
|
|
@@ -1372,7 +1553,7 @@ declare const SUPPORTED_DATE_TOKENS: readonly string[];
|
|
|
1372
1553
|
* never coerces to a `Date`, and never assumes UTC for a missing offset.
|
|
1373
1554
|
*
|
|
1374
1555
|
* Returns `{ raw, valid: false, hasTimezone: false }` (no parts) for empty,
|
|
1375
|
-
* malformed, or calendar-out-of-range input
|
|
1556
|
+
* malformed, or calendar-out-of-range input: never throws. A fractional
|
|
1376
1557
|
* component is only accepted at full second precision.
|
|
1377
1558
|
*
|
|
1378
1559
|
* @example
|
|
@@ -1392,12 +1573,12 @@ declare const SUPPORTED_DATE_TOKENS: readonly string[];
|
|
|
1392
1573
|
declare function parseDtm(raw: string): DtmParts;
|
|
1393
1574
|
/**
|
|
1394
1575
|
* Reconstruct the HL7 DTM string from {@link DtmParts}. The inverse of
|
|
1395
|
-
* {@link parseDtm} for a strict HL7 parse
|
|
1576
|
+
* {@link parseDtm} for a strict HL7 parse: `formatDtm(parseDtm(s)) === s` for
|
|
1396
1577
|
* any well-formed `s`, including the byte-preserving `-0000` (which HL7, unlike
|
|
1397
1578
|
* RFC 3339, treats as UTC but whose sign we retain for exact round-trip).
|
|
1398
1579
|
* Returns the raw string unchanged when `parts.valid` is `false`.
|
|
1399
1580
|
*
|
|
1400
|
-
* Emits exactly the populated precision
|
|
1581
|
+
* Emits exactly the populated precision, no zero-fill, so a year-only value
|
|
1401
1582
|
* re-serializes to four characters, never `YYYY0101`.
|
|
1402
1583
|
*
|
|
1403
1584
|
* @example
|
|
@@ -1423,14 +1604,14 @@ declare function formatDtm(parts: DtmParts): string;
|
|
|
1423
1604
|
interface DtmToDateOptions {
|
|
1424
1605
|
/**
|
|
1425
1606
|
* Offset (signed minutes east of UTC) to assume when the value carries **no**
|
|
1426
|
-
* timezone. Without it, an offset-less value resolves to `undefined
|
|
1607
|
+
* timezone. Without it, an offset-less value resolves to `undefined`,
|
|
1427
1608
|
* {@link dtmToDate} never guesses a zone. Ignored when the value already has
|
|
1428
1609
|
* an offset. Pass `0` to explicitly treat a naive value as UTC.
|
|
1429
1610
|
*/
|
|
1430
1611
|
readonly assumeOffsetMinutes?: number;
|
|
1431
1612
|
}
|
|
1432
1613
|
/**
|
|
1433
|
-
* Materialize an absolute-instant JS `Date` from {@link DtmParts}
|
|
1614
|
+
* Materialize an absolute-instant JS `Date` from {@link DtmParts}: **only on
|
|
1434
1615
|
* explicit caller request**. Truncated fields fill to their lowest legal value
|
|
1435
1616
|
* (month → January, day → 1, time → 0) *for instant construction only*; the
|
|
1436
1617
|
* value's stated `precision` still tells the truth.
|
|
@@ -1448,35 +1629,35 @@ interface DtmToDateOptions {
|
|
|
1448
1629
|
* import { parseDtm, dtmToDate } from "@cosyte/hl7";
|
|
1449
1630
|
*
|
|
1450
1631
|
* dtmToDate(parseDtm("20250102153045-0500"))?.toISOString();
|
|
1451
|
-
* // "2025-01-02T20:30:45.000Z"
|
|
1632
|
+
* // "2025-01-02T20:30:45.000Z": exact, offset-derived
|
|
1452
1633
|
*
|
|
1453
|
-
* dtmToDate(parseDtm("20250102")); // undefined
|
|
1634
|
+
* dtmToDate(parseDtm("20250102")); // undefined: refuses to guess the zone
|
|
1454
1635
|
* dtmToDate(parseDtm("20250102"), { assumeOffsetMinutes: 0 })?.toISOString();
|
|
1455
|
-
* // "2025-01-02T00:00:00.000Z"
|
|
1636
|
+
* // "2025-01-02T00:00:00.000Z": caller explicitly assumed UTC
|
|
1456
1637
|
* ```
|
|
1457
1638
|
*/
|
|
1458
1639
|
declare function dtmToDate(parts: DtmParts, options?: DtmToDateOptions): Date | undefined;
|
|
1459
1640
|
|
|
1460
1641
|
/**
|
|
1461
|
-
* TS/DTM
|
|
1642
|
+
* TS/DTM: HL7 v2 Time Stamp composite. Delegates parsing to the Phase 2
|
|
1462
1643
|
* helper `src/parser/dates.ts::parseDtm` (D-10 "zero duplicate date logic").
|
|
1463
1644
|
*
|
|
1464
1645
|
* Phase N (datetime precision + timezone fidelity) reshaped this composite from
|
|
1465
1646
|
* the old `{ raw, date }` (which zero-filled truncations and assumed UTC for a
|
|
1466
|
-
* missing offset
|
|
1647
|
+
* missing offset: an architectural defect) to the fidelity-preserving
|
|
1467
1648
|
* {@link DtmParts}: raw string + typed parts + stated precision + timezone
|
|
1468
1649
|
* presence, with **no eager `Date`**. Callers that need an absolute instant
|
|
1469
|
-
* opt in explicitly via `dtmToDate(ts, opts)
|
|
1650
|
+
* opt in explicitly via `dtmToDate(ts, opts)`: which refuses to guess a zone
|
|
1470
1651
|
* for an offset-less value rather than silently defaulting to UTC.
|
|
1471
1652
|
*
|
|
1472
|
-
* Zero runtime deps
|
|
1653
|
+
* Zero runtime deps: delegates to `parseDtm` + `unescape`.
|
|
1473
1654
|
*/
|
|
1474
1655
|
|
|
1475
1656
|
/**
|
|
1476
|
-
* HL7 v2 Time Stamp (TS) / Date Time (DTM) composite
|
|
1657
|
+
* HL7 v2 Time Stamp (TS) / Date Time (DTM) composite: the raw HL7 string plus
|
|
1477
1658
|
* its parsed parts, preserving the **stated precision** and **timezone
|
|
1478
1659
|
* fidelity**. This is a {@link DtmParts}: `valid` is `false` (with no parts)
|
|
1479
|
-
* for unparseable input
|
|
1660
|
+
* for unparseable input: NEVER throws (TYPES-04 no-throw guarantee).
|
|
1480
1661
|
*
|
|
1481
1662
|
* There is deliberately **no `date` field**: a day-only value coerced to a JS
|
|
1482
1663
|
* `Date` at UTC midnight silently shifts the calendar day in negative-offset
|
|
@@ -1489,14 +1670,14 @@ declare function dtmToDate(parts: DtmParts, options?: DtmToDateOptions): Date |
|
|
|
1489
1670
|
* import { dtmToDate } from "@cosyte/hl7";
|
|
1490
1671
|
* const dob: TS = { raw: "19880705", valid: true, precision: "day",
|
|
1491
1672
|
* year: 1988, month: 7, day: 5, hasTimezone: false };
|
|
1492
|
-
* console.log(dob.precision); // "day"
|
|
1493
|
-
* console.log(dtmToDate(dob)); // undefined
|
|
1673
|
+
* console.log(dob.precision); // "day": not a full timestamp
|
|
1674
|
+
* console.log(dtmToDate(dob)); // undefined: refuses to guess the zone
|
|
1494
1675
|
* ```
|
|
1495
1676
|
*/
|
|
1496
1677
|
type TS = DtmParts;
|
|
1497
1678
|
/**
|
|
1498
1679
|
* Parse an HL7 v2 TS/DTM repetition into fidelity {@link TS} parts. Delegates
|
|
1499
|
-
* to `parseDtm
|
|
1680
|
+
* to `parseDtm`: the same structural parser that backs every timestamp in the
|
|
1500
1681
|
* library. No user `dateFormats` at this layer; non-composite callers (e.g.
|
|
1501
1682
|
* `msg.meta.timestamp`) that know `ParseOptions.dateFormats` use
|
|
1502
1683
|
* `parseDtmCascade` directly.
|
|
@@ -1517,22 +1698,22 @@ type TS = DtmParts;
|
|
|
1517
1698
|
declare function parseTs(rep: RawRepetition, _enc: EncodingCharacters): TS;
|
|
1518
1699
|
|
|
1519
1700
|
/**
|
|
1520
|
-
* XAD
|
|
1701
|
+
* XAD: HL7 v2 Extended Address composite. 12-component structured address
|
|
1521
1702
|
* shape parsed from a `RawRepetition` on demand by `Field.asXad()` (wired in
|
|
1522
|
-
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
1703
|
+
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes),
|
|
1523
1704
|
* NEVER set to `undefined`.
|
|
1524
1705
|
*
|
|
1525
|
-
* Zero runtime deps
|
|
1706
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1526
1707
|
*/
|
|
1527
1708
|
|
|
1528
1709
|
/**
|
|
1529
|
-
* HL7 v2 Extended Address (XAD)
|
|
1710
|
+
* HL7 v2 Extended Address (XAD): structured postal address per HL7 Chapter
|
|
1530
1711
|
* 2. All 12 components are optional. Fields are OMITTED when the underlying
|
|
1531
1712
|
* component is absent (exactOptionalPropertyTypes).
|
|
1532
1713
|
*
|
|
1533
1714
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
1534
|
-
* 1. street
|
|
1535
|
-
* 2. otherDesignation
|
|
1715
|
+
* 1. street: street address (house number + street name).
|
|
1716
|
+
* 2. otherDesignation: apartment number, suite, floor, etc.
|
|
1536
1717
|
* 3. city
|
|
1537
1718
|
* 4. stateOrProvince
|
|
1538
1719
|
* 5. zipOrPostalCode
|
|
@@ -1566,7 +1747,7 @@ interface XAD {
|
|
|
1566
1747
|
}
|
|
1567
1748
|
/**
|
|
1568
1749
|
* Parse an HL7 v2 XAD repetition into a structured `XAD` object. Components
|
|
1569
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1750
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1570
1751
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1571
1752
|
* from the result (exactOptionalPropertyTypes semantics).
|
|
1572
1753
|
*
|
|
@@ -1587,7 +1768,7 @@ interface XAD {
|
|
|
1587
1768
|
declare function parseXad(rep: RawRepetition, enc: EncodingCharacters): XAD;
|
|
1588
1769
|
|
|
1589
1770
|
/**
|
|
1590
|
-
* XCN
|
|
1771
|
+
* XCN: HL7 v2 Extended Composite ID Number and Name for Persons. Common in
|
|
1591
1772
|
* OBR-16 (ordering provider), PV1-7 (attending doctor), PV1-8 (referring
|
|
1592
1773
|
* doctor), and other fields that identify a human clinician.
|
|
1593
1774
|
*
|
|
@@ -1596,29 +1777,29 @@ declare function parseXad(rep: RawRepetition, enc: EncodingCharacters): XAD;
|
|
|
1596
1777
|
* most-used components (HL7 v2.5 defines 23); additional components may be
|
|
1597
1778
|
* restored in v2 if vendor-quirk fixtures require them.
|
|
1598
1779
|
*
|
|
1599
|
-
* Zero runtime deps
|
|
1780
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1600
1781
|
* Parser is silent (D-09 composite-parser silence).
|
|
1601
1782
|
*/
|
|
1602
1783
|
|
|
1603
1784
|
/**
|
|
1604
|
-
* HL7 v2 Extended Composite ID Number and Name for Persons (XCN)
|
|
1785
|
+
* HL7 v2 Extended Composite ID Number and Name for Persons (XCN): per HL7
|
|
1605
1786
|
* Chapter 2.A.88. All 13 v1 components are optional. Fields are OMITTED when
|
|
1606
1787
|
* the underlying component is absent (exactOptionalPropertyTypes).
|
|
1607
1788
|
*
|
|
1608
1789
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
1609
|
-
* 1. idNumber
|
|
1790
|
+
* 1. idNumber: e.g. employee ID, NPI digits, DEA number (CX-1 analogue)
|
|
1610
1791
|
* 2. familyName (XPN-1)
|
|
1611
1792
|
* 3. givenName (XPN-2)
|
|
1612
|
-
* 4. secondName
|
|
1613
|
-
* 5. suffix
|
|
1614
|
-
* 6. prefix
|
|
1615
|
-
* 7. degree
|
|
1793
|
+
* 4. secondName: second and further given names (XPN-3)
|
|
1794
|
+
* 5. suffix: Jr., III, etc. (XPN-4)
|
|
1795
|
+
* 6. prefix: Dr., Mrs., etc. (XPN-5)
|
|
1796
|
+
* 7. degree: MD, PhD, etc. (XPN-6)
|
|
1616
1797
|
* 8. sourceTable
|
|
1617
|
-
* 9. assigningAuthority
|
|
1618
|
-
* 10. nameTypeCode
|
|
1798
|
+
* 9. assigningAuthority: nested HD (CX-4 analogue)
|
|
1799
|
+
* 10. nameTypeCode: L=Legal, M=Maiden, N=Nickname, ... (XPN-7)
|
|
1619
1800
|
* 11. identifierCheckDigit
|
|
1620
|
-
* 12. checkDigitScheme
|
|
1621
|
-
* 13. identifierTypeCode
|
|
1801
|
+
* 12. checkDigitScheme: ISO 7064, M10, M11, NPI
|
|
1802
|
+
* 13. identifierTypeCode: "NPI", "DN" (DEA number), ... (CX-5 analogue)
|
|
1622
1803
|
*
|
|
1623
1804
|
* @example
|
|
1624
1805
|
* ```ts
|
|
@@ -1648,7 +1829,7 @@ interface XCN {
|
|
|
1648
1829
|
}
|
|
1649
1830
|
/**
|
|
1650
1831
|
* Parse an HL7 v2 XCN repetition into a structured `XCN` object. Components
|
|
1651
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1832
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1652
1833
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1653
1834
|
* from the result (exactOptionalPropertyTypes semantics). Component 9
|
|
1654
1835
|
* (`assigningAuthority`) is parsed as a nested `HD`; see component table in
|
|
@@ -1682,16 +1863,16 @@ interface XCN {
|
|
|
1682
1863
|
declare function parseXcn(rep: RawRepetition, enc: EncodingCharacters): XCN;
|
|
1683
1864
|
|
|
1684
1865
|
/**
|
|
1685
|
-
* XPN
|
|
1866
|
+
* XPN: HL7 v2 Extended Person Name composite. 14-component structured-name
|
|
1686
1867
|
* shape parsed from a `RawRepetition` on demand by `Field.asXpn()` (wired in
|
|
1687
|
-
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
1868
|
+
* Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes),
|
|
1688
1869
|
* NEVER set to `undefined`.
|
|
1689
1870
|
*
|
|
1690
|
-
* Zero runtime deps
|
|
1871
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1691
1872
|
*/
|
|
1692
1873
|
|
|
1693
1874
|
/**
|
|
1694
|
-
* HL7 v2 Extended Person Name (XPN)
|
|
1875
|
+
* HL7 v2 Extended Person Name (XPN): structured name per HL7 Chapter 2. All
|
|
1695
1876
|
* 14 components are optional. Fields are OMITTED when the underlying
|
|
1696
1877
|
* component is absent (exactOptionalPropertyTypes).
|
|
1697
1878
|
*
|
|
@@ -1704,10 +1885,10 @@ declare function parseXcn(rep: RawRepetition, enc: EncodingCharacters): XCN;
|
|
|
1704
1885
|
* 6. degree (e.g. MD, PhD)
|
|
1705
1886
|
* 7. nameTypeCode (L=Legal, M=Maiden, N=Nickname, S=Coded Pseudo-Name, ...)
|
|
1706
1887
|
* 8. nameRepresentationCode
|
|
1707
|
-
* 9. nameContext (flattened to string in v1
|
|
1888
|
+
* 9. nameContext (flattened to string in v1: CWE nesting is out of scope)
|
|
1708
1889
|
* 10. nameValidityRange
|
|
1709
1890
|
* 11. nameAssemblyOrder (F=family first, G=given first)
|
|
1710
|
-
* 12. effectiveDate (raw HL7 TS string
|
|
1891
|
+
* 12. effectiveDate (raw HL7 TS string: caller may parse via parseDtm)
|
|
1711
1892
|
* 13. expirationDate
|
|
1712
1893
|
* 14. professionalSuffix
|
|
1713
1894
|
*
|
|
@@ -1735,7 +1916,7 @@ interface XPN {
|
|
|
1735
1916
|
}
|
|
1736
1917
|
/**
|
|
1737
1918
|
* Parse an HL7 v2 XPN repetition into a structured `XPN` object. Components
|
|
1738
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1919
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1739
1920
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1740
1921
|
* from the result (exactOptionalPropertyTypes semantics).
|
|
1741
1922
|
*
|
|
@@ -1754,34 +1935,34 @@ interface XPN {
|
|
|
1754
1935
|
declare function parseXpn(rep: RawRepetition, enc: EncodingCharacters): XPN;
|
|
1755
1936
|
|
|
1756
1937
|
/**
|
|
1757
|
-
* XTN
|
|
1938
|
+
* XTN: HL7 v2 Extended Telecommunication Number composite. 12-component
|
|
1758
1939
|
* structured telecom shape (v1 trimmed from the HL7 v2.5 full 14-component
|
|
1759
|
-
* XTN
|
|
1940
|
+
* XTN: slots 13/14 are rarely-used legacy fields; v2 may restore the full
|
|
1760
1941
|
* shape) parsed from a `RawRepetition` on demand by `Field.asXtn()` (wired
|
|
1761
1942
|
* in Plan 04). Fields are OMITTED when absent (exactOptionalPropertyTypes)
|
|
1762
|
-
|
|
1943
|
+
*: NEVER set to `undefined`.
|
|
1763
1944
|
*
|
|
1764
|
-
* Zero runtime deps
|
|
1945
|
+
* Zero runtime deps: pure function over the raw positional tree + `unescape`.
|
|
1765
1946
|
*/
|
|
1766
1947
|
|
|
1767
1948
|
/**
|
|
1768
|
-
* HL7 v2 Extended Telecommunication Number (XTN)
|
|
1949
|
+
* HL7 v2 Extended Telecommunication Number (XTN): structured telecom per
|
|
1769
1950
|
* HL7 Chapter 2. All 12 v1 components are optional. Fields are OMITTED when
|
|
1770
1951
|
* the underlying component is absent (exactOptionalPropertyTypes).
|
|
1771
1952
|
*
|
|
1772
1953
|
* Component positions (HL7 1-indexed; this interface is 0-indexed by key):
|
|
1773
|
-
* 1. telephoneNumber
|
|
1774
|
-
* 2. telecommunicationUseCode
|
|
1954
|
+
* 1. telephoneNumber: formatted or unformatted phone number
|
|
1955
|
+
* 2. telecommunicationUseCode: PRN=Primary Residence, WPN=Work, NET=Internet,
|
|
1775
1956
|
* ORN=Other Residence, BPN=Beeper, VHN=Vacation Home, ASN=Answering
|
|
1776
1957
|
* Service, EMR=Emergency, ...
|
|
1777
|
-
* 3. telecommunicationEquipmentType
|
|
1958
|
+
* 3. telecommunicationEquipmentType: PH=Phone, FX=Fax, MD=Modem,
|
|
1778
1959
|
* CP=Cellular Phone, BP=Beeper, Internet, X.400, TDD, TTY
|
|
1779
1960
|
* 4. emailAddress
|
|
1780
1961
|
* 5. countryCode (e.g. "+1")
|
|
1781
1962
|
* 6. areaCityCode
|
|
1782
1963
|
* 7. localNumber
|
|
1783
1964
|
* 8. extension
|
|
1784
|
-
* 9. anyText
|
|
1965
|
+
* 9. anyText: free-text note
|
|
1785
1966
|
* 10. extensionPrefix (e.g. "x")
|
|
1786
1967
|
* 11. speedDialCode
|
|
1787
1968
|
* 12. unformattedTelephoneNumber
|
|
@@ -1812,7 +1993,7 @@ interface XTN {
|
|
|
1812
1993
|
}
|
|
1813
1994
|
/**
|
|
1814
1995
|
* Parse an HL7 v2 XTN repetition into a structured `XTN` object. Components
|
|
1815
|
-
* are returned verbatim (already decoded once by the tokenizer
|
|
1996
|
+
* are returned verbatim (already decoded once by the tokenizer: never re-unescaped,
|
|
1816
1997
|
* HL7-VALUE-REDECODE). Absent / empty components are OMITTED
|
|
1817
1998
|
* from the result (exactOptionalPropertyTypes semantics). Components past
|
|
1818
1999
|
* position 12 are silently ignored in v1.
|
|
@@ -1832,7 +2013,7 @@ interface XTN {
|
|
|
1832
2013
|
declare function parseXtn(rep: RawRepetition, enc: EncodingCharacters): XTN;
|
|
1833
2014
|
|
|
1834
2015
|
/**
|
|
1835
|
-
* `Field
|
|
2016
|
+
* `Field`: wrapper over a `RawField` exposing HL7 null/empty discrimination
|
|
1836
2017
|
* (`isNull`), the raw repetitions tree, and a convenience `value` getter that
|
|
1837
2018
|
* reads the first subcomponent of the first component of the first
|
|
1838
2019
|
* repetition. Constructed internally by `Segment.field(n)`; user code never
|
|
@@ -1841,7 +2022,7 @@ declare function parseXtn(rep: RawRepetition, enc: EncodingCharacters): XTN;
|
|
|
1841
2022
|
* Typed composite coercions (`asXpn`, `asXad`, `asCx`, `asCwe`, `asCe`,
|
|
1842
2023
|
* `asXtn`, `asPl`, `asTs`, `asNm`, `asHd`) delegate to the corresponding
|
|
1843
2024
|
* composite parsers from `./types/*`. Coercions are lazy and NOT memoized
|
|
1844
|
-
* in v1 (D-09)
|
|
2025
|
+
* in v1 (D-09): each call re-parses the first repetition. Empty fields
|
|
1845
2026
|
* return empty typed objects (`{}` for optional-field composites,
|
|
1846
2027
|
* `{ raw: "", date: undefined }` / `{ raw: "", value: undefined }` for
|
|
1847
2028
|
* the TS/NM scalar composites).
|
|
@@ -1850,7 +2031,7 @@ declare function parseXtn(rep: RawRepetition, enc: EncodingCharacters): XTN;
|
|
|
1850
2031
|
/**
|
|
1851
2032
|
* Wrapper over a `RawField` exposing HL7 null/empty discrimination
|
|
1852
2033
|
* (`isNull`), the repetitions tree, and a decoded `value` getter.
|
|
1853
|
-
* `seg.field(3) === seg.field(3)
|
|
2034
|
+
* `seg.field(3) === seg.field(3)`: Field instances are referentially stable
|
|
1854
2035
|
* per segment position (D-12).
|
|
1855
2036
|
*
|
|
1856
2037
|
* @example
|
|
@@ -1858,19 +2039,19 @@ declare function parseXtn(rep: RawRepetition, enc: EncodingCharacters): XTN;
|
|
|
1858
2039
|
* import { parseHL7 } from "@cosyte/hl7";
|
|
1859
2040
|
* const msg = parseHL7(raw);
|
|
1860
2041
|
* const pid5 = msg.segments("PID")[0]?.field(5);
|
|
1861
|
-
* console.log(pid5?.value); // "Smith"
|
|
1862
|
-
* console.log(pid5?.isNull); // false
|
|
2042
|
+
* console.log(pid5?.value); // "Smith": decoded at parse
|
|
2043
|
+
* console.log(pid5?.isNull); // false: HL7 explicit null is "", absent is not
|
|
1863
2044
|
* console.log(pid5?.repetitions.length); // 1
|
|
1864
2045
|
* ```
|
|
1865
2046
|
*/
|
|
1866
2047
|
declare class Field {
|
|
1867
|
-
/** HL7 null indicator
|
|
2048
|
+
/** HL7 null indicator: `true` iff the underlying field was the two-char literal `""`. */
|
|
1868
2049
|
readonly isNull: boolean;
|
|
1869
2050
|
/** Reference to the underlying `RawField.repetitions` (no defensive copy). */
|
|
1870
2051
|
readonly repetitions: readonly RawRepetition[];
|
|
1871
2052
|
/** The 5 encoding characters for this message. Exposed for composite parsers (Plan 04). @internal */
|
|
1872
2053
|
readonly enc: EncodingCharacters;
|
|
1873
|
-
/** Position of this field in the parent message
|
|
2054
|
+
/** Position of this field in the parent message: used for position-aware error messages. @internal */
|
|
1874
2055
|
readonly position: Hl7Position;
|
|
1875
2056
|
/** The full `RawField` this wrapper wraps. Exposed for composite parsers (Plan 02/03/04). @internal */
|
|
1876
2057
|
readonly raw: RawField;
|
|
@@ -1882,7 +2063,7 @@ declare class Field {
|
|
|
1882
2063
|
constructor(raw: RawField, enc: EncodingCharacters, position: Hl7Position);
|
|
1883
2064
|
/**
|
|
1884
2065
|
* First-repetition, first-component, first-subcomponent value as a decoded
|
|
1885
|
-
* string
|
|
2066
|
+
* string: unescaped ONCE by the tokenizer on parse, returned verbatim here
|
|
1886
2067
|
* (never re-unescaped, HL7-VALUE-REDECODE). Returns `""` when the field is
|
|
1887
2068
|
* empty or HL7 null. Equivalent to `msg.get('SEG.N')` for a top-level access.
|
|
1888
2069
|
*
|
|
@@ -1895,13 +2076,13 @@ declare class Field {
|
|
|
1895
2076
|
*/
|
|
1896
2077
|
get value(): string;
|
|
1897
2078
|
/**
|
|
1898
|
-
* The field's **canonical wire text
|
|
2079
|
+
* The field's **canonical wire text**: the full field re-serialized with
|
|
1899
2080
|
* the active delimiters and re-escaped content (repetitions, components,
|
|
1900
2081
|
* and subcomponents included). Contrast with {@link value}, which
|
|
1901
2082
|
* returns only the first subcomponent of the first component of the first
|
|
1902
|
-
* repetition (decoded once at parse
|
|
2083
|
+
* repetition (decoded once at parse: never re-unescaped).
|
|
1903
2084
|
*
|
|
1904
|
-
* Use this when a field must be compared or echoed as a whole
|
|
2085
|
+
* Use this when a field must be compared or echoed as a whole: e.g.
|
|
1905
2086
|
* correlating an ACK's MSA-2 against the inbound MSH-10, where a
|
|
1906
2087
|
* vendor-quirk control id containing an unescaped delimiter (`ID^X`) must
|
|
1907
2088
|
* not be truncated to its first component.
|
|
@@ -1920,7 +2101,7 @@ declare class Field {
|
|
|
1920
2101
|
*
|
|
1921
2102
|
* **MSH-1/MSH-2 caveat.** Like {@link value}, calling this on MSH-1 or
|
|
1922
2103
|
* MSH-2 (the delimiter-definition fields) re-escapes the encoding
|
|
1923
|
-
* characters themselves and produces garbage
|
|
2104
|
+
* characters themselves and produces garbage: those two fields are only
|
|
1924
2105
|
* meaningful through `Hl7Message.encodingCharacters`.
|
|
1925
2106
|
*
|
|
1926
2107
|
* @example
|
|
@@ -1933,12 +2114,31 @@ declare class Field {
|
|
|
1933
2114
|
*/
|
|
1934
2115
|
get text(): string;
|
|
1935
2116
|
/**
|
|
1936
|
-
*
|
|
2117
|
+
* Render this field's formatted text (HL7 v2 §2.7 highlight + formatting
|
|
2118
|
+
* escapes) into a normalized {@link RenderedText} display model: plain text
|
|
2119
|
+
* plus highlight-aware runs. A **read projection** over the field's
|
|
2120
|
+
* byte-verbatim wire {@link text}: it never mutates the raw value, and it
|
|
2121
|
+
* never fabricates (an unrenderable escape is preserved + flagged). Use this
|
|
2122
|
+
* to surface a clinical narrative (an NTE / OBX-5 note) to a human without
|
|
2123
|
+
* the raw `\.br\` / `\H\` sentinels.
|
|
2124
|
+
*
|
|
2125
|
+
* @param opts - see {@link RenderTextOptions} (e.g. a custom line-break string).
|
|
2126
|
+
* @returns the normalized display model.
|
|
2127
|
+
*
|
|
2128
|
+
* @example
|
|
2129
|
+
* ```ts
|
|
2130
|
+
* const note = msg.segments("OBX")[0]?.field(5);
|
|
2131
|
+
* note?.render().text; // "Specimen received.\nGross exam normal."
|
|
2132
|
+
* ```
|
|
2133
|
+
*/
|
|
2134
|
+
render(opts?: RenderTextOptions): RenderedText;
|
|
2135
|
+
/**
|
|
2136
|
+
* Return a synthetic empty `Field` sentinel: used by `Segment.field(n)` to
|
|
1937
2137
|
* honor MODEL-05's "never throws on missing" contract. The returned Field
|
|
1938
2138
|
* has `isNull === false`, `repetitions === []`, and `value === ""`.
|
|
1939
2139
|
* Referentially stable across calls (same instance returned each time).
|
|
1940
2140
|
*
|
|
1941
|
-
* The `enc` argument is accepted for API symmetry but ignored
|
|
2141
|
+
* The `enc` argument is accepted for API symmetry but ignored: the
|
|
1942
2142
|
* synthetic field carries no content, so unescape would be a no-op
|
|
1943
2143
|
* regardless of the active encoding characters.
|
|
1944
2144
|
*
|
|
@@ -1953,7 +2153,7 @@ declare class Field {
|
|
|
1953
2153
|
/**
|
|
1954
2154
|
* Coerce this field's first repetition to a typed `XPN` (Extended Person
|
|
1955
2155
|
* Name). Absent components are OMITTED from the result
|
|
1956
|
-
* (`exactOptionalPropertyTypes`). Not memoized in v1
|
|
2156
|
+
* (`exactOptionalPropertyTypes`). Not memoized in v1: each call re-parses
|
|
1957
2157
|
* (D-09).
|
|
1958
2158
|
*
|
|
1959
2159
|
* @example
|
|
@@ -2029,7 +2229,7 @@ declare class Field {
|
|
|
2029
2229
|
*/
|
|
2030
2230
|
asPl(): PL;
|
|
2031
2231
|
/**
|
|
2032
|
-
* Coerce this field's first repetition to a typed `TS` (Time Stamp)
|
|
2232
|
+
* Coerce this field's first repetition to a typed `TS` (Time Stamp): the
|
|
2033
2233
|
* fidelity `DtmParts` (raw + parts + precision + timezone). `valid` is
|
|
2034
2234
|
* `false` on unparseable input (no throw). Build an absolute instant only on
|
|
2035
2235
|
* explicit request via `dtmToDate(ts)`.
|
|
@@ -2043,7 +2243,7 @@ declare class Field {
|
|
|
2043
2243
|
asTs(): TS;
|
|
2044
2244
|
/**
|
|
2045
2245
|
* Coerce this field's first repetition to a typed `NM` (Numeric).
|
|
2046
|
-
* `{ raw, value }
|
|
2246
|
+
* `{ raw, value }`: `value` is `undefined` on non-numeric input.
|
|
2047
2247
|
*
|
|
2048
2248
|
* @example
|
|
2049
2249
|
* ```ts
|
|
@@ -2094,18 +2294,18 @@ declare class Field {
|
|
|
2094
2294
|
}
|
|
2095
2295
|
|
|
2096
2296
|
/**
|
|
2097
|
-
* `Segment
|
|
2297
|
+
* `Segment`: wrapper class over a `RawSegment` that exposes `field(n)` with
|
|
2098
2298
|
* referentially stable `Field` instances (D-12). Constructed internally by
|
|
2099
2299
|
* `Hl7Message.segments(type)` / `Hl7Message.allSegments()`; user code never
|
|
2100
2300
|
* calls `new Segment(...)` directly.
|
|
2101
2301
|
*
|
|
2102
|
-
* The wrapper does NOT copy `raw.fields
|
|
2302
|
+
* The wrapper does NOT copy `raw.fields`: it holds a reference so mutations
|
|
2103
2303
|
* through Plan 04's `setField` / `addSegment` / `removeSegment` stay visible.
|
|
2104
2304
|
*/
|
|
2105
2305
|
|
|
2106
2306
|
/**
|
|
2107
2307
|
* Wrapper over a `RawSegment` exposing typed per-position `Field` instances.
|
|
2108
|
-
* `seg.field(3) === seg.field(3)
|
|
2308
|
+
* `seg.field(3) === seg.field(3)`: referential stability is guaranteed per
|
|
2109
2309
|
* segment instance.
|
|
2110
2310
|
*
|
|
2111
2311
|
* @example
|
|
@@ -2119,7 +2319,7 @@ declare class Field {
|
|
|
2119
2319
|
declare class Segment {
|
|
2120
2320
|
/** Segment name (3 chars, e.g. `"PID"`, `"OBX"`, `"ZPI"`). */
|
|
2121
2321
|
readonly type: string;
|
|
2122
|
-
/** Reference to the underlying `RawSegment.fields
|
|
2322
|
+
/** Reference to the underlying `RawSegment.fields`: 1-indexed per HL7 convention. */
|
|
2123
2323
|
readonly fields: readonly RawField[];
|
|
2124
2324
|
/** The 5 encoding characters for this message. Exposed for composite parsers. @internal */
|
|
2125
2325
|
readonly enc: EncodingCharacters;
|
|
@@ -2135,7 +2335,7 @@ declare class Segment {
|
|
|
2135
2335
|
* @internal
|
|
2136
2336
|
*/
|
|
2137
2337
|
readonly customFields: Readonly<Record<string, number>> | undefined;
|
|
2138
|
-
/** Lazy cache of Field wrappers
|
|
2338
|
+
/** Lazy cache of Field wrappers: one per fields[] position. @internal */
|
|
2139
2339
|
private _fieldWrappers;
|
|
2140
2340
|
/**
|
|
2141
2341
|
* Construct a new `Segment`. Called internally by `Hl7Message`; user code
|
|
@@ -2151,16 +2351,16 @@ declare class Segment {
|
|
|
2151
2351
|
constructor(raw: RawSegment, enc: EncodingCharacters, absoluteIndex: number, customFields?: Readonly<Record<string, number>>);
|
|
2152
2352
|
/**
|
|
2153
2353
|
* Return the `Field` wrapper at HL7 position `n`. Indexing follows the HL7
|
|
2154
|
-
* 1-indexed convention
|
|
2354
|
+
* 1-indexed convention: `seg.field(5)` on a PID segment maps to PID-5.
|
|
2155
2355
|
* MSH segments use the same user-facing convention: `msh.field(1)` returns
|
|
2156
2356
|
* the field-separator (MSH-1), `msh.field(2)` returns encoding chars
|
|
2157
|
-
* (MSH-2), `msh.field(3)` returns MSH-3, and so on
|
|
2357
|
+
* (MSH-2), `msh.field(3)` returns MSH-3, and so on: the internal
|
|
2158
2358
|
* `fields[N-1]` offset for MSH segments is applied here (mirrors the
|
|
2159
2359
|
* dot-path resolver in `dot-path.ts`, keeping `msg.segments('MSH')[0].field(3)`
|
|
2160
2360
|
* and `msg.get('MSH.3')` in agreement).
|
|
2161
2361
|
*
|
|
2162
2362
|
* Returns a synthetic empty `Field` (`.isNull === false`, `.value === ""`)
|
|
2163
|
-
* when `n` is out of range
|
|
2363
|
+
* when `n` is out of range: never throws (MODEL-05). Successive calls with
|
|
2164
2364
|
* the same `n` return the same `Field` instance (D-12).
|
|
2165
2365
|
*
|
|
2166
2366
|
* @example
|
|
@@ -2175,13 +2375,13 @@ declare class Segment {
|
|
|
2175
2375
|
/**
|
|
2176
2376
|
* Return the `Field` at the profile-declared position for `name`, or
|
|
2177
2377
|
* `undefined` when no custom mapping exists (PROF-07). Unlike `field(n)`,
|
|
2178
|
-
* missing names return `undefined
|
|
2378
|
+
* missing names return `undefined`, NOT a synthetic empty Field, so
|
|
2179
2379
|
* typos surface instead of silently resolving to an empty string (D-14).
|
|
2180
2380
|
*
|
|
2181
2381
|
* For segments without a profile-declared customSegments slice (most
|
|
2182
2382
|
* non-Z segments, and any Z-segment whose host message had no profile
|
|
2183
2383
|
* applied), this method always returns `undefined` (D-15 defense-in-depth
|
|
2184
|
-
|
|
2384
|
+
*: D-05 already rejects standard-segment overlays at `defineProfile()`
|
|
2185
2385
|
* time).
|
|
2186
2386
|
*
|
|
2187
2387
|
* When the declared position is out of range for the underlying
|
|
@@ -2200,7 +2400,7 @@ declare class Segment {
|
|
|
2200
2400
|
}
|
|
2201
2401
|
|
|
2202
2402
|
/**
|
|
2203
|
-
* `identityEvents
|
|
2403
|
+
* `identityEvents`: roadmap Phase K read-side helper that recognizes the ADT
|
|
2204
2404
|
* patient-identity management trigger events and surfaces the MRG segment's
|
|
2205
2405
|
* *prior* (non-surviving) identifiers alongside the PID/PV1 *surviving*
|
|
2206
2406
|
* identifiers, each labelled by role.
|
|
@@ -2208,7 +2408,7 @@ declare class Segment {
|
|
|
2208
2408
|
* Spec traceability (HL7 v2 Ch. 3, Patient Administration):
|
|
2209
2409
|
* - A18 "The PID segment contains the surviving patient ID information. The
|
|
2210
2410
|
* MRG segment contains the non-surviving information." The merge direction
|
|
2211
|
-
* is spec-explicit and constant: **MRG (prior) → PID (surviving)
|
|
2411
|
+
* is spec-explicit and constant: **MRG (prior) → PID (surviving)**: this
|
|
2212
2412
|
* helper NEVER infers it from content and never reverses it.
|
|
2213
2413
|
* - Merge family (§3.3.34–§3.3.42): A34/A35/A36 (v2.3-era merges), A39 merge
|
|
2214
2414
|
* person, A40 merge patient identifier list, A41 merge account, A42 merge
|
|
@@ -2217,33 +2417,33 @@ declare class Segment {
|
|
|
2217
2417
|
* A28 / A31 (PID only, no MRG).
|
|
2218
2418
|
* - MRG field map is **version-scoped** (v2.5.1 §3.4.10): MRG-1 prior patient
|
|
2219
2419
|
* identifier list (CX, repeating), MRG-3 prior patient account number (CX),
|
|
2220
|
-
* MRG-4 prior patient ID (CX, backward-compat only
|
|
2420
|
+
* MRG-4 prior patient ID (CX, backward-compat only: **withdrawn as of
|
|
2221
2421
|
* v2.7** in favour of MRG-1), MRG-5 prior visit number (CX), MRG-7 prior
|
|
2222
2422
|
* patient name (XPN). MRG-2 / MRG-6 (prior *alternate* patient/visit IDs,
|
|
2223
2423
|
* also withdrawn v2.7) are deliberately not surfaced. On a v2.7+ message
|
|
2224
|
-
* (MSH-12) the withdrawn MRG-4
|
|
2424
|
+
* (MSH-12) the withdrawn MRG-4 (and the symmetric withdrawn PID-2) are
|
|
2225
2425
|
* NOT read as identity fields.
|
|
2226
2426
|
*
|
|
2227
2427
|
* Fail-safe: a merge/move event missing either side of the MRG→PID pair
|
|
2228
2428
|
* surfaces what is present plus a `MERGE_MISSING_PRIOR_OR_SURVIVOR` warning on
|
|
2229
|
-
* the event
|
|
2429
|
+
* the event: the MRG is **never dropped** (an orphaned MRG yields its own
|
|
2230
2430
|
* event) and the direction is **never guessed**. The helper never throws.
|
|
2231
2431
|
*
|
|
2232
2432
|
* PHI note: identifiers and names are the *payload* of the typed view (by
|
|
2233
|
-
* design
|
|
2433
|
+
* design: the consumer needs them to apply the merge), but warning messages
|
|
2234
2434
|
* carry only structural facts (event code + missing role), never a value.
|
|
2235
2435
|
*
|
|
2236
2436
|
* Applying the merge (re-pointing stored data to the survivor) is the
|
|
2237
|
-
* consumer's / integration engine's job
|
|
2437
|
+
* consumer's / integration engine's job: this helper only surfaces it.
|
|
2238
2438
|
*/
|
|
2239
2439
|
|
|
2240
2440
|
/**
|
|
2241
2441
|
* Classification of a recognized identity trigger event.
|
|
2242
2442
|
*
|
|
2243
|
-
* - `merge
|
|
2244
|
-
* - `move
|
|
2245
|
-
* - `link` / `unlink
|
|
2246
|
-
* - `add` / `update
|
|
2443
|
+
* - `merge`: A18 / A34 / A35 / A36 / A39 / A40 / A41 / A42 (MRG expected)
|
|
2444
|
+
* - `move`: A43 / A44 (MRG expected)
|
|
2445
|
+
* - `link` / `unlink`: A24 / A37 (two PID groups, no MRG)
|
|
2446
|
+
* - `add` / `update`: A28 / A31 (person add/update, no MRG)
|
|
2247
2447
|
*
|
|
2248
2448
|
* @example
|
|
2249
2449
|
* ```ts
|
|
@@ -2255,7 +2455,7 @@ type IdentityEventKind = "merge" | "move" | "link" | "unlink" | "add" | "update"
|
|
|
2255
2455
|
/**
|
|
2256
2456
|
* Role of one party in an identity event. `surviving` / `subject` / `linked`
|
|
2257
2457
|
* parties are ONLY ever sourced from PID (+ PV1); `prior` parties are ONLY
|
|
2258
|
-
* ever sourced from MRG
|
|
2458
|
+
* ever sourced from MRG: the role-labelling invariant Phase K exists for.
|
|
2259
2459
|
*
|
|
2260
2460
|
* @example
|
|
2261
2461
|
* ```ts
|
|
@@ -2289,15 +2489,15 @@ type IdentityRole = "surviving" | "prior" | "linked" | "subject";
|
|
|
2289
2489
|
* ```
|
|
2290
2490
|
*/
|
|
2291
2491
|
interface IdentityParty {
|
|
2292
|
-
/** Role of this party in the event
|
|
2492
|
+
/** Role of this party in the event: the safety-critical label. */
|
|
2293
2493
|
readonly role: IdentityRole;
|
|
2294
|
-
/** Segment this party was sourced from
|
|
2494
|
+
/** Segment this party was sourced from: provenance for the role label. */
|
|
2295
2495
|
readonly sourceSegment: "PID" | "MRG";
|
|
2296
2496
|
/** Identifier list (PID-3 / MRG-1), every non-empty CX repetition. */
|
|
2297
2497
|
readonly identifiers: readonly CX[];
|
|
2298
2498
|
/**
|
|
2299
2499
|
* Legacy single patient ID (PID-2 / MRG-4). Backward-compat only; withdrawn
|
|
2300
|
-
* as of HL7 v2.7
|
|
2500
|
+
* as of HL7 v2.7: OMITTED (not read) when MSH-12 declares v2.7 or later.
|
|
2301
2501
|
*/
|
|
2302
2502
|
readonly legacyPatientId?: CX;
|
|
2303
2503
|
/** Patient account number (PID-18 / MRG-3). */
|
|
@@ -2310,11 +2510,11 @@ interface IdentityParty {
|
|
|
2310
2510
|
/**
|
|
2311
2511
|
* One recognized patient-identity event. For `merge` / `move` kinds the
|
|
2312
2512
|
* `surviving` (PID/PV1-sourced) and `prior` (MRG-sourced) parties are also
|
|
2313
|
-
* exposed directly, with the spec-constant `direction: "MRG_TO_PID"
|
|
2513
|
+
* exposed directly, with the spec-constant `direction: "MRG_TO_PID"`: the
|
|
2314
2514
|
* prior identifiers are the ones being retired in favour of the surviving
|
|
2315
2515
|
* ones, never the reverse (HL7 v2 Ch. 3, A18/A39/A40).
|
|
2316
2516
|
*
|
|
2317
|
-
* `parties` is the complete role-labelled surface in document order
|
|
2517
|
+
* `parties` is the complete role-labelled surface in document order: nothing
|
|
2318
2518
|
* present in the message is dropped, including a nonconforming MRG in a
|
|
2319
2519
|
* link/add message (surfaced as a `prior`-role party).
|
|
2320
2520
|
*
|
|
@@ -2330,7 +2530,7 @@ interface IdentityParty {
|
|
|
2330
2530
|
* if (ev.kind === "merge" && ev.prior && ev.surviving) {
|
|
2331
2531
|
* // retire ev.prior.identifiers in favour of ev.surviving.identifiers
|
|
2332
2532
|
* } else if (ev.warnings.length > 0) {
|
|
2333
|
-
* // incomplete pair
|
|
2533
|
+
* // incomplete pair: do NOT apply; route for review
|
|
2334
2534
|
* }
|
|
2335
2535
|
* }
|
|
2336
2536
|
* ```
|
|
@@ -2340,11 +2540,11 @@ interface IdentityEvent {
|
|
|
2340
2540
|
readonly eventType: string;
|
|
2341
2541
|
/** Classification of the trigger event. */
|
|
2342
2542
|
readonly kind: IdentityEventKind;
|
|
2343
|
-
/** Every party in document order, role-labelled
|
|
2543
|
+
/** Every party in document order, role-labelled: the complete surface. */
|
|
2344
2544
|
readonly parties: readonly IdentityParty[];
|
|
2345
|
-
/** The surviving party (merge/move)
|
|
2545
|
+
/** The surviving party (merge/move): ONLY ever sourced from PID/PV1. */
|
|
2346
2546
|
readonly surviving?: IdentityParty;
|
|
2347
|
-
/** The prior (non-surviving) party (merge/move)
|
|
2547
|
+
/** The prior (non-surviving) party (merge/move): ONLY ever sourced from MRG. */
|
|
2348
2548
|
readonly prior?: IdentityParty;
|
|
2349
2549
|
/**
|
|
2350
2550
|
* Spec-constant merge/move direction: the MRG (prior) identifiers merge
|
|
@@ -2357,14 +2557,14 @@ interface IdentityEvent {
|
|
|
2357
2557
|
}
|
|
2358
2558
|
|
|
2359
2559
|
/**
|
|
2360
|
-
* Phase 4 helper type declarations
|
|
2560
|
+
* Phase 4 helper type declarations: the 9 typed shapes returned by
|
|
2361
2561
|
* `Hl7Message.meta`/`.patient`/`.visit` getters and the 6 collection methods
|
|
2362
2562
|
* (`observations()`, `orders()`, `nextOfKin()`, `allergies()`, `diagnoses()`,
|
|
2363
2563
|
* `insurance()`). Every field is `readonly`; every optional key is
|
|
2364
2564
|
* `exactOptionalPropertyTypes`-safe (never `| undefined` on the declaration).
|
|
2365
2565
|
*
|
|
2366
2566
|
* Types compose on the 11 v1 composites from `src/model/types/*.ts` (XPN, XAD,
|
|
2367
|
-
* CX, CWE, CE, XTN, PL, XCN)
|
|
2567
|
+
* CX, CWE, CE, XTN, PL, XCN): helpers are pure views on top of Phase 3's
|
|
2368
2568
|
* composite coercions, never re-parsing the raw tree.
|
|
2369
2569
|
*
|
|
2370
2570
|
* Field lists locked by Phase 4 CONTEXT.md decisions D-01..D-24. Nine interfaces:
|
|
@@ -2401,7 +2601,7 @@ interface Meta {
|
|
|
2401
2601
|
readonly triggerEvent?: string;
|
|
2402
2602
|
/** MSH-9.3 message structure (e.g. "ADT_A01"). */
|
|
2403
2603
|
readonly messageStructure?: string;
|
|
2404
|
-
/** MSH-10 message control ID
|
|
2604
|
+
/** MSH-10 message control ID: unique per message per sender. */
|
|
2405
2605
|
readonly controlId?: string;
|
|
2406
2606
|
/** MSH-7 message date/time as the fidelity `TS` (Phase N). */
|
|
2407
2607
|
readonly timestamp?: TS;
|
|
@@ -2422,7 +2622,7 @@ interface Meta {
|
|
|
2422
2622
|
* PID-derived patient view (HELPERS-02). `msg.patient` is `undefined` (D-04)
|
|
2423
2623
|
* when no PID segment exists; this interface describes the shape when
|
|
2424
2624
|
* present. `identifiers` and `phoneNumbers` are ALWAYS present as arrays
|
|
2425
|
-
* (D-09 / D-20)
|
|
2625
|
+
* (D-09 / D-20): empty when the underlying field is absent. `name` is
|
|
2426
2626
|
* ALWAYS present (D-19) even if `{}` when PID-5 is empty.
|
|
2427
2627
|
*
|
|
2428
2628
|
* @example
|
|
@@ -2457,7 +2657,7 @@ interface Patient {
|
|
|
2457
2657
|
readonly fullName?: string;
|
|
2458
2658
|
/**
|
|
2459
2659
|
* PID-7 date of birth as the fidelity `TS` (Phase N). A day-only DOB keeps
|
|
2460
|
-
* `precision: "day"
|
|
2660
|
+
* `precision: "day"`: never coerced to a UTC-midnight instant that would
|
|
2461
2661
|
* read as the previous day in a negative-offset zone.
|
|
2462
2662
|
*/
|
|
2463
2663
|
readonly dateOfBirth?: TS;
|
|
@@ -2474,7 +2674,7 @@ interface Patient {
|
|
|
2474
2674
|
/** PID-15 primary language. */
|
|
2475
2675
|
readonly language?: CE;
|
|
2476
2676
|
/**
|
|
2477
|
-
* NTE note lines positionally attached to the (first) PID (Phase P)
|
|
2677
|
+
* NTE note lines positionally attached to the (first) PID (Phase P): notes
|
|
2478
2678
|
* immediately following the patient's PID segment, HL7-unescaped, in
|
|
2479
2679
|
* document order. OMITTED when the patient carries no notes. High-PHI-risk
|
|
2480
2680
|
* clinical narrative.
|
|
@@ -2530,7 +2730,7 @@ interface Visit {
|
|
|
2530
2730
|
* ```
|
|
2531
2731
|
*/
|
|
2532
2732
|
interface ObservationBase {
|
|
2533
|
-
/** OBX-1 set id (string
|
|
2733
|
+
/** OBX-1 set id (string: typically sequential "1", "2", ...). */
|
|
2534
2734
|
readonly setId?: string;
|
|
2535
2735
|
/** OBX-3 observation identifier. Always present (may be `{}` if OBX-3 absent). */
|
|
2536
2736
|
readonly identifier: CWE;
|
|
@@ -2538,11 +2738,11 @@ interface ObservationBase {
|
|
|
2538
2738
|
readonly units?: CWE;
|
|
2539
2739
|
/**
|
|
2540
2740
|
* `true` iff OBX-6's coding system (CWE.3, "name of coding system") is
|
|
2541
|
-
* exactly `UCUM` (HL7 Table 0396)
|
|
2741
|
+
* exactly `UCUM` (HL7 Table 0396): i.e. the unit is declared UCUM and is
|
|
2542
2742
|
* safe to interpret as a computable unit. `false` means a unit IS present
|
|
2543
2743
|
* but is NOT declared UCUM (e.g. a local code or free text) and is surfaced
|
|
2544
2744
|
* as-is, never coerced. OMITTED when OBX-6 is absent. This is a *claim* check
|
|
2545
|
-
* only
|
|
2745
|
+
* only: the library does not validate UCUM grammar or check the alternate
|
|
2546
2746
|
* coding system (CWE.6).
|
|
2547
2747
|
*/
|
|
2548
2748
|
readonly unitsAreUcum?: boolean;
|
|
@@ -2555,7 +2755,7 @@ interface ObservationBase {
|
|
|
2555
2755
|
/** OBX-14 date/time of observation as the fidelity `TS` (Phase N). */
|
|
2556
2756
|
readonly observedDateTime?: TS;
|
|
2557
2757
|
/**
|
|
2558
|
-
* NTE note lines positionally attached to this OBX (Phase P)
|
|
2758
|
+
* NTE note lines positionally attached to this OBX (Phase P): each
|
|
2559
2759
|
* non-empty NTE-3 (Comment, FT) repetition of every NTE immediately
|
|
2560
2760
|
* following this observation, HL7-unescaped, in document order. OMITTED when
|
|
2561
2761
|
* the observation carries no notes. High-PHI-risk clinical narrative.
|
|
@@ -2608,30 +2808,30 @@ type Observation = ObservationBase & ({
|
|
|
2608
2808
|
readonly value: string | undefined;
|
|
2609
2809
|
});
|
|
2610
2810
|
/**
|
|
2611
|
-
* How an order/medication `RepeatPattern` code (HL7 Table 0335) is classified
|
|
2811
|
+
* How an order/medication `RepeatPattern` code (HL7 Table 0335) is classified,
|
|
2612
2812
|
* **provenance only, never used to resolve a schedule** (Phase M). The `code`
|
|
2613
2813
|
* is always authoritative and surfaced verbatim; this flag only tells a
|
|
2614
2814
|
* consumer *what kind* of pattern it is looking at so it can decide whether a
|
|
2615
2815
|
* load-bearing integer is present.
|
|
2616
2816
|
*
|
|
2617
|
-
* - `"parametric"
|
|
2817
|
+
* - `"parametric"`: a `Q<integer><unit>` template (`Q6H`, `Q30M`, `Q2D`,
|
|
2618
2818
|
* `Q1W`, `Q3J5`) whose **integer is load-bearing**: `Q6H` (every 6 hours) is
|
|
2619
2819
|
* a different dose count from `Q8H`. The parsed integer + unit ride on
|
|
2620
2820
|
* {@link RepeatPattern.interval}.
|
|
2621
|
-
* - `"named"
|
|
2821
|
+
* - `"named"`: a recognized fixed Table-0335 mnemonic scheduled at
|
|
2622
2822
|
* institution-specified times (`BID`, `TID`, `QID`, `QOD`, `QHS`, `QAM`,
|
|
2623
2823
|
* `QPM`, `QSHIFT`, `PRN`, `AC`, `PC`, `HS`, `C`). No numeric interval.
|
|
2624
|
-
* - `"unknown"
|
|
2824
|
+
* - `"unknown"`: anything else (a local code, free text, an unrecognized
|
|
2625
2825
|
* mnemonic). Surfaced **verbatim**, never mapped to a frequency.
|
|
2626
2826
|
*/
|
|
2627
2827
|
type RepeatPatternKind = "parametric" | "named" | "unknown";
|
|
2628
2828
|
/**
|
|
2629
|
-
* An order/medication timing **repeat pattern** (HL7 Table 0335)
|
|
2829
|
+
* An order/medication timing **repeat pattern** (HL7 Table 0335): the
|
|
2630
2830
|
* frequency/SIG field (TQ1-3, or the legacy embedded TQ interval RI.1), Phase M.
|
|
2631
2831
|
*
|
|
2632
2832
|
* **Safety contract.** `code` is the **decoded field value** (HL7 escapes are
|
|
2633
2833
|
* unescaped as with every field read) and is **never resolved to clock times,
|
|
2634
|
-
* normalized, or mapped to a different frequency
|
|
2834
|
+
* normalized, or mapped to a different frequency**: reading `Q6H` as "daily"
|
|
2635
2835
|
* or silently dropping a `BID` changes the administered dose count, a
|
|
2636
2836
|
* transcription-class harm. `kind`/`interval` are convenience provenance ONLY;
|
|
2637
2837
|
* `code` is the authoritative value.
|
|
@@ -2646,12 +2846,12 @@ type RepeatPatternKind = "parametric" | "named" | "unknown";
|
|
|
2646
2846
|
interface RepeatPattern {
|
|
2647
2847
|
/** The Table-0335 repeat-pattern code exactly as authored (e.g. `"Q6H"`, `"BID"`). Never normalized. */
|
|
2648
2848
|
readonly code: string;
|
|
2649
|
-
/** Provenance classification of `code
|
|
2849
|
+
/** Provenance classification of `code`: never used to resolve a schedule. See {@link RepeatPatternKind}. */
|
|
2650
2850
|
readonly kind: RepeatPatternKind;
|
|
2651
2851
|
/**
|
|
2652
2852
|
* For a `"parametric"` `Q<integer><unit>` template only: the load-bearing
|
|
2653
2853
|
* integer and its unit letter (`S`/`M`/`H`/`D`/`W`/`L`, or `J` for
|
|
2654
|
-
* day-of-week). OMITTED for `"named"`/`"unknown"` patterns. Informational
|
|
2854
|
+
* day-of-week). OMITTED for `"named"`/`"unknown"` patterns. Informational,
|
|
2655
2855
|
* `code` remains authoritative.
|
|
2656
2856
|
*/
|
|
2657
2857
|
readonly interval?: {
|
|
@@ -2660,7 +2860,7 @@ interface RepeatPattern {
|
|
|
2660
2860
|
};
|
|
2661
2861
|
}
|
|
2662
2862
|
/**
|
|
2663
|
-
* A composite-quantity (CQ) value on an order/medication timing
|
|
2863
|
+
* A composite-quantity (CQ) value on an order/medication timing: the TQ1-2
|
|
2664
2864
|
* service quantity, Phase M. `value` is strict-`Number()` parsed (`undefined`,
|
|
2665
2865
|
* never `NaN`); `units` carries any CQ.2 units. Both keys OMITTED when absent.
|
|
2666
2866
|
*
|
|
@@ -2677,7 +2877,7 @@ interface TimingQuantity {
|
|
|
2677
2877
|
readonly units?: CWE;
|
|
2678
2878
|
}
|
|
2679
2879
|
/**
|
|
2680
|
-
* The order/medication **timing** structure
|
|
2880
|
+
* The order/medication **timing** structure: one TQ1 segment (v2.5+) or the
|
|
2681
2881
|
* legacy embedded TQ in ORC-7 / RXE-1 (pre-v2.5), Phase M. Attached to
|
|
2682
2882
|
* {@link Order.timings} and {@link Medication.timings}.
|
|
2683
2883
|
*
|
|
@@ -2686,7 +2886,7 @@ interface TimingQuantity {
|
|
|
2686
2886
|
* clock times, or interpret sig. The load-bearing {@link repeatPattern} and
|
|
2687
2887
|
* {@link totalOccurrences} are preserved verbatim (see {@link RepeatPattern});
|
|
2688
2888
|
* `startDateTime`/`endDateTime` keep the Phase N `TS` precision + timezone
|
|
2689
|
-
* fidelity. A malformed timing never throws
|
|
2889
|
+
* fidelity. A malformed timing never throws: absent pieces are omitted keys.
|
|
2690
2890
|
*
|
|
2691
2891
|
* @example
|
|
2692
2892
|
* ```ts
|
|
@@ -2704,18 +2904,18 @@ interface OrderTiming {
|
|
|
2704
2904
|
* Which structure this timing was read from: the dedicated **TQ1** segment
|
|
2705
2905
|
* (v2.5+) or the **legacy** embedded TQ data type in ORC-7 (orders) / RXE-1
|
|
2706
2906
|
* (encoded medications, pre-v2.5). The library treats the presence of a TQ1
|
|
2707
|
-
* segment as the v2.5+ signal
|
|
2907
|
+
* segment as the v2.5+ signal: the legacy embedded TQ is surfaced only when
|
|
2708
2908
|
* no TQ1 accompanies the order, so the same timing is never double-counted
|
|
2709
2909
|
* and a legacy-only timing is never dropped.
|
|
2710
2910
|
*/
|
|
2711
2911
|
readonly source: "TQ1" | "legacy";
|
|
2712
2912
|
/** TQ1-2 / legacy TQ.1 service quantity (CQ). */
|
|
2713
2913
|
readonly quantity?: TimingQuantity;
|
|
2714
|
-
/** TQ1-3 / legacy TQ.2 interval RI.1 repeat pattern (Table 0335)
|
|
2914
|
+
/** TQ1-3 / legacy TQ.2 interval RI.1 repeat pattern (Table 0335): verbatim. See {@link RepeatPattern}. */
|
|
2715
2915
|
readonly repeatPattern?: RepeatPattern;
|
|
2716
|
-
/** TQ1-4 / legacy TQ.2 interval RI.2 explicit time(s)
|
|
2916
|
+
/** TQ1-4 / legacy TQ.2 interval RI.2 explicit time(s): surfaced verbatim (first repetition/value). */
|
|
2717
2917
|
readonly explicitTime?: string;
|
|
2718
|
-
/** TQ1-6 / legacy TQ.3 service duration
|
|
2918
|
+
/** TQ1-6 / legacy TQ.3 service duration: surfaced verbatim. */
|
|
2719
2919
|
readonly serviceDuration?: string;
|
|
2720
2920
|
/** TQ1-7 / legacy TQ.4 start date/time as the fidelity `TS` (Phase N). */
|
|
2721
2921
|
readonly startDateTime?: TS;
|
|
@@ -2724,7 +2924,7 @@ interface OrderTiming {
|
|
|
2724
2924
|
/** TQ1-9 priority (CWE) / legacy TQ.6 priority (surfaced as a CWE `{ identifier }`). */
|
|
2725
2925
|
readonly priority?: CWE;
|
|
2726
2926
|
/**
|
|
2727
|
-
* TQ1-14 / legacy TQ.12 total occurrences (NM)
|
|
2927
|
+
* TQ1-14 / legacy TQ.12 total occurrences (NM): how many times the service
|
|
2728
2928
|
* is to be performed (strict-parsed; never `NaN`). **TQ1-14, not TQ1-11**
|
|
2729
2929
|
* (TQ1-11 is Text Instruction). Load-bearing: losing it drops the total
|
|
2730
2930
|
* administered count.
|
|
@@ -2733,8 +2933,8 @@ interface OrderTiming {
|
|
|
2733
2933
|
}
|
|
2734
2934
|
/**
|
|
2735
2935
|
* OBR-derived order (HELPERS-05, D-16) with positionally-grouped OBX children
|
|
2736
|
-
* (D-12). `observations` is ALWAYS present
|
|
2737
|
-
* OBR before the next OBR or end-of-message. Phase M adds `timings
|
|
2936
|
+
* (D-12). `observations` is ALWAYS present: empty when no OBX follows this
|
|
2937
|
+
* OBR before the next OBR or end-of-message. Phase M adds `timings`: ALWAYS
|
|
2738
2938
|
* present, empty when the order carries no TQ1 / legacy embedded TQ.
|
|
2739
2939
|
*
|
|
2740
2940
|
* @example
|
|
@@ -2757,7 +2957,7 @@ interface Order {
|
|
|
2757
2957
|
readonly fillerOrderNumber?: string;
|
|
2758
2958
|
/** OBR-4 universal service identifier (test code + description). */
|
|
2759
2959
|
readonly universalServiceId?: CWE;
|
|
2760
|
-
/** OBR-5 order status (v1
|
|
2960
|
+
/** OBR-5 order status (v1: Phase 7 may reconcile with OBR-25). */
|
|
2761
2961
|
readonly orderStatus?: string;
|
|
2762
2962
|
/** ORC-1 order control when an ORC precedes this OBR. */
|
|
2763
2963
|
readonly orderControl?: string;
|
|
@@ -2767,23 +2967,23 @@ interface Order {
|
|
|
2767
2967
|
readonly observations: readonly Observation[];
|
|
2768
2968
|
/**
|
|
2769
2969
|
* TQ1 / legacy embedded-TQ timing(s) grouped under this order (Phase M).
|
|
2770
|
-
* Always present
|
|
2970
|
+
* Always present: empty when the order carries no timing. See
|
|
2771
2971
|
* {@link OrderTiming}.
|
|
2772
2972
|
*/
|
|
2773
2973
|
readonly timings: readonly OrderTiming[];
|
|
2774
2974
|
/**
|
|
2775
|
-
* NTE note lines positionally attached to this order (Phase P)
|
|
2975
|
+
* NTE note lines positionally attached to this order (Phase P): the
|
|
2776
2976
|
* ORC-region notes (before the OBR) followed by the OBR-region notes, in
|
|
2777
2977
|
* document order. Several ORCs before one OBR all contribute here; nothing is
|
|
2778
2978
|
* dropped. OMITTED when the order carries no notes. A note on a trailing or
|
|
2779
2979
|
* dangling ORC that never opens an order is surfaced at message level
|
|
2780
|
-
* (`msg.notes()`), not here
|
|
2980
|
+
* (`msg.notes()`), not here: still never dropped. High-PHI-risk clinical
|
|
2781
2981
|
* narrative.
|
|
2782
2982
|
*/
|
|
2783
2983
|
readonly notes?: readonly string[];
|
|
2784
2984
|
}
|
|
2785
2985
|
/**
|
|
2786
|
-
* NK1-derived next-of-kin entry (HELPERS-06). Lean subset
|
|
2986
|
+
* NK1-derived next-of-kin entry (HELPERS-06). Lean subset: callers can
|
|
2787
2987
|
* reach for `msg.segments("NK1")` when they need the full NK1 surface.
|
|
2788
2988
|
*
|
|
2789
2989
|
* @example
|
|
@@ -2857,21 +3057,21 @@ interface Diagnosis {
|
|
|
2857
3057
|
readonly type?: string;
|
|
2858
3058
|
}
|
|
2859
3059
|
/**
|
|
2860
|
-
* Which pharmacy/treatment segment a `Medication` was extracted from
|
|
3060
|
+
* Which pharmacy/treatment segment a `Medication` was extracted from: the
|
|
2861
3061
|
* clinical "phase" of the medication. Each value maps 1:1 to one RX* parent
|
|
2862
3062
|
* segment (Phase D, Ch. 4A):
|
|
2863
|
-
* - `"order"
|
|
3063
|
+
* - `"order"`: **RXO** (Pharmacy/Treatment Order): the originally *requested*
|
|
2864
3064
|
* give code/amount/dosage-form, before pharmacy encoding.
|
|
2865
|
-
* - `"encoded"
|
|
2866
|
-
* code/amount, plus the give *strength* (RXE-25/26)
|
|
3065
|
+
* - `"encoded"`: **RXE** (Encoded Order): the pharmacy-encoded give
|
|
3066
|
+
* code/amount, plus the give *strength* (RXE-25/26): the only context that
|
|
2867
3067
|
* carries a separate strength.
|
|
2868
|
-
* - `"dispense"
|
|
2869
|
-
* - `"administration"
|
|
3068
|
+
* - `"dispense"`: **RXD** (Dispense): what was actually dispensed.
|
|
3069
|
+
* - `"administration"`: **RXA** (Administration): what was actually given to
|
|
2870
3070
|
* the patient.
|
|
2871
3071
|
*
|
|
2872
3072
|
* The context is preserved verbatim and never collapsed: an RDE order that
|
|
2873
3073
|
* carries both an RXO request and an RXE encoded line surfaces as TWO
|
|
2874
|
-
* `Medication` entries with distinct contexts
|
|
3074
|
+
* `Medication` entries with distinct contexts: the helper never reconciles
|
|
2875
3075
|
* one against the other.
|
|
2876
3076
|
*/
|
|
2877
3077
|
type MedicationContext = "order" | "encoded" | "dispense" | "administration";
|
|
@@ -2880,10 +3080,10 @@ type MedicationContext = "order" | "encoded" | "dispense" | "administration";
|
|
|
2880
3080
|
* Carries the HL7 min/max amount pair and its units.
|
|
2881
3081
|
*
|
|
2882
3082
|
* - For an **order** (RXO-2/3) or **encoded** order (RXE-3/4) the amount is a
|
|
2883
|
-
* genuine min..max range
|
|
3083
|
+
* genuine min..max range: both keys may be present.
|
|
2884
3084
|
* - For a **dispense** (RXD-4) or **administration** (RXA-6) there is a SINGLE
|
|
2885
3085
|
* amount; it is surfaced as `minimum` with `maximum` OMITTED. This is a
|
|
2886
|
-
* single value, not a range
|
|
3086
|
+
* single value, not a range: do not read the absent `maximum` as "no upper
|
|
2887
3087
|
* bound on a range".
|
|
2888
3088
|
*
|
|
2889
3089
|
* `minimum`/`maximum` are strict-`Number()` parsed (`undefined`, never `NaN`).
|
|
@@ -2906,7 +3106,7 @@ interface MedicationAmount {
|
|
|
2906
3106
|
}
|
|
2907
3107
|
/**
|
|
2908
3108
|
* The give *strength* of an **encoded** `Medication` (RXE-25 value + RXE-26
|
|
2909
|
-
* units)
|
|
3109
|
+
* units): Phase D. Strength is the concentration of active ingredient (e.g.
|
|
2910
3110
|
* "250 mg"), distinct from the give *amount* (how much is administered, e.g.
|
|
2911
3111
|
* "2 tablets"). Only the `"encoded"` (RXE) context carries strength.
|
|
2912
3112
|
*
|
|
@@ -2914,7 +3114,7 @@ interface MedicationAmount {
|
|
|
2914
3114
|
* RXE-25/26 fields declare it, and is NEVER reconciled against any strength
|
|
2915
3115
|
* *implied* by the give code (e.g. an NDC that encodes "250 mg"). A consumer
|
|
2916
3116
|
* that sees both an explicit strength here and a coded drug in `giveCode` must
|
|
2917
|
-
* treat a disagreement as a real signal
|
|
3117
|
+
* treat a disagreement as a real signal: the library does not silently pick a
|
|
2918
3118
|
* winner. `value` is strict-`Number()` parsed (`undefined`, never `NaN`).
|
|
2919
3119
|
*
|
|
2920
3120
|
* @example
|
|
@@ -2932,7 +3132,7 @@ interface MedicationStrength {
|
|
|
2932
3132
|
/**
|
|
2933
3133
|
* One RXR (Pharmacy/Treatment Route) grouped under its parent RX* segment
|
|
2934
3134
|
* (Phase D). `route` is HL7 Table 0162 (CWE); `site` is Table 0163 (CWE).
|
|
2935
|
-
* Provenance travels on the CWE (`route.nameOfCodingSystem`)
|
|
3135
|
+
* Provenance travels on the CWE (`route.nameOfCodingSystem`): a "PO" route is
|
|
2936
3136
|
* only safe to act on when you know the system it was coded against.
|
|
2937
3137
|
*
|
|
2938
3138
|
* @example
|
|
@@ -2949,7 +3149,7 @@ interface MedicationRoute {
|
|
|
2949
3149
|
}
|
|
2950
3150
|
/**
|
|
2951
3151
|
* One RXC (Pharmacy/Treatment Component Order) grouped under its parent RX*
|
|
2952
|
-
* segment (Phase D)
|
|
3152
|
+
* segment (Phase D): a component of a compound/IV. Surfaced STRUCTURALLY
|
|
2953
3153
|
* (the component list as authored), NOT pharmacologically resolved.
|
|
2954
3154
|
*
|
|
2955
3155
|
* @example
|
|
@@ -2959,7 +3159,7 @@ interface MedicationRoute {
|
|
|
2959
3159
|
* ```
|
|
2960
3160
|
*/
|
|
2961
3161
|
interface MedicationComponent {
|
|
2962
|
-
/** RXC-1 component type (e.g. "B"=base, "A"=additive
|
|
3162
|
+
/** RXC-1 component type (e.g. "B"=base, "A"=additive: HL7 Table 0166). */
|
|
2963
3163
|
readonly type?: string;
|
|
2964
3164
|
/** RXC-2 component code. */
|
|
2965
3165
|
readonly code?: CWE;
|
|
@@ -2977,15 +3177,15 @@ interface MedicationComponent {
|
|
|
2977
3177
|
* **Safety contract.** A wrong drug, strength, or route can harm a real
|
|
2978
3178
|
* patient, so this view is deliberately conservative:
|
|
2979
3179
|
* - `giveCode` carries its own coding-system provenance via the CWE
|
|
2980
|
-
* (`giveCode.nameOfCodingSystem
|
|
3180
|
+
* (`giveCode.nameOfCodingSystem`: e.g. `RXN` RxNorm, `NDC`). The helper
|
|
2981
3181
|
* surfaces the *claim*; it never validates or looks the code up.
|
|
2982
3182
|
* - `amount` (how much) and `strength` (concentration) are SEPARATE fields and
|
|
2983
|
-
* are never reconciled
|
|
3183
|
+
* are never reconciled: including against any strength a coded drug implies
|
|
2984
3184
|
* (Phase D §4). A disagreement is preserved for the consumer to see.
|
|
2985
|
-
* - Malformed RX* segments never throw
|
|
3185
|
+
* - Malformed RX* segments never throw: absent fields are omitted keys.
|
|
2986
3186
|
*
|
|
2987
3187
|
* `routes` and `components` are ALWAYS present (possibly empty). Phase M adds
|
|
2988
|
-
* `timings
|
|
3188
|
+
* `timings`: ALWAYS present, empty when no TQ1 / legacy embedded TQ (RXE-1)
|
|
2989
3189
|
* accompanies the medication; the repeat pattern is surfaced **verbatim, never
|
|
2990
3190
|
* normalized to a schedule**. Deferred (not v1): sig/frequency *interpretation*,
|
|
2991
3191
|
* dose-range or interaction checking, pharmacologic resolution of compounds.
|
|
@@ -3011,7 +3211,7 @@ interface Medication {
|
|
|
3011
3211
|
readonly giveCode?: CWE;
|
|
3012
3212
|
/** Give/dispense/administered amount (+ units). See {@link MedicationAmount}. */
|
|
3013
3213
|
readonly amount?: MedicationAmount;
|
|
3014
|
-
/** RXE-25/26 give strength
|
|
3214
|
+
/** RXE-25/26 give strength: ENCODED context only; never reconciled with `giveCode`. */
|
|
3015
3215
|
readonly strength?: MedicationStrength;
|
|
3016
3216
|
/** RXO-5 requested dosage form (order context). */
|
|
3017
3217
|
readonly dosageForm?: CWE;
|
|
@@ -3021,7 +3221,7 @@ interface Medication {
|
|
|
3021
3221
|
readonly components: readonly MedicationComponent[];
|
|
3022
3222
|
/**
|
|
3023
3223
|
* TQ1 / legacy embedded-TQ (RXE-1) timing(s) grouped under this medication
|
|
3024
|
-
* (Phase M). Always present
|
|
3224
|
+
* (Phase M). Always present: empty when the medication carries no timing.
|
|
3025
3225
|
* See {@link OrderTiming}.
|
|
3026
3226
|
*/
|
|
3027
3227
|
readonly timings: readonly OrderTiming[];
|
|
@@ -3067,11 +3267,11 @@ interface Insurance {
|
|
|
3067
3267
|
}
|
|
3068
3268
|
/**
|
|
3069
3269
|
* Whether an `Immunization` records a dose that was **administered** by the
|
|
3070
|
-
* reporting system or is **historical** information sourced from elsewhere
|
|
3270
|
+
* reporting system or is **historical** information sourced from elsewhere,
|
|
3071
3271
|
* derived conservatively from RXA-9.1 against HL7 Table NIP001 (Immunization
|
|
3072
3272
|
* Information Source, CDC v2.5.1 Immunization Messaging IG):
|
|
3073
|
-
* - `"administered"
|
|
3074
|
-
* - `"historical"
|
|
3273
|
+
* - `"administered"`: RXA-9.1 is exactly `"00"` (New immunization record).
|
|
3274
|
+
* - `"historical"`: RXA-9.1 is `"01".."08"` (any "Historical information …"
|
|
3075
3275
|
* source).
|
|
3076
3276
|
*
|
|
3077
3277
|
* **Fail-safe:** this is OMITTED (never guessed) when RXA-9 is absent or carries
|
|
@@ -3079,7 +3279,7 @@ interface Insurance {
|
|
|
3079
3279
|
* always preserved verbatim on `Immunization.informationSource`, so a consumer
|
|
3080
3280
|
* can inspect the original code even when `recordOrigin` is undefined. The
|
|
3081
3281
|
* distinction matters because an IIS de-duplicates a *historical* report
|
|
3082
|
-
* differently from a dose it believes was *administered
|
|
3282
|
+
* differently from a dose it believes was *administered*: guessing corrupts
|
|
3083
3283
|
* the registry.
|
|
3084
3284
|
*/
|
|
3085
3285
|
type ImmunizationRecordOrigin = "administered" | "historical";
|
|
@@ -3094,18 +3294,18 @@ type ImmunizationRecordOrigin = "administered" | "historical";
|
|
|
3094
3294
|
* a patient or corrupt an IIS (Immunization Information System) registry, so
|
|
3095
3295
|
* this view is deliberately conservative:
|
|
3096
3296
|
* - `vaccineCode` carries its own coding-system provenance via the CWE
|
|
3097
|
-
* (`vaccineCode.nameOfCodingSystem
|
|
3297
|
+
* (`vaccineCode.nameOfCodingSystem`: `CVX` HL7 Table 0292; live IIS feeds
|
|
3098
3298
|
* frequently *dual-code* RXA-5 with an alternate CVX/NDC in CWE.4-6, surfaced
|
|
3099
3299
|
* as `vaccineCode.alternateIdentifier`/`alternateText`/`nameOfAlternateCodingSystem`).
|
|
3100
3300
|
* The helper reports the *claim*; it never validates or looks the code up.
|
|
3101
3301
|
* - `actionCode` (RXA-21, `A`/`D`/`U`) is surfaced **verbatim** and never
|
|
3102
|
-
* defaulted
|
|
3302
|
+
* defaulted: mis-keying it corrupts a registry's add/delete/update dedup.
|
|
3103
3303
|
* - `doseAmount` is strict-`Number()` parsed; the IIS "unknown dose" sentinel
|
|
3104
3304
|
* `999` is surfaced **as the number `999`**, never specially coerced.
|
|
3105
3305
|
* - `recordOrigin` (administered vs historical) is derived only from the
|
|
3106
|
-
* well-known NIP001 RXA-9.1 codes and OMITTED otherwise
|
|
3306
|
+
* well-known NIP001 RXA-9.1 codes and OMITTED otherwise: see
|
|
3107
3307
|
* {@link ImmunizationRecordOrigin}.
|
|
3108
|
-
* - Malformed RXA segments never throw
|
|
3308
|
+
* - Malformed RXA segments never throw: absent fields are omitted keys.
|
|
3109
3309
|
*
|
|
3110
3310
|
* `routes` and `observations` are ALWAYS present (possibly empty). Deferred (not
|
|
3111
3311
|
* v1): IIS-specific state profile constraints; CVX/MVX validity checks; the 2nd+
|
|
@@ -3140,9 +3340,9 @@ interface Immunization {
|
|
|
3140
3340
|
readonly doseUnits?: CWE;
|
|
3141
3341
|
/**
|
|
3142
3342
|
* `true` iff RXA-7's coding system (CWE.3) is exactly `UCUM` (HL7 Table 0396)
|
|
3143
|
-
|
|
3343
|
+
*: i.e. the dose unit is declared UCUM and safe to interpret as computable.
|
|
3144
3344
|
* `false` means a unit IS present but is NOT declared UCUM (surfaced as-is,
|
|
3145
|
-
* never coerced). OMITTED when RXA-7 is absent. A *claim* check only
|
|
3345
|
+
* never coerced). OMITTED when RXA-7 is absent. A *claim* check only: UCUM
|
|
3146
3346
|
* grammar is not validated here.
|
|
3147
3347
|
*/
|
|
3148
3348
|
readonly doseUnitsAreUcum?: boolean;
|
|
@@ -3162,7 +3362,7 @@ interface Immunization {
|
|
|
3162
3362
|
readonly refusalReason?: CWE;
|
|
3163
3363
|
/** RXA-20 completion status (`CP`=complete, `RE`=refused, `NA`=not administered, `PA`=partially administered). */
|
|
3164
3364
|
readonly completionStatus?: string;
|
|
3165
|
-
/** RXA-21 action code (`A`=add, `D`=delete, `U`=update)
|
|
3365
|
+
/** RXA-21 action code (`A`=add, `D`=delete, `U`=update): preserved verbatim, NEVER defaulted. */
|
|
3166
3366
|
readonly actionCode?: string;
|
|
3167
3367
|
/** RXR children grouped under this RXA (Table 0162 route / Table 0163 site). Always present (possibly empty). */
|
|
3168
3368
|
readonly routes: readonly MedicationRoute[];
|
|
@@ -3170,7 +3370,7 @@ interface Immunization {
|
|
|
3170
3370
|
readonly observations: readonly Observation[];
|
|
3171
3371
|
}
|
|
3172
3372
|
/**
|
|
3173
|
-
* One appointment resource grouped under a `SCH` (Phase Q)
|
|
3373
|
+
* One appointment resource grouped under a `SCH` (Phase Q): an AIS (service),
|
|
3174
3374
|
* AIG (general resource), AIL (location), or AIP (personnel / provider) segment.
|
|
3175
3375
|
* The resource identifier lives at position 3 of every AI* segment; for the
|
|
3176
3376
|
* personnel resource (AIP) it is additionally surfaced as a typed `person`
|
|
@@ -3186,7 +3386,7 @@ interface AppointmentResource {
|
|
|
3186
3386
|
/** Which AI* segment sourced this resource: AIS→service, AIG→general, AIL→location, AIP→personnel. */
|
|
3187
3387
|
readonly kind: "service" | "general" | "location" | "personnel";
|
|
3188
3388
|
/**
|
|
3189
|
-
* The AI*-3 resource identifier surfaced as a coded element
|
|
3389
|
+
* The AI*-3 resource identifier surfaced as a coded element: `code.identifier`
|
|
3190
3390
|
* is the resource id (first component, verbatim). AIS-3 / AIG-3 are coded
|
|
3191
3391
|
* elements, so `code.text` / `code.nameOfCodingSystem` are meaningful there;
|
|
3192
3392
|
* AIL-3 is a **PL** (location) rather than a coded element, so only
|
|
@@ -3194,11 +3394,11 @@ interface AppointmentResource {
|
|
|
3194
3394
|
* fields are positional provenance, not a coding system. Provenance-only.
|
|
3195
3395
|
*/
|
|
3196
3396
|
readonly code?: CWE;
|
|
3197
|
-
/** AIP-3 personnel resource as a typed `XCN` (personnel resources only)
|
|
3397
|
+
/** AIP-3 personnel resource as a typed `XCN` (personnel resources only): the appointment provider. */
|
|
3198
3398
|
readonly person?: XCN;
|
|
3199
3399
|
}
|
|
3200
3400
|
/**
|
|
3201
|
-
* SCH-derived appointment entry (Phase Q
|
|
3401
|
+
* SCH-derived appointment entry (Phase Q: SIU scheduling breadth). Surfaces the
|
|
3202
3402
|
* appointment identifiers, filler status (SCH-25, Table 0278), SCH-11 start/end
|
|
3203
3403
|
* timing, and the AI* resource groups. NOT a scheduling-workflow state machine.
|
|
3204
3404
|
*
|
|
@@ -3217,19 +3417,19 @@ interface Appointment {
|
|
|
3217
3417
|
readonly placerAppointmentId?: string;
|
|
3218
3418
|
/** SCH-2 filler appointment ID (EI first component, verbatim). */
|
|
3219
3419
|
readonly fillerAppointmentId?: string;
|
|
3220
|
-
/** SCH-25 filler status code (HL7 Table 0278)
|
|
3420
|
+
/** SCH-25 filler status code (HL7 Table 0278): the appointment status, verbatim/provenance-only. */
|
|
3221
3421
|
readonly fillerStatusCode?: CWE;
|
|
3222
|
-
/** Appointment start date/time
|
|
3422
|
+
/** Appointment start date/time: SCH-11 TQ.4 (fidelity `TS`, Phase N). */
|
|
3223
3423
|
readonly startDateTime?: TS;
|
|
3224
|
-
/** Appointment end date/time
|
|
3424
|
+
/** Appointment end date/time: SCH-11 TQ.5 (fidelity `TS`, Phase N). */
|
|
3225
3425
|
readonly endDateTime?: TS;
|
|
3226
3426
|
/** AIS/AIG/AIL/AIP resources grouped under this SCH. Always present (possibly empty). */
|
|
3227
3427
|
readonly resources: readonly AppointmentResource[];
|
|
3228
3428
|
}
|
|
3229
3429
|
/**
|
|
3230
|
-
* TXA-derived clinical-document entry (Phase Q
|
|
3430
|
+
* TXA-derived clinical-document entry (Phase Q: MDM document breadth). The
|
|
3231
3431
|
* load-bearing safety property: **completion status (TXA-17) and availability
|
|
3232
|
-
* status (TXA-19) are DISTINCT fields and are never conflated
|
|
3432
|
+
* status (TXA-19) are DISTINCT fields and are never conflated**: a document can
|
|
3233
3433
|
* be *available* before it is *authenticated*, and reading a preliminary
|
|
3234
3434
|
* document as final is the clinical harm. Both are verbatim / provenance-only.
|
|
3235
3435
|
*
|
|
@@ -3238,8 +3438,8 @@ interface Appointment {
|
|
|
3238
3438
|
* import type { ClinicalDocument } from "@cosyte/hl7";
|
|
3239
3439
|
* const doc: ClinicalDocument = {
|
|
3240
3440
|
* documentType: "DS",
|
|
3241
|
-
* completionStatus: "IP", // in progress
|
|
3242
|
-
* availabilityStatus: "AV", // available
|
|
3441
|
+
* completionStatus: "IP", // in progress: NOT yet authenticated
|
|
3442
|
+
* availabilityStatus: "AV", // available: a different axis
|
|
3243
3443
|
* observations: [],
|
|
3244
3444
|
* };
|
|
3245
3445
|
* ```
|
|
@@ -3248,14 +3448,14 @@ interface ClinicalDocument {
|
|
|
3248
3448
|
/** TXA-2 document type (HL7 Table 0270), verbatim. */
|
|
3249
3449
|
readonly documentType?: string;
|
|
3250
3450
|
/**
|
|
3251
|
-
* TXA-17 document **completion** status (HL7 Table 0271
|
|
3451
|
+
* TXA-17 document **completion** status (HL7 Table 0271: e.g. `DO` documented,
|
|
3252
3452
|
* `IP` in progress, `AU` authenticated, `LA` legally authenticated, `IN`
|
|
3253
3453
|
* incomplete). Surfaced DISTINCT from {@link availabilityStatus}; verbatim,
|
|
3254
3454
|
* never validated, never merged.
|
|
3255
3455
|
*/
|
|
3256
3456
|
readonly completionStatus?: string;
|
|
3257
3457
|
/**
|
|
3258
|
-
* TXA-19 document **availability** status (HL7 Table 0273
|
|
3458
|
+
* TXA-19 document **availability** status (HL7 Table 0273: `AV` available,
|
|
3259
3459
|
* `CA` cancelled, `OB` obsolete, `UN` unavailable). Surfaced DISTINCT from
|
|
3260
3460
|
* {@link completionStatus}; verbatim, never validated, never merged.
|
|
3261
3461
|
*/
|
|
@@ -3264,14 +3464,14 @@ interface ClinicalDocument {
|
|
|
3264
3464
|
readonly activityDateTime?: TS;
|
|
3265
3465
|
/** TXA-12 unique document number (EI first component, verbatim). */
|
|
3266
3466
|
readonly uniqueDocumentNumber?: string;
|
|
3267
|
-
/** TXA-13 parent document number (EI first component)
|
|
3467
|
+
/** TXA-13 parent document number (EI first component): addendum / replacement link. */
|
|
3268
3468
|
readonly parentDocumentNumber?: string;
|
|
3269
3469
|
/** OBX narrative body grouped under this TXA. Always present (possibly empty). */
|
|
3270
3470
|
readonly observations: readonly Observation[];
|
|
3271
3471
|
}
|
|
3272
3472
|
/**
|
|
3273
|
-
* FT1-derived charge entry (Phase Q
|
|
3274
|
-
* fields surfaced with **no billing logic and no money-as-float
|
|
3473
|
+
* FT1-derived charge entry (Phase Q: DFT financial breadth). Billing-critical
|
|
3474
|
+
* fields surfaced with **no billing logic and no money-as-float**: the
|
|
3275
3475
|
* extended/unit amounts are the verbatim CP wire text, never parsed to a number.
|
|
3276
3476
|
*
|
|
3277
3477
|
* @example
|
|
@@ -3288,29 +3488,29 @@ interface ClinicalDocument {
|
|
|
3288
3488
|
interface Charge {
|
|
3289
3489
|
/** FT1-4 transaction date (fidelity `TS`, Phase N). */
|
|
3290
3490
|
readonly transactionDate?: TS;
|
|
3291
|
-
/** FT1-6 transaction type (HL7 Table 0017
|
|
3491
|
+
/** FT1-6 transaction type (HL7 Table 0017: `CG` charge, `CD` credit, `PY` payment, `AJ` adjustment). Verbatim. */
|
|
3292
3492
|
readonly transactionType?: string;
|
|
3293
|
-
/** FT1-7 transaction code
|
|
3493
|
+
/** FT1-7 transaction code: the institution charge/procedure code (CWE, provenance-only, never validated). */
|
|
3294
3494
|
readonly transactionCode?: CWE;
|
|
3295
3495
|
/** FT1-10 transaction quantity (NM; strict-parsed, never `NaN`). */
|
|
3296
3496
|
readonly quantity?: number;
|
|
3297
|
-
/** FT1-11 transaction amount, extended (CP)
|
|
3497
|
+
/** 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. */
|
|
3298
3498
|
readonly amountExtended?: string;
|
|
3299
|
-
/** FT1-12 transaction amount, unit (CP)
|
|
3499
|
+
/** FT1-12 transaction amount, unit (CP): canonical wire text; never parsed to a number. */
|
|
3300
3500
|
readonly amountUnit?: string;
|
|
3301
|
-
/** FT1-19 diagnosis code(s) linked to this charge (CE, repeating)
|
|
3501
|
+
/** FT1-19 diagnosis code(s) linked to this charge (CE, repeating): billing diagnosis linkage. Always present (possibly empty). */
|
|
3302
3502
|
readonly diagnoses: readonly CWE[];
|
|
3303
3503
|
}
|
|
3304
3504
|
|
|
3305
3505
|
/**
|
|
3306
|
-
* Message-type & structure awareness (roadmap Phase G)
|
|
3506
|
+
* Message-type & structure awareness (roadmap Phase G): a conservative
|
|
3307
3507
|
* **misroute / truncation safety net**, NOT an abstract-message-syntax
|
|
3308
3508
|
* validator.
|
|
3309
3509
|
*
|
|
3310
3510
|
* It answers one question per message: **"for this trigger event, are the
|
|
3311
3511
|
* core segment groups the spec marks Required actually present?"** When an
|
|
3312
3512
|
* expected group is entirely absent (e.g. an `ORU^R01` carrying no `OBR`/`OBX`
|
|
3313
|
-
* result group
|
|
3513
|
+
* result group: the classic signature of a truncated or misrouted feed), the
|
|
3314
3514
|
* parser emits a single additive Tier-2 `MISSING_EXPECTED_GROUP` warning. It
|
|
3315
3515
|
* never throws, never refuses, never rewrites the message.
|
|
3316
3516
|
*
|
|
@@ -3318,24 +3518,24 @@ interface Charge {
|
|
|
3318
3518
|
* false-positives):
|
|
3319
3519
|
*
|
|
3320
3520
|
* - **Per trigger event, never per family.** The claim that an ADT family
|
|
3321
|
-
* shares one shape is false
|
|
3521
|
+
* shares one shape is false: events diverge by version and trigger. Every
|
|
3322
3522
|
* definition keys on the (messageCode, triggerEvent) pair.
|
|
3323
3523
|
* - **Required (R) anchors only.** A group is modelled only when its anchor
|
|
3324
3524
|
* segment is genuinely Required by the v2.5.1 abstract syntax for that
|
|
3325
3525
|
* event. Optional groups (e.g. PID in ORU, RXA in VXU, OBR in OML) are
|
|
3326
3526
|
* deliberately excluded so a conformant-but-sparse message never warns.
|
|
3327
3527
|
* - **Presence is liberal.** A group counts as present if ANY of its anchor
|
|
3328
|
-
* segments appears
|
|
3528
|
+
* segments appears: the conservative direction (it suppresses a warning
|
|
3329
3529
|
* rather than inventing one).
|
|
3330
3530
|
* - **Unrecognized = silent.** A message whose type has no definition here
|
|
3331
3531
|
* produces an unrecognized structure summary and zero structural warnings.
|
|
3332
3532
|
*
|
|
3333
|
-
* Spec traceability: HL7 v2.5.1
|
|
3334
|
-
* (financial
|
|
3335
|
-
* (scheduling
|
|
3533
|
+
* Spec traceability: HL7 v2.5.1: Ch. 3 (ADT), Ch. 4 (orders), Ch. 6
|
|
3534
|
+
* (financial: DFT), Ch. 7 (observation: ORU), Ch. 9 (MDM), Ch. 10
|
|
3535
|
+
* (scheduling: SIU), Ch. 2 (ACK), CDC IG (VXU). Per-entry sourcing lives in
|
|
3336
3536
|
* `docs-content/spec-notes-structure.md`.
|
|
3337
3537
|
*
|
|
3338
|
-
* Zero runtime deps
|
|
3538
|
+
* Zero runtime deps: pure data + pure functions.
|
|
3339
3539
|
*/
|
|
3340
3540
|
/**
|
|
3341
3541
|
* One expected segment group for a recognized trigger event. The group is
|
|
@@ -3397,7 +3597,7 @@ interface StructureGroup {
|
|
|
3397
3597
|
readonly present: boolean;
|
|
3398
3598
|
}
|
|
3399
3599
|
/**
|
|
3400
|
-
* The structure summary for a parsed message
|
|
3600
|
+
* The structure summary for a parsed message: the data behind
|
|
3401
3601
|
* `Hl7Message.structure`. For an unrecognized type, `recognized` is `false`,
|
|
3402
3602
|
* `expectedGroups` is empty, and `missingGroups` is empty (the safety net is
|
|
3403
3603
|
* deliberately silent on types it does not model).
|
|
@@ -3418,7 +3618,7 @@ interface MessageStructure {
|
|
|
3418
3618
|
* Analyze a parsed message's structure against the conservative expected-group
|
|
3419
3619
|
* registry. Pure: it takes the message code, trigger event, and the set of
|
|
3420
3620
|
* segment names actually present, and returns a `MessageStructure` summary. It
|
|
3421
|
-
* decides nothing about whether to warn
|
|
3621
|
+
* decides nothing about whether to warn: the caller (parser) emits one
|
|
3422
3622
|
* `MISSING_EXPECTED_GROUP` warning per name in `missingGroups`.
|
|
3423
3623
|
*
|
|
3424
3624
|
* @param messageCode - MSH-9.1 (e.g. `"ORU"`); empty string if absent.
|
|
@@ -3436,7 +3636,7 @@ interface MessageStructure {
|
|
|
3436
3636
|
declare function analyzeMessageStructure(messageCode: string, triggerEvent: string, presentSegmentNames: ReadonlySet<string>): MessageStructure;
|
|
3437
3637
|
|
|
3438
3638
|
/**
|
|
3439
|
-
* `emitJson` + `SerializedMessage
|
|
3639
|
+
* `emitJson` + `SerializedMessage`: snapshot-stable JSON projection of an
|
|
3440
3640
|
* `Hl7Message`. Mirrors `rawSegments` one-for-one; preserves `isNull`;
|
|
3441
3641
|
* always includes `warnings: []`; omits `profile` when absent.
|
|
3442
3642
|
*
|
|
@@ -3449,15 +3649,15 @@ declare function analyzeMessageStructure(messageCode: string, triggerEvent: stri
|
|
|
3449
3649
|
* warnings + optional profile).
|
|
3450
3650
|
* - D-18: `Hl7Message.toJSON` delegates here so `JSON.stringify(msg)` Just Works.
|
|
3451
3651
|
* - D-19: warnings array is ALWAYS present (`warnings: []` when empty).
|
|
3452
|
-
* - D-20: `profile` field appears only when `msg.profile` is truthy
|
|
3652
|
+
* - D-20: `profile` field appears only when `msg.profile` is truthy: then
|
|
3453
3653
|
* contains only `name` + `lineage`.
|
|
3454
3654
|
* - D-21: `SerializedMessage` is exported from `src/index.ts` (not nested
|
|
3455
3655
|
* under HL7 namespace).
|
|
3456
3656
|
*
|
|
3457
3657
|
* Note on runtime freeze scope: `emitJson` returns an object that is
|
|
3458
|
-
* boundary-frozen (top level only
|
|
3658
|
+
* boundary-frozen (top level only: `Object.isFrozen(msg.toJSON()) === true`).
|
|
3459
3659
|
* Inner arrays are readonly at the TypeScript type level but MUTABLE at
|
|
3460
|
-
* runtime
|
|
3660
|
+
* runtime: consumers should treat the returned structure as immutable
|
|
3461
3661
|
* and not mutate nested arrays. Deep-freeze is explicitly rejected per
|
|
3462
3662
|
* D-30 (emit is hot-path; runtime deep-freeze would add cost with no
|
|
3463
3663
|
* observable benefit given the type-level readonly contract).
|
|
@@ -3469,7 +3669,7 @@ declare function analyzeMessageStructure(messageCode: string, triggerEvent: stri
|
|
|
3469
3669
|
*
|
|
3470
3670
|
* Runtime immutability: the top-level object is `Object.freeze`d
|
|
3471
3671
|
* (boundary-frozen). Inner arrays are readonly at the TypeScript type
|
|
3472
|
-
* level but mutable at runtime
|
|
3672
|
+
* level but mutable at runtime: treat as immutable, do not mutate.
|
|
3473
3673
|
*
|
|
3474
3674
|
* @example
|
|
3475
3675
|
* ```ts
|
|
@@ -3503,11 +3703,214 @@ interface SerializedMessage {
|
|
|
3503
3703
|
}
|
|
3504
3704
|
|
|
3505
3705
|
/**
|
|
3506
|
-
*
|
|
3706
|
+
* Typed-composite **encoders** for the `@cosyte/hl7` emit path (roadmap Phase
|
|
3707
|
+
* T): the conservative-emit mirror of the `src/model/types/*` read parsers.
|
|
3708
|
+
*
|
|
3709
|
+
* Each encoder takes one typed composite (an {@link XPN} name, a {@link CX}
|
|
3710
|
+
* identifier, a {@link TS} timestamp, …) and produces a `RawField` whose
|
|
3711
|
+
* positional structure matches exactly what the corresponding `parseXxx`
|
|
3712
|
+
* reads back. The load-bearing guarantees:
|
|
3713
|
+
*
|
|
3714
|
+
* - **No delimiter injection.** Component values are stored **decoded** (the
|
|
3715
|
+
* same surface `parseXxx` returns), and the serializer (`emit-field.ts`)
|
|
3716
|
+
* runs every subcomponent through `reescape` (the HL7-R encode-safe codec)
|
|
3717
|
+
* on the way out. A `familyName` of `"Smith^Jr"` is therefore emitted as
|
|
3718
|
+
* `Smith\S\Jr` (one component) and re-parses to the exact string `"Smith^Jr"`,
|
|
3719
|
+
* never forging a component boundary. The caller never hand-assembles
|
|
3720
|
+
* `^`/`&`/`~`.
|
|
3721
|
+
* - **`emit ∘ parse` identity on the modelled fields.** Interior empty
|
|
3722
|
+
* components are preserved (so component positions stay aligned) and only
|
|
3723
|
+
* trailing empties are trimmed (matching the serializer's D-02 strip and the
|
|
3724
|
+
* parser's omit-on-absent), so `parseXxx(encodeXxx(v)) ` reproduces `v` on
|
|
3725
|
+
* every modelled field.
|
|
3726
|
+
* - **Never fabricate.** An omitted optional field encodes to an empty /
|
|
3727
|
+
* absent component: never a defaulted value. An all-empty composite encodes
|
|
3728
|
+
* to an absent field (`repetitions: []`).
|
|
3729
|
+
*
|
|
3730
|
+
* Zero runtime deps: pure functions over the raw positional tree.
|
|
3731
|
+
*/
|
|
3732
|
+
|
|
3733
|
+
/**
|
|
3734
|
+
* The 11 typed composite kinds this module can encode. Mirrors the read-side
|
|
3735
|
+
* composite set (`XPN`, `XAD`, `CX`, `CWE`, `CE`, `XTN`, `PL`, `TS`, `NM`,
|
|
3736
|
+
* `HD`, `XCN`).
|
|
3737
|
+
*/
|
|
3738
|
+
type CompositeKind = "XPN" | "XAD" | "CX" | "CWE" | "CE" | "XTN" | "PL" | "TS" | "NM" | "HD" | "XCN";
|
|
3739
|
+
/**
|
|
3740
|
+
* Maps each {@link CompositeKind} to the typed value {@link encodeComposite}
|
|
3741
|
+
* (and `setComposite`) accept for it. `TS` also accepts a pre-formatted HL7
|
|
3742
|
+
* timestamp string; `NM` also accepts a `number` or a raw numeric string,
|
|
3743
|
+
* both are emitted verbatim (the serializer never re-formats a claimed value).
|
|
3744
|
+
*/
|
|
3745
|
+
interface CompositeValueByKind {
|
|
3746
|
+
readonly XPN: XPN;
|
|
3747
|
+
readonly XAD: XAD;
|
|
3748
|
+
readonly CX: CX;
|
|
3749
|
+
readonly CWE: CWE;
|
|
3750
|
+
readonly CE: CE;
|
|
3751
|
+
readonly XTN: XTN;
|
|
3752
|
+
readonly PL: PL;
|
|
3753
|
+
readonly TS: TS | string;
|
|
3754
|
+
readonly NM: NM | number | string;
|
|
3755
|
+
readonly HD: HD;
|
|
3756
|
+
readonly XCN: XCN;
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Encode an XPN (14 components) to a spec-clean field.
|
|
3760
|
+
*
|
|
3761
|
+
* @example
|
|
3762
|
+
* ```ts
|
|
3763
|
+
* import { encodeXpn } from "@cosyte/hl7";
|
|
3764
|
+
* encodeXpn({ familyName: "Doe", givenName: "Jane", prefix: "Dr" });
|
|
3765
|
+
* ```
|
|
3766
|
+
*/
|
|
3767
|
+
declare function encodeXpn(v: XPN): RawField;
|
|
3768
|
+
/**
|
|
3769
|
+
* Encode an XAD (12 components) to a spec-clean field.
|
|
3770
|
+
*
|
|
3771
|
+
* @example
|
|
3772
|
+
* ```ts
|
|
3773
|
+
* import { encodeXad } from "@cosyte/hl7";
|
|
3774
|
+
* encodeXad({ street: "123 Main St", city: "Boston", stateOrProvince: "MA" });
|
|
3775
|
+
* ```
|
|
3776
|
+
*/
|
|
3777
|
+
declare function encodeXad(v: XAD): RawField;
|
|
3778
|
+
/**
|
|
3779
|
+
* Encode a CX (identifier): component 4 is a nested HD (assigningAuthority).
|
|
3780
|
+
*
|
|
3781
|
+
* @example
|
|
3782
|
+
* ```ts
|
|
3783
|
+
* import { encodeCx } from "@cosyte/hl7";
|
|
3784
|
+
* encodeCx({ idNumber: "MRN001", assigningAuthority: { namespaceId: "HOSP" }, identifierTypeCode: "MR" });
|
|
3785
|
+
* ```
|
|
3786
|
+
*/
|
|
3787
|
+
declare function encodeCx(v: CX): RawField;
|
|
3788
|
+
/**
|
|
3789
|
+
* Encode a CWE (coded element, 9 modelled + preserved `extraComponents`).
|
|
3790
|
+
*
|
|
3791
|
+
* @example
|
|
3792
|
+
* ```ts
|
|
3793
|
+
* import { encodeCwe } from "@cosyte/hl7";
|
|
3794
|
+
* encodeCwe({ identifier: "GLU", text: "Glucose", nameOfCodingSystem: "LN" });
|
|
3795
|
+
* ```
|
|
3796
|
+
*/
|
|
3797
|
+
declare function encodeCwe(v: CWE): RawField;
|
|
3798
|
+
/**
|
|
3799
|
+
* Encode a CE (coded element, 6 modelled + preserved `extraComponents`).
|
|
3800
|
+
*
|
|
3801
|
+
* @example
|
|
3802
|
+
* ```ts
|
|
3803
|
+
* import { encodeCe } from "@cosyte/hl7";
|
|
3804
|
+
* encodeCe({ identifier: "GLU", text: "Glucose", nameOfCodingSystem: "L" });
|
|
3805
|
+
* ```
|
|
3806
|
+
*/
|
|
3807
|
+
declare function encodeCe(v: CE): RawField;
|
|
3808
|
+
/**
|
|
3809
|
+
* Encode an XTN (telecom, 12 components) to a spec-clean field.
|
|
3810
|
+
*
|
|
3811
|
+
* @example
|
|
3812
|
+
* ```ts
|
|
3813
|
+
* import { encodeXtn } from "@cosyte/hl7";
|
|
3814
|
+
* encodeXtn({ telephoneNumber: "555-1234", telecommunicationUseCode: "PRN" });
|
|
3815
|
+
* ```
|
|
3816
|
+
*/
|
|
3817
|
+
declare function encodeXtn(v: XTN): RawField;
|
|
3818
|
+
/**
|
|
3819
|
+
* Encode a PL (person location): component 4 is a nested HD (facility).
|
|
3820
|
+
*
|
|
3821
|
+
* @example
|
|
3822
|
+
* ```ts
|
|
3823
|
+
* import { encodePl } from "@cosyte/hl7";
|
|
3824
|
+
* encodePl({ pointOfCare: "ICU", room: "101", bed: "A" });
|
|
3825
|
+
* ```
|
|
3826
|
+
*/
|
|
3827
|
+
declare function encodePl(v: PL): RawField;
|
|
3828
|
+
/**
|
|
3829
|
+
* Encode an HD (hierarchic designator, 3 components) to a spec-clean field.
|
|
3830
|
+
*
|
|
3831
|
+
* @example
|
|
3832
|
+
* ```ts
|
|
3833
|
+
* import { encodeHd } from "@cosyte/hl7";
|
|
3834
|
+
* encodeHd({ namespaceId: "EPIC", universalId: "1.2.840", universalIdType: "ISO" });
|
|
3835
|
+
* ```
|
|
3836
|
+
*/
|
|
3837
|
+
declare function encodeHd(v: HD): RawField;
|
|
3838
|
+
/**
|
|
3839
|
+
* Encode a TS/DTM timestamp. Accepts either the typed {@link TS} (its `raw`
|
|
3840
|
+
* string is emitted verbatim: the serializer never re-derives a timestamp
|
|
3841
|
+
* from parts) or a pre-formatted HL7 timestamp string.
|
|
3842
|
+
*
|
|
3843
|
+
* @example
|
|
3844
|
+
* ```ts
|
|
3845
|
+
* import { encodeTs } from "@cosyte/hl7";
|
|
3846
|
+
* encodeTs("20260721101500");
|
|
3847
|
+
* ```
|
|
3848
|
+
*/
|
|
3849
|
+
declare function encodeTs(v: TS | string): RawField;
|
|
3850
|
+
/**
|
|
3851
|
+
* Encode an NM numeric. Accepts the typed {@link NM} (its `raw` string emitted
|
|
3852
|
+
* verbatim, preserving the sender's precision/formatting), a `number`, or a raw
|
|
3853
|
+
* string. A `number` is stringified with `String(n)`; the value is never
|
|
3854
|
+
* reconciled or rounded.
|
|
3855
|
+
*
|
|
3856
|
+
* @example
|
|
3857
|
+
* ```ts
|
|
3858
|
+
* import { encodeNm } from "@cosyte/hl7";
|
|
3859
|
+
* encodeNm("120.50"); // precision preserved verbatim
|
|
3860
|
+
* ```
|
|
3861
|
+
*/
|
|
3862
|
+
declare function encodeNm(v: NM | number | string): RawField;
|
|
3863
|
+
/**
|
|
3864
|
+
* Encode an XCN: component 9 is a nested HD (assigningAuthority).
|
|
3865
|
+
*
|
|
3866
|
+
* @example
|
|
3867
|
+
* ```ts
|
|
3868
|
+
* import { encodeXcn } from "@cosyte/hl7";
|
|
3869
|
+
* encodeXcn({ idNumber: "1234567890", familyName: "Welby", identifierTypeCode: "NPI" });
|
|
3870
|
+
* ```
|
|
3871
|
+
*/
|
|
3872
|
+
declare function encodeXcn(v: XCN): RawField;
|
|
3873
|
+
/**
|
|
3874
|
+
* Encode any typed composite into a spec-clean `RawField` by its
|
|
3875
|
+
* {@link CompositeKind}. The single dispatcher `setComposite` and the typed
|
|
3876
|
+
* builders (`buildAdt`/`buildOru`) route through.
|
|
3877
|
+
*
|
|
3878
|
+
* Encoding takes **no** encoding-characters argument on purpose: the field it
|
|
3879
|
+
* produces carries the **decoded** component values, and the actual
|
|
3880
|
+
* delimiter-escaping happens later in the serializer against the message's own
|
|
3881
|
+
* encoding characters: so a composite is delimiter-independent to encode.
|
|
3882
|
+
*
|
|
3883
|
+
* @example
|
|
3884
|
+
* ```ts
|
|
3885
|
+
* import { encodeComposite } from "@cosyte/hl7";
|
|
3886
|
+
* // A hostile family name cannot break framing on emit:
|
|
3887
|
+
* const f = encodeComposite("XPN", { familyName: "Smith^Jr", givenName: "Ann" });
|
|
3888
|
+
* ```
|
|
3889
|
+
*/
|
|
3890
|
+
declare function encodeComposite<K extends CompositeKind>(kind: K, value: CompositeValueByKind[K]): RawField;
|
|
3891
|
+
/**
|
|
3892
|
+
* Encode an array of typed composites into a single repeating `RawField`: one
|
|
3893
|
+
* HL7 repetition (`~`-joined on emit) per array element. Used for repeating
|
|
3894
|
+
* fields such as PID-3 (patient identifier list) and PID-11 (addresses). An
|
|
3895
|
+
* empty array yields an absent field.
|
|
3896
|
+
*
|
|
3897
|
+
* @example
|
|
3898
|
+
* ```ts
|
|
3899
|
+
* import { encodeCompositeReps } from "@cosyte/hl7";
|
|
3900
|
+
* const ids = encodeCompositeReps("CX", [
|
|
3901
|
+
* { idNumber: "MRN001", identifierTypeCode: "MR" },
|
|
3902
|
+
* { idNumber: "9990", identifierTypeCode: "SS" },
|
|
3903
|
+
* ]);
|
|
3904
|
+
* ```
|
|
3905
|
+
*/
|
|
3906
|
+
declare function encodeCompositeReps<K extends CompositeKind>(kind: K, values: readonly CompositeValueByKind[K][]): RawField;
|
|
3907
|
+
|
|
3908
|
+
/**
|
|
3909
|
+
* `Hl7Message`: the immutable parsed-message model produced by `parseHL7`.
|
|
3507
3910
|
* Phase 2 shipped the read-only shell; Phase 3 extends the same class with
|
|
3508
3911
|
* the read-path traversal methods (`get`, `getAll`, `segments`,
|
|
3509
3912
|
* `allSegments`) on top of lazy, referentially stable Segment/Field caches
|
|
3510
|
-
* (D-11/D-12). The constructor surface is unchanged (Phase 2 D-05 lock)
|
|
3913
|
+
* (D-11/D-12). The constructor surface is unchanged (Phase 2 D-05 lock),
|
|
3511
3914
|
* `init.segments` is still the parser-side key, but the public raw-tree
|
|
3512
3915
|
* field is now exposed as `rawSegments` to free up the name `segments` for
|
|
3513
3916
|
* the typed `segments(type)` method.
|
|
@@ -3521,7 +3924,7 @@ interface SerializedMessage {
|
|
|
3521
3924
|
*
|
|
3522
3925
|
* @remarks
|
|
3523
3926
|
* With `exactOptionalPropertyTypes: true`, callers cannot pass
|
|
3524
|
-
* `{ profile: undefined }
|
|
3927
|
+
* `{ profile: undefined }`: either omit the key or pass a real profile
|
|
3525
3928
|
* descriptor. This matches the `Hl7Message.profile` public field shape
|
|
3526
3929
|
* (`... | undefined`).
|
|
3527
3930
|
*
|
|
@@ -3547,7 +3950,7 @@ interface Hl7MessageInit {
|
|
|
3547
3950
|
*/
|
|
3548
3951
|
readonly customSegments?: Readonly<Record<string, CustomSegmentDefinition>>;
|
|
3549
3952
|
/**
|
|
3550
|
-
* Merged `dateFormats` list
|
|
3953
|
+
* Merged `dateFormats` list: `options.dateFormats ++ profile.dateFormats`
|
|
3551
3954
|
* deduped first-occurrence per D-21. Consumed by `msg.meta.timestamp` and
|
|
3552
3955
|
* any future helper that calls `parseDtmCascade` directly. Absent when
|
|
3553
3956
|
* neither `options.dateFormats` nor `profile.dateFormats` was supplied.
|
|
@@ -3557,7 +3960,7 @@ interface Hl7MessageInit {
|
|
|
3557
3960
|
/**
|
|
3558
3961
|
* Parsed HL7 v2 message. Produced by `parseHL7`. Exposes the raw positional
|
|
3559
3962
|
* tree (`rawSegments`), delimiter metadata, warnings, and a typed traversal
|
|
3560
|
-
* surface
|
|
3963
|
+
* surface: `get(path)` for dot-paths, `getAll(type)` / `segments(type)` /
|
|
3561
3964
|
* `allSegments()` for wrapper-level iteration.
|
|
3562
3965
|
*
|
|
3563
3966
|
* @remarks
|
|
@@ -3582,7 +3985,7 @@ declare class Hl7Message {
|
|
|
3582
3985
|
/**
|
|
3583
3986
|
* Raw positional tree produced by the parser. 1-indexed per HL7 convention
|
|
3584
3987
|
* (`fields[0]` is the segment-name / MSH separator placeholder slot). Use
|
|
3585
|
-
* `segments(type)` / `allSegments()` for typed wrapper access
|
|
3988
|
+
* `segments(type)` / `allSegments()` for typed wrapper access: this field
|
|
3586
3989
|
* is exposed for advanced callers that need the raw tree directly (e.g.
|
|
3587
3990
|
* future serialization phase 5).
|
|
3588
3991
|
*/
|
|
@@ -3595,7 +3998,7 @@ declare class Hl7Message {
|
|
|
3595
3998
|
readonly lineage: readonly string[];
|
|
3596
3999
|
} | undefined;
|
|
3597
4000
|
/**
|
|
3598
|
-
* Merged `dateFormats` list
|
|
4001
|
+
* Merged `dateFormats` list: `options.dateFormats ++ profile.dateFormats`
|
|
3599
4002
|
* deduped first-occurrence per D-21. Empty array when neither source
|
|
3600
4003
|
* supplied any formats. Exposed publicly so helpers (`msg.meta.timestamp`)
|
|
3601
4004
|
* and advanced callers can introspect the active cascade.
|
|
@@ -3625,14 +4028,14 @@ declare class Hl7Message {
|
|
|
3625
4028
|
private _allSegments;
|
|
3626
4029
|
/**
|
|
3627
4030
|
* Lazily built `Meta` view (Phase 4 D-02 memoization). Dropped wholesale
|
|
3628
|
-
* by `invalidateCaches`. `Meta` is always defined (D-03
|
|
4031
|
+
* by `invalidateCaches`. `Meta` is always defined (D-03: MSH absence
|
|
3629
4032
|
* throws `NO_MSH_SEGMENT` at parse time) so no null-sentinel is needed.
|
|
3630
4033
|
* @internal
|
|
3631
4034
|
*/
|
|
3632
4035
|
private _meta;
|
|
3633
4036
|
/**
|
|
3634
4037
|
* Lazily built `MessageStructure` view (Phase G). Dropped wholesale by
|
|
3635
|
-
* `invalidateCaches`. Always defined
|
|
4038
|
+
* `invalidateCaches`. Always defined: `analyzeMessageStructure` returns an
|
|
3636
4039
|
* `recognized: false` summary for unmodelled types rather than absence.
|
|
3637
4040
|
* @internal
|
|
3638
4041
|
*/
|
|
@@ -3661,9 +4064,9 @@ declare class Hl7Message {
|
|
|
3661
4064
|
constructor(init: Hl7MessageInit);
|
|
3662
4065
|
/**
|
|
3663
4066
|
* Resolve a dot-path (e.g. `PID.5.1`, `OBX[2].5`, `PID.3[0].1`) to its
|
|
3664
|
-
* decoded leaf string (unescaped once at parse
|
|
4067
|
+
* decoded leaf string (unescaped once at parse: never re-unescaped on read,
|
|
3665
4068
|
* HL7-VALUE-REDECODE). Returns `undefined` when the path doesn't
|
|
3666
|
-
* resolve
|
|
4069
|
+
* resolve: never throws on missing path (MODEL-05). Throws `TypeError`
|
|
3667
4070
|
* on malformed path syntax (e.g. `"pid.5"`, empty string).
|
|
3668
4071
|
*
|
|
3669
4072
|
* @example
|
|
@@ -3672,14 +4075,14 @@ declare class Hl7Message {
|
|
|
3672
4075
|
* msg.get("PID.5.1"); // "Smith"
|
|
3673
4076
|
* msg.get("OBX[2].5"); // third OBX's 5th field
|
|
3674
4077
|
* msg.get("NOT.9.9"); // undefined
|
|
3675
|
-
* msg.get("MSH.12"); // "2.5"
|
|
4078
|
+
* msg.get("MSH.12"); // "2.5": HL7 version string
|
|
3676
4079
|
* ```
|
|
3677
4080
|
*/
|
|
3678
4081
|
get(path: string): string | undefined;
|
|
3679
4082
|
/**
|
|
3680
4083
|
* Return every `Segment` of `segmentType` in document order. Returns `[]`
|
|
3681
4084
|
* (empty array, NEVER `undefined`) when no segment of that type exists
|
|
3682
|
-
* (MODEL-02). Alias for `segments(segmentType)
|
|
4085
|
+
* (MODEL-02). Alias for `segments(segmentType)`: shares the same cache.
|
|
3683
4086
|
*
|
|
3684
4087
|
* @example
|
|
3685
4088
|
* ```ts
|
|
@@ -3718,7 +4121,7 @@ declare class Hl7Message {
|
|
|
3718
4121
|
allSegments(): readonly Segment[];
|
|
3719
4122
|
/**
|
|
3720
4123
|
* MSH-derived message metadata (type, controlId, timestamp, version, etc.).
|
|
3721
|
-
* D-01: plain object. D-02: memoized
|
|
4124
|
+
* D-01: plain object. D-02: memoized: `msg.meta === msg.meta` across
|
|
3722
4125
|
* reads until mutation invalidates. D-03: always defined (MSH absence
|
|
3723
4126
|
* throws `NO_MSH_SEGMENT` at parse time).
|
|
3724
4127
|
*
|
|
@@ -3732,10 +4135,10 @@ declare class Hl7Message {
|
|
|
3732
4135
|
get meta(): Meta;
|
|
3733
4136
|
/**
|
|
3734
4137
|
* Structural-conformance summary for the common message types (roadmap
|
|
3735
|
-
* Phase G)
|
|
4138
|
+
* Phase G): a misroute/truncation safety net, NOT a conformance validator.
|
|
3736
4139
|
* Reports, per the message's recognized (MSH-9.1, MSH-9.2) type, which
|
|
3737
4140
|
* Required segment groups are present and which are entirely absent
|
|
3738
|
-
* (`missingGroups
|
|
4141
|
+
* (`missingGroups`: the same set the parser flags as
|
|
3739
4142
|
* `MISSING_EXPECTED_GROUP` warnings). For an unmodelled type, `recognized`
|
|
3740
4143
|
* is `false` and `missingGroups` is empty. D-02: memoized.
|
|
3741
4144
|
*
|
|
@@ -3748,14 +4151,14 @@ declare class Hl7Message {
|
|
|
3748
4151
|
get structure(): MessageStructure;
|
|
3749
4152
|
/**
|
|
3750
4153
|
* PID-derived patient view, or `undefined` when no PID segment exists
|
|
3751
|
-
* (D-04). D-02: memoized. HELPERS-07: never throws
|
|
4154
|
+
* (D-04). D-02: memoized. HELPERS-07: never throws: absent fields
|
|
3752
4155
|
* surface as `undefined` on the returned `Patient` object.
|
|
3753
4156
|
*
|
|
3754
4157
|
* @example
|
|
3755
4158
|
* ```ts
|
|
3756
4159
|
* console.log(msg.patient?.mrn);
|
|
3757
4160
|
* console.log(msg.patient?.fullName);
|
|
3758
|
-
* console.log(msg.patient?.dateOfBirth?.raw); // fidelity TS
|
|
4161
|
+
* console.log(msg.patient?.dateOfBirth?.raw); // fidelity TS: e.g. "19800115"
|
|
3759
4162
|
* ```
|
|
3760
4163
|
*/
|
|
3761
4164
|
get patient(): Patient | undefined;
|
|
@@ -3773,7 +4176,7 @@ declare class Hl7Message {
|
|
|
3773
4176
|
get visit(): Visit | undefined;
|
|
3774
4177
|
/**
|
|
3775
4178
|
* Every OBX segment as a typed Observation in document order. D-05:
|
|
3776
|
-
* returns `[]` when no OBX present. D-06: NOT memoized
|
|
4179
|
+
* returns `[]` when no OBX present. D-06: NOT memoized: each call
|
|
3777
4180
|
* re-walks `rawSegments`. Value type is discriminated per D-13.
|
|
3778
4181
|
*
|
|
3779
4182
|
* @example
|
|
@@ -3786,7 +4189,7 @@ declare class Hl7Message {
|
|
|
3786
4189
|
observations(): readonly Observation[];
|
|
3787
4190
|
/**
|
|
3788
4191
|
* Every OBR as an Order with its OBX children grouped positionally (D-12) and
|
|
3789
|
-
* its TQ1 / legacy embedded-TQ (ORC-7) `timings` (Phase M
|
|
4192
|
+
* its TQ1 / legacy embedded-TQ (ORC-7) `timings` (Phase M: the repeat pattern
|
|
3790
4193
|
* surfaced verbatim, never resolved to a schedule). D-05: returns `[]` when no
|
|
3791
4194
|
* OBR present. D-06: not memoized.
|
|
3792
4195
|
*
|
|
@@ -3794,17 +4197,17 @@ declare class Hl7Message {
|
|
|
3794
4197
|
* ```ts
|
|
3795
4198
|
* for (const order of msg.orders()) {
|
|
3796
4199
|
* console.log(order.placerOrderNumber, order.observations.length);
|
|
3797
|
-
* for (const t of order.timings) console.log(t.repeatPattern?.code); // e.g. "Q6H"
|
|
4200
|
+
* for (const t of order.timings) console.log(t.repeatPattern?.code); // e.g. "Q6H": verbatim
|
|
3798
4201
|
* }
|
|
3799
4202
|
* ```
|
|
3800
4203
|
*/
|
|
3801
4204
|
orders(): readonly Order[];
|
|
3802
4205
|
/**
|
|
3803
|
-
* Message-level NTE notes (Phase P)
|
|
4206
|
+
* Message-level NTE notes (Phase P): every NTE segment with no recognized
|
|
3804
4207
|
* preceding parent (not immediately following a `PID`, `ORC`, `OBR`, or
|
|
3805
4208
|
* `OBX`), surfaced verbatim in document order so nothing is dropped. Notes
|
|
3806
4209
|
* that DO attach to a specific patient / order / result are exposed on those
|
|
3807
|
-
* helper outputs (`msg.patient?.notes`, `order.notes`, `observation.notes`)
|
|
4210
|
+
* helper outputs (`msg.patient?.notes`, `order.notes`, `observation.notes`),
|
|
3808
4211
|
* not here. D-05: returns `[]` when there are none. D-06: NOT memoized.
|
|
3809
4212
|
*
|
|
3810
4213
|
* @example
|
|
@@ -3815,7 +4218,7 @@ declare class Hl7Message {
|
|
|
3815
4218
|
notes(): readonly string[];
|
|
3816
4219
|
/**
|
|
3817
4220
|
* Every recognized ADT patient-identity event (merge / move / link /
|
|
3818
|
-
* unlink / person add/update
|
|
4221
|
+
* unlink / person add/update: roadmap Phase K), with the MRG-sourced
|
|
3819
4222
|
* `prior` and PID/PV1-sourced `surviving` parties labelled by role and the
|
|
3820
4223
|
* spec-constant `direction: "MRG_TO_PID"` on merge/move events. Returns
|
|
3821
4224
|
* `[]` when the trigger event is not in the identity family. D-06: not
|
|
@@ -3840,7 +4243,7 @@ declare class Hl7Message {
|
|
|
3840
4243
|
* and never reconciled (Phase D §4).
|
|
3841
4244
|
*
|
|
3842
4245
|
* Each medication also carries its TQ1 / legacy embedded-TQ (RXE-1) `timings`
|
|
3843
|
-
* (Phase M
|
|
4246
|
+
* (Phase M: repeat pattern verbatim, never resolved to a schedule).
|
|
3844
4247
|
*
|
|
3845
4248
|
* @example
|
|
3846
4249
|
* ```ts
|
|
@@ -3859,7 +4262,7 @@ declare class Hl7Message {
|
|
|
3859
4262
|
* returns `[]` when no RXA present. D-06: not memoized. The vaccine code
|
|
3860
4263
|
* carries its own provenance; the action code (RXA-21) is surfaced verbatim
|
|
3861
4264
|
* and `recordOrigin` (administered vs historical) is derived only from the
|
|
3862
|
-
* well-known NIP001 RXA-9.1 codes
|
|
4265
|
+
* well-known NIP001 RXA-9.1 codes: never guessed.
|
|
3863
4266
|
*
|
|
3864
4267
|
* @example
|
|
3865
4268
|
* ```ts
|
|
@@ -3919,7 +4322,7 @@ declare class Hl7Message {
|
|
|
3919
4322
|
* status (Table 0278), SCH-11 start/end timing, and the resource groups
|
|
3920
4323
|
* (service / general / location / personnel). D-05: returns `[]` when no SCH
|
|
3921
4324
|
* is present. D-06: not memoized. Never throws (HELPERS-07). Not a
|
|
3922
|
-
* scheduling-workflow state machine
|
|
4325
|
+
* scheduling-workflow state machine: see the package known-limitations.
|
|
3923
4326
|
*
|
|
3924
4327
|
* @example
|
|
3925
4328
|
* ```ts
|
|
@@ -3934,7 +4337,7 @@ declare class Hl7Message {
|
|
|
3934
4337
|
* Every TXA of an MDM message as a typed `ClinicalDocument` (roadmap Phase Q),
|
|
3935
4338
|
* with the OBX narrative body grouped positionally under that TXA. The
|
|
3936
4339
|
* completion status (TXA-17) and availability status (TXA-19) are surfaced as
|
|
3937
|
-
* **distinct** fields and never conflated
|
|
4340
|
+
* **distinct** fields and never conflated: a document can be available before
|
|
3938
4341
|
* it is authenticated, and reading a preliminary document as final is the
|
|
3939
4342
|
* harm. D-05: returns `[]` when no TXA is present. D-06: not memoized. Never
|
|
3940
4343
|
* throws (HELPERS-07).
|
|
@@ -3952,7 +4355,7 @@ declare class Hl7Message {
|
|
|
3952
4355
|
* Every FT1 of a DFT message as a typed `Charge` (roadmap Phase Q), one per
|
|
3953
4356
|
* FT1 in document order. Surfaces billing-critical fields (FT1-6 transaction
|
|
3954
4357
|
* type, FT1-7 code, FT1-11/12 extended/unit amount, FT1-19 diagnosis linkage)
|
|
3955
|
-
* with **no billing logic and no money-as-float
|
|
4358
|
+
* with **no billing logic and no money-as-float**: amounts are the verbatim
|
|
3956
4359
|
* CP wire text. D-05: returns `[]` when no FT1 is present. D-06: not memoized.
|
|
3957
4360
|
* Never throws (HELPERS-07).
|
|
3958
4361
|
*
|
|
@@ -3971,7 +4374,7 @@ declare class Hl7Message {
|
|
|
3971
4374
|
* D-05; MSH-1 and MSH-2 are inlined verbatim from
|
|
3972
4375
|
* `this.encodingCharacters` per D-06; every field string passes through
|
|
3973
4376
|
* `reescape` per D-04. `RawField.isNull === true` is preserved as the
|
|
3974
|
-
* HL7 literal `""` (D-02). Pure
|
|
4377
|
+
* HL7 literal `""` (D-02). Pure: never warns, never throws (D-07).
|
|
3975
4378
|
*
|
|
3976
4379
|
* @example
|
|
3977
4380
|
* ```ts
|
|
@@ -3987,7 +4390,7 @@ declare class Hl7Message {
|
|
|
3987
4390
|
* Re-walks `rawSegments` on every call (D-30 no caching). Mirrors the
|
|
3988
4391
|
* raw tree one-for-one, preserves `isNull`, always includes
|
|
3989
4392
|
* `warnings: []`, and includes `profile: { name, lineage }` only when
|
|
3990
|
-
* `this.profile` is truthy (D-19/D-20). Pure
|
|
4393
|
+
* `this.profile` is truthy (D-19/D-20). Pure: never warns, never throws.
|
|
3991
4394
|
*
|
|
3992
4395
|
* @example
|
|
3993
4396
|
* ```ts
|
|
@@ -4004,16 +4407,16 @@ declare class Hl7Message {
|
|
|
4004
4407
|
* debugging (SER-04). Single opinionated format (D-22 no options):
|
|
4005
4408
|
* header line with type / controlId / timestamp / segment count, then
|
|
4006
4409
|
* one line per segment with labeled `[N]=value` fields (D-23). Composite
|
|
4007
|
-
* values render as their raw HL7 string
|
|
4008
|
-
* (D-24). Pure
|
|
4410
|
+
* values render as their raw HL7 string: depth stops at field level
|
|
4411
|
+
* (D-24). Pure: never warns, never throws (D-26).
|
|
4009
4412
|
*
|
|
4010
4413
|
* **Field values render as their raw HL7 string representation.**
|
|
4011
|
-
* Embedded delimiters in user data appear as escape sequences
|
|
4414
|
+
* Embedded delimiters in user data appear as escape sequences: e.g.
|
|
4012
4415
|
* a patient family name containing `|` renders as `Smith\F\Jones`
|
|
4013
4416
|
* (NOT `Smith|Jones`). This preserves round-trip fidelity: copy-pasting
|
|
4014
4417
|
* prettyPrint output into `parseHL7` yields a structurally equivalent
|
|
4015
4418
|
* message. For un-escaped human display, parse the composite first via
|
|
4016
|
-
* typed accessors (e.g. `msg.patient?.familyName`)
|
|
4419
|
+
* typed accessors (e.g. `msg.patient?.familyName`): those return
|
|
4017
4420
|
* already-decoded strings.
|
|
4018
4421
|
*
|
|
4019
4422
|
* @example
|
|
@@ -4031,13 +4434,13 @@ declare class Hl7Message {
|
|
|
4031
4434
|
* Set the string value at a dot-path. Mutates the underlying tree and
|
|
4032
4435
|
* returns `this` for chaining (D-15). Auto-creates missing repetitions,
|
|
4033
4436
|
* components, and subcomponents WITHIN an existing field, but does NOT
|
|
4034
|
-
* auto-create segments
|
|
4437
|
+
* auto-create segments: callers must `addSegment` first (throws
|
|
4035
4438
|
* `TypeError` with an actionable message otherwise).
|
|
4036
4439
|
*
|
|
4037
4440
|
* The value is accepted verbatim: unescaped delimiter characters are NOT
|
|
4038
4441
|
* rejected on input (D-18). Re-escaping on serialize is Phase 5's concern.
|
|
4039
4442
|
*
|
|
4040
|
-
* MSH-1 / MSH-2 follow the user-facing HL7 convention
|
|
4443
|
+
* MSH-1 / MSH-2 follow the user-facing HL7 convention: `setField("MSH.3", ...)`
|
|
4041
4444
|
* targets MSH-3 (sending application), matching `msg.get("MSH.3")`.
|
|
4042
4445
|
*
|
|
4043
4446
|
* Segment/Field wrapper caches are invalidated wholesale on success (D-17).
|
|
@@ -4051,9 +4454,41 @@ declare class Hl7Message {
|
|
|
4051
4454
|
* ```
|
|
4052
4455
|
*/
|
|
4053
4456
|
setField(path: string, value: string): this;
|
|
4457
|
+
/**
|
|
4458
|
+
* Set a **typed composite** at a field (or field-repetition) dot-path,
|
|
4459
|
+
* roadmap Phase T, the conservative-emit mirror of the typed read accessors
|
|
4460
|
+
* (`asXpn`/`asCx`/…). The caller passes a structured value (an XPN name, a CX
|
|
4461
|
+
* identifier, a TS timestamp, …) by its {@link CompositeKind}, and the setter
|
|
4462
|
+
* encodes it into a spec-clean field using the HL7-R encode-safe path: any
|
|
4463
|
+
* delimiter embedded in a component value is **escaped, never injected**, so
|
|
4464
|
+
* a `familyName` of `"Smith^Jr"` re-parses to exactly that string rather than
|
|
4465
|
+
* forging a component boundary. No hand-assembly of `^`/`&`/`~`.
|
|
4466
|
+
*
|
|
4467
|
+
* The path must resolve to a **field** (`"PID.5"`) or a specific
|
|
4468
|
+
* **repetition** of a field (`"PID.11[1]"`). A component/subcomponent-level
|
|
4469
|
+
* path (`"PID.5.1"`) is rejected with `TypeError`: a composite occupies a
|
|
4470
|
+
* whole field, not a single component. Like {@link setField}, the target
|
|
4471
|
+
* segment must already exist (`addSegment` first); the repetition defaults to
|
|
4472
|
+
* index 0 and other repetitions of the field are preserved.
|
|
4473
|
+
*
|
|
4474
|
+
* Never fabricates: an omitted optional composite field encodes to an
|
|
4475
|
+
* empty/absent component, never a defaulted value; an all-empty composite
|
|
4476
|
+
* clears the field. Segment/helper caches are invalidated on success; the
|
|
4477
|
+
* frozen `warnings` array is untouched.
|
|
4478
|
+
*
|
|
4479
|
+
* @example
|
|
4480
|
+
* ```ts
|
|
4481
|
+
* const msg = buildMessage({ type: "ADT^A01" }).addSegment("PID", [""]);
|
|
4482
|
+
* msg.setComposite("PID.5", "XPN", { familyName: "Smith", givenName: "Ann" });
|
|
4483
|
+
* msg.setComposite("PID.3", "CX", { idNumber: "MRN001", identifierTypeCode: "MR" });
|
|
4484
|
+
* msg.setComposite("PID.7", "TS", "19880705");
|
|
4485
|
+
* msg.get("PID.5.1"); // "Smith"
|
|
4486
|
+
* ```
|
|
4487
|
+
*/
|
|
4488
|
+
setComposite<K extends CompositeKind>(path: string, kind: K, value: CompositeValueByKind[K]): this;
|
|
4054
4489
|
/**
|
|
4055
4490
|
* Append a new segment to the end of the message. `name` must match
|
|
4056
|
-
* `/^(?:[A-Z]{3}|Z[A-Z0-9]{2})$/u
|
|
4491
|
+
* `/^(?:[A-Z]{3}|Z[A-Z0-9]{2})$/u`: throws `TypeError` otherwise (D-19).
|
|
4057
4492
|
*
|
|
4058
4493
|
* `fields` is interpreted in HL7 1-indexed terms: `addSegment("NTE", [a, b, c])`
|
|
4059
4494
|
* produces a segment whose `NTE-1 = a`, `NTE-2 = b`, `NTE-3 = c`. The
|
|
@@ -4076,14 +4511,14 @@ declare class Hl7Message {
|
|
|
4076
4511
|
/**
|
|
4077
4512
|
* Remove segments by type + occurrence or by type + all. Call shapes:
|
|
4078
4513
|
*
|
|
4079
|
-
* - `removeSegment("NTE")
|
|
4080
|
-
* - `removeSegment("OBX", 1)
|
|
4081
|
-
* - `removeSegment("OBX", { all: true })
|
|
4514
|
+
* - `removeSegment("NTE")`: remove the FIRST NTE (occurrence 0).
|
|
4515
|
+
* - `removeSegment("OBX", 1)`: remove the SECOND OBX (0-indexed per D-01).
|
|
4516
|
+
* - `removeSegment("OBX", { all: true })`: remove ALL OBX segments.
|
|
4082
4517
|
*
|
|
4083
|
-
* `MSH` is protected
|
|
4518
|
+
* `MSH` is protected: `removeSegment("MSH")` throws `TypeError` (every
|
|
4084
4519
|
* HL7 message must retain its MSH segment). Unknown segment types are a
|
|
4085
4520
|
* no-op (idempotent; no throw). Segment name must match the D-19 shape
|
|
4086
|
-
* regex
|
|
4521
|
+
* regex: invalid shapes throw `TypeError` for symmetry with `addSegment`.
|
|
4087
4522
|
*
|
|
4088
4523
|
* Invalidates caches on return; warnings untouched (D-16).
|
|
4089
4524
|
*
|
|
@@ -4108,7 +4543,7 @@ declare class Hl7Message {
|
|
|
4108
4543
|
}
|
|
4109
4544
|
|
|
4110
4545
|
/**
|
|
4111
|
-
* Public entry point for the `@cosyte/hl7` parser
|
|
4546
|
+
* Public entry point for the `@cosyte/hl7` parser: composes every
|
|
4112
4547
|
* parser stage built across Plans 01–05 (normalize, strip MLLP, split
|
|
4113
4548
|
* segments, read delimiters, tokenize) and routes every Tier-2 warning
|
|
4114
4549
|
* through a single `emitWarning` chokepoint. The four Tier-3 fatal codes
|
|
@@ -4160,7 +4595,7 @@ declare function parseHL7(raw: string | Buffer, options: ParseOptions): Hl7Messa
|
|
|
4160
4595
|
* thrown directly; consumers narrow via the `code` discriminant.
|
|
4161
4596
|
*
|
|
4162
4597
|
* `ProfileDefinitionError` is declared here so the error taxonomy is locked
|
|
4163
|
-
* before Phase 6 wires profile validation
|
|
4598
|
+
* before Phase 6 wires profile validation: keeping file ownership clean
|
|
4164
4599
|
* across phases.
|
|
4165
4600
|
*/
|
|
4166
4601
|
|
|
@@ -4218,7 +4653,7 @@ type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];
|
|
|
4218
4653
|
* snippet of the offending input so consumers can log actionable errors.
|
|
4219
4654
|
*
|
|
4220
4655
|
* @remarks
|
|
4221
|
-
* Snippets may contain PHI when parsing real clinical messages
|
|
4656
|
+
* Snippets may contain PHI when parsing real clinical messages: redact at
|
|
4222
4657
|
* the call site if required by your compliance posture. The library does
|
|
4223
4658
|
* not redact snippets itself.
|
|
4224
4659
|
*
|
|
@@ -4229,7 +4664,7 @@ type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];
|
|
|
4229
4664
|
* parseHL7("");
|
|
4230
4665
|
* } catch (err) {
|
|
4231
4666
|
* if (err instanceof Hl7ParseError && err.code === "EMPTY_INPUT") {
|
|
4232
|
-
* // handle empty input
|
|
4667
|
+
* // handle empty input: err.position, err.snippet available
|
|
4233
4668
|
* }
|
|
4234
4669
|
* }
|
|
4235
4670
|
* ```
|
|
@@ -4248,7 +4683,7 @@ declare class Hl7ParseError extends Error {
|
|
|
4248
4683
|
}
|
|
4249
4684
|
/**
|
|
4250
4685
|
* Thrown by `defineProfile()` and profile-validation code (Phase 6) when a
|
|
4251
|
-
* profile definition is structurally invalid
|
|
4686
|
+
* profile definition is structurally invalid: e.g. references an undefined
|
|
4252
4687
|
* parent, declares a malformed custom segment, or includes an unsupported
|
|
4253
4688
|
* date format. Declared in Phase 2 so the error taxonomy is locked before
|
|
4254
4689
|
* Phase 6 lands; callers may optionally supply the offending profile name
|
|
@@ -4279,13 +4714,13 @@ declare class ProfileDefinitionError extends Error {
|
|
|
4279
4714
|
* HL7 v2 **Table 0211 (Alternate Character Sets)** resolution for the parser's
|
|
4280
4715
|
* `Buffer`-decode stage (Phase O). Mirrors the frozen read-only registries
|
|
4281
4716
|
* `KNOWN_SEGMENTS` / `KNOWN_CODING_SYSTEMS`: a single source of truth mapping an
|
|
4282
|
-
* MSH-18 character-set code to how the parser treats it
|
|
4717
|
+
* MSH-18 character-set code to how the parser treats it: **decode** (single-byte
|
|
4283
4718
|
* ISO-8859 family + Unicode UTF-8) or **preserve-verbatim** (the multibyte /
|
|
4284
4719
|
* ISO-2022-switched East-Asian sets and UTF-16/32, which HL7 renders through
|
|
4285
4720
|
* stateful `\Mxxyyzz\` escapes this phase deliberately does not fully decode).
|
|
4286
4721
|
*
|
|
4287
4722
|
* Spec traceability: HL7 v2 Chapter 2, **MSH-18** (item 00692) + **Table 0211**
|
|
4288
|
-
* (OID 2.16.840.1.113883.18.116). MSH-18 is **repeating
|
|
4723
|
+
* (OID 2.16.840.1.113883.18.116). MSH-18 is **repeating**: the first occurrence
|
|
4289
4724
|
* is the message's default encoding, later occurrences are alternates activated
|
|
4290
4725
|
* mid-message by the §2.7.4 charset-switch escapes (`\Cxxyy\` single-byte,
|
|
4291
4726
|
* `\Mxxyyzz\` multi-byte). A **blank** MSH-18 means 7-bit ASCII (the default).
|
|
@@ -4299,18 +4734,18 @@ declare class ProfileDefinitionError extends Error {
|
|
|
4299
4734
|
* be re-decoded downstream via `Buffer.from(value, "latin1")`. For a **multibyte**
|
|
4300
4735
|
* set (UTF-16/32, the East-Asian DBCS/ISO-2022 sets) a content byte can coincide
|
|
4301
4736
|
* with a structural byte, so tokenization runs on raw bytes and framing is
|
|
4302
|
-
* best-effort, not byte-recoverable
|
|
4737
|
+
* best-effort, not byte-recoverable: which is exactly why decoding those sets is
|
|
4303
4738
|
* deferred (see the parser's known-limitations). Because a recognized-but-undecoded
|
|
4304
4739
|
* set and an unrecognized label both preserve bytes, the exact Table-0211 spelling
|
|
4305
|
-
* of the multibyte codes is not load-bearing for safety
|
|
4740
|
+
* of the multibyte codes is not load-bearing for safety: only for which warning
|
|
4306
4741
|
* (`UNSUPPORTED_CHARSET` vs `UNKNOWN_CHARSET`) fires.
|
|
4307
4742
|
*/
|
|
4308
4743
|
/**
|
|
4309
4744
|
* How the parser treats a resolved MSH-18 character set.
|
|
4310
4745
|
*
|
|
4311
|
-
* - `decode
|
|
4312
|
-
* - `verbatim
|
|
4313
|
-
* (byte-recoverable for single-byte content
|
|
4746
|
+
* - `decode`: decode the byte stream to text with {@link CharsetResolution.decoder}.
|
|
4747
|
+
* - `verbatim`: read the raw bytes as a 1:1 `latin1` mapping; do not decode
|
|
4748
|
+
* (byte-recoverable for single-byte content: see the module header).
|
|
4314
4749
|
*/
|
|
4315
4750
|
type CharsetTreatment = "decode" | "verbatim";
|
|
4316
4751
|
/**
|
|
@@ -4372,7 +4807,7 @@ declare function canonicalCharset(raw: string): string;
|
|
|
4372
4807
|
|
|
4373
4808
|
/**
|
|
4374
4809
|
* HL7 escape-sequence handling for the `@cosyte/hl7` parser pipeline
|
|
4375
|
-
|
|
4810
|
+
*: expands reserved-delimiter escapes on parse (`unescape`) and re-escapes
|
|
4376
4811
|
* them back when emitting spec-clean HL7 on serialize (`reescape`).
|
|
4377
4812
|
*
|
|
4378
4813
|
* The HL7 v2 spec reserves the delimiter characters (field, component,
|
|
@@ -4385,7 +4820,7 @@ declare function canonicalCharset(raw: string): string;
|
|
|
4385
4820
|
* sequences. We **decode** sequences whose meaning is byte-exact (delimiter
|
|
4386
4821
|
* back-substitution, hex, newline, truncation char). We **recognize and
|
|
4387
4822
|
* preserve verbatim** sequences whose intent is presentational or sender-
|
|
4388
|
-
* specific
|
|
4823
|
+
* specific: decoding them would either lose information (formatting/charset)
|
|
4389
4824
|
* or invent a rendering policy the spec leaves to the consumer. Either way,
|
|
4390
4825
|
* a recognized standard sequence never emits `UNKNOWN_ESCAPE_SEQUENCE`;
|
|
4391
4826
|
* that warning is reserved for genuinely-unknown bodies (`\Z..\`, garbage).
|
|
@@ -4405,7 +4840,7 @@ declare function canonicalCharset(raw: string): string;
|
|
|
4405
4840
|
* \.sp\, \.in\, \.ti\, \.fi\, formatting commands (§2.7.6, kept as
|
|
4406
4841
|
* \.nf\, \.ce\ semantic sentinels for a renderer)
|
|
4407
4842
|
* \Cxxyy\, \Mxxyyzz\ single- / multi-byte charset switch
|
|
4408
|
-
* (§2.7.4
|
|
4843
|
+
* (§2.7.4: sender-local byte sequences;
|
|
4409
4844
|
* decoding requires charset context
|
|
4410
4845
|
* this module does not have)
|
|
4411
4846
|
*
|
|
@@ -4424,7 +4859,7 @@ declare function canonicalCharset(raw: string): string;
|
|
|
4424
4859
|
* Unknown or malformed sequences are preserved VERBATIM in the output and
|
|
4425
4860
|
* emit an `UNKNOWN_ESCAPE_SEQUENCE` warning via `emit`. Unterminated escapes
|
|
4426
4861
|
* (an escape character with no closing partner before end-of-input) are also
|
|
4427
|
-
* preserved in full and warn once
|
|
4862
|
+
* preserved in full and warn once: the scan is strictly O(n) and cannot
|
|
4428
4863
|
* infinite-loop on malformed input.
|
|
4429
4864
|
*
|
|
4430
4865
|
* @example
|
|
@@ -4458,10 +4893,10 @@ declare function unescape(input: string, enc: EncodingCharacters, emit: (w: Hl7P
|
|
|
4458
4893
|
* "\n" (LF) → \.br\
|
|
4459
4894
|
* "\r" (CR) → \X0D\ (a decoded CR is the HL7 segment separator;
|
|
4460
4895
|
* emitting it raw would corrupt wire framing, so
|
|
4461
|
-
* it re-encodes to its hex escape
|
|
4896
|
+
* it re-encodes to its hex escape: see below)
|
|
4462
4897
|
*
|
|
4463
4898
|
* **Lossy by construction for the non-delimiter escape families.** `reescape`
|
|
4464
|
-
* only knows about the reserved characters above
|
|
4899
|
+
* only knows about the reserved characters above: it cannot reconstruct a
|
|
4465
4900
|
* recognize-and-preserve escape (`\H\`, formatting, charset, `\Z..\`) or the
|
|
4466
4901
|
* original bytes of a hex escape (`\X41\` decoded to `A`; casing of `\X0d\`),
|
|
4467
4902
|
* because those decode to ordinary characters that carry no "I was an escape"
|
|
@@ -4483,17 +4918,120 @@ declare function unescape(input: string, enc: EncodingCharacters, emit: (w: Hl7P
|
|
|
4483
4918
|
*/
|
|
4484
4919
|
declare function reescape(input: string, enc: EncodingCharacters): string;
|
|
4485
4920
|
|
|
4921
|
+
/**
|
|
4922
|
+
* First-class HL7 v2 text codec for `@cosyte/hl7`: the ergonomic
|
|
4923
|
+
* decode / encode pair that sits on top of the parser-internal
|
|
4924
|
+
* `unescape` / `reescape` primitives.
|
|
4925
|
+
*
|
|
4926
|
+
* - {@link decodeText} turns a field's escape-bearing wire text into its
|
|
4927
|
+
* decoded value (delimiter + hex + `\.br\` escapes resolved; presentational
|
|
4928
|
+
* escapes preserved verbatim), with no warning plumbing to wire up.
|
|
4929
|
+
* - {@link encodeText} is the **encode-safe** direction: an arbitrary string in,
|
|
4930
|
+
* a spec-clean field body out that can **never inject a delimiter or forge a
|
|
4931
|
+
* component boundary**. This is the primitive Phase T (typed emit) builds on.
|
|
4932
|
+
*
|
|
4933
|
+
* For a human-readable rendering (formatting commands → whitespace, highlight
|
|
4934
|
+
* dropped), use {@link renderText} instead of {@link decodeText}.
|
|
4935
|
+
*/
|
|
4936
|
+
|
|
4937
|
+
/**
|
|
4938
|
+
* Decode a field's escape-bearing HL7 text to its human value: the five
|
|
4939
|
+
* delimiter escapes (`\F\ \S\ \T\ \R\ \E\`), the truncation escape (`\P\`),
|
|
4940
|
+
* `\.br\` (→ newline), and hex (`\Xdddd…\`) are resolved; presentational
|
|
4941
|
+
* escapes (`\H\`/`\N\`, formatting, charset, vendor `\Z..\`) are **preserved
|
|
4942
|
+
* verbatim** so nothing is lost. Never throws.
|
|
4943
|
+
*
|
|
4944
|
+
* This is the one-call inverse of {@link encodeText} for delimiter-bearing
|
|
4945
|
+
* content. It does **not** interpret formatting/highlight: for a normalized
|
|
4946
|
+
* display string use {@link renderText}.
|
|
4947
|
+
*
|
|
4948
|
+
* @param input - the field's escape-bearing text.
|
|
4949
|
+
* @param enc - encoding characters; defaults to the HL7 standard `|^~\&`.
|
|
4950
|
+
* @returns the decoded value string.
|
|
4951
|
+
*
|
|
4952
|
+
* @example
|
|
4953
|
+
* ```ts
|
|
4954
|
+
* import { decodeText } from "@cosyte/hl7";
|
|
4955
|
+
* decodeText("Doe\\S\\John"); // "Doe^John": \S\ → component separator
|
|
4956
|
+
* decodeText("line1\\.br\\line2"); // "line1\nline2"
|
|
4957
|
+
* ```
|
|
4958
|
+
*/
|
|
4959
|
+
declare function decodeText(input: string, enc?: EncodingCharacters): string;
|
|
4960
|
+
/**
|
|
4961
|
+
* **Encode-safe** direction: escape an arbitrary string so it can be placed in
|
|
4962
|
+
* an HL7 field as data without ever breaking framing. Every reserved character
|
|
4963
|
+
*: the escape char (escaped **first**, so decoding is unambiguous), the field,
|
|
4964
|
+
* component, subcomponent, and repetition separators, the declared truncation
|
|
4965
|
+
* char, and the framing-critical `\n`/`\r`: is replaced by its escape
|
|
4966
|
+
* sequence, so the value **cannot inject a delimiter or forge a component /
|
|
4967
|
+
* subcomponent / repetition boundary**.
|
|
4968
|
+
*
|
|
4969
|
+
* The hard invariant, property-tested over arbitrary strings:
|
|
4970
|
+
* `decodeText(encodeText(s, enc), enc) === s`, and a message field carrying
|
|
4971
|
+
* `encodeText(s)` **cannot forge a component / subcomponent / repetition
|
|
4972
|
+
* boundary or break framing**: the value never escapes its field.
|
|
4973
|
+
*
|
|
4974
|
+
* Two caveats are inherent to HL7 field encoding, not to this codec, and apply
|
|
4975
|
+
* to *whole-field* re-parse (they do not weaken the no-injection guarantee):
|
|
4976
|
+
* the two-character string `""` is HL7's explicit-null token, so a field whose
|
|
4977
|
+
* entire value is `""` re-parses as null; and the default parser trims field
|
|
4978
|
+
* whitespace, so a value with leading/trailing spaces re-parses trimmed. Encode
|
|
4979
|
+
* such values into a component/subcomponent position, or parse with trimming
|
|
4980
|
+
* off, to preserve them exactly.
|
|
4981
|
+
*
|
|
4982
|
+
* @param input - the arbitrary string to encode.
|
|
4983
|
+
* @param enc - encoding characters; defaults to the HL7 standard `|^~\&`.
|
|
4984
|
+
* @returns the spec-clean, delimiter-safe field body.
|
|
4985
|
+
*
|
|
4986
|
+
* @example
|
|
4987
|
+
* ```ts
|
|
4988
|
+
* import { encodeText, parseHL7 } from "@cosyte/hl7";
|
|
4989
|
+
* // A value full of delimiters cannot break out of its field:
|
|
4990
|
+
* const hostile = "a|b^c~d\\e&f";
|
|
4991
|
+
* const body = encodeText(hostile); // "a\\F\\b\\S\\c\\R\\d\\E\\e\\T\\f"
|
|
4992
|
+
* const msg = parseHL7(`MSH|^~\\&|A|B|C|D|20260101||ADT^A01|1|P|2.5\rNTE|1||${body}`);
|
|
4993
|
+
* msg.segments("NTE")[0]?.field(3).value === hostile; // true: round-trips exactly
|
|
4994
|
+
* ```
|
|
4995
|
+
*/
|
|
4996
|
+
declare function encodeText(input: string, enc?: EncodingCharacters): string;
|
|
4997
|
+
|
|
4998
|
+
/**
|
|
4999
|
+
* The `@cosyte/hl7` **text codec**: a first-class, documented namespace for
|
|
5000
|
+
* getting the human string out of a field and safely encoding an arbitrary
|
|
5001
|
+
* string in.
|
|
5002
|
+
*
|
|
5003
|
+
* Exposed on the package as the `text` namespace object so a consumer can, in
|
|
5004
|
+
* one call:
|
|
5005
|
+
*
|
|
5006
|
+
* - `text.decode(field.text)`: resolve delimiter/hex/`\.br\` escapes to a value;
|
|
5007
|
+
* - `text.render(field.text)`: normalize formatting + highlight to a display model;
|
|
5008
|
+
* - `text.encode(anyString)`: encode-safe: no delimiter injection (Phase T's primitive).
|
|
5009
|
+
*
|
|
5010
|
+
* ```ts
|
|
5011
|
+
* import { text } from "@cosyte/hl7";
|
|
5012
|
+
* text.render("Result \\H\\HIGH\\N\\\\.br\\see note").text; // "Result HIGH\nsee note"
|
|
5013
|
+
* text.encode("a|b^c").length > 0; // delimiter-safe
|
|
5014
|
+
* ```
|
|
5015
|
+
*
|
|
5016
|
+
* The same functions are also available as top-level named exports
|
|
5017
|
+
* (`decodeText`, `encodeText`, `renderText`) for tree-shaking-friendly imports.
|
|
5018
|
+
*/
|
|
5019
|
+
|
|
5020
|
+
declare namespace index$1 {
|
|
5021
|
+
export { decodeText as decode, encodeText as encode, renderText as render };
|
|
5022
|
+
}
|
|
5023
|
+
|
|
4486
5024
|
/**
|
|
4487
5025
|
* Dot-path tokenizer and resolver for the `@cosyte/hl7` structural
|
|
4488
5026
|
* model. Parses strings like `PID.5.1`, `OBX[2].5`, `PID.3[0].1` into a
|
|
4489
5027
|
* discriminated-token descriptor, then resolves that descriptor against a
|
|
4490
5028
|
* `readonly RawSegment[]` tree to produce the decoded leaf string (or
|
|
4491
|
-
* `undefined` on missing path). Zero runtime deps
|
|
5029
|
+
* `undefined` on missing path). Zero runtime deps: a hand-rolled linear scan
|
|
4492
5030
|
* (analog: `src/parser/dates.ts::matchTokenFormat`).
|
|
4493
5031
|
*
|
|
4494
5032
|
* Indexing conventions (locked in Phase 3 CONTEXT.md):
|
|
4495
|
-
* - `[N]` is ALWAYS 0-indexed
|
|
4496
|
-
* - Dot-numbers are ALWAYS 1-indexed
|
|
5033
|
+
* - `[N]` is ALWAYS 0-indexed: applies to segment repeats AND field repeats (D-01).
|
|
5034
|
+
* - Dot-numbers are ALWAYS 1-indexed: matches HL7 spec and Phase 2's 1-indexed
|
|
4497
5035
|
* `RawSegment.fields` (D-02). `PID.5` → `fields[5]`.
|
|
4498
5036
|
* - MSH.1 / MSH.2 are the separator char and encoding-chars string, both
|
|
4499
5037
|
* stored at `fields[0]` / `fields[1]` by Phase 2 tokenize.ts (D-05).
|
|
@@ -4553,7 +5091,7 @@ declare function parsePath(path: string): DotPath;
|
|
|
4553
5091
|
* leaf value. Returns `undefined` whenever the path does not resolve (missing
|
|
4554
5092
|
* segment, out-of-range field/component/subcomponent/repetition), and never
|
|
4555
5093
|
* throws on a missing value. Throws `TypeError` only when `path` itself is
|
|
4556
|
-
* malformed
|
|
5094
|
+
* malformed: callers relying on "never throws" should pre-validate or wrap
|
|
4557
5095
|
* in try/catch.
|
|
4558
5096
|
*
|
|
4559
5097
|
* @example
|
|
@@ -4567,14 +5105,14 @@ declare function parsePath(path: string): DotPath;
|
|
|
4567
5105
|
declare function resolvePath(path: string, segments: readonly RawSegment[], _enc: EncodingCharacters): string | undefined;
|
|
4568
5106
|
|
|
4569
5107
|
/**
|
|
4570
|
-
* `HL7` namespace
|
|
5108
|
+
* `HL7` namespace: type-only re-exports of the 10 composite interfaces
|
|
4571
5109
|
* (XPN, XAD, CX, CWE, CE, XTN, PL, TS, NM, HD). Consumed via
|
|
4572
5110
|
* `export * as HL7 from "./model/types/namespace.js";` in the top-level barrel
|
|
4573
5111
|
* (`src/index.ts`). Named exports of the same interfaces live in the public
|
|
4574
5112
|
* barrel alongside this namespace for developers who prefer the direct
|
|
4575
5113
|
* `import type { XPN } ...` style (D-13).
|
|
4576
5114
|
*
|
|
4577
|
-
* The namespace is TYPES-ONLY
|
|
5115
|
+
* The namespace is TYPES-ONLY: parser functions (`parseXpn`, `parseXad`, ...)
|
|
4578
5116
|
* are exported as value exports from the main barrel, not under `HL7`.
|
|
4579
5117
|
*/
|
|
4580
5118
|
|
|
@@ -4595,7 +5133,7 @@ declare namespace namespace {
|
|
|
4595
5133
|
}
|
|
4596
5134
|
|
|
4597
5135
|
/**
|
|
4598
|
-
* `pickMrn
|
|
5136
|
+
* `pickMrn`: pick the Medical Record Number string from a PID-3 identifier
|
|
4599
5137
|
* list. Isolated from `patient.ts` so Phase 6 profile hooks can substitute
|
|
4600
5138
|
* a profile-aware variant without patching the helper that calls it.
|
|
4601
5139
|
*
|
|
@@ -4606,12 +5144,12 @@ declare namespace namespace {
|
|
|
4606
5144
|
* Pick the Medical Record Number string from a list of PID-3 CX identifiers.
|
|
4607
5145
|
*
|
|
4608
5146
|
* D-07: prefer the first CX whose `identifierTypeCode === "MR"` (HL7 v2.5+
|
|
4609
|
-
* canonical MRN marker). D-10: the match is case-SENSITIVE
|
|
5147
|
+
* canonical MRN marker). D-10: the match is case-SENSITIVE: lowercase
|
|
4610
5148
|
* `"mr"` does NOT match; spec mandates uppercase.
|
|
4611
5149
|
*
|
|
4612
5150
|
* D-08: when no MR-typed identifier is found, fall back to the first CX's
|
|
4613
5151
|
* `idNumber`. Returns `undefined` when the first CX has no `idNumber` (even
|
|
4614
|
-
* if later CXs do
|
|
5152
|
+
* if later CXs do: the fallback is strictly "first CX", not "first CX with
|
|
4615
5153
|
* idNumber", because we need a deterministic answer).
|
|
4616
5154
|
*
|
|
4617
5155
|
* D-21: no warning emitted. Callers who need strict MR resolution can walk
|
|
@@ -4627,7 +5165,7 @@ declare namespace namespace {
|
|
|
4627
5165
|
* // → "MRN001"
|
|
4628
5166
|
*
|
|
4629
5167
|
* pickMrn([{ idNumber: "X1" }]);
|
|
4630
|
-
* // → "X1" (fallback
|
|
5168
|
+
* // → "X1" (fallback: no MR entry)
|
|
4631
5169
|
*
|
|
4632
5170
|
* pickMrn([]);
|
|
4633
5171
|
* // → undefined
|
|
@@ -4636,7 +5174,7 @@ declare namespace namespace {
|
|
|
4636
5174
|
declare function pickMrn(identifiers: readonly CX[]): string | undefined;
|
|
4637
5175
|
|
|
4638
5176
|
/**
|
|
4639
|
-
* `buildMessage
|
|
5177
|
+
* `buildMessage`: top-level outbound factory for the `@cosyte/hl7`
|
|
4640
5178
|
* package. Synthesizes a complete MSH `RawSegment` from `BuildMessageInit`
|
|
4641
5179
|
* and returns a real `Hl7Message` (not a builder subtype). Callers chain
|
|
4642
5180
|
* `.addSegment(...)` (Phase 3 mutation method, unchanged) to append PID,
|
|
@@ -4651,7 +5189,7 @@ declare function pickMrn(identifiers: readonly CX[]): string | undefined;
|
|
|
4651
5189
|
* - D-10: `BuildMessageInit` shape below.
|
|
4652
5190
|
* - D-11: internally synthesises a complete MSH `RawSegment` and hands to
|
|
4653
5191
|
* `new Hl7Message({...})`.
|
|
4654
|
-
* - D-12: controlId auto-gen
|
|
5192
|
+
* - D-12: controlId auto-gen: YYYYMMDDHHmmssSSS + 6 alnum = 23 chars.
|
|
4655
5193
|
* - D-13: `timestamp` accepts `Date | string`; `Date` formats to
|
|
4656
5194
|
* `YYYYMMDDHHmmss` UTC.
|
|
4657
5195
|
* - D-14: encoding chars always `DEFAULT_ENCODING_CHARACTERS`.
|
|
@@ -4661,9 +5199,9 @@ declare function pickMrn(identifiers: readonly CX[]): string | undefined;
|
|
|
4661
5199
|
*
|
|
4662
5200
|
* **Absent vs. explicit null at the wire level:** at the HL7 wire level,
|
|
4663
5201
|
* an empty-string field and an omitted field produce IDENTICAL output
|
|
4664
|
-
* (both emit as absent
|
|
5202
|
+
* (both emit as absent: `||` in the line). If you need to distinguish
|
|
4665
5203
|
* "explicitly null" (HL7 `""`) from "absent" in an outbound message, use
|
|
4666
|
-
* `buildMessage({...}).setField(path, '""')` after construction
|
|
5204
|
+
* `buildMessage({...}).setField(path, '""')` after construction: the
|
|
4667
5205
|
* Phase 3 `setField` mutation method sets `RawField.isNull = true`, which
|
|
4668
5206
|
* the emitter preserves as the literal two-char string `""` per D-02.
|
|
4669
5207
|
*/
|
|
@@ -4678,7 +5216,7 @@ declare function pickMrn(identifiers: readonly CX[]): string | undefined;
|
|
|
4678
5216
|
* empty string produce IDENTICAL wire output (both emit as an absent
|
|
4679
5217
|
* positional field). To emit an HL7 explicit null (`""`) at a specific
|
|
4680
5218
|
* position in an outbound message, build the message first and then call
|
|
4681
|
-
* `.setField(path, '""')
|
|
5219
|
+
* `.setField(path, '""')`: the Phase 3 mutation method sets `isNull=true`
|
|
4682
5220
|
* on the underlying `RawField`, and the emitter preserves that as the
|
|
4683
5221
|
* literal two-char output per D-02.
|
|
4684
5222
|
*
|
|
@@ -4707,7 +5245,7 @@ interface BuildMessageInit {
|
|
|
4707
5245
|
*
|
|
4708
5246
|
* The string is split on `^` into MSH-9 components; each component is
|
|
4709
5247
|
* emitted verbatim. Literal `^` characters in a component are NOT
|
|
4710
|
-
* representable via this field
|
|
5248
|
+
* representable via this field: splitting is unconditional. Callers
|
|
4711
5249
|
* needing that edge case should build the message and then use
|
|
4712
5250
|
* `.setField("MSH.9.1", ...)` etc. after construction.
|
|
4713
5251
|
*
|
|
@@ -4752,7 +5290,7 @@ interface BuildMessageInit {
|
|
|
4752
5290
|
*
|
|
4753
5291
|
* **Empty string vs. omitted field (W1):** at the HL7 wire level, passing
|
|
4754
5292
|
* `sendingApp: ""` and omitting `sendingApp` produce IDENTICAL output
|
|
4755
|
-
* (both emit as absent
|
|
5293
|
+
* (both emit as absent: `||` at the MSH-3 position). If you need to
|
|
4756
5294
|
* emit an HL7 explicit null (`""`, the two-char literal) at a specific
|
|
4757
5295
|
* position, build the message first, then use `setField`:
|
|
4758
5296
|
*
|
|
@@ -4784,23 +5322,273 @@ interface BuildMessageInit {
|
|
|
4784
5322
|
*/
|
|
4785
5323
|
declare function buildMessage(init: BuildMessageInit): Hl7Message;
|
|
4786
5324
|
|
|
5325
|
+
/**
|
|
5326
|
+
* Shared field/segment assembly primitives for the typed builders
|
|
5327
|
+
* (`buildAdt`/`buildOru`: roadmap Phase T). Centralises the MSH synthesis and
|
|
5328
|
+
* the sparse positional-segment assembly so each builder stays declarative and
|
|
5329
|
+
* every builder emits an identical, spec-clean MSH.
|
|
5330
|
+
*
|
|
5331
|
+
* Zero runtime deps: pure functions over the raw positional tree.
|
|
5332
|
+
*/
|
|
5333
|
+
|
|
5334
|
+
/**
|
|
5335
|
+
* The MSH envelope shared by every typed builder: the addressing + control
|
|
5336
|
+
* metadata that is independent of the message body. Mirrors `BuildMessageInit`
|
|
5337
|
+
* minus `type` (each builder derives MSH-9 from its own trigger).
|
|
5338
|
+
*/
|
|
5339
|
+
interface MessageEnvelope {
|
|
5340
|
+
readonly sendingApp?: string;
|
|
5341
|
+
readonly sendingFacility?: string;
|
|
5342
|
+
readonly receivingApp?: string;
|
|
5343
|
+
readonly receivingFacility?: string;
|
|
5344
|
+
/** Auto-generated via `generateControlId()` when omitted. */
|
|
5345
|
+
readonly controlId?: string;
|
|
5346
|
+
/**
|
|
5347
|
+
* `Date` → HL7 `YYYYMMDDHHmmss` (UTC, seconds); a pre-formatted HL7 TS string
|
|
5348
|
+
* passes through verbatim. Defaults to `new Date()`.
|
|
5349
|
+
*/
|
|
5350
|
+
readonly timestamp?: Date | string;
|
|
5351
|
+
/** Defaults to `"2.5"`. */
|
|
5352
|
+
readonly version?: string;
|
|
5353
|
+
/** Defaults to `"P"` (production). */
|
|
5354
|
+
readonly processingId?: string;
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
/**
|
|
5358
|
+
* `buildAdt`: author a spec-clean HL7 v2 **ADT** (admit/discharge/transfer)
|
|
5359
|
+
* message from typed inputs (roadmap Phase T). The conservative-emit mirror of
|
|
5360
|
+
* the read helpers (`msg.patient`, `msg.visit`): a caller supplies structured
|
|
5361
|
+
* values (an {@link XPN} name, {@link CX} identifiers, a {@link TS} birth date,
|
|
5362
|
+
* a {@link PL} location, …) and `buildAdt` assembles `MSH + EVN + PID + PV1`
|
|
5363
|
+
* with correct `^`/`&`/`~` structure via the HL7-R encode-safe path: never
|
|
5364
|
+
* hand-assembling delimiters, never injecting one.
|
|
5365
|
+
*
|
|
5366
|
+
* **Structurally complete + zero-warning.** The emitted message carries the
|
|
5367
|
+
* segment groups the parser's own structure net marks Required for the ADT
|
|
5368
|
+
* events it recognises (patient = PID, visit = PV1), so it re-parses with
|
|
5369
|
+
* **zero warnings** and `msg.structure.missingGroups` empty.
|
|
5370
|
+
*
|
|
5371
|
+
* **Never fabricate.** Only values the caller supplies are emitted; an omitted
|
|
5372
|
+
* optional field becomes an empty/absent field, never a defaulted clinical
|
|
5373
|
+
* value. `patient` is **required**: an ADT patient event with no patient is a
|
|
5374
|
+
* typed `TypeError`, not a guessed PID.
|
|
5375
|
+
*
|
|
5376
|
+
* Spec traceability: HL7 v2 Ch. 3 (ADT: MSH/EVN/PID/PV1), Ch. 2 datatypes.
|
|
5377
|
+
* Zero runtime deps.
|
|
5378
|
+
*/
|
|
5379
|
+
|
|
5380
|
+
/** Typed PID (patient identification) content for {@link buildAdt}. */
|
|
5381
|
+
interface AdtPatient {
|
|
5382
|
+
/** PID-1 Set ID. */
|
|
5383
|
+
readonly setId?: string;
|
|
5384
|
+
/** PID-3 Patient Identifier List: one or more CX identifiers (MRN, SSN, …). */
|
|
5385
|
+
readonly identifiers?: CX | readonly CX[];
|
|
5386
|
+
/** PID-5 Patient Name. */
|
|
5387
|
+
readonly name?: XPN | readonly XPN[];
|
|
5388
|
+
/** PID-6 Mother's Maiden Name. */
|
|
5389
|
+
readonly mothersMaidenName?: XPN;
|
|
5390
|
+
/** PID-7 Date/Time of Birth. */
|
|
5391
|
+
readonly birthDateTime?: TS | string;
|
|
5392
|
+
/** PID-8 Administrative Sex (e.g. `"F"`, `"M"`, `"U"`). */
|
|
5393
|
+
readonly administrativeSex?: string;
|
|
5394
|
+
/** PID-11 Patient Address: one or more XAD addresses. */
|
|
5395
|
+
readonly address?: XAD | readonly XAD[];
|
|
5396
|
+
/** PID-13 Phone Number - Home: one or more XTN telecoms. */
|
|
5397
|
+
readonly phoneHome?: XTN | readonly XTN[];
|
|
5398
|
+
/** PID-18 Patient Account Number. */
|
|
5399
|
+
readonly accountNumber?: CX;
|
|
5400
|
+
}
|
|
5401
|
+
/** Typed PV1 (patient visit) content for {@link buildAdt}. */
|
|
5402
|
+
interface AdtVisit {
|
|
5403
|
+
/** PV1-1 Set ID. */
|
|
5404
|
+
readonly setId?: string;
|
|
5405
|
+
/** PV1-2 Patient Class (e.g. `"I"` inpatient, `"O"` outpatient, `"E"` emergency). */
|
|
5406
|
+
readonly patientClass?: string;
|
|
5407
|
+
/** PV1-3 Assigned Patient Location. */
|
|
5408
|
+
readonly assignedLocation?: PL;
|
|
5409
|
+
/** PV1-7 Attending Doctor. */
|
|
5410
|
+
readonly attendingDoctor?: XCN | readonly XCN[];
|
|
5411
|
+
/** PV1-8 Referring Doctor. */
|
|
5412
|
+
readonly referringDoctor?: XCN | readonly XCN[];
|
|
5413
|
+
/** PV1-19 Visit Number. */
|
|
5414
|
+
readonly visitNumber?: CX;
|
|
5415
|
+
/** PV1-44 Admit Date/Time. */
|
|
5416
|
+
readonly admitDateTime?: TS | string;
|
|
5417
|
+
}
|
|
5418
|
+
/** Typed EVN (event type) content for {@link buildAdt}. */
|
|
5419
|
+
interface AdtEvent {
|
|
5420
|
+
/** EVN-2 Recorded Date/Time. */
|
|
5421
|
+
readonly recordedDateTime?: TS | string;
|
|
5422
|
+
/** EVN-6 Event Occurred. */
|
|
5423
|
+
readonly eventOccurred?: TS | string;
|
|
5424
|
+
}
|
|
5425
|
+
/** Input for {@link buildAdt}: the MSH envelope plus the typed segment bodies. */
|
|
5426
|
+
interface BuildAdtInit extends MessageEnvelope {
|
|
5427
|
+
/** PID content. **Required**: never fabricated. */
|
|
5428
|
+
readonly patient: AdtPatient;
|
|
5429
|
+
/** PV1 content. Optional; an (empty) PV1 is emitted regardless so the visit group is present. */
|
|
5430
|
+
readonly visit?: AdtVisit;
|
|
5431
|
+
/** EVN content (EVN-1 is the trigger event; EVN-2/6 optional). */
|
|
5432
|
+
readonly event?: AdtEvent;
|
|
5433
|
+
}
|
|
5434
|
+
/**
|
|
5435
|
+
* Build a spec-clean ADT message for `event` (the MSH-9.2 trigger, e.g.
|
|
5436
|
+
* `"A01"`, `"A04"`, `"A08"`) from typed inputs.
|
|
5437
|
+
*
|
|
5438
|
+
* The result is a real {@link Hl7Message}: `msg.toString()` serialises it,
|
|
5439
|
+
* `parseHL7(msg.toString())` round-trips with zero warnings, and
|
|
5440
|
+
* `msg.patient` / `msg.visit` read back the values supplied.
|
|
5441
|
+
*
|
|
5442
|
+
* @param event - the ADT trigger event (MSH-9.2). Required, non-empty.
|
|
5443
|
+
* @param init - the MSH envelope + typed PID/PV1/EVN content (`patient` required).
|
|
5444
|
+
* @throws TypeError when `event` is empty or `init.patient` is absent.
|
|
5445
|
+
*
|
|
5446
|
+
* @example
|
|
5447
|
+
* ```ts
|
|
5448
|
+
* import { buildAdt, parseHL7 } from "@cosyte/hl7";
|
|
5449
|
+
* const msg = buildAdt("A01", {
|
|
5450
|
+
* sendingApp: "CLINIC",
|
|
5451
|
+
* receivingApp: "LAB",
|
|
5452
|
+
* patient: {
|
|
5453
|
+
* identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
|
|
5454
|
+
* name: { familyName: "Test", givenName: "Ann" },
|
|
5455
|
+
* birthDateTime: "19880705",
|
|
5456
|
+
* administrativeSex: "F",
|
|
5457
|
+
* },
|
|
5458
|
+
* visit: { patientClass: "I", assignedLocation: { pointOfCare: "ICU", room: "1", bed: "A" } },
|
|
5459
|
+
* });
|
|
5460
|
+
* const round = parseHL7(msg.toString());
|
|
5461
|
+
* round.warnings.length; // 0
|
|
5462
|
+
* round.patient?.familyName; // "Test"
|
|
5463
|
+
* round.structure.missingGroups; // []
|
|
5464
|
+
* ```
|
|
5465
|
+
*/
|
|
5466
|
+
declare function buildAdt(event: string, init: BuildAdtInit): Hl7Message;
|
|
5467
|
+
|
|
5468
|
+
/**
|
|
5469
|
+
* `buildOru`: author a spec-clean HL7 v2 **ORU^R01** (observation result)
|
|
5470
|
+
* message from typed inputs (roadmap Phase T). The conservative-emit mirror of
|
|
5471
|
+
* the read helpers (`msg.orders`, `msg.observations`): a caller supplies a
|
|
5472
|
+
* typed patient, an optional order (OBR), and one or more typed observations
|
|
5473
|
+
* (OBX), and `buildOru` assembles `MSH + PID + OBR + OBX…` with correct
|
|
5474
|
+
* `^`/`&`/`~` structure via the HL7-R encode-safe path.
|
|
5475
|
+
*
|
|
5476
|
+
* **Structurally complete + zero-warning.** The emitted message carries the
|
|
5477
|
+
* ORU^R01 result group the parser's structure net marks Required (OBR/OBX), so
|
|
5478
|
+
* it re-parses with **zero warnings** and `msg.structure.missingGroups` empty.
|
|
5479
|
+
*
|
|
5480
|
+
* **Never fabricate.** Only supplied values are emitted; omitted fields become
|
|
5481
|
+
* empty/absent. `patient` and at least one `observations` entry are
|
|
5482
|
+
* **required**: an ORU with no subject or no result is a typed `TypeError`,
|
|
5483
|
+
* not a guessed PID or a fabricated OBX.
|
|
5484
|
+
*
|
|
5485
|
+
* Spec traceability: HL7 v2 Ch. 7 (ORU: MSH/PID/OBR/OBX), Ch. 2 datatypes.
|
|
5486
|
+
* Zero runtime deps.
|
|
5487
|
+
*/
|
|
5488
|
+
|
|
5489
|
+
/**
|
|
5490
|
+
* Typed PID content for {@link buildOru}. Reuses {@link AdtPatient}: the
|
|
5491
|
+
* patient-identification shape is identical across message families.
|
|
5492
|
+
*/
|
|
5493
|
+
type OruPatient = AdtPatient;
|
|
5494
|
+
/** Typed OBR (observation request / order) content for {@link buildOru}. */
|
|
5495
|
+
interface OruOrder {
|
|
5496
|
+
/** OBR-1 Set ID. */
|
|
5497
|
+
readonly setId?: string;
|
|
5498
|
+
/** OBR-2 Placer Order Number. */
|
|
5499
|
+
readonly placerOrderNumber?: string;
|
|
5500
|
+
/** OBR-3 Filler Order Number. */
|
|
5501
|
+
readonly fillerOrderNumber?: string;
|
|
5502
|
+
/** OBR-4 Universal Service Identifier. */
|
|
5503
|
+
readonly universalServiceId?: CWE;
|
|
5504
|
+
/** OBR-7 Observation Date/Time. */
|
|
5505
|
+
readonly observationDateTime?: TS | string;
|
|
5506
|
+
/** OBR-16 Ordering Provider. */
|
|
5507
|
+
readonly orderingProvider?: XCN | readonly XCN[];
|
|
5508
|
+
/** OBR-25 Result Status (e.g. `"F"` final, `"P"` preliminary, `"C"` corrected). */
|
|
5509
|
+
readonly resultStatus?: string;
|
|
5510
|
+
}
|
|
5511
|
+
/** Typed OBX (observation / result) content for {@link buildOru}. */
|
|
5512
|
+
interface OruObservation {
|
|
5513
|
+
/** OBX-1 Set ID. */
|
|
5514
|
+
readonly setId?: string;
|
|
5515
|
+
/** OBX-2 Value Type (e.g. `"NM"`, `"ST"`, `"CE"`, `"TX"`). */
|
|
5516
|
+
readonly valueType?: string;
|
|
5517
|
+
/** OBX-3 Observation Identifier. */
|
|
5518
|
+
readonly identifier?: CWE;
|
|
5519
|
+
/** OBX-5 Observation Value (emitted verbatim: the caller owns its formatting). */
|
|
5520
|
+
readonly value?: string;
|
|
5521
|
+
/** OBX-6 Units. */
|
|
5522
|
+
readonly units?: CWE;
|
|
5523
|
+
/** OBX-7 References Range. */
|
|
5524
|
+
readonly referenceRange?: string;
|
|
5525
|
+
/** OBX-8 Abnormal Flags (e.g. `"H"`, `"L"`, `"N"`). */
|
|
5526
|
+
readonly abnormalFlags?: string;
|
|
5527
|
+
/** OBX-11 Observation Result Status (e.g. `"F"` final, `"P"` preliminary). */
|
|
5528
|
+
readonly observationResultStatus?: string;
|
|
5529
|
+
/** OBX-14 Date/Time of the Observation. */
|
|
5530
|
+
readonly observationDateTime?: TS | string;
|
|
5531
|
+
}
|
|
5532
|
+
/** Input for {@link buildOru}: the MSH envelope plus the typed segment bodies. */
|
|
5533
|
+
interface BuildOruInit extends MessageEnvelope {
|
|
5534
|
+
/** PID content. **Required**: never fabricated. */
|
|
5535
|
+
readonly patient: OruPatient;
|
|
5536
|
+
/** OBR content. Optional; an (empty) OBR is emitted regardless so the result group is well-formed. */
|
|
5537
|
+
readonly order?: OruOrder;
|
|
5538
|
+
/** OBX content. **Required, non-empty**: an ORU with no result is a typed error. */
|
|
5539
|
+
readonly observations: readonly OruObservation[];
|
|
5540
|
+
}
|
|
5541
|
+
/**
|
|
5542
|
+
* Build a spec-clean ORU^R01 observation-result message from typed inputs.
|
|
5543
|
+
*
|
|
5544
|
+
* The result is a real {@link Hl7Message}: `msg.toString()` serialises it,
|
|
5545
|
+
* `parseHL7(msg.toString())` round-trips with zero warnings, and
|
|
5546
|
+
* `msg.observations()` reads back the results supplied.
|
|
5547
|
+
*
|
|
5548
|
+
* @param init - the MSH envelope + typed PID/OBR/OBX content. `patient` and a
|
|
5549
|
+
* non-empty `observations` list are required.
|
|
5550
|
+
* @throws TypeError when `patient` is absent or `observations` is empty.
|
|
5551
|
+
*
|
|
5552
|
+
* @example
|
|
5553
|
+
* ```ts
|
|
5554
|
+
* import { buildOru, parseHL7 } from "@cosyte/hl7";
|
|
5555
|
+
* const msg = buildOru({
|
|
5556
|
+
* sendingApp: "LAB",
|
|
5557
|
+
* receivingApp: "EHR",
|
|
5558
|
+
* patient: { identifiers: { idNumber: "MRN001", identifierTypeCode: "MR" },
|
|
5559
|
+
* name: { familyName: "Test", givenName: "Ann" } },
|
|
5560
|
+
* order: { universalServiceId: { identifier: "CBC", text: "Complete Blood Count", nameOfCodingSystem: "L" },
|
|
5561
|
+
* resultStatus: "F" },
|
|
5562
|
+
* observations: [
|
|
5563
|
+
* { setId: "1", valueType: "NM",
|
|
5564
|
+
* identifier: { identifier: "WBC", text: "White Blood Cells", nameOfCodingSystem: "LN" },
|
|
5565
|
+
* value: "7.2", units: { identifier: "10*3/uL" }, observationResultStatus: "F" },
|
|
5566
|
+
* ],
|
|
5567
|
+
* });
|
|
5568
|
+
* const round = parseHL7(msg.toString());
|
|
5569
|
+
* round.warnings.length; // 0
|
|
5570
|
+
* round.observations()[0]?.value; // "7.2"
|
|
5571
|
+
* ```
|
|
5572
|
+
*/
|
|
5573
|
+
declare function buildOru(init: BuildOruInit): Hl7Message;
|
|
5574
|
+
|
|
4787
5575
|
/**
|
|
4788
5576
|
* Frozen HL7 control-vocabulary tables for ACK generation (Phase C). These
|
|
4789
|
-
* are read-only enums sourced from the HL7 v2 standard
|
|
5577
|
+
* are read-only enums sourced from the HL7 v2 standard: `@cosyte/hl7` is the
|
|
4790
5578
|
* single owner of this vocabulary; siblings (notably `@cosyte/mllp`) consume
|
|
4791
5579
|
* `buildAck` rather than re-declaring these codes.
|
|
4792
5580
|
*
|
|
4793
|
-
* Provenance (re-verify before editing
|
|
4794
|
-
* - **Table 0008** Acknowledgment code
|
|
4795
|
-
* - **Table 0357** Message error condition codes
|
|
4796
|
-
* - **Table 0516** Error severity
|
|
4797
|
-
* - **Table 0155** Accept/application acknowledgment conditions (MSH-15/16)
|
|
5581
|
+
* Provenance (re-verify before editing: HL7 tables move):
|
|
5582
|
+
* - **Table 0008** Acknowledgment code: https://terminology.hl7.org/CodeSystem-v2-0008.html
|
|
5583
|
+
* - **Table 0357** Message error condition codes: https://terminology.hl7.org/CodeSystem-v2-0357.html
|
|
5584
|
+
* - **Table 0516** Error severity: HL7 v2 Chapter 2 §2.14.5 (ERR-4)
|
|
5585
|
+
* - **Table 0155** Accept/application acknowledgment conditions (MSH-15/16),
|
|
4798
5586
|
* HL7 v2 Chapter 2 §2.14.9.15–16
|
|
4799
5587
|
*
|
|
4800
5588
|
* Zero runtime deps.
|
|
4801
5589
|
*/
|
|
4802
5590
|
/**
|
|
4803
|
-
* HL7 Table 0008
|
|
5591
|
+
* HL7 Table 0008: Acknowledgment code (MSA-1). The two acknowledgment
|
|
4804
5592
|
* **vocabularies**:
|
|
4805
5593
|
* - **original** mode: `AA` Application Accept · `AE` Application Error ·
|
|
4806
5594
|
* `AR` Application Reject.
|
|
@@ -4838,14 +5626,14 @@ type AckCode = (typeof ACK_CODES)[keyof typeof ACK_CODES];
|
|
|
4838
5626
|
*/
|
|
4839
5627
|
declare function isPositiveAck(code: string | undefined): boolean;
|
|
4840
5628
|
/**
|
|
4841
|
-
* Downgrade a positive acknowledgment code to its matching error code
|
|
5629
|
+
* Downgrade a positive acknowledgment code to its matching error code,
|
|
4842
5630
|
* `AA` → `AE` (original mode), `CA` → `CE` (enhanced mode). Every other code
|
|
4843
5631
|
* passes through unchanged.
|
|
4844
5632
|
*
|
|
4845
5633
|
* This is the **single upstream source of truth** for the fail-safe downgrade
|
|
4846
5634
|
* pair: `buildAck` applies it when the inbound carries no MSH-10 correlation
|
|
4847
5635
|
* id, and `@cosyte/mllp`'s `ack-from-hl7` adapter applies it when the inbound
|
|
4848
|
-
* cannot be parsed at all
|
|
5636
|
+
* cannot be parsed at all: neither ever fabricates an unverifiable positive
|
|
4849
5637
|
* acknowledgment, and neither carries its own copy of the mapping.
|
|
4850
5638
|
*
|
|
4851
5639
|
* @example
|
|
@@ -4858,7 +5646,7 @@ declare function isPositiveAck(code: string | undefined): boolean;
|
|
|
4858
5646
|
*/
|
|
4859
5647
|
declare function downgradePositiveAck(code: AckCode): AckCode;
|
|
4860
5648
|
/**
|
|
4861
|
-
* HL7 Table 0516
|
|
5649
|
+
* HL7 Table 0516: Error severity (ERR-4). A v2.5+ construct (ERR was
|
|
4862
5650
|
* structured differently in v2.3.1).
|
|
4863
5651
|
*
|
|
4864
5652
|
* @example
|
|
@@ -4878,13 +5666,13 @@ declare const ERR_SEVERITIES: {
|
|
|
4878
5666
|
/** Error-severity union (HL7 Table 0516, ERR-4). */
|
|
4879
5667
|
type ErrSeverity = (typeof ERR_SEVERITIES)[keyof typeof ERR_SEVERITIES];
|
|
4880
5668
|
/**
|
|
4881
|
-
* HL7 Table 0357
|
|
5669
|
+
* HL7 Table 0357: Message error condition codes (ERR-3.1 → ERR-3.2 text).
|
|
4882
5670
|
* Frozen read-only map of `code → standard display text`. The code system
|
|
4883
5671
|
* name emitted in ERR-3.3 is {@link ERR_CONDITION_CODE_SYSTEM}.
|
|
4884
5672
|
*
|
|
4885
5673
|
* Codes `104` (value too long) and `105` (table value not found) are v2.7+
|
|
4886
5674
|
* additions; the rest are present from v2.5. `buildAck` emits whatever code it
|
|
4887
|
-
* is told
|
|
5675
|
+
* is told (it never invents a condition) and looks up the display text here.
|
|
4888
5676
|
*
|
|
4889
5677
|
* @example
|
|
4890
5678
|
* ```ts
|
|
@@ -4896,7 +5684,7 @@ declare const ERR_CONDITION_CODES: Readonly<Record<string, string>>;
|
|
|
4896
5684
|
/** Code-system name emitted in ERR-3.3 for Table 0357 condition codes. */
|
|
4897
5685
|
declare const ERR_CONDITION_CODE_SYSTEM = "HL70357";
|
|
4898
5686
|
/**
|
|
4899
|
-
* HL7 Table 0155
|
|
5687
|
+
* HL7 Table 0155: Accept/application acknowledgment conditions (MSH-15 /
|
|
4900
5688
|
* MSH-16): `AL` Always · `NE` Never · `ER` Error/reject conditions only ·
|
|
4901
5689
|
* `SU` Successful completion only. Exposed read-only for adapters that
|
|
4902
5690
|
* surface the inbound sender's stated acknowledgment expectations.
|
|
@@ -4922,12 +5710,12 @@ type AckCondition = (typeof ACK_CONDITIONS)[keyof typeof ACK_CONDITIONS];
|
|
|
4922
5710
|
type AckMode = "original" | "enhanced";
|
|
4923
5711
|
|
|
4924
5712
|
/**
|
|
4925
|
-
* `buildAck
|
|
5713
|
+
* `buildAck`: generate a spec-clean HL7 v2 acknowledgment (ACK) message from
|
|
4926
5714
|
* an inbound message (Phase C). The boundary with `@cosyte/mllp` is resolved
|
|
4927
5715
|
* (hl7 roadmap §10.1): **hl7 owns ACK *content*** (this file + the control
|
|
4928
5716
|
* tables); mllp owns *policy/timing* (the commit contract) and *framing*.
|
|
4929
5717
|
*
|
|
4930
|
-
* `buildAck` is **mechanical
|
|
5718
|
+
* `buildAck` is **mechanical**: it builds the disposition it is *told* via
|
|
4931
5719
|
* `options.code`; it never decides accept-vs-reject. The one safety override is
|
|
4932
5720
|
* the fail-safe below: it refuses to fabricate a positive `AA`/`CA` for an
|
|
4933
5721
|
* inbound message that carries no MSH-10 correlation id.
|
|
@@ -4941,7 +5729,7 @@ type AckMode = "original" | "enhanced";
|
|
|
4941
5729
|
|
|
4942
5730
|
/**
|
|
4943
5731
|
* One ERR segment's worth of error detail for `buildAck`. Carries **codes and
|
|
4944
|
-
* locations only
|
|
5732
|
+
* locations only: never echoed PHI values** (the phi-redaction-review gate
|
|
4945
5733
|
* binds this contract).
|
|
4946
5734
|
*
|
|
4947
5735
|
* @example
|
|
@@ -4958,7 +5746,7 @@ interface AckErrorDetail {
|
|
|
4958
5746
|
* HL7 Table 0357 message error condition code (ERR-3.1). Defaults to `"207"`
|
|
4959
5747
|
* (Application internal error). The standard display text is looked up from
|
|
4960
5748
|
* Table 0357 and emitted in ERR-3.2; unknown codes emit with empty text
|
|
4961
|
-
* (the code is preserved verbatim
|
|
5749
|
+
* (the code is preserved verbatim: never dropped).
|
|
4962
5750
|
*/
|
|
4963
5751
|
readonly conditionCode?: string;
|
|
4964
5752
|
/** HL7 Table 0516 severity (ERR-4). Defaults to `"E"` (Error). */
|
|
@@ -4966,7 +5754,7 @@ interface AckErrorDetail {
|
|
|
4966
5754
|
/**
|
|
4967
5755
|
* Error location (ERR-2, an HL7 ERL). A structural path such as `"PID^1^5"`
|
|
4968
5756
|
* (segment id ^ segment sequence ^ field position). **Must not contain a
|
|
4969
|
-
* patient data value
|
|
5757
|
+
* patient data value**: locations point at *where*, never *what*.
|
|
4970
5758
|
*/
|
|
4971
5759
|
readonly location?: string;
|
|
4972
5760
|
}
|
|
@@ -4983,7 +5771,7 @@ interface BuildAckOptions {
|
|
|
4983
5771
|
/**
|
|
4984
5772
|
* The acknowledgment disposition to emit in MSA-1 (HL7 Table 0008). One of
|
|
4985
5773
|
* `AA`/`AE`/`AR` (original) or `CA`/`CE`/`CR` (enhanced accept-level).
|
|
4986
|
-
* Required
|
|
5774
|
+
* Required: `buildAck` builds the disposition it is told. An unknown code
|
|
4987
5775
|
* is a programming error and throws `TypeError`.
|
|
4988
5776
|
*/
|
|
4989
5777
|
readonly code: AckCode;
|
|
@@ -4995,7 +5783,7 @@ interface BuildAckOptions {
|
|
|
4995
5783
|
/**
|
|
4996
5784
|
* Optional explicit acknowledgment mode. When omitted it is derived from the
|
|
4997
5785
|
* inbound MSH-15/16 via {@link detectAckMode}. `buildAck` emits `code`
|
|
4998
|
-
* verbatim regardless of mode
|
|
5786
|
+
* verbatim regardless of mode: this field is advisory metadata for adapters
|
|
4999
5787
|
* (e.g. `@cosyte/mllp`'s commit-policy layer) that need the detected mode.
|
|
5000
5788
|
*/
|
|
5001
5789
|
readonly mode?: AckMode;
|
|
@@ -5005,7 +5793,7 @@ interface BuildAckOptions {
|
|
|
5005
5793
|
* acknowledgment type) and MSH-16 (application acknowledgment type), per HL7 v2
|
|
5006
5794
|
* Chapter 2 §2.9: **original** when *both* are absent/empty; **enhanced** when
|
|
5007
5795
|
* *either* is present. This split is spec-exact (unlike the disposition mapping,
|
|
5008
|
-
* which has no single correct model
|
|
5796
|
+
* which has no single correct model: see the package README).
|
|
5009
5797
|
*
|
|
5010
5798
|
* @example
|
|
5011
5799
|
* ```ts
|
|
@@ -5018,33 +5806,33 @@ declare function detectAckMode(inbound: Hl7Message): AckMode;
|
|
|
5018
5806
|
* Build a spec-clean ACK (`MSH` + `MSA` [+ `ERR`…]) responding to `inbound`.
|
|
5019
5807
|
*
|
|
5020
5808
|
* Behavior:
|
|
5021
|
-
* - **MSH
|
|
5809
|
+
* - **MSH**: sender/receiver are swapped (inbound MSH-5/6 → ACK MSH-3/4;
|
|
5022
5810
|
* inbound MSH-3/4 → ACK MSH-5/6); MSH-7 is the current UTC time; MSH-9 is
|
|
5023
5811
|
* `ACK` (with the inbound trigger event echoed as `ACK^<trigger>^ACK` when
|
|
5024
5812
|
* present); MSH-10 is a freshly generated control id; MSH-11 (processing id)
|
|
5025
5813
|
* and MSH-12 (version) echo the inbound values.
|
|
5026
|
-
* - **MSA
|
|
5027
|
-
* (the raw field structure is carried over whole
|
|
5814
|
+
* - **MSA**: MSA-1 = `code`; **MSA-2 echoes the full inbound MSH-10 field**
|
|
5815
|
+
* (the raw field structure is carried over whole: a vendor-quirk id like
|
|
5028
5816
|
* `ID^X` is never truncated to its first component). The echo carries the
|
|
5029
5817
|
* inbound field's escape-fidelity overlay (HL7-ESC), so an id bearing a hex
|
|
5030
5818
|
* escape (`ID\X41\Q`) or a preserved escape (`\H\`) echoes **byte-verbatim**,
|
|
5031
|
-
* not canonicalized
|
|
5819
|
+
* not canonicalized: exactly the bytes the sender put on the wire, which is
|
|
5032
5820
|
* what MSA-2 correlation compares. The only structural transform is
|
|
5033
5821
|
* trailing-empty canonicalization (D-02). (A sender that used *custom*
|
|
5034
5822
|
* encoding characters is **re-delimited spec-cleanly**: the overlay carries
|
|
5035
5823
|
* the sender's raw bytes in the sender's alphabet, so echoing them verbatim
|
|
5036
5824
|
* under the ACK's default alphabet would corrupt the field's structure and
|
|
5037
|
-
* break correlation
|
|
5825
|
+
* break correlation: the overlay is therefore bypassed and the decoded id is
|
|
5038
5826
|
* re-escaped under default, which re-parses back to the same control id.
|
|
5039
5827
|
* Default-delimiter senders, the norm, keep the byte-exact overlay echo.)
|
|
5040
|
-
* - **ERR
|
|
5828
|
+
* - **ERR**: one segment per supplied {@link AckErrorDetail}: ERR-2 location
|
|
5041
5829
|
* (when given), ERR-3 the Table 0357 condition code as a CWE
|
|
5042
5830
|
* (`code^text^HL70357`), ERR-4 the Table 0516 severity.
|
|
5043
5831
|
*
|
|
5044
5832
|
* **Fail-safe (roadmap §Phase C).** If the inbound message has no MSH-10, the
|
|
5045
5833
|
* ACK cannot be correlated. `buildAck` then leaves MSA-2 empty and, if a
|
|
5046
5834
|
* positive accept (`AA`/`CA`) was requested, **downgrades it** to the matching
|
|
5047
|
-
* error code (`AE`/`CE`)
|
|
5835
|
+
* error code (`AE`/`CE`): it never fabricates an unverifiable positive ACK.
|
|
5048
5836
|
* The returned message carries an `ACK_NO_CORRELATION_ID` warning. (This is the
|
|
5049
5837
|
* inbound-side complement to `@cosyte/mllp`'s "no commit ⇒ never AA".)
|
|
5050
5838
|
*
|
|
@@ -5063,16 +5851,16 @@ declare function detectAckMode(inbound: Hl7Message): AckMode;
|
|
|
5063
5851
|
declare function buildAck(inbound: Hl7Message, options: BuildAckOptions): Hl7Message;
|
|
5064
5852
|
|
|
5065
5853
|
/**
|
|
5066
|
-
* `interpretAck
|
|
5854
|
+
* `interpretAck`: read-side helper that interprets an inbound HL7 v2
|
|
5067
5855
|
* acknowledgment (ACK) message into a typed {@link Acknowledgment} view
|
|
5068
5856
|
* (Phase C). The mechanical inverse of `buildAck`: it reads MSA-1 (code),
|
|
5069
5857
|
* MSA-2 (the correlated control id), and every ERR segment.
|
|
5070
5858
|
*
|
|
5071
|
-
* Fail-safe: an absent or unrecognized MSA-1 yields `accepted: false
|
|
5859
|
+
* Fail-safe: an absent or unrecognized MSA-1 yields `accepted: false`: the
|
|
5072
5860
|
* interpreter NEVER reports an acknowledgment as accepted on ambiguous input.
|
|
5073
5861
|
*
|
|
5074
5862
|
* Spec traceability: HL7 v2 Chapter 2 §2.14.8 (MSA), §2.14.5 (ERR). Codes are
|
|
5075
|
-
* surfaced verbatim (Table 0008 / 0357 / 0516)
|
|
5863
|
+
* surfaced verbatim (Table 0008 / 0357 / 0516): no validation or lookup.
|
|
5076
5864
|
*
|
|
5077
5865
|
* Zero runtime deps.
|
|
5078
5866
|
*/
|
|
@@ -5080,18 +5868,18 @@ declare function buildAck(inbound: Hl7Message, options: BuildAckOptions): Hl7Mes
|
|
|
5080
5868
|
/**
|
|
5081
5869
|
* One ERR segment interpreted from an ACK. All fields are surfaced verbatim
|
|
5082
5870
|
* (no table lookup) and OMITTED when the underlying component is absent
|
|
5083
|
-
* (exactOptionalPropertyTypes). Codes/locations only
|
|
5871
|
+
* (exactOptionalPropertyTypes). Codes/locations only: never PHI.
|
|
5084
5872
|
*/
|
|
5085
5873
|
interface AckErrorEntry {
|
|
5086
|
-
/** ERR-3.1
|
|
5874
|
+
/** ERR-3.1: HL7 Table 0357 condition code. */
|
|
5087
5875
|
readonly conditionCode?: string;
|
|
5088
|
-
/** ERR-3.2
|
|
5876
|
+
/** ERR-3.2: condition code display text. */
|
|
5089
5877
|
readonly conditionText?: string;
|
|
5090
|
-
/** ERR-3.3
|
|
5878
|
+
/** ERR-3.3: condition code system name (e.g. `HL70357`). */
|
|
5091
5879
|
readonly conditionCodeSystem?: string;
|
|
5092
|
-
/** ERR-4
|
|
5880
|
+
/** ERR-4: HL7 Table 0516 severity (`I`/`W`/`E`). */
|
|
5093
5881
|
readonly severity?: string;
|
|
5094
|
-
/** ERR-2
|
|
5882
|
+
/** ERR-2: error location (an HL7 ERL), surfaced verbatim. */
|
|
5095
5883
|
readonly location?: string;
|
|
5096
5884
|
}
|
|
5097
5885
|
/**
|
|
@@ -5115,7 +5903,7 @@ interface Acknowledgment {
|
|
|
5115
5903
|
readonly code?: string;
|
|
5116
5904
|
/**
|
|
5117
5905
|
* MSA-2 message control id (the correlated inbound MSH-10), surfaced as the
|
|
5118
|
-
* field's **canonical wire text** (`Field.text`)
|
|
5906
|
+
* field's **canonical wire text** (`Field.text`): the whole field,
|
|
5119
5907
|
* delimiters included, never truncated to the first component. Note this is
|
|
5120
5908
|
* the *re-escaped* form: a properly-escaped id arrives as its wire bytes
|
|
5121
5909
|
* (`ID\S\X`), and an HL7 explicit-null MSA-2 surfaces as the literal
|
|
@@ -5147,7 +5935,7 @@ interface Acknowledgment {
|
|
|
5147
5935
|
declare function interpretAck(msg: Hl7Message): Acknowledgment;
|
|
5148
5936
|
|
|
5149
5937
|
/**
|
|
5150
|
-
* `defineProfile()
|
|
5938
|
+
* `defineProfile()`: public factory for building `Profile` objects with
|
|
5151
5939
|
* validation + `describe()` attached (PROF-01, PROF-04, PROF-05).
|
|
5152
5940
|
*
|
|
5153
5941
|
* Wave 1 (Plan 06-01) ships the single-profile (no-extends) path;
|
|
@@ -5197,7 +5985,7 @@ interface DefineProfileOptions {
|
|
|
5197
5985
|
* unknown top-level keys with typo hints (D-07), and missing/empty
|
|
5198
5986
|
* name.
|
|
5199
5987
|
*
|
|
5200
|
-
* Wave 1 (Plan 06-01) ships the no-extends path
|
|
5988
|
+
* Wave 1 (Plan 06-01) ships the no-extends path: `opts.extends` is
|
|
5201
5989
|
* ACCEPTED but IGNORED, resulting in `lineage === [opts.name]`
|
|
5202
5990
|
* regardless. Wave 2 (Plan 06-02) adds full lineage computation + merge
|
|
5203
5991
|
* semantics.
|
|
@@ -5223,7 +6011,7 @@ declare function defineProfile(opts: DefineProfileOptions): Profile;
|
|
|
5223
6011
|
|
|
5224
6012
|
/**
|
|
5225
6013
|
* Process-scoped default profile management (PROF-08 / D-18). This module
|
|
5226
|
-
* holds the first mutable module-scoped state in the codebase
|
|
6014
|
+
* holds the first mutable module-scoped state in the codebase: an
|
|
5227
6015
|
* intentional trade-off documented in PROJECT.md Key Decisions:
|
|
5228
6016
|
* `setDefaultProfile` EXISTS but is DISCOURAGED. It is scoped to the
|
|
5229
6017
|
* current Node process, NOT shared across workers, NOT a symbol-keyed
|
|
@@ -5245,7 +6033,7 @@ declare function defineProfile(opts: DefineProfileOptions): Profile;
|
|
|
5245
6033
|
* arg of `parseHL7` (D-20): customSegments, dateFormats, onWarning chain,
|
|
5246
6034
|
* and profile attribution all apply the same way.
|
|
5247
6035
|
*
|
|
5248
|
-
* Explicit args ALWAYS win
|
|
6036
|
+
* Explicit args ALWAYS win: `parseHL7(raw, myProfile)` uses `myProfile`
|
|
5249
6037
|
* regardless of the default; `parseHL7(raw, { profile: null })` opts out
|
|
5250
6038
|
* of the default for a single call without changing the registered
|
|
5251
6039
|
* default.
|
|
@@ -5274,7 +6062,7 @@ declare function defineProfile(opts: DefineProfileOptions): Profile;
|
|
|
5274
6062
|
declare function setDefaultProfile(profile: Profile | null): void;
|
|
5275
6063
|
/**
|
|
5276
6064
|
* Return the current default profile, or `undefined` if none is
|
|
5277
|
-
* registered. Consistent with `msg.profile` convention
|
|
6065
|
+
* registered. Consistent with `msg.profile` convention: `undefined`
|
|
5278
6066
|
* rather than `null`.
|
|
5279
6067
|
*
|
|
5280
6068
|
* @example
|
|
@@ -5292,7 +6080,7 @@ declare function getDefaultProfile(): Profile | undefined;
|
|
|
5292
6080
|
* `ZCA` (care team membership). BIP-04.
|
|
5293
6081
|
*
|
|
5294
6082
|
* The AM/PM meridian variant (`MM/DD/YYYY HH:mm AM/PM`) is intentionally
|
|
5295
|
-
* NOT shipped
|
|
6083
|
+
* NOT shipped: the library's format-token set does not include AM/PM
|
|
5296
6084
|
* (`SUPPORTED_DATE_TOKENS` = YYYY/MM/DD/HH/mm/ss/SSSS). Consumers needing
|
|
5297
6085
|
* meridian support can extend this profile or supply their own format
|
|
5298
6086
|
* handler.
|
|
@@ -5350,7 +6138,7 @@ declare const cerner: Profile;
|
|
|
5350
6138
|
* feeds, plus two Z-segments: `ZDP` (department context) and `ZRS`
|
|
5351
6139
|
* (result status). BIP-01.
|
|
5352
6140
|
*
|
|
5353
|
-
* Authored from the public `defineProfile()` API
|
|
6141
|
+
* Authored from the public `defineProfile()` API: zero privileged
|
|
5354
6142
|
* internal coupling. Consumers extend this profile via
|
|
5355
6143
|
* `defineProfile({ name: '...', extends: profiles.epic, ... })`.
|
|
5356
6144
|
*
|
|
@@ -5380,7 +6168,7 @@ declare const epic: Profile;
|
|
|
5380
6168
|
* `YYYY-MM-DD` (ISO date-only). Two Z-segments: `ZLB` (lab-override
|
|
5381
6169
|
* flags) and `ZNT` (lab note). BIP-05.
|
|
5382
6170
|
*
|
|
5383
|
-
* The canonical HL7 format `YYYYMMDDHHmmss` is NOT shipped here
|
|
6171
|
+
* The canonical HL7 format `YYYYMMDDHHmmss` is NOT shipped here: it's
|
|
5384
6172
|
* already the parser's primary (zero-warning) match for TS/DTM.
|
|
5385
6173
|
*
|
|
5386
6174
|
* @example
|
|
@@ -5404,22 +6192,54 @@ declare const epic: Profile;
|
|
|
5404
6192
|
declare const genericLab: Profile;
|
|
5405
6193
|
|
|
5406
6194
|
/**
|
|
5407
|
-
*
|
|
5408
|
-
* (`
|
|
5409
|
-
*
|
|
5410
|
-
*
|
|
6195
|
+
* MEDITECH (MAGIC / 6.x / Expanse) HL7 quirks. Ships minute-precision
|
|
6196
|
+
* timestamps (`YYYYMMDDHHMM`, no seconds) and two DFT charge-capture
|
|
6197
|
+
* Z-segments: `ZF1` (provider-encounter copay data) and `ZF2`
|
|
6198
|
+
* (encounter-procedure data). BIP-03.
|
|
6199
|
+
*
|
|
6200
|
+
* Grounded in **publicly downloadable** MEDITECH interface specifications
|
|
6201
|
+
* (ADR 0018: public specs count as real artifacts), not invented:
|
|
6202
|
+
*
|
|
6203
|
+
* - The **minute-precision** date/time format is confirmed by *two*
|
|
6204
|
+
* public MEDITECH specs: "MEDITECH Admissions and Registration to Other
|
|
6205
|
+
* Vendor Ancillary" (Version 2.4, © 2021 Medical Information Technology,
|
|
6206
|
+
* Inc.) ("Date and Time in Admissions. Format is YYYYMMDDHHMM") and
|
|
6207
|
+
* "MEDITECH Ancillary Charges (LAB/PHA/ITS/IDM)" (Version 2.1, © 2021),
|
|
6208
|
+
* whose MSH-7 is length **12** (= `YYYYMMDDHHMM`) and whose EVN-2/PID-7
|
|
6209
|
+
* are documented "Format is YYYYMMDDHHMM". Minute precision is
|
|
6210
|
+
* HL7-conformant (TS/DTM permit variable precision), so declaring it here
|
|
6211
|
+
* is not about accepting an illegal value: it records MEDITECH's
|
|
6212
|
+
* characteristic no-seconds convention so date resolution across a
|
|
6213
|
+
* MEDITECH feed is explicit rather than incidental.
|
|
6214
|
+
* - `ZF1` and `ZF2` are the DFT (charge) Z-segments defined verbatim in
|
|
6215
|
+
* the Ancillary Charges spec (Version 2.1): `ZF1` = "PROVIDER ENCOUNTER
|
|
6216
|
+
* COPAY DATA" (ZF1-1 PROVIDER ENCOUNTER, ZF1-2 MIS SERVICE GROUP, ZF1-3
|
|
6217
|
+
* SERVICE GROUP COPAY, ZF1-4 VISIT COPAY, ZF1-5 COPAY MINIMUM, ZF1-6
|
|
6218
|
+
* COPAY MAXIMUM); `ZF2` = "ENCOUNTER PROCEDURE DATA" (ZF2-1 SET ID, ZF2-2
|
|
6219
|
+
* PROVIDER ENCOUNTER, ZF2-3 ENCOUNTER DATE, ZF2-4 ENCOUNTER PROCEDURE,
|
|
6220
|
+
* ZF2-5 ENCOUNTER PROCEDURE QUANTITY, ZF2-6 ENCOUNTER PROCEDURE CHARGE,
|
|
6221
|
+
* ZF2-7 PRV PROCEDURE AMOUNT PAID, ZF2-8 PRV PROCEDURE AMOUNT DUE).
|
|
6222
|
+
*
|
|
6223
|
+
* (This replaced an earlier `ZVI` "visit info" segment that was a
|
|
6224
|
+
* community-sourced prior with no citable public grounding: HL7-I
|
|
6225
|
+
* re-grounding, ADR 0018. Field positions here are transcribed directly
|
|
6226
|
+
* from the spec's segment tables.)
|
|
6227
|
+
*
|
|
6228
|
+
* Authored from the public `defineProfile()` API: zero privileged
|
|
6229
|
+
* internal coupling. Consumers extend this profile via
|
|
6230
|
+
* `defineProfile({ name: '...', extends: profiles.meditech, ... })`.
|
|
5411
6231
|
*
|
|
5412
6232
|
* @example
|
|
5413
6233
|
* ```ts
|
|
5414
6234
|
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
5415
6235
|
* const msg = parseHL7(raw, profiles.meditech);
|
|
5416
|
-
* const
|
|
5417
|
-
* console.log(
|
|
6236
|
+
* const zf1 = msg.allSegments().find((s) => s.type === "ZF1");
|
|
6237
|
+
* console.log(zf1?.get("visitCopay")?.value);
|
|
5418
6238
|
* ```
|
|
5419
6239
|
*/
|
|
5420
6240
|
/**
|
|
5421
|
-
* Built-in
|
|
5422
|
-
* rationale; use via `parseHL7(raw, profiles.meditech)`.
|
|
6241
|
+
* Built-in MEDITECH MAGIC/6.x/Expanse profile (BIP-03). See file header for
|
|
6242
|
+
* rationale and spec citations; use via `parseHL7(raw, profiles.meditech)`.
|
|
5423
6243
|
*
|
|
5424
6244
|
* @example
|
|
5425
6245
|
* ```ts
|
|
@@ -5429,22 +6249,173 @@ declare const genericLab: Profile;
|
|
|
5429
6249
|
*/
|
|
5430
6250
|
declare const meditech: Profile;
|
|
5431
6251
|
|
|
6252
|
+
/**
|
|
6253
|
+
* Philips Vue PACS ("IS Link") imaging HL7 interface quirks. The Vue PACS
|
|
6254
|
+
* IS Link component receives RIS-side ORM/ADT feeds and carries its
|
|
6255
|
+
* PACS-specific data in six custom Z-segments the HL7 standard does not
|
|
6256
|
+
* define: one per *filler role*:
|
|
6257
|
+
*
|
|
6258
|
+
* - **`ZDS`**: Order Filler: the DICOM **Study Instance UID** (field 1,
|
|
6259
|
+
* first component), the RIS↔PACS bridge (as in the IHE Radiology TF and
|
|
6260
|
+
* the `visage` profile); IS Link uses it to update the accession number.
|
|
6261
|
+
* - **`ZLK`**: Order Filler: study/order **linking** ids (external
|
|
6262
|
+
* workitem id, order link id) that group studies and orders.
|
|
6263
|
+
* - **`ZAO`**: Order Filler: order **additional details** (a base64 XML
|
|
6264
|
+
* key/value blob) plus modality, body part, transfer/acquisition status,
|
|
6265
|
+
* technician + radiologist names/ids, and vendor "custom" string/number/
|
|
6266
|
+
* date slots.
|
|
6267
|
+
* - **`ZEB`**: Patient Filler: an **encrypted** patient-info blob (base64).
|
|
6268
|
+
* - **`ZAP`**: Patient Filler: patient **additional details** (base64 XML)
|
|
6269
|
+
* plus patient "custom" string/number/date slots.
|
|
6270
|
+
* - **`ZAV`**: Visit Filler: visit **additional details** (base64 XML).
|
|
6271
|
+
*
|
|
6272
|
+
* Grounded in the public **Vue PACS 12.2.8 HL7 Interface Specifications**
|
|
6273
|
+
* (Philips, document HA1669 Rev A), §§5.11–5.16: a publicly downloadable
|
|
6274
|
+
* vendor interface spec, not an invented quirk (ADR 0018). The spec's
|
|
6275
|
+
* `TS` timestamps are the HL7-native `YYYYMMDDHHMMSS` form (§2.6), so this
|
|
6276
|
+
* profile declares **no** custom `dateFormats`: only the Z-segments.
|
|
6277
|
+
*
|
|
6278
|
+
* Field positions are transcribed verbatim from the spec's segment tables,
|
|
6279
|
+
* including two of its own gaps/quirks: `ZAO` has **no field 7** (the table
|
|
6280
|
+
* jumps 6 → 8) and `ZAP` has **no field 2** (jumps 1 → 3); the three
|
|
6281
|
+
* `ZAO`/`ZAP` "Custom Date" rows are all printed "…Date 1" in the spec (a
|
|
6282
|
+
* transcription slip) but sit at consecutive positions, so they are named
|
|
6283
|
+
* `…CustomDate1/2/3` here to keep each field name unique.
|
|
6284
|
+
*
|
|
6285
|
+
* `ZEB` holds *encrypted* patient information and `ZAP`/`ZAV` carry
|
|
6286
|
+
* patient/visit blobs: naming the field is exactly how a consumer learns
|
|
6287
|
+
* that segment is PHI-bearing and must not be logged. This profile only
|
|
6288
|
+
* *names* the fields; it never decodes, decrypts, or rewrites them.
|
|
6289
|
+
*
|
|
6290
|
+
* Authored from the public `defineProfile()` API: zero privileged internal
|
|
6291
|
+
* coupling. Consumers extend this profile via
|
|
6292
|
+
* `defineProfile({ name: '...', extends: profiles.philips, ... })`.
|
|
6293
|
+
*
|
|
6294
|
+
* @example
|
|
6295
|
+
* ```ts
|
|
6296
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6297
|
+
* const msg = parseHL7(raw, profiles.philips);
|
|
6298
|
+
* const zds = msg.allSegments().find((s) => s.type === "ZDS");
|
|
6299
|
+
* console.log(zds?.get("studyInstanceUid")?.value); // "1.2.826.0.1.3680043.10.99999.20250326.7"
|
|
6300
|
+
* ```
|
|
6301
|
+
*/
|
|
6302
|
+
/**
|
|
6303
|
+
* Built-in Philips Vue PACS ("IS Link") imaging profile (BIP-08). See file
|
|
6304
|
+
* header for rationale; use via `parseHL7(raw, profiles.philips)`.
|
|
6305
|
+
*
|
|
6306
|
+
* @example
|
|
6307
|
+
* ```ts
|
|
6308
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6309
|
+
* const msg = parseHL7(raw, profiles.philips);
|
|
6310
|
+
* ```
|
|
6311
|
+
*/
|
|
6312
|
+
declare const philips: Profile;
|
|
6313
|
+
|
|
6314
|
+
/**
|
|
6315
|
+
* VA VistA Radiology / Nuclear Medicine imaging HL7 interface quirks. The
|
|
6316
|
+
* Veterans Affairs VistA Radiology package exchanges HL7 **v2.4** messages
|
|
6317
|
+
* with commercial RIS/PACS systems and carries the DICOM **Study Instance
|
|
6318
|
+
* UID** in a `ZDS` Z-segment (field 1, `RP` "Reference Pointer" composite,
|
|
6319
|
+
* the UID sits in the first component, ZDS-1.1 "Pointer"), so the receiving
|
|
6320
|
+
* image manager can correlate the HL7 order/result with the DICOM study.
|
|
6321
|
+
* This is the same IHE Radiology cross-vendor bridge segment declared by the
|
|
6322
|
+
* `visage` and `philips` profiles: `ZDS` is an IHE extension, not a
|
|
6323
|
+
* VA-proprietary quirk; the value this profile adds is a **named federal
|
|
6324
|
+
* source** grounded in a distinct public spec, plus coverage of the shape the
|
|
6325
|
+
* VA spec documents that the imaging-vendor specs do not: `ZDS` on **ORU**
|
|
6326
|
+
* result messages (VistA sends `ZDS` in **both ORM and ORU**), not just on
|
|
6327
|
+
* the ORM order.
|
|
6328
|
+
*
|
|
6329
|
+
* Grounded in the public **Radiology/Nuclear Medicine 5.0 HL7 Interface
|
|
6330
|
+
* Specification** (Version 3.6, revised for Patch RA*5.0*203, June 2024, U.S.
|
|
6331
|
+
* Department of Veterans Affairs), which documents "ZDS Segment Fields in ORU
|
|
6332
|
+
* and ORM" with `ZDS-1` = Study Instance UID (`RP`: 1.1 Pointer, 1.2
|
|
6333
|
+
* Application ID, 1.3 Type of Data, 1.4 Subtype). It is a publicly
|
|
6334
|
+
* downloadable federal interface spec on the VA VistA Documentation Library
|
|
6335
|
+
* (`va.gov/vdl`), not an invented quirk (ADR 0018). The spec's messaging is
|
|
6336
|
+
* HL7-native v2.4, so its `TS` timestamps use the `YYYYMMDDHHMMSS` form: this
|
|
6337
|
+
* profile therefore declares **no** custom `dateFormats`, only the ZDS
|
|
6338
|
+
* segment.
|
|
6339
|
+
*
|
|
6340
|
+
* Authored from the public `defineProfile()` API: zero privileged internal
|
|
6341
|
+
* coupling. Consumers extend this profile via
|
|
6342
|
+
* `defineProfile({ name: '...', extends: profiles.va, ... })`.
|
|
6343
|
+
*
|
|
6344
|
+
* @example
|
|
6345
|
+
* ```ts
|
|
6346
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6347
|
+
* const msg = parseHL7(raw, profiles.va);
|
|
6348
|
+
* const zds = msg.allSegments().find((s) => s.type === "ZDS");
|
|
6349
|
+
* console.log(zds?.get("studyInstanceUid")?.value); // "1.2.826.0.1.3680043.10.99999.20250326.9"
|
|
6350
|
+
* ```
|
|
6351
|
+
*/
|
|
6352
|
+
/**
|
|
6353
|
+
* Built-in VA VistA Radiology / Nuclear Medicine imaging profile (BIP-09).
|
|
6354
|
+
* See file header for rationale; use via `parseHL7(raw, profiles.va)`.
|
|
6355
|
+
*
|
|
6356
|
+
* @example
|
|
6357
|
+
* ```ts
|
|
6358
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6359
|
+
* const msg = parseHL7(raw, profiles.va);
|
|
6360
|
+
* ```
|
|
6361
|
+
*/
|
|
6362
|
+
declare const va: Profile;
|
|
6363
|
+
|
|
6364
|
+
/**
|
|
6365
|
+
* Visage 7 imaging/PACS HL7 interface quirks. Radiology/PACS order feeds
|
|
6366
|
+
* carry the DICOM **Study Instance UID** in a `ZDS` Z-segment (field 1,
|
|
6367
|
+
* first component) so the receiving image manager can correlate the HL7
|
|
6368
|
+
* order with the DICOM study: the classic RIS/PACS ↔ HL7 bridge segment
|
|
6369
|
+
* defined by the IHE Radiology Technical Framework. BIP-07.
|
|
6370
|
+
*
|
|
6371
|
+
* Grounded in the public **Visage 7 – HL7 Interface Specification**
|
|
6372
|
+
* (V23.00, Jun 2026, Visage Imaging GmbH), §4.4 "ORM Messages" + the ZDS
|
|
6373
|
+
* segment table ("Study Instance UID … contained in the first component of
|
|
6374
|
+
* the first field of the ZDS Segment"): a publicly downloadable vendor
|
|
6375
|
+
* interface spec, not an invented quirk (ADR 0018). Dates in that spec use
|
|
6376
|
+
* the HL7-native `YYYYMMDDHHMMSS` form, so this profile declares **no**
|
|
6377
|
+
* custom `dateFormats`: only the ZDS segment.
|
|
6378
|
+
*
|
|
6379
|
+
* Authored from the public `defineProfile()` API: zero privileged internal
|
|
6380
|
+
* coupling. Consumers extend this profile via
|
|
6381
|
+
* `defineProfile({ name: '...', extends: profiles.visage, ... })`.
|
|
6382
|
+
*
|
|
6383
|
+
* @example
|
|
6384
|
+
* ```ts
|
|
6385
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6386
|
+
* const msg = parseHL7(raw, profiles.visage);
|
|
6387
|
+
* const zds = msg.allSegments().find((s) => s.type === "ZDS");
|
|
6388
|
+
* console.log(zds?.get("studyInstanceUid")?.value); // "1.2.826.0.1.3680043.10.99999.20250326.1"
|
|
6389
|
+
* ```
|
|
6390
|
+
*/
|
|
6391
|
+
/**
|
|
6392
|
+
* Built-in Visage 7 imaging/PACS profile (BIP-07). See file header for
|
|
6393
|
+
* rationale; use via `parseHL7(raw, profiles.visage)`.
|
|
6394
|
+
*
|
|
6395
|
+
* @example
|
|
6396
|
+
* ```ts
|
|
6397
|
+
* import { parseHL7, profiles } from "@cosyte/hl7";
|
|
6398
|
+
* const msg = parseHL7(raw, profiles.visage);
|
|
6399
|
+
* ```
|
|
6400
|
+
*/
|
|
6401
|
+
declare const visage: Profile;
|
|
6402
|
+
|
|
5432
6403
|
/**
|
|
5433
6404
|
* Public barrel for the `@cosyte/hl7` profile subsystem. Assembles
|
|
5434
|
-
* the `profiles` namespace object (
|
|
6405
|
+
* the `profiles` namespace object (8 built-ins) and re-exports the public
|
|
5435
6406
|
* profile API: `defineProfile`, `setDefaultProfile`, `getDefaultProfile`,
|
|
5436
6407
|
* plus the two type exports `DefineProfileOptions` and
|
|
5437
6408
|
* `CustomSegmentDefinition`.
|
|
5438
6409
|
*
|
|
5439
|
-
* D-26 contract: individual built-ins are NOT top-level named exports
|
|
6410
|
+
* D-26 contract: individual built-ins are NOT top-level named exports,
|
|
5440
6411
|
* consumers access them via `profiles.epic`, `profiles.cerner`, etc.
|
|
5441
6412
|
* ("epic" is too generic a name for a top-level export.)
|
|
5442
6413
|
*/
|
|
5443
6414
|
|
|
5444
6415
|
/**
|
|
5445
|
-
* Namespace object exposing the
|
|
5446
|
-
* cerner, meditech, athena, genericLab). Each is authored
|
|
5447
|
-
* `defineProfile()` API (BIP-01..
|
|
6416
|
+
* Namespace object exposing the 8 shipped built-in vendor profiles (epic,
|
|
6417
|
+
* cerner, meditech, athena, genericLab, visage, philips, va). Each is authored
|
|
6418
|
+
* via the public `defineProfile()` API (BIP-01..09).
|
|
5448
6419
|
*
|
|
5449
6420
|
* @example
|
|
5450
6421
|
* ```ts
|
|
@@ -5459,6 +6430,9 @@ declare const profiles: {
|
|
|
5459
6430
|
readonly epic: typeof epic;
|
|
5460
6431
|
readonly genericLab: typeof genericLab;
|
|
5461
6432
|
readonly meditech: typeof meditech;
|
|
6433
|
+
readonly philips: typeof philips;
|
|
6434
|
+
readonly va: typeof va;
|
|
6435
|
+
readonly visage: typeof visage;
|
|
5462
6436
|
};
|
|
5463
6437
|
|
|
5464
6438
|
/**
|
|
@@ -5468,7 +6442,7 @@ declare const profiles: {
|
|
|
5468
6442
|
*
|
|
5469
6443
|
* Derived from HL7 v2.5.1 chapters 3-7 (core message segments, order/result
|
|
5470
6444
|
* tree, financial tree, scheduling, master files). Z-segments
|
|
5471
|
-
* (`/^Z[A-Z0-9]{2}$/`) are NOT in this set
|
|
6445
|
+
* (`/^Z[A-Z0-9]{2}$/`) are NOT in this set: they're always user-defined
|
|
5472
6446
|
* and must be declared in a profile to avoid the warning.
|
|
5473
6447
|
*
|
|
5474
6448
|
* Frozen as `ReadonlySet<string>` for O(1) lookup without mutation risk.
|
|
@@ -5489,7 +6463,7 @@ declare const profiles: {
|
|
|
5489
6463
|
declare const KNOWN_SEGMENTS: ReadonlySet<string>;
|
|
5490
6464
|
|
|
5491
6465
|
/**
|
|
5492
|
-
* Coding-system provenance (HL7 Table 0396, READ-ONLY)
|
|
6466
|
+
* Coding-system provenance (HL7 Table 0396, READ-ONLY): roadmap Phase F.
|
|
5493
6467
|
*
|
|
5494
6468
|
* Answers one question about a coded element: **"what coding system does this
|
|
5495
6469
|
* code CLAIM?"** It surfaces the sender's CWE.3 / CE.3 "Name of Coding System"
|
|
@@ -5502,20 +6476,20 @@ declare const KNOWN_SEGMENTS: ReadonlySet<string>;
|
|
|
5502
6476
|
* - **No lookup, no network, no bundled codeset.** Zero runtime deps; this is
|
|
5503
6477
|
* a frozen acronym→name map, nothing more.
|
|
5504
6478
|
* - **Fail-safe.** An unregistered / local / mistyped system id is surfaced
|
|
5505
|
-
* **verbatim** with `known: false
|
|
6479
|
+
* **verbatim** with `known: false`: never dropped, never guessed.
|
|
5506
6480
|
*
|
|
5507
6481
|
* Because provenance is the *sender's claim*, not a verified fact, a consumer
|
|
5508
6482
|
* MUST still treat the code per its own trust policy. The value of this module
|
|
5509
|
-
* is letting that consumer ask "which system?" before interpreting a code
|
|
6483
|
+
* is letting that consumer ask "which system?" before interpreting a code: a
|
|
5510
6484
|
* "PCN" allergen or an "I10" diagnosis is only safe to read once you know the
|
|
5511
6485
|
* system it belongs to.
|
|
5512
6486
|
*
|
|
5513
|
-
* Spec traceability: HL7 Table 0396 (Coding System)
|
|
6487
|
+
* Spec traceability: HL7 Table 0396 (Coding System): the registry of
|
|
5514
6488
|
* coding-system acronyms carried in the CWE.3 / CE.3 component. v2.7+ expects
|
|
5515
6489
|
* the registered acronym. See `docs-content/spec-notes-coding-system.md` for
|
|
5516
6490
|
* the per-entry source and the `I10` (ICD-10 vs ICD-10-CM) nuance.
|
|
5517
6491
|
*
|
|
5518
|
-
* Zero runtime deps
|
|
6492
|
+
* Zero runtime deps: pure data + pure functions.
|
|
5519
6493
|
*/
|
|
5520
6494
|
/**
|
|
5521
6495
|
* One recognized HL7 Table 0396 coding system: its registered acronym (the
|
|
@@ -5536,14 +6510,14 @@ interface KnownCodingSystem {
|
|
|
5536
6510
|
}
|
|
5537
6511
|
/**
|
|
5538
6512
|
* The safety-relevant subset of HL7 Table 0396 this library recognizes for
|
|
5539
|
-
* provenance. Deliberately small and frozen
|
|
6513
|
+
* provenance. Deliberately small and frozen: it is NOT the full Table 0396
|
|
5540
6514
|
* registry. Each entry's source is recorded in
|
|
5541
6515
|
* `docs-content/spec-notes-coding-system.md`.
|
|
5542
6516
|
*
|
|
5543
6517
|
* Note on `I10`: Table 0396 registers `I10` as **ICD-10** (the WHO base
|
|
5544
6518
|
* classification). US v2 feeds frequently send `I10` when they mean
|
|
5545
6519
|
* ICD-10-**CM**, but that clinical-modification specificity is the sender's
|
|
5546
|
-
* convention, not what the acronym registers
|
|
6520
|
+
* convention, not what the acronym registers: so this map reports the
|
|
5547
6521
|
* registered claim (`"ICD-10"`) and does not silently upgrade it to CM.
|
|
5548
6522
|
*
|
|
5549
6523
|
* @example
|
|
@@ -5554,14 +6528,14 @@ interface KnownCodingSystem {
|
|
|
5554
6528
|
*/
|
|
5555
6529
|
declare const KNOWN_CODING_SYSTEMS: readonly KnownCodingSystem[];
|
|
5556
6530
|
/**
|
|
5557
|
-
* A coding-system provenance answer
|
|
6531
|
+
* A coding-system provenance answer: the system a code CLAIMS, never
|
|
5558
6532
|
* validated. `claimed` is always present and verbatim (never dropped); the
|
|
5559
6533
|
* resolved `id` / `name` are present only when the claim maps to a registered
|
|
5560
6534
|
* Table 0396 entry.
|
|
5561
6535
|
*/
|
|
5562
6536
|
interface CodingSystemInfo {
|
|
5563
6537
|
/**
|
|
5564
|
-
* The coding-system id exactly as it appeared in CWE.3 / CE.3
|
|
6538
|
+
* The coding-system id exactly as it appeared in CWE.3 / CE.3: preserved
|
|
5565
6539
|
* verbatim (original case and spelling), never altered, never dropped.
|
|
5566
6540
|
*/
|
|
5567
6541
|
readonly claimed: string;
|
|
@@ -5575,12 +6549,12 @@ interface CodingSystemInfo {
|
|
|
5575
6549
|
/**
|
|
5576
6550
|
* Resolve a raw coding-system id (a CWE.3 / CE.3 "Name of Coding System"
|
|
5577
6551
|
* value) to its provenance. Returns `undefined` when there is no claim to
|
|
5578
|
-
* resolve
|
|
6552
|
+
* resolve: `id` is `undefined`, empty, or whitespace-only.
|
|
5579
6553
|
*
|
|
5580
6554
|
* Matching is case-insensitive and tolerant of surrounding whitespace, and
|
|
5581
6555
|
* normalizes the well-known aliases in {@link KNOWN_CODING_SYSTEMS} (e.g.
|
|
5582
6556
|
* `"LOINC"` → `LN`, `"SNOMED"` → `SCT`, `"RxNorm"` → `RXN`). An unrecognized
|
|
5583
|
-
* id is returned verbatim with `known: false
|
|
6557
|
+
* id is returned verbatim with `known: false`: never guessed.
|
|
5584
6558
|
*
|
|
5585
6559
|
* @example
|
|
5586
6560
|
* ```ts
|
|
@@ -5594,7 +6568,7 @@ interface CodingSystemInfo {
|
|
|
5594
6568
|
declare function codingSystem(id: string | undefined): CodingSystemInfo | undefined;
|
|
5595
6569
|
/**
|
|
5596
6570
|
* Structural shape of any coded element that carries a primary + alternate
|
|
5597
|
-
* coding system
|
|
6571
|
+
* coding system: both {@link CWE} and {@link CE} satisfy it. Kept structural
|
|
5598
6572
|
* (not a union) so callers can pass a `dg.code` / `obs.code` directly.
|
|
5599
6573
|
*/
|
|
5600
6574
|
interface CodedSystemFields {
|
|
@@ -5646,10 +6620,10 @@ declare function alternateCodingSystemOf(coded: CodedSystemFields): CodingSystem
|
|
|
5646
6620
|
*
|
|
5647
6621
|
* `splitBatch()` demarcates the individual `MSH`-led messages by **MSH
|
|
5648
6622
|
* boundaries**, parses each one with {@link parseHL7} (so every message is
|
|
5649
|
-
* returned either as a parsed `Hl7Message` **or** as a typed failure entry
|
|
6623
|
+
* returned either as a parsed `Hl7Message` **or** as a typed failure entry,
|
|
5650
6624
|
* a malformed message mid-stream is isolated, never suppresses its siblings),
|
|
5651
6625
|
* and reconciles the declared envelope counts (BTS-1 batch message count,
|
|
5652
|
-
* FTS-1 file batch count) against the actual counts
|
|
6626
|
+
* FTS-1 file batch count) against the actual counts: surfacing a
|
|
5653
6627
|
* {@link batchCountMismatch} warning on any mismatch **without ever dropping
|
|
5654
6628
|
* the tail**. A bare single message with no envelope passes straight through
|
|
5655
6629
|
* as a one-message result.
|
|
@@ -5657,7 +6631,7 @@ declare function alternateCodingSystemOf(coded: CodedSystemFields): CodingSystem
|
|
|
5657
6631
|
* This is a **separate surface** from `parseHL7` on purpose: `parseHL7` rejects
|
|
5658
6632
|
* non-`MSH`-first input outright (that is correct for a single-message
|
|
5659
6633
|
* parser), so batch demarcation lives here. `splitBatch` is a **splitter**, not
|
|
5660
|
-
* an enforcer
|
|
6634
|
+
* an enforcer: it does not send batch ACKs, does not enforce any profile's
|
|
5661
6635
|
* mandatory-envelope rule (it warns via {@link batchMissingTrailer}; the caller
|
|
5662
6636
|
* decides to reject), and does not de-batch across transport framing (that is
|
|
5663
6637
|
* `@cosyte/mllp`). See `docs-content/spec-notes-batch.md`.
|
|
@@ -5676,7 +6650,7 @@ type BatchEnvelopeName = "FHS" | "BHS" | "BTS" | "FTS";
|
|
|
5676
6650
|
/**
|
|
5677
6651
|
* A raw batch-envelope segment (`FHS`/`BHS`/`BTS`/`FTS`) surfaced by
|
|
5678
6652
|
* {@link splitBatch}. `fields` is the segment split on its own field separator
|
|
5679
|
-
* with `fields[0]` holding the segment name
|
|
6653
|
+
* with `fields[0]` holding the segment name: deliberately the raw token
|
|
5680
6654
|
* array, not a typed model: first-class FHS/BHS field helpers are deferred
|
|
5681
6655
|
* (roadmap Phase L defers "typed FHS/BHS helpers beyond raw fields"). Note the
|
|
5682
6656
|
* HL7 MSH-family indexing quirk: for `FHS`/`BHS`, `fields[1]` is the
|
|
@@ -5704,7 +6678,7 @@ interface BatchEnvelopeSegment {
|
|
|
5704
6678
|
* One message extracted from a batch stream. Discriminated on `ok`: a
|
|
5705
6679
|
* successful parse carries the `Hl7Message` (whose own `.warnings` hold any
|
|
5706
6680
|
* per-message Tier-2 deviations); a message that hit one of the four Tier-3
|
|
5707
|
-
* fatal conditions carries the `Hl7ParseError` instead
|
|
6681
|
+
* fatal conditions carries the `Hl7ParseError` instead: **isolated**, so the
|
|
5708
6682
|
* rest of the batch still yields. `raw` is the message's verbatim source
|
|
5709
6683
|
* (re-parseable by {@link parseHL7}); `position` is the message's `MSH` (or,
|
|
5710
6684
|
* for pre-`MSH` stray content, its first) segment index in the stream.
|
|
@@ -5733,7 +6707,7 @@ type BatchMessageEntry = {
|
|
|
5733
6707
|
* One batch within a stream: a run of messages delimited by a `BHS` header
|
|
5734
6708
|
* and/or a `BTS` trailer (both optional in §2.10.3, so a `BTS` closes a
|
|
5735
6709
|
* preceding run into a batch even with no `BHS`). A run of messages with
|
|
5736
|
-
* **neither** a header nor a trailer is *not* a batch
|
|
6710
|
+
* **neither** a header nor a trailer is *not* a batch: those messages live in
|
|
5737
6711
|
* {@link BatchSplitResult.messages} only, so they never inflate a batch count.
|
|
5738
6712
|
* `declaredMessageCount` is BTS-1 when the trailer declared a usable
|
|
5739
6713
|
* non-negative integer (it is **optional [0..1]** in the spec, so it may be
|
|
@@ -5761,7 +6735,7 @@ interface Batch {
|
|
|
5761
6735
|
}
|
|
5762
6736
|
/**
|
|
5763
6737
|
* The result of {@link splitBatch}: the flattened `messages` (every message
|
|
5764
|
-
* across every batch, in stream order
|
|
6738
|
+
* across every batch, in stream order: the primary surface), the nested
|
|
5765
6739
|
* `batches`, the raw file envelope segments, and the batch-level `warnings`
|
|
5766
6740
|
* (count-mismatch / missing-trailer). `hadEnvelope` is `false` for a bare
|
|
5767
6741
|
* passthrough (no FHS/BHS/BTS/FTS seen).
|
|
@@ -5777,7 +6751,7 @@ interface Batch {
|
|
|
5777
6751
|
*/
|
|
5778
6752
|
interface BatchSplitResult {
|
|
5779
6753
|
/**
|
|
5780
|
-
* Every message split out of the stream, in order
|
|
6754
|
+
* Every message split out of the stream, in order: the primary surface. This
|
|
5781
6755
|
* includes messages that belong to no explicit batch (a bare stream, or
|
|
5782
6756
|
* content outside any `BHS`/`BTS`), so it is a superset of the messages
|
|
5783
6757
|
* reachable via `batches`.
|
|
@@ -5806,16 +6780,16 @@ interface BatchSplitResult {
|
|
|
5806
6780
|
* - handles a file with **multiple batches** and a batch with multiple messages;
|
|
5807
6781
|
* - a **bare single message** (no envelope) passes straight through as one entry;
|
|
5808
6782
|
* - a **malformed message mid-batch is isolated** (returned as a typed failure
|
|
5809
|
-
* entry)
|
|
6783
|
+
* entry): its siblings are still returned, the tail is never dropped;
|
|
5810
6784
|
* - reconciles BTS-1 (batch message count) and FTS-1 (file batch count) and
|
|
5811
|
-
* emits {@link batchCountMismatch} on a mismatch
|
|
6785
|
+
* emits {@link batchCountMismatch} on a mismatch: counts only, never PHI;
|
|
5812
6786
|
* - emits {@link batchMissingTrailer} when a `BHS`/`FHS` header opens a scope no
|
|
5813
|
-
* `BTS`/`FTS` closes
|
|
6787
|
+
* `BTS`/`FTS` closes: a warning, never a throw (the caller decides to reject).
|
|
5814
6788
|
*
|
|
5815
6789
|
* The second argument, when given, is forwarded verbatim to {@link parseHL7}
|
|
5816
6790
|
* for **each** message (profile, `strict`, `charset`, `dateFormats`, …). Under
|
|
5817
6791
|
* `strict`, a message that would warn surfaces as a failure entry (still
|
|
5818
|
-
* isolated). `splitBatch` itself never throws
|
|
6792
|
+
* isolated). `splitBatch` itself never throws: an empty stream yields an empty
|
|
5819
6793
|
* result.
|
|
5820
6794
|
*
|
|
5821
6795
|
* @example
|
|
@@ -5833,13 +6807,622 @@ interface BatchSplitResult {
|
|
|
5833
6807
|
* for (const entry of messages) {
|
|
5834
6808
|
* if (entry.ok) console.log(entry.message.version); // "2.5"
|
|
5835
6809
|
* }
|
|
5836
|
-
* console.log(warnings.length); // 0
|
|
6810
|
+
* console.log(warnings.length); // 0: declared counts match
|
|
5837
6811
|
* ```
|
|
5838
6812
|
*/
|
|
5839
6813
|
declare function splitBatch(raw: string | Buffer): BatchSplitResult;
|
|
5840
6814
|
declare function splitBatch(raw: string | Buffer, profile: Profile): BatchSplitResult;
|
|
5841
6815
|
declare function splitBatch(raw: string | Buffer, options: ParseOptions): BatchSplitResult;
|
|
5842
6816
|
|
|
6817
|
+
/**
|
|
6818
|
+
* Public types for the conformance-profile engine (roadmap Phase U). A
|
|
6819
|
+
* **consumer authors** a declarative {@link ConformanceProfile}: a bounded
|
|
6820
|
+
* subset of the HL7 v2 Message-Profile / NIST-IGAMT model (usage codes,
|
|
6821
|
+
* cardinality, length, value-set binding against a consumer-supplied code
|
|
6822
|
+
* list): and {@link validateAgainstProfile} runs it against a parsed message,
|
|
6823
|
+
* returning typed {@link ConformanceFinding}s.
|
|
6824
|
+
*
|
|
6825
|
+
* This is the **sanctioned functionality-plane replacement for the retired
|
|
6826
|
+
* vendor-corpus arc**: instead of hl7 shipping vendor/IHE/regulatory profiles
|
|
6827
|
+
* or code sets, the consumer brings their own interface spec and hl7 validates
|
|
6828
|
+
* against it. hl7 ships **no** profile, **no** code set, and makes **no**
|
|
6829
|
+
* network call: the profile and every value set are supplied by the caller.
|
|
6830
|
+
*
|
|
6831
|
+
* Two invariants govern the whole surface:
|
|
6832
|
+
*
|
|
6833
|
+
* 1. **"No findings" is NOT a conformance attestation.** An empty
|
|
6834
|
+
* {@link ConformanceResult.findings} means "nothing THIS profile checked was
|
|
6835
|
+
* violated": never "this message is conformant." The profile only covers
|
|
6836
|
+
* what the author declared; everything undeclared is unchecked.
|
|
6837
|
+
* 2. **No PHI in findings.** A finding names the structural **locus** (segment
|
|
6838
|
+
* name, field / component index, repetition, occurrence) and the rule that
|
|
6839
|
+
* fired: **never** the offending field value. A "value not in set" finding
|
|
6840
|
+
* says *where* and *which value set*, never *what the value was*.
|
|
6841
|
+
*
|
|
6842
|
+
* @see validateAgainstProfile
|
|
6843
|
+
*/
|
|
6844
|
+
/**
|
|
6845
|
+
* The six HL7 v2 conformance **usage codes** (HL7 Conformance Methodology,
|
|
6846
|
+
* Message Profiles; IHE ITI TF Vol.2 Appendix C). They constrain whether an
|
|
6847
|
+
* element must, may, or must not appear:
|
|
6848
|
+
*
|
|
6849
|
+
* - **`R`**: Required. The element SHALL be present (with a value). Absent →
|
|
6850
|
+
* {@link FINDING_CODES.PROFILE_REQUIRED_ABSENT}.
|
|
6851
|
+
* - **`RE`**: Required but may be Empty. The element is supported and SHALL be
|
|
6852
|
+
* sent when the sender has the data; its **absence is never a violation**
|
|
6853
|
+
* (this engine cannot know whether the sender had the data).
|
|
6854
|
+
* - **`C`**: Conditional. Presence depends on a predicate. This bounded engine
|
|
6855
|
+
* ships **no predicate language** (a documented defer: roadmap §5), so a
|
|
6856
|
+
* `C` element's **presence is not evaluated** (treated as optional); its
|
|
6857
|
+
* length / value-set / cardinality rules still apply when it IS present.
|
|
6858
|
+
* - **`CE`**: Conditional but may be Empty. Same non-evaluation as `C`.
|
|
6859
|
+
* - **`O`**: Optional. No presence constraint.
|
|
6860
|
+
* - **`X`**: Not supported / not permitted. The element SHALL NOT be present.
|
|
6861
|
+
* Present → {@link FINDING_CODES.PROFILE_NOT_PERMITTED}.
|
|
6862
|
+
*
|
|
6863
|
+
* @example
|
|
6864
|
+
* ```ts
|
|
6865
|
+
* import type { UsageCode } from "@cosyte/hl7";
|
|
6866
|
+
* const usage: UsageCode = "R";
|
|
6867
|
+
* ```
|
|
6868
|
+
*/
|
|
6869
|
+
type UsageCode = "R" | "RE" | "C" | "CE" | "O" | "X";
|
|
6870
|
+
/**
|
|
6871
|
+
* The frozen set of valid {@link UsageCode}s, for runtime validation and
|
|
6872
|
+
* introspection. `USAGE_CODES.R === "R"`.
|
|
6873
|
+
*
|
|
6874
|
+
* @example
|
|
6875
|
+
* ```ts
|
|
6876
|
+
* import { USAGE_CODES } from "@cosyte/hl7";
|
|
6877
|
+
* USAGE_CODES.includes("R" as const); // true
|
|
6878
|
+
* ```
|
|
6879
|
+
*/
|
|
6880
|
+
declare const USAGE_CODES: readonly UsageCode[];
|
|
6881
|
+
/**
|
|
6882
|
+
* A repetition-count constraint. `min` / `max` are inclusive bounds on the
|
|
6883
|
+
* number of repetitions (for a field rule) or occurrences (for a segment
|
|
6884
|
+
* rule). `max` may be the literal `"*"` for "unbounded". Omitted bounds are
|
|
6885
|
+
* unconstrained on that side.
|
|
6886
|
+
*
|
|
6887
|
+
* **Cardinality `min` is checked only when the element is present.** An absent
|
|
6888
|
+
* **Required** element is reported as {@link FINDING_CODES.PROFILE_REQUIRED_ABSENT}
|
|
6889
|
+
* (a usage finding), not a cardinality finding: so a missing `R` field with
|
|
6890
|
+
* `cardinality.min = 1` yields exactly one finding, never two.
|
|
6891
|
+
*
|
|
6892
|
+
* @example
|
|
6893
|
+
* ```ts
|
|
6894
|
+
* import type { Cardinality } from "@cosyte/hl7";
|
|
6895
|
+
* const once: Cardinality = { min: 1, max: 1 };
|
|
6896
|
+
* const many: Cardinality = { min: 1, max: "*" };
|
|
6897
|
+
* ```
|
|
6898
|
+
*/
|
|
6899
|
+
interface Cardinality {
|
|
6900
|
+
readonly min?: number;
|
|
6901
|
+
readonly max?: number | "*";
|
|
6902
|
+
}
|
|
6903
|
+
/**
|
|
6904
|
+
* A rule for one **field position** within a segment (roadmap Phase U). Every
|
|
6905
|
+
* constraint is optional; a rule with only a `field` index is a no-op.
|
|
6906
|
+
*
|
|
6907
|
+
* @remarks
|
|
6908
|
+
* `field` is the 1-indexed HL7 position (MSH offset handled internally, exactly
|
|
6909
|
+
* like `Segment.field(n)`: `{ field: 9 }` on `MSH` targets MSH-9). Length and
|
|
6910
|
+
* value-set checks read the value at `component` (default **1**), so a coded
|
|
6911
|
+
* field's code (`CWE.1` / `CE.1`) is checked by default; set `component` to
|
|
6912
|
+
* check a different component. Both are applied **per present repetition**.
|
|
6913
|
+
*/
|
|
6914
|
+
interface FieldRule {
|
|
6915
|
+
/** 1-indexed HL7 field position (e.g. `3` for PID-3, `9` for MSH-9). */
|
|
6916
|
+
readonly field: number;
|
|
6917
|
+
/**
|
|
6918
|
+
* Optional human label for the field (e.g. `"Patient Identifier List"`).
|
|
6919
|
+
* Structural documentation for the profile author only: findings identify a
|
|
6920
|
+
* field by its PHI-free structural locus (segment + index), never by this
|
|
6921
|
+
* label, so the label is never echoed into a finding message.
|
|
6922
|
+
*/
|
|
6923
|
+
readonly name?: string;
|
|
6924
|
+
/** Usage constraint (see {@link UsageCode}). Omitted ⇒ Optional. */
|
|
6925
|
+
readonly usage?: UsageCode;
|
|
6926
|
+
/** Repetition-count constraint for this field. */
|
|
6927
|
+
readonly cardinality?: Cardinality;
|
|
6928
|
+
/** Maximum character length of the checked component value (inclusive). */
|
|
6929
|
+
readonly length?: number;
|
|
6930
|
+
/**
|
|
6931
|
+
* Consumer-supplied permitted-value list. The checked component value must be
|
|
6932
|
+
* a member (case-sensitive exact match). **hl7 ships no code set**: this is
|
|
6933
|
+
* BYO terminology; membership is a literal string check, never a LOINC /
|
|
6934
|
+
* SNOMED / ICD / RxNorm lookup and never a network call.
|
|
6935
|
+
*/
|
|
6936
|
+
readonly valueSet?: readonly string[];
|
|
6937
|
+
/**
|
|
6938
|
+
* 1-indexed component whose value the `length` / `valueSet` checks read.
|
|
6939
|
+
* Defaults to `1` (the first component: a coded element's code).
|
|
6940
|
+
*/
|
|
6941
|
+
readonly component?: number;
|
|
6942
|
+
/**
|
|
6943
|
+
* Severity for findings this rule produces. Defaults to `"error"`. A profile
|
|
6944
|
+
* author can downgrade a data-quality rule (e.g. a length or value-set check)
|
|
6945
|
+
* to `"warning"` or `"info"` without changing the check itself.
|
|
6946
|
+
*/
|
|
6947
|
+
readonly severity?: FindingSeverity;
|
|
6948
|
+
}
|
|
6949
|
+
/**
|
|
6950
|
+
* A rule for one **segment type** (roadmap Phase U). `usage` constrains whether
|
|
6951
|
+
* the segment must / must not appear; `cardinality` constrains how many times;
|
|
6952
|
+
* `fields` are the per-field rules, applied to **every** occurrence of the
|
|
6953
|
+
* segment.
|
|
6954
|
+
*/
|
|
6955
|
+
interface SegmentRule {
|
|
6956
|
+
/** Segment name: 3 chars, `[A-Z][A-Z0-9]{2}` (standard or `Z…` segment). */
|
|
6957
|
+
readonly segment: string;
|
|
6958
|
+
/**
|
|
6959
|
+
* Usage for the segment as a whole. `R` ⇒ at least one occurrence required;
|
|
6960
|
+
* `X` ⇒ none permitted; `RE` / `O` ⇒ no presence constraint; `C` / `CE` ⇒
|
|
6961
|
+
* presence not evaluated (no predicate language). Omitted ⇒ Optional.
|
|
6962
|
+
*/
|
|
6963
|
+
readonly usage?: UsageCode;
|
|
6964
|
+
/** Occurrence-count constraint for this segment across the message. */
|
|
6965
|
+
readonly cardinality?: Cardinality;
|
|
6966
|
+
/** Per-field rules, applied to each occurrence of this segment. */
|
|
6967
|
+
readonly fields?: readonly FieldRule[];
|
|
6968
|
+
/** Severity for the segment-level presence / cardinality findings. Default `"error"`. */
|
|
6969
|
+
readonly severity?: FindingSeverity;
|
|
6970
|
+
}
|
|
6971
|
+
/**
|
|
6972
|
+
* A **user-authored, declarative** conformance profile (roadmap Phase U). The
|
|
6973
|
+
* consumer supplies this; hl7 ships none. It is a bounded subset of the HL7 v2
|
|
6974
|
+
* Message-Profile model: usage / cardinality / length / consumer-supplied
|
|
6975
|
+
* value set: with **no** conditional-predicate language, **no** bundled code
|
|
6976
|
+
* set, and **no** network binding (all deliberate scope boundaries).
|
|
6977
|
+
*
|
|
6978
|
+
* @example
|
|
6979
|
+
* ```ts
|
|
6980
|
+
* import type { ConformanceProfile } from "@cosyte/hl7";
|
|
6981
|
+
*
|
|
6982
|
+
* // A minimal ADT profile the CONSUMER authors: example, NOT an attestation.
|
|
6983
|
+
* const profile: ConformanceProfile = {
|
|
6984
|
+
* name: "example-adt-min",
|
|
6985
|
+
* segments: [
|
|
6986
|
+
* { segment: "MSH", usage: "R", fields: [{ field: 10, name: "Control ID", usage: "R" }] },
|
|
6987
|
+
* { segment: "PID", usage: "R", cardinality: { min: 1, max: 1 }, fields: [
|
|
6988
|
+
* { field: 3, name: "Patient Identifiers", usage: "R", cardinality: { min: 1, max: 1 } },
|
|
6989
|
+
* { field: 8, name: "Administrative Sex", usage: "RE", valueSet: ["M", "F", "U"] },
|
|
6990
|
+
* ] },
|
|
6991
|
+
* { segment: "ZZZ", usage: "X" },
|
|
6992
|
+
* ],
|
|
6993
|
+
* };
|
|
6994
|
+
* ```
|
|
6995
|
+
*/
|
|
6996
|
+
interface ConformanceProfile {
|
|
6997
|
+
/** A name for provenance: echoed into {@link ConformanceResult.profileName}. */
|
|
6998
|
+
readonly name: string;
|
|
6999
|
+
/** The segment rules, evaluated in array order (stable finding order). */
|
|
7000
|
+
readonly segments: readonly SegmentRule[];
|
|
7001
|
+
}
|
|
7002
|
+
/**
|
|
7003
|
+
* Severity of a {@link ConformanceFinding}. `error` is a constraint violation;
|
|
7004
|
+
* `warning` / `info` are author-downgraded advisories (via a rule's
|
|
7005
|
+
* `severity`). {@link FINDING_CODES.PROFILE_MALFORMED} is always `error`.
|
|
7006
|
+
*/
|
|
7007
|
+
type FindingSeverity = "error" | "warning" | "info";
|
|
7008
|
+
/**
|
|
7009
|
+
* The frozen registry of finding codes. Stable, additive string codes: a
|
|
7010
|
+
* consumer compares `finding.code === FINDING_CODES.PROFILE_REQUIRED_ABSENT`.
|
|
7011
|
+
* Segment-level vs field-level is disambiguated by whether the finding's
|
|
7012
|
+
* {@link FindingLocus.field} is present, not by separate codes.
|
|
7013
|
+
*
|
|
7014
|
+
* @example
|
|
7015
|
+
* ```ts
|
|
7016
|
+
* import { validateAgainstProfile, FINDING_CODES } from "@cosyte/hl7";
|
|
7017
|
+
* const { findings } = validateAgainstProfile(msg, profile);
|
|
7018
|
+
* const missing = findings.filter((f) => f.code === FINDING_CODES.PROFILE_REQUIRED_ABSENT);
|
|
7019
|
+
* ```
|
|
7020
|
+
*/
|
|
7021
|
+
declare const FINDING_CODES: {
|
|
7022
|
+
/** A Required (`R`) segment or field is absent (or present-but-empty). */
|
|
7023
|
+
readonly PROFILE_REQUIRED_ABSENT: "PROFILE_REQUIRED_ABSENT";
|
|
7024
|
+
/** A Not-permitted (`X`) segment or field is present. */
|
|
7025
|
+
readonly PROFILE_NOT_PERMITTED: "PROFILE_NOT_PERMITTED";
|
|
7026
|
+
/** A segment-occurrence or field-repetition count is outside its cardinality. */
|
|
7027
|
+
readonly PROFILE_CARDINALITY: "PROFILE_CARDINALITY";
|
|
7028
|
+
/** A checked component value exceeds the declared maximum length. */
|
|
7029
|
+
readonly PROFILE_LENGTH: "PROFILE_LENGTH";
|
|
7030
|
+
/** A checked component value is not a member of the consumer-supplied value set. */
|
|
7031
|
+
readonly PROFILE_VALUE_NOT_IN_SET: "PROFILE_VALUE_NOT_IN_SET";
|
|
7032
|
+
/** The profile ITSELF is structurally malformed (a diagnostic, not a message finding). */
|
|
7033
|
+
readonly PROFILE_MALFORMED: "PROFILE_MALFORMED";
|
|
7034
|
+
};
|
|
7035
|
+
/**
|
|
7036
|
+
* Discriminant union of every {@link ConformanceFinding} code. Enables
|
|
7037
|
+
* exhaustive `switch` narrowing (the `switch-exhaustiveness-check` lint rule).
|
|
7038
|
+
*/
|
|
7039
|
+
type FindingCode = (typeof FINDING_CODES)[keyof typeof FINDING_CODES];
|
|
7040
|
+
/**
|
|
7041
|
+
* The **structural** locus a finding refers to: segment name plus, where
|
|
7042
|
+
* applicable, the field position, component, repetition, and segment
|
|
7043
|
+
* occurrence. Every member is a name or an index: a locus is **inherently
|
|
7044
|
+
* PHI-free** and never carries a field value.
|
|
7045
|
+
*
|
|
7046
|
+
* A finding with no {@link field} is **segment-level** (presence / cardinality
|
|
7047
|
+
* of the segment itself); a finding with a `field` is **field-level**. For a
|
|
7048
|
+
* {@link FINDING_CODES.PROFILE_MALFORMED} diagnostic (a defect in the profile,
|
|
7049
|
+
* not the message) `segment` may be the sentinel `"(profile)"`.
|
|
7050
|
+
*/
|
|
7051
|
+
interface FindingLocus {
|
|
7052
|
+
/** Segment name (e.g. `"PID"`), or `"(profile)"` for a profile-shape defect. */
|
|
7053
|
+
readonly segment: string;
|
|
7054
|
+
/** 1-indexed field position, when the finding is field-level. */
|
|
7055
|
+
readonly field?: number;
|
|
7056
|
+
/** 1-indexed component, when a component-scoped check (length / value-set) fired. */
|
|
7057
|
+
readonly component?: number;
|
|
7058
|
+
/** 0-indexed field repetition, when the finding is repetition-scoped. */
|
|
7059
|
+
readonly repetition?: number;
|
|
7060
|
+
/** 0-indexed segment occurrence, when the segment type repeats. */
|
|
7061
|
+
readonly occurrence?: number;
|
|
7062
|
+
}
|
|
7063
|
+
/**
|
|
7064
|
+
* One typed conformance finding. Carries the {@link FindingCode}, a
|
|
7065
|
+
* {@link FindingSeverity}, the structural {@link FindingLocus}, and a
|
|
7066
|
+
* human-readable `message` describing the rule that fired.
|
|
7067
|
+
*
|
|
7068
|
+
* **The `message` is PHI-safe by construction**: it names the locus, the rule,
|
|
7069
|
+
* and (for a value-set miss) the SIZE of the value set, but **never** the
|
|
7070
|
+
* offending field value.
|
|
7071
|
+
*
|
|
7072
|
+
* @example
|
|
7073
|
+
* ```ts
|
|
7074
|
+
* import type { ConformanceFinding } from "@cosyte/hl7";
|
|
7075
|
+
* const f: ConformanceFinding = {
|
|
7076
|
+
* code: "PROFILE_VALUE_NOT_IN_SET",
|
|
7077
|
+
* severity: "error",
|
|
7078
|
+
* locus: { segment: "PID", field: 8, component: 1 },
|
|
7079
|
+
* message: 'PID-8 component 1 value is not in the profile value set (3 permitted codes).',
|
|
7080
|
+
* };
|
|
7081
|
+
* ```
|
|
7082
|
+
*/
|
|
7083
|
+
interface ConformanceFinding {
|
|
7084
|
+
readonly code: FindingCode;
|
|
7085
|
+
readonly severity: FindingSeverity;
|
|
7086
|
+
readonly locus: FindingLocus;
|
|
7087
|
+
readonly message: string;
|
|
7088
|
+
}
|
|
7089
|
+
/**
|
|
7090
|
+
* The result of {@link validateAgainstProfile}: the profile's `name` and the
|
|
7091
|
+
* ordered list of `findings`.
|
|
7092
|
+
*
|
|
7093
|
+
* **`findings.length === 0` is NOT a conformance attestation.** It means every
|
|
7094
|
+
* rule the profile declared was satisfied: nothing about the parts of the
|
|
7095
|
+
* message the profile did not cover, and nothing about clinical correctness.
|
|
7096
|
+
* Read it as "no declared rule was violated," never as "conformant."
|
|
7097
|
+
*/
|
|
7098
|
+
interface ConformanceResult {
|
|
7099
|
+
readonly profileName: string;
|
|
7100
|
+
readonly findings: readonly ConformanceFinding[];
|
|
7101
|
+
}
|
|
7102
|
+
|
|
7103
|
+
/**
|
|
7104
|
+
* The conformance-profile engine (roadmap Phase U): `validateAgainstProfile`.
|
|
7105
|
+
* Runs a **consumer-authored** declarative {@link ConformanceProfile} against a
|
|
7106
|
+
* parsed {@link Hl7Message} and returns typed {@link ConformanceFinding}s.
|
|
7107
|
+
*
|
|
7108
|
+
* The sanctioned functionality-plane replacement for the retired vendor-corpus
|
|
7109
|
+
* arc: the consumer brings the profile and every value set; hl7 ships **no**
|
|
7110
|
+
* profile, **no** code set, and makes **no** network call. Four invariants the
|
|
7111
|
+
* engine holds absolutely:
|
|
7112
|
+
*
|
|
7113
|
+
* 1. **Never throws.** Any message × any profile (even a malformed profile, or a
|
|
7114
|
+
* value cast through `any`) yields a {@link ConformanceResult}, never an
|
|
7115
|
+
* exception. A malformed profile becomes `PROFILE_MALFORMED` findings.
|
|
7116
|
+
* 2. **Valid ⇒ zero findings**, and **zero findings is NOT an attestation**,
|
|
7117
|
+
* it means no *declared* rule was violated, nothing more (see
|
|
7118
|
+
* {@link ConformanceResult}).
|
|
7119
|
+
* 3. **No PHI in findings.** Findings name the structural locus and the rule,
|
|
7120
|
+
* never the offending value (see `src/conformance/findings.ts`).
|
|
7121
|
+
* 4. **Read-only.** Validation never mutates the message.
|
|
7122
|
+
*/
|
|
7123
|
+
|
|
7124
|
+
/**
|
|
7125
|
+
* Validate a parsed HL7 v2 message against a **user-authored** declarative
|
|
7126
|
+
* conformance profile (roadmap Phase U) and return typed findings.
|
|
7127
|
+
*
|
|
7128
|
+
* **Never throws.** A well-formed profile is evaluated rule by rule; a
|
|
7129
|
+
* malformed profile is reported as `PROFILE_MALFORMED` findings (the engine
|
|
7130
|
+
* does not validate against a profile it cannot trust: never a silent pass).
|
|
7131
|
+
* Either way you get a {@link ConformanceResult}. For fail-fast authoring, run
|
|
7132
|
+
* {@link defineConformanceProfile} first: it throws on a malformed profile.
|
|
7133
|
+
*
|
|
7134
|
+
* **`findings.length === 0` is NOT a conformance attestation.** It means every
|
|
7135
|
+
* rule the profile declared was satisfied: nothing about the parts of the
|
|
7136
|
+
* message the profile did not cover, and nothing about clinical correctness.
|
|
7137
|
+
*
|
|
7138
|
+
* **No PHI in findings**: each finding names the structural locus (segment /
|
|
7139
|
+
* field / component / repetition) and the rule, never the offending value.
|
|
7140
|
+
*
|
|
7141
|
+
* **Read-only**: the message is never mutated.
|
|
7142
|
+
*
|
|
7143
|
+
* @param message - a parsed message from {@link parseHL7}.
|
|
7144
|
+
* @param profile - the consumer's declarative {@link ConformanceProfile}.
|
|
7145
|
+
* @returns the profile name and the ordered findings (empty ⇒ no declared rule violated).
|
|
7146
|
+
*
|
|
7147
|
+
* @example
|
|
7148
|
+
* ```ts
|
|
7149
|
+
* import { parseHL7, validateAgainstProfile, type ConformanceProfile } from "@cosyte/hl7";
|
|
7150
|
+
*
|
|
7151
|
+
* const profile: ConformanceProfile = {
|
|
7152
|
+
* name: "example-adt-min",
|
|
7153
|
+
* segments: [
|
|
7154
|
+
* { segment: "PID", usage: "R", fields: [
|
|
7155
|
+
* { field: 3, name: "Patient Identifiers", usage: "R" },
|
|
7156
|
+
* { field: 8, name: "Administrative Sex", usage: "RE", valueSet: ["M", "F", "U"] },
|
|
7157
|
+
* ] },
|
|
7158
|
+
* ],
|
|
7159
|
+
* };
|
|
7160
|
+
*
|
|
7161
|
+
* const msg = parseHL7(raw);
|
|
7162
|
+
* const { findings } = validateAgainstProfile(msg, profile);
|
|
7163
|
+
* for (const f of findings) console.log(f.severity, f.code, f.message);
|
|
7164
|
+
* // findings.length === 0 ⇒ no declared rule violated (NOT an attestation)
|
|
7165
|
+
* ```
|
|
7166
|
+
*/
|
|
7167
|
+
declare function validateAgainstProfile(message: Hl7Message, profile: ConformanceProfile): ConformanceResult;
|
|
7168
|
+
|
|
7169
|
+
/**
|
|
7170
|
+
* Profile-shape validation for the conformance engine (roadmap Phase U). A
|
|
7171
|
+
* conformance profile is **consumer-authored configuration**, so it can be
|
|
7172
|
+
* malformed. Two entry points share one total defect collector:
|
|
7173
|
+
*
|
|
7174
|
+
* - {@link collectProfileDefects}: **total over `unknown`, never throws.** It
|
|
7175
|
+
* returns a list of structural {@link ProfileDefect}s (empty ⇒ well-formed).
|
|
7176
|
+
* {@link validateAgainstProfile} calls it first and, on any defect, returns
|
|
7177
|
+
* `PROFILE_MALFORMED` findings instead of validating a profile it cannot
|
|
7178
|
+
* trust (never a silent pass).
|
|
7179
|
+
* - {@link defineConformanceProfile}: the **fail-fast authoring** helper. It
|
|
7180
|
+
* runs the same collector and **throws {@link ProfileDefinitionError}** on the
|
|
7181
|
+
* first-through-all defects, returning the profile typed on success. Use it at
|
|
7182
|
+
* profile-authoring time; the engine itself never throws (task invariant).
|
|
7183
|
+
*
|
|
7184
|
+
* A defect `detail` describes the profile's SHAPE (`segments[2].fields[0].field
|
|
7185
|
+
* must be a positive integer`): profile structure only, never PHI.
|
|
7186
|
+
*
|
|
7187
|
+
* @internal (the two functions are re-exported from the package root)
|
|
7188
|
+
*/
|
|
7189
|
+
|
|
7190
|
+
/**
|
|
7191
|
+
* One structural defect in a profile: the PHI-free `detail` plus a best-effort
|
|
7192
|
+
* {@link FindingLocus} (so the engine can attach it to a `PROFILE_MALFORMED`
|
|
7193
|
+
* finding). When the defect is above segment level, `locus.segment` is the
|
|
7194
|
+
* sentinel `"(profile)"`.
|
|
7195
|
+
*
|
|
7196
|
+
* @internal
|
|
7197
|
+
*/
|
|
7198
|
+
interface ProfileDefect {
|
|
7199
|
+
readonly locus: FindingLocus;
|
|
7200
|
+
readonly detail: string;
|
|
7201
|
+
}
|
|
7202
|
+
/**
|
|
7203
|
+
* Collect every structural defect in a candidate conformance profile. **Total
|
|
7204
|
+
* over `unknown` and never throws**: a caller may pass any value (even one
|
|
7205
|
+
* cast through `any`) and get back a defect list rather than an exception. An
|
|
7206
|
+
* empty result means the profile is well-formed enough to validate against.
|
|
7207
|
+
*
|
|
7208
|
+
* @internal
|
|
7209
|
+
*/
|
|
7210
|
+
declare function collectProfileDefects(profile: unknown): readonly ProfileDefect[];
|
|
7211
|
+
/**
|
|
7212
|
+
* The **fail-fast authoring** gate for a conformance profile (roadmap Phase U's
|
|
7213
|
+
* "malformed profile → typed `ProfileDefinitionError`, build-time, before
|
|
7214
|
+
* validation"). Runs {@link collectProfileDefects}; on any defect, throws a
|
|
7215
|
+
* single {@link ProfileDefinitionError} listing every defect. On success,
|
|
7216
|
+
* returns the profile typed as {@link ConformanceProfile}.
|
|
7217
|
+
*
|
|
7218
|
+
* This is **optional**: {@link validateAgainstProfile} tolerates a raw profile
|
|
7219
|
+
* object and never throws: but it lets an author catch a typo when the profile
|
|
7220
|
+
* is written rather than when it is run.
|
|
7221
|
+
*
|
|
7222
|
+
* @throws {ProfileDefinitionError} when the profile is structurally malformed.
|
|
7223
|
+
*
|
|
7224
|
+
* @example
|
|
7225
|
+
* ```ts
|
|
7226
|
+
* import { defineConformanceProfile } from "@cosyte/hl7";
|
|
7227
|
+
* const profile = defineConformanceProfile({
|
|
7228
|
+
* name: "example-adt-min",
|
|
7229
|
+
* segments: [{ segment: "PID", usage: "R" }],
|
|
7230
|
+
* });
|
|
7231
|
+
* // A typo throws at authoring time:
|
|
7232
|
+
* // defineConformanceProfile({ name: "x", segments: [{ segment: "pid" }] });
|
|
7233
|
+
* // → ProfileDefinitionError: segments[0].segment must be a valid segment name…
|
|
7234
|
+
* ```
|
|
7235
|
+
*/
|
|
7236
|
+
declare function defineConformanceProfile(profile: unknown): ConformanceProfile;
|
|
7237
|
+
|
|
7238
|
+
/**
|
|
7239
|
+
* Barrel for the conformance-profile engine (roadmap Phase U). Re-exports the
|
|
7240
|
+
* engine, the fail-fast profile-authoring gate, and the public types. Also
|
|
7241
|
+
* available as the `conformance` namespace object from the package root.
|
|
7242
|
+
*
|
|
7243
|
+
* @see validateAgainstProfile: run a user-authored profile against a message.
|
|
7244
|
+
* @see defineConformanceProfile: optional fail-fast profile-authoring gate.
|
|
7245
|
+
*/
|
|
7246
|
+
|
|
7247
|
+
type index_Cardinality = Cardinality;
|
|
7248
|
+
type index_ConformanceFinding = ConformanceFinding;
|
|
7249
|
+
type index_ConformanceProfile = ConformanceProfile;
|
|
7250
|
+
type index_ConformanceResult = ConformanceResult;
|
|
7251
|
+
declare const index_FINDING_CODES: typeof FINDING_CODES;
|
|
7252
|
+
type index_FieldRule = FieldRule;
|
|
7253
|
+
type index_FindingCode = FindingCode;
|
|
7254
|
+
type index_FindingLocus = FindingLocus;
|
|
7255
|
+
type index_FindingSeverity = FindingSeverity;
|
|
7256
|
+
type index_ProfileDefect = ProfileDefect;
|
|
7257
|
+
type index_SegmentRule = SegmentRule;
|
|
7258
|
+
declare const index_USAGE_CODES: typeof USAGE_CODES;
|
|
7259
|
+
type index_UsageCode = UsageCode;
|
|
7260
|
+
declare const index_collectProfileDefects: typeof collectProfileDefects;
|
|
7261
|
+
declare const index_defineConformanceProfile: typeof defineConformanceProfile;
|
|
7262
|
+
declare const index_validateAgainstProfile: typeof validateAgainstProfile;
|
|
7263
|
+
declare namespace index {
|
|
7264
|
+
export { type index_Cardinality as Cardinality, type index_ConformanceFinding as ConformanceFinding, type index_ConformanceProfile as ConformanceProfile, type index_ConformanceResult as ConformanceResult, index_FINDING_CODES as FINDING_CODES, type index_FieldRule as FieldRule, type index_FindingCode as FindingCode, type index_FindingLocus as FindingLocus, type index_FindingSeverity as FindingSeverity, type index_ProfileDefect as ProfileDefect, type index_SegmentRule as SegmentRule, index_USAGE_CODES as USAGE_CODES, type index_UsageCode as UsageCode, index_collectProfileDefects as collectProfileDefects, index_defineConformanceProfile as defineConformanceProfile, index_validateAgainstProfile as validateAgainstProfile };
|
|
7265
|
+
}
|
|
7266
|
+
|
|
7267
|
+
/**
|
|
7268
|
+
* Streaming / incremental parse for the `@cosyte/hl7` parser (roadmap
|
|
7269
|
+
* Phase S). Real ELR / IIS / lab feeds and files ship many messages and can be
|
|
7270
|
+
* far too large to buffer whole; a live source (an already-de-framed MLLP feed,
|
|
7271
|
+
* a growing file) has no "whole" to buffer at all. `parseStream()` consumes a
|
|
7272
|
+
* **chunked** byte/string source: a Node `Readable`, an async-iterable, or a
|
|
7273
|
+
* plain iterable of chunks: and **yields one {@link StreamMessageEntry} per
|
|
7274
|
+
* `MSH`-delimited message** as each boundary completes, holding only
|
|
7275
|
+
* **O(one message)** of state (the in-flight message's lines plus the current
|
|
7276
|
+
* partial-segment buffer): never the whole stream.
|
|
7277
|
+
*
|
|
7278
|
+
* Design (HL7 v2 Ch. 2; note this is engineering guidance, not a spec-mandated
|
|
7279
|
+
* streaming grammar: the base standard prescribes no incremental parse):
|
|
7280
|
+
*
|
|
7281
|
+
* - **`MSH` at segment start is the message boundary.** A message runs from an
|
|
7282
|
+
* `MSH` segment up to (but not including) the next `MSH` (or a batch-envelope
|
|
7283
|
+
* segment, or end of stream). This mirrors {@link splitBatch}'s in-memory
|
|
7284
|
+
* demarcation exactly, so the streamed decomposition of a buffer is identical
|
|
7285
|
+
* to the whole-buffer one.
|
|
7286
|
+
* - **Batch-aware.** The four batch-envelope segments (`FHS`/`BHS`/`BTS`/`FTS`,
|
|
7287
|
+
* §2.10.3) act as message boundaries (they flush the current message) and are
|
|
7288
|
+
* **never yielded as messages**, so `yielded count == MSH count`. Envelope
|
|
7289
|
+
* **count reconciliation** (BTS-1 / FTS-1) is {@link splitBatch}'s job, not this
|
|
7290
|
+
* surface's: `parseStream` streams *messages*, not a batch report.
|
|
7291
|
+
* - **Chunk-boundary invariant.** Segments (including the `MSH|^~\&` header) are
|
|
7292
|
+
* only demarcated once their terminator arrives, so a message split across two
|
|
7293
|
+
* chunks (mid-segment, mid-field, even mid-`MSH`) is reassembled correctly.
|
|
7294
|
+
* Feeding the same bytes in 1-byte chunks vs. one big chunk yields **identical**
|
|
7295
|
+
* messages. A `\r\n` terminator split across a chunk boundary is not mistaken
|
|
7296
|
+
* for a bare `\r` + empty segment (a trailing `\r` waits for the next chunk).
|
|
7297
|
+
* - **Terminator tolerance.** `\r`, `\r\n`, and `\n` are all accepted as segment
|
|
7298
|
+
* terminators (Postel's Law: real feeds mix them), matching the core parser's
|
|
7299
|
+
* `normalize`.
|
|
7300
|
+
* - **Isolation, never dropped tails.** Each message is parsed by the shipped
|
|
7301
|
+
* {@link parseHL7} (no second grammar; raw extraction semantics unchanged) and
|
|
7302
|
+
* surfaced as either an `ok` entry (its own `.warnings` hold per-message
|
|
7303
|
+
* deviations) or a typed **failure** entry carrying the `Hl7ParseError`. A
|
|
7304
|
+
* corrupt message mid-stream is isolated: it **never suppresses** the messages
|
|
7305
|
+
* after it. A final message that ends without a segment terminator is still
|
|
7306
|
+
* yielded in full, flagged via a stream-level {@link unterminatedStreamMessage}
|
|
7307
|
+
* warning (never a throw), so a truncated feed is surfaced, never silently
|
|
7308
|
+
* dropped.
|
|
7309
|
+
*
|
|
7310
|
+
* Transport framing (MLLP `<SB>…<EB><CR>`) is **out of scope**: this consumes an
|
|
7311
|
+
* already-de-framed stream; `@cosyte/mllp` owns the wire. See
|
|
7312
|
+
* `docs-content/spec-notes-stream.md`.
|
|
7313
|
+
*/
|
|
7314
|
+
|
|
7315
|
+
/**
|
|
7316
|
+
* A chunked source `parseStream` can consume: a Node `Readable`, any
|
|
7317
|
+
* async-iterable, or any plain iterable of chunks. Chunks are `string`
|
|
7318
|
+
* (text stream) or `Buffer`/`Uint8Array` (binary stream). A real Node stream in
|
|
7319
|
+
* binary mode yields `Buffer`s; in text mode it yields `string`s: a source is
|
|
7320
|
+
* expected to be **homogeneous** (all-text or all-binary), which every real
|
|
7321
|
+
* `Readable` is. A binary chunk is decoded 1:1 via `latin1` (a lossless
|
|
7322
|
+
* byte↔codepoint mapping) so each message's own MSH-18 charset resolution runs
|
|
7323
|
+
* on its original bytes, exactly as {@link splitBatch} does.
|
|
7324
|
+
*
|
|
7325
|
+
* @example
|
|
7326
|
+
* ```ts
|
|
7327
|
+
* import { createReadStream } from "node:fs";
|
|
7328
|
+
* import { parseStream } from "@cosyte/hl7";
|
|
7329
|
+
*
|
|
7330
|
+
* const src: Hl7StreamSource = createReadStream("feed.hl7");
|
|
7331
|
+
* for await (const entry of parseStream(src)) {
|
|
7332
|
+
* if (entry.ok) handle(entry.message);
|
|
7333
|
+
* else quarantine(entry.raw, entry.error.code);
|
|
7334
|
+
* }
|
|
7335
|
+
* ```
|
|
7336
|
+
*/
|
|
7337
|
+
type Hl7StreamSource = AsyncIterable<string | Buffer | Uint8Array> | Iterable<string | Buffer | Uint8Array>;
|
|
7338
|
+
/**
|
|
7339
|
+
* One message yielded by {@link parseStream}. Discriminated on `ok`, mirroring
|
|
7340
|
+
* {@link splitBatch}'s per-message entry: a successful parse carries the
|
|
7341
|
+
* `Hl7Message` (whose own `.warnings` hold per-message Tier-2 deviations); a
|
|
7342
|
+
* message that hit one of the four Tier-3 fatals carries the `Hl7ParseError`
|
|
7343
|
+
* instead: **isolated**, so the rest of the stream still yields.
|
|
7344
|
+
*
|
|
7345
|
+
* `raw` is the message's verbatim source (re-parseable by {@link parseHL7});
|
|
7346
|
+
* `position.segmentIndex` is the message's `MSH` (or, for stray pre-`MSH`
|
|
7347
|
+
* content, its first) segment index in the overall stream: the streaming
|
|
7348
|
+
* analogue of {@link splitBatch}'s message position. `streamWarnings` holds
|
|
7349
|
+
* **stream-level** diagnostics that are not per-message parse warnings: today
|
|
7350
|
+
* only {@link unterminatedStreamMessage} on a final message that lacked a
|
|
7351
|
+
* terminator. It is kept separate from `message.warnings` (which stays exactly
|
|
7352
|
+
* what a whole-buffer `parseHL7` of the same bytes would produce) and is
|
|
7353
|
+
* **empty** for every message but a possibly-truncated final one. Both the entry
|
|
7354
|
+
* and `streamWarnings` are frozen.
|
|
7355
|
+
*
|
|
7356
|
+
* @example
|
|
7357
|
+
* ```ts
|
|
7358
|
+
* import { parseStream, WARNING_CODES } from "@cosyte/hl7";
|
|
7359
|
+
* for await (const entry of parseStream(source)) {
|
|
7360
|
+
* for (const w of entry.streamWarnings) {
|
|
7361
|
+
* if (w.code === WARNING_CODES.UNTERMINATED_STREAM_MESSAGE) {
|
|
7362
|
+
* // the feed may have been cut off mid-message
|
|
7363
|
+
* }
|
|
7364
|
+
* }
|
|
7365
|
+
* }
|
|
7366
|
+
* ```
|
|
7367
|
+
*/
|
|
7368
|
+
type StreamMessageEntry = {
|
|
7369
|
+
readonly ok: true;
|
|
7370
|
+
readonly message: Hl7Message;
|
|
7371
|
+
readonly raw: string;
|
|
7372
|
+
readonly position: Hl7Position;
|
|
7373
|
+
readonly streamWarnings: readonly Hl7ParseWarning[];
|
|
7374
|
+
} | {
|
|
7375
|
+
readonly ok: false;
|
|
7376
|
+
readonly error: Hl7ParseError;
|
|
7377
|
+
readonly raw: string;
|
|
7378
|
+
readonly position: Hl7Position;
|
|
7379
|
+
readonly streamWarnings: readonly Hl7ParseWarning[];
|
|
7380
|
+
};
|
|
7381
|
+
/**
|
|
7382
|
+
* Incrementally parse a chunked HL7 v2 byte / text stream, yielding one
|
|
7383
|
+
* {@link StreamMessageEntry} per `MSH`-delimited message as its boundary
|
|
7384
|
+
* completes, with **O(one-message)** memory: the whole stream is never
|
|
7385
|
+
* retained. Demarcates by `MSH` boundaries inside the optional
|
|
7386
|
+
* `[FHS] { [BHS] { MSH… } [BTS] } [FTS]` batch frame (HL7 v2 Ch. 2 §2.10.3):
|
|
7387
|
+
*
|
|
7388
|
+
* - a message split across chunk boundaries (mid-segment, mid-field, even
|
|
7389
|
+
* mid-`MSH|^~\&`) is reassembled correctly: feeding the same bytes in 1-byte
|
|
7390
|
+
* chunks vs. one chunk yields **identical** messages;
|
|
7391
|
+
* - `\r`, `\r\n`, and `\n` segment terminators are all tolerated (a `\r\n` split
|
|
7392
|
+
* across a chunk boundary is not mistaken for a bare `\r`);
|
|
7393
|
+
* - each message is parsed by the shipped {@link parseHL7} (no second grammar),
|
|
7394
|
+
* `ok` entries carry the `Hl7Message`, a Tier-3 fatal is an isolated failure
|
|
7395
|
+
* entry; a **malformed message never suppresses later messages**;
|
|
7396
|
+
* - batch-envelope segments (`FHS`/`BHS`/`BTS`/`FTS`) are treated as boundaries
|
|
7397
|
+
* and never yielded as messages, so `yielded count == MSH count` (envelope
|
|
7398
|
+
* count reconciliation is {@link splitBatch}'s job);
|
|
7399
|
+
* - a final message with no trailing terminator is still yielded, flagged with a
|
|
7400
|
+
* stream-level {@link unterminatedStreamMessage} warning: **never a throw, the
|
|
7401
|
+
* tail is never dropped**.
|
|
7402
|
+
*
|
|
7403
|
+
* The second argument, when given, is forwarded verbatim to {@link parseHL7} for
|
|
7404
|
+
* **each** message (profile, `strict`, `charset`, `dateFormats`, …), exactly as
|
|
7405
|
+
* {@link splitBatch} forwards it.
|
|
7406
|
+
*
|
|
7407
|
+
* @example
|
|
7408
|
+
* ```ts
|
|
7409
|
+
* import { parseStream } from "@cosyte/hl7";
|
|
7410
|
+
*
|
|
7411
|
+
* async function* chunks() {
|
|
7412
|
+
* yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|1|P|2.5\rPID|||1\r";
|
|
7413
|
+
* yield "MSH|^~\\&|A|F|B|G|20260101||ADT^A01|2|P|2.5\rPID|||2\r";
|
|
7414
|
+
* }
|
|
7415
|
+
*
|
|
7416
|
+
* let count = 0;
|
|
7417
|
+
* for await (const entry of parseStream(chunks())) {
|
|
7418
|
+
* if (entry.ok) count += 1; // 2: one per MSH, streamed, released each time
|
|
7419
|
+
* }
|
|
7420
|
+
* ```
|
|
7421
|
+
*/
|
|
7422
|
+
declare function parseStream(source: Hl7StreamSource): AsyncGenerator<StreamMessageEntry, void, void>;
|
|
7423
|
+
declare function parseStream(source: Hl7StreamSource, profile: Profile): AsyncGenerator<StreamMessageEntry, void, void>;
|
|
7424
|
+
declare function parseStream(source: Hl7StreamSource, options: ParseOptions): AsyncGenerator<StreamMessageEntry, void, void>;
|
|
7425
|
+
|
|
5843
7426
|
/**
|
|
5844
7427
|
* Public entry point for the `@cosyte/hl7` package.
|
|
5845
7428
|
*
|
|
@@ -5861,4 +7444,4 @@ declare function splitBatch(raw: string | Buffer, options: ParseOptions): BatchS
|
|
|
5861
7444
|
*/
|
|
5862
7445
|
declare const VERSION: string;
|
|
5863
7446
|
|
|
5864
|
-
export { ACK_CODES, ACK_CONDITIONS, type AckCode, type AckCondition, type AckErrorDetail, type AckErrorEntry, type AckMode, type Acknowledgment, type Allergy, type Appointment, type AppointmentResource, BUILTIN_DATE_FALLBACKS, type Batch, type BatchEnvelopeName, type BatchEnvelopeSegment, type BatchMessageEntry, type BatchSplitResult, type BuildAckOptions, type BuildMessageInit, type CE, type CWE, type CX, type Charge, type CharsetResolution, type CharsetTreatment, type ClinicalDocument, type CodedSystemFields, type CodingSystemInfo, type CustomSegmentDefinition, DEFAULT_ENCODING_CHARACTERS, type DefineProfileOptions, type Diagnosis, type DotPath, type DtmParts, type DtmPrecision, type DtmToDateOptions, ERR_CONDITION_CODES, ERR_CONDITION_CODE_SYSTEM, ERR_SEVERITIES, type EncodingCharacters, type ErrSeverity, type ExpectedSegmentGroup, FATAL_CODES, type FatalCode, Field, type HD, namespace as HL7, Hl7Message, Hl7ParseError, type Hl7ParseWarning, type Hl7Position, type IdentityEvent, type IdentityEventKind, type IdentityParty, type IdentityRole, type Immunization, type ImmunizationRecordOrigin, type Insurance, KNOWN_CODING_SYSTEMS, KNOWN_SEGMENTS, type KnownCodingSystem, MESSAGE_STRUCTURE_DEFINITIONS, type Medication, type MedicationAmount, type MedicationComponent, type MedicationContext, type MedicationRoute, type MedicationStrength, type MessageStructure, type MessageStructureDefinition, type Meta, type NM, type NextOfKin, type Observation, type ObservationBase, type OnWarningCallback, type Order, type OrderTiming, type PL, type ParseOptions, type Patient, type Profile, ProfileDefinitionError, type RawComponent, type RawField, type RawRepetition, type RawSegment, type RepeatPattern, type RepeatPatternKind, type SN, SUPPORTED_DATE_TOKENS, Segment, type SerializedMessage, type StructureGroup, type TS, type TimingQuantity, VERSION, type Visit, WARNING_CODES, type WarningCode, type XAD, type XCN, type XPN, type XTN, ackNoCorrelationId, alternateCodingSystemOf, analyzeMessageStructure, batchCountMismatch, batchMissingTrailer, buildAck, buildMessage, canonicalCharset, codingSystem, codingSystemOf, defineProfile, detectAckMode, downgradePositiveAck, dtmToDate, duplicateRequiredSegment, encodingMismatch, extraFields, fieldWhitespaceTrimmed, formatDtm, getDefaultProfile, interpretAck, isPositiveAck, mergeMissingPriorOrSurvivor, missingExpectedGroup, missingRequiredField, mllpFramingStripped, outOfOrderSegment, parseCe, parseCwe, parseCx, parseDtm, parseHL7, parseHd, parseNm, parsePath, parsePl, parseSn, parseTs, parseXad, parseXcn, parseXpn, parseXtn, pickMrn, profiles, reescape, resolveCharset, resolvePath, segmentCase, setDefaultProfile, splitBatch, timestampFallbackFormat, unescape, unknownCharset, unknownEscapeSequence, unknownSegment, unsupportedCharset, unterminatedEscapeSequence, versionMismatch };
|
|
7447
|
+
export { ACK_CODES, ACK_CONDITIONS, type AckCode, type AckCondition, type AckErrorDetail, type AckErrorEntry, type AckMode, type Acknowledgment, type AdtEvent, type AdtPatient, type AdtVisit, type Allergy, type Appointment, type AppointmentResource, BUILTIN_DATE_FALLBACKS, type Batch, type BatchEnvelopeName, type BatchEnvelopeSegment, type BatchMessageEntry, type BatchSplitResult, type BuildAckOptions, type BuildAdtInit, type BuildMessageInit, type BuildOruInit, type CE, type CWE, type CX, type Cardinality, type Charge, type CharsetResolution, type CharsetTreatment, type ClinicalDocument, type CodedSystemFields, type CodingSystemInfo, type CompositeKind, type CompositeValueByKind, type ConformanceFinding, type ConformanceProfile, type ConformanceResult, type CustomSegmentDefinition, DEFAULT_ENCODING_CHARACTERS, type DefineProfileOptions, type Diagnosis, type DotPath, type DtmParts, type DtmPrecision, type DtmToDateOptions, ERR_CONDITION_CODES, ERR_CONDITION_CODE_SYSTEM, ERR_SEVERITIES, type EncodingCharacters, type ErrSeverity, type ExpectedSegmentGroup, FATAL_CODES, FINDING_CODES, type FatalCode, Field, type FieldRule, type FindingCode, type FindingLocus, type FindingSeverity, type HD, namespace as HL7, Hl7Message, Hl7ParseError, type Hl7ParseWarning, type Hl7Position, type Hl7StreamSource, type IdentityEvent, type IdentityEventKind, type IdentityParty, type IdentityRole, type Immunization, type ImmunizationRecordOrigin, type Insurance, KNOWN_CODING_SYSTEMS, KNOWN_SEGMENTS, type KnownCodingSystem, MESSAGE_STRUCTURE_DEFINITIONS, type Medication, type MedicationAmount, type MedicationComponent, type MedicationContext, type MedicationRoute, type MedicationStrength, type MessageStructure, type MessageStructureDefinition, type Meta, type NM, type NextOfKin, type Observation, type ObservationBase, type OnWarningCallback, type Order, type OrderTiming, type OruObservation, type OruOrder, type OruPatient, type PL, type ParseOptions, type Patient, type Profile, ProfileDefinitionError, type RawComponent, type RawField, type RawRepetition, type RawSegment, type RenderTextOptions, type RenderedText, type RepeatPattern, type RepeatPatternKind, type SN, SUPPORTED_DATE_TOKENS, Segment, type SegmentRule, type SerializedMessage, type StreamMessageEntry, type StructureGroup, type TS, type TextRun, type TimingQuantity, USAGE_CODES, type UsageCode, VERSION, type Visit, WARNING_CODES, type WarningCode, type XAD, type XCN, type XPN, type XTN, ackNoCorrelationId, alternateCodingSystemOf, analyzeMessageStructure, batchCountMismatch, batchMissingTrailer, buildAck, buildAdt, buildMessage, buildOru, canonicalCharset, codingSystem, codingSystemOf, index as conformance, decodeText, defineConformanceProfile, defineProfile, detectAckMode, downgradePositiveAck, dtmToDate, duplicateRequiredSegment, encodeCe, encodeComposite, encodeCompositeReps, encodeCwe, encodeCx, encodeHd, encodeNm, encodePl, encodeText, encodeTs, encodeXad, encodeXcn, encodeXpn, encodeXtn, encodingMismatch, extraFields, fieldWhitespaceTrimmed, formatDtm, getDefaultProfile, interpretAck, isPositiveAck, mergeMissingPriorOrSurvivor, missingExpectedGroup, missingRequiredField, mllpFramingStripped, outOfOrderSegment, parseCe, parseCwe, parseCx, parseDtm, parseHL7, parseHd, parseNm, parsePath, parsePl, parseSn, parseStream, parseTs, parseXad, parseXcn, parseXpn, parseXtn, pickMrn, profiles, reescape, renderText, resolveCharset, resolvePath, segmentCase, setDefaultProfile, splitBatch, index$1 as text, timestampFallbackFormat, unescape, unknownCharset, unknownEscapeSequence, unknownSegment, unsupportedCharset, unterminatedEscapeSequence, unterminatedStreamMessage, validateAgainstProfile, versionMismatch };
|