@cosyte/astm 0.0.13 → 0.0.14
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 +69 -0
- package/README.md +7 -1
- package/dist/index.cjs +13 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -16
- package/dist/index.d.ts +86 -16
- package/dist/index.mjs +13 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1103,7 +1103,7 @@ declare function detectFraming(bytes: Uint8Array, options?: DetectFramingOptions
|
|
|
1103
1103
|
* Any warning code from any of the three registries: the record layer
|
|
1104
1104
|
* (`ASTM_RECORD_*` / `ASTM_NONSTANDARD_DELIMITERS` / `ASTM_UNKNOWN_ESCAPE_SEQUENCE`),
|
|
1105
1105
|
* the frame codec (`ASTM_FRAME_*`), or the LTP protocol layer (`ASTM_LTP_*`). A
|
|
1106
|
-
* {@link
|
|
1106
|
+
* {@link AstmQuirkTolerance} may *name* any of them, but the safety gate refuses all
|
|
1107
1107
|
* but the small, benign, record-layer subset, so the union exists mainly to let
|
|
1108
1108
|
* the gate reject a frame/LTP code with a precise, typed message.
|
|
1109
1109
|
*/
|
|
@@ -1136,7 +1136,7 @@ interface AstmProfileProvenance {
|
|
|
1136
1136
|
readonly note?: string;
|
|
1137
1137
|
}
|
|
1138
1138
|
/**
|
|
1139
|
-
* Optional structural narrowing for
|
|
1139
|
+
* Optional structural narrowing for an {@link AstmQuirkTolerance}. When present, the
|
|
1140
1140
|
* tolerance applies only to warnings whose PHI-free {@link AstmPosition} matches
|
|
1141
1141
|
* every provided field, so a profile can expect a deviation on one record type
|
|
1142
1142
|
* (e.g. an unknown escape only inside `R` result values) without blanket-tolerating
|
|
@@ -1911,7 +1911,7 @@ interface AstmMessage {
|
|
|
1911
1911
|
* The vendor profile that was active for this parse, when one applied: its
|
|
1912
1912
|
* `name` and resolved `lineage` (attribution only; the profile object itself is
|
|
1913
1913
|
* not embedded). Absent when no profile applied. A profile that tolerated a
|
|
1914
|
-
* deviation shows up as a `PROFILE_QUIRK_APPLIED` entry in {@link warnings}, not
|
|
1914
|
+
* deviation shows up as a `PROFILE_QUIRK_APPLIED` entry in {@link AstmMessage.warnings}, not
|
|
1915
1915
|
* here.
|
|
1916
1916
|
*/
|
|
1917
1917
|
readonly profile?: {
|
|
@@ -4407,7 +4407,8 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4407
4407
|
* case of);
|
|
4408
4408
|
* - the **frame number** `0`–`7`, starting at `1` and rolling over `7 → 0 → 1`,
|
|
4409
4409
|
* continuous across every frame in the stream (as the decoder's sequence check
|
|
4410
|
-
* expects);
|
|
4410
|
+
* expects); a caller may start the sequence elsewhere to compose one transfer
|
|
4411
|
+
* across several calls, but only at a number a frame can actually carry;
|
|
4411
4412
|
* - the **240-byte text split**: a record longer than 240 bytes is split across
|
|
4412
4413
|
* frames closed by `ETB` (intermediate) with a final `ETX`; the seven framing
|
|
4413
4414
|
* control bytes are never counted toward the 240.
|
|
@@ -4432,6 +4433,9 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4432
4433
|
* - `ASTM_FRAME_RESERVED_BYTE`: a record holds a byte this layer reads as frame
|
|
4433
4434
|
* structure (`STX`, `ETB` or `ETX`). Framing has no escape mechanism, so such
|
|
4434
4435
|
* a byte cannot be carried inside a frame at all.
|
|
4436
|
+
* - `ASTM_FRAME_INVALID_START_FRAME_NUMBER`: `options.startFrameNumber` is not a
|
|
4437
|
+
* whole number from `0` to `7`. A frame's number is one ASCII digit, so a
|
|
4438
|
+
* value outside that range has no digit to be written as.
|
|
4435
4439
|
*
|
|
4436
4440
|
* @example
|
|
4437
4441
|
* ```ts
|
|
@@ -4439,7 +4443,7 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4439
4443
|
* const code: AstmFrameEncodeErrorCode = "ASTM_FRAME_UNENCODABLE_CHARACTER";
|
|
4440
4444
|
* ```
|
|
4441
4445
|
*/
|
|
4442
|
-
type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODABLE_CHARACTER" | "ASTM_FRAME_RESERVED_BYTE";
|
|
4446
|
+
type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODABLE_CHARACTER" | "ASTM_FRAME_RESERVED_BYTE" | "ASTM_FRAME_INVALID_START_FRAME_NUMBER";
|
|
4443
4447
|
/**
|
|
4444
4448
|
* Thrown by {@link composeAstmFrames} when the input cannot be framed into a
|
|
4445
4449
|
* spec-clean stream. Carries a stable code + positional context, never the
|
|
@@ -4471,6 +4475,20 @@ type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODA
|
|
|
4471
4475
|
* before framing: which byte belongs in a clinical value is the sender's call, not
|
|
4472
4476
|
* this library's, so it refuses rather than substituting or deleting one.
|
|
4473
4477
|
*
|
|
4478
|
+
* **The start-frame-number case, and why an option gets an error of its own.** A
|
|
4479
|
+
* frame's number is a single ASCII digit, `FN_ZERO + n` for `n` in `0`-`7`, and
|
|
4480
|
+
* nothing used to check that `options.startFrameNumber` was one. What went out
|
|
4481
|
+
* instead was whatever byte that sum truncated to: measured on this package's own
|
|
4482
|
+
* round trip, `-1` wrote `/` into the frame-number position and `NaN`, `Infinity`
|
|
4483
|
+
* and `-Infinity` each wrote a `NUL` byte into every frame of the stream, after
|
|
4484
|
+
* which the decoder read no frame number at all and emitted **none** of the
|
|
4485
|
+
* records. Out-of-domain values that happened to land back on a digit were worse
|
|
4486
|
+
* in a quieter way: `1.5` and `257` both produced the byte-for-byte stream a
|
|
4487
|
+
* `startFrameNumber` of `1` produces, so the option silently accepted a value it
|
|
4488
|
+
* documented as invalid. This one refusal is about the caller's own option rather
|
|
4489
|
+
* than about record content, so its message names the value received. Nothing
|
|
4490
|
+
* from the stream is quoted.
|
|
4491
|
+
*
|
|
4474
4492
|
* @example
|
|
4475
4493
|
* ```ts
|
|
4476
4494
|
* import { composeAstmFrames, AstmFrameEncodeError } from "@cosyte/astm";
|
|
@@ -4500,8 +4518,42 @@ declare class AstmFrameEncodeError extends Error {
|
|
|
4500
4518
|
/** Options for {@link composeAstmFrames}. */
|
|
4501
4519
|
interface ComposeFramesOptions {
|
|
4502
4520
|
/**
|
|
4503
|
-
* The frame number to start the sequence at
|
|
4504
|
-
*
|
|
4521
|
+
* The frame number to start the sequence at: a whole number from `0` to `7`.
|
|
4522
|
+
* Defaults to `1`, the number ASTM gives the first frame of a transfer and the
|
|
4523
|
+
* one {@link decodeAstmFrames} expects to read first.
|
|
4524
|
+
*
|
|
4525
|
+
* **Any other value writes a continuation rather than the start of a transfer,
|
|
4526
|
+
* and that is what the option is for**: composing one transfer across more than
|
|
4527
|
+
* one call. Concatenating `composeAstmFrames(head)` with
|
|
4528
|
+
* `composeAstmFrames(tail, { startFrameNumber: n })`, where `n` is the number
|
|
4529
|
+
* after the last frame `head` used, is byte-identical to composing the whole
|
|
4530
|
+
* list in a single call and decodes with an empty warnings array, across the
|
|
4531
|
+
* `7 → 0` rollover included.
|
|
4532
|
+
*
|
|
4533
|
+
* **Decoded on its own, a stream that starts anywhere but `1` opens on a
|
|
4534
|
+
* sequence gap.** The decoder never bridges a gap silently, so it warns
|
|
4535
|
+
* (`ASTM_FRAME_SEQUENCE_GAP`) and does not emit that first record.
|
|
4536
|
+
* **What {@link parseFramedAstm} does after that varies with the message
|
|
4537
|
+
* shape and no rule is offered for it here.** It may throw, under more than
|
|
4538
|
+
* one code, and it may return a message that is simply one record short. **The
|
|
4539
|
+
* one thing that does hold is that the record layer never reports the loss**:
|
|
4540
|
+
* `parseFramedAstm` hands the record parser only the frames the codec
|
|
4541
|
+
* vouched for, so `message.warnings` carries what the surviving records
|
|
4542
|
+
* warrant and nothing about the record that did not survive. **Read
|
|
4543
|
+
* `frameWarnings`.** That is the cost of the option rather than a defect in
|
|
4544
|
+
* the caller's records, and it is why `1` is the default.
|
|
4545
|
+
*
|
|
4546
|
+
* **Finding the number to continue from.** Nothing here returns it, and the
|
|
4547
|
+
* frame count is not it once a record splits: decode the part you just
|
|
4548
|
+
* composed and read the last frame's number, then add one modulo 8, as in
|
|
4549
|
+
* `((decodeAstmFrames(part).frames.at(-1)?.frameNumber ?? 0) + 1) % 8`. A frame
|
|
4550
|
+
* carries no number only when the stream ends immediately after its `STX`,
|
|
4551
|
+
* which is not something this encoder writes, so that fallback never fires on
|
|
4552
|
+
* a part composed here. Getting the number wrong costs the record at the join,
|
|
4553
|
+
* warned rather than silently.
|
|
4554
|
+
*
|
|
4555
|
+
* A value outside `0`-`7`, or one that is not a whole number, is refused with
|
|
4556
|
+
* `ASTM_FRAME_INVALID_START_FRAME_NUMBER`.
|
|
4505
4557
|
*/
|
|
4506
4558
|
readonly startFrameNumber?: number;
|
|
4507
4559
|
}
|
|
@@ -4539,6 +4591,17 @@ interface ComposeFramesOptions {
|
|
|
4539
4591
|
* absorbed into the previous one with an empty warnings array at both layers.
|
|
4540
4592
|
* Supplying the record as a `Uint8Array` does not route around this check.
|
|
4541
4593
|
*
|
|
4594
|
+
* **`options.startFrameNumber` is checked before *this function* reads a record**,
|
|
4595
|
+
* so on this entry point the refusal never depends on the caller's data.
|
|
4596
|
+
* ({@link serializeFramedAstm} serializes every record *before* it gets here, so
|
|
4597
|
+
* a record that cannot be serialized is refused first on that route.) It has to
|
|
4598
|
+
* be a whole number from `0` to `7`, because a frame's number is one ASCII digit;
|
|
4599
|
+
* anything else is `ASTM_FRAME_INVALID_START_FRAME_NUMBER` rather than whatever
|
|
4600
|
+
* byte the arithmetic truncated to. A value other than the default `1` writes a
|
|
4601
|
+
* **continuation** of a sequence already in progress, which is what the option is
|
|
4602
|
+
* for, and a continuation decoded on its own opens on a sequence gap: see
|
|
4603
|
+
* {@link ComposeFramesOptions}.
|
|
4604
|
+
*
|
|
4542
4605
|
* What is still **not** guaranteed: that an accepted record round-trips through
|
|
4543
4606
|
* the *record* layer. A delimiter colliding with a record's type letter, for one,
|
|
4544
4607
|
* frames and de-frames byte-exactly and still re-reads as a different record.
|
|
@@ -4547,10 +4610,12 @@ interface ComposeFramesOptions {
|
|
|
4547
4610
|
* `string`), one entry per complete record.
|
|
4548
4611
|
* @param options - Encode options.
|
|
4549
4612
|
* @returns The framed byte stream.
|
|
4550
|
-
* @throws {@link AstmFrameEncodeError} when
|
|
4551
|
-
*
|
|
4552
|
-
*
|
|
4553
|
-
*
|
|
4613
|
+
* @throws {@link AstmFrameEncodeError} when `options.startFrameNumber` is not a
|
|
4614
|
+
* whole number from `0` to `7` (`ASTM_FRAME_INVALID_START_FRAME_NUMBER`), the
|
|
4615
|
+
* list is empty, a record has no bytes (`ASTM_FRAME_EMPTY_RECORD`), a record
|
|
4616
|
+
* string holds a character above `U+00FF`
|
|
4617
|
+
* (`ASTM_FRAME_UNENCODABLE_CHARACTER`), or a record holds an `STX`, `ETB` or
|
|
4618
|
+
* `ETX` byte (`ASTM_FRAME_RESERVED_BYTE`).
|
|
4554
4619
|
* @example
|
|
4555
4620
|
* ```ts
|
|
4556
4621
|
* import { composeAstmFrames, decodeAstmFrames } from "@cosyte/astm";
|
|
@@ -4625,7 +4690,11 @@ declare function parseFramedAstm(bytes: Uint8Array, options?: FrameOptions & Ast
|
|
|
4625
4690
|
* delimiters, embedded delimiters re-escaped) and then framed **independently**
|
|
4626
4691
|
* (one record per `ETX`-closed frame run), so the framing exactly mirrors what
|
|
4627
4692
|
* {@link decodeAstmFrames} reassembles: `parseFramedAstm(serializeFramedAstm(msg))`
|
|
4628
|
-
* yields an equal message
|
|
4693
|
+
* yields an equal message **with the default `startFrameNumber`**. That clause is
|
|
4694
|
+
* load-bearing rather than pedantic: a non-default start writes a continuation of
|
|
4695
|
+
* a sequence already in progress, and a continuation read on its own opens on a
|
|
4696
|
+
* sequence gap, so the decoder does not emit its first record and this round trip
|
|
4697
|
+
* does not hold for it. See {@link ComposeFramesOptions}.
|
|
4629
4698
|
*
|
|
4630
4699
|
* A value carrying a character above `U+00FF` is **refused** here rather than
|
|
4631
4700
|
* framed: the record layer is happy to hold one, but a frame carries bytes and
|
|
@@ -4645,10 +4714,11 @@ declare function parseFramedAstm(bytes: Uint8Array, options?: FrameOptions & Ast
|
|
|
4645
4714
|
* survive being written in the canonical set this function serializes with
|
|
4646
4715
|
* (`ASTM_EMIT_TYPE_LETTER_COLLISION`). The second reaches messages parsed under a
|
|
4647
4716
|
* **different** delimiter set, since this function passes no set of its own.
|
|
4648
|
-
* @throws {@link AstmFrameEncodeError} when
|
|
4649
|
-
*
|
|
4650
|
-
* (`
|
|
4651
|
-
* `
|
|
4717
|
+
* @throws {@link AstmFrameEncodeError} when `options.startFrameNumber` is not a
|
|
4718
|
+
* whole number from `0` to `7` (`ASTM_FRAME_INVALID_START_FRAME_NUMBER`), there
|
|
4719
|
+
* are no records to frame (`ASTM_FRAME_EMPTY_RECORD`), a value holds a
|
|
4720
|
+
* character above `U+00FF` (`ASTM_FRAME_UNENCODABLE_CHARACTER`), or a record
|
|
4721
|
+
* holds an `STX`, `ETB` or `ETX` byte (`ASTM_FRAME_RESERVED_BYTE`).
|
|
4652
4722
|
* @example
|
|
4653
4723
|
* ```ts
|
|
4654
4724
|
* import { parseAstmRecords, serializeFramedAstm, parseFramedAstm } from "@cosyte/astm";
|
package/dist/index.d.ts
CHANGED
|
@@ -1103,7 +1103,7 @@ declare function detectFraming(bytes: Uint8Array, options?: DetectFramingOptions
|
|
|
1103
1103
|
* Any warning code from any of the three registries: the record layer
|
|
1104
1104
|
* (`ASTM_RECORD_*` / `ASTM_NONSTANDARD_DELIMITERS` / `ASTM_UNKNOWN_ESCAPE_SEQUENCE`),
|
|
1105
1105
|
* the frame codec (`ASTM_FRAME_*`), or the LTP protocol layer (`ASTM_LTP_*`). A
|
|
1106
|
-
* {@link
|
|
1106
|
+
* {@link AstmQuirkTolerance} may *name* any of them, but the safety gate refuses all
|
|
1107
1107
|
* but the small, benign, record-layer subset, so the union exists mainly to let
|
|
1108
1108
|
* the gate reject a frame/LTP code with a precise, typed message.
|
|
1109
1109
|
*/
|
|
@@ -1136,7 +1136,7 @@ interface AstmProfileProvenance {
|
|
|
1136
1136
|
readonly note?: string;
|
|
1137
1137
|
}
|
|
1138
1138
|
/**
|
|
1139
|
-
* Optional structural narrowing for
|
|
1139
|
+
* Optional structural narrowing for an {@link AstmQuirkTolerance}. When present, the
|
|
1140
1140
|
* tolerance applies only to warnings whose PHI-free {@link AstmPosition} matches
|
|
1141
1141
|
* every provided field, so a profile can expect a deviation on one record type
|
|
1142
1142
|
* (e.g. an unknown escape only inside `R` result values) without blanket-tolerating
|
|
@@ -1911,7 +1911,7 @@ interface AstmMessage {
|
|
|
1911
1911
|
* The vendor profile that was active for this parse, when one applied: its
|
|
1912
1912
|
* `name` and resolved `lineage` (attribution only; the profile object itself is
|
|
1913
1913
|
* not embedded). Absent when no profile applied. A profile that tolerated a
|
|
1914
|
-
* deviation shows up as a `PROFILE_QUIRK_APPLIED` entry in {@link warnings}, not
|
|
1914
|
+
* deviation shows up as a `PROFILE_QUIRK_APPLIED` entry in {@link AstmMessage.warnings}, not
|
|
1915
1915
|
* here.
|
|
1916
1916
|
*/
|
|
1917
1917
|
readonly profile?: {
|
|
@@ -4407,7 +4407,8 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4407
4407
|
* case of);
|
|
4408
4408
|
* - the **frame number** `0`–`7`, starting at `1` and rolling over `7 → 0 → 1`,
|
|
4409
4409
|
* continuous across every frame in the stream (as the decoder's sequence check
|
|
4410
|
-
* expects);
|
|
4410
|
+
* expects); a caller may start the sequence elsewhere to compose one transfer
|
|
4411
|
+
* across several calls, but only at a number a frame can actually carry;
|
|
4411
4412
|
* - the **240-byte text split**: a record longer than 240 bytes is split across
|
|
4412
4413
|
* frames closed by `ETB` (intermediate) with a final `ETX`; the seven framing
|
|
4413
4414
|
* control bytes are never counted toward the 240.
|
|
@@ -4432,6 +4433,9 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4432
4433
|
* - `ASTM_FRAME_RESERVED_BYTE`: a record holds a byte this layer reads as frame
|
|
4433
4434
|
* structure (`STX`, `ETB` or `ETX`). Framing has no escape mechanism, so such
|
|
4434
4435
|
* a byte cannot be carried inside a frame at all.
|
|
4436
|
+
* - `ASTM_FRAME_INVALID_START_FRAME_NUMBER`: `options.startFrameNumber` is not a
|
|
4437
|
+
* whole number from `0` to `7`. A frame's number is one ASCII digit, so a
|
|
4438
|
+
* value outside that range has no digit to be written as.
|
|
4435
4439
|
*
|
|
4436
4440
|
* @example
|
|
4437
4441
|
* ```ts
|
|
@@ -4439,7 +4443,7 @@ declare function decodeAstmFrames(bytes: Uint8Array, options?: FrameOptions): De
|
|
|
4439
4443
|
* const code: AstmFrameEncodeErrorCode = "ASTM_FRAME_UNENCODABLE_CHARACTER";
|
|
4440
4444
|
* ```
|
|
4441
4445
|
*/
|
|
4442
|
-
type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODABLE_CHARACTER" | "ASTM_FRAME_RESERVED_BYTE";
|
|
4446
|
+
type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODABLE_CHARACTER" | "ASTM_FRAME_RESERVED_BYTE" | "ASTM_FRAME_INVALID_START_FRAME_NUMBER";
|
|
4443
4447
|
/**
|
|
4444
4448
|
* Thrown by {@link composeAstmFrames} when the input cannot be framed into a
|
|
4445
4449
|
* spec-clean stream. Carries a stable code + positional context, never the
|
|
@@ -4471,6 +4475,20 @@ type AstmFrameEncodeErrorCode = "ASTM_FRAME_EMPTY_RECORD" | "ASTM_FRAME_UNENCODA
|
|
|
4471
4475
|
* before framing: which byte belongs in a clinical value is the sender's call, not
|
|
4472
4476
|
* this library's, so it refuses rather than substituting or deleting one.
|
|
4473
4477
|
*
|
|
4478
|
+
* **The start-frame-number case, and why an option gets an error of its own.** A
|
|
4479
|
+
* frame's number is a single ASCII digit, `FN_ZERO + n` for `n` in `0`-`7`, and
|
|
4480
|
+
* nothing used to check that `options.startFrameNumber` was one. What went out
|
|
4481
|
+
* instead was whatever byte that sum truncated to: measured on this package's own
|
|
4482
|
+
* round trip, `-1` wrote `/` into the frame-number position and `NaN`, `Infinity`
|
|
4483
|
+
* and `-Infinity` each wrote a `NUL` byte into every frame of the stream, after
|
|
4484
|
+
* which the decoder read no frame number at all and emitted **none** of the
|
|
4485
|
+
* records. Out-of-domain values that happened to land back on a digit were worse
|
|
4486
|
+
* in a quieter way: `1.5` and `257` both produced the byte-for-byte stream a
|
|
4487
|
+
* `startFrameNumber` of `1` produces, so the option silently accepted a value it
|
|
4488
|
+
* documented as invalid. This one refusal is about the caller's own option rather
|
|
4489
|
+
* than about record content, so its message names the value received. Nothing
|
|
4490
|
+
* from the stream is quoted.
|
|
4491
|
+
*
|
|
4474
4492
|
* @example
|
|
4475
4493
|
* ```ts
|
|
4476
4494
|
* import { composeAstmFrames, AstmFrameEncodeError } from "@cosyte/astm";
|
|
@@ -4500,8 +4518,42 @@ declare class AstmFrameEncodeError extends Error {
|
|
|
4500
4518
|
/** Options for {@link composeAstmFrames}. */
|
|
4501
4519
|
interface ComposeFramesOptions {
|
|
4502
4520
|
/**
|
|
4503
|
-
* The frame number to start the sequence at
|
|
4504
|
-
*
|
|
4521
|
+
* The frame number to start the sequence at: a whole number from `0` to `7`.
|
|
4522
|
+
* Defaults to `1`, the number ASTM gives the first frame of a transfer and the
|
|
4523
|
+
* one {@link decodeAstmFrames} expects to read first.
|
|
4524
|
+
*
|
|
4525
|
+
* **Any other value writes a continuation rather than the start of a transfer,
|
|
4526
|
+
* and that is what the option is for**: composing one transfer across more than
|
|
4527
|
+
* one call. Concatenating `composeAstmFrames(head)` with
|
|
4528
|
+
* `composeAstmFrames(tail, { startFrameNumber: n })`, where `n` is the number
|
|
4529
|
+
* after the last frame `head` used, is byte-identical to composing the whole
|
|
4530
|
+
* list in a single call and decodes with an empty warnings array, across the
|
|
4531
|
+
* `7 → 0` rollover included.
|
|
4532
|
+
*
|
|
4533
|
+
* **Decoded on its own, a stream that starts anywhere but `1` opens on a
|
|
4534
|
+
* sequence gap.** The decoder never bridges a gap silently, so it warns
|
|
4535
|
+
* (`ASTM_FRAME_SEQUENCE_GAP`) and does not emit that first record.
|
|
4536
|
+
* **What {@link parseFramedAstm} does after that varies with the message
|
|
4537
|
+
* shape and no rule is offered for it here.** It may throw, under more than
|
|
4538
|
+
* one code, and it may return a message that is simply one record short. **The
|
|
4539
|
+
* one thing that does hold is that the record layer never reports the loss**:
|
|
4540
|
+
* `parseFramedAstm` hands the record parser only the frames the codec
|
|
4541
|
+
* vouched for, so `message.warnings` carries what the surviving records
|
|
4542
|
+
* warrant and nothing about the record that did not survive. **Read
|
|
4543
|
+
* `frameWarnings`.** That is the cost of the option rather than a defect in
|
|
4544
|
+
* the caller's records, and it is why `1` is the default.
|
|
4545
|
+
*
|
|
4546
|
+
* **Finding the number to continue from.** Nothing here returns it, and the
|
|
4547
|
+
* frame count is not it once a record splits: decode the part you just
|
|
4548
|
+
* composed and read the last frame's number, then add one modulo 8, as in
|
|
4549
|
+
* `((decodeAstmFrames(part).frames.at(-1)?.frameNumber ?? 0) + 1) % 8`. A frame
|
|
4550
|
+
* carries no number only when the stream ends immediately after its `STX`,
|
|
4551
|
+
* which is not something this encoder writes, so that fallback never fires on
|
|
4552
|
+
* a part composed here. Getting the number wrong costs the record at the join,
|
|
4553
|
+
* warned rather than silently.
|
|
4554
|
+
*
|
|
4555
|
+
* A value outside `0`-`7`, or one that is not a whole number, is refused with
|
|
4556
|
+
* `ASTM_FRAME_INVALID_START_FRAME_NUMBER`.
|
|
4505
4557
|
*/
|
|
4506
4558
|
readonly startFrameNumber?: number;
|
|
4507
4559
|
}
|
|
@@ -4539,6 +4591,17 @@ interface ComposeFramesOptions {
|
|
|
4539
4591
|
* absorbed into the previous one with an empty warnings array at both layers.
|
|
4540
4592
|
* Supplying the record as a `Uint8Array` does not route around this check.
|
|
4541
4593
|
*
|
|
4594
|
+
* **`options.startFrameNumber` is checked before *this function* reads a record**,
|
|
4595
|
+
* so on this entry point the refusal never depends on the caller's data.
|
|
4596
|
+
* ({@link serializeFramedAstm} serializes every record *before* it gets here, so
|
|
4597
|
+
* a record that cannot be serialized is refused first on that route.) It has to
|
|
4598
|
+
* be a whole number from `0` to `7`, because a frame's number is one ASCII digit;
|
|
4599
|
+
* anything else is `ASTM_FRAME_INVALID_START_FRAME_NUMBER` rather than whatever
|
|
4600
|
+
* byte the arithmetic truncated to. A value other than the default `1` writes a
|
|
4601
|
+
* **continuation** of a sequence already in progress, which is what the option is
|
|
4602
|
+
* for, and a continuation decoded on its own opens on a sequence gap: see
|
|
4603
|
+
* {@link ComposeFramesOptions}.
|
|
4604
|
+
*
|
|
4542
4605
|
* What is still **not** guaranteed: that an accepted record round-trips through
|
|
4543
4606
|
* the *record* layer. A delimiter colliding with a record's type letter, for one,
|
|
4544
4607
|
* frames and de-frames byte-exactly and still re-reads as a different record.
|
|
@@ -4547,10 +4610,12 @@ interface ComposeFramesOptions {
|
|
|
4547
4610
|
* `string`), one entry per complete record.
|
|
4548
4611
|
* @param options - Encode options.
|
|
4549
4612
|
* @returns The framed byte stream.
|
|
4550
|
-
* @throws {@link AstmFrameEncodeError} when
|
|
4551
|
-
*
|
|
4552
|
-
*
|
|
4553
|
-
*
|
|
4613
|
+
* @throws {@link AstmFrameEncodeError} when `options.startFrameNumber` is not a
|
|
4614
|
+
* whole number from `0` to `7` (`ASTM_FRAME_INVALID_START_FRAME_NUMBER`), the
|
|
4615
|
+
* list is empty, a record has no bytes (`ASTM_FRAME_EMPTY_RECORD`), a record
|
|
4616
|
+
* string holds a character above `U+00FF`
|
|
4617
|
+
* (`ASTM_FRAME_UNENCODABLE_CHARACTER`), or a record holds an `STX`, `ETB` or
|
|
4618
|
+
* `ETX` byte (`ASTM_FRAME_RESERVED_BYTE`).
|
|
4554
4619
|
* @example
|
|
4555
4620
|
* ```ts
|
|
4556
4621
|
* import { composeAstmFrames, decodeAstmFrames } from "@cosyte/astm";
|
|
@@ -4625,7 +4690,11 @@ declare function parseFramedAstm(bytes: Uint8Array, options?: FrameOptions & Ast
|
|
|
4625
4690
|
* delimiters, embedded delimiters re-escaped) and then framed **independently**
|
|
4626
4691
|
* (one record per `ETX`-closed frame run), so the framing exactly mirrors what
|
|
4627
4692
|
* {@link decodeAstmFrames} reassembles: `parseFramedAstm(serializeFramedAstm(msg))`
|
|
4628
|
-
* yields an equal message
|
|
4693
|
+
* yields an equal message **with the default `startFrameNumber`**. That clause is
|
|
4694
|
+
* load-bearing rather than pedantic: a non-default start writes a continuation of
|
|
4695
|
+
* a sequence already in progress, and a continuation read on its own opens on a
|
|
4696
|
+
* sequence gap, so the decoder does not emit its first record and this round trip
|
|
4697
|
+
* does not hold for it. See {@link ComposeFramesOptions}.
|
|
4629
4698
|
*
|
|
4630
4699
|
* A value carrying a character above `U+00FF` is **refused** here rather than
|
|
4631
4700
|
* framed: the record layer is happy to hold one, but a frame carries bytes and
|
|
@@ -4645,10 +4714,11 @@ declare function parseFramedAstm(bytes: Uint8Array, options?: FrameOptions & Ast
|
|
|
4645
4714
|
* survive being written in the canonical set this function serializes with
|
|
4646
4715
|
* (`ASTM_EMIT_TYPE_LETTER_COLLISION`). The second reaches messages parsed under a
|
|
4647
4716
|
* **different** delimiter set, since this function passes no set of its own.
|
|
4648
|
-
* @throws {@link AstmFrameEncodeError} when
|
|
4649
|
-
*
|
|
4650
|
-
* (`
|
|
4651
|
-
* `
|
|
4717
|
+
* @throws {@link AstmFrameEncodeError} when `options.startFrameNumber` is not a
|
|
4718
|
+
* whole number from `0` to `7` (`ASTM_FRAME_INVALID_START_FRAME_NUMBER`), there
|
|
4719
|
+
* are no records to frame (`ASTM_FRAME_EMPTY_RECORD`), a value holds a
|
|
4720
|
+
* character above `U+00FF` (`ASTM_FRAME_UNENCODABLE_CHARACTER`), or a record
|
|
4721
|
+
* holds an `STX`, `ETB` or `ETX` byte (`ASTM_FRAME_RESERVED_BYTE`).
|
|
4652
4722
|
* @example
|
|
4653
4723
|
* ```ts
|
|
4654
4724
|
* import { parseAstmRecords, serializeFramedAstm, parseFramedAstm } from "@cosyte/astm";
|
package/dist/index.mjs
CHANGED
|
@@ -2288,6 +2288,15 @@ function assertFramable(bytes, recordIndex) {
|
|
|
2288
2288
|
}
|
|
2289
2289
|
}
|
|
2290
2290
|
}
|
|
2291
|
+
function assertStartFrameNumber(value) {
|
|
2292
|
+
if (!Number.isInteger(value) || value < 0 || value > FRAME_NUMBER_MODULUS - 1) {
|
|
2293
|
+
throw new AstmFrameEncodeError(
|
|
2294
|
+
`startFrameNumber must be a whole number from 0 to 7: a frame's number is one ASCII digit and the sequence is modulo 8. Received: ${String(value)}.`,
|
|
2295
|
+
void 0,
|
|
2296
|
+
"ASTM_FRAME_INVALID_START_FRAME_NUMBER"
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2291
2300
|
function encodeFrame(text, frameNumber, isFinal) {
|
|
2292
2301
|
const term = isFinal ? ETX : ETB;
|
|
2293
2302
|
const bytes = [STX, FN_ZERO + frameNumber, ...text, term];
|
|
@@ -2296,11 +2305,13 @@ function encodeFrame(text, frameNumber, isFinal) {
|
|
|
2296
2305
|
return [...bytes, hex.charCodeAt(0), hex.charCodeAt(1), CR, LF];
|
|
2297
2306
|
}
|
|
2298
2307
|
function composeAstmFrames(records, options = {}) {
|
|
2308
|
+
const start = options.startFrameNumber ?? FIRST_FRAME_NUMBER;
|
|
2309
|
+
assertStartFrameNumber(start);
|
|
2299
2310
|
if (records.length === 0) {
|
|
2300
2311
|
throw new AstmFrameEncodeError("Cannot frame an empty record list: nothing to transmit.");
|
|
2301
2312
|
}
|
|
2302
2313
|
const out = [];
|
|
2303
|
-
let frameNumber =
|
|
2314
|
+
let frameNumber = start;
|
|
2304
2315
|
records.forEach((record, recordIndex) => {
|
|
2305
2316
|
const bytes = toBytes(record, recordIndex);
|
|
2306
2317
|
if (bytes.length === 0) {
|
|
@@ -2345,7 +2356,7 @@ function concatBytes3(chunks) {
|
|
|
2345
2356
|
}
|
|
2346
2357
|
|
|
2347
2358
|
// src/index.ts
|
|
2348
|
-
var VERSION = "0.0.
|
|
2359
|
+
var VERSION = "0.0.14";
|
|
2349
2360
|
|
|
2350
2361
|
export { ALL_ASTM_WARNING_CODES, AMBIGUOUS_CODES, ACK as ASTM_ACK, ENQ as ASTM_ENQ, EOT as ASTM_EOT, NAK as ASTM_NAK, AstmAmbiguousStreamError, AstmFrameEncodeError, AstmFrameStrictError, AstmParseError, AstmProfileDefinitionError, AstmSerializeError, AstmStrictError, CANONICAL_DELIMITERS, FATAL_CODES, FRAME_WARNING_CODES, LIVD_WARNING_CODES, LTP_WARNING_CODES, SAFETY_CRITICAL_CODES, TOLERABLE_CODES, VERSION, WARNING_CODES, ambiguousMessageKind, ambiguousValueSplit, applyAstmProfile, applyAstmProfileToWarnings, applyLivd, astmDateToLocalISO, astmProfiles, attachComments, buildAstmMessage, classifyMessage, comments, commentsFor, composeAstmFrames, computeChecksum, decodeAstmFrames, decodeEscapes, deepFreeze, defineAstmProfile, defineLivdCatalog, delimitersRedeclared, detectFraming, encodeComponent, fieldScalar, 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 };
|
|
2351
2362
|
//# sourceMappingURL=index.mjs.map
|