@cosyte/astm 0.0.3 → 0.0.5

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/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/common/errors.ts
2
2
  var FATAL_CODES = {
3
- /** Input was empty or whitespace-only — there is nothing to parse. Shared across layers. */
3
+ /** Input was empty or whitespace-only: there is nothing to parse. Shared across layers. */
4
4
  EMPTY_INPUT: "EMPTY_INPUT",
5
- /** The first record is not an `H` (header) record — an ASTM message must lead with `H`. */
5
+ /** The first record is not an `H` (header) record: an ASTM message must lead with `H`. */
6
6
  ASTM_RECORD_NO_HEADER: "ASTM_RECORD_NO_HEADER",
7
7
  /** The `H` record is too short to declare the four delimiters (field/repeat/component/escape). */
8
8
  ASTM_RECORD_UNDECLARED_DELIMITERS: "ASTM_RECORD_UNDECLARED_DELIMITERS"
@@ -119,21 +119,21 @@ function primaryCode(u) {
119
119
 
120
120
  // src/common/warnings.ts
121
121
  var WARNING_CODES = {
122
- /** A record's type letter is not one of the modeled types — surfaced as an unsupported record. */
122
+ /** A record's type letter is not one of the modeled types: surfaced as an unsupported record. */
123
123
  ASTM_RECORD_UNKNOWN_TYPE: "ASTM_RECORD_UNKNOWN_TYPE",
124
- /** The header declared delimiters other than the canonical `H|\^&` — tolerated, noted. */
124
+ /** The header declared delimiters other than the canonical `H|\^&`: tolerated, noted. */
125
125
  ASTM_NONSTANDARD_DELIMITERS: "ASTM_NONSTANDARD_DELIMITERS",
126
- /** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&` — preserved verbatim. */
126
+ /** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&`: preserved verbatim. */
127
127
  ASTM_UNKNOWN_ESCAPE_SEQUENCE: "ASTM_UNKNOWN_ESCAPE_SEQUENCE",
128
128
  /**
129
129
  * A result value field carried an *unescaped* component delimiter, so it split into more than one
130
- * component. Both the full raw value and the split are surfaced and this warning fires — the
130
+ * component. Both the full raw value and the split are surfaced and this warning fires: the
131
131
  * ambiguity is never resolved silently into a truncated value.
132
132
  */
133
133
  ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT: "ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT",
134
134
  /**
135
135
  * A result's abnormal-flag field (`R` field 7) carried a letter outside HL7 Table 0078. The flag is
136
- * surfaced as `undefined` — never dropped, and **never coerced to `normal`** (a clinical error).
136
+ * surfaced as `undefined`: never dropped, and **never coerced to `normal`** (a clinical error).
137
137
  */
138
138
  ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG: "ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG",
139
139
  /**
@@ -143,7 +143,7 @@ var WARNING_CODES = {
143
143
  ASTM_RECORD_UNDEFINED_RESULT_STATUS: "ASTM_RECORD_UNDEFINED_RESULT_STATUS",
144
144
  /**
145
145
  * A result's reference-range field (`R` field 6) did not match a recognized form (`low-high`,
146
- * `<high`, `>low`). The text is surfaced verbatim as `unparsed` — a bound is **never fabricated**.
146
+ * `<high`, `>low`). The text is surfaced verbatim as `unparsed`: a bound is **never fabricated**.
147
147
  */
148
148
  ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE: "ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE",
149
149
  /**
@@ -152,35 +152,57 @@ var WARNING_CODES = {
152
152
  */
153
153
  ASTM_RECORD_UNITS_ABSENT: "ASTM_RECORD_UNITS_ABSENT",
154
154
  /**
155
- * A `C` (comment) record had no valid preceding `H`/`P`/`O`/`R` parent — an **orphan**. The comment
155
+ * A `C` (comment) record had no valid preceding `H`/`P`/`O`/`R` parent: an **orphan**. The comment
156
156
  * is attached to the message root (`attachedToRoot: true`) and surfaced, **never dropped**.
157
157
  */
158
158
  ASTM_RECORD_ORPHAN_COMMENT: "ASTM_RECORD_ORPHAN_COMMENT",
159
159
  /**
160
160
  * A `YYYYMMDDHHMMSS` timestamp had an odd digit run that truncates a two-digit component in half
161
161
  * (e.g. a partial day/hour). The raw run is preserved and the structured value stops at the last
162
- * **complete** component — the dangling digit is **never zero-filled into a fabricated time**.
162
+ * **complete** component: the dangling digit is **never zero-filled into a fabricated time**.
163
163
  */
164
164
  ASTM_RECORD_PARTIAL_TIMESTAMP: "ASTM_RECORD_PARTIAL_TIMESTAMP",
165
165
  /**
166
166
  * A `Q` (request-information) record carried a request-information status code (field 13). The code
167
167
  * *set* is `[OSS-derived / paywalled]` with no publicly-groundable enumeration, so the parser
168
168
  * interprets **none** of them: the status is surfaced verbatim and this value-free warning flags that
169
- * it was passed through **uninterpreted** — never mapped to a guessed meaning.
169
+ * it was passed through **uninterpreted**, never mapped to a guessed meaning.
170
170
  */
171
171
  ASTM_RECORD_UNINTERPRETED_QUERY_STATUS: "ASTM_RECORD_UNINTERPRETED_QUERY_STATUS",
172
172
  /**
173
- * A message carried **both** a `Q` (request) and an `R` (result) record — a contradictory shape. The
173
+ * A message carried **both** a `Q` (request) and an `R` (result) record: a contradictory shape. The
174
174
  * message is classified `host-query` (the `Q` **dominates**, so it is never read as a result set) and
175
175
  * this warning flags the anomaly. Positional context only; no field value.
176
176
  */
177
177
  ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND: "ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND",
178
+ /**
179
+ * A later `H` (header) record declared a **different** delimiter set from the one in force, and the
180
+ * parser **followed it**: that header and every record after it are read with the newly-declared
181
+ * delimiters, until the next `H`. Records already read keep the set that was in force when they were
182
+ * read: a redeclaration never reinterprets bytes that have already been consumed.
183
+ *
184
+ * A stream carrying several messages back to back is ordinary, and a header that simply **repeats**
185
+ * the delimiters already in force is a no-op that warns nothing. This code fires only when the set
186
+ * actually changes, because that is the point at which a reader still using the old set would begin
187
+ * merging fields together.
188
+ */
189
+ ASTM_RECORD_DELIMITERS_REDECLARED: "ASTM_RECORD_DELIMITERS_REDECLARED",
190
+ /**
191
+ * A later `H` (header) record could not declare a usable delimiter set: it was too short, or the
192
+ * field separator it named also appeared among the other three, leaving the four roles
193
+ * indistinguishable. The delimiters **already in force are kept** and every record is still surfaced;
194
+ * a set is never guessed and no record is dropped.
195
+ *
196
+ * The same condition on the *first* header is unrecoverable and remains the
197
+ * `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal: there is no earlier set to fall back to.
198
+ */
199
+ ASTM_RECORD_UNREADABLE_REDECLARATION: "ASTM_RECORD_UNREADABLE_REDECLARATION",
178
200
  /**
179
201
  * The downgraded form an active vendor {@link AstmProfile} produces from a deviation it *expects*
180
202
  * (see `src/profiles/`). The original warning is **never dropped**: its code moves to
181
203
  * {@link AstmRecordWarning.toleratedCode}, the warning is re-badged `PROFILE_QUIRK_APPLIED` with
182
204
  * `expected: true` and the tolerating profile named, so a consumer can filter known, grounded noise
183
- * while the fact of the deviation — and where it was — survives. A profile can only ever reach this
205
+ * while the fact of the deviation, and where it was, survives. A profile can only ever reach this
184
206
  * path for a **non-safety-critical** code (enforced at profile-definition time); a safety-critical
185
207
  * deviation (a result value, flag, status, patient identifier, code system, or a frame-integrity
186
208
  * warning) can **never** be tolerated, so it can never be re-badged here.
@@ -190,14 +212,28 @@ var WARNING_CODES = {
190
212
  function unknownRecordType(position) {
191
213
  return {
192
214
  code: WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE,
193
- message: "Unrecognized record type \u2014 surfaced verbatim as an unsupported record.",
215
+ message: "Unrecognized record type, surfaced verbatim as an unsupported record.",
194
216
  position
195
217
  };
196
218
  }
197
219
  function nonStandardDelimiters(position) {
198
220
  return {
199
221
  code: WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS,
200
- message: "Header declared non-canonical delimiters \u2014 read from the header and honored.",
222
+ message: "Header declared non-canonical delimiters, read from the header and honored.",
223
+ position
224
+ };
225
+ }
226
+ function delimitersRedeclared(position) {
227
+ return {
228
+ code: WARNING_CODES.ASTM_RECORD_DELIMITERS_REDECLARED,
229
+ message: "A later header declared different delimiters, honored from that header onward; earlier records keep the set they were read with.",
230
+ position
231
+ };
232
+ }
233
+ function unreadableRedeclaration(position) {
234
+ return {
235
+ code: WARNING_CODES.ASTM_RECORD_UNREADABLE_REDECLARATION,
236
+ message: "A later header could not declare a usable delimiter set, the delimiters already in force were kept.",
201
237
  position
202
238
  };
203
239
  }
@@ -211,63 +247,63 @@ function unknownEscapeSequence(position) {
211
247
  function ambiguousValueSplit(position) {
212
248
  return {
213
249
  code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT,
214
- message: "Result value contained an unescaped component delimiter \u2014 full raw value and split both surfaced.",
250
+ message: "Result value contained an unescaped component delimiter, full raw value and split both surfaced.",
215
251
  position
216
252
  };
217
253
  }
218
254
  function undefinedAbnormalFlag(position) {
219
255
  return {
220
256
  code: WARNING_CODES.ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG,
221
- message: "Abnormal flag is not in HL7 Table 0078 \u2014 surfaced as undefined, never as normal.",
257
+ message: "Abnormal flag is not in HL7 Table 0078, surfaced as undefined, never as normal.",
222
258
  position
223
259
  };
224
260
  }
225
261
  function undefinedResultStatus(position) {
226
262
  return {
227
263
  code: WARNING_CODES.ASTM_RECORD_UNDEFINED_RESULT_STATUS,
228
- message: "Result status is not a recognized status letter \u2014 surfaced as undefined.",
264
+ message: "Result status is not a recognized status letter, surfaced as undefined.",
229
265
  position
230
266
  };
231
267
  }
232
268
  function unparseableReferenceRange(position) {
233
269
  return {
234
270
  code: WARNING_CODES.ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE,
235
- message: "Reference range did not match a recognized form \u2014 surfaced verbatim, no bound invented.",
271
+ message: "Reference range did not match a recognized form, surfaced verbatim, no bound invented.",
236
272
  position
237
273
  };
238
274
  }
239
275
  function unitsAbsent(position) {
240
276
  return {
241
277
  code: WARNING_CODES.ASTM_RECORD_UNITS_ABSENT,
242
- message: "Numeric result value carried no units \u2014 never defaulted, guessed, or converted.",
278
+ message: "Numeric result value carried no units, never defaulted, guessed, or converted.",
243
279
  position
244
280
  };
245
281
  }
246
282
  function orphanComment(position) {
247
283
  return {
248
284
  code: WARNING_CODES.ASTM_RECORD_ORPHAN_COMMENT,
249
- message: "Comment had no valid preceding parent \u2014 attached to the message root, never dropped.",
285
+ message: "Comment had no valid preceding parent, attached to the message root, never dropped.",
250
286
  position
251
287
  };
252
288
  }
253
289
  function partialTimestamp(position) {
254
290
  return {
255
291
  code: WARNING_CODES.ASTM_RECORD_PARTIAL_TIMESTAMP,
256
- message: "Timestamp digit run truncates a component \u2014 preserved verbatim, never zero-filled.",
292
+ message: "Timestamp digit run truncates a component, preserved verbatim, never zero-filled.",
257
293
  position
258
294
  };
259
295
  }
260
296
  function uninterpretedQueryStatus(position) {
261
297
  return {
262
298
  code: WARNING_CODES.ASTM_RECORD_UNINTERPRETED_QUERY_STATUS,
263
- message: "Query request-information status surfaced verbatim \u2014 code set paywalled, never interpreted.",
299
+ message: "Query request-information status surfaced verbatim, code set paywalled, never interpreted.",
264
300
  position
265
301
  };
266
302
  }
267
303
  function ambiguousMessageKind(position) {
268
304
  return {
269
305
  code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND,
270
- message: "Message carried both a Q (request) and an R (result) record \u2014 classified host-query; Q dominates.",
306
+ message: "Message carried both a Q (request) and an R (result) record, classified host-query; Q dominates.",
271
307
  position
272
308
  };
273
309
  }
@@ -531,12 +567,12 @@ var FRAME_WARNING_CODES = {
531
567
  * A frame's two-hex-char checksum did not match the modulo-256 sum recomputed over its bytes. The
532
568
  * frame is surfaced with `trusted: false` and its text is **never merged** into a reassembled
533
569
  * record (default **warn** in lenient mode, escalated to a thrown error in `strict`). Corruption is
534
- * never silently trusted — the "checksums are routinely not validated" claim was refuted; we
570
+ * never silently trusted: the "checksums are routinely not validated" claim was refuted; we
535
571
  * validate.
536
572
  */
537
573
  ASTM_FRAME_BAD_CHECKSUM: "ASTM_FRAME_BAD_CHECKSUM",
538
574
  /**
539
- * A frame's sequence number was not the expected next value (`1 → … → 7 → 0 → …`) — a frame was
575
+ * A frame's sequence number was not the expected next value (`1 → … → 7 → 0 → …`): a frame was
540
576
  * possibly dropped. The stream is **never silently concatenated across the gap** as if contiguous;
541
577
  * the in-progress record is tainted and not emitted as a clean reassembly.
542
578
  */
@@ -550,7 +586,7 @@ var FRAME_WARNING_CODES = {
550
586
  ASTM_FRAME_UNTERMINATED: "ASTM_FRAME_UNTERMINATED",
551
587
  /**
552
588
  * A frame's record text exceeded the 240-byte limit without a split. The frame is still surfaced
553
- * (and, if its checksum validates, reassembled) — the deviation is flagged, not silently dropped
589
+ * (and, if its checksum validates, reassembled): the deviation is flagged, not silently dropped
554
590
  * (warn in lenient, thrown in `strict`).
555
591
  */
556
592
  ASTM_FRAME_OVERSIZE: "ASTM_FRAME_OVERSIZE"
@@ -558,28 +594,28 @@ var FRAME_WARNING_CODES = {
558
594
  function frameBadChecksum(position) {
559
595
  return {
560
596
  code: FRAME_WARNING_CODES.ASTM_FRAME_BAD_CHECKSUM,
561
- message: "Frame checksum mismatch \u2014 surfaced untrusted, never merged into a record.",
597
+ message: "Frame checksum mismatch, surfaced untrusted, never merged into a record.",
562
598
  position
563
599
  };
564
600
  }
565
601
  function frameSequenceGap(position) {
566
602
  return {
567
603
  code: FRAME_WARNING_CODES.ASTM_FRAME_SEQUENCE_GAP,
568
- message: "Frame number out of sequence \u2014 possible dropped frame, never silently bridged.",
604
+ message: "Frame number out of sequence, possible dropped frame, never silently bridged.",
569
605
  position
570
606
  };
571
607
  }
572
608
  function frameUnterminated(position) {
573
609
  return {
574
610
  code: FRAME_WARNING_CODES.ASTM_FRAME_UNTERMINATED,
575
- message: "Unterminated frame \u2014 partial bytes surfaced untrusted, no partial record invented.",
611
+ message: "Unterminated frame, partial bytes surfaced untrusted, no partial record invented.",
576
612
  position
577
613
  };
578
614
  }
579
615
  function frameOversize(position) {
580
616
  return {
581
617
  code: FRAME_WARNING_CODES.ASTM_FRAME_OVERSIZE,
582
- message: "Frame text exceeded the 240-byte limit without a split \u2014 flagged, never dropped.",
618
+ message: "Frame text exceeded the 240-byte limit without a split, flagged, never dropped.",
583
619
  position
584
620
  };
585
621
  }
@@ -588,21 +624,21 @@ function frameOversize(position) {
588
624
  var LTP_WARNING_CODES = {
589
625
  /**
590
626
  * The transport detector could not tell a framed stream from a raw (unframed) one from its leading
591
- * byte — it was neither `STX`/`ENQ` nor a bare record letter. It **defaults to framed** and warns,
627
+ * byte: it was neither `STX`/`ENQ` nor a bare record letter. It **defaults to framed** and warns,
592
628
  * never guessing silently into data loss; a profile override forces the mode. (cobas b121 drops
593
- * framing over TCP; cobas 4800 / Iguana retain it — both realities exist, so an unrecognizable lead
629
+ * framing over TCP; cobas 4800 / Iguana retain it, both realities exist, so an unrecognizable lead
594
630
  * is defaulted, not assumed.)
595
631
  */
596
632
  ASTM_LTP_AMBIGUOUS_TRANSPORT: "ASTM_LTP_AMBIGUOUS_TRANSPORT",
597
633
  /**
598
- * A control event arrived in a protocol state that did not expect it — e.g. an inbound `ACK`/`NAK`
634
+ * A control event arrived in a protocol state that did not expect it: e.g. an inbound `ACK`/`NAK`
599
635
  * at a receiver (which sends, never receives, those), or an `ENQ` mid-transfer. The event is
600
636
  * surfaced and handled defensively (an unexpected `ACK`/`NAK` is **never** read as acceptance of
601
637
  * data); it never advances the transfer as if valid.
602
638
  */
603
639
  ASTM_LTP_UNEXPECTED_EVENT: "ASTM_LTP_UNEXPECTED_EVENT",
604
640
  /**
605
- * The receiver rejected a received frame with a `NAK` instead of accepting it — because its
641
+ * The receiver rejected a received frame with a `NAK` instead of accepting it: because its
606
642
  * checksum failed, it was unterminated, or its frame number was out of sequence. The frame's text
607
643
  * is **never** appended to the record and the transfer does **not** advance; the receiver awaits the
608
644
  * sender's retransmit. This is the protocol-level face of the frame codec's fail-safe: a bad frame
@@ -613,20 +649,20 @@ var LTP_WARNING_CODES = {
613
649
  function ltpAmbiguousTransport() {
614
650
  return {
615
651
  code: LTP_WARNING_CODES.ASTM_LTP_AMBIGUOUS_TRANSPORT,
616
- message: "Leading byte was neither a frame start (STX/ENQ) nor a record letter \u2014 defaulted to framed; override with a profile to force raw."
652
+ message: "Leading byte was neither a frame start (STX/ENQ) nor a record letter, defaulted to framed; override with a profile to force raw."
617
653
  };
618
654
  }
619
655
  function ltpUnexpectedEvent(frameNumber) {
620
656
  return {
621
657
  code: LTP_WARNING_CODES.ASTM_LTP_UNEXPECTED_EVENT,
622
- message: "Control event arrived in a state that did not expect it \u2014 handled defensively, never read as acceptance.",
658
+ message: "Control event arrived in a state that did not expect it, handled defensively, never read as acceptance.",
623
659
  ...frameNumber !== void 0 ? { frameNumber } : {}
624
660
  };
625
661
  }
626
662
  function ltpFrameRejected(frameNumber) {
627
663
  return {
628
664
  code: LTP_WARNING_CODES.ASTM_LTP_FRAME_REJECTED,
629
- message: "Frame rejected (bad checksum, unterminated, or out of sequence) \u2014 NAK sent, retransmit expected, never accepted.",
665
+ message: "Frame rejected (bad checksum, unterminated, or out of sequence), NAK sent, retransmit expected, never accepted.",
630
666
  ...frameNumber !== void 0 ? { frameNumber } : {}
631
667
  };
632
668
  }
@@ -734,7 +770,7 @@ function validateTolerations(tolerate, profileName) {
734
770
  }
735
771
  if (isSafetyCriticalCode(t.code)) {
736
772
  throw new AstmProfileDefinitionError(
737
- `Profile '${profileName}' may not tolerate '${t.code}' \u2014 it is a safety-critical warning code (a result value / flag / status / range / units, patient or comment context, message-kind ambiguity, code system, or a frame / LTP integrity warning). A profile quiets benign structural noise, never a deviation that could change a clinical reading or corrupt the wire \u2014 it can never make a bad checksum "ok" or a cancelled result read "final."`,
773
+ `Profile '${profileName}' may not tolerate '${t.code}': it is a safety-critical warning code (a result value / flag / status / range / units, patient or comment context, message-kind ambiguity, code system, or a frame / LTP integrity warning). A profile quiets benign structural noise, never a deviation that could change a clinical reading or corrupt the wire: it can never make a bad checksum "ok" or a cancelled result read "final."`,
738
774
  profileName
739
775
  );
740
776
  }
@@ -777,14 +813,14 @@ function defineAstmProfile(opts) {
777
813
  // src/profiles/default.ts
778
814
  var defaultProfile = defineAstmProfile({
779
815
  name: "default",
780
- description: "Conservative baseline \u2014 tolerates nothing; every deviation surfaces as a warning.",
816
+ description: "Conservative baseline: tolerates nothing; every deviation surfaces as a warning.",
781
817
  tolerate: []
782
818
  });
783
819
 
784
820
  // src/profiles/reference-corpus.ts
785
821
  var referenceCorpus = defineAstmProfile({
786
822
  name: "referenceCorpus",
787
- description: "Non-standard-escape tolerance grounded firsthand in the escape-agnostic OSS reference corpus (python-astm / senaite). Syntactic encoding noise only \u2014 the value is preserved verbatim, never a safety-critical value.",
823
+ description: "Non-standard-escape tolerance grounded firsthand in the escape-agnostic OSS reference corpus (python-astm / senaite). Syntactic encoding noise only: the value is preserved verbatim, never a safety-critical value.",
788
824
  provenance: {
789
825
  source: "kxepal/python-astm codec.py (BSD) + senaite.astm / senaite.lis2a",
790
826
  reference: "https://github.com/kxepal/python-astm/blob/master/astm/codec.py ; https://github.com/senaite/senaite.astm",
@@ -897,6 +933,17 @@ function tokenizeRecord(record, d, onUnknownEscape) {
897
933
  const rawFields = splitEscapeAware(record, d.field, d.escape);
898
934
  return rawFields.map((raw, fieldIndex) => toField(raw, d, () => onUnknownEscape?.(fieldIndex)));
899
935
  }
936
+ function tokenizeHeader(record, d, onUnknownEscape) {
937
+ const defEnd = record.indexOf(d.field, 2);
938
+ const definition = defEnd === -1 ? record.slice(2) : record.slice(2, defEnd);
939
+ const head = [opaqueField(record.slice(0, Math.min(1, record.length))), opaqueField(definition)];
940
+ if (defEnd === -1) return head;
941
+ const data = tokenizeRecord(record.slice(defEnd + 1), d, (i) => onUnknownEscape?.(i + 2));
942
+ return [...head, ...data];
943
+ }
944
+ function opaqueField(raw) {
945
+ return { raw, components: [raw], repeats: [[raw]] };
946
+ }
900
947
  function toField(raw, d, onUnknownEscape) {
901
948
  const rawRepeats = splitEscapeAware(raw, d.repeat, d.escape);
902
949
  const repeats = rawRepeats.map(
@@ -950,9 +997,13 @@ function parseAstmRecords(raw, options = {}) {
950
997
  if (isNonStandard(delimiters)) {
951
998
  warnings.push(nonStandardDelimiters({ recordIndex: 0, recordType: "H" }));
952
999
  }
953
- const built = lines.map(
954
- (line2, recordIndex) => buildRecord(line2, recordIndex, delimiters, warnings)
955
- );
1000
+ let active = delimiters;
1001
+ const built = lines.map((line2, recordIndex) => {
1002
+ if (recordIndex > 0 && line2.charAt(0) === "H") {
1003
+ active = adoptRedeclaredDelimiters(line2, active, recordIndex, warnings);
1004
+ }
1005
+ return buildRecord(line2, recordIndex, active, warnings);
1006
+ });
956
1007
  const records = attachComments(built, warnings);
957
1008
  const classification = classifyMessage(records);
958
1009
  if (classification.hasQuery && classification.hasResults) {
@@ -982,6 +1033,21 @@ function resolveProfile(options) {
982
1033
  if (options.profile !== void 0) return options.profile;
983
1034
  return getDefaultAstmProfile();
984
1035
  }
1036
+ function adoptRedeclaredDelimiters(line2, active, recordIndex, warnings) {
1037
+ const position = { recordIndex, recordType: "H" };
1038
+ const declared = readDelimiters(line2);
1039
+ if (declared === void 0) {
1040
+ warnings.push(unreadableRedeclaration(position));
1041
+ return active;
1042
+ }
1043
+ if (sameDelimiters(declared, active)) return active;
1044
+ warnings.push(delimitersRedeclared(position));
1045
+ if (isNonStandard(declared)) warnings.push(nonStandardDelimiters(position));
1046
+ return declared;
1047
+ }
1048
+ function sameDelimiters(a, b) {
1049
+ return a.field === b.field && a.repeat === b.repeat && a.component === b.component && a.escape === b.escape;
1050
+ }
985
1051
  function decodeBytes(bytes) {
986
1052
  let out = "";
987
1053
  for (const b of bytes) out += String.fromCharCode(b);
@@ -992,11 +1058,12 @@ function astmField(fields, n) {
992
1058
  }
993
1059
  function buildRecord(line2, recordIndex, d, warnings) {
994
1060
  const rawType = line2.charAt(0);
995
- const fields = tokenizeRecord(line2, d, (fieldIndex) => {
1061
+ const onUnknownEscape = (fieldIndex) => {
996
1062
  warnings.push(
997
1063
  unknownEscapeSequence({ recordIndex, recordType: rawType, fieldIndex: fieldIndex + 1 })
998
1064
  );
999
- });
1065
+ };
1066
+ const fields = rawType === "H" ? tokenizeHeader(line2, d, onUnknownEscape) : tokenizeRecord(line2, d, onUnknownEscape);
1000
1067
  switch (rawType) {
1001
1068
  case "H":
1002
1069
  return {
@@ -1017,7 +1084,7 @@ function buildRecord(line2, recordIndex, d, warnings) {
1017
1084
  case "Q":
1018
1085
  return buildQuery(recordIndex, fields, warnings);
1019
1086
  // `M` (manufacturer) and `S` (scientific) are vendor-defined free-form data surfaced VERBATIM and
1020
- // NEVER interpreted into typed clinical fields — the exact wire line is preserved for round-trip.
1087
+ // NEVER interpreted into typed clinical fields: the exact wire line is preserved for round-trip.
1021
1088
  case "M":
1022
1089
  return { type: "M", recordIndex, fields, rawLine: line2 };
1023
1090
  case "S":
@@ -1050,7 +1117,7 @@ function buildPatient(recordIndex, fields, warnings) {
1050
1117
  recordIndex,
1051
1118
  fields,
1052
1119
  ...definedString("seq", fieldScalar(astmField(fields, 2))),
1053
- // The three patient identifiers stay DISTINCT — practice-assigned (3), laboratory-assigned (4),
1120
+ // The three patient identifiers stay DISTINCT: practice-assigned (3), laboratory-assigned (4),
1054
1121
  // and a third ID (5) never collapse into one; conflating them is the primary misfiling path.
1055
1122
  ...definedString("practiceAssignedId", fieldScalar(astmField(fields, 3))),
1056
1123
  ...definedString("laboratoryAssignedId", fieldScalar(astmField(fields, 4))),
@@ -1073,7 +1140,7 @@ function buildOrder(recordIndex, fields) {
1073
1140
  ...hasContent(utidField) ? { universalTestId: recognizeUniversalTestId(utidField.components) } : {},
1074
1141
  // Priority (6), action code (~12), and report type (~26) are surfaced verbatim. The `~` field
1075
1142
  // indices and the code sets are `[OSS-derived]` (paywalled), so they are never mapped to a
1076
- // guessed meaning — see the JSDoc on `OrderRecord`.
1143
+ // guessed meaning: see the JSDoc on `OrderRecord`.
1077
1144
  ...definedString("priority", fieldScalar(astmField(fields, 6))),
1078
1145
  ...definedString("actionCode", fieldScalar(astmField(fields, 12))),
1079
1146
  ...definedString("reportType", fieldScalar(astmField(fields, 26)))
@@ -1218,19 +1285,57 @@ function definedString(key, value) {
1218
1285
 
1219
1286
  // src/records/serialize.ts
1220
1287
  var AstmSerializeError = class extends Error {
1221
- /** Stable discriminant; `ASTM_EMIT_UNENCODABLE_VALUE` for a `CR`/`LF` in a value. */
1288
+ /**
1289
+ * Stable discriminant. `ASTM_EMIT_UNENCODABLE_VALUE` for a `CR`/`LF` in a value;
1290
+ * `ASTM_EMIT_INVALID_DELIMITERS` for a delimiter set that cannot be emitted
1291
+ * reversibly.
1292
+ */
1222
1293
  code;
1223
1294
  /** 0-based ordinal of the record within the message, when known. */
1224
1295
  recordIndex;
1225
1296
  /** @internal */
1226
- constructor(message, recordIndex) {
1297
+ constructor(message, recordIndex, code = "ASTM_EMIT_UNENCODABLE_VALUE") {
1227
1298
  super(message);
1228
1299
  this.name = "AstmSerializeError";
1229
- this.code = "ASTM_EMIT_UNENCODABLE_VALUE";
1300
+ this.code = code;
1230
1301
  if (recordIndex !== void 0) this.recordIndex = recordIndex;
1231
1302
  }
1232
1303
  };
1304
+ var DELIMITER_ROLES = ["field", "repeat", "component", "escape"];
1305
+ var CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/;
1306
+ function assertEmittableDelimiters(d, recordIndex) {
1307
+ const invalid = (reason) => {
1308
+ throw new AstmSerializeError(
1309
+ `Cannot emit against these delimiters: ${reason}.`,
1310
+ recordIndex,
1311
+ "ASTM_EMIT_INVALID_DELIMITERS"
1312
+ );
1313
+ };
1314
+ if (typeof d !== "object" || d === null) {
1315
+ invalid("a delimiter set is required");
1316
+ }
1317
+ for (const role of DELIMITER_ROLES) {
1318
+ const char = d[role];
1319
+ if (typeof char !== "string" || char.length !== 1) {
1320
+ invalid(`the ${role} delimiter must be exactly one character`);
1321
+ }
1322
+ if (char === "\r" || char === "\n") {
1323
+ invalid(`the ${role} delimiter must not be a record terminator (CR/LF)`);
1324
+ }
1325
+ }
1326
+ DELIMITER_ROLES.forEach((a, i) => {
1327
+ for (const b of DELIMITER_ROLES.slice(i + 1)) {
1328
+ if (d[a] === d[b]) {
1329
+ invalid(`the ${a} and ${b} delimiters must not be the same character`);
1330
+ }
1331
+ }
1332
+ });
1333
+ }
1233
1334
  function encodeComponent(leaf, d, recordIndex) {
1335
+ assertEmittableDelimiters(d, recordIndex);
1336
+ return encodeLeaf(leaf, d, recordIndex);
1337
+ }
1338
+ function encodeLeaf(leaf, d, recordIndex) {
1234
1339
  if (leaf.includes("\r") || leaf.includes("\n")) {
1235
1340
  throw new AstmSerializeError(
1236
1341
  "A value contains a record terminator (CR/LF), which cannot be escaped without breaking framing.",
@@ -1240,28 +1345,58 @@ function encodeComponent(leaf, d, recordIndex) {
1240
1345
  return leaf.split(d.escape).join(d.escape + "E" + d.escape).split(d.field).join(d.escape + "F" + d.escape).split(d.component).join(d.escape + "S" + d.escape).split(d.repeat).join(d.escape + "R" + d.escape);
1241
1346
  }
1242
1347
  function encodeField(repeats, d, recordIndex) {
1243
- return repeats.map((rep) => rep.map((c) => encodeComponent(c, d, recordIndex)).join(d.component)).join(d.repeat);
1348
+ return repeats.map((rep) => rep.map((c) => encodeLeaf(c, d, recordIndex)).join(d.component)).join(d.repeat);
1244
1349
  }
1245
1350
  function serializeAstmRecord(record, d = CANONICAL_DELIMITERS) {
1246
- if (record.type === "M" || record.type === "S") return record.rawLine;
1351
+ assertEmittableDelimiters(d, record.recordIndex);
1352
+ return serializeRecordChecked(record, d);
1353
+ }
1354
+ function serializeRecordChecked(record, d) {
1355
+ if (record.type === "M" || record.type === "S") return serializeVerbatimRecord(record, d);
1247
1356
  if (record.type === "H") return serializeHeader(record, d);
1248
1357
  return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);
1249
1358
  }
1359
+ function serializeVerbatimRecord(record, d) {
1360
+ if (recoversVerbatim(record, d)) return record.rawLine;
1361
+ return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);
1362
+ }
1363
+ function recoversVerbatim(record, d) {
1364
+ return sameFieldTree(tokenizeRecord(record.rawLine, d), record.fields);
1365
+ }
1366
+ function sameFieldTree(a, b) {
1367
+ if (a.length !== b.length) return false;
1368
+ return a.every((fieldA, i) => {
1369
+ const fieldB = b[i];
1370
+ if (fieldB === void 0) return false;
1371
+ if (fieldA.repeats.length !== fieldB.repeats.length) return false;
1372
+ return fieldA.repeats.every((repA, r) => {
1373
+ const repB = fieldB.repeats[r];
1374
+ if (repB === void 0 || repA.length !== repB.length) return false;
1375
+ return repA.every((comp, c) => comp === repB[c]);
1376
+ });
1377
+ });
1378
+ }
1250
1379
  function serializeHeader(header, d) {
1251
- const head = "H" + d.field + d.repeat + d.component + d.escape;
1252
- const src = header.delimiters;
1253
- const defEnd = header.rawLine.indexOf(src.field, 2);
1254
- if (defEnd === -1) return head;
1255
- const dataPortion = header.rawLine.slice(defEnd + 1);
1256
- const dataFields = tokenizeRecord(dataPortion, src);
1380
+ const head = "H" + d.field + d.repeat + d.component + d.escape + declarationResidual(header, d);
1381
+ const dataFields = header.fields.slice(2);
1257
1382
  const rest = dataFields.map((f) => d.field + encodeField(f.repeats, d, header.recordIndex)).join("");
1258
1383
  return head + rest;
1259
1384
  }
1385
+ function declarationResidual(header, d) {
1386
+ const declaration = header.fields[1]?.raw ?? "";
1387
+ const roles = d.repeat + d.component + d.escape;
1388
+ if (!declaration.startsWith(roles)) return "";
1389
+ const residual = declaration.slice(roles.length);
1390
+ if (residual.includes(d.field) || CONTROL_CHARACTER.test(residual)) return "";
1391
+ return residual;
1392
+ }
1260
1393
  function serializeAstmRecords(input, d = CANONICAL_DELIMITERS) {
1394
+ assertEmittableDelimiters(d);
1261
1395
  const records = Array.isArray(input) ? input : input.records;
1262
- return records.map((r) => serializeAstmRecord(r, d) + "\r").join("");
1396
+ return records.map((r) => serializeRecordChecked(r, d) + "\r").join("");
1263
1397
  }
1264
1398
  function serializeField(field, d = CANONICAL_DELIMITERS) {
1399
+ assertEmittableDelimiters(d);
1265
1400
  return encodeField(field.repeats, d);
1266
1401
  }
1267
1402
 
@@ -1396,17 +1531,108 @@ function buildAstmMessage(input) {
1396
1531
  return lines.map((l) => l + "\r").join("");
1397
1532
  }
1398
1533
 
1534
+ // src/records/messages.ts
1535
+ var AMBIGUOUS_CODES = {
1536
+ /** The stream carries more than one `H` … `L` message, so a stream-wide answer spans patients. */
1537
+ ASTM_AMBIGUOUS_MULTI_MESSAGE: "ASTM_AMBIGUOUS_MULTI_MESSAGE",
1538
+ /** The one message in the stream carries more than one `P`, so "the patient" is not determined. */
1539
+ ASTM_AMBIGUOUS_MULTI_PATIENT: "ASTM_AMBIGUOUS_MULTI_PATIENT"
1540
+ };
1541
+ var AstmAmbiguousStreamError = class extends Error {
1542
+ /** The stable discriminant: see {@link AMBIGUOUS_CODES}. */
1543
+ code;
1544
+ /** Where the ambiguity became visible: the second header, or the second `P`. Value-free. */
1545
+ position;
1546
+ /** How many `H` … `L` messages the stream carries. */
1547
+ messageCount;
1548
+ /** How many `P` records the message in question carries. */
1549
+ patientCount;
1550
+ /** @internal */
1551
+ constructor(code, message, position, counts) {
1552
+ super(message);
1553
+ this.name = "AstmAmbiguousStreamError";
1554
+ this.code = code;
1555
+ this.position = position;
1556
+ this.messageCount = counts.messageCount;
1557
+ this.patientCount = counts.patientCount;
1558
+ }
1559
+ };
1560
+ function messages(msg) {
1561
+ const groups = [];
1562
+ let current;
1563
+ for (const record of msg.records) {
1564
+ if (record.type === "H" || current === void 0) {
1565
+ current = [];
1566
+ groups.push(current);
1567
+ }
1568
+ current.push(record);
1569
+ }
1570
+ return Object.freeze(
1571
+ groups.map((records, index) => {
1572
+ const header = records.find((r) => r.type === "H") ?? msg.header;
1573
+ const patients = records.filter((r) => r.type === "P");
1574
+ return Object.freeze({
1575
+ index,
1576
+ header,
1577
+ delimiters: header.delimiters,
1578
+ records: Object.freeze(records),
1579
+ patient: patients.length === 1 ? patients[0] : void 0,
1580
+ patients: Object.freeze(patients),
1581
+ results: Object.freeze(records.filter((r) => r.type === "R")),
1582
+ orders: Object.freeze(records.filter((r) => r.type === "O")),
1583
+ comments: Object.freeze(records.filter((r) => r.type === "C")),
1584
+ queries: Object.freeze(records.filter((r) => r.type === "Q"))
1585
+ });
1586
+ })
1587
+ );
1588
+ }
1589
+ function surveyMessages(msg) {
1590
+ let count = 0;
1591
+ let secondHeader;
1592
+ for (const record of msg.records) {
1593
+ if (record.type !== "H") continue;
1594
+ count += 1;
1595
+ if (count === 2) secondHeader = record;
1596
+ }
1597
+ return { count, secondHeader };
1598
+ }
1599
+ function assertSingleMessage(msg, accessor) {
1600
+ const { count, secondHeader } = surveyMessages(msg);
1601
+ if (count <= 1) return;
1602
+ throw new AstmAmbiguousStreamError(
1603
+ AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_MESSAGE,
1604
+ `${accessor} is scoped to the whole stream, and this stream carries ${String(count)} messages, so its answer would span patients. Use messages() and read each message's own records.`,
1605
+ { recordIndex: secondHeader?.recordIndex ?? 0, recordType: "H" },
1606
+ { messageCount: count, patientCount: 0 }
1607
+ );
1608
+ }
1609
+ function assertSinglePatient(msg, accessor) {
1610
+ const patients = msg.records.filter((r) => r.type === "P");
1611
+ if (patients.length <= 1) return;
1612
+ throw new AstmAmbiguousStreamError(
1613
+ AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_PATIENT,
1614
+ `${accessor} cannot name one patient: this message carries ${String(patients.length)} P records. Read them from messages()[n].patients and pair each with the records it owns.`,
1615
+ { recordIndex: patients[1]?.recordIndex ?? 0, recordType: "P" },
1616
+ { messageCount: 1, patientCount: patients.length }
1617
+ );
1618
+ }
1619
+
1399
1620
  // src/records/extractors.ts
1400
1621
  function results(msg) {
1622
+ assertSingleMessage(msg, "results()");
1401
1623
  return msg.records.filter((r) => r.type === "R");
1402
1624
  }
1403
1625
  function patient(msg) {
1626
+ assertSingleMessage(msg, "patient()");
1627
+ assertSinglePatient(msg, "patient()");
1404
1628
  return msg.records.find((r) => r.type === "P");
1405
1629
  }
1406
1630
  function orders(msg) {
1631
+ assertSingleMessage(msg, "orders()");
1407
1632
  return msg.records.filter((r) => r.type === "O");
1408
1633
  }
1409
1634
  function comments(msg) {
1635
+ assertSingleMessage(msg, "comments()");
1410
1636
  return msg.records.filter((r) => r.type === "C");
1411
1637
  }
1412
1638
  function commentsFor(msg, parent) {
@@ -1415,6 +1641,7 @@ function commentsFor(msg, parent) {
1415
1641
  );
1416
1642
  }
1417
1643
  function query(msg) {
1644
+ assertSingleMessage(msg, "query()");
1418
1645
  return msg.records.filter((r) => r.type === "Q");
1419
1646
  }
1420
1647
 
@@ -1457,14 +1684,14 @@ function defineLivdCatalog(entries) {
1457
1684
  var LIVD_WARNING_CODES = {
1458
1685
  /**
1459
1686
  * A record carried a vendor/local test code, but the consumer-supplied LIVD catalog held **no**
1460
- * entry for it. The code stays surfaced verbatim and the mapping is `unmapped` — a LOINC is
1687
+ * entry for it. The code stays surfaced verbatim and the mapping is `unmapped`: a LOINC is
1461
1688
  * **never** guessed. Purely advisory: the raw code and value are untouched.
1462
1689
  */
1463
1690
  ASTM_LIVD_UNMAPPED_CODE: "ASTM_LIVD_UNMAPPED_CODE",
1464
1691
  /**
1465
1692
  * A vendor/local test code matched **more than one distinct LOINC** in the catalog (e.g. the same
1466
1693
  * transmission code used by two devices for different analytes). The mapping is `ambiguous` and the
1467
- * candidate LOINCs are surfaced for inspection, but **none is chosen** — refusing to pick is the
1694
+ * candidate LOINCs are surfaced for inspection, but **none is chosen**: refusing to pick is the
1468
1695
  * fail-safe, since guessing wrong mis-identifies the test.
1469
1696
  */
1470
1697
  ASTM_LIVD_AMBIGUOUS_MAPPING: "ASTM_LIVD_AMBIGUOUS_MAPPING"
@@ -1472,14 +1699,14 @@ var LIVD_WARNING_CODES = {
1472
1699
  function livdUnmappedCode(position) {
1473
1700
  return {
1474
1701
  code: LIVD_WARNING_CODES.ASTM_LIVD_UNMAPPED_CODE,
1475
- message: "Reported test code had no LIVD mapping \u2014 surfaced unmapped, never a guessed LOINC.",
1702
+ message: "Reported test code had no LIVD mapping, surfaced unmapped, never a guessed LOINC.",
1476
1703
  position
1477
1704
  };
1478
1705
  }
1479
1706
  function livdAmbiguousMapping(position) {
1480
1707
  return {
1481
1708
  code: LIVD_WARNING_CODES.ASTM_LIVD_AMBIGUOUS_MAPPING,
1482
- message: "Reported test code mapped to multiple distinct LOINCs \u2014 surfaced ambiguous, never one guessed.",
1709
+ message: "Reported test code mapped to multiple distinct LOINCs, surfaced ambiguous, never one guessed.",
1483
1710
  position
1484
1711
  };
1485
1712
  }
@@ -1923,7 +2150,7 @@ function encodeFrame(text, frameNumber, isFinal) {
1923
2150
  }
1924
2151
  function composeAstmFrames(records, options = {}) {
1925
2152
  if (records.length === 0) {
1926
- throw new AstmFrameEncodeError("Cannot frame an empty record list \u2014 nothing to transmit.");
2153
+ throw new AstmFrameEncodeError("Cannot frame an empty record list: nothing to transmit.");
1927
2154
  }
1928
2155
  const out = [];
1929
2156
  let frameNumber = options.startFrameNumber ?? FIRST_FRAME_NUMBER;
@@ -1931,7 +2158,7 @@ function composeAstmFrames(records, options = {}) {
1931
2158
  const bytes = toBytes(record);
1932
2159
  if (bytes.length === 0) {
1933
2160
  throw new AstmFrameEncodeError(
1934
- "Cannot frame an empty record \u2014 a frame must carry record bytes.",
2161
+ "Cannot frame an empty record: a frame must carry record bytes.",
1935
2162
  recordIndex
1936
2163
  );
1937
2164
  }
@@ -1970,8 +2197,8 @@ function concatBytes3(chunks) {
1970
2197
  }
1971
2198
 
1972
2199
  // src/index.ts
1973
- var VERSION = "0.0.3";
2200
+ var VERSION = "0.0.5";
1974
2201
 
1975
- export { ALL_ASTM_WARNING_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, AstmFrameEncodeError, AstmFrameStrictError, AstmParseError, AstmProfileDefinitionError, AstmSerializeError, AstmStrictError, CANONICAL_DELIMITERS, FATAL_CODES, FRAME_WARNING_CODES, LIVD_WARNING_CODES, LTP_WARNING_CODES, SAFETY_CRITICAL_CODES, TOLERABLE_CODES, VERSION, WARNING_CODES, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, detectFraming, encodeComponent, fieldScalar, frameBadChecksum, frameOversize, frameSequenceGap, frameUnterminated, getAstmProfile, getDefaultAstmProfile, interpretAbnormalFlag, interpretResultStatus, isNonStandard, isSafetyCriticalCode, listAstmProfiles, livdAmbiguousMapping, livdUnmappedCode, lookupLivdForRecord, ltpAmbiguousTransport, ltpFrameRejected, ltpInitialState, ltpReduce, ltpUnexpectedEvent, nonStandardDelimiters, orders, orphanComment, parseAstmDate, parseAstmRecords, parseChecksumHex, parseFramedAstm, parseReferenceRange, partialTimestamp, patient, primaryCode, profileQuirkApplied, query, readDelimiters, recognizeUniversalTestId, resolveProfileTransport, results, serializeAstmRecord, serializeAstmRecords, serializeField, serializeFramedAstm, setDefaultAstmProfile, splitEscapeAware, toChecksumHex, tokenizeRecord, undefinedAbnormalFlag, undefinedResultStatus, uninterpretedQueryStatus, unitsAbsent, unknownEscapeSequence, unknownRecordType, unparseableReferenceRange };
2202
+ export { ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, AstmAmbiguousStreamError, AstmFrameEncodeError, AstmFrameStrictError, AstmParseError, AstmProfileDefinitionError, AstmSerializeError, AstmStrictError, CANONICAL_DELIMITERS, FATAL_CODES, FRAME_WARNING_CODES, LIVD_WARNING_CODES, LTP_WARNING_CODES, SAFETY_CRITICAL_CODES, TOLERABLE_CODES, VERSION, WARNING_CODES, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimitersRedeclared, detectFraming, encodeComponent, fieldScalar, frameBadChecksum, frameOversize, frameSequenceGap, frameUnterminated, getAstmProfile, getDefaultAstmProfile, interpretAbnormalFlag, interpretResultStatus, isNonStandard, isSafetyCriticalCode, listAstmProfiles, livdAmbiguousMapping, livdUnmappedCode, lookupLivdForRecord, ltpAmbiguousTransport, ltpFrameRejected, ltpInitialState, ltpReduce, ltpUnexpectedEvent, messages, nonStandardDelimiters, orders, orphanComment, parseAstmDate, parseAstmRecords, parseChecksumHex, parseFramedAstm, parseReferenceRange, partialTimestamp, patient, primaryCode, profileQuirkApplied, query, readDelimiters, recognizeUniversalTestId, resolveProfileTransport, results, serializeAstmRecord, serializeAstmRecords, serializeField, serializeFramedAstm, setDefaultAstmProfile, splitEscapeAware, toChecksumHex, tokenizeHeader, tokenizeRecord, undefinedAbnormalFlag, undefinedResultStatus, uninterpretedQueryStatus, unitsAbsent, unknownEscapeSequence, unknownRecordType, unparseableReferenceRange, unreadableRedeclaration };
1976
2203
  //# sourceMappingURL=index.mjs.map
1977
2204
  //# sourceMappingURL=index.mjs.map