@cosyte/astm 0.0.14 → 0.0.17
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 +294 -0
- package/README.md +97 -11
- package/dist/index.cjs +330 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +767 -64
- package/dist/index.d.ts +767 -64
- package/dist/index.mjs +325 -43
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -74,18 +74,170 @@ declare const WARNING_CODES: {
|
|
|
74
74
|
* boundaries, silently, and this can happen to one record inside a run of these warnings); and a
|
|
75
75
|
* set differing in the repeat, component or escape role usually splits into fields normally,
|
|
76
76
|
* where a mis-split component can cost a test identity while the value survives, and where an
|
|
77
|
-
* `&X&` sequence whose body is
|
|
77
|
+
* `&X&` sequence whose body is an unrecognized character that is itself a delimiter in force is an
|
|
78
|
+
* opaque atom, so that delimiter does not split
|
|
78
79
|
* and every field after it shifts. The escape role's worst case has narrowed and not
|
|
79
80
|
* disappeared: an escape character heading no sequence is now read as a literal and reported
|
|
80
81
|
* under {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} rather than merging the rest of the
|
|
81
|
-
* record,
|
|
82
|
-
* {@link WARNING_CODES.
|
|
82
|
+
* record, and a delimiter swallowed inside an `&X&` body now raises
|
|
83
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE} as well as the tolerable
|
|
84
|
+
* {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, though it still splits the same way. The
|
|
85
|
+
* mirror of that case, where the leftmost alignment lets a delimiter split that a competing
|
|
86
|
+
* alignment would have held, gains a boundary rather than losing one, so the record splits into
|
|
87
|
+
* more fields than another reading gives and this code cannot see it either
|
|
88
|
+
* ({@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} reports that). Treat this code as a
|
|
83
89
|
* report that one record definitely lost its fields, never as a sweep that would have fired if
|
|
84
90
|
* any had.
|
|
85
91
|
*/
|
|
86
92
|
readonly ASTM_RECORD_FIELDS_UNSEPARATED: "ASTM_RECORD_FIELDS_UNSEPARATED";
|
|
87
93
|
/** The header declared delimiters other than the canonical `H|\^&`: tolerated, noted. */
|
|
88
94
|
readonly ASTM_NONSTANDARD_DELIMITERS: "ASTM_NONSTANDARD_DELIMITERS";
|
|
95
|
+
/**
|
|
96
|
+
* A header declared **one character in two roles**, so the boundary between those two roles is
|
|
97
|
+
* not recoverable from the bytes. The declaration is still read and honored and no record is
|
|
98
|
+
* dropped: what is gone is a distinction the sender's own bytes no longer carry.
|
|
99
|
+
*
|
|
100
|
+
* The field separator is not part of this: a declaration naming it in another role is refused
|
|
101
|
+
* earlier (the `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal on the first header,
|
|
102
|
+
* {@link WARNING_CODES.ASTM_RECORD_UNREADABLE_REDECLARATION} on a later one). What this code
|
|
103
|
+
* covers is the three unordered pairs among the rest: **repeat/component, repeat/escape,
|
|
104
|
+
* component/escape**.
|
|
105
|
+
*
|
|
106
|
+
* Measured on `H|^^&` (repeat and component both `^`): the field `A^B^C^D` reads back as four
|
|
107
|
+
* repeats of one component each, so `components` holds only `A` and a two-repeats-of-two-components
|
|
108
|
+
* reading cannot be recovered. Measured on `H|\&&` (component and escape both `&`): `A&B` splits
|
|
109
|
+
* into two components while `A&F&B` reads as the single component `A|B`, so the same character
|
|
110
|
+
* means two different things depending on what follows it.
|
|
111
|
+
*
|
|
112
|
+
* **It is not tolerable**, and the reason is the pair it travels with: such a set is always
|
|
113
|
+
* non-canonical, so before this code existed the only warning on the stream was
|
|
114
|
+
* {@link WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS}, which a profile may tolerate. That made a
|
|
115
|
+
* structurally unreadable declaration indistinguishable, to a strict consumer, from an ordinary
|
|
116
|
+
* vendor set. Emit refuses the same sets outright (`ASTM_EMIT_INVALID_DELIMITERS`).
|
|
117
|
+
*
|
|
118
|
+
* One warning per header that **changes** the set in force into such a set, not one per colliding
|
|
119
|
+
* pair. A later header restating the colliding set already in force is a no-op and warns nothing,
|
|
120
|
+
* on the same rule as {@link WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS}: the set it names was
|
|
121
|
+
* already reported when it came into force.
|
|
122
|
+
*/
|
|
123
|
+
readonly ASTM_RECORD_DELIMITER_ROLE_COLLISION: "ASTM_RECORD_DELIMITER_ROLE_COLLISION";
|
|
124
|
+
/**
|
|
125
|
+
* An escape sequence whose body was **not** a recognized mnemonic held a character that is one of
|
|
126
|
+
* the three splitting delimiters in force, and the atom rule (an `&X&` triple is opaque) kept it
|
|
127
|
+
* out of the split, so a boundary the bytes carried never became one. The sequence is preserved
|
|
128
|
+
* verbatim in the value; nothing is dropped and nothing is re-split.
|
|
129
|
+
*
|
|
130
|
+
* Measured on the canonical set: `R|1|^^^687|28.6&|&U/L||||F` reads `value` = `28.6&|&U/L`, with
|
|
131
|
+
* **no units** and status `unspecified` rather than `final`.
|
|
132
|
+
*
|
|
133
|
+
* **This is the code that says a boundary was lost.** The same condition also raises
|
|
134
|
+
* {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, which stays and stays tolerable: that code
|
|
135
|
+
* reports only that a body was not recognized, which is true of bodies that cost nothing. This one
|
|
136
|
+
* is the narrower, safety-critical half, and no profile may tolerate it.
|
|
137
|
+
*
|
|
138
|
+
* **What it does not do is repair anything.** The atom rule is unchanged (it is what keeps `&F&`
|
|
139
|
+
* one token under a set that names `F` as a delimiter), so the value is byte-identical to what it
|
|
140
|
+
* was before this code existed. It also cannot see the condition through a re-emit: emit rewrites
|
|
141
|
+
* the preserved sequence into recognized mnemonics, and the resulting stream says that value
|
|
142
|
+
* unambiguously, so a second-generation read is silent and correct about its own bytes. The place
|
|
143
|
+
* to catch this is the first read of the wire bytes, which is where it now refuses a strict parse.
|
|
144
|
+
*/
|
|
145
|
+
readonly ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE: "ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE";
|
|
146
|
+
/**
|
|
147
|
+
* Escape sequences are matched greedily and leftmost, so the escape character that **closed** an
|
|
148
|
+
* unrecognized sequence could not also **open** the next one. Where it could have, and where the
|
|
149
|
+
* body it would have held is the delimiter that was split on, the same bytes carry two alignments
|
|
150
|
+
* that disagree by one boundary: under the reading taken that delimiter ends a field, repeat or
|
|
151
|
+
* component, and under the other it sits inside an opaque atom and ends nothing. The leftmost
|
|
152
|
+
* reading is kept, every byte is preserved, and nothing is re-split.
|
|
153
|
+
*
|
|
154
|
+
* **This is the mirror of {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE}, and
|
|
155
|
+
* the direction is the reason it needs its own code.** That one reports a boundary the reading
|
|
156
|
+
* **lost**; this one reports a boundary the reading may have **gained**, which is the more
|
|
157
|
+
* dangerous direction, because a gained boundary hands back a value the sender's bytes do not
|
|
158
|
+
* unambiguously carry. Measured on the canonical set: `R|1|^^^687|28.6&Z&|&U/L||||F` reads
|
|
159
|
+
* `value` = `28.6&Z&` and `units` = `&U/L` under the leftmost alignment, and reads as a single
|
|
160
|
+
* unsplit field carrying both under the other.
|
|
161
|
+
*
|
|
162
|
+
* **Two exclusions, both deliberate, and the first is wider than its own argument.** Where the
|
|
163
|
+
* earlier sequence's body is a **recognized** mnemonic nothing is reported, because the reading
|
|
164
|
+
* taken interprets a construct (`&F&` is the sender escaping a field separator, which is what the
|
|
165
|
+
* mechanism is for) while the competing alignment's body is a delimiter character it usually
|
|
166
|
+
* cannot interpret at all, so this codec's own vocabulary prefers the reading taken. That is not
|
|
167
|
+
* the same as the reading taken being conformant, and where the declared set names a mnemonic
|
|
168
|
+
* letter as a splitting delimiter both alignments interpret one construct and neither is
|
|
169
|
+
* preferred, yet this stays silent. Both residues are measured and recorded rather than closed by
|
|
170
|
+
* widening the test. And where the escape
|
|
171
|
+
* character after the delimiter does not itself close a sequence there is no competing alignment
|
|
172
|
+
* at all, so an ordinary escaped value followed by an ordinary boundary is silent.
|
|
173
|
+
*
|
|
174
|
+
* **It is a report, not a repair, and not a round-trip guard.** The reading is unchanged: picking
|
|
175
|
+
* the other alignment would be a different guess with no more evidence behind it. Emit then
|
|
176
|
+
* rewrites the preserved sequences into recognized mnemonics, and those bytes carry the reading
|
|
177
|
+
* that was taken unambiguously, so a second-generation read is silent and is correct about its own
|
|
178
|
+
* bytes. The first read of the wire bytes is the only place the ambiguity exists to be caught.
|
|
179
|
+
*/
|
|
180
|
+
readonly ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT: "ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT";
|
|
181
|
+
/**
|
|
182
|
+
* Two escape alignments of the same bytes disagreed about a **field** boundary, the reading taken
|
|
183
|
+
* kept it, and the escape character that reading resumes on **heads no sequence at all**, so the
|
|
184
|
+
* boundary was bought with a byte this reading cannot read while the competing alignment is
|
|
185
|
+
* exactly the reading that can. Every field after that point sits one place further right than the
|
|
186
|
+
* competing alignment puts it.
|
|
187
|
+
*
|
|
188
|
+
* **The shift is the harm, and on a result record it reaches the status slot.** Measured on the
|
|
189
|
+
* canonical set, `R|1|^^^687|28.6&F&|&U/L||||F` reads **9** fields under the reading taken and
|
|
190
|
+
* **8** under the competing one, so the sender's trailing `F` lands in field 9 (the result status)
|
|
191
|
+
* under the first and in no field at all under the second. The parse hands back units `&U/L` and a
|
|
192
|
+
* status of **`final`**, and both are consequences of the alignment rather than values the sender
|
|
193
|
+
* placed in those slots. A downstream system reading `final` would act on a result the bytes do
|
|
194
|
+
* not say was finalised. Before this code the only warning on that stream was the **tolerable**
|
|
195
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}, so the widest gate-legal profile plus
|
|
196
|
+
* `{ strict: true }` accepted it.
|
|
197
|
+
*
|
|
198
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is identical, and
|
|
199
|
+
* the units and status read are the ones that were always read. Picking the other alignment would
|
|
200
|
+
* be a different guess with no more evidence behind it, and it would change values on a published
|
|
201
|
+
* package. What is new is that the shift is reported by a code no profile may tolerate.
|
|
202
|
+
*
|
|
203
|
+
* **It fires alongside {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT}, not instead
|
|
204
|
+
* of it, and neither test is a widening of the other.** That code asks whether this codec's
|
|
205
|
+
* vocabulary prefers the reading taken *at* the contested position, and is silent where the
|
|
206
|
+
* earlier body is a recognized mnemonic. This one asks what the reading taken makes of the bytes
|
|
207
|
+
* *after* the boundary, and does not consult the earlier body at all.
|
|
208
|
+
*
|
|
209
|
+
* **Two deliberate bounds, both stated rather than left to be found.**
|
|
210
|
+
* - **Only the field role, and that bound is a CHOICE rather than a consequence.** A gained repeat
|
|
211
|
+
* or component boundary divides one field and reaches nothing outside it, so it moves no
|
|
212
|
+
* **field-indexed** slot: the units and the status stay where they were. **It does not follow
|
|
213
|
+
* that it moves no modeled slot at all, and writing that down would be false.** Components are
|
|
214
|
+
* modeled *inside* a field: a Universal Test ID's four components are the LOINC-candidate slot,
|
|
215
|
+
* the test name, the **coding scheme** and the **local code**, and a patient name's three are
|
|
216
|
+
* last, first and middle. A gained component boundary shifts those, so a local code can be read
|
|
217
|
+
* as a coding scheme and a given name as a middle name. Measured, and **where the earlier body is
|
|
218
|
+
* a recognized mnemonic, reported by nothing** (only the tolerable
|
|
219
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} fires, so a gate-legal profile accepts it).
|
|
220
|
+
* An **unrecognized** earlier body raises
|
|
221
|
+
* {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} there and is refused, which is the
|
|
222
|
+
* same split this code's own exclusion takes. It is a separate, open condition rather than something this
|
|
223
|
+
* code covers, because wiring this sink to another split is a different criterion needing its own
|
|
224
|
+
* population measurement. The repeat role costs the **value**, which this code does not report
|
|
225
|
+
* either.
|
|
226
|
+
* - **The tail is weighed one construct deep.** Where the escape character the reading taken
|
|
227
|
+
* resumes on heads a sequence this codec *recognizes*, the reading taken interprets it and the
|
|
228
|
+
* competing alignment would leave it bare, so the bytes prefer the reading taken: under a set
|
|
229
|
+
* naming the field separator `F`, `28.6&F&F&F&U/L` is that separator escaped, written, and
|
|
230
|
+
* escaped again, and refusing it would be an over-refusal of a well-formed stream. Where it
|
|
231
|
+
* heads a sequence whose body is *unrecognized*, the reading taken still consumes it while the
|
|
232
|
+
* competing alignment would leave **two** escape characters bare, so the preference is stronger
|
|
233
|
+
* again, and this stays silent there even though the field shift is real. That last case is the
|
|
234
|
+
* named residue: measured, not overlooked.
|
|
235
|
+
*
|
|
236
|
+
* **Catch it on the first read.** Emit rewrites the preserved sequences into recognized mnemonics,
|
|
237
|
+
* and those bytes carry the reading that was taken unambiguously, so a second-generation read is
|
|
238
|
+
* silent and is correct about its own bytes. A clean re-read is not evidence.
|
|
239
|
+
*/
|
|
240
|
+
readonly ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS: "ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS";
|
|
89
241
|
/** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&`: preserved verbatim. */
|
|
90
242
|
readonly ASTM_UNKNOWN_ESCAPE_SEQUENCE: "ASTM_UNKNOWN_ESCAPE_SEQUENCE";
|
|
91
243
|
/**
|
|
@@ -107,7 +259,8 @@ declare const WARNING_CODES: {
|
|
|
107
259
|
* *different* escape character in the same record may still head a real three-character sequence,
|
|
108
260
|
* and if that sequence's body happens to be a delimiter (`&|&` under the canonical set) the atom
|
|
109
261
|
* rule means that delimiter does not split. That case is reported separately, under
|
|
110
|
-
* {@link WARNING_CODES.
|
|
262
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE}, and it still costs a field
|
|
263
|
+
* boundary.
|
|
111
264
|
*/
|
|
112
265
|
readonly ASTM_UNPAIRED_ESCAPE_CHARACTER: "ASTM_UNPAIRED_ESCAPE_CHARACTER";
|
|
113
266
|
/**
|
|
@@ -173,7 +326,8 @@ declare const WARNING_CODES: {
|
|
|
173
326
|
*/
|
|
174
327
|
readonly ASTM_RECORD_DELIMITERS_REDECLARED: "ASTM_RECORD_DELIMITERS_REDECLARED";
|
|
175
328
|
/**
|
|
176
|
-
* A later `H` (header) record could not declare a usable delimiter set: it was
|
|
329
|
+
* A later `H` (header) record could not declare a usable delimiter set: it was not a header, it
|
|
330
|
+
* was too short, its delimiter definition held fewer than three characters, or the
|
|
177
331
|
* field separator it named also appeared among the other three, leaving the four roles
|
|
178
332
|
* indistinguishable. The delimiters **already in force are kept** and every record is still surfaced;
|
|
179
333
|
* a set is never guessed and no record is dropped.
|
|
@@ -283,6 +437,80 @@ declare function fieldsUnseparated(position: AstmPosition): AstmRecordWarning;
|
|
|
283
437
|
* ```
|
|
284
438
|
*/
|
|
285
439
|
declare function nonStandardDelimiters(position: AstmPosition): AstmRecordWarning;
|
|
440
|
+
/**
|
|
441
|
+
* Build an `ASTM_RECORD_DELIMITER_ROLE_COLLISION` warning. Emitted when a header
|
|
442
|
+
* declares one character in two of the repeat / component / escape roles. The
|
|
443
|
+
* declaration is honored and no record is dropped; the message names no character,
|
|
444
|
+
* because a delimiter is a byte off the wire.
|
|
445
|
+
*
|
|
446
|
+
* A profile may **not** tolerate this code: it reports a distinction the bytes no
|
|
447
|
+
* longer carry, and the only other warning such a set raises
|
|
448
|
+
* ({@link WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS}) is tolerable.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```ts
|
|
452
|
+
* import { delimiterRoleCollision } from "@cosyte/astm";
|
|
453
|
+
* delimiterRoleCollision({ recordIndex: 0, recordType: "H" });
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
declare function delimiterRoleCollision(position: AstmPosition): AstmRecordWarning;
|
|
457
|
+
/**
|
|
458
|
+
* Build an `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` warning. Emitted when an
|
|
459
|
+
* unrecognized escape body is itself a splitting delimiter in force, so the atom
|
|
460
|
+
* rule kept it out of the split. The value is preserved verbatim and is identical
|
|
461
|
+
* with the warning and without it; what the warning reports is the boundary that
|
|
462
|
+
* did not happen.
|
|
463
|
+
*
|
|
464
|
+
* A profile may **not** tolerate this code. It fires alongside
|
|
465
|
+
* {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, which remains tolerable and
|
|
466
|
+
* reports the strictly weaker fact that a body was not recognized.
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```ts
|
|
470
|
+
* import { delimiterSwallowedByEscape } from "@cosyte/astm";
|
|
471
|
+
* delimiterSwallowedByEscape({ recordIndex: 4, recordType: "R", fieldIndex: 4 });
|
|
472
|
+
* ```
|
|
473
|
+
*/
|
|
474
|
+
declare function delimiterSwallowedByEscape(position: AstmPosition): AstmRecordWarning;
|
|
475
|
+
/**
|
|
476
|
+
* Build an `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT` warning. Emitted when the escape
|
|
477
|
+
* character that closed an unrecognized escape sequence could instead have opened one
|
|
478
|
+
* holding the delimiter that was split on, so the bytes carry two alignments that
|
|
479
|
+
* disagree about that boundary. The leftmost alignment is kept and every byte is
|
|
480
|
+
* preserved; what the warning reports is that the boundary is a choice.
|
|
481
|
+
*
|
|
482
|
+
* A profile may **not** tolerate this code. It fires alongside
|
|
483
|
+
* {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, which remains tolerable and
|
|
484
|
+
* reports the strictly weaker fact that a body was not recognized.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```ts
|
|
488
|
+
* import { ambiguousEscapeAlignment } from "@cosyte/astm";
|
|
489
|
+
* ambiguousEscapeAlignment({ recordIndex: 4, recordType: "R", fieldIndex: 4 });
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
declare function ambiguousEscapeAlignment(position: AstmPosition): AstmRecordWarning;
|
|
493
|
+
/**
|
|
494
|
+
* Build an `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` warning. Emitted when a competing escape
|
|
495
|
+
* alignment decided a **field** boundary and the escape character the reading taken resumes on
|
|
496
|
+
* heads no sequence of its own, so every field after that point sits one place further right than
|
|
497
|
+
* the competing alignment puts it. On a result record that reaches the units and the **result
|
|
498
|
+
* status**: a trailing status letter lands in field 9 under the reading taken and in no field at
|
|
499
|
+
* all under the competing one.
|
|
500
|
+
*
|
|
501
|
+
* A profile may **not** tolerate this code. It fires alongside
|
|
502
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}, which remains tolerable and reports the
|
|
503
|
+
* strictly weaker fact that one escape character was read as a literal, and alongside
|
|
504
|
+
* {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} where that also applies. The reading
|
|
505
|
+
* is unchanged: this reports the shift, it does not repair it.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```ts
|
|
509
|
+
* import { alignmentShiftedFields } from "@cosyte/astm";
|
|
510
|
+
* alignmentShiftedFields({ recordIndex: 2, recordType: "R", fieldIndex: 4 });
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
declare function alignmentShiftedFields(position: AstmPosition): AstmRecordWarning;
|
|
286
514
|
/**
|
|
287
515
|
* Build an `ASTM_RECORD_DELIMITERS_REDECLARED` warning. Emitted when a later `H`
|
|
288
516
|
* record declares a delimiter set different from the one in force; the new set is
|
|
@@ -328,8 +556,10 @@ declare function unknownEscapeSequence(position: AstmPosition): AstmRecordWarnin
|
|
|
328
556
|
*
|
|
329
557
|
* **It is a statement about that one character, not about the record.** A
|
|
330
558
|
* different escape character in the same record may head a real three-character
|
|
331
|
-
* sequence, and if that sequence's body is
|
|
332
|
-
*
|
|
559
|
+
* sequence, and if that sequence's body is an unrecognized character that is itself a delimiter in
|
|
560
|
+
* force, that delimiter does not
|
|
561
|
+
* split, which is reported separately by
|
|
562
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE}.
|
|
333
563
|
*
|
|
334
564
|
* A profile **may** tolerate this code: the value it reports is byte-identical
|
|
335
565
|
* with the warning and without it, because reading the character as a literal is
|
|
@@ -528,13 +758,39 @@ interface Delimiters {
|
|
|
528
758
|
* ```
|
|
529
759
|
*/
|
|
530
760
|
declare const CANONICAL_DELIMITERS: Delimiters;
|
|
761
|
+
/**
|
|
762
|
+
* Why a header record could not declare a usable delimiter set. Four distinct
|
|
763
|
+
* conditions, kept distinct because a consumer is told which one it was and two of
|
|
764
|
+
* them describe records that are **not** short.
|
|
765
|
+
*
|
|
766
|
+
* - `not-a-header`: the record does not begin with an `H` type letter.
|
|
767
|
+
* - `record-too-short`: fewer than five characters, so `H` plus a field separator
|
|
768
|
+
* plus a three-character definition cannot fit.
|
|
769
|
+
* - `definition-truncated`: the delimiter-definition field runs to the next field
|
|
770
|
+
* separator (or to the end of the record) and holds fewer than three characters.
|
|
771
|
+
* **This is the one a full-length header reaches**, and it is what a declaration
|
|
772
|
+
* naming its own field separator among the other three produces, because that
|
|
773
|
+
* separator ends the definition where it appears.
|
|
774
|
+
* - `field-separator-reused`: the field separator is also the repeat, component or
|
|
775
|
+
* escape character.
|
|
776
|
+
*/
|
|
777
|
+
type DelimiterDeclarationFault = "not-a-header" | "record-too-short" | "definition-truncated" | "field-separator-reused";
|
|
778
|
+
/** The result of reading delimiters from a header record: either resolved or a named failure. */
|
|
779
|
+
type DelimiterReadResult = {
|
|
780
|
+
readonly ok: true;
|
|
781
|
+
readonly delimiters: Delimiters;
|
|
782
|
+
} | {
|
|
783
|
+
readonly ok: false;
|
|
784
|
+
readonly fault: DelimiterDeclarationFault;
|
|
785
|
+
};
|
|
531
786
|
/**
|
|
532
787
|
* Read the four delimiters from a header record's raw text (a single `H`
|
|
533
788
|
* record, its terminator already stripped).
|
|
534
789
|
*
|
|
535
|
-
* Returns `undefined` when the record cannot declare all four delimiters
|
|
536
|
-
*
|
|
537
|
-
*
|
|
790
|
+
* Returns `undefined` when the record cannot declare all four delimiters. The four
|
|
791
|
+
* ways that happens are named by {@link readDelimiterDeclaration}, which is this
|
|
792
|
+
* function with the reason kept; use it wherever the reason is shown to a consumer,
|
|
793
|
+
* because "too short" is false of some of them.
|
|
538
794
|
*
|
|
539
795
|
* What the caller does with that depends on **which** header it is. On the first
|
|
540
796
|
* header it is the `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal, because there is no
|
|
@@ -543,6 +799,12 @@ declare const CANONICAL_DELIMITERS: Delimiters;
|
|
|
543
799
|
* and an `ASTM_RECORD_UNREADABLE_REDECLARATION` warning is raised instead; a set is
|
|
544
800
|
* never guessed and no record is dropped.
|
|
545
801
|
*
|
|
802
|
+
* **A set two of whose other three roles share a character is resolved, not
|
|
803
|
+
* refused**, because the stream is still readable and refusing it would drop
|
|
804
|
+
* records the sender did send. What it costs is the boundary between those two
|
|
805
|
+
* roles, which the bytes no longer carry: see {@link hasCollidingRoles}, which
|
|
806
|
+
* the parse path calls to report it (`ASTM_RECORD_DELIMITER_ROLE_COLLISION`).
|
|
807
|
+
*
|
|
546
808
|
* This function does not throw: delimiter resolution and the escalation decision are
|
|
547
809
|
* kept separate so the reader stays pure and testable.
|
|
548
810
|
*
|
|
@@ -556,6 +818,68 @@ declare const CANONICAL_DELIMITERS: Delimiters;
|
|
|
556
818
|
* ```
|
|
557
819
|
*/
|
|
558
820
|
declare function readDelimiters(headerRecord: string): Delimiters | undefined;
|
|
821
|
+
/**
|
|
822
|
+
* The same read as {@link readDelimiters}, keeping **why** it failed.
|
|
823
|
+
*
|
|
824
|
+
* The reason is not cosmetic. A caller that reports every failure as "too short"
|
|
825
|
+
* says something false about `H||^&`, a full-length header whose definition field is
|
|
826
|
+
* empty because its own field separator ends it, and a consumer reading that
|
|
827
|
+
* diagnostic looks for the wrong thing. The two conditions are separate rules that
|
|
828
|
+
* happen to coincide today (see the fault list on
|
|
829
|
+
* {@link DelimiterDeclarationFault}), so they are named separately.
|
|
830
|
+
*
|
|
831
|
+
* **`field-separator-reused` is unreachable through this function today, and the
|
|
832
|
+
* check stays.** A field separator occurring in the definition positions sits at
|
|
833
|
+
* index 2, 3 or 4, so it ends the definition where it appears and the definition is
|
|
834
|
+
* under three characters: `definition-truncated` answers first, every time. The
|
|
835
|
+
* outcome is right either way (such a declaration does not resolve at all). The
|
|
836
|
+
* check is the statement of an invariant the length rule currently enforces on its
|
|
837
|
+
* behalf, and the two are not the same rule: a change to how the definition field is
|
|
838
|
+
* bounded would separate them again. Deleting it would leave the invariant
|
|
839
|
+
* unstated and the change silent.
|
|
840
|
+
*
|
|
841
|
+
* @param headerRecord - The raw `H` record text (no trailing CR/LF).
|
|
842
|
+
* @returns The resolved delimiters, or the named reason they could not be read.
|
|
843
|
+
* @example
|
|
844
|
+
* ```ts
|
|
845
|
+
* import { readDelimiterDeclaration } from "@cosyte/astm";
|
|
846
|
+
* readDelimiterDeclaration("H|\\^&").ok; // true
|
|
847
|
+
* readDelimiterDeclaration("H||^&"); // { ok: false, fault: "definition-truncated" }
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
declare function readDelimiterDeclaration(headerRecord: string): DelimiterReadResult;
|
|
851
|
+
/**
|
|
852
|
+
* Whether a resolved set names one character in two roles, so the boundary
|
|
853
|
+
* between those two roles cannot be recovered from the bytes.
|
|
854
|
+
*
|
|
855
|
+
* {@link readDelimiters} already refuses a declaration whose **field** separator
|
|
856
|
+
* is one of the other three (it returns `undefined`, which is the
|
|
857
|
+
* `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal on the first header and the
|
|
858
|
+
* `ASTM_RECORD_UNREADABLE_REDECLARATION` warning on a later one), so what is left
|
|
859
|
+
* to test here is the three unordered pairs among the remaining roles:
|
|
860
|
+
* **repeat/component, repeat/escape, and component/escape**. Three pairs, named
|
|
861
|
+
* rather than counted, because the count is only meaningful with the list.
|
|
862
|
+
*
|
|
863
|
+
* Such a declaration is read and honored (nothing is guessed, no record is
|
|
864
|
+
* dropped) and the loss it causes is real and was previously silent. Measured on
|
|
865
|
+
* the canonical-looking `H|^^&`, where the repeat and component roles are both
|
|
866
|
+
* `^`: the field `A^B^C^D` reads back as **four repeats of one component each**,
|
|
867
|
+
* so a two-repeats-of-two-components reading is unrecoverable and `components`
|
|
868
|
+
* holds only `A`. On `H|\&&`, where the component and escape roles are both `&`,
|
|
869
|
+
* the same character splits (`A&B` reads as two components) or opens an atom
|
|
870
|
+
* (`A&F&B` reads as the single component `A|B`) depending only on what follows
|
|
871
|
+
* it. Emit refuses such a set outright (`ASTM_EMIT_INVALID_DELIMITERS`).
|
|
872
|
+
*
|
|
873
|
+
* @param d - A resolved delimiter set.
|
|
874
|
+
* @returns `true` iff two of the repeat / component / escape roles share a character.
|
|
875
|
+
* @example
|
|
876
|
+
* ```ts
|
|
877
|
+
* import { hasCollidingRoles, readDelimiters } from "@cosyte/astm";
|
|
878
|
+
* hasCollidingRoles(readDelimiters("H|\\^&")!); // false
|
|
879
|
+
* hasCollidingRoles(readDelimiters("H|^^&")!); // true (repeat === component)
|
|
880
|
+
* ```
|
|
881
|
+
*/
|
|
882
|
+
declare function hasCollidingRoles(d: Delimiters): boolean;
|
|
559
883
|
/**
|
|
560
884
|
* Whether a resolved delimiter set differs from the canonical `H|\^&`. A `true`
|
|
561
885
|
* result is a tolerated vendor quirk, surfaced as a value-free warning so a
|
|
@@ -2042,7 +2366,10 @@ declare function attachComments(records: readonly AstmRecord[], warnings: AstmRe
|
|
|
2042
2366
|
* set (a non-canonical source is normalized to `H|\^&` by default). Normalizing sets the
|
|
2043
2367
|
* four delimiter **roles**; it does not delete a declaration's surplus characters, so a
|
|
2044
2368
|
* canonically-declared header that arrived carrying surplus keeps it on the default path
|
|
2045
|
-
* too
|
|
2369
|
+
* too, **except where that surplus could not be read back as surplus, when all of it is
|
|
2370
|
+
* dropped** (see `declarationResidual` below). Emit re-encodes what it read, so bytes
|
|
2371
|
+
* can differ without anything being lost: `O&BRIEN` in a canonical stream goes out as
|
|
2372
|
+
* `O&E&BRIEN`.
|
|
2046
2373
|
*
|
|
2047
2374
|
* **One delimiter set for the whole stream.** Every record (including `H`, `M` and `S`)
|
|
2048
2375
|
* is emitted against the delimiters being emitted with, and the header declares that same
|
|
@@ -2063,7 +2390,11 @@ declare function attachComments(records: readonly AstmRecord[], warnings: AstmRe
|
|
|
2063
2390
|
* `H|\^&#` rather than losing the `#`. That holds on the **default** canonical path as
|
|
2064
2391
|
* well as when a set is passed explicitly: the surplus of a canonically-declared header
|
|
2065
2392
|
* survives normalization, because normalization is about the four roles and the surplus
|
|
2066
|
-
* has none.
|
|
2393
|
+
* has none. It stops holding where the surplus itself could not be read back, and then
|
|
2394
|
+
* **the whole surplus is dropped, not the offending character alone**, on the default
|
|
2395
|
+
* path as much as any other: `declarationResidual` states which surpluses those are and
|
|
2396
|
+
* why an all-or-nothing drop is the only one of the available answers that invents
|
|
2397
|
+
* nothing.
|
|
2067
2398
|
*
|
|
2068
2399
|
* **Never break framing.** A component leaf that contains a record terminator
|
|
2069
2400
|
* (`CR`/`LF`) cannot be escaped by the ASTM escape codec (only the four declared
|
|
@@ -2084,10 +2415,15 @@ declare function attachComments(records: readonly AstmRecord[], warnings: AstmRe
|
|
|
2084
2415
|
* output, which is why they are refused at this one.
|
|
2085
2416
|
*
|
|
2086
2417
|
* **One position is not a modeled value and does not keep the byte:** the surplus
|
|
2087
|
-
* of a header's delimiter declaration, where
|
|
2418
|
+
* of a header's delimiter declaration, where a control character is dropped
|
|
2088
2419
|
* silently (see `declarationResidual` below, where that disposition is argued and
|
|
2089
2420
|
* where it long predates the frame-layer refusal). So "the record layer carries
|
|
2090
2421
|
* these bytes" is a statement about values, not about every byte of every line.
|
|
2422
|
+
* Measured, one character at a time in the surplus of an otherwise spec-clean
|
|
2423
|
+
* `H|\^&` header: **31 of the 33 C0/`DEL` characters reach that rule and are
|
|
2424
|
+
* dropped**, with `warnings: []` and no error. The two that do not are `CR` and
|
|
2425
|
+
* `LF`, which never reach it at all, because they end the record while it is being
|
|
2426
|
+
* read and so are never part of any surplus the model carries.
|
|
2091
2427
|
*
|
|
2092
2428
|
* **Check the delimiter set before writing.** Three conditions are required for the
|
|
2093
2429
|
* emitted bytes to read back as the records that produced them (each separator exactly
|
|
@@ -2104,10 +2440,18 @@ declare function attachComments(records: readonly AstmRecord[], warnings: AstmRe
|
|
|
2104
2440
|
*
|
|
2105
2441
|
* **What is still not guaranteed.** Together the two checks guarantee that every
|
|
2106
2442
|
* record re-reads as its own *type*. They do not guarantee that every *field* of it
|
|
2107
|
-
* lands where it did: an escape sequence whose body is
|
|
2443
|
+
* lands where it did: an escape sequence whose body is an unrecognized character that is itself a
|
|
2444
|
+
* delimiter in force is read as
|
|
2108
2445
|
* one opaque atom, so that delimiter never becomes a boundary and the fields after it
|
|
2109
|
-
* shift. That is reported on the parse side
|
|
2110
|
-
*
|
|
2446
|
+
* shift. That is reported on the parse side, by `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE`
|
|
2447
|
+
* (which no profile may tolerate) alongside the tolerable `ASTM_UNKNOWN_ESCAPE_SEQUENCE`, and is
|
|
2448
|
+
* not what these refusals cover. Nor is the mirror of it: sequences are matched leftmost, so one
|
|
2449
|
+
* can end where another could have begun and the delimiter between them splits where the competing
|
|
2450
|
+
* alignment would have held it, reported by `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`. **Neither
|
|
2451
|
+
* report survives this serializer**, by construction rather than by oversight: it rewrites the
|
|
2452
|
+
* preserved characters into recognized mnemonics, so the emitted stream carries the reading that
|
|
2453
|
+
* was taken with nothing ambiguous left in it, and a second-generation parse is silent and correct
|
|
2454
|
+
* about its own bytes. Catch both on the first read of the wire bytes.
|
|
2111
2455
|
*/
|
|
2112
2456
|
|
|
2113
2457
|
/**
|
|
@@ -2166,10 +2510,12 @@ declare class AstmSerializeError extends Error {
|
|
|
2166
2510
|
type AstmSerializeErrorCode = "ASTM_EMIT_UNENCODABLE_VALUE" | "ASTM_EMIT_INVALID_DELIMITERS" | "ASTM_EMIT_TYPE_LETTER_COLLISION";
|
|
2167
2511
|
/**
|
|
2168
2512
|
* Escape-encode one component leaf for spec-clean emit: the inverse of
|
|
2169
|
-
* `decodeEscapes`.
|
|
2170
|
-
*
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
2513
|
+
* `decodeEscapes`. Each character is read **once**, in one left-to-right pass, and
|
|
2514
|
+
* written either as itself or as a whole `escape` + mnemonic + `escape` triple
|
|
2515
|
+
* (`&` → `&E&`, the field / component / repeat delimiters → `&F&` / `&S&` / `&R&`).
|
|
2516
|
+
* Nothing this encoder writes is examined again, which is what makes it the exact
|
|
2517
|
+
* inverse of the decoder: the decoder reads the same output one token at a time,
|
|
2518
|
+
* so every triple it meets is a triple this encoder wrote.
|
|
2173
2519
|
*
|
|
2174
2520
|
* A `CR`/`LF` in the leaf has no escape mnemonic and would break framing, so it
|
|
2175
2521
|
* is rejected with an {@link AstmSerializeError} rather than emitted raw, as is a
|
|
@@ -2199,7 +2545,11 @@ declare function encodeComponent(leaf: string, d: Delimiters, recordIndex?: numb
|
|
|
2199
2545
|
* The header (`H`) is special-cased: its delimiter-definition field is emitted as
|
|
2200
2546
|
* the **literal** declaration of `d`, never escaped, escaping it would corrupt the
|
|
2201
2547
|
* very declaration a reader depends on, followed by any characters the modeled
|
|
2202
|
-
* declaration carried beyond the three a reader takes its roles from.
|
|
2548
|
+
* declaration carried beyond the three a reader takes its roles from. Those surplus
|
|
2549
|
+
* characters are kept unless the surplus could not be read back as surplus, when
|
|
2550
|
+
* **all** of it is dropped: that covers a surplus carrying the field delimiter or
|
|
2551
|
+
* any control character, and it is silent, because emit has no warning channel.
|
|
2552
|
+
* Manufacturer
|
|
2203
2553
|
* (`M`) and scientific (`S`) records are reproduced **byte-identically** from their
|
|
2204
2554
|
* preserved `rawLine` when they are already in `d`, and re-encoded from their fields
|
|
2205
2555
|
* when they are not, so their fields never collapse into one on the next read.
|
|
@@ -2234,7 +2584,11 @@ declare function serializeAstmRecord(record: AstmRecord, d?: Delimiters): string
|
|
|
2234
2584
|
* re-parsing it recovers every field. Passing `d` explicitly emits against that set
|
|
2235
2585
|
* instead, and the header declares it. Normalization replaces the four delimiter
|
|
2236
2586
|
* **roles**; a header declaration carrying characters beyond the three that hold a
|
|
2237
|
-
* role keeps them, on this path as on any other, rather than being truncated.
|
|
2587
|
+
* role keeps them, on this path as on any other, rather than being truncated. The
|
|
2588
|
+
* exception is a surplus that could not be read back as one, carrying the field
|
|
2589
|
+
* delimiter or any control character: there the **whole** surplus is dropped, on
|
|
2590
|
+
* every path including the default one, and silently, because emit returns a bare
|
|
2591
|
+
* string with no channel to say so.
|
|
2238
2592
|
*
|
|
2239
2593
|
* **`d` is checked before anything is written.** Each of the four separators must be
|
|
2240
2594
|
* exactly one character, none may be a record terminator, and no two may share a
|
|
@@ -2251,9 +2605,14 @@ declare function serializeAstmRecord(record: AstmRecord, d?: Delimiters): string
|
|
|
2251
2605
|
* therefore checked to start with the letter the record models, and one that does
|
|
2252
2606
|
* not is `ASTM_EMIT_TYPE_LETTER_COLLISION` rather than a stream that reads back as
|
|
2253
2607
|
* different records. What neither check promises is that every *field* lands where
|
|
2254
|
-
* it did: an escape sequence whose body is
|
|
2608
|
+
* it did: an escape sequence whose body is an unrecognized character that is itself a delimiter in
|
|
2609
|
+
* force is an opaque atom, so that
|
|
2255
2610
|
* delimiter never becomes a boundary, and the parse side reports that
|
|
2256
|
-
* (`
|
|
2611
|
+
* (`ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE`, plus the tolerable
|
|
2612
|
+
* `ASTM_UNKNOWN_ESCAPE_SEQUENCE`) rather than emit refusing it. The mirror case, where a sequence
|
|
2613
|
+
* ends where another could have begun so a delimiter splits that the competing alignment would
|
|
2614
|
+
* have held, is reported the same way (`ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`). Emitting
|
|
2615
|
+
* normalizes both away rather than preserving them, so neither reaches a second generation.
|
|
2257
2616
|
*
|
|
2258
2617
|
* @param input - A parsed {@link AstmMessage} or a list of {@link AstmRecord}s.
|
|
2259
2618
|
* @param d - The delimiters to emit against; defaults to the canonical `H|\^&` set.
|
|
@@ -2874,6 +3233,20 @@ declare function classifyMessage(records: readonly AstmRecord[]): AstmMessageCla
|
|
|
2874
3233
|
* unrecognized escape sequence encountered, so the caller can warn.
|
|
2875
3234
|
* @param onUnpairedEscape - Called (with the 0-based field index) for each escape
|
|
2876
3235
|
* character that heads no escape sequence and was therefore read as a literal.
|
|
3236
|
+
* @param onSwallowedDelimiter - Called (with the 0-based field index) for each
|
|
3237
|
+
* unrecognized escape sequence whose body is a splitting delimiter in force, so
|
|
3238
|
+
* that delimiter never became a boundary.
|
|
3239
|
+
* @param onAmbiguousAlignment - Called (with the 0-based field index) for each
|
|
3240
|
+
* unrecognized escape sequence whose closing escape character could instead have
|
|
3241
|
+
* opened one holding the delimiter that split, so the boundary is one of two
|
|
3242
|
+
* readings the bytes carry.
|
|
3243
|
+
* @param onAlignmentShiftedFields - Called (with the 0-based field index) for each
|
|
3244
|
+
* contested **field** boundary the reading took while resuming on an escape
|
|
3245
|
+
* character that heads no sequence, so every later field is one place further
|
|
3246
|
+
* right than the competing alignment puts it. Wired only to the field split: a
|
|
3247
|
+
* repeat or component boundary divides one field and so moves no field-indexed
|
|
3248
|
+
* slot. That is a choice and not a consequence, because components are modeled
|
|
3249
|
+
* inside a field; see {@link ShiftedFieldsSink}.
|
|
2877
3250
|
* @returns The record's fields.
|
|
2878
3251
|
* @example
|
|
2879
3252
|
* ```ts
|
|
@@ -2882,7 +3255,7 @@ declare function classifyMessage(records: readonly AstmRecord[]): AstmMessageCla
|
|
|
2882
3255
|
* fields[3].components[0]; // "28.6"
|
|
2883
3256
|
* ```
|
|
2884
3257
|
*/
|
|
2885
|
-
declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?: (fieldIndex: number) => void, onUnpairedEscape?: (fieldIndex: number) => void): AstmField[];
|
|
3258
|
+
declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?: (fieldIndex: number) => void, onUnpairedEscape?: (fieldIndex: number) => void, onSwallowedDelimiter?: (fieldIndex: number) => void, onAmbiguousAlignment?: (fieldIndex: number) => void, onAlignmentShiftedFields?: (fieldIndex: number) => void): AstmField[];
|
|
2886
3259
|
/**
|
|
2887
3260
|
* Tokenize an `H` (header) record into its fields.
|
|
2888
3261
|
*
|
|
@@ -2907,6 +3280,17 @@ declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?:
|
|
|
2907
3280
|
* @param onUnpairedEscape - Called with the 0-based whole-record field index for
|
|
2908
3281
|
* each unpaired escape character in the data portion. The declaration itself is
|
|
2909
3282
|
* opaque, so the escape character it names never reports here.
|
|
3283
|
+
* @param onSwallowedDelimiter - Called with the 0-based whole-record field index
|
|
3284
|
+
* for each unrecognized escape sequence in the data portion whose body is a
|
|
3285
|
+
* splitting delimiter in force. The declaration is opaque, so the delimiters it
|
|
3286
|
+
* names literally never report here.
|
|
3287
|
+
* @param onAmbiguousAlignment - Called with the 0-based whole-record field index for
|
|
3288
|
+
* each competing escape alignment in the data portion. The declaration is opaque,
|
|
3289
|
+
* so the characters it names literally never report here either.
|
|
3290
|
+
* @param onAlignmentShiftedFields - Called with the 0-based whole-record field index
|
|
3291
|
+
* for each contested field boundary in the data portion whose reading resumes on
|
|
3292
|
+
* an escape character heading no sequence. The declaration is opaque, so it never
|
|
3293
|
+
* reports here either.
|
|
2910
3294
|
* @returns The header's fields.
|
|
2911
3295
|
* @example
|
|
2912
3296
|
* ```ts
|
|
@@ -2916,7 +3300,7 @@ declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?:
|
|
|
2916
3300
|
* fields[4].raw; // "sender"
|
|
2917
3301
|
* ```
|
|
2918
3302
|
*/
|
|
2919
|
-
declare function tokenizeHeader(record: string, d: Delimiters, onUnknownEscape?: (fieldIndex: number) => void, onUnpairedEscape?: (fieldIndex: number) => void): AstmField[];
|
|
3303
|
+
declare function tokenizeHeader(record: string, d: Delimiters, onUnknownEscape?: (fieldIndex: number) => void, onUnpairedEscape?: (fieldIndex: number) => void, onSwallowedDelimiter?: (fieldIndex: number) => void, onAmbiguousAlignment?: (fieldIndex: number) => void, onAlignmentShiftedFields?: (fieldIndex: number) => void): AstmField[];
|
|
2920
3304
|
/**
|
|
2921
3305
|
* The primary scalar of a field: its first repeat's first component, decoded.
|
|
2922
3306
|
* Returns `undefined` for a truly empty field so callers can distinguish
|
|
@@ -2968,7 +3352,19 @@ declare const FATAL_CODES: {
|
|
|
2968
3352
|
readonly EMPTY_INPUT: "EMPTY_INPUT";
|
|
2969
3353
|
/** The first record is not an `H` (header) record: an ASTM message must lead with `H`. */
|
|
2970
3354
|
readonly ASTM_RECORD_NO_HEADER: "ASTM_RECORD_NO_HEADER";
|
|
2971
|
-
/**
|
|
3355
|
+
/**
|
|
3356
|
+
* The first `H` record could not declare all four delimiters (field/repeat/component/escape).
|
|
3357
|
+
* One code, four reasons, and the message says which: the record is not a header, it is shorter
|
|
3358
|
+
* than a header plus a three-character definition, its definition field holds fewer than three
|
|
3359
|
+
* characters before the next field separator, or its field separator is also one of the other
|
|
3360
|
+
* three. Only the second of those is "too short", so read the message rather than assuming it.
|
|
3361
|
+
*
|
|
3362
|
+
* **Two of the four cannot be reached on this fatal, and are not dead code.** A first record that
|
|
3363
|
+
* is not an `H` raises `ASTM_RECORD_NO_HEADER` before the declaration is ever read, and a field
|
|
3364
|
+
* separator reused among the other three ends the delimiter definition where it appears, so the
|
|
3365
|
+
* truncation reason answers first. The reader names all four because it is also called directly
|
|
3366
|
+
* and on later headers, where the same conditions are a warning rather than this fatal.
|
|
3367
|
+
*/
|
|
2972
3368
|
readonly ASTM_RECORD_UNDECLARED_DELIMITERS: "ASTM_RECORD_UNDECLARED_DELIMITERS";
|
|
2973
3369
|
};
|
|
2974
3370
|
/**
|
|
@@ -3043,14 +3439,53 @@ declare class AstmParseError extends Error {
|
|
|
3043
3439
|
* next escape character with no bound, which meant a single unescaped `&` in a
|
|
3044
3440
|
* value merged every field after it into that value, in silence.
|
|
3045
3441
|
*
|
|
3046
|
-
* **The atom rule is unchanged and still costs a boundary in one case
|
|
3047
|
-
* is opaque by design, so where its body is
|
|
3048
|
-
* canonical set) that delimiter does not
|
|
3049
|
-
* keeps `&F&` one token under a declared
|
|
3050
|
-
*
|
|
3051
|
-
*
|
|
3052
|
-
*
|
|
3053
|
-
*
|
|
3442
|
+
* **The atom rule is unchanged and still costs a boundary in one case, which now
|
|
3443
|
+
* has a report of its own.** A triple is opaque by design, so where its body is
|
|
3444
|
+
* itself a delimiter (`&|&` under the canonical set) that delimiter does not
|
|
3445
|
+
* split. That is deliberate: it is what keeps `&F&` one token under a declared
|
|
3446
|
+
* set that names `F` as a delimiter. Where the body is not a recognized mnemonic
|
|
3447
|
+
* the parse layer reports it as `ASTM_UNKNOWN_ESCAPE_SEQUENCE`, and where that
|
|
3448
|
+
* unrecognized body is *also* one of the three splitting roles in force it
|
|
3449
|
+
* reports `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` **as well**, which is the
|
|
3450
|
+
* code that says a boundary went missing rather than merely that a body was not
|
|
3451
|
+
* recognized. Do not "fix" this by narrowing the atom: the narrowing would break
|
|
3452
|
+
* the guarantee the atom exists for. The split is unchanged; what changed is that
|
|
3453
|
+
* the loss is now reported by a code no profile may tolerate.
|
|
3454
|
+
*
|
|
3455
|
+
* **The match is greedy and leftmost, and that costs a boundary in the opposite
|
|
3456
|
+
* direction, which also has a report of its own.** Triples are taken left to
|
|
3457
|
+
* right, so the escape character that *closes* one triple cannot also *open* the
|
|
3458
|
+
* next. Where it could have (the character after a triple is a splitting
|
|
3459
|
+
* delimiter and the one after that is the escape character), the bytes carry two
|
|
3460
|
+
* alignments that disagree about that delimiter: under the reading taken it ends
|
|
3461
|
+
* a field, repeat or component, and under the other it sits inside an opaque
|
|
3462
|
+
* atom and ends nothing. Where the earlier triple's body was **not** a
|
|
3463
|
+
* recognized mnemonic, the reading taken rests on a triple this codec cannot
|
|
3464
|
+
* interpret at all, so nothing in its own vocabulary prefers that reading to the
|
|
3465
|
+
* competing one, and the parse layer reports
|
|
3466
|
+
* `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`. The split is unchanged
|
|
3467
|
+
* here too: the leftmost reading is kept, and what is new is that a boundary
|
|
3468
|
+
* which may not be the sender's is no longer reported only by codes a profile
|
|
3469
|
+
* may tolerate. Where that earlier body **was** a recognized mnemonic nothing is
|
|
3470
|
+
* reported, and the reason is narrower than it looks, so read
|
|
3471
|
+
* {@link AmbiguousAlignmentSink} before widening it.
|
|
3472
|
+
*
|
|
3473
|
+
* **A second question is asked at the same position, and it is a different one,
|
|
3474
|
+
* which is why it is a second code and not a widening of the first.** That report
|
|
3475
|
+
* asks whether this codec's vocabulary prefers the reading taken *at* the contested
|
|
3476
|
+
* position. It says nothing about what the reading taken makes of the bytes *after*
|
|
3477
|
+
* the boundary, and that is where the two alignments really diverge: they resume
|
|
3478
|
+
* one character apart, so they disagree about the whole tail. Where the escape
|
|
3479
|
+
* character the reading taken resumes on heads no sequence at all, the boundary was
|
|
3480
|
+
* bought with a byte this reading cannot read, while the competing alignment is
|
|
3481
|
+
* precisely the reading that can. On the **field** separator that gained boundary
|
|
3482
|
+
* shifts every later field one place, so a result record's units and **result
|
|
3483
|
+
* status** are read out of slots the competing alignment does not put them in, and
|
|
3484
|
+
* a status of `final` can be a consequence of the alignment rather than something
|
|
3485
|
+
* the sender wrote there. That is `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`; see
|
|
3486
|
+
* {@link ShiftedFieldsSink}, including the tail and the two other delimiter roles it
|
|
3487
|
+
* deliberately stays silent on, and why neither silence is a claim that nothing was
|
|
3488
|
+
* lost there.
|
|
3054
3489
|
*
|
|
3055
3490
|
* Re-escaping (the inverse, for spec-clean emit) lives in the serializer and is
|
|
3056
3491
|
* deliberately not implemented here.
|
|
@@ -3070,6 +3505,130 @@ type UnknownEscapeSink = () => void;
|
|
|
3070
3505
|
* `ASTM_UNPAIRED_ESCAPE_CHARACTER` warning. Optional so the codec can be used purely.
|
|
3071
3506
|
*/
|
|
3072
3507
|
type UnpairedEscapeSink = () => void;
|
|
3508
|
+
/**
|
|
3509
|
+
* A callback the codec calls when an **unrecognized** escape body is itself one of
|
|
3510
|
+
* the three splitting delimiters in force (field, repeat, component), so the atom
|
|
3511
|
+
* rule kept that character out of the split and a boundary the sender's bytes
|
|
3512
|
+
* carried never became one. The value is preserved verbatim either way; the
|
|
3513
|
+
* callback lets the parser surface a value-free
|
|
3514
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` warning. Optional so the codec can
|
|
3515
|
+
* be used purely.
|
|
3516
|
+
*
|
|
3517
|
+
* The **escape** character is deliberately not in that test: it is not a splitting
|
|
3518
|
+
* role, so `&&&` under the canonical set loses no boundary. A body that is a
|
|
3519
|
+
* *recognized* mnemonic is not in it either, whatever character it is: `&F&` under
|
|
3520
|
+
* a set naming `F` as the repeat delimiter is the escaped field delimiter the
|
|
3521
|
+
* sender wrote, and reading it as a swallowed repeat boundary would report the
|
|
3522
|
+
* escape mechanism working as a defect.
|
|
3523
|
+
*/
|
|
3524
|
+
type SwallowedDelimiterSink = () => void;
|
|
3525
|
+
/**
|
|
3526
|
+
* A callback the split calls when the escape character that **closed** an
|
|
3527
|
+
* unrecognized escape sequence could instead have **opened** one whose body is the
|
|
3528
|
+
* delimiter being split on, so the two alignments of the same bytes disagree about
|
|
3529
|
+
* whether that delimiter ends a field, repeat or component. The leftmost reading is
|
|
3530
|
+
* kept and nothing is re-split; the callback lets the parser surface a value-free
|
|
3531
|
+
* `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT` warning. Optional so the split can be
|
|
3532
|
+
* used purely.
|
|
3533
|
+
*
|
|
3534
|
+
* Two exclusions, both deliberate. The first is what keeps this off conformant
|
|
3535
|
+
* streams, and it is **wider than the case it is justified on**, so the residue is
|
|
3536
|
+
* named here rather than left to be found:
|
|
3537
|
+
*
|
|
3538
|
+
* - **The earlier sequence's body must not be a recognized mnemonic.** The test is
|
|
3539
|
+
* which alignment this codec's own vocabulary supports, not which one is tidier.
|
|
3540
|
+
* Where the earlier body is unrecognized the reading taken rests on a triple the
|
|
3541
|
+
* codec cannot interpret, while the competitor's body is the delimiter character,
|
|
3542
|
+
* which it usually cannot interpret either: nothing prefers one, and that is what
|
|
3543
|
+
* this reports. Where the earlier body is a recognized mnemonic the reading taken
|
|
3544
|
+
* interprets a construct (`&F&` is the sender escaping a field separator, which is
|
|
3545
|
+
* what the mechanism is for) and the competitor **usually** interprets none, so the
|
|
3546
|
+
* vocabulary usually prefers one, and reporting it would report the escape
|
|
3547
|
+
* mechanism working. Usually, not always: see the second residue below.
|
|
3548
|
+
* **What that argument does not cover, measured.** It does not follow that the
|
|
3549
|
+
* reading taken is *conformant*: under `28.6&F&|&U/L` it is `&F&`, a real
|
|
3550
|
+
* separator, and then a bare escape character, which this package reports as a
|
|
3551
|
+
* deviation of its own. And where the declared set names a mnemonic letter as a
|
|
3552
|
+
* splitting delimiter, both alignments interpret exactly one construct and
|
|
3553
|
+
* neither is preferred, yet the exclusion still silences it. Both residues are
|
|
3554
|
+
* recorded with their measurements rather than closed by widening this test: the
|
|
3555
|
+
* criterion that would cover them is a different one (counting what each
|
|
3556
|
+
* alignment interprets), and swapping criteria moves which streams a published
|
|
3557
|
+
* package refuses.
|
|
3558
|
+
* - **The following character must be the delimiter this split is taken on.** The
|
|
3559
|
+
* split runs once per role, so a character that is a delimiter in a *later* role
|
|
3560
|
+
* is reported by that role's pass, on the segment it survives into, and never
|
|
3561
|
+
* twice. A delimiter with no escape character two positions past it is excluded by
|
|
3562
|
+
* the rule that defines the condition rather than by a judgement: the sequence the
|
|
3563
|
+
* competing alignment would need never closes, so there is no competitor.
|
|
3564
|
+
*
|
|
3565
|
+
* @param segmentIndex - The 0-based index of the segment being accumulated when the
|
|
3566
|
+
* ambiguity was seen, so a caller splitting a record into fields can report which
|
|
3567
|
+
* field it sat in. A caller splitting one field into repeats or components
|
|
3568
|
+
* already knows the field index and ignores this.
|
|
3569
|
+
*/
|
|
3570
|
+
type AmbiguousAlignmentSink = (segmentIndex: number) => void;
|
|
3571
|
+
/**
|
|
3572
|
+
* A callback the split calls when a competing escape alignment decided a boundary
|
|
3573
|
+
* **and the reading taken cannot read the byte immediately past it**: the escape
|
|
3574
|
+
* character the leftmost reading resumes on heads no escape sequence at all, so it
|
|
3575
|
+
* is kept as a bare literal (reported separately as
|
|
3576
|
+
* `ASTM_UNPAIRED_ESCAPE_CHARACTER`), while the competing alignment is exactly the
|
|
3577
|
+
* reading that gives that character a job, as the close of its own triple.
|
|
3578
|
+
*
|
|
3579
|
+
* Wired **only to the split taken on the field separator**, because that is the
|
|
3580
|
+
* whole of its claim: a gained *field* boundary shifts every later field one place,
|
|
3581
|
+
* so a record's modeled slots after it are decided by the alignment rather than by
|
|
3582
|
+
* the sender's own positions. On a result record that is the units slot and the
|
|
3583
|
+
* **result status** slot: the sender's trailing letter lands in field 9 under the
|
|
3584
|
+
* reading taken and in no field at all under the competing one, so a status of
|
|
3585
|
+
* `final` can be a consequence of the alignment rather than something the sender
|
|
3586
|
+
* put there. A gained *repeat* or *component* boundary divides one field and
|
|
3587
|
+
* reaches nothing outside it, so it moves no **field-indexed** slot and is
|
|
3588
|
+
* deliberately outside this. **That bound is a choice, not a consequence, and the
|
|
3589
|
+
* difference matters**: components are modeled *inside* a field (a Universal Test
|
|
3590
|
+
* ID's coding scheme and local code, a patient name's parts), so a gained component
|
|
3591
|
+
* boundary does move a modeled slot. That is measured, unreported by anything, and
|
|
3592
|
+
* open, not something this sink covers. The callback lets the parser surface a
|
|
3593
|
+
* value-free `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` warning. Optional so the split
|
|
3594
|
+
* can be used purely.
|
|
3595
|
+
*
|
|
3596
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is
|
|
3597
|
+
* identical, and the status read is the status that was always read. What is new is
|
|
3598
|
+
* that the shift is reported by a code no profile may tolerate, where before it was
|
|
3599
|
+
* covered only by tolerable ones.
|
|
3600
|
+
*
|
|
3601
|
+
* **This is independent of {@link AmbiguousAlignmentSink}, and fires alongside it
|
|
3602
|
+
* rather than instead of it.** That one asks whether the codec's vocabulary prefers
|
|
3603
|
+
* the reading taken *at* the contested position and is silent where the earlier
|
|
3604
|
+
* body is a recognized mnemonic; this one asks what the reading taken makes of the
|
|
3605
|
+
* bytes *after* the boundary and does not consult the earlier body at all. The two
|
|
3606
|
+
* questions are different, so neither test is widened to answer the other.
|
|
3607
|
+
*
|
|
3608
|
+
* **The tail is weighed one construct deep, and that bound is stated rather than
|
|
3609
|
+
* left to be found.** Two tails are excluded, with the same reason: the bytes past
|
|
3610
|
+
* the boundary prefer the reading taken.
|
|
3611
|
+
*
|
|
3612
|
+
* - **The escape character heads a sequence this codec recognizes.** Then the
|
|
3613
|
+
* reading taken interprets it and leaves nothing bare, while the competing
|
|
3614
|
+
* alignment would leave it bare. Under a set naming the field separator `F`,
|
|
3615
|
+
* `28.6&F&F&F&U/L` is the sender escaping that separator, writing it, and
|
|
3616
|
+
* escaping it again: entirely well formed, and refusing it is the over-refusal
|
|
3617
|
+
* that sank the preceding candidate criterion for this family.
|
|
3618
|
+
* - **The escape character heads a sequence whose body this codec does not
|
|
3619
|
+
* recognize.** Then the reading taken still consumes it as a sequence and carries
|
|
3620
|
+
* one unreadable body, while the competing alignment would leave *two* escape
|
|
3621
|
+
* characters bare. The preference is stronger there, not weaker.
|
|
3622
|
+
*
|
|
3623
|
+
* What that bound leaves open is the second of those tails, where the field shift
|
|
3624
|
+
* is real and this stays silent. It is measured and named rather than closed by
|
|
3625
|
+
* widening the test, because widening it would report a boundary the bytes prefer.
|
|
3626
|
+
*
|
|
3627
|
+
* @param segmentIndex - The 0-based index of the field being accumulated when the
|
|
3628
|
+
* shifting boundary was taken. Every field after it is one place further right
|
|
3629
|
+
* than the competing alignment puts it.
|
|
3630
|
+
*/
|
|
3631
|
+
type ShiftedFieldsSink = (segmentIndex: number) => void;
|
|
3073
3632
|
/**
|
|
3074
3633
|
* Decode the four recognized ASTM escape mnemonics in a single already-split
|
|
3075
3634
|
* leaf (a component string), substituting the *active* delimiters read from the
|
|
@@ -3084,6 +3643,8 @@ type UnpairedEscapeSink = () => void;
|
|
|
3084
3643
|
* @param d - The delimiters resolved from the header.
|
|
3085
3644
|
* @param onUnknown - Called once per unrecognized escape body encountered.
|
|
3086
3645
|
* @param onUnpaired - Called once per escape character that heads no sequence.
|
|
3646
|
+
* @param onSwallowedDelimiter - Called once per unrecognized escape body that is
|
|
3647
|
+
* itself a splitting delimiter in force, so that delimiter never split.
|
|
3087
3648
|
* @returns The decoded string.
|
|
3088
3649
|
* @example
|
|
3089
3650
|
* ```ts
|
|
@@ -3092,7 +3653,7 @@ type UnpairedEscapeSink = () => void;
|
|
|
3092
3653
|
* decodeEscapes("O&Brien", CANONICAL_DELIMITERS); // "O&Brien" (literal, reported)
|
|
3093
3654
|
* ```
|
|
3094
3655
|
*/
|
|
3095
|
-
declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownEscapeSink, onUnpaired?: UnpairedEscapeSink): string;
|
|
3656
|
+
declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownEscapeSink, onUnpaired?: UnpairedEscapeSink, onSwallowedDelimiter?: SwallowedDelimiterSink): string;
|
|
3096
3657
|
/**
|
|
3097
3658
|
* Split `text` on `delimiter`, treating an escape sequence (escape, one body
|
|
3098
3659
|
* character, escape) as an opaque atom so a delimiter that appears *inside* an
|
|
@@ -3110,11 +3671,33 @@ declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownE
|
|
|
3110
3671
|
* closes is what used to merge the whole remainder of a record into one field.
|
|
3111
3672
|
* Decoding the resulting leaf is what reports it, so this function stays a pure
|
|
3112
3673
|
* split. Note the two rules together: a delimiter after such a character does
|
|
3113
|
-
* split, and a delimiter sitting inside a real three-character atom does not.
|
|
3674
|
+
* split, and a delimiter sitting inside a real three-character atom does not. The
|
|
3675
|
+
* second of those is reported, from the decode step rather than from here,
|
|
3676
|
+
* whenever the atom's body was not a recognized mnemonic.
|
|
3677
|
+
*
|
|
3678
|
+
* **Atoms are matched greedily, leftmost first, and where that choice decides a
|
|
3679
|
+
* boundary it is reported from here.** The escape character closing one triple
|
|
3680
|
+
* cannot also open the next, so `28.6&Z&|&U/L` is read as the atom `&Z&`, then a
|
|
3681
|
+
* field separator that splits, and not as `&Z`, then the atom `&|&` whose field
|
|
3682
|
+
* separator would not have split. Both alignments are in the bytes and they
|
|
3683
|
+
* disagree by one boundary. The reading is **not** changed (that would only pick
|
|
3684
|
+
* the other alignment, with no more evidence for it); it is reported through
|
|
3685
|
+
* `onAmbiguousAlignment`, and only where the earlier body was unrecognized, since
|
|
3686
|
+
* a recognized mnemonic is a construct this codec can interpret and the
|
|
3687
|
+
* competitor's body usually is not. That exclusion is wider than that argument:
|
|
3688
|
+
* see {@link AmbiguousAlignmentSink}.
|
|
3114
3689
|
*
|
|
3115
3690
|
* @param text - The field or repeat string to split.
|
|
3116
3691
|
* @param delimiter - The delimiter to split on.
|
|
3117
3692
|
* @param escape - The active escape character.
|
|
3693
|
+
* @param onAmbiguousAlignment - Called once per unrecognized escape sequence whose
|
|
3694
|
+
* closing escape character could instead have opened a sequence holding this
|
|
3695
|
+
* delimiter, so the boundary taken here is not the only reading of the bytes.
|
|
3696
|
+
* @param onShiftedFields - Called once per contested boundary where the escape
|
|
3697
|
+
* character this reading resumes on heads no sequence of its own, so the boundary
|
|
3698
|
+
* was bought with a byte the reading cannot read. Wired only by the split taken on
|
|
3699
|
+
* the field separator: see {@link ShiftedFieldsSink} for why, and for the tail it
|
|
3700
|
+
* deliberately does not report.
|
|
3118
3701
|
* @returns The raw segments, in order.
|
|
3119
3702
|
* @example
|
|
3120
3703
|
* ```ts
|
|
@@ -3124,7 +3707,7 @@ declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownE
|
|
|
3124
3707
|
* splitEscapeAware("O&Brien^John", "^", "&"); // ["O&Brien", "John"] (unpaired: literal)
|
|
3125
3708
|
* ```
|
|
3126
3709
|
*/
|
|
3127
|
-
declare function splitEscapeAware(text: string, delimiter: string, escape: string): string[];
|
|
3710
|
+
declare function splitEscapeAware(text: string, delimiter: string, escape: string, onAmbiguousAlignment?: AmbiguousAlignmentSink, onShiftedFields?: ShiftedFieldsSink): string[];
|
|
3128
3711
|
|
|
3129
3712
|
/**
|
|
3130
3713
|
* Deep-freeze helper for the immutable ASTM model.
|
|
@@ -3457,9 +4040,60 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
3457
4040
|
* field separator was absent outright, so it is a reader of record structure that
|
|
3458
4041
|
* cannot be relied on as a sweep.
|
|
3459
4042
|
*
|
|
4043
|
+
* **A fourth and a fifth reader of record structure landed together, and the
|
|
4044
|
+
* allow-list was re-derived against them.** `ASTM_RECORD_DELIMITER_ROLE_COLLISION`
|
|
4045
|
+
* reports a header declaring one character in two of the repeat / component /
|
|
4046
|
+
* escape roles, so the boundary between those two roles is not in the bytes;
|
|
4047
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` reports an unrecognized escape body
|
|
4048
|
+
* that is itself a splitting delimiter, so the atom rule kept it out of the split.
|
|
4049
|
+
* Both are safety-critical by construction, because that side is computed rather
|
|
4050
|
+
* than listed. What they required was re-reading the survivors below against them,
|
|
4051
|
+
* and two of the four needed their arguments rewritten rather than merely
|
|
4052
|
+
* re-checked: each was, on its own, the *only* warning a stream exhibiting one of
|
|
4053
|
+
* these conditions raised. That is the state this file exists to prevent, and it is
|
|
4054
|
+
* why the remedy in both cases was a **second, narrower, non-tolerable code**
|
|
4055
|
+
* rather than striking the tolerable one off. Striking it off would have changed
|
|
4056
|
+
* behavior for every profile naming it while still leaving the two conditions
|
|
4057
|
+
* reported by codes that also fire on cases costing nothing.
|
|
4058
|
+
*
|
|
4059
|
+
* **A sixth reader landed with `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`, and it is
|
|
4060
|
+
* the mirror of the fifth.** That code reports that the escape character closing an
|
|
4061
|
+
* unrecognized sequence could instead have opened one holding the delimiter that
|
|
4062
|
+
* split, so the bytes carry two alignments disagreeing by one boundary and the
|
|
4063
|
+
* leftmost was taken. It is safety-critical by construction, and the direction is
|
|
4064
|
+
* why it could not be left to the codes below: the fifth reports a boundary the
|
|
4065
|
+
* reading **lost**, this one a boundary the reading may have **gained**, and a
|
|
4066
|
+
* gained boundary hands back a value the sender's bytes do not unambiguously carry.
|
|
4067
|
+
* Both of the codes that condition previously raised are on the list below, so a
|
|
4068
|
+
* profile naming them left `{ strict: true }` accepting it. Re-reading the survivors
|
|
4069
|
+
* against it changed no admission and rewrote no argument: none of the four is a
|
|
4070
|
+
* statement about where a boundary was taken, and the new code is not tolerable, so
|
|
4071
|
+
* the same "a profile re-badges the code it names and no other" reason already
|
|
4072
|
+
* written under the two escape entries covers it.
|
|
4073
|
+
*
|
|
4074
|
+
* **A further reader landed with `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, and it is
|
|
4075
|
+
* the first one that reaches a MODELED SLOT rather than a boundary.** It reports
|
|
4076
|
+
* that a contested alignment decided a **field** boundary while the reading taken
|
|
4077
|
+
* resumes on an escape character heading no sequence, so every later field sits one
|
|
4078
|
+
* place further right than the competing alignment puts it. On a result record that
|
|
4079
|
+
* moves the units and the **result status**: the sender's trailing letter is read out
|
|
4080
|
+
* of field 9 under the reading taken and out of no field at all under the other, so a
|
|
4081
|
+
* status of `final` can be a consequence of the alignment rather than something the
|
|
4082
|
+
* sender wrote there. Until it existed the only warning on that stream was the
|
|
4083
|
+
* **tolerable** `ASTM_UNPAIRED_ESCAPE_CHARACTER`, so the widest gate-legal profile
|
|
4084
|
+
* plus `{ strict: true }` accepted a fabricated `final` on a lab result. It is
|
|
4085
|
+
* safety-critical by construction and must never be admitted. Re-reading the
|
|
4086
|
+
* survivors against it moved no admission, and the reason is worth stating because it
|
|
4087
|
+
* is not last time's: `ASTM_UNPAIRED_ESCAPE_CHARACTER` stays on the list because it
|
|
4088
|
+
* remains true and benign of every case that costs nothing (a bare escape character
|
|
4089
|
+
* in a value alters no boundary at all), and the case where it accompanies a shifted
|
|
4090
|
+
* field is now reported by a code no profile may re-badge.
|
|
4091
|
+
*
|
|
3460
4092
|
* **So the ones that remain are recorded with the reading each one survives**, not
|
|
3461
|
-
* merely with the value it preserves. Each was re-derived against
|
|
3462
|
-
* record structure above
|
|
4093
|
+
* merely with the value it preserves. Each was re-derived against **every** reader of
|
|
4094
|
+
* record structure named above (this sentence deliberately names no count: it has
|
|
4095
|
+
* been corrected upward at every slice that landed one), plus the single-message /
|
|
4096
|
+
* single-patient guards:
|
|
3463
4097
|
*
|
|
3464
4098
|
* - `ASTM_NONSTANDARD_DELIMITERS`: the header's own declared delimiters are read
|
|
3465
4099
|
* and honored either way, and this notes only that they differ from the canonical
|
|
@@ -3471,29 +4105,70 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
3471
4105
|
* force. A stream may be wholly non-canonical and split perfectly, and a wholly
|
|
3472
4106
|
* canonical stream can still carry a record that does not split, so tolerating the
|
|
3473
4107
|
* first can never quiet the second.
|
|
4108
|
+
*
|
|
4109
|
+
* **Its admission was resting on a set of declarations it does not distinguish,
|
|
4110
|
+
* and no longer is.** A declaration naming one character in two roles is
|
|
4111
|
+
* necessarily non-canonical, so this code fired on it, and until
|
|
4112
|
+
* `ASTM_RECORD_DELIMITER_ROLE_COLLISION` existed it was the *only* warning such a
|
|
4113
|
+
* stream raised: tolerating "the declared set is unusual" therefore quieted
|
|
4114
|
+
* "the declared set cannot express the boundary it was read with". That second
|
|
4115
|
+
* condition now has its own code, which is not tolerable, so what is left here is
|
|
4116
|
+
* the honest statement it always meant to be: the set differs from the canonical
|
|
4117
|
+
* one and was honored. The reason tolerating this one cannot quiet that one is the
|
|
4118
|
+
* profile mechanism and nothing subtler: a profile re-badges the code it names and
|
|
4119
|
+
* no other. It is **not** that the two conditions are independent. They are not:
|
|
4120
|
+
* every colliding set is non-canonical, which is exactly why this bullet needed
|
|
4121
|
+
* rewriting.
|
|
3474
4122
|
* - `ASTM_UNKNOWN_ESCAPE_SEQUENCE`: an unrecognized escape body is **preserved
|
|
3475
4123
|
* byte-for-byte** in the decoded value (the escape codec does not guess at one),
|
|
3476
|
-
* so the value is identical with or without the profile.
|
|
3477
|
-
*
|
|
3478
|
-
*
|
|
3479
|
-
*
|
|
3480
|
-
*
|
|
3481
|
-
* **
|
|
3482
|
-
*
|
|
3483
|
-
*
|
|
3484
|
-
*
|
|
3485
|
-
*
|
|
3486
|
-
*
|
|
4124
|
+
* so the value is identical with or without the profile. A decoded field value is
|
|
4125
|
+
* never cut back into records, the type letter is read before decoding, and the
|
|
4126
|
+
* split reader counts fields the escape-aware tokenizer has already finished
|
|
4127
|
+
* dividing, so none of those three sees it.
|
|
4128
|
+
*
|
|
4129
|
+
* **Part 2 acquired a reader here, and it is named rather than left to be found.**
|
|
4130
|
+
* `isSplittingDelimiter` in `../common/escapes.ts` runs **only** on the
|
|
4131
|
+
* unrecognized-body condition this code reports, and decides out of it whether to
|
|
4132
|
+
* raise `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE`. That is a reader of the
|
|
4133
|
+
* reported condition, which is exactly what part 2 asks about, so this entry no
|
|
4134
|
+
* longer survives on "nothing reads it". It survives on a narrower and checkable
|
|
4135
|
+
* claim: a profile tolerating a code re-badges **that code and no other**, so
|
|
4136
|
+
* tolerating this one cannot quiet what the reader raises, and what it raises is
|
|
4137
|
+
* not tolerable in any case. If a future reader of this same condition ever
|
|
4138
|
+
* produces something a profile **can** tolerate, this entry stops being admissible
|
|
4139
|
+
* and comes off the list.
|
|
4140
|
+
*
|
|
4141
|
+
* **A second reader of the same condition landed, and it is named here for the
|
|
4142
|
+
* same reason.** The mnemonic test in `../common/escapes.ts` is now asked twice:
|
|
4143
|
+
* once by the decoder, to decide whether a body is recognized, and once by the
|
|
4144
|
+
* escape-aware split, which raises `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT` only
|
|
4145
|
+
* where an **unrecognized** body sits in a sequence a competing alignment would
|
|
4146
|
+
* have aligned differently. That is the condition this code reports, read a second
|
|
4147
|
+
* time, and this entry survives it on the same narrower claim: what that reader
|
|
4148
|
+
* raises is a different code, which a profile cannot name and cannot tolerate.
|
|
4149
|
+
*
|
|
4150
|
+
* **This entry used to be recorded as questionable, and what made it questionable
|
|
4151
|
+
* is now a separate code that is not on this list.** The argument above is about
|
|
4152
|
+
* the *decoded value*, and it holds. What it did not cover is that the
|
|
4153
|
+
* escape-aware split itself treats an `&X&` triple as opaque, so where `X` is a
|
|
4154
|
+
* delimiter that delimiter never became a boundary: the split the argument says
|
|
4155
|
+
* has "already finished dividing" divided one time too few, and this code was the
|
|
4156
|
+
* only report of it. Measured on the canonical set:
|
|
3487
4157
|
* `R|1|^^^687|28.6&|&U/L||||F` yields a value of `28.6&|&U/L`, no units, and status
|
|
3488
|
-
* `unspecified` rather than `final`,
|
|
3489
|
-
* tolerating it (the shipped `referenceCorpus` does)
|
|
3490
|
-
* it. That
|
|
3491
|
-
*
|
|
3492
|
-
*
|
|
3493
|
-
*
|
|
3494
|
-
*
|
|
3495
|
-
*
|
|
3496
|
-
*
|
|
4158
|
+
* `unspecified` rather than `final`, and this was the sole warning, so a profile
|
|
4159
|
+
* tolerating it (the shipped `referenceCorpus` does) let `{ strict: true }` accept
|
|
4160
|
+
* it. That failed part 1 of the two-clause test on the reading, not on the value.
|
|
4161
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` now fires **alongside** this code on
|
|
4162
|
+
* exactly that subset and is safety-critical, so the lost boundary is reported by
|
|
4163
|
+
* something no profile may tolerate and that same stream is refused under
|
|
4164
|
+
* `{ strict: true }`. This code was **not** removed, for two reasons: removing it
|
|
4165
|
+
* changes behavior for every profile naming it, and it remains true and benign of
|
|
4166
|
+
* every unrecognized body that is not a delimiter in force, where the sequence is
|
|
4167
|
+
* preserved byte-for-byte and no boundary is lost. What is scoped away here is a
|
|
4168
|
+
* claim, not a guard. Do not close the underlying condition by narrowing the atom,
|
|
4169
|
+
* which is what keeps `&F&` one token under a delimiter set that names `F` as a
|
|
4170
|
+
* delimiter, and do not read the new code as a repair: the value is byte-identical
|
|
4171
|
+
* to what it was, and only the reporting changed.
|
|
3497
4172
|
* - `ASTM_UNPAIRED_ESCAPE_CHARACTER`: an escape character heading no escape
|
|
3498
4173
|
* sequence, read as the **literal character it is** and kept byte-for-byte in the
|
|
3499
4174
|
* decoded value, so the value is identical with or without the profile. Note what
|
|
@@ -3506,6 +4181,14 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
3506
4181
|
* replaced, which **would** have failed part 1: reading the same character as an
|
|
3507
4182
|
* unterminated sequence merged every field after it into one, and a code reporting
|
|
3508
4183
|
* that would have been a report of lost values.
|
|
4184
|
+
*
|
|
4185
|
+
* **The alignment reader is not a reader of this condition, and the distinction is
|
|
4186
|
+
* worth stating rather than leaving to be checked.** It asks whether the character
|
|
4187
|
+
* two positions past a sequence *is* the escape character, never whether that
|
|
4188
|
+
* character heads a sequence of its own, which is the question this code answers.
|
|
4189
|
+
* The two are independent in both directions, measured: `28.6&Z&|&U/L` raises this
|
|
4190
|
+
* code and the alignment code together, and `28.6&Z&|&U&L` raises the alignment
|
|
4191
|
+
* code with no unpaired character anywhere in it.
|
|
3509
4192
|
* - `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS`: a request-information status carried
|
|
3510
4193
|
* verbatim as a leaf field on a `Q` record; the code set is paywalled and is not
|
|
3511
4194
|
* interpreted, profile or not. Classification reads whether a `Q` record is
|
|
@@ -3517,8 +4200,11 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
3517
4200
|
* undefined abnormal flag or result status, an unparseable reference range, absent
|
|
3518
4201
|
* units, a mis-attached comment, a partial timestamp, a query-vs-result ambiguity,
|
|
3519
4202
|
* an unrecognized record type, a record the delimiters in force could not split, a
|
|
3520
|
-
*
|
|
3521
|
-
*
|
|
4203
|
+
* declaration naming one character in two roles, a delimiter an unrecognized escape
|
|
4204
|
+
* sequence kept out of the split, a boundary a competing escape alignment disagrees
|
|
4205
|
+
* about, a bad frame checksum / sequence gap / unterminated
|
|
4206
|
+
* / oversize frame, an ambiguous transport, an unexpected protocol event, or a
|
|
4207
|
+
* rejected frame: is forbidden.
|
|
3522
4208
|
*
|
|
3523
4209
|
* **What this file cannot do for you.** Part 2 is a review obligation, not a
|
|
3524
4210
|
* mechanical one. There is no honest automatic check for "nothing load-bearing
|
|
@@ -3549,6 +4235,20 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
3549
4235
|
* reports a record the delimiters in force could not split, so every modeled field
|
|
3550
4236
|
* of that record is missing: it fails the first half of the test outright.
|
|
3551
4237
|
*
|
|
4238
|
+
* `ASTM_RECORD_DELIMITER_ROLE_COLLISION`,
|
|
4239
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` and
|
|
4240
|
+
* `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT` are not on this list and must not be
|
|
4241
|
+
* added to it. Each reports a boundary the reading cannot defend from the bytes (the
|
|
4242
|
+
* first two a boundary that is not in the reading, the third one that may not be in
|
|
4243
|
+
* the bytes), and each exists precisely because the only warnings its condition
|
|
4244
|
+
* previously raised were among the four below.
|
|
4245
|
+
*
|
|
4246
|
+
* `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` is not on this list and must not be added
|
|
4247
|
+
* to it either, and it is the strongest case of the four: it reports not merely a
|
|
4248
|
+
* boundary but a modeled slot changing hands, up to and including a result status
|
|
4249
|
+
* reading `final` that the competing alignment of the same bytes puts in no field at
|
|
4250
|
+
* all.
|
|
4251
|
+
*
|
|
3552
4252
|
* @example
|
|
3553
4253
|
* ```ts
|
|
3554
4254
|
* import { TOLERABLE_CODES } from "@cosyte/astm";
|
|
@@ -3653,7 +4353,10 @@ declare const defaultProfile: AstmProfile;
|
|
|
3653
4353
|
* only re-badges the *warning*: no value, flag, status, or identifier is altered
|
|
3654
4354
|
* or dropped. And by the safety gate in `defineAstmProfile`, it **cannot** tolerate
|
|
3655
4355
|
* a result value / flag / status / range / units, a patient or comment context, a
|
|
3656
|
-
* message-kind ambiguity, an unrecognized record type,
|
|
4356
|
+
* message-kind ambiguity, an unrecognized record type, a declaration naming one
|
|
4357
|
+
* character in two delimiter roles, a delimiter an unrecognized
|
|
4358
|
+
* escape sequence kept out of the split, a field boundary a competing escape
|
|
4359
|
+
* alignment disagrees about, or any frame / LTP integrity
|
|
3657
4360
|
* warning: those always surface, profile or not. The unrecognized-record-type case
|
|
3658
4361
|
* matters because message grouping reads a record's type letter, so a letter the
|
|
3659
4362
|
* reader does not recognize may be a header, and a header read as something else
|
|
@@ -4890,4 +5593,4 @@ declare class AstmFrameStrictError extends Error {
|
|
|
4890
5593
|
*/
|
|
4891
5594
|
declare const VERSION: string;
|
|
4892
5595
|
|
|
4893
|
-
export { ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, type AbnormalFlag, type AbnormalFlagCode, type AbnormalFlagMeaning, type AmbiguousCode, type AnyAstmWarningCode, AstmAmbiguousStreamError, type AstmDate, type AstmDatePrecision, type AstmField, type AstmFrame, AstmFrameEncodeError, type AstmFrameEncodeErrorCode, type AstmFramePosition, AstmFrameStrictError, type AstmFrameWarning, type AstmFraming, type AstmLivdWarning, type AstmLtpWarning, type AstmMessage, type AstmMessageClassification, type AstmMessageKind, AstmParseError, type AstmParseOptions, type AstmPosition, type AstmProfile, AstmProfileDefinitionError, type AstmProfileProvenance, type AstmQuirkMatch, type AstmQuirkTolerance, type AstmRecord, type AstmRecordInput, type AstmRecordWarning, AstmSerializeError, type AstmSerializeErrorCode, type AstmStreamMessage, AstmStrictError, CANONICAL_DELIMITERS, type CommentInput, type CommentRecord, type ComposeFramesOptions, type DecodeAstmFramesResult, type DefineAstmProfileOptions, type Delimiters, type DetectFramingOptions, type DetectFramingResult, FATAL_CODES, FRAME_WARNING_CODES, type FatalCode, type FrameChecksum, type FrameOptions, type FrameTerminator, type FrameWarningCode, type FramedAstmResult, type HeaderInput, type HeaderRecord, LIVD_WARNING_CODES, LTP_WARNING_CODES, type LivdAnnotation, type LivdCatalog, type LivdEntry, type LivdLookup, type LivdMapping, type LivdResult, type LivdWarningCode, type LtpAction, type LtpEvent, type LtpPhase, type LtpState, type LtpTransition, type LtpWarningCode, type ManufacturerRecord, type MessageInput, type OrderInput, type OrderRecord, type PatientInput, type PatientName, type PatientNameInput, type PatientRecord, type QueryInput, type QueryRecord, type ReferenceRange, type ReferenceRangeKind, type ResultInput, type ResultRecord, type ResultStatus, type ResultStatusCode, type ResultStatusMeaning, SAFETY_CRITICAL_CODES, type ScientificRecord, TOLERABLE_CODES, type TerminatorRecord, type UniversalTestId, type UniversalTestIdProvenance, type UnknownEscapeSink, type UnpairedEscapeSink, type UnsupportedRecord, VERSION, type VerbatimInput, WARNING_CODES, type WarningCode, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimitersRedeclared, detectFraming, encodeComponent, fieldScalar, fieldsUnseparated, 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, unpairedEscapeCharacter, unparseableReferenceRange, unreadableRedeclaration };
|
|
5596
|
+
export { ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, type AbnormalFlag, type AbnormalFlagCode, type AbnormalFlagMeaning, type AmbiguousAlignmentSink, type AmbiguousCode, type AnyAstmWarningCode, AstmAmbiguousStreamError, type AstmDate, type AstmDatePrecision, type AstmField, type AstmFrame, AstmFrameEncodeError, type AstmFrameEncodeErrorCode, type AstmFramePosition, AstmFrameStrictError, type AstmFrameWarning, type AstmFraming, type AstmLivdWarning, type AstmLtpWarning, type AstmMessage, type AstmMessageClassification, type AstmMessageKind, AstmParseError, type AstmParseOptions, type AstmPosition, type AstmProfile, AstmProfileDefinitionError, type AstmProfileProvenance, type AstmQuirkMatch, type AstmQuirkTolerance, type AstmRecord, type AstmRecordInput, type AstmRecordWarning, AstmSerializeError, type AstmSerializeErrorCode, type AstmStreamMessage, AstmStrictError, CANONICAL_DELIMITERS, type CommentInput, type CommentRecord, type ComposeFramesOptions, type DecodeAstmFramesResult, type DefineAstmProfileOptions, type DelimiterDeclarationFault, type DelimiterReadResult, type Delimiters, type DetectFramingOptions, type DetectFramingResult, FATAL_CODES, FRAME_WARNING_CODES, type FatalCode, type FrameChecksum, type FrameOptions, type FrameTerminator, type FrameWarningCode, type FramedAstmResult, type HeaderInput, type HeaderRecord, LIVD_WARNING_CODES, LTP_WARNING_CODES, type LivdAnnotation, type LivdCatalog, type LivdEntry, type LivdLookup, type LivdMapping, type LivdResult, type LivdWarningCode, type LtpAction, type LtpEvent, type LtpPhase, type LtpState, type LtpTransition, type LtpWarningCode, type ManufacturerRecord, type MessageInput, type OrderInput, type OrderRecord, type PatientInput, type PatientName, type PatientNameInput, type PatientRecord, type QueryInput, type QueryRecord, type ReferenceRange, type ReferenceRangeKind, type ResultInput, type ResultRecord, type ResultStatus, type ResultStatusCode, type ResultStatusMeaning, SAFETY_CRITICAL_CODES, type ScientificRecord, type ShiftedFieldsSink, type SwallowedDelimiterSink, TOLERABLE_CODES, type TerminatorRecord, type UniversalTestId, type UniversalTestIdProvenance, type UnknownEscapeSink, type UnpairedEscapeSink, type UnsupportedRecord, VERSION, type VerbatimInput, WARNING_CODES, type WarningCode, alignmentShiftedFields, ambiguousEscapeAlignment, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimiterRoleCollision, delimiterSwallowedByEscape, delimitersRedeclared, detectFraming, encodeComponent, fieldScalar, fieldsUnseparated, frameBadChecksum, frameOversize, frameSequenceGap, frameUnterminated, getAstmProfile, getDefaultAstmProfile, hasCollidingRoles, 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, readDelimiterDeclaration, readDelimiters, recognizeUniversalTestId, resolveProfileTransport, results, serializeAstmRecord, serializeAstmRecords, serializeField, serializeFramedAstm, setDefaultAstmProfile, splitEscapeAware, toChecksumHex, tokenizeHeader, tokenizeRecord, undefinedAbnormalFlag, undefinedResultStatus, uninterpretedQueryStatus, unitsAbsent, unknownEscapeSequence, unknownRecordType, unpairedEscapeCharacter, unparseableReferenceRange, unreadableRedeclaration };
|