@cosyte/astm 0.0.17 → 0.0.19
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 +380 -11
- package/README.md +93 -15
- package/dist/index.cjs +304 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +721 -91
- package/dist/index.d.ts +721 -91
- package/dist/index.mjs +303 -41
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -180,10 +180,11 @@ declare const WARNING_CODES: {
|
|
|
180
180
|
readonly ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT: "ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT";
|
|
181
181
|
/**
|
|
182
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
|
|
184
|
-
* boundary was bought with
|
|
185
|
-
* exactly the reading that can. Every field after that point sits one place further
|
|
186
|
-
* competing alignment puts it
|
|
183
|
+
* kept it, and the escape character that reading resumes on **heads no sequence this codec can
|
|
184
|
+
* interpret**, so the boundary was bought with bytes this reading cannot read while the competing
|
|
185
|
+
* alignment is exactly the reading that can. Every field after that point sits one place further
|
|
186
|
+
* right than the competing alignment puts it, **except on the one class named at the end of this
|
|
187
|
+
* entry**, where the two readings instead read the same number of fields in different places.
|
|
187
188
|
*
|
|
188
189
|
* **The shift is the harm, and on a result record it reaches the status slot.** Measured on the
|
|
189
190
|
* canonical set, `R|1|^^^687|28.6&F&|&U/L||||F` reads **9** fields under the reading taken and
|
|
@@ -214,30 +215,230 @@ declare const WARNING_CODES: {
|
|
|
214
215
|
* modeled *inside* a field: a Universal Test ID's four components are the LOINC-candidate slot,
|
|
215
216
|
* the test name, the **coding scheme** and the **local code**, and a patient name's three are
|
|
216
217
|
* 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
|
|
218
|
-
* a recognized mnemonic
|
|
219
|
-
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}
|
|
220
|
-
*
|
|
221
|
-
* {@link WARNING_CODES.
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* - **The tail is weighed one construct deep.** Where the escape
|
|
227
|
-
* resumes on heads a sequence this codec *recognizes*, the reading
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
218
|
+
* as a coding scheme and a given name as a middle name. Measured, and it used to be reported by
|
|
219
|
+
* nothing where the earlier body was a recognized mnemonic (only the tolerable
|
|
220
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} fired, so a gate-legal profile accepted
|
|
221
|
+
* it). It is now reported by
|
|
222
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS}, a separate code on the same
|
|
223
|
+
* tail test wired to the component split, because wiring this sink to another split is a
|
|
224
|
+
* different criterion needing its own population measurement. It is still not something **this**
|
|
225
|
+
* code covers. The repeat role costs the **value**, which this code does not report either, and
|
|
226
|
+
* which {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD} reports.
|
|
227
|
+
* - **The tail is weighed one construct deep, and exactly one tail is excluded.** Where the escape
|
|
228
|
+
* character the reading taken resumes on heads a sequence this codec *recognizes*, the reading
|
|
229
|
+
* taken interprets a construct and leaves nothing unread: under a set naming the field separator
|
|
230
|
+
* `F`, `28.6&F&F&F&U/L` is that separator escaped, written, and escaped again, entirely well
|
|
231
|
+
* formed, and refusing it would be an over-refusal. That is the only tail on which a stream's
|
|
232
|
+
* escaping can be clean, which is why it is the only exclusion. Where the tail heads a sequence
|
|
233
|
+
* whose body is *unrecognized*, the reading taken consumes a triple it cannot read, preserved
|
|
234
|
+
* verbatim and never guessed at, the field shift is the same shift, and **this reports it**.
|
|
235
|
+
* Consuming a triple is not interpreting one.
|
|
236
|
+
* - **That remaining silence is a TRADE, not a claim that nothing was lost.** The gained field
|
|
237
|
+
* boundary is exactly as real on the excluded tail, and there it is not merely under-reported:
|
|
238
|
+
* it is `warnings: []`. Under the canonical set `R|1|^^^687|28.6&F&|&F&U/L||||F` reads **nine**
|
|
239
|
+
* fields against the competing alignment's eight and hands back a status of **`final`** with an
|
|
240
|
+
* empty warning list. Reporting it would refuse a stream whose escaping is working, which is
|
|
241
|
+
* the trade this bound makes. **Read the raw line when an escape character sits next to a
|
|
242
|
+
* delimiter, whether or not anything fired.**
|
|
243
|
+
*
|
|
244
|
+
* **⚠️ THE ONE CLASS WHERE NO FIELD INDEX MOVES, NAMED RATHER THAN LEFT TO BE FOUND.** Where the
|
|
245
|
+
* sequence past the boundary carries the **field separator itself** as its body, the reading taken
|
|
246
|
+
* holds that character inside an opaque atom while the competing alignment splits on it, so the
|
|
247
|
+
* two readings return the **same number** of fields in **different places**. Under the canonical
|
|
248
|
+
* set `R|1|^^^687|28.6&F&|&|&U/L||||F` reads nine fields under both, the status `F` sits in field
|
|
249
|
+
* 9 under both, and what differs is the units: `&|&U/L` against `&U/L`. This still fires there,
|
|
250
|
+
* which is over-reporting relative to the field indexes and never under-reporting, and the class
|
|
251
|
+
* costs no stream its disposition: that tail body is a splitting delimiter in force, so
|
|
252
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE} has already refused the record.
|
|
253
|
+
* What holds on **every** firing tuple is that the two readings disagree and that both consume
|
|
254
|
+
* every byte, so neither is forced.
|
|
235
255
|
*
|
|
236
256
|
* **Catch it on the first read.** Emit rewrites the preserved sequences into recognized mnemonics,
|
|
237
257
|
* and those bytes carry the reading that was taken unambiguously, so a second-generation read is
|
|
238
258
|
* silent and is correct about its own bytes. A clean re-read is not evidence.
|
|
239
259
|
*/
|
|
240
260
|
readonly ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS: "ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS";
|
|
261
|
+
/**
|
|
262
|
+
* Two escape alignments of the same bytes disagreed about a **repeat** boundary, the reading
|
|
263
|
+
* taken kept it, and the escape character that reading resumes on **heads no sequence this codec
|
|
264
|
+
* can interpret**. Same contested position and same tail test as
|
|
265
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS}, on a different delimiter role, and
|
|
266
|
+
* what it costs is a different thing, which is why it is a different code.
|
|
267
|
+
*
|
|
268
|
+
* **Nothing shifts, and that is not the same as nothing being lost.** No field-indexed slot
|
|
269
|
+
* moves: the units and the result status are read out of the same field numbers under either
|
|
270
|
+
* alignment. What this reports is that **the field is read as more repeats than the competing
|
|
271
|
+
* alignment gives it**, which holds wherever it fires **except on the one class named at the end
|
|
272
|
+
* of this entry**. What that costs depends on **which** boundary was gained, and the two cases
|
|
273
|
+
* are stated separately rather than folded into one claim.
|
|
274
|
+
*
|
|
275
|
+
* **Where it is the FIRST boundary in the field, a modeled slot is lost**, because a field's
|
|
276
|
+
* modeled value and components are taken from its **first repeat alone**. Everything past the
|
|
277
|
+
* boundary stays on the wire and stays in `repeats`, and leaves every modeled slot:
|
|
278
|
+
*
|
|
279
|
+
* - **A value truncates.** Under `H|F^&`, `R|1|^^^687|28.6&S&F&U/L||||F` reads the value field as
|
|
280
|
+
* the two repeats `28.6^` and `&U/L`, so a consumer reads `28.6^` and the rest of the value is
|
|
281
|
+
* gone. The competing alignment reads one repeat carrying all of it.
|
|
282
|
+
* - **A modeled component list is deleted rather than shifted.** The same boundary inside a
|
|
283
|
+
* Universal Test ID leaves the components of the first repeat only: under `H|F^&`,
|
|
284
|
+
* `R|1|&F&F&687|28.6|U/L||||F` reads a UTID of one component holding a decoded field separator,
|
|
285
|
+
* with no coding scheme and no local code, where the bytes carry `687`. A patient name loses
|
|
286
|
+
* its given and middle names the same way. That is the half of this condition the shift report
|
|
287
|
+
* could not cover, because components are modeled **inside** a field.
|
|
288
|
+
*
|
|
289
|
+
* Before this code the only warnings on those streams were **tolerable** ones
|
|
290
|
+
* ({@link WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS} and, where the tail applies,
|
|
291
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}), so the widest gate-legal profile plus
|
|
292
|
+
* `{ strict: true }` accepted a truncated value and an emptied Universal Test ID.
|
|
293
|
+
*
|
|
294
|
+
* **Where it is a LATER boundary, no modeled slot moves and this still fires.** The first repeat
|
|
295
|
+
* is identical under both alignments, so the value, the components and every slot taken from them
|
|
296
|
+
* read the same either way (`R|1|^^^687|5.0F28.6&S&F&U/L||||F` under `H|F^&` reads a value of
|
|
297
|
+
* `5.0` under both), and what differs is the repeat structure after the first. It fires there
|
|
298
|
+
* deliberately: the boundary is still one the bytes do not force and a consumer reading `repeats`
|
|
299
|
+
* is still reading an alignment guess. Relative to the modeled slots that is **over-reporting,
|
|
300
|
+
* never under-reporting**. Narrowing it to the first boundary would change which streams a
|
|
301
|
+
* published package refuses and wants its own measurement, so the bound is written down instead.
|
|
302
|
+
*
|
|
303
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is identical,
|
|
304
|
+
* `repeats` still carries every one of them, and the value read is the value that was always
|
|
305
|
+
* read. Picking the other alignment would be a different guess with no more evidence behind it,
|
|
306
|
+
* on a published package.
|
|
307
|
+
*
|
|
308
|
+
* **Two further deliberate bounds, both stated rather than left to be found.**
|
|
309
|
+
* - **Only the repeat role.** The **component** role reaches a modeled slot too, and differently:
|
|
310
|
+
* there the components stay in the record and move one slot along, so a local code reads as a
|
|
311
|
+
* coding scheme and a given name as a middle name. That is measured and is now reported by
|
|
312
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS}, a third code on the same tail
|
|
313
|
+
* test wired to the component split. It is still not covered **here**, because wiring a sink to
|
|
314
|
+
* another split is another criterion, which is why it took its own population measurement.
|
|
315
|
+
* - **The tail is weighed one construct deep**, exactly as for the shift report, **and exactly
|
|
316
|
+
* one tail is excluded.** Where the escape character the reading resumes on heads a sequence
|
|
317
|
+
* this codec *recognizes*, it interprets a construct and the repeats are the ones the sender
|
|
318
|
+
* wrote: under a set naming the repeat separator `F`, `28.6&F&F&F&U/L` is that separator
|
|
319
|
+
* escaped, written, and escaped again, entirely well formed, and refusing it would be an
|
|
320
|
+
* over-refusal. That is the only tail on which a stream's escaping can be clean, which is why it
|
|
321
|
+
* is the only exclusion. Where the tail heads a sequence whose body is *unrecognized*, the
|
|
322
|
+
* reading taken consumes a triple it cannot read, preserved verbatim and never guessed at, the
|
|
323
|
+
* truncation is the same truncation, and **this reports it**.
|
|
324
|
+
* - **That remaining silence is a TRADE, not a claim that nothing was lost.** On the excluded tail
|
|
325
|
+
* the gained boundary is exactly as real and it is `warnings: []`: under the canonical set
|
|
326
|
+
* `R|1|^^^687|28.6&S&\&S&U/L|U/L||||F` reads a value of `28.6^`, and `^U/L` leaves every
|
|
327
|
+
* modeled slot, with an empty warning list. Reporting it would refuse a stream whose escaping
|
|
328
|
+
* is working.
|
|
329
|
+
*
|
|
330
|
+
* **⚠️ THE ONE CLASS WHERE THE FIELD IS NOT READ AS MORE REPEATS, NAMED RATHER THAN LEFT TO BE
|
|
331
|
+
* FOUND.** Where the sequence past the boundary carries the **repeat separator itself** as its
|
|
332
|
+
* body, the reading taken holds that character inside an opaque atom while the competing
|
|
333
|
+
* alignment splits on it, so the two readings return the **same number** of repeats in
|
|
334
|
+
* **different places** and what differs is their contents: under the canonical set
|
|
335
|
+
* `28.6&F&\&\&U/L` reads two repeats under both. This still fires there, which is over-reporting
|
|
336
|
+
* relative to the repeat count and never under-reporting, and the class costs no stream its
|
|
337
|
+
* disposition: that tail body is a splitting delimiter in force, so
|
|
338
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE} has already refused the record.
|
|
339
|
+
* What holds on **every** firing tuple is that the two readings disagree and that both consume
|
|
340
|
+
* every byte, so neither is forced.
|
|
341
|
+
*
|
|
342
|
+
* **Catch it on the first read.** Emit rewrites the preserved sequences into recognized
|
|
343
|
+
* mnemonics, and those bytes carry the reading that was taken unambiguously, so a
|
|
344
|
+
* second-generation read is silent and is correct about its own bytes. A clean re-read is not
|
|
345
|
+
* evidence.
|
|
346
|
+
*/
|
|
347
|
+
readonly ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD: "ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD";
|
|
348
|
+
/**
|
|
349
|
+
* Two escape alignments of the same bytes disagreed about a **component** boundary, the reading
|
|
350
|
+
* taken kept it, and the escape character that reading resumes on **heads no sequence this codec
|
|
351
|
+
* can interpret**. Same contested position and same tail test as
|
|
352
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS} and
|
|
353
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD}, on the third and last splitting
|
|
354
|
+
* role, and what it costs is a third thing again, which is why it is a third code.
|
|
355
|
+
*
|
|
356
|
+
* **The components are neither shifted out of the record nor dropped from it: they MOVE one slot
|
|
357
|
+
* along.** A field's components are modeled *inside* it, so a gained component boundary shifts
|
|
358
|
+
* every component after it exactly as a gained field boundary shifts every later field, **except
|
|
359
|
+
* on the one class named at the end of this entry**, where the two readings instead read the same
|
|
360
|
+
* number of components in different places. Nothing leaves the record and nothing changes field
|
|
361
|
+
* number; what changes is which modeled slot each component lands in, and those slots are named
|
|
362
|
+
* things:
|
|
363
|
+
*
|
|
364
|
+
* - **A Universal Test ID's coding scheme and local code.** Under the canonical set,
|
|
365
|
+
* `R|1|&F&^&GLU^L^687|28.6|U/L||||F` reads four components, so `L` is the coding scheme and
|
|
366
|
+
* `687` the vendor's local code. The competing alignment reads three, so `L` is the test name
|
|
367
|
+
* and **`687` is the coding scheme**. A local code and a code-system selector are not the same
|
|
368
|
+
* thing, and a consumer routing on one of them routes on the alignment.
|
|
369
|
+
* - **A patient's given and middle names.** `P|1||MRN-0001||DOE&F&^&JANE^A||19700101|F` reads a
|
|
370
|
+
* given name of `&JANE` and a middle name of `A` under the reading taken, and under the
|
|
371
|
+
* competing one `A` is the **given** name with no middle name at all.
|
|
372
|
+
*
|
|
373
|
+
* Before this code the only warning on those streams was the **tolerable**
|
|
374
|
+
* {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}, so the widest gate-legal profile plus
|
|
375
|
+
* `{ strict: true }` accepted a test identity and a patient name whose parts were decided by the
|
|
376
|
+
* alignment rather than by the sender.
|
|
377
|
+
*
|
|
378
|
+
* **Every gained boundary at or before the LAST MODELED COMPONENT INDEX moves a slot, not only
|
|
379
|
+
* the first, and that is the difference from the repeat role.** There the field is modeled out of
|
|
380
|
+
* its first repeat alone, so only the first gained boundary reaches a modeled slot; here the shift
|
|
381
|
+
* propagates from wherever the boundary sits to the end of the component list.
|
|
382
|
+
*
|
|
383
|
+
* **TWO bounds run the other way, and this fires inside both**, which is **over-reporting, never
|
|
384
|
+
* under-reporting**: the direction this package errs in. Both axes are swept on their own rather
|
|
385
|
+
* than left to the shared corpus, which holds them fixed, and neither is closed by narrowing the
|
|
386
|
+
* guard, because narrowing changes which streams a published package refuses and wants its own
|
|
387
|
+
* measurement.
|
|
388
|
+
*
|
|
389
|
+
* - **Past the last modeled component index, nothing NAMED moves.** A model reads a fixed number
|
|
390
|
+
* of components: a patient name three (last, first, middle), a Universal Test ID four. A
|
|
391
|
+
* contested boundary further right than that still shifts the components after it while every
|
|
392
|
+
* named slot reads byte-identically under both alignments.
|
|
393
|
+
* - **Inside a LATER repeat nothing modeled moves at all**, because `components` is the first
|
|
394
|
+
* repeat, so what changes is `repeats[n]` for that `n` alone.
|
|
395
|
+
*
|
|
396
|
+
* It fires in both because the boundary is still one the bytes do not force, and a consumer
|
|
397
|
+
* reading `components` or `repeats` is still reading an alignment guess.
|
|
398
|
+
*
|
|
399
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is identical, and
|
|
400
|
+
* the components read are the components that were always read. Picking the other alignment would
|
|
401
|
+
* be a different guess with no more evidence behind it, on a published package. **Withholding the
|
|
402
|
+
* moved slots is a separate question and is deliberately not answered here**, for the reason the
|
|
403
|
+
* shift report already gives: declining to model a slot changes an extracted value for every
|
|
404
|
+
* consumer of a package already on the registry.
|
|
405
|
+
*
|
|
406
|
+
* **The tail is weighed one construct deep**, exactly as for the other two, **and exactly one tail
|
|
407
|
+
* is excluded.** Where the escape character the reading resumes on heads a sequence this codec
|
|
408
|
+
* *recognizes*, it interprets a construct and the components are the ones the sender wrote: under
|
|
409
|
+
* a set naming the component separator `F`, `GLU&F&F&F&L` is that separator escaped, written, and
|
|
410
|
+
* escaped again, entirely well formed, and refusing it would be an over-refusal. That is the only
|
|
411
|
+
* tail on which a stream's escaping can be clean, which is why it is the only exclusion. Where the
|
|
412
|
+
* tail heads a sequence whose body is *unrecognized*, the reading taken consumes a triple it
|
|
413
|
+
* cannot read, preserved verbatim and never guessed at, the slots move exactly as far, and **this
|
|
414
|
+
* reports it**.
|
|
415
|
+
*
|
|
416
|
+
* **That remaining silence is a TRADE, not a claim that nothing was lost.** On the excluded tail
|
|
417
|
+
* the gained boundary is exactly as real and it is `warnings: []`: under the canonical set
|
|
418
|
+
* `&F&^&F&GLU^L^687` reads one component more than the competing alignment, so `L` is the coding
|
|
419
|
+
* scheme and `687` the local code under one reading and `687` the **coding scheme** under the
|
|
420
|
+
* other, with an empty warning list. Reporting it would refuse a stream whose escaping is
|
|
421
|
+
* working.
|
|
422
|
+
*
|
|
423
|
+
* **⚠️ THE ONE CLASS WHERE NO COMPONENT INDEX MOVES, NAMED RATHER THAN LEFT TO BE FOUND.** It is a
|
|
424
|
+
* **third** bound running the other way, and unlike the two above it is about the bytes past the
|
|
425
|
+
* boundary rather than about where the boundary sits. Where the sequence past it carries the
|
|
426
|
+
* **component separator itself** as its body, the reading taken holds that character inside an
|
|
427
|
+
* opaque atom while the competing alignment splits on it, so the two readings return the **same
|
|
428
|
+
* number** of components in **different places** and what differs is their contents: under the
|
|
429
|
+
* canonical set `P|1||MRN-0001||DOE&F&^&^&JANE^A||19700101|F` reads three components under both,
|
|
430
|
+
* with `A` the middle name under both. This still fires there, and the class costs no stream its
|
|
431
|
+
* disposition: that tail body is a splitting delimiter in force, so
|
|
432
|
+
* {@link WARNING_CODES.ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE} has already refused the record.
|
|
433
|
+
* What holds on **every** firing tuple is that the two readings disagree and that both consume
|
|
434
|
+
* every byte, so neither is forced.
|
|
435
|
+
*
|
|
436
|
+
* **Catch it on the first read.** Emit rewrites the preserved sequences into recognized
|
|
437
|
+
* mnemonics, and those bytes carry the reading that was taken unambiguously, so a
|
|
438
|
+
* second-generation read is silent and is correct about its own bytes. A clean re-read is not
|
|
439
|
+
* evidence.
|
|
440
|
+
*/
|
|
441
|
+
readonly ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS: "ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS";
|
|
241
442
|
/** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&`: preserved verbatim. */
|
|
242
443
|
readonly ASTM_UNKNOWN_ESCAPE_SEQUENCE: "ASTM_UNKNOWN_ESCAPE_SEQUENCE";
|
|
243
444
|
/**
|
|
@@ -493,15 +694,21 @@ declare function ambiguousEscapeAlignment(position: AstmPosition): AstmRecordWar
|
|
|
493
694
|
/**
|
|
494
695
|
* Build an `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` warning. Emitted when a competing escape
|
|
495
696
|
* alignment decided a **field** boundary and the escape character the reading taken resumes on
|
|
496
|
-
* heads no sequence
|
|
697
|
+
* heads no sequence this codec can interpret, so every field after that point sits one place further
|
|
698
|
+
* right than
|
|
497
699
|
* the competing alignment puts it. On a result record that reaches the units and the **result
|
|
498
700
|
* 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
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
* {@link WARNING_CODES.
|
|
701
|
+
* all under the competing one. **Except where the sequence past the boundary carries the field
|
|
702
|
+
* separator itself**, where the two readings read the same number of fields in different places and
|
|
703
|
+
* no field index moves; see {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS}.
|
|
704
|
+
*
|
|
705
|
+
* A profile may **not** tolerate this code. **Which tolerable escape report accompanies it depends
|
|
706
|
+
* on the tail**: {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} where the tail heads no
|
|
707
|
+
* sequence at all, and {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE} where it heads one whose
|
|
708
|
+
* body is unrecognized. Both report strictly weaker facts, and wherever the escape role is a
|
|
709
|
+
* character distinct from the three splitting roles one of them always fires beside this code. It
|
|
710
|
+
* fires alongside {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} where that also
|
|
711
|
+
* applies. The reading
|
|
505
712
|
* is unchanged: this reports the shift, it does not repair it.
|
|
506
713
|
*
|
|
507
714
|
* @example
|
|
@@ -511,6 +718,70 @@ declare function ambiguousEscapeAlignment(position: AstmPosition): AstmRecordWar
|
|
|
511
718
|
* ```
|
|
512
719
|
*/
|
|
513
720
|
declare function alignmentShiftedFields(position: AstmPosition): AstmRecordWarning;
|
|
721
|
+
/**
|
|
722
|
+
* Build an `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` warning. Emitted when a competing escape
|
|
723
|
+
* alignment decided a **repeat** boundary and the escape character the reading taken resumes on
|
|
724
|
+
* heads no sequence this codec can interpret, so the field is read as more repeats than the
|
|
725
|
+
* competing alignment
|
|
726
|
+
* gives it. No field-indexed slot moves, and that is the difference from
|
|
727
|
+
* {@link alignmentShiftedFields}. Where the gained boundary is the **first** in the field it still
|
|
728
|
+
* reaches a modeled slot, because a field's modeled value and components are taken from its first
|
|
729
|
+
* repeat alone: everything past the boundary stays in `repeats` and leaves every modeled slot, so a
|
|
730
|
+
* result value truncates and a Universal Test ID or a patient name loses the components that sat
|
|
731
|
+
* after it. At a **later** boundary nothing modeled moves and this still fires, which is
|
|
732
|
+
* over-reporting relative to those slots and never under-reporting, and **where the sequence past
|
|
733
|
+
* the boundary carries the repeat separator itself** the two readings read the same number of
|
|
734
|
+
* repeats in different places, so the field is not read as more repeats at all; see
|
|
735
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD}.
|
|
736
|
+
*
|
|
737
|
+
* A profile may **not** tolerate this code. **Which tolerable escape report accompanies it depends
|
|
738
|
+
* on the tail**: {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} where the tail heads no
|
|
739
|
+
* sequence at all, and {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE} where it heads one whose
|
|
740
|
+
* body is unrecognized. Both report strictly weaker facts, and wherever the escape role is a
|
|
741
|
+
* character distinct from the three splitting roles one of them always fires beside this code. It
|
|
742
|
+
* fires alongside {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} where that also
|
|
743
|
+
* applies. The reading
|
|
744
|
+
* is unchanged: this reports the gained boundary, it does not repair it.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* import { alignmentTruncatedField } from "@cosyte/astm";
|
|
749
|
+
* alignmentTruncatedField({ recordIndex: 2, recordType: "R", fieldIndex: 4 });
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
declare function alignmentTruncatedField(position: AstmPosition): AstmRecordWarning;
|
|
753
|
+
/**
|
|
754
|
+
* Build an `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` warning. Emitted when a competing escape
|
|
755
|
+
* alignment decided a **component** boundary and the escape character the reading taken resumes on
|
|
756
|
+
* heads no sequence this codec can interpret, so every component after that point sits one place
|
|
757
|
+
* further right
|
|
758
|
+
* than the competing alignment puts it. No field number changes and nothing leaves the record, and
|
|
759
|
+
* that is the difference from {@link alignmentShiftedFields} and {@link alignmentTruncatedField}:
|
|
760
|
+
* what moves is which modeled slot each component lands in, so a Universal Test ID's coding scheme
|
|
761
|
+
* and local code, or a patient's given and middle names, are read out of positions the competing
|
|
762
|
+
* alignment does not put them in. **Every gained boundary at or before the last modeled component
|
|
763
|
+
* index moves those slots, not only the first**; past that index, and inside a **later repeat**,
|
|
764
|
+
* nothing named moves and this still fires, which is over-reporting and never under-reporting, and
|
|
765
|
+
* **where the sequence past the boundary carries the component separator itself** the two readings
|
|
766
|
+
* read the same number of components in different places, so no component index moves at all. See
|
|
767
|
+
* {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS}.
|
|
768
|
+
*
|
|
769
|
+
* A profile may **not** tolerate this code. **Which tolerable escape report accompanies it depends
|
|
770
|
+
* on the tail**: {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} where the tail heads no
|
|
771
|
+
* sequence at all, and {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE} where it heads one whose
|
|
772
|
+
* body is unrecognized. Both report strictly weaker facts, and wherever the escape role is a
|
|
773
|
+
* character distinct from the three splitting roles one of them always fires beside this code. It
|
|
774
|
+
* fires alongside {@link WARNING_CODES.ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT} where that also
|
|
775
|
+
* applies. The reading
|
|
776
|
+
* is unchanged: this reports the moved components, it does not repair them.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```ts
|
|
780
|
+
* import { alignmentShiftedComponents } from "@cosyte/astm";
|
|
781
|
+
* alignmentShiftedComponents({ recordIndex: 2, recordType: "R", fieldIndex: 3 });
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
declare function alignmentShiftedComponents(position: AstmPosition): AstmRecordWarning;
|
|
514
785
|
/**
|
|
515
786
|
* Build an `ASTM_RECORD_DELIMITERS_REDECLARED` warning. Emitted when a later `H`
|
|
516
787
|
* record declares a delimiter set different from the one in force; the new set is
|
|
@@ -3242,11 +3513,20 @@ declare function classifyMessage(records: readonly AstmRecord[]): AstmMessageCla
|
|
|
3242
3513
|
* readings the bytes carry.
|
|
3243
3514
|
* @param onAlignmentShiftedFields - Called (with the 0-based field index) for each
|
|
3244
3515
|
* contested **field** boundary the reading took while resuming on an escape
|
|
3245
|
-
* character that heads no sequence
|
|
3246
|
-
* right than the competing alignment puts it. Wired only to the field split: a
|
|
3516
|
+
* character that heads no sequence this codec can interpret. Wired only to the field split: a
|
|
3247
3517
|
* repeat or component boundary divides one field and so moves no field-indexed
|
|
3248
3518
|
* slot. That is a choice and not a consequence, because components are modeled
|
|
3249
|
-
* inside a field
|
|
3519
|
+
* inside a field. **What the boundary costs, and the one class in which nothing
|
|
3520
|
+
* moves index, are stated in {@link ShiftedFieldsSink} and not restated here.**
|
|
3521
|
+
* @param onAlignmentTruncatedField - Called (with the 0-based field index) for that
|
|
3522
|
+
* same condition on the **repeat** split, where no field-indexed slot moves.
|
|
3523
|
+
* **What the boundary costs, its two index bounds and the one class in which nothing
|
|
3524
|
+
* moves index are stated in {@link TruncatedFieldSink} and not restated here.**
|
|
3525
|
+
* @param onAlignmentShiftedComponents - Called (with the 0-based field index) for that same
|
|
3526
|
+
* condition on the **component** split, where no field-indexed slot moves and nothing leaves the
|
|
3527
|
+
* record. **What the boundary costs, its three index bounds and the one class in which nothing
|
|
3528
|
+
* moves index are stated in {@link ShiftedComponentsSink} and not restated here.** All three
|
|
3529
|
+
* splitting roles are wired now.
|
|
3250
3530
|
* @returns The record's fields.
|
|
3251
3531
|
* @example
|
|
3252
3532
|
* ```ts
|
|
@@ -3255,7 +3535,7 @@ declare function classifyMessage(records: readonly AstmRecord[]): AstmMessageCla
|
|
|
3255
3535
|
* fields[3].components[0]; // "28.6"
|
|
3256
3536
|
* ```
|
|
3257
3537
|
*/
|
|
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[];
|
|
3538
|
+
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, onAlignmentTruncatedField?: (fieldIndex: number) => void, onAlignmentShiftedComponents?: (fieldIndex: number) => void): AstmField[];
|
|
3259
3539
|
/**
|
|
3260
3540
|
* Tokenize an `H` (header) record into its fields.
|
|
3261
3541
|
*
|
|
@@ -3288,9 +3568,15 @@ declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?:
|
|
|
3288
3568
|
* each competing escape alignment in the data portion. The declaration is opaque,
|
|
3289
3569
|
* so the characters it names literally never report here either.
|
|
3290
3570
|
* @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
|
-
*
|
|
3293
|
-
* reports here either.
|
|
3571
|
+
* for each contested field boundary in the data portion whose reading resumes on an
|
|
3572
|
+
* escape character heading no sequence this codec can interpret. The declaration is
|
|
3573
|
+
* opaque, so it never reports here either.
|
|
3574
|
+
* @param onAlignmentTruncatedField - Called with the 0-based whole-record field index
|
|
3575
|
+
* for each contested **repeat** boundary in the data portion on that same tail
|
|
3576
|
+
* test. The declaration is opaque, so it never reports here either.
|
|
3577
|
+
* @param onAlignmentShiftedComponents - Called with the 0-based whole-record field index for each
|
|
3578
|
+
* contested **component** boundary in the data portion on that same tail test. The declaration is
|
|
3579
|
+
* opaque, so it never reports here either.
|
|
3294
3580
|
* @returns The header's fields.
|
|
3295
3581
|
* @example
|
|
3296
3582
|
* ```ts
|
|
@@ -3300,7 +3586,7 @@ declare function tokenizeRecord(record: string, d: Delimiters, onUnknownEscape?:
|
|
|
3300
3586
|
* fields[4].raw; // "sender"
|
|
3301
3587
|
* ```
|
|
3302
3588
|
*/
|
|
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[];
|
|
3589
|
+
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, onAlignmentTruncatedField?: (fieldIndex: number) => void, onAlignmentShiftedComponents?: (fieldIndex: number) => void): AstmField[];
|
|
3304
3590
|
/**
|
|
3305
3591
|
* The primary scalar of a field: its first repeat's first component, decoded.
|
|
3306
3592
|
* Returns `undefined` for a truly empty field so callers can distinguish
|
|
@@ -3475,17 +3761,39 @@ declare class AstmParseError extends Error {
|
|
|
3475
3761
|
* asks whether this codec's vocabulary prefers the reading taken *at* the contested
|
|
3476
3762
|
* position. It says nothing about what the reading taken makes of the bytes *after*
|
|
3477
3763
|
* the boundary, and that is where the two alignments really diverge: they resume
|
|
3478
|
-
* one character apart, so they disagree about the
|
|
3479
|
-
*
|
|
3480
|
-
*
|
|
3764
|
+
* one character apart, so they disagree about the bytes past the boundary, and
|
|
3765
|
+
* on most tails about the whole of them. Where the escape
|
|
3766
|
+
* character the reading taken resumes on heads no sequence this codec can
|
|
3767
|
+
* **interpret** (it heads none at all, or it heads one whose body is not a recognized
|
|
3768
|
+
* mnemonic and is therefore preserved verbatim rather than read), the boundary was
|
|
3769
|
+
* bought with bytes this reading cannot read, while the competing alignment is
|
|
3481
3770
|
* precisely the reading that can. On the **field** separator that gained boundary
|
|
3482
3771
|
* shifts every later field one place, so a result record's units and **result
|
|
3483
3772
|
* status** are read out of slots the competing alignment does not put them in, and
|
|
3484
3773
|
* a status of `final` can be a consequence of the alignment rather than something
|
|
3485
3774
|
* the sender wrote there. That is `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`; see
|
|
3486
|
-
* {@link ShiftedFieldsSink}, including the tail
|
|
3487
|
-
*
|
|
3488
|
-
* lost there.
|
|
3775
|
+
* {@link ShiftedFieldsSink}, including the one tail it deliberately stays silent on,
|
|
3776
|
+
* why that silence is what keeps this off conformant traffic, and why it is still not
|
|
3777
|
+
* a claim that nothing was lost there.
|
|
3778
|
+
*
|
|
3779
|
+
* **The same question asked on the repeat separator has a different answer, so it
|
|
3780
|
+
* has a code of its own.** A gained repeat boundary moves no field-indexed slot, and
|
|
3781
|
+
* where it is the **first** boundary in the field it still reaches a modeled slot,
|
|
3782
|
+
* because a field's modeled reading is taken from its first repeat alone: everything
|
|
3783
|
+
* past that boundary leaves the record, so a result value truncates and a Universal
|
|
3784
|
+
* Test ID or a patient name loses the components that sat after it. That is
|
|
3785
|
+
* `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD`; see {@link TruncatedFieldSink}, including
|
|
3786
|
+
* what it does at a later boundary, where it fires and no modeled slot moves.
|
|
3787
|
+
*
|
|
3788
|
+
* **The same question asked on the component separator has a third answer again, and
|
|
3789
|
+
* so a third code.** There the components neither leave the record nor change field
|
|
3790
|
+
* number: they move one slot along, because components are modeled *inside* a field.
|
|
3791
|
+
* A Universal Test ID's coding scheme and local code, and a patient's given and middle
|
|
3792
|
+
* names, are then read out of positions the competing alignment does not put them in.
|
|
3793
|
+
* That is `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`; see {@link ShiftedComponentsSink},
|
|
3794
|
+
* including what it does inside a later repeat, where it fires and no modeled slot
|
|
3795
|
+
* moves. All three roles are wired now; there is no fourth, because nothing splits on
|
|
3796
|
+
* the escape role.
|
|
3489
3797
|
*
|
|
3490
3798
|
* Re-escaping (the inverse, for spec-clean emit) lives in the serializer and is
|
|
3491
3799
|
* deliberately not implemented here.
|
|
@@ -3570,11 +3878,18 @@ type SwallowedDelimiterSink = () => void;
|
|
|
3570
3878
|
type AmbiguousAlignmentSink = (segmentIndex: number) => void;
|
|
3571
3879
|
/**
|
|
3572
3880
|
* A callback the split calls when a competing escape alignment decided a boundary
|
|
3573
|
-
* **and the reading taken cannot read
|
|
3574
|
-
* character the leftmost reading resumes on heads no
|
|
3575
|
-
*
|
|
3576
|
-
*
|
|
3577
|
-
*
|
|
3881
|
+
* **and the reading taken cannot read what lies immediately past it**: the escape
|
|
3882
|
+
* character the leftmost reading resumes on heads no sequence this codec can
|
|
3883
|
+
* *interpret*, while the competing alignment is exactly the reading that gives that
|
|
3884
|
+
* character a job, as the close of its own triple. Two tails satisfy that, and they
|
|
3885
|
+
* are reported alike because they cost alike:
|
|
3886
|
+
*
|
|
3887
|
+
* - **It heads no sequence at all**, so it is kept as a bare literal and reported
|
|
3888
|
+
* separately as `ASTM_UNPAIRED_ESCAPE_CHARACTER`.
|
|
3889
|
+
* - **It heads a sequence whose body is not a recognized mnemonic**, so the triple is
|
|
3890
|
+
* preserved verbatim, never guessed at, and reported separately as
|
|
3891
|
+
* `ASTM_UNKNOWN_ESCAPE_SEQUENCE`. **Consuming a triple is not interpreting one**,
|
|
3892
|
+
* and the boundary is bought with bytes this reading cannot read either way.
|
|
3578
3893
|
*
|
|
3579
3894
|
* Wired **only to the split taken on the field separator**, because that is the
|
|
3580
3895
|
* whole of its claim: a gained *field* boundary shifts every later field one place,
|
|
@@ -3587,11 +3902,16 @@ type AmbiguousAlignmentSink = (segmentIndex: number) => void;
|
|
|
3587
3902
|
* reaches nothing outside it, so it moves no **field-indexed** slot and is
|
|
3588
3903
|
* deliberately outside this. **That bound is a choice, not a consequence, and the
|
|
3589
3904
|
* 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
|
|
3591
|
-
* boundary does
|
|
3592
|
-
*
|
|
3593
|
-
*
|
|
3594
|
-
*
|
|
3905
|
+
* ID's coding scheme and local code, a patient name's parts), so a gained repeat or
|
|
3906
|
+
* component boundary does reach a modeled slot. The **repeat** half of that is
|
|
3907
|
+
* reported by {@link TruncatedFieldSink}, on the same tail test and under its own
|
|
3908
|
+
* code, because what it costs is a different thing. The **component** half is
|
|
3909
|
+
* reported by {@link ShiftedComponentsSink}, on that same tail test and under a code of
|
|
3910
|
+
* its own again, because what it costs is a third thing: there the components move one
|
|
3911
|
+
* slot along rather than leaving the record. The
|
|
3912
|
+
* callback lets the parser surface a value-free
|
|
3913
|
+
* `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` warning. Optional so the split can be used
|
|
3914
|
+
* purely.
|
|
3595
3915
|
*
|
|
3596
3916
|
* **It is a report, not a repair.** The split is unchanged, every decoded byte is
|
|
3597
3917
|
* identical, and the status read is the status that was always read. What is new is
|
|
@@ -3606,29 +3926,230 @@ type AmbiguousAlignmentSink = (segmentIndex: number) => void;
|
|
|
3606
3926
|
* questions are different, so neither test is widened to answer the other.
|
|
3607
3927
|
*
|
|
3608
3928
|
* **The tail is weighed one construct deep, and that bound is stated rather than
|
|
3609
|
-
* left to be found.**
|
|
3610
|
-
*
|
|
3611
|
-
*
|
|
3612
|
-
*
|
|
3613
|
-
*
|
|
3614
|
-
*
|
|
3615
|
-
*
|
|
3616
|
-
*
|
|
3617
|
-
*
|
|
3618
|
-
*
|
|
3619
|
-
*
|
|
3620
|
-
*
|
|
3621
|
-
*
|
|
3622
|
-
*
|
|
3623
|
-
*
|
|
3624
|
-
*
|
|
3625
|
-
*
|
|
3929
|
+
* left to be found.** Exactly one tail is excluded: **the escape character heads a
|
|
3930
|
+
* sequence this codec recognizes.** Then the reading taken interprets a construct
|
|
3931
|
+
* and leaves nothing unread, while the competing alignment would leave it bare.
|
|
3932
|
+
* Under a set naming the field separator `F`, `28.6&F&F&F&U/L` is the sender
|
|
3933
|
+
* escaping that separator, writing it, and escaping it again: entirely well formed,
|
|
3934
|
+
* and refusing it is the over-refusal that sank the preceding candidate criterion
|
|
3935
|
+
* for this family.
|
|
3936
|
+
*
|
|
3937
|
+
* **That exclusion is not a matter of degree, which is why it is the only one.** It
|
|
3938
|
+
* is the sole tail on which a stream's escaping can be clean at all: on the other
|
|
3939
|
+
* two the package already reports a deviation, so this report can never be the
|
|
3940
|
+
* reason a well-formed stream is refused. A tail comparison that instead weighs
|
|
3941
|
+
* which alignment the bytes *prefer* would exclude the unrecognized body too (the
|
|
3942
|
+
* competing alignment leaves two escape characters bare there against the reading
|
|
3943
|
+
* taken's one unreadable body) and it is the wrong question: these codes report a
|
|
3944
|
+
* **cost**, not a preference, and the cost is identical under both reported tails.
|
|
3945
|
+
*
|
|
3946
|
+
* **That silence is a TRADE, and it is not a claim that nothing was lost there.** The
|
|
3947
|
+
* gained field boundary is exactly as real on the excluded tail, and it can be
|
|
3948
|
+
* entirely silent: under the canonical set `R|1|^^^687|28.6&F&|&F&U/L||||F` reads
|
|
3949
|
+
* **nine** fields against the competing alignment's eight with `warnings: []`, so a
|
|
3950
|
+
* result status of `final` is read out of a slot the other reading does not have.
|
|
3951
|
+
* Reporting it would refuse a stream whose escaping is working, which is the trade
|
|
3952
|
+
* this bound makes. **Read the raw line when an escape character sits next to a
|
|
3953
|
+
* delimiter, whether or not anything fired.**
|
|
3954
|
+
*
|
|
3955
|
+
* **⚠️ THE SHIFT IS NOT UNIVERSAL OVER THE FIRING POPULATION, AND THE EXCEPTION IS
|
|
3956
|
+
* NAMED RATHER THAN LEFT TO BE FOUND.** Where the sequence past the boundary carries
|
|
3957
|
+
* the **field separator itself** as its body, the reading taken holds that character
|
|
3958
|
+
* inside an opaque atom while the competing alignment splits on it, so the two
|
|
3959
|
+
* readings return the **same number** of fields in **different places**: no field
|
|
3960
|
+
* index moves, and what differs is the **contents** of the fields at and after the
|
|
3961
|
+
* boundary. Under the canonical set `R|1|^^^687|28.6&F&|&|&U/L||||F` reads nine
|
|
3962
|
+
* fields under both, with the status `F` in field 9 under both, and units of `&|&U/L`
|
|
3963
|
+
* against `&U/L`. That class is bounded and costs no stream its disposition: the tail
|
|
3964
|
+
* body is then a splitting delimiter in force, so
|
|
3965
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` has already refused the record. What
|
|
3966
|
+
* holds on **every** firing tuple is that the two readings disagree and that both
|
|
3967
|
+
* consume every byte, so neither is forced.
|
|
3626
3968
|
*
|
|
3627
3969
|
* @param segmentIndex - The 0-based index of the field being accumulated when the
|
|
3628
|
-
*
|
|
3629
|
-
* than the competing alignment puts it
|
|
3970
|
+
* contested boundary was taken. Every field after it is one place further right
|
|
3971
|
+
* than the competing alignment puts it, **except** where the sequence past the
|
|
3972
|
+
* boundary carries the field separator as its body, where the two readings return
|
|
3973
|
+
* the same number of fields with different contents instead.
|
|
3630
3974
|
*/
|
|
3631
3975
|
type ShiftedFieldsSink = (segmentIndex: number) => void;
|
|
3976
|
+
/**
|
|
3977
|
+
* A callback the split calls on **exactly the condition {@link ShiftedFieldsSink}
|
|
3978
|
+
* reports**, wired instead to the split taken on the **repeat** separator. The two
|
|
3979
|
+
* sinks test the same predicate, because the predicate is about the bytes and the
|
|
3980
|
+
* split does not know which role it is being taken on; they are separate so the
|
|
3981
|
+
* caller that does know can name what the gained boundary costs, which is not the
|
|
3982
|
+
* same thing in the two roles. Do not "simplify" them into one: the codes would then
|
|
3983
|
+
* have to make one claim covering both, and the claim is different.
|
|
3984
|
+
*
|
|
3985
|
+
* **What a gained repeat boundary costs, and it is not a shift.** No field-indexed
|
|
3986
|
+
* slot moves: the units slot and the result-status slot are read out of the same
|
|
3987
|
+
* field numbers under either alignment. What the report says is that **the field is
|
|
3988
|
+
* read as more repeats than the competing alignment gives it**, and that holds
|
|
3989
|
+
* wherever it fires **except on the named class below**, where the two readings read
|
|
3990
|
+
* the same number of repeats in different places. What it costs depends on **which**
|
|
3991
|
+
* boundary was gained, and the two cases are stated separately rather than folded
|
|
3992
|
+
* into one claim:
|
|
3993
|
+
*
|
|
3994
|
+
* - **The gained boundary is the FIRST in the field, and then a modeled slot is
|
|
3995
|
+
* lost.** A field's modeled value and its components are taken from its **first
|
|
3996
|
+
* repeat alone**, so everything past the boundary is still on the wire, still in
|
|
3997
|
+
* `repeats`, and gone from every modeled slot. Under a set naming the repeat
|
|
3998
|
+
* separator `F`, the value `28.6&S&F&U/L` reads as the two repeats `28.6^` and
|
|
3999
|
+
* `&U/L`, and every value extractor answers `28.6^`, while the competing alignment
|
|
4000
|
+
* reads one repeat carrying all of it. The same boundary inside a Universal Test ID
|
|
4001
|
+
* field leaves the components of the first repeat only, so a UTID whose bytes carry
|
|
4002
|
+
* a test code can come back as a single component holding a decoded delimiter, with
|
|
4003
|
+
* the coding scheme and local code read from nothing at all. A patient name loses
|
|
4004
|
+
* its given and middle names the same way.
|
|
4005
|
+
* - **The gained boundary is a LATER one, and then no modeled slot moves.** The first
|
|
4006
|
+
* repeat is identical under both alignments, so the value, the components and every
|
|
4007
|
+
* slot taken from them read the same either way; what differs is the repeat
|
|
4008
|
+
* structure after the first. **This still fires there, deliberately and measurably**
|
|
4009
|
+
* (`5.0F28.6&S&F&U/L` under that same set), because the boundary is still one the
|
|
4010
|
+
* bytes do not force and a consumer reading `repeats` is still reading an alignment
|
|
4011
|
+
* guess. Relative to the modeled slots that is **over-reporting, never
|
|
4012
|
+
* under-reporting**, which is the direction this package errs in. Narrowing the sink
|
|
4013
|
+
* to the first boundary would change which streams a published package refuses and
|
|
4014
|
+
* wants its own measurement, so the bound is written down instead of guessed at.
|
|
4015
|
+
*
|
|
4016
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is
|
|
4017
|
+
* identical, `repeats` still carries every one of them, and the value read is the
|
|
4018
|
+
* value that was always read. What is new is that the gained boundary is reported by
|
|
4019
|
+
* a code no profile may tolerate, where before it was covered only by tolerable ones.
|
|
4020
|
+
*
|
|
4021
|
+
* **The tail is weighed one construct deep, exactly as in {@link ShiftedFieldsSink},
|
|
4022
|
+
* and one tail is excluded for the same reason.** Where the escape character the
|
|
4023
|
+
* reading taken resumes on heads a sequence this codec **recognizes**, it interprets
|
|
4024
|
+
* a construct and the two repeats are the ones the sender wrote: under a set naming
|
|
4025
|
+
* the repeat separator `F`, `28.6&F&F&F&U/L` is that separator escaped, written, and
|
|
4026
|
+
* escaped again, with nothing reported at all. Refusing that is the over-refusal that
|
|
4027
|
+
* sank an earlier candidate criterion for this family, and that tail is the only one
|
|
4028
|
+
* on which a stream's escaping can be clean. Where the tail heads a sequence whose
|
|
4029
|
+
* body is *unrecognized*, the reading taken consumes a triple it cannot read, the
|
|
4030
|
+
* truncation is the same truncation, and this reports it.
|
|
4031
|
+
*
|
|
4032
|
+
* **That silence is a TRADE, and it is not a claim that nothing was lost there.** The
|
|
4033
|
+
* gained repeat boundary is exactly as real on the excluded tail and can be entirely
|
|
4034
|
+
* silent: under the canonical set `28.6&S&\&S&U/L` reads a value of `28.6^` with
|
|
4035
|
+
* `warnings: []`, so `^U/L` leaves every modeled slot at a boundary the bytes do not
|
|
4036
|
+
* force. Reporting it would refuse a stream whose escaping is working.
|
|
4037
|
+
*
|
|
4038
|
+
* **⚠️ THE TRUNCATION IS NOT UNIVERSAL OVER THE FIRING POPULATION, AND THE EXCEPTION
|
|
4039
|
+
* IS NAMED RATHER THAN LEFT TO BE FOUND.** Where the sequence past the boundary
|
|
4040
|
+
* carries the **repeat separator itself** as its body, the reading taken holds that
|
|
4041
|
+
* character inside an opaque atom while the competing alignment splits on it, so the
|
|
4042
|
+
* two readings return the **same number** of repeats in **different places**: the
|
|
4043
|
+
* field is not read as more repeats than the competing alignment gives it, and what
|
|
4044
|
+
* differs is the **contents**. Under the canonical set `28.6&F&\&\&U/L` reads two
|
|
4045
|
+
* repeats under both. That class is bounded and costs no stream its disposition: the
|
|
4046
|
+
* tail body is then a splitting delimiter in force, so
|
|
4047
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` has already refused the record. What
|
|
4048
|
+
* holds on **every** firing tuple is that the two readings disagree and that both
|
|
4049
|
+
* consume every byte, so neither is forced.
|
|
4050
|
+
*
|
|
4051
|
+
* @param segmentIndex - The 0-based index of the repeat being accumulated when the
|
|
4052
|
+
* contested boundary was taken, so `0` is the boundary that ends the first repeat
|
|
4053
|
+
* and is the one that reaches a modeled slot. It is **not** a field index: the
|
|
4054
|
+
* caller splitting one field into repeats already knows which field it is in, and
|
|
4055
|
+
* reports that.
|
|
4056
|
+
*/
|
|
4057
|
+
type TruncatedFieldSink = (segmentIndex: number) => void;
|
|
4058
|
+
/**
|
|
4059
|
+
* A callback the split calls on **exactly the condition {@link ShiftedFieldsSink}
|
|
4060
|
+
* reports**, wired instead to the split taken on the **component** separator. The
|
|
4061
|
+
* third and last of the three sinks that share that one predicate, for the reason the
|
|
4062
|
+
* other two share it: the predicate is about the bytes and the split does not know
|
|
4063
|
+
* which role it is being taken on, so the caller that does know names what the gained
|
|
4064
|
+
* boundary costs. Do not "simplify" the three into one: the codes would then have to
|
|
4065
|
+
* make one claim covering all of them, and the three claims are different.
|
|
4066
|
+
*
|
|
4067
|
+
* **What a gained component boundary costs, and it is neither of the other two.** No
|
|
4068
|
+
* field number changes, so nothing shifts between field-indexed slots, and nothing
|
|
4069
|
+
* leaves the record, so nothing truncates. Components are modeled **inside** a field,
|
|
4070
|
+
* so what happens is that every component after the gained boundary is one place
|
|
4071
|
+
* further right than the competing alignment puts it, and the slots that indexes into
|
|
4072
|
+
* are named things: a Universal Test ID's LOINC-candidate slot, test name, **coding
|
|
4073
|
+
* scheme** and **local code**; a patient name's last, first and middle. Under the
|
|
4074
|
+
* canonical set `&F&^&GLU^L^687` reads four components, so `L` is the coding scheme
|
|
4075
|
+
* and `687` the vendor's local code, while the competing alignment reads three and
|
|
4076
|
+
* `687` is the **coding scheme**. `DOE&F&^&JANE^A` reads a given name of `&JANE` and a
|
|
4077
|
+
* middle name of `A`, while the competing alignment makes `A` the **given** name with
|
|
4078
|
+
* no middle name at all.
|
|
4079
|
+
*
|
|
4080
|
+
* **Every gained boundary at or before the LAST MODELED COMPONENT INDEX moves a
|
|
4081
|
+
* modeled slot, not only the first.** That is the structural difference from
|
|
4082
|
+
* {@link TruncatedFieldSink}: a field is modeled out of its first repeat alone, so
|
|
4083
|
+
* there only the first gained boundary reaches a modeled slot, while here the shift
|
|
4084
|
+
* propagates from wherever the boundary sits to the end of the component list.
|
|
4085
|
+
*
|
|
4086
|
+
* **TWO bounds run the other way, and the report fires inside both, which is
|
|
4087
|
+
* over-reporting and never under-reporting.** Neither is an oversight and neither is
|
|
4088
|
+
* closed by narrowing the guard, because narrowing changes which streams a published
|
|
4089
|
+
* package refuses and wants its own measurement. Both axes are swept on their own
|
|
4090
|
+
* rather than left to the shared corpus, which holds them fixed.
|
|
4091
|
+
*
|
|
4092
|
+
* - **Past the last modeled component index, nothing NAMED moves.** A model reads a
|
|
4093
|
+
* fixed number of components: a patient name three (last, first, middle), a
|
|
4094
|
+
* Universal Test ID four. A contested boundary further right than that still shifts
|
|
4095
|
+
* the components after it and leaves every named slot byte-identical under both
|
|
4096
|
+
* alignments, and this fires there.
|
|
4097
|
+
* - **Inside a LATER repeat nothing modeled moves at all.** `components` is
|
|
4098
|
+
* `repeats[0]`, so a component boundary gained inside the second or a later repeat
|
|
4099
|
+
* changes only `repeats[n]` for that `n`.
|
|
4100
|
+
*
|
|
4101
|
+
* It fires in both because the boundary is still one the bytes do not force, and a
|
|
4102
|
+
* consumer reading `components` or `repeats` is still reading an alignment guess.
|
|
4103
|
+
*
|
|
4104
|
+
* **It is a report, not a repair.** The split is unchanged, every decoded byte is
|
|
4105
|
+
* identical, and the components read are the components that were always read. What is
|
|
4106
|
+
* new is that the moved slots are reported by a code no profile may tolerate, where
|
|
4107
|
+
* before they were covered only by tolerable ones. **Withholding them is a separate
|
|
4108
|
+
* question, deliberately not answered here**: declining to model a slot changes an
|
|
4109
|
+
* extracted value for every consumer of a package already on the registry.
|
|
4110
|
+
*
|
|
4111
|
+
* **The tail is weighed one construct deep, exactly as in the other two sinks, and one
|
|
4112
|
+
* tail is excluded for the same reason.** Where the escape character the reading taken
|
|
4113
|
+
* resumes on heads a sequence this codec **recognizes**, it interprets a construct and
|
|
4114
|
+
* the components are the ones the sender wrote: under a set naming the component
|
|
4115
|
+
* separator `F`, `GLU&F&F&F&L` is that separator escaped, written, and escaped again,
|
|
4116
|
+
* with nothing reported at all. Refusing that is the over-refusal that sank an earlier
|
|
4117
|
+
* candidate criterion for this family, and that tail is the only one on which a
|
|
4118
|
+
* stream's escaping can be clean. Where the tail heads a sequence whose body is
|
|
4119
|
+
* *unrecognized*, the reading taken consumes a triple it cannot read, the slots move
|
|
4120
|
+
* exactly as far, and this reports it.
|
|
4121
|
+
*
|
|
4122
|
+
* **That silence is a TRADE, and it is not a claim that nothing was lost there.** The
|
|
4123
|
+
* gained component boundary is exactly as real on the excluded tail and can be
|
|
4124
|
+
* entirely silent: under the canonical set `&F&^&F&GLU^L^687` reads one component more
|
|
4125
|
+
* than the competing alignment with `warnings: []`, so a coding scheme and a local code
|
|
4126
|
+
* still sit where an alignment guess put them. Reporting it would refuse a stream whose
|
|
4127
|
+
* escaping is working.
|
|
4128
|
+
*
|
|
4129
|
+
* **⚠️ THE MOVE IS NOT UNIVERSAL OVER THE FIRING POPULATION, AND THE EXCEPTION IS NAMED
|
|
4130
|
+
* RATHER THAN LEFT TO BE FOUND.** It is a **third** bound running the other way, and it
|
|
4131
|
+
* differs from the two above: those two fire where nothing named moves because of where
|
|
4132
|
+
* the boundary sits, while this one is about the bytes past it. Where the sequence past
|
|
4133
|
+
* the boundary carries the **component separator itself** as its body, the reading taken
|
|
4134
|
+
* holds that character inside an opaque atom while the competing alignment splits on it,
|
|
4135
|
+
* so the two readings return the **same number** of components in **different places**:
|
|
4136
|
+
* no component index moves, and what differs is the **contents**. Under the canonical
|
|
4137
|
+
* set `DOE&F&^&^&JANE^A` reads three components under both, with `A` the middle name
|
|
4138
|
+
* under both. That class is bounded and costs no stream its disposition: the tail body
|
|
4139
|
+
* is then a splitting delimiter in force, so
|
|
4140
|
+
* `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` has already refused the record. What holds
|
|
4141
|
+
* on **every** firing tuple is that the two readings disagree and that both consume
|
|
4142
|
+
* every byte, so neither is forced.
|
|
4143
|
+
*
|
|
4144
|
+
* @param segmentIndex - The 0-based index of the component being accumulated when the
|
|
4145
|
+
* contested boundary was taken. Every component after it is one place further right
|
|
4146
|
+
* than the competing alignment puts it, **except** where the sequence past the
|
|
4147
|
+
* boundary carries the component separator as its body, where the two readings return
|
|
4148
|
+
* the same number of components with different contents instead. It is **not** a field
|
|
4149
|
+
* index, and it is **not** a repeat index: the caller splitting one repeat into
|
|
4150
|
+
* components already knows both, and reports the field.
|
|
4151
|
+
*/
|
|
4152
|
+
type ShiftedComponentsSink = (segmentIndex: number) => void;
|
|
3632
4153
|
/**
|
|
3633
4154
|
* Decode the four recognized ASTM escape mnemonics in a single already-split
|
|
3634
4155
|
* leaf (a component string), substituting the *active* delimiters read from the
|
|
@@ -3694,10 +4215,20 @@ declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownE
|
|
|
3694
4215
|
* closing escape character could instead have opened a sequence holding this
|
|
3695
4216
|
* delimiter, so the boundary taken here is not the only reading of the bytes.
|
|
3696
4217
|
* @param onShiftedFields - Called once per contested boundary where the escape
|
|
3697
|
-
* character this reading resumes on heads no sequence
|
|
3698
|
-
*
|
|
3699
|
-
*
|
|
3700
|
-
*
|
|
4218
|
+
* character this reading resumes on heads no sequence this codec can *interpret*
|
|
4219
|
+
* (none at all, or one whose body is not a recognized mnemonic), so the boundary
|
|
4220
|
+
* was bought with bytes the reading cannot read. Wired only by the split taken on
|
|
4221
|
+
* the field separator: see {@link ShiftedFieldsSink} for why, and for the one tail
|
|
4222
|
+
* it deliberately does not report.
|
|
4223
|
+
* @param onTruncatedField - Called on that same condition, wired only by the split
|
|
4224
|
+
* taken on the repeat separator, where the cost is not a shift. **What it is, its
|
|
4225
|
+
* two index bounds and the one class in which nothing moves index are stated in
|
|
4226
|
+
* {@link TruncatedFieldSink} and not restated here.**
|
|
4227
|
+
* @param onShiftedComponents - Called on that same condition again, wired only by the
|
|
4228
|
+
* split taken on the component separator, where nothing shifts between fields and
|
|
4229
|
+
* nothing leaves the record. **What the boundary costs, its three index bounds and
|
|
4230
|
+
* the one class in which nothing moves index are stated in
|
|
4231
|
+
* {@link ShiftedComponentsSink} and not restated here.**
|
|
3701
4232
|
* @returns The raw segments, in order.
|
|
3702
4233
|
* @example
|
|
3703
4234
|
* ```ts
|
|
@@ -3707,7 +4238,7 @@ declare function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownE
|
|
|
3707
4238
|
* splitEscapeAware("O&Brien^John", "^", "&"); // ["O&Brien", "John"] (unpaired: literal)
|
|
3708
4239
|
* ```
|
|
3709
4240
|
*/
|
|
3710
|
-
declare function splitEscapeAware(text: string, delimiter: string, escape: string, onAmbiguousAlignment?: AmbiguousAlignmentSink, onShiftedFields?: ShiftedFieldsSink): string[];
|
|
4241
|
+
declare function splitEscapeAware(text: string, delimiter: string, escape: string, onAmbiguousAlignment?: AmbiguousAlignmentSink, onShiftedFields?: ShiftedFieldsSink, onTruncatedField?: TruncatedFieldSink, onShiftedComponents?: ShiftedComponentsSink): string[];
|
|
3711
4242
|
|
|
3712
4243
|
/**
|
|
3713
4244
|
* Deep-freeze helper for the immutable ASTM model.
|
|
@@ -4074,8 +4605,11 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4074
4605
|
* **A further reader landed with `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, and it is
|
|
4075
4606
|
* the first one that reaches a MODELED SLOT rather than a boundary.** It reports
|
|
4076
4607
|
* that a contested alignment decided a **field** boundary while the reading taken
|
|
4077
|
-
* resumes on an escape character heading no sequence
|
|
4078
|
-
* place further right than the competing alignment puts
|
|
4608
|
+
* resumes on an escape character heading no sequence this codec can interpret, so
|
|
4609
|
+
* every later field sits one place further right than the competing alignment puts
|
|
4610
|
+
* it (**except where the sequence past the boundary carries the field separator
|
|
4611
|
+
* itself**, where the two readings read the same number of fields in different
|
|
4612
|
+
* places). On a result record that
|
|
4079
4613
|
* moves the units and the **result status**: the sender's trailing letter is read out
|
|
4080
4614
|
* of field 9 under the reading taken and out of no field at all under the other, so a
|
|
4081
4615
|
* status of `final` can be a consequence of the alignment rather than something the
|
|
@@ -4089,6 +4623,57 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4089
4623
|
* in a value alters no boundary at all), and the case where it accompanies a shifted
|
|
4090
4624
|
* field is now reported by a code no profile may re-badge.
|
|
4091
4625
|
*
|
|
4626
|
+
* **`ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` landed next, on the same contested
|
|
4627
|
+
* position and the same tail test, and it reaches a modeled slot by the other
|
|
4628
|
+
* route.** It reports a contested alignment deciding a **repeat** boundary. Nothing
|
|
4629
|
+
* shifts: no field-indexed slot moves, which is exactly why the shift report could
|
|
4630
|
+
* not cover it. What it reports is that the field is read as more repeats than the
|
|
4631
|
+
* competing alignment gives it (**except where the sequence past the boundary carries
|
|
4632
|
+
* the repeat separator itself**, where the two readings read the same number of
|
|
4633
|
+
* repeats in different places), and **where that gained boundary is the first one in
|
|
4634
|
+
* the field** it reaches a modeled slot, because a field's modeled value and
|
|
4635
|
+
* components are taken from its **first repeat alone**: everything past it stays in
|
|
4636
|
+
* `repeats` and leaves every modeled slot, so a result value truncates and a
|
|
4637
|
+
* Universal Test ID or a patient name comes back holding only the components that sat
|
|
4638
|
+
* before it. Until it existed the warnings on those streams were
|
|
4639
|
+
* `ASTM_NONSTANDARD_DELIMITERS` and `ASTM_UNPAIRED_ESCAPE_CHARACTER`, both on the list
|
|
4640
|
+
* below, so the widest gate-legal profile plus `{ strict: true }` accepted a truncated
|
|
4641
|
+
* value and an emptied test identity. **At a later boundary it fires and no modeled
|
|
4642
|
+
* slot moves**, which is over-reporting relative to those slots and never
|
|
4643
|
+
* under-reporting: measured, and stated on the code itself rather than left to be
|
|
4644
|
+
* found. It is safety-critical by construction and must never be admitted.
|
|
4645
|
+
* Re-reading the survivors against it moved no admission, and the reason is the one
|
|
4646
|
+
* the entry above already turns on: each of them re-badges the code it names and no
|
|
4647
|
+
* other, and what this condition now raises is a code no profile may name.
|
|
4648
|
+
*
|
|
4649
|
+
* **`ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` completes the set, on the same
|
|
4650
|
+
* contested position and the same tail test, wired to the third and last splitting
|
|
4651
|
+
* role.** It reports a contested alignment deciding a **component** boundary. Nothing
|
|
4652
|
+
* shifts between fields and nothing leaves the record, which is why neither of the two
|
|
4653
|
+
* above could cover it: components are modeled **inside** a field, so every component
|
|
4654
|
+
* after the gained boundary is one place further right than the competing alignment
|
|
4655
|
+
* puts it (**except where the sequence past the boundary carries the component
|
|
4656
|
+
* separator itself**, where the two readings read the same number of components in
|
|
4657
|
+
* different places). That indexes into named slots. A Universal Test ID's four components are the
|
|
4658
|
+
* LOINC-candidate slot, the test name, the **coding scheme** and the **local code**, so
|
|
4659
|
+
* `&F&^&GLU^L^687` reads `687` as the vendor's local code under the reading taken and
|
|
4660
|
+
* as the **coding scheme** under the competing one; a patient name's three are last,
|
|
4661
|
+
* first and middle, so `DOE&F&^&JANE^A` reads `A` as the middle name under the reading
|
|
4662
|
+
* taken and as the **given** name under the other. Until it existed the only warning on
|
|
4663
|
+
* those streams was `ASTM_UNPAIRED_ESCAPE_CHARACTER`, on the list below, so the widest
|
|
4664
|
+
* gate-legal profile plus `{ strict: true }` accepted a test identity and a patient name
|
|
4665
|
+
* whose parts were decided by the alignment. **Unlike the repeat role, every gained
|
|
4666
|
+
* boundary at or before the last modeled component index moves those slots and not only
|
|
4667
|
+
* the first**, because the shift propagates to the end of the component list. **THREE
|
|
4668
|
+
* bounds run the other way and it fires inside all three**, which is over-reporting and
|
|
4669
|
+
* never under-reporting: past the last modeled index nothing NAMED moves (a name models
|
|
4670
|
+
* three components, a Universal Test ID four); inside a later repeat nothing modeled moves
|
|
4671
|
+
* at all; and on the class named above nothing moves index at all. All three axes are
|
|
4672
|
+
* measured on sweeps of their own rather than on the shared corpus, which holds them
|
|
4673
|
+
* fixed. It is safety-critical by construction and must never be
|
|
4674
|
+
* admitted. Re-reading the survivors against it moved no admission, for the reason the
|
|
4675
|
+
* two entries above already turn on.
|
|
4676
|
+
*
|
|
4092
4677
|
* **So the ones that remain are recorded with the reading each one survives**, not
|
|
4093
4678
|
* merely with the value it preserves. Each was re-derived against **every** reader of
|
|
4094
4679
|
* record structure named above (this sentence deliberately names no count: it has
|
|
@@ -4147,6 +4732,17 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4147
4732
|
* time, and this entry survives it on the same narrower claim: what that reader
|
|
4148
4733
|
* raises is a different code, which a profile cannot name and cannot tolerate.
|
|
4149
4734
|
*
|
|
4735
|
+
* **A THIRD reader landed with the widened tail bound, and it is named here for the
|
|
4736
|
+
* same reason.** `headsInterpretableSequence` in `../common/escapes.ts` asks the
|
|
4737
|
+
* mnemonic test about the escape character just past a contested boundary, so an
|
|
4738
|
+
* **unrecognized** body there, which is exactly the condition this code reports, now
|
|
4739
|
+
* also decides whether `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`,
|
|
4740
|
+
* `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` or `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`
|
|
4741
|
+
* fires. Three sinks, one predicate, and this entry survives all three on the same
|
|
4742
|
+
* narrower claim: none of the codes they raise may be named by a profile.
|
|
4743
|
+
* **Re-derive this list whenever something new starts reading record structure**: the
|
|
4744
|
+
* count is now three and this is the second time it has grown.
|
|
4745
|
+
*
|
|
4150
4746
|
* **This entry used to be recorded as questionable, and what made it questionable
|
|
4151
4747
|
* is now a separate code that is not on this list.** The argument above is about
|
|
4152
4748
|
* the *decoded value*, and it holds. What it did not cover is that the
|
|
@@ -4174,21 +4770,35 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4174
4770
|
* decoded value, so the value is identical with or without the profile. Note what
|
|
4175
4771
|
* makes this admissible is a property of the *parse*, not of the warning: reading
|
|
4176
4772
|
* the character as a literal is unconditional, and tolerating the code changes
|
|
4177
|
-
* nothing about how the record splits.
|
|
4178
|
-
*
|
|
4773
|
+
* nothing about how the record splits. None of the three readers named at the top of
|
|
4774
|
+
* this file sees it: the type letter is read before any decoding, the split reader
|
|
4179
4775
|
* counts fields the escape-aware tokenizer has already finished dividing, and a
|
|
4180
|
-
* decoded value is never cut back into records.
|
|
4776
|
+
* decoded value is never cut back into records. **Three later readers DO see it**, and
|
|
4777
|
+
* they are named below rather than left out of this bullet. Contrast the condition it
|
|
4181
4778
|
* replaced, which **would** have failed part 1: reading the same character as an
|
|
4182
4779
|
* unterminated sequence merged every field after it into one, and a code reporting
|
|
4183
4780
|
* that would have been a report of lost values.
|
|
4184
4781
|
*
|
|
4185
|
-
* **The alignment reader is not a reader of this condition
|
|
4186
|
-
*
|
|
4187
|
-
*
|
|
4188
|
-
*
|
|
4189
|
-
*
|
|
4190
|
-
*
|
|
4191
|
-
*
|
|
4782
|
+
* **The alignment reader is not a reader of this condition**, and that much still
|
|
4783
|
+
* holds: it asks whether the character two positions past a sequence *is* the escape
|
|
4784
|
+
* character, never whether that character heads a sequence of its own. The two are
|
|
4785
|
+
* independent in both directions, measured: `28.6&Z&|&U/L` raises this code and the
|
|
4786
|
+
* alignment code together, and `28.6&Z&|&U&L` raises the alignment code with no
|
|
4787
|
+
* unpaired character anywhere in it.
|
|
4788
|
+
*
|
|
4789
|
+
* **The TAIL readers are a different matter, and "no reader sees it" is
|
|
4790
|
+
* therefore FALSE of this entry and must not be restored.** There are three of them
|
|
4791
|
+
* now, one per splitting role: `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`,
|
|
4792
|
+
* `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` and
|
|
4793
|
+
* `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` all fire where the character at
|
|
4794
|
+
* `i + 4` heads no sequence this codec can interpret, and heading no sequence at all
|
|
4795
|
+
* is one of the two ways to satisfy that, so this code's own question is asked about
|
|
4796
|
+
* the character just past a contested boundary. That is a reader of the reported
|
|
4797
|
+
* condition, so this entry survives on the same narrower claim the unknown-sequence
|
|
4798
|
+
* entry above already uses, not on nothing reading it: a profile re-badges the code
|
|
4799
|
+
* it names and no other, and what those three readers raise is not tolerable in any
|
|
4800
|
+
* case. If a future reader of this condition ever produces something a profile
|
|
4801
|
+
* **can** tolerate, this entry stops being admissible and comes off the list.
|
|
4192
4802
|
* - `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS`: a request-information status carried
|
|
4193
4803
|
* verbatim as a leaf field on a `Q` record; the code set is paywalled and is not
|
|
4194
4804
|
* interpreted, profile or not. Classification reads whether a `Q` record is
|
|
@@ -4202,7 +4812,9 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4202
4812
|
* an unrecognized record type, a record the delimiters in force could not split, a
|
|
4203
4813
|
* declaration naming one character in two roles, a delimiter an unrecognized escape
|
|
4204
4814
|
* sequence kept out of the split, a boundary a competing escape alignment disagrees
|
|
4205
|
-
* about, a
|
|
4815
|
+
* about, a field whose modeled reading stops at a boundary only one alignment has,
|
|
4816
|
+
* a component list only one alignment aligns that way,
|
|
4817
|
+
* a bad frame checksum / sequence gap / unterminated
|
|
4206
4818
|
* / oversize frame, an ambiguous transport, an unexpected protocol event, or a
|
|
4207
4819
|
* rejected frame: is forbidden.
|
|
4208
4820
|
*
|
|
@@ -4249,6 +4861,24 @@ declare function resolveProfileTransport(profile: AstmProfile | undefined): Astm
|
|
|
4249
4861
|
* reading `final` that the competing alignment of the same bytes puts in no field at
|
|
4250
4862
|
* all.
|
|
4251
4863
|
*
|
|
4864
|
+
* `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` is not on this list and must not be added
|
|
4865
|
+
* to it either. It reports the same contested alignment deciding a **repeat**
|
|
4866
|
+
* boundary, where no slot changes hands and a modeled reading is cut off instead: the
|
|
4867
|
+
* field is read out of its first repeat alone, so where that boundary is the first
|
|
4868
|
+
* one a value truncates and a Universal Test ID's coding scheme and local code are
|
|
4869
|
+
* read from bytes that are no longer in any modeled slot. Reporting a value that is
|
|
4870
|
+
* not the whole of what the sender wrote fails the first half of the two-clause test
|
|
4871
|
+
* outright.
|
|
4872
|
+
*
|
|
4873
|
+
* `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` is not on this list and must not be added
|
|
4874
|
+
* to it either. It reports the same contested alignment deciding a **component**
|
|
4875
|
+
* boundary, where nothing leaves the record and no field number changes, and every
|
|
4876
|
+
* component after it moves one slot along instead: a Universal Test ID's coding scheme
|
|
4877
|
+
* and local code, and a patient's given and middle names, are read out of positions the
|
|
4878
|
+
* competing alignment does not put them in. Reporting a code system, a vendor's local
|
|
4879
|
+
* code or a given name that the bytes do not unambiguously carry fails the first half of
|
|
4880
|
+
* the two-clause test outright.
|
|
4881
|
+
*
|
|
4252
4882
|
* @example
|
|
4253
4883
|
* ```ts
|
|
4254
4884
|
* import { TOLERABLE_CODES } from "@cosyte/astm";
|
|
@@ -5593,4 +6223,4 @@ declare class AstmFrameStrictError extends Error {
|
|
|
5593
6223
|
*/
|
|
5594
6224
|
declare const VERSION: string;
|
|
5595
6225
|
|
|
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 };
|
|
6226
|
+
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 ShiftedComponentsSink, type ShiftedFieldsSink, type SwallowedDelimiterSink, TOLERABLE_CODES, type TerminatorRecord, type TruncatedFieldSink, type UniversalTestId, type UniversalTestIdProvenance, type UnknownEscapeSink, type UnpairedEscapeSink, type UnsupportedRecord, VERSION, type VerbatimInput, WARNING_CODES, type WarningCode, alignmentShiftedComponents, alignmentShiftedFields, alignmentTruncatedField, 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 };
|