@cosyte/astm 0.0.7 → 0.0.9

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/common/errors.ts","../src/common/freeze.ts","../src/common/delimiters.ts","../src/common/dates.ts","../src/common/coding-system.ts","../src/common/warnings.ts","../src/records/result-semantics.ts","../src/records/host-query.ts","../src/profiles/apply.ts","../src/profiles/describe.ts","../src/profiles/merge.ts","../src/profiles/errors.ts","../src/frames/warnings.ts","../src/ltp/warnings.ts","../src/profiles/safety.ts","../src/profiles/validate.ts","../src/profiles/define.ts","../src/profiles/default.ts","../src/profiles/reference-corpus.ts","../src/profiles/registry.ts","../src/common/escapes.ts","../src/records/tokenize.ts","../src/records/parse.ts","../src/records/serialize.ts","../src/records/build.ts","../src/records/messages.ts","../src/records/extractors.ts","../src/profiles/index.ts","../src/terminology/catalog.ts","../src/terminology/warnings.ts","../src/terminology/apply.ts","../src/ltp/constants.ts","../src/frames/constants.ts","../src/ltp/transport.ts","../src/ltp/reducer.ts","../src/frames/checksum.ts","../src/frames/errors.ts","../src/frames/decode.ts","../src/frames/encode.ts","../src/frames/compose.ts","../src/index.ts"],"names":["line","next","at","text","concatBytes"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Fatal error taxonomy for the `@cosyte/astm` record parser.\n *\n * Tier-3 **fatal** codes mark input the parser cannot recover into a structured\n * `AstmMessage`; anything less severe is a Tier-2 warning (see `./warnings.ts`).\n * `AstmParseError` is thrown directly and consumers narrow via the `code`\n * discriminant. {@link FatalCode} is **closed**: these three are all of it. The frame\n * codec reuses `EMPTY_INPUT` (an empty stream is fatal in both its lenient and strict\n * modes) but does not widen the union, its own thrown errors are separate types with\n * their own discriminants: {@link AstmFrameEncodeError} carries\n * `ASTM_FRAME_EMPTY_RECORD`, and {@link AstmFrameStrictError} carries the rejected\n * warnings rather than a `code`. So narrowing an `AstmParseError` on `code` will only\n * ever see a value from this set.\n */\n\nimport type { AstmPosition } from \"./position.js\";\n\n/**\n * Stable string codes for every Tier-3 fatal the record parser may throw.\n * Consumers narrow on `err.code` to react to specific structural failures.\n * Renaming a code is a **breaking change**.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, FATAL_CODES, AstmParseError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"\");\n * } catch (err) {\n * if (err instanceof AstmParseError && err.code === FATAL_CODES.EMPTY_INPUT) {\n * // handle empty input\n * }\n * }\n * ```\n */\nexport const FATAL_CODES = {\n /** Input was empty or whitespace-only: there is nothing to parse. Shared across layers. */\n EMPTY_INPUT: \"EMPTY_INPUT\",\n /** The first record is not an `H` (header) record: an ASTM message must lead with `H`. */\n ASTM_RECORD_NO_HEADER: \"ASTM_RECORD_NO_HEADER\",\n /** The `H` record is too short to declare the four delimiters (field/repeat/component/escape). */\n ASTM_RECORD_UNDECLARED_DELIMITERS: \"ASTM_RECORD_UNDECLARED_DELIMITERS\",\n} as const;\n\n/**\n * A value from {@link FATAL_CODES}: the type carried by a thrown {@link AstmParseError}.\n */\nexport type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];\n\n/**\n * Thrown by {@link parseAstmRecords} when the input violates one of the Tier-3\n * unrecoverable structural rules (empty input, no leading `H`, or an `H` that\n * cannot declare its delimiters). Carries positional context so consumers can\n * log an actionable error.\n *\n * **PHI:** the error carries a stable code + position only, never a field value.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, AstmParseError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"P|1\");\n * } catch (err) {\n * if (err instanceof AstmParseError && err.code === \"ASTM_RECORD_NO_HEADER\") {\n * // err.position is available; err carries no field value\n * }\n * }\n * ```\n */\nexport class AstmParseError extends Error {\n public readonly code: FatalCode;\n public readonly position: AstmPosition;\n\n /**\n * Construct a new `AstmParseError`. Both fields are required so every thrower\n * populates positional context and no PHI-bearing snippet is ever attached.\n *\n * @internal\n */\n public constructor(code: FatalCode, message: string, position: AstmPosition) {\n super(message);\n this.name = \"AstmParseError\";\n this.code = code;\n this.position = position;\n }\n}\n","/**\n * Deep-freeze helper for the immutable ASTM model.\n *\n * The parser returns plain readonly data; `deepFreeze` makes that immutability\n * enforced at runtime (a stray `msg.records[0].fields[0] = …` throws in strict\n * mode and is a no-op otherwise), which the shared `immutabilityProperty`\n * invariant asserts. Zero-dep, Node stdlib only.\n */\n\n/**\n * Recursively freeze an object graph (objects and arrays), returning the same\n * reference typed as deeply readonly. Cyclic graphs are not produced by the\n * parser, so a simple recursive walk suffices.\n *\n * @param value - The value to freeze in place.\n * @returns The same value, now deeply frozen.\n * @example\n * ```ts\n * import { deepFreeze } from \"@cosyte/astm\";\n * const frozen = deepFreeze({ a: [1, 2] });\n * Object.isFrozen(frozen.a); // true\n * ```\n */\nexport function deepFreeze<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (Object.isFrozen(value)) return value;\n Object.freeze(value);\n for (const key of Object.keys(value)) {\n deepFreeze((value as Record<string, unknown>)[key]);\n }\n return value;\n}\n","/**\n * The ASTM delimiter model and its self-declaration reader.\n *\n * ASTM/CLSI-LIS02 messages are **self-describing**: every `H` (header) record\n * declares the four delimiters it uses, in the bytes immediately after the `H`.\n * A parser MUST read them from the record and MUST NOT hardcode them: a vendor\n * is free to declare non-standard delimiters and a conformant reader follows.\n *\n * The canonical declaration is `H|\\^&`:\n *\n * | position | char | role |\n * |--------------------------|------|-------------------------|\n * | immediately after `H` | `\\|` | **field** delimiter |\n * | delimiter-definition [0] | `\\` | **repeat** delimiter |\n * | delimiter-definition [1] | `^` | **component** delimiter |\n * | delimiter-definition [2] | `&` | **escape** delimiter |\n *\n * Note ASTM differs from HL7 v2 here: ASTM's `\\` is the **repeat** delimiter and\n * `&` is the **escape** delimiter, where HL7 uses `\\` for escape and `~` for\n * repetition. Getting this mapping wrong silently mis-reads values, so the roles\n * are read positionally from the record, never assumed.\n */\n\n/**\n * The four ASTM delimiters resolved from an `H` record. Immutable; carried on\n * the parsed message as delimiter provenance.\n *\n * @example\n * ```ts\n * import { readDelimiters } from \"@cosyte/astm\";\n * const d = readDelimiters(\"H|\\\\^&\");\n * d.field; // \"|\"\n * d.repeat; // \"\\\\\"\n * d.component; // \"^\"\n * d.escape; // \"&\"\n * ```\n */\nexport interface Delimiters {\n /** Field separator: the char immediately after `H` in the header. */\n readonly field: string;\n /** Repeat (repetition) separator: ASTM `\\` by default. */\n readonly repeat: string;\n /** Component separator: ASTM `^` by default. */\n readonly component: string;\n /** Escape character: ASTM `&` by default (introduces `&F&`/`&S&`/`&R&`/`&E&`). */\n readonly escape: string;\n}\n\n/**\n * The canonical ASTM delimiter set (`H|\\^&`). Used only as documentation and as\n * a comparison baseline for the non-standard-delimiter warning: never as a\n * parse-time default (delimiters are always read from the header).\n *\n * @example\n * ```ts\n * import { CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * CANONICAL_DELIMITERS.repeat; // \"\\\\\"\n * ```\n */\nexport const CANONICAL_DELIMITERS: Delimiters = {\n field: \"|\",\n repeat: \"\\\\\",\n component: \"^\",\n escape: \"&\",\n};\n\n/** The result of reading delimiters from a header record: either resolved or a declared failure. */\nexport type DelimiterReadResult =\n | { readonly ok: true; readonly delimiters: Delimiters }\n | { readonly ok: false };\n\n/**\n * Read the four delimiters from a header record's raw text (a single `H`\n * record, its terminator already stripped).\n *\n * Returns `undefined` when the record cannot declare all four delimiters: it is\n * shorter than `H` + a field separator + a 3-char delimiter definition, or the\n * field separator it names also appears among the other three.\n *\n * What the caller does with that depends on **which** header it is. On the first\n * header it is the `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal, because there is no\n * earlier set to fall back to. On any later header (a stream may carry several\n * messages, each declaring its own set) the delimiters already in force are kept\n * and an `ASTM_RECORD_UNREADABLE_REDECLARATION` warning is raised instead; a set is\n * never guessed and no record is dropped.\n *\n * This function does not throw: delimiter resolution and the escalation decision are\n * kept separate so the reader stays pure and testable.\n *\n * @param headerRecord - The raw `H` record text (no trailing CR/LF).\n * @returns The resolved delimiters, or a declared failure.\n * @example\n * ```ts\n * import { readDelimiters } from \"@cosyte/astm\";\n * const d = readDelimiters(\"H|\\\\^&|||sender\");\n * d.field; // \"|\"\n * ```\n */\nexport function readDelimiters(headerRecord: string): Delimiters | undefined {\n // Need \"H\" + field-sep + at least the 3-char delimiter definition (repeat/component/escape).\n if (headerRecord.length < 5) return undefined;\n if (headerRecord.charAt(0) !== \"H\") return undefined;\n\n const field = headerRecord.charAt(1);\n // The delimiter-definition field runs from index 2 up to the next field separator.\n const defEnd = headerRecord.indexOf(field, 2);\n const definition = headerRecord.slice(2, defEnd === -1 ? headerRecord.length : defEnd);\n if (definition.length < 3) return undefined;\n\n const repeat = definition.charAt(0);\n const component = definition.charAt(1);\n const escape = definition.charAt(2);\n\n // A field separator that also appears among the other three delimiters is not a coherent\n // declaration: the four roles must be distinguishable. Refuse rather than mis-split.\n if (field === repeat || field === component || field === escape) return undefined;\n\n return { field, repeat, component, escape };\n}\n\n/**\n * Whether a resolved delimiter set differs from the canonical `H|\\^&`. A `true`\n * result is a tolerated vendor quirk, surfaced as a value-free warning so a\n * consumer can notice a non-standard stream without the parser refusing it.\n *\n * @example\n * ```ts\n * import { isNonStandard, readDelimiters } from \"@cosyte/astm\";\n * isNonStandard(readDelimiters(\"H|\\\\^&\")!); // false\n * ```\n */\nexport function isNonStandard(d: Delimiters): boolean {\n return (\n d.field !== CANONICAL_DELIMITERS.field ||\n d.repeat !== CANONICAL_DELIMITERS.repeat ||\n d.component !== CANONICAL_DELIMITERS.component ||\n d.escape !== CANONICAL_DELIMITERS.escape\n );\n}\n","/**\n * The ASTM date/time value: `YYYYMMDDHHMMSS`, precision-preserving, no timezone.\n *\n * ASTM timestamps are a run of digits, most-significant first, truncated at\n * whatever precision the instrument reports. There is **no timezone field**:\n * the value is local to the instrument and this parser never assumes UTC. A\n * date-only value (`YYYYMMDD`) is a normal, complete value at day precision:\n * **not** an error and never zero-filled into a fake time.\n */\n\n/** The precision to which an {@link AstmDate} is populated. */\nexport type AstmDatePrecision = \"year\" | \"month\" | \"day\" | \"hour\" | \"minute\" | \"second\";\n\n/**\n * A parsed ASTM date/time. Immutable plain data; the populated fields extend\n * exactly as far as {@link AstmDate.precision}. Absent components are left\n * `undefined` rather than defaulted, so a consumer can tell \"midnight\" from\n * \"no time given\". No timezone is modeled: the value is instrument-local.\n *\n * @example\n * ```ts\n * import { parseAstmDate } from \"@cosyte/astm\";\n * const d = parseAstmDate(\"20240315\");\n * d?.precision; // \"day\"\n * d?.hour; // undefined (not 0)\n * ```\n */\nexport interface AstmDate {\n /** The raw digit string as it appeared on the wire. */\n readonly raw: string;\n readonly year: number;\n readonly month?: number;\n readonly day?: number;\n readonly hour?: number;\n readonly minute?: number;\n readonly second?: number;\n /** How far the components are populated. */\n readonly precision: AstmDatePrecision;\n /**\n * `true` when the digit run does **not** align to a whole-component boundary: an odd number of\n * digits that cuts a two-digit component (month/day/hour/minute/second) in half (lengths 5, 7, 9,\n * 11, 13). The full run is preserved in {@link AstmDate.raw} and the structured value is truncated\n * to the last **complete** component: the dangling digit is **never zero-filled into a fabricated\n * time**. Absent (never `false`) for a clean value. A caller surfaces this as a value-free\n * `ASTM_RECORD_PARTIAL_TIMESTAMP` warning.\n */\n readonly truncated?: true;\n}\n\n/**\n * Parse an ASTM `YYYYMMDDHHMMSS` value, preserving whatever precision is\n * present. Returns `undefined` for a value that is not a usable timestamp (fewer\n * than four leading digits for the year, or non-digit content): a caller keeps\n * the raw field text either way, so nothing is lost. A partial value is parsed,\n * never rejected.\n *\n * Extra trailing digits beyond seconds (fractional seconds some vendors append)\n * are ignored for the structured value; the full input remains in {@link AstmDate.raw}.\n *\n * @param raw - The raw field text.\n * @returns The parsed date, or `undefined` when it is not a timestamp.\n * @example\n * ```ts\n * import { parseAstmDate } from \"@cosyte/astm\";\n * parseAstmDate(\"20240315093000\")?.precision; // \"second\"\n * parseAstmDate(\"202403\")?.precision; // \"month\"\n * ```\n */\nexport function parseAstmDate(raw: string): AstmDate | undefined {\n const s = raw.trim();\n if (!/^\\d{4,}$/u.test(s)) return undefined;\n\n const year = Number(s.slice(0, 4));\n // An odd length below the full 14 digits cuts a two-digit component in half: the value is\n // truncated mid-component. The raw run is preserved verbatim; the dangling digit is dropped from\n // the structured value rather than zero-filled into a fabricated time, and the flag lets a caller\n // warn. Extra digits beyond 14 (fractional seconds some vendors append) are not \"truncated\".\n const truncated = s.length < 14 && s.length % 2 === 1;\n const base = { raw: s, year, ...(truncated ? { truncated: true as const } : {}) };\n\n if (s.length < 6) return { ...base, precision: \"year\" };\n const month = Number(s.slice(4, 6));\n if (s.length < 8) return { ...base, month, precision: \"month\" };\n const day = Number(s.slice(6, 8));\n if (s.length < 10) return { ...base, month, day, precision: \"day\" };\n const hour = Number(s.slice(8, 10));\n if (s.length < 12) return { ...base, month, day, hour, precision: \"hour\" };\n const minute = Number(s.slice(10, 12));\n if (s.length < 14) return { ...base, month, day, hour, minute, precision: \"minute\" };\n const second = Number(s.slice(12, 14));\n return { ...base, month, day, hour, minute, second, precision: \"second\" };\n}\n\n/**\n * Render an {@link AstmDate} as an ISO-8601-*like* string truncated to its\n * precision, with **no** `Z` and **no** offset: because ASTM carries no\n * timezone and appending one would fabricate information. A consumer that knows\n * the instrument's zone can attach it; this function never assumes UTC.\n *\n * @param d - The date to render.\n * @returns e.g. `\"2024-03-15T09:30\"` (minute precision) or `\"2024-03\"` (month).\n * @example\n * ```ts\n * import { astmDateToLocalISO, parseAstmDate } from \"@cosyte/astm\";\n * astmDateToLocalISO(parseAstmDate(\"20240315\")!); // \"2024-03-15\"\n * ```\n */\nexport function astmDateToLocalISO(d: AstmDate): string {\n const p2 = (n: number): string => String(n).padStart(2, \"0\");\n let out = String(d.year).padStart(4, \"0\");\n if (d.month !== undefined) out += `-${p2(d.month)}`;\n if (d.day !== undefined) out += `-${p2(d.day)}`;\n if (d.hour !== undefined) out += `T${p2(d.hour)}`;\n if (d.minute !== undefined) out += `:${p2(d.minute)}`;\n if (d.second !== undefined) out += `:${p2(d.second)}`;\n return out;\n}\n","/**\n * Code-system **provenance recognition** for the ASTM Universal Test ID.\n *\n * ASTM carries a test identifier as a caret-component field (`^^^687`): the\n * spec's component order is LOINC-slot / test-name / coding-scheme / local-code.\n * In the field the way real analyzers emit it:\n *\n * - **component 1** is a LOINC slot: *almost always empty*; a few vendors place\n * an inline LOINC when one exists;\n * - **component 4** is the vendor/local code: the identifier that is actually\n * present, and therefore the **primary** identifier here.\n *\n * Following the sibling parsers' conservative posture, this module is a\n * **structural recognizer, not a dictionary**: it tags where a code came from\n * (its provenance) and surfaces every code verbatim. It does not look codes up,\n * does not validate them, and bundles no LOINC/SNOMED tables.\n */\n\n/** Where a Universal Test ID's usable identifier came from. */\nexport type UniversalTestIdProvenance =\n /** Component 1 (the LOINC slot) is populated: a candidate LOINC, recognized not validated. */\n | \"inline-loinc-candidate\"\n /** Component 4 (the vendor/local code) carries the identifier. */\n | \"local-code\"\n /** Only the test name (component 2) is present: no code. */\n | \"name-only\"\n /** Nothing usable in the field. */\n | \"empty\";\n\n/**\n * A recognized ASTM Universal Test ID. All components are surfaced verbatim\n * (already escape-decoded by the tokenizer); nothing is looked up.\n *\n * @example\n * ```ts\n * import { recognizeUniversalTestId } from \"@cosyte/astm\";\n * const u = recognizeUniversalTestId([\"\", \"\", \"\", \"687\"]);\n * u.localCode; // \"687\"\n * u.provenance; // \"local-code\"\n * ```\n */\nexport interface UniversalTestId {\n /** The field's components, verbatim and in order. */\n readonly components: readonly string[];\n /** Component 1 when populated: a *candidate* LOINC (provenance only, never validated). */\n readonly loincCandidate?: string;\n /** Component 2: the test / battery name, when present. */\n readonly testName?: string;\n /** Component 3: the coding-scheme selector, when present. */\n readonly codingScheme?: string;\n /** Component 4, the vendor/local code: the primary identifier when no inline LOINC is given. */\n readonly localCode?: string;\n /** Where the primary identifier came from. */\n readonly provenance: UniversalTestIdProvenance;\n}\n\n/**\n * Recognize a Universal Test ID from a field's already-decoded components.\n *\n * @param components - The component strings (escape-decoded), in order.\n * @returns The recognized, provenance-tagged Universal Test ID.\n * @example\n * ```ts\n * import { recognizeUniversalTestId, primaryCode } from \"@cosyte/astm\";\n * primaryCode(recognizeUniversalTestId([\"\", \"Glucose\", \"L\", \"687\"])); // \"687\"\n * ```\n */\nexport function recognizeUniversalTestId(components: readonly string[]): UniversalTestId {\n const nonEmpty = (i: number): string | undefined => {\n const c = components[i];\n return c !== undefined && c.length > 0 ? c : undefined;\n };\n const loincCandidate = nonEmpty(0);\n const testName = nonEmpty(1);\n const codingScheme = nonEmpty(2);\n const localCode = nonEmpty(3);\n\n let provenance: UniversalTestIdProvenance;\n if (loincCandidate !== undefined) provenance = \"inline-loinc-candidate\";\n else if (localCode !== undefined) provenance = \"local-code\";\n else if (testName !== undefined) provenance = \"name-only\";\n else provenance = \"empty\";\n\n const base = { components, provenance };\n return {\n ...base,\n ...(loincCandidate !== undefined ? { loincCandidate } : {}),\n ...(testName !== undefined ? { testName } : {}),\n ...(codingScheme !== undefined ? { codingScheme } : {}),\n ...(localCode !== undefined ? { localCode } : {}),\n };\n}\n\n/**\n * The primary code to key a result on: the inline LOINC candidate when a vendor\n * supplied one, otherwise the local (vendor) code. Returns `undefined` when the\n * field carries no code at all (name-only or empty): never a guess.\n *\n * @param u - A recognized Universal Test ID.\n * @returns The primary code, or `undefined`.\n * @example\n * ```ts\n * import { primaryCode, recognizeUniversalTestId } from \"@cosyte/astm\";\n * primaryCode(recognizeUniversalTestId([\"2345-7\", \"Glucose\", \"LN\", \"687\"])); // \"2345-7\"\n * ```\n */\nexport function primaryCode(u: UniversalTestId): string | undefined {\n return u.loincCandidate ?? u.localCode;\n}\n","/**\n * Tier-2 warning registry and factories for the `@cosyte/astm` record parser.\n *\n * A warning is the lenient parser's record of a tolerated deviation: it never\n * throws, never drops data, and never fabricates a value. Consumers compare\n * `warning.code === WARNING_CODES.<CODE>` to react; renaming a code is a\n * **breaking change**. Every warning carries a stable code plus a\n * {@link AstmPosition} and **never** a field value (PHI discipline).\n */\n\nimport type { AstmPosition } from \"./position.js\";\n\n/**\n * Stable string codes for every Tier-2 warning the record parser may emit.\n * `key === value` so `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, WARNING_CODES } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rL|1\\r\");\n * msg.warnings.some((w) => w.code === WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE);\n * ```\n */\nexport const WARNING_CODES = {\n /** A record's type letter is not one of the modeled types: surfaced as an unsupported record. */\n ASTM_RECORD_UNKNOWN_TYPE: \"ASTM_RECORD_UNKNOWN_TYPE\",\n /** The header declared delimiters other than the canonical `H|\\^&`: tolerated, noted. */\n ASTM_NONSTANDARD_DELIMITERS: \"ASTM_NONSTANDARD_DELIMITERS\",\n /** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&`: preserved verbatim. */\n ASTM_UNKNOWN_ESCAPE_SEQUENCE: \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\",\n /**\n * A result value field carried an *unescaped* component delimiter, so it split into more than one\n * component. Both the full raw value and the split are surfaced and this warning fires: the\n * ambiguity is never resolved silently into a truncated value.\n */\n ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT: \"ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT\",\n /**\n * A result's abnormal-flag field (`R` field 7) carried a letter outside HL7 Table 0078. The flag is\n * surfaced as `undefined`: never dropped, and **never coerced to `normal`** (a clinical error).\n */\n ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG: \"ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG\",\n /**\n * A result's status field (`R` field 9) carried a letter that is not a recognized status. It is\n * surfaced as `undefined` and, like every non-`F` status, never reads as active-final.\n */\n ASTM_RECORD_UNDEFINED_RESULT_STATUS: \"ASTM_RECORD_UNDEFINED_RESULT_STATUS\",\n /**\n * A result's reference-range field (`R` field 6) did not match a recognized form (`low-high`,\n * `<high`, `>low`). The text is surfaced verbatim as `unparsed`: a bound is **never fabricated**.\n */\n ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE: \"ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE\",\n /**\n * A result carried a numeric value but no units (`R` field 5 empty). Units are vendor free text\n * (not UCUM); a missing unit is flagged here and **never defaulted, guessed, or converted**.\n */\n ASTM_RECORD_UNITS_ABSENT: \"ASTM_RECORD_UNITS_ABSENT\",\n /**\n * A `C` (comment) record had no valid preceding `H`/`P`/`O`/`R` parent: an **orphan**. The comment\n * is attached to the message root (`attachedToRoot: true`) and surfaced, **never dropped**.\n */\n ASTM_RECORD_ORPHAN_COMMENT: \"ASTM_RECORD_ORPHAN_COMMENT\",\n /**\n * A `YYYYMMDDHHMMSS` timestamp had an odd digit run that truncates a two-digit component in half\n * (e.g. a partial day/hour). The raw run is preserved and the structured value stops at the last\n * **complete** component: the dangling digit is **never zero-filled into a fabricated time**.\n */\n ASTM_RECORD_PARTIAL_TIMESTAMP: \"ASTM_RECORD_PARTIAL_TIMESTAMP\",\n /**\n * A `Q` (request-information) record carried a request-information status code (field 13). The code\n * *set* is `[OSS-derived / paywalled]` with no publicly-groundable enumeration, so the parser\n * interprets **none** of them: the status is surfaced verbatim and this value-free warning flags that\n * it was passed through **uninterpreted**, never mapped to a guessed meaning.\n */\n ASTM_RECORD_UNINTERPRETED_QUERY_STATUS: \"ASTM_RECORD_UNINTERPRETED_QUERY_STATUS\",\n /**\n * A message carried **both** a `Q` (request) and an `R` (result) record: a contradictory shape. The\n * message is classified `host-query` (the `Q` **dominates**, so it is never read as a result set) and\n * this warning flags the anomaly. Positional context only; no field value.\n */\n ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND: \"ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND\",\n /**\n * A later `H` (header) record declared a **different** delimiter set from the one in force, and the\n * parser **followed it**: that header and every record after it are read with the newly-declared\n * delimiters, until the next `H`. Records already read keep the set that was in force when they were\n * read: a redeclaration never reinterprets bytes that have already been consumed.\n *\n * A stream carrying several messages back to back is ordinary, and a header that simply **repeats**\n * the delimiters already in force is a no-op that warns nothing. This code fires only when the set\n * actually changes, because that is the point at which a reader still using the old set would begin\n * merging fields together.\n */\n ASTM_RECORD_DELIMITERS_REDECLARED: \"ASTM_RECORD_DELIMITERS_REDECLARED\",\n /**\n * A later `H` (header) record could not declare a usable delimiter set: it was too short, or the\n * field separator it named also appeared among the other three, leaving the four roles\n * indistinguishable. The delimiters **already in force are kept** and every record is still surfaced;\n * a set is never guessed and no record is dropped.\n *\n * The same condition on the *first* header is unrecoverable and remains the\n * `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal: there is no earlier set to fall back to.\n */\n ASTM_RECORD_UNREADABLE_REDECLARATION: \"ASTM_RECORD_UNREADABLE_REDECLARATION\",\n /**\n * The downgraded form an active vendor {@link AstmProfile} produces from a deviation it *expects*\n * (see `src/profiles/`). The original warning is **never dropped**: its code moves to\n * {@link AstmRecordWarning.toleratedCode}, the warning is re-badged `PROFILE_QUIRK_APPLIED` with\n * `expected: true` and the tolerating profile named, so a consumer can filter known, grounded noise\n * while the fact of the deviation, and where it was, survives. A profile can only ever reach this\n * path for a **non-safety-critical** code (enforced at profile-definition time); a safety-critical\n * deviation (a result value, flag, status, patient identifier, code system, or a frame-integrity\n * warning) can **never** be tolerated, so it can never be re-badged here.\n */\n PROFILE_QUIRK_APPLIED: \"PROFILE_QUIRK_APPLIED\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmRecordWarning.code}. Narrowing by this code\n * lets consumers write exhaustive `switch` blocks and guarantees a typo-free\n * comparison against {@link WARNING_CODES}.\n */\nexport type WarningCode = (typeof WARNING_CODES)[keyof typeof WARNING_CODES];\n\n/**\n * A single Tier-2 warning: a stable code, a value-free human-readable message,\n * and positional context. Plain data, accumulated onto `AstmMessage.warnings`.\n *\n * @example\n * ```ts\n * import type { AstmRecordWarning } from \"@cosyte/astm\";\n * const w: AstmRecordWarning = {\n * code: \"ASTM_RECORD_UNKNOWN_TYPE\",\n * message: \"Unknown record type.\",\n * position: { recordIndex: 2, recordType: \"Z\" },\n * };\n * ```\n */\nexport interface AstmRecordWarning {\n readonly code: WarningCode;\n /** Human-readable detail for logs. Never contains a field value. */\n readonly message: string;\n readonly position: AstmPosition;\n /**\n * `true` when an active vendor {@link AstmProfile} *expected* this deviation and re-badged it as a\n * {@link WARNING_CODES.PROFILE_QUIRK_APPLIED}. An `expected` warning does **not** escalate to a\n * thrown `AstmStrictError` in strict mode (the whole point of the profile is that this deviation is\n * known and benign): it is still recorded, so nothing is hidden. Absent on an untolerated warning.\n */\n readonly expected?: boolean;\n /** The name of the {@link AstmProfile} that tolerated this warning, when `expected`. */\n readonly profile?: string;\n /**\n * When `code` is {@link WARNING_CODES.PROFILE_QUIRK_APPLIED}, the original warning code the profile\n * tolerated, so a consumer can still see *which* deviation was re-badged as expected.\n */\n readonly toleratedCode?: WarningCode;\n}\n\n/**\n * Build an `ASTM_RECORD_UNKNOWN_TYPE` warning. The record is still surfaced (as\n * an unsupported record), never dropped.\n *\n * @example\n * ```ts\n * import { unknownRecordType } from \"@cosyte/astm\";\n * unknownRecordType({ recordIndex: 3, recordType: \"Z\" });\n * ```\n */\nexport function unknownRecordType(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE,\n message: \"Unrecognized record type, surfaced verbatim as an unsupported record.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_NONSTANDARD_DELIMITERS` warning. The declared delimiters are\n * used as-is; this only flags that they differ from the canonical set.\n *\n * @example\n * ```ts\n * import { nonStandardDelimiters } from \"@cosyte/astm\";\n * nonStandardDelimiters({ recordIndex: 0, recordType: \"H\" });\n * ```\n */\nexport function nonStandardDelimiters(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS,\n message: \"Header declared non-canonical delimiters, read from the header and honored.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_DELIMITERS_REDECLARED` warning. Emitted when a later `H`\n * record declares a delimiter set different from the one in force; the new set is\n * honored from that header onward. Positional context only: never the delimiters\n * themselves.\n *\n * @example\n * ```ts\n * import { delimitersRedeclared } from \"@cosyte/astm\";\n * delimitersRedeclared({ recordIndex: 5, recordType: \"H\" });\n * ```\n */\nexport function delimitersRedeclared(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_DELIMITERS_REDECLARED,\n message:\n \"A later header declared different delimiters, honored from that header onward; earlier records keep the set they were read with.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNREADABLE_REDECLARATION` warning. Emitted when a later\n * `H` record cannot declare a usable delimiter set; the set already in force is\n * kept and every record is still surfaced.\n *\n * @example\n * ```ts\n * import { unreadableRedeclaration } from \"@cosyte/astm\";\n * unreadableRedeclaration({ recordIndex: 2, recordType: \"H\" });\n * ```\n */\nexport function unreadableRedeclaration(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNREADABLE_REDECLARATION,\n message:\n \"A later header could not declare a usable delimiter set, the delimiters already in force were kept.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_UNKNOWN_ESCAPE_SEQUENCE` warning. The sequence is preserved\n * verbatim in the decoded value; the warning body carries neither the sequence\n * nor its surrounding text.\n *\n * @example\n * ```ts\n * import { unknownEscapeSequence } from \"@cosyte/astm\";\n * unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 4 });\n * ```\n */\nexport function unknownEscapeSequence(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE,\n message: \"Unrecognized escape sequence preserved verbatim.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT` warning. Emitted when a result\n * value field split on an unescaped component delimiter: the full raw value and\n * the split are both surfaced, never a silent truncation.\n *\n * @example\n * ```ts\n * import { ambiguousValueSplit } from \"@cosyte/astm\";\n * ambiguousValueSplit({ recordIndex: 3, recordType: \"R\", fieldIndex: 4 });\n * ```\n */\nexport function ambiguousValueSplit(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT,\n message:\n \"Result value contained an unescaped component delimiter, full raw value and split both surfaced.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG` warning. The flag is surfaced as\n * `undefined` (never coerced to `normal`); the warning carries only the position.\n *\n * @example\n * ```ts\n * import { undefinedAbnormalFlag } from \"@cosyte/astm\";\n * undefinedAbnormalFlag({ recordIndex: 4, recordType: \"R\", fieldIndex: 7 });\n * ```\n */\nexport function undefinedAbnormalFlag(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG,\n message: \"Abnormal flag is not in HL7 Table 0078, surfaced as undefined, never as normal.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNDEFINED_RESULT_STATUS` warning. The status is surfaced\n * as `undefined` and, like every non-final status, never reads as active-final.\n *\n * @example\n * ```ts\n * import { undefinedResultStatus } from \"@cosyte/astm\";\n * undefinedResultStatus({ recordIndex: 4, recordType: \"R\", fieldIndex: 9 });\n * ```\n */\nexport function undefinedResultStatus(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNDEFINED_RESULT_STATUS,\n message: \"Result status is not a recognized status letter, surfaced as undefined.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE` warning. The range text is\n * surfaced verbatim as `unparsed`; no bound is fabricated.\n *\n * @example\n * ```ts\n * import { unparseableReferenceRange } from \"@cosyte/astm\";\n * unparseableReferenceRange({ recordIndex: 4, recordType: \"R\", fieldIndex: 6 });\n * ```\n */\nexport function unparseableReferenceRange(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE,\n message:\n \"Reference range did not match a recognized form, surfaced verbatim, no bound invented.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNITS_ABSENT` warning. Emitted when a result carries a\n * numeric value but no units; units are never defaulted, guessed, or converted.\n *\n * @example\n * ```ts\n * import { unitsAbsent } from \"@cosyte/astm\";\n * unitsAbsent({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * ```\n */\nexport function unitsAbsent(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNITS_ABSENT,\n message: \"Numeric result value carried no units, never defaulted, guessed, or converted.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_ORPHAN_COMMENT` warning. Emitted when a `C` record had no\n * valid preceding `H`/`P`/`O`/`R` parent; the comment is attached to the message\n * root and surfaced, never dropped.\n *\n * @example\n * ```ts\n * import { orphanComment } from \"@cosyte/astm\";\n * orphanComment({ recordIndex: 5, recordType: \"C\" });\n * ```\n */\nexport function orphanComment(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_ORPHAN_COMMENT,\n message: \"Comment had no valid preceding parent, attached to the message root, never dropped.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_PARTIAL_TIMESTAMP` warning. Emitted when a\n * `YYYYMMDDHHMMSS` value had an odd digit run that truncates a component; the raw\n * run is preserved and no time is fabricated.\n *\n * @example\n * ```ts\n * import { partialTimestamp } from \"@cosyte/astm\";\n * partialTimestamp({ recordIndex: 2, recordType: \"P\", fieldIndex: 8 });\n * ```\n */\nexport function partialTimestamp(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_PARTIAL_TIMESTAMP,\n message: \"Timestamp digit run truncates a component, preserved verbatim, never zero-filled.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS` warning. Emitted when a `Q`\n * record carries a request-information status code; the code set is paywalled, so\n * the status is surfaced verbatim and never mapped to a guessed meaning.\n *\n * @example\n * ```ts\n * import { uninterpretedQueryStatus } from \"@cosyte/astm\";\n * uninterpretedQueryStatus({ recordIndex: 2, recordType: \"Q\", fieldIndex: 13 });\n * ```\n */\nexport function uninterpretedQueryStatus(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNINTERPRETED_QUERY_STATUS,\n message:\n \"Query request-information status surfaced verbatim, code set paywalled, never interpreted.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND` warning. Emitted when a message\n * carries both a `Q` (request) and an `R` (result) record; the message is\n * classified as a host-query request (the `Q` dominates) and the anomaly is\n * flagged.\n *\n * @example\n * ```ts\n * import { ambiguousMessageKind } from \"@cosyte/astm\";\n * ambiguousMessageKind({ recordIndex: 0, recordType: \"H\" });\n * ```\n */\nexport function ambiguousMessageKind(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND,\n message:\n \"Message carried both a Q (request) and an R (result) record, classified host-query; Q dominates.\",\n position,\n };\n}\n\n/**\n * Build a `PROFILE_QUIRK_APPLIED` warning: the downgraded form an active vendor profile produces from\n * a deviation it *expects*. The original warning is **not dropped**: its `code` moves to\n * `toleratedCode`, the warning is re-badged `PROFILE_QUIRK_APPLIED`, `expected` is set, and the\n * tolerating profile is named. The original `position` and `message` are preserved (both PHI-free by\n * the same construction as every other factory), so a consumer can filter known, grounded noise while\n * the fact of the deviation, and where it was, survive. A profile can only ever reach this path for a\n * **non-safety-critical** code (enforced at profile-definition time by the safety gate).\n *\n * @param original - The warning the profile tolerated.\n * @param profileName - The name of the tolerating profile.\n * @returns The re-badged, still-informative warning.\n * @example\n * ```ts\n * import { profileQuirkApplied, unknownEscapeSequence } from \"@cosyte/astm\";\n * const original = unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * const w = profileQuirkApplied(original, \"referenceCorpus\");\n * w.code; // \"PROFILE_QUIRK_APPLIED\"\n * w.toleratedCode; // \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"\n * ```\n */\nexport function profileQuirkApplied(\n original: AstmRecordWarning,\n profileName: string,\n): AstmRecordWarning {\n return {\n code: WARNING_CODES.PROFILE_QUIRK_APPLIED,\n message: `Profile \"${profileName}\" expected ${original.code}: ${original.message}`,\n position: original.position,\n expected: true,\n profile: profileName,\n toleratedCode: original.code,\n };\n}\n","/**\n * Safety-critical result semantics for the ASTM `R` (result) record.\n *\n * The parser surfaces the `R` record's flag, status, units, and reference-range\n * fields **raw**. This module turns those raw letters into modeled, **fail-safe**\n * semantics, under one rule: **never a confident wrong value.**\n *\n * - An **abnormal flag** (field 7) is recognized against HL7 Table 0078; an\n * unrecognized flag is surfaced as `undefined` (with a warning) and is **never\n * coerced to `normal`**.\n * - A **result status** (field 9) is modeled so a **correction (`C`)** or a\n * **cancellation (`X`)** can never read as an active-final result, and an\n * **absent** status is a typed `unspecified`: **never assumed `F` (final)**.\n * - A **reference range** (field 6) is parsed into a low/high (or open-ended)\n * pair; an unparseable range is surfaced **verbatim**, a bound is **never\n * fabricated**.\n *\n * Every bound and code is surfaced verbatim; nothing is collapsed, reconciled,\n * or converted. Units are handled at the record layer (a numeric value without\n * units warns; units are never defaulted, guessed, or converted, see\n * `./parse.ts`).\n */\n\n/* ────────────────────────────── abnormal flags ────────────────────────────── */\n\n/**\n * The recognized abnormal-flag letters, from **HL7 v2.2 Table 0078** (the flag\n * *value* set, a published fact set, not CLSI prose). Read in the context of an\n * `R` record's field 7: here `S`/`R`/`I` are the microbiology susceptibility\n * codes and `R` is **not** the repeat delimiter.\n */\nexport type AbnormalFlagCode =\n | \"L\"\n | \"H\"\n | \"LL\"\n | \"HH\"\n | \"<\"\n | \">\"\n | \"N\"\n | \"A\"\n | \"AA\"\n | \"U\"\n | \"D\"\n | \"B\"\n | \"W\"\n | \"S\"\n | \"R\"\n | \"I\";\n\n/**\n * The modeled meaning of a recognized {@link AbnormalFlagCode}, plus the two\n * fail-safe sentinels: `undefined` (a flag was present but is not in Table 0078),\n * which is **never** collapsed to `normal`.\n */\nexport type AbnormalFlagMeaning =\n | \"below-normal\"\n | \"above-normal\"\n | \"critically-below-normal\"\n | \"critically-above-normal\"\n | \"below-scale\"\n | \"above-scale\"\n | \"normal\"\n | \"abnormal\"\n | \"very-abnormal\"\n | \"significant-change-up\"\n | \"significant-change-down\"\n | \"better\"\n | \"worse\"\n | \"susceptible\"\n | \"resistant\"\n | \"intermediate\"\n | \"undefined\";\n\n/**\n * HL7 Table 0078 flag letter → modeled meaning. `U`/`D` are **directional**\n * significant-change flags (up / down), *not* units or a delta magnitude, a\n * distinction that misreads a trend if collapsed. `LL`/`HH` are panic (critical)\n * low/high; `<`/`>` are off-scale low/high; `AA` is very-abnormal.\n */\nconst ABNORMAL_FLAG_MEANINGS: Readonly<Record<AbnormalFlagCode, AbnormalFlagMeaning>> = {\n L: \"below-normal\",\n H: \"above-normal\",\n LL: \"critically-below-normal\",\n HH: \"critically-above-normal\",\n \"<\": \"below-scale\",\n \">\": \"above-scale\",\n N: \"normal\",\n A: \"abnormal\",\n AA: \"very-abnormal\",\n U: \"significant-change-up\",\n D: \"significant-change-down\",\n B: \"better\",\n W: \"worse\",\n S: \"susceptible\",\n R: \"resistant\",\n I: \"intermediate\",\n};\n\n/**\n * A recognized (or explicitly *un*recognized) abnormal flag. The raw field text\n * is always preserved; `recognized` is `false` and `meaning` is `\"undefined\"`\n * for any letter outside Table 0078, the flag is surfaced, never dropped, and\n * **never coerced to `normal`**.\n *\n * @example\n * ```ts\n * import { interpretAbnormalFlag } from \"@cosyte/astm\";\n * const f = interpretAbnormalFlag(\"HH\");\n * f.meaning; // \"critically-above-normal\"\n * f.recognized; // true\n * ```\n */\nexport interface AbnormalFlag {\n /** The verbatim field text, exactly as received. */\n readonly raw: string;\n /** The Table 0078 code, present only when the raw text is a recognized flag. */\n readonly code?: AbnormalFlagCode;\n /** The modeled meaning; `\"undefined\"` (never `\"normal\"`) for an unrecognized flag. */\n readonly meaning: AbnormalFlagMeaning;\n /** Whether the raw text matched a Table 0078 flag. */\n readonly recognized: boolean;\n}\n\n/**\n * Interpret an `R`-record abnormal-flag field (field 7) against HL7 Table 0078.\n *\n * Leading/trailing whitespace is ignored for the lookup but the `raw` text is\n * preserved verbatim. An unrecognized flag yields `{ recognized: false, meaning:\n * \"undefined\" }`, surfaced, never dropped, and **never `\"normal\"`**.\n *\n * @param raw - The verbatim field-7 text.\n * @returns The interpreted flag.\n * @example\n * ```ts\n * import { interpretAbnormalFlag } from \"@cosyte/astm\";\n * interpretAbnormalFlag(\"U\").meaning; // \"significant-change-up\"\n * interpretAbnormalFlag(\"ZZ\").meaning; // \"undefined\" (never \"normal\")\n * ```\n */\nexport function interpretAbnormalFlag(raw: string): AbnormalFlag {\n const key = raw.trim();\n if (isAbnormalFlagCode(key)) {\n return { raw, code: key, meaning: ABNORMAL_FLAG_MEANINGS[key], recognized: true };\n }\n return { raw, meaning: \"undefined\", recognized: false };\n}\n\nfunction isAbnormalFlagCode(value: string): value is AbnormalFlagCode {\n return Object.prototype.hasOwnProperty.call(ABNORMAL_FLAG_MEANINGS, value);\n}\n\n/* ────────────────────────────── result status ────────────────────────────── */\n\n/**\n * The recognized result-status letters (`R`-record field 9). The clinically\n * load-bearing members are **`C` (correction, supersedes a previously\n * transmitted value)** and **`X` (cannot be done / cancelled)**.\n */\nexport type ResultStatusCode = \"F\" | \"C\" | \"P\" | \"R\" | \"S\" | \"I\" | \"X\";\n\n/**\n * The modeled meaning of a result status, plus two fail-safe sentinels:\n * `unspecified` (the field was absent, **never assumed `final`**) and\n * `undefined` (a letter was present but is not a recognized status).\n */\nexport type ResultStatusMeaning =\n | \"final\"\n | \"correction\"\n | \"preliminary\"\n | \"previously-transmitted\"\n | \"partial\"\n | \"pending\"\n | \"cancelled\"\n | \"unspecified\"\n | \"undefined\";\n\nconst RESULT_STATUS_MEANINGS: Readonly<Record<ResultStatusCode, ResultStatusMeaning>> = {\n F: \"final\",\n C: \"correction\",\n P: \"preliminary\",\n R: \"previously-transmitted\",\n S: \"partial\",\n I: \"pending\",\n X: \"cancelled\",\n};\n\n/**\n * A modeled result status. The three booleans are the safety surface a consumer\n * reads instead of string-matching the code:\n *\n * - **`isActiveFinal`** is `true` **only** for a plain `F` (final). It is `false`\n * for a correction (`C`), a cancellation (`X`), a preliminary/partial/pending\n * result, an absent status, and an unrecognized one, so a superseded or\n * cancelled result can **never** read as current/final.\n * - **`supersedes`** is `true` for `C`: this value replaces a previously\n * transmitted one.\n * - **`cancelled`** is `true` for `X`: the result cannot be done / was cancelled.\n *\n * @example\n * ```ts\n * import { interpretResultStatus } from \"@cosyte/astm\";\n * interpretResultStatus(\"C\").isActiveFinal; // false (a correction is not active-final)\n * interpretResultStatus(\"X\").cancelled; // true\n * interpretResultStatus(undefined).meaning; // \"unspecified\" (never \"final\")\n * ```\n */\nexport interface ResultStatus {\n /** The verbatim field text, present only when field 9 carried a value. */\n readonly raw?: string;\n /** The recognized status code, present only when the raw text is a known status. */\n readonly code?: ResultStatusCode;\n /** The modeled meaning; `\"unspecified\"` when absent, `\"undefined\"` when unrecognized. */\n readonly meaning: ResultStatusMeaning;\n /** Whether the raw text matched a recognized status letter. */\n readonly recognized: boolean;\n /** `true` **only** for a plain `F` (final): never for `C`, `X`, absent, or unrecognized. */\n readonly isActiveFinal: boolean;\n /** `true` for `C`: this value supersedes a previously transmitted result. */\n readonly supersedes: boolean;\n /** `true` for `X`: the result cannot be done / was cancelled. */\n readonly cancelled: boolean;\n}\n\n/**\n * Interpret an `R`-record result-status field (field 9).\n *\n * An **absent** status (`undefined` or empty) is typed `unspecified`: **never\n * assumed `final`**. A **present but unrecognized** letter is `undefined`\n * (recognized `false`). In every non-`F` case `isActiveFinal` is `false`, so a\n * correction, a cancellation, or an unknown status can never read as current.\n *\n * @param raw - The verbatim field-9 text, or `undefined`/empty when absent.\n * @returns The modeled status.\n * @example\n * ```ts\n * import { interpretResultStatus } from \"@cosyte/astm\";\n * const s = interpretResultStatus(\"F\");\n * s.isActiveFinal; // true\n * s.supersedes; // false\n * ```\n */\nexport function interpretResultStatus(raw: string | undefined): ResultStatus {\n if (raw === undefined || raw.trim().length === 0) {\n return {\n meaning: \"unspecified\",\n recognized: false,\n isActiveFinal: false,\n supersedes: false,\n cancelled: false,\n };\n }\n const key = raw.trim();\n if (isResultStatusCode(key)) {\n return {\n raw,\n code: key,\n meaning: RESULT_STATUS_MEANINGS[key],\n recognized: true,\n isActiveFinal: key === \"F\",\n supersedes: key === \"C\",\n cancelled: key === \"X\",\n };\n }\n return {\n raw,\n meaning: \"undefined\",\n recognized: false,\n isActiveFinal: false,\n supersedes: false,\n cancelled: false,\n };\n}\n\nfunction isResultStatusCode(value: string): value is ResultStatusCode {\n return Object.prototype.hasOwnProperty.call(RESULT_STATUS_MEANINGS, value);\n}\n\n/* ────────────────────────────── reference range ────────────────────────────── */\n\n/**\n * The shape of a parsed reference range.\n *\n * - `closed`: both a low and a high bound (`3.5-5.0`).\n * - `open-low`: an upper bound only (`<5`), so everything at or below `high`.\n * - `open-high`: a lower bound only (`>10`), so everything at or above `low`.\n * - `unparsed`: the text did not match a recognized form; both bounds are\n * absent and the raw text is surfaced. **A bound is never fabricated.**\n */\nexport type ReferenceRangeKind = \"closed\" | \"open-low\" | \"open-high\" | \"unparsed\";\n\n/**\n * A parsed reference range. Bounds are surfaced as the **verbatim numeric text**\n * (never coerced to a float, never rounded, never converted) so nothing is lost\n * or fabricated.\n *\n * **`[OSS-derived]`: the exact lower/upper delimiter (`-`) and the open-ended\n * `<x`/`>x` forms are taken from the permissively-licensed OSS reference parsers\n * and cross-verified vendor transcripts; they are not confirmed against the\n * purchased CLSI LIS02-A2. Anything that does not match these\n * forms is surfaced verbatim as `unparsed`, never guessed into a bound.**\n *\n * @example\n * ```ts\n * import { parseReferenceRange } from \"@cosyte/astm\";\n * const r = parseReferenceRange(\"3.5-5.0\");\n * r.kind; // \"closed\"\n * r.low; // \"3.5\"\n * r.high; // \"5.0\"\n * ```\n */\nexport interface ReferenceRange {\n /** The verbatim field text, exactly as received. */\n readonly raw: string;\n /** The lower bound, verbatim numeric text (for `closed` and `open-high`). */\n readonly low?: string;\n /** The upper bound, verbatim numeric text (for `closed` and `open-low`). */\n readonly high?: string;\n /** The recognized shape, or `\"unparsed\"` when the text matched no known form. */\n readonly kind: ReferenceRangeKind;\n}\n\n/** Matches a single numeric literal: optional sign, digits, optional fraction. */\nconst NUMBER = \"-?\\\\d+(?:\\\\.\\\\d+)?\";\nconst OPEN_LOW = new RegExp(`^<\\\\s*(${NUMBER})$`, \"u\");\nconst OPEN_HIGH = new RegExp(`^>\\\\s*(${NUMBER})$`, \"u\");\nconst CLOSED = new RegExp(`^(${NUMBER})\\\\s*-\\\\s*(${NUMBER})$`, \"u\");\n\n/**\n * Parse an `R`-record reference-range field (field 6) into a low/high (or\n * open-ended) pair.\n *\n * Recognized forms are `low-high` (closed), `<high` (open-low), and `>low`\n * (open-high). Anything else, including an ambiguous multi-dash string or a\n * bare non-numeric token, is returned as `kind: \"unparsed\"` with the raw text\n * preserved and **no bound invented**. Bounds are surfaced as verbatim text, not\n * coerced to numbers.\n *\n * @param raw - The verbatim field-6 text.\n * @returns The parsed reference range.\n * @example\n * ```ts\n * import { parseReferenceRange } from \"@cosyte/astm\";\n * parseReferenceRange(\"<5\").kind; // \"open-low\"\n * parseReferenceRange(\">10\").low; // \"10\"\n * parseReferenceRange(\"weird\").kind; // \"unparsed\" (never a fabricated bound)\n * ```\n */\nexport function parseReferenceRange(raw: string): ReferenceRange {\n const text = raw.trim();\n\n const low = OPEN_LOW.exec(text);\n if (low?.[1] !== undefined) return { raw, high: low[1], kind: \"open-low\" };\n\n const high = OPEN_HIGH.exec(text);\n if (high?.[1] !== undefined) return { raw, low: high[1], kind: \"open-high\" };\n\n const closed = CLOSED.exec(text);\n if (closed?.[1] !== undefined && closed[2] !== undefined) {\n return { raw, low: closed[1], high: closed[2], kind: \"closed\" };\n }\n\n return { raw, kind: \"unparsed\" };\n}\n","/**\n * The **host-query flow**: classify a message as a request, a result upload, an\n * order download, or indeterminate.\n *\n * On real analyzers the host-query mode is first-class and sometimes mandatory\n * (on the Roche cobas 4800 there is *no* results-sending-only option): the\n * analyzer sends an `H/P/Q/L` **request** and the LIS answers with an `H/P/O/L`\n * **response**. Misreading a query as a result upload (or the reverse) breaks the\n * order flow, so this module makes the distinction **explicit**:\n *\n * **The `Q` record dominates.** Any message carrying a `Q` (request-information)\n * record is classified `host-query`, a request, and is **never** treated as a\n * result set, even if a result record is also present (an anomaly, which the\n * parser flags separately with `ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND`). This is the\n * whole point: a `Q`-bearing message can never silently read as a result upload.\n */\n\nimport type { AstmMessageClassification, AstmRecord } from \"./types.js\";\n\n/**\n * Classify a record stream by the host-query flow. Pure and total: it only reads\n * the record type letters, never a field value.\n *\n * `Q` dominates: a message with any `Q` record is a `host-query` request even when\n * a result (`R`) record is also present, so a query is never misread as a result\n * upload. (The `Q`+`R` anomaly is separately warned at parse time.)\n *\n * @param records - The parsed records, in wire order.\n * @returns The message classification.\n * @example\n * ```ts\n * import { classifyMessage, parseAstmRecords } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rQ|1|^SPEC-7||ALL\\rL|1\\r\");\n * classifyMessage(msg.records).kind; // \"host-query\"\n * ```\n */\nexport function classifyMessage(records: readonly AstmRecord[]): AstmMessageClassification {\n let hasQuery = false;\n let hasResults = false;\n let hasOrders = false;\n for (const r of records) {\n if (r.type === \"Q\") hasQuery = true;\n else if (r.type === \"R\") hasResults = true;\n else if (r.type === \"O\") hasOrders = true;\n }\n\n // `Q` dominates so a query is never read as a result set; then results, then orders.\n let kind: AstmMessageClassification[\"kind\"];\n if (hasQuery) kind = \"host-query\";\n else if (hasResults) kind = \"results\";\n else if (hasOrders) kind = \"orders\";\n else kind = \"indeterminate\";\n\n return { kind, hasQuery, hasResults, hasOrders, isHostQueryRequest: kind === \"host-query\" };\n}\n","/**\n * The runtime side of the profile subsystem: turning an active {@link AstmProfile}\n * into a warning transform the record parser runs over its accumulated warnings,\n * plus the transport-override accessor the framing detector consumes.\n *\n * A profile **never touches an extracted value**. The warning transform operates\n * purely at the warning layer: downgrading a deviation it *expects* into a\n * `PROFILE_QUIRK_APPLIED` warning (flagged `expected`, carrying the original\n * `toleratedCode`) while leaving every un-expected warning untouched. Because the\n * transform only ever rewrites a warning object (never a record, field, or value),\n * a profile cannot change how any byte parses: a spec-clean message parses\n * identically with or without a profile.\n */\n\nimport { profileQuirkApplied, type AstmRecordWarning } from \"../common/warnings.js\";\nimport type { AstmFraming } from \"../ltp/transport.js\";\n\nimport type { AstmProfile, AstmQuirkTolerance } from \"./types.js\";\n\n/**\n * Does `tolerance` apply to `warning`? The codes must match, and every structural\n * key present in the tolerance's `match` must equal the warning's position: a\n * tolerance with no `match` applies to every warning of its code. Matching is on\n * PHI-free structural identifiers only (record-type letter, 1-based field index).\n *\n * @internal\n */\nfunction toleranceApplies(tolerance: AstmQuirkTolerance, warning: AstmRecordWarning): boolean {\n if (tolerance.code !== warning.code) return false;\n const match = tolerance.match;\n if (match === undefined) return true;\n if (match.recordType !== undefined && warning.position.recordType !== match.recordType) {\n return false;\n }\n if (match.fieldIndex !== undefined && warning.position.fieldIndex !== match.fieldIndex) {\n return false;\n }\n return true;\n}\n\n/**\n * Apply a profile to a single warning. Returns a downgraded `PROFILE_QUIRK_APPLIED`\n * warning when the profile expects this deviation; otherwise returns the original\n * warning unchanged (referential identity preserved, so an un-tolerated warning is\n * never reallocated). A warning that is already `expected` (e.g. re-processed) is\n * passed through untouched.\n *\n * @param profile - The active profile.\n * @param warning - One accumulated warning.\n * @returns The re-badged warning when tolerated, else the original.\n * @example\n * ```ts\n * import { applyAstmProfile, astmProfiles, unknownEscapeSequence } from \"@cosyte/astm\";\n * const w = unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * const out = applyAstmProfile(astmProfiles.referenceCorpus, w);\n * out.code; // \"PROFILE_QUIRK_APPLIED\"\n * out.toleratedCode; // \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"\n * ```\n */\nexport function applyAstmProfile(\n profile: AstmProfile,\n warning: AstmRecordWarning,\n): AstmRecordWarning {\n if (warning.expected === true) return warning;\n for (const tolerance of profile.tolerate) {\n if (toleranceApplies(tolerance, warning)) {\n return profileQuirkApplied(warning, profile.name);\n }\n }\n return warning;\n}\n\n/**\n * Apply a profile across a whole warning list, returning a NEW array (the input is\n * never mutated). Returned unchanged (same reference contents, a shallow copy) when\n * `profile` is `undefined`, so the no-profile path pays only a copy. Every tolerated\n * warning is re-badged; every other warning passes through by identity, nothing is\n * dropped, reordered, or reallocated beyond the re-badge.\n *\n * @param warnings - The accumulated record warnings.\n * @param profile - The active profile, or `undefined` for no transform.\n * @returns A new array with tolerated warnings downgraded.\n * @example\n * ```ts\n * import { applyAstmProfileToWarnings, astmProfiles } from \"@cosyte/astm\";\n * const out = applyAstmProfileToWarnings(msgWarnings, astmProfiles.referenceCorpus);\n * ```\n */\nexport function applyAstmProfileToWarnings(\n warnings: readonly AstmRecordWarning[],\n profile: AstmProfile | undefined,\n): AstmRecordWarning[] {\n if (profile === undefined) return [...warnings];\n return warnings.map((w) => applyAstmProfile(profile, w));\n}\n\n/**\n * The profile's transport override, if any: the value a consumer feeds to\n * `detectFraming(bytes, { override })` to force framed/raw and bypass leading-byte\n * auto-detection. `undefined` means \"let detection decide.\"\n *\n * @param profile - The active profile, or `undefined`.\n * @returns `\"framed\"` / `\"raw\"` when the profile forces one, else `undefined`.\n * @example\n * ```ts\n * import { resolveProfileTransport, detectFraming, astmProfiles } from \"@cosyte/astm\";\n * const override = resolveProfileTransport(myRawTcpProfile);\n * const { framing } = detectFraming(bytes, override !== undefined ? { override } : {});\n * ```\n */\nexport function resolveProfileTransport(profile: AstmProfile | undefined): AstmFraming | undefined {\n return profile?.transport;\n}\n","/**\n * Build the multi-line `describe()` output for an {@link AstmProfile}. Omits lines\n * for absent fields; lineage renders as `a → b → c`. Guaranteed non-empty and\n * always starts with `Profile '<name>'` so the \"contains the profile name\" contract\n * holds regardless of which lines are omitted.\n *\n * @internal\n */\n\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * Format an {@link AstmProfile} as a human-readable multi-line description.\n *\n * @internal\n */\nexport function buildDescribe(p: AstmProfile): string {\n const lines: string[] = [`Profile '${p.name}'`];\n if (p.description !== undefined) {\n lines.push(` description: ${p.description}`);\n }\n // `describe()` is only ever attached by the factory, which always fills `lineage`\n // with at least the profile's own name, so it is non-empty here.\n lines.push(` lineage: ${p.lineage.join(\" → \")}`);\n if (p.transport !== undefined) {\n lines.push(` transport: forces ${p.transport} framing (detection override)`);\n }\n if (p.provenance !== undefined) {\n lines.push(` grounded in: ${p.provenance.source} (${p.provenance.reference})`);\n }\n if (p.tolerate.length === 0) {\n lines.push(\" tolerates: nothing (conservative baseline)\");\n } else {\n lines.push(` tolerates ${String(p.tolerate.length)} quirk(s):`);\n for (const t of p.tolerate) {\n const scope =\n t.match?.recordType !== undefined\n ? ` @record ${t.match.recordType}${\n t.match.fieldIndex !== undefined ? `.${String(t.match.fieldIndex)}` : \"\"\n }`\n : t.match?.fieldIndex !== undefined\n ? ` @field ${String(t.match.fieldIndex)}`\n : \"\";\n lines.push(` - ${t.code}${scope}: ${t.rationale}`);\n }\n }\n return lines.join(\"\\n\");\n}\n","/**\n * Pure merge helpers consumed by `defineAstmProfile` when `opts.extends` is\n * supplied. Every helper takes a `readonly parents[]` + a self value and returns\n * the merged result; none mutate input. Mirrors the sibling `@cosyte/hl7` /\n * `@cosyte/ccda` profile merge semantics: lineage first-occurrence dedupe,\n * `tolerate` concat + dedupe (parents before self, last-wins on rationale), scalar\n * last-wins (child, else the last parent that carries a value).\n *\n * Zero runtime deps. Post-merge safety re-validation (a tolerated code must not be\n * safety-critical) is the CALLER's responsibility: these helpers are pure reducers.\n *\n * @internal\n */\n\nimport type { AstmFraming } from \"../ltp/transport.js\";\nimport type { AstmProfile, AstmProfileProvenance, AstmQuirkTolerance } from \"./types.js\";\n\n/**\n * Normalise the `extends` input to a readonly array. Accepts a single profile or an\n * array; returns `[]` for `undefined` so callers treat \"no parents\" identically to\n * \"zero parents\".\n *\n * @internal\n */\nexport function normaliseParents(\n ext: AstmProfile | readonly AstmProfile[] | undefined,\n): readonly AstmProfile[] {\n if (ext === undefined) return [];\n if (Array.isArray(ext)) return ext as readonly AstmProfile[];\n return [ext as AstmProfile];\n}\n\n/**\n * Compute lineage: flatten parent lineages (or `[parent.name]` when a parent has\n * none), append `selfName`, dedupe preserving first occurrence.\n *\n * @internal\n */\nexport function mergeLineage(parents: readonly AstmProfile[], selfName: string): readonly string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const p of parents) {\n const parentLineage = p.lineage.length > 0 ? p.lineage : [p.name];\n for (const n of parentLineage) {\n if (!seen.has(n)) {\n seen.add(n);\n out.push(n);\n }\n }\n }\n if (!seen.has(selfName)) out.push(selfName);\n return Object.freeze(out);\n}\n\n/**\n * Stable signature for a tolerance, used for concat-dedupe: the code plus the\n * optional structural match keys. Two tolerances with the same signature collapse\n * to the last (child-wins on rationale).\n *\n * @internal\n */\nfunction toleranceKey(t: AstmQuirkTolerance): string {\n return `${t.code} ${t.match?.recordType ?? \"\"} ${String(t.match?.fieldIndex ?? \"\")}`;\n}\n\n/**\n * Merge `tolerate` sets: every parent (left-to-right) then self, deduped by\n * {@link toleranceKey} with the **last** occurrence winning (so a child can refine\n * a parent's rationale for the same code+match). Order of first appearance is\n * preserved.\n *\n * @internal\n */\nexport function mergeTolerations(\n parents: readonly AstmProfile[],\n self: readonly AstmQuirkTolerance[],\n): readonly AstmQuirkTolerance[] {\n const order: string[] = [];\n const byKey = new Map<string, AstmQuirkTolerance>();\n const layer = (list: readonly AstmQuirkTolerance[]): void => {\n for (const t of list) {\n const key = toleranceKey(t);\n if (!byKey.has(key)) order.push(key);\n byKey.set(key, t);\n }\n };\n for (const p of parents) layer(p.tolerate);\n layer(self);\n return Object.freeze(order.map((k) => byKey.get(k) as AstmQuirkTolerance));\n}\n\n/**\n * Merge the `provenance` scalar: child wins when provided; otherwise the LAST parent\n * that carries one; otherwise `undefined`.\n *\n * @internal\n */\nexport function mergeProvenance(\n parents: readonly AstmProfile[],\n self: AstmProfileProvenance | undefined,\n): AstmProfileProvenance | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.provenance !== undefined) return p.provenance;\n }\n return undefined;\n}\n\n/**\n * Merge the `description` scalar: child wins when provided; otherwise the LAST\n * parent that carries one; otherwise `undefined`.\n *\n * @internal\n */\nexport function mergeDescription(\n parents: readonly AstmProfile[],\n self: string | undefined,\n): string | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.description !== undefined) return p.description;\n }\n return undefined;\n}\n\n/**\n * Merge the `transport` override scalar: child wins when provided; otherwise the\n * LAST parent that carries one; otherwise `undefined` (let detection decide).\n *\n * @internal\n */\nexport function mergeTransport(\n parents: readonly AstmProfile[],\n self: AstmFraming | undefined,\n): AstmFraming | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.transport !== undefined) return p.transport;\n }\n return undefined;\n}\n","/**\n * The definition-time error for the `@cosyte/astm` vendor-profile subsystem.\n *\n * A profile is validated when it is *defined*, not when it is applied, so a\n * misconfigured profile (a bad name, an unknown option key, a tolerated code that\n * is unknown or **safety-critical**) fails loudly at construction, long before it\n * could quiet a real deviation. This is the class that failure throws. It is a\n * programming error (a bad profile literal), distinct from `AstmParseError` (bad\n * input), so consumers narrow on it separately.\n */\n\n/**\n * Thrown by `defineAstmProfile` when a profile definition is invalid: a missing or\n * empty `name`, an unknown option key, an invalid `transport` value, or a\n * `tolerate` entry whose code is unknown, whose `rationale` is empty, or, the\n * load-bearing safety rule, whose code is **safety-critical** (a result value,\n * abnormal flag, result status, reference range, units, patient/comment context,\n * message-kind, code system, or any frame/LTP integrity warning). Carries the\n * offending profile's `name` when it is known, so a multi-profile build names the\n * culprit.\n *\n * @example\n * ```ts\n * import { defineAstmProfile, AstmProfileDefinitionError } from \"@cosyte/astm\";\n * try {\n * defineAstmProfile({\n * name: \"unsafe\",\n * tolerate: [{ code: \"ASTM_FRAME_BAD_CHECKSUM\", rationale: \"no\" }],\n * });\n * } catch (err) {\n * if (err instanceof AstmProfileDefinitionError) err.profileName; // \"unsafe\"\n * }\n * ```\n */\nexport class AstmProfileDefinitionError extends Error {\n /** The offending profile's `name`, when it could be read. */\n public readonly profileName?: string;\n\n /**\n * Construct a new `AstmProfileDefinitionError`.\n *\n * @param message - A human-readable explanation of what was invalid.\n * @param profileName - The offending profile's name, when known.\n * @internal\n */\n public constructor(message: string, profileName?: string) {\n super(message);\n this.name = \"AstmProfileDefinitionError\";\n if (profileName !== undefined) this.profileName = profileName;\n }\n}\n","/**\n * The **frame** warning registry (`ASTM_FRAME_*`) for the ASTM/CLSI-LIS01 codec:\n * the second of the package's two registries, alongside the record layer's\n * `ASTM_RECORD_*` (both share only the `EMPTY_INPUT` fatal).\n *\n * A frame warning is the lenient codec's record of a tolerated byte-level\n * deviation: it never crashes, never invents a value, and, critically, never\n * lets an untrusted frame be silently merged into a record. Every warning carries\n * a stable code plus an {@link AstmFramePosition} (frame number + byte offset) and\n * **never** the record bytes a frame carries (PHI discipline). Consumers compare\n * `warning.code === FRAME_WARNING_CODES.<CODE>`; renaming a code is a **breaking\n * change**.\n */\n\nimport type { AstmFramePosition } from \"./position.js\";\n\n/**\n * Stable string codes for every frame-codec warning. `key === value` so\n * `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { decodeAstmFrames, FRAME_WARNING_CODES } from \"@cosyte/astm\";\n * const bytes = new Uint8Array([]); // ...a framed stream...\n * void bytes;\n * FRAME_WARNING_CODES.ASTM_FRAME_BAD_CHECKSUM; // \"ASTM_FRAME_BAD_CHECKSUM\"\n * ```\n */\nexport const FRAME_WARNING_CODES = {\n /**\n * A frame's two-hex-char checksum did not match the modulo-256 sum recomputed over its bytes. The\n * frame is surfaced with `trusted: false` and its text is **never merged** into a reassembled\n * record (default **warn** in lenient mode, escalated to a thrown error in `strict`). Corruption is\n * never silently trusted: the \"checksums are routinely not validated\" claim was refuted; we\n * validate.\n */\n ASTM_FRAME_BAD_CHECKSUM: \"ASTM_FRAME_BAD_CHECKSUM\",\n /**\n * A frame's sequence number was not the expected next value (`1 → … → 7 → 0 → …`): a frame was\n * possibly dropped. The stream is **never silently concatenated across the gap** as if contiguous;\n * the in-progress record is tainted and not emitted as a clean reassembly.\n */\n ASTM_FRAME_SEQUENCE_GAP: \"ASTM_FRAME_SEQUENCE_GAP\",\n /**\n * A frame opened with `STX` but no valid terminator + checksum was found before the stream ended\n * (or before the next `STX`), or a record's frames ended on an intermediate `ETB` with no final\n * `ETX`. The partial bytes are surfaced flagged untrusted and **no partial record is invented**\n * (warn in lenient, thrown in `strict`).\n */\n ASTM_FRAME_UNTERMINATED: \"ASTM_FRAME_UNTERMINATED\",\n /**\n * A frame's record text exceeded the 240-byte limit without a split. The frame is still surfaced\n * (and, if its checksum validates, reassembled): the deviation is flagged, not silently dropped\n * (warn in lenient, thrown in `strict`).\n */\n ASTM_FRAME_OVERSIZE: \"ASTM_FRAME_OVERSIZE\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmFrameWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link FRAME_WARNING_CODES}.\n */\nexport type FrameWarningCode = (typeof FRAME_WARNING_CODES)[keyof typeof FRAME_WARNING_CODES];\n\n/**\n * A single frame-codec warning: a stable code, a value-free human-readable\n * message, and positional context (frame number + byte offset).\n *\n * @example\n * ```ts\n * import type { AstmFrameWarning } from \"@cosyte/astm\";\n * const w: AstmFrameWarning = {\n * code: \"ASTM_FRAME_BAD_CHECKSUM\",\n * message: \"Frame checksum mismatch.\",\n * position: { frameNumber: 2, byteOffset: 251 },\n * };\n * ```\n */\nexport interface AstmFrameWarning {\n readonly code: FrameWarningCode;\n /** Human-readable detail for logs. Never contains the frame's record bytes. */\n readonly message: string;\n readonly position: AstmFramePosition;\n}\n\n/**\n * Build an `ASTM_FRAME_BAD_CHECKSUM` warning. The frame is surfaced flagged\n * untrusted and never merged into a record; the warning carries position only.\n *\n * @example\n * ```ts\n * import { frameBadChecksum } from \"@cosyte/astm\";\n * frameBadChecksum({ frameNumber: 2, byteOffset: 251 });\n * ```\n */\nexport function frameBadChecksum(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_BAD_CHECKSUM,\n message: \"Frame checksum mismatch, surfaced untrusted, never merged into a record.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_SEQUENCE_GAP` warning. A frame was possibly dropped; the\n * record is not concatenated across the gap as if contiguous.\n *\n * @example\n * ```ts\n * import { frameSequenceGap } from \"@cosyte/astm\";\n * frameSequenceGap({ frameNumber: 3, byteOffset: 502 });\n * ```\n */\nexport function frameSequenceGap(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_SEQUENCE_GAP,\n message: \"Frame number out of sequence, possible dropped frame, never silently bridged.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_UNTERMINATED` warning. The partial bytes are surfaced\n * flagged untrusted; no partial record is invented.\n *\n * @example\n * ```ts\n * import { frameUnterminated } from \"@cosyte/astm\";\n * frameUnterminated({ byteOffset: 730 });\n * ```\n */\nexport function frameUnterminated(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_UNTERMINATED,\n message: \"Unterminated frame, partial bytes surfaced untrusted, no partial record invented.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_OVERSIZE` warning. The 240-byte text limit was exceeded\n * without a split; the frame is flagged, not silently dropped.\n *\n * @example\n * ```ts\n * import { frameOversize } from \"@cosyte/astm\";\n * frameOversize({ frameNumber: 1, byteOffset: 0 });\n * ```\n */\nexport function frameOversize(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_OVERSIZE,\n message: \"Frame text exceeded the 240-byte limit without a split, flagged, never dropped.\",\n position,\n };\n}\n","/**\n * The **LTP** warning registry (`ASTM_LTP_*`) for the ASTM/CLSI-LIS01 protocol\n * layer: the third of the package's registries, alongside the record layer's\n * `ASTM_RECORD_*` and the frame codec's `ASTM_FRAME_*`.\n *\n * An LTP warning is the protocol reducer's (or the transport detector's) record\n * of a tolerated session-level deviation: an ambiguous framed/raw stream, a\n * control event that arrived in a state that did not expect it, or a frame the\n * receiver had to reject (`NAK`) rather than accept. Every warning carries a\n * stable code plus, at most, a **frame number**: never the record bytes a frame\n * carries (PHI discipline). Consumers compare `warning.code ===\n * LTP_WARNING_CODES.<CODE>`; renaming a code is a **breaking change**.\n */\n\n/**\n * Stable string codes for every LTP protocol / transport warning. `key === value`\n * so `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { LTP_WARNING_CODES } from \"@cosyte/astm\";\n * LTP_WARNING_CODES.ASTM_LTP_FRAME_REJECTED; // \"ASTM_LTP_FRAME_REJECTED\"\n * ```\n */\nexport const LTP_WARNING_CODES = {\n /**\n * The transport detector could not tell a framed stream from a raw (unframed) one from its leading\n * byte: it was neither `STX`/`ENQ` nor a bare record letter. It **defaults to framed** and warns,\n * never guessing silently into data loss; a profile override forces the mode. (cobas b121 drops\n * framing over TCP; cobas 4800 / Iguana retain it, both realities exist, so an unrecognizable lead\n * is defaulted, not assumed.)\n */\n ASTM_LTP_AMBIGUOUS_TRANSPORT: \"ASTM_LTP_AMBIGUOUS_TRANSPORT\",\n /**\n * A control event arrived in a protocol state that did not expect it: e.g. an inbound `ACK`/`NAK`\n * at a receiver (which sends, never receives, those), or an `ENQ` mid-transfer. The event is\n * surfaced and handled defensively (an unexpected `ACK`/`NAK` is **never** read as acceptance of\n * data); it never advances the transfer as if valid.\n */\n ASTM_LTP_UNEXPECTED_EVENT: \"ASTM_LTP_UNEXPECTED_EVENT\",\n /**\n * The receiver rejected a received frame with a `NAK` instead of accepting it: because its\n * checksum failed, it was unterminated, or its frame number was out of sequence. The frame's text\n * is **never** appended to the record and the transfer does **not** advance; the receiver awaits the\n * sender's retransmit. This is the protocol-level face of the frame codec's fail-safe: a bad frame\n * drives retransmit, not acceptance.\n */\n ASTM_LTP_FRAME_REJECTED: \"ASTM_LTP_FRAME_REJECTED\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmLtpWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link LTP_WARNING_CODES}.\n */\nexport type LtpWarningCode = (typeof LTP_WARNING_CODES)[keyof typeof LTP_WARNING_CODES];\n\n/**\n * A single LTP protocol / transport warning: a stable code, a value-free\n * human-readable message, and, for a frame-scoped deviation, the frame number\n * only. Never carries a frame's record bytes.\n *\n * @example\n * ```ts\n * import type { AstmLtpWarning } from \"@cosyte/astm\";\n * const w: AstmLtpWarning = {\n * code: \"ASTM_LTP_FRAME_REJECTED\",\n * message: \"Frame rejected, NAK sent, retransmit expected.\",\n * frameNumber: 2,\n * };\n * ```\n */\nexport interface AstmLtpWarning {\n readonly code: LtpWarningCode;\n /** Human-readable detail for logs. Never contains the frame's record bytes. */\n readonly message: string;\n /** The frame's sequence number when the warning is frame-scoped; absent otherwise. */\n readonly frameNumber?: number;\n}\n\n/**\n * Build an `ASTM_LTP_AMBIGUOUS_TRANSPORT` warning. The detector defaulted to\n * framed; a profile override can force raw.\n *\n * @example\n * ```ts\n * import { ltpAmbiguousTransport } from \"@cosyte/astm\";\n * ltpAmbiguousTransport();\n * ```\n */\nexport function ltpAmbiguousTransport(): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_AMBIGUOUS_TRANSPORT,\n message:\n \"Leading byte was neither a frame start (STX/ENQ) nor a record letter, defaulted to framed; override with a profile to force raw.\",\n };\n}\n\n/**\n * Build an `ASTM_LTP_UNEXPECTED_EVENT` warning. The event was surfaced and handled\n * defensively; it never advanced the transfer as if valid.\n *\n * @param frameNumber - The frame number, when the unexpected event was a frame; omitted otherwise.\n * @example\n * ```ts\n * import { ltpUnexpectedEvent } from \"@cosyte/astm\";\n * ltpUnexpectedEvent();\n * ```\n */\nexport function ltpUnexpectedEvent(frameNumber?: number): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_UNEXPECTED_EVENT,\n message:\n \"Control event arrived in a state that did not expect it, handled defensively, never read as acceptance.\",\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n };\n}\n\n/**\n * Build an `ASTM_LTP_FRAME_REJECTED` warning. The receiver sent a `NAK`; the\n * frame's text was never appended and the transfer did not advance.\n *\n * @param frameNumber - The rejected frame's sequence number, when it could be read.\n * @example\n * ```ts\n * import { ltpFrameRejected } from \"@cosyte/astm\";\n * ltpFrameRejected(2);\n * ```\n */\nexport function ltpFrameRejected(frameNumber?: number): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_FRAME_REJECTED,\n message:\n \"Frame rejected (bad checksum, unterminated, or out of sequence), NAK sent, retransmit expected, never accepted.\",\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n };\n}\n","/**\n * The safety spine of the profile subsystem: the set of warning codes a profile is\n * **forbidden** to tolerate. A vendor profile exists to quiet known, benign\n * structural noise: never to hide a deviation that could change a clinical\n * reading or corrupt the wire.\n *\n * **Design: default-deny.** Rather than enumerate the *forbidden* codes (a list you\n * can forget to extend when a new warning ships, the exact incompleteness a\n * safety gate must not have), this module enumerates the tiny set of **explicitly\n * tolerable** codes and treats *every other* code, across all three registries\n * (record `ASTM_RECORD_*`, frame `ASTM_FRAME_*`, protocol `ASTM_LTP_*`), as\n * safety-critical. A warning code added later is therefore\n * safety-critical **by default** until someone deliberately, reviewably adds it to\n * the allow-list below. Widening the allow-list can only ever be an explicit,\n * argued act; forgetting to touch this file can never *weaken* the gate.\n *\n * The four tolerable codes are the only deviations that (a) are structural/\n * syntactic vendor noise and (b) **cannot alter, drop, or fabricate an extracted\n * value**: because a profile only ever re-badges a *warning*, never re-parses. In\n * every one, the underlying datum is already surfaced verbatim regardless of the\n * profile:\n *\n * - `ASTM_RECORD_UNKNOWN_TYPE`: an unmodeled record type, surfaced verbatim as an\n * `unsupported` record carrying no interpreted clinical field.\n * - `ASTM_NONSTANDARD_DELIMITERS`: the header's own declared delimiters are read\n * and honored either way; this only notes they differ from the canonical set.\n * - `ASTM_UNKNOWN_ESCAPE_SEQUENCE`: an unrecognized `&…&` body is **preserved\n * byte-for-byte** in the decoded value (the escape codec never guesses); the\n * warning is a purely syntactic advisory, the value is identical with or without\n * the profile.\n * - `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS`: a `Q`-record request-information\n * status surfaced verbatim on a *request* record; the code set is paywalled and\n * is never interpreted, profile or not.\n *\n * Everything a wrong value could hide from: a result value split ambiguity, an\n * undefined abnormal flag or result status, an unparseable reference range, absent\n * units, a mis-attached comment, a partial timestamp, a query-vs-result ambiguity,\n * a bad frame checksum / sequence gap / unterminated / oversize frame, an\n * ambiguous transport, an unexpected protocol event, or a rejected frame: is\n * forbidden.\n */\n\nimport { WARNING_CODES } from \"../common/warnings.js\";\nimport { FRAME_WARNING_CODES } from \"../frames/warnings.js\";\nimport { LTP_WARNING_CODES } from \"../ltp/warnings.js\";\n\nimport type { AnyAstmWarningCode } from \"./types.js\";\n\n/**\n * The **only** warning codes a profile may list in its `tolerate` set: benign,\n * structural/syntactic vendor noise that cannot alter, drop, or fabricate an\n * extracted value. Frozen so it cannot be mutated at runtime to smuggle a code in.\n * Adding to this set is a deliberate, reviewable act (and each addition must be an\n * `expected: false` non-safety code by the same reasoning as the four here).\n *\n * @example\n * ```ts\n * import { TOLERABLE_CODES } from \"@cosyte/astm\";\n * TOLERABLE_CODES.has(\"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"); // true\n * TOLERABLE_CODES.has(\"ASTM_FRAME_BAD_CHECKSUM\"); // false\n * ```\n */\nexport const TOLERABLE_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>([\n WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE,\n WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS,\n WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE,\n WARNING_CODES.ASTM_RECORD_UNINTERPRETED_QUERY_STATUS,\n ]),\n);\n\n/**\n * Every real warning code across the three registries: the universe the safety\n * gate reasons over. `PROFILE_QUIRK_APPLIED` is included (it is a real record code)\n * so a profile can never tolerate the marker itself.\n *\n * @internal\n */\nconst ALL_WARNING_CODES: readonly AnyAstmWarningCode[] = [\n ...Object.values(WARNING_CODES),\n ...Object.values(FRAME_WARNING_CODES),\n ...Object.values(LTP_WARNING_CODES),\n];\n\n/**\n * The set of every real warning code, for O(1) membership checks (used by the\n * validator to distinguish \"unknown code\" from \"known but forbidden\"). Frozen.\n *\n * @example\n * ```ts\n * import { ALL_ASTM_WARNING_CODES } from \"@cosyte/astm\";\n * ALL_ASTM_WARNING_CODES.has(\"ASTM_LTP_FRAME_REJECTED\"); // true\n * ```\n */\nexport const ALL_ASTM_WARNING_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>(ALL_WARNING_CODES),\n);\n\n/**\n * The forbidden set: computed as **every known code minus the tolerable\n * allow-list**, so it is complete by construction. Frozen. A code appears here iff\n * it is a real warning code that is not in {@link TOLERABLE_CODES}.\n *\n * @example\n * ```ts\n * import { SAFETY_CRITICAL_CODES } from \"@cosyte/astm\";\n * SAFETY_CRITICAL_CODES.has(\"ASTM_RECORD_UNDEFINED_RESULT_STATUS\"); // true\n * SAFETY_CRITICAL_CODES.has(\"ASTM_FRAME_BAD_CHECKSUM\"); // true\n * ```\n */\nexport const SAFETY_CRITICAL_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>(ALL_WARNING_CODES.filter((c) => !TOLERABLE_CODES.has(c))),\n);\n\n/**\n * True when `code` is a known warning code that is **not** tolerable: i.e. a\n * profile may never list it. A code that is not a real warning code at all returns\n * `false` here (the validator reports \"unknown code\" separately, a distinct\n * failure with a distinct message).\n *\n * @param code - The warning code to test.\n * @returns `true` iff the code is a real code outside the tolerable allow-list.\n * @example\n * ```ts\n * import { isSafetyCriticalCode } from \"@cosyte/astm\";\n * isSafetyCriticalCode(\"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"); // false (tolerable)\n * isSafetyCriticalCode(\"ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT\"); // true\n * ```\n */\nexport function isSafetyCriticalCode(code: string): boolean {\n return SAFETY_CRITICAL_CODES.has(code as AnyAstmWarningCode);\n}\n","/**\n * Validation helpers for `defineAstmProfile`. Every validator returns `void` on\n * success and throws {@link AstmProfileDefinitionError} on failure. The name\n * validator is split out so the factory can call it FIRST and pass `opts.name` to\n * every subsequent throw site (a bad-tolerance error should name the profile it\n * came from).\n *\n * The load-bearing check is {@link validateTolerations}: it refuses any `tolerate`\n * entry whose code is unknown, whose rationale is empty, or, the safety rule,\n * whose code is **safety-critical** (default-deny; see `src/profiles/safety.ts`).\n * That refusal is what lets the rest of the system treat \"an active profile\" as\n * safe by construction: it can only ever quiet a benign, value-preserving warning.\n *\n * Zero runtime deps: inlined Levenshtein for the \"did you mean?\" hint.\n *\n * @internal\n */\n\nimport { AstmProfileDefinitionError } from \"./errors.js\";\nimport { ALL_ASTM_WARNING_CODES, isSafetyCriticalCode } from \"./safety.js\";\nimport type { AstmQuirkTolerance, DefineAstmProfileOptions } from \"./types.js\";\n\n/**\n * Known top-level option keys accepted by `defineAstmProfile`. Any key outside this\n * list throws with an optional Levenshtein-based \"did you mean?\" hint.\n *\n * @internal\n */\nconst KNOWN_OPTION_KEYS: readonly string[] = [\n \"name\",\n \"description\",\n \"tolerate\",\n \"transport\",\n \"provenance\",\n \"extends\",\n];\n\n/** The two legal {@link AstmFraming} values a profile's `transport` may take. */\nconst KNOWN_TRANSPORTS: readonly string[] = [\"framed\", \"raw\"];\n\n/**\n * Iterative DP Levenshtein distance. Zero-dep, ≤15 LoC; used only for the\n * unknown-option-key hint.\n *\n * @internal\n */\nfunction levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n let prev: number[] = [];\n for (let j = 0; j <= b.length; j++) prev.push(j);\n for (let i = 1; i <= a.length; i++) {\n const curr: number[] = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;\n curr.push(Math.min((prev[j] ?? 0) + 1, (curr[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost));\n }\n prev = curr;\n }\n return prev[b.length] ?? 0;\n}\n\n/**\n * Validate the profile NAME (fail-fast). Throws on null/undefined opts, a\n * non-string name, or an empty/whitespace-only name.\n *\n * @internal\n */\nexport function validateProfileName(opts: DefineAstmProfileOptions): void {\n if (opts === null || opts === undefined) {\n throw new AstmProfileDefinitionError(\n `defineAstmProfile: options is required and must be an object. Received: ${String(opts)}.`,\n );\n }\n if (typeof opts.name !== \"string\") {\n throw new AstmProfileDefinitionError(\n \"defineAstmProfile: 'name' is required and must be a non-empty string. \" +\n `Received: ${JSON.stringify((opts as { name?: unknown }).name)}.`,\n );\n }\n if (opts.name.trim().length === 0) {\n throw new AstmProfileDefinitionError(\n \"defineAstmProfile: 'name' is required and must be a non-empty string. \" +\n `Received: ${JSON.stringify(opts.name)}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate TOP-LEVEL option keys. Throws on any unknown key with a\n * Levenshtein-based hint when the edit distance to a known key is ≤ 2.\n *\n * @internal\n */\nexport function validateOptionKeys(opts: DefineAstmProfileOptions): void {\n for (const key of Object.keys(opts)) {\n if (KNOWN_OPTION_KEYS.includes(key)) continue;\n let hint: string | undefined;\n for (const known of KNOWN_OPTION_KEYS) {\n if (levenshtein(key, known) <= 2) {\n hint = known;\n break;\n }\n }\n throw new AstmProfileDefinitionError(\n `Profile '${opts.name}' has unknown option key '${key}'. ` +\n (hint !== undefined ? `Did you mean '${hint}'? ` : \"\") +\n `Known keys: ${KNOWN_OPTION_KEYS.join(\", \")}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate the optional `transport` override: absent, or exactly `\"framed\"` /\n * `\"raw\"`. A profile that forces framing must force a real framing.\n *\n * @internal\n */\nexport function validateTransport(opts: DefineAstmProfileOptions): void {\n if (opts.transport === undefined) return;\n if (typeof opts.transport !== \"string\" || !KNOWN_TRANSPORTS.includes(opts.transport)) {\n throw new AstmProfileDefinitionError(\n `Profile '${opts.name}' has an invalid 'transport' ${JSON.stringify(opts.transport)}. ` +\n `It must be one of: ${KNOWN_TRANSPORTS.join(\", \")}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate a `tolerate` list: every entry's `code` must be a real warning code,\n * must **not** be safety-critical (default-deny), and must carry a non-empty\n * `rationale` (a tolerated deviation without a stated, grounded reason is exactly\n * the \"invented quirk\" the anti-invention rule forbids). Runs post-merge so a\n * tolerance inherited from a rogue parent is caught too.\n *\n * @internal\n */\nexport function validateTolerations(\n tolerate: readonly AstmQuirkTolerance[],\n profileName: string,\n): void {\n for (const t of tolerate) {\n if (typeof t.code !== \"string\" || !ALL_ASTM_WARNING_CODES.has(t.code)) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' tolerate entry has unknown warning code ${JSON.stringify(t.code)}. ` +\n `Only real codes from the record / frame / LTP registries may be named.`,\n profileName,\n );\n }\n if (isSafetyCriticalCode(t.code)) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' may not tolerate '${t.code}': it is a safety-critical warning ` +\n `code (a result value / flag / status / range / units, patient or comment context, ` +\n `message-kind ambiguity, code system, or a frame / LTP integrity warning). A profile quiets ` +\n `benign structural noise, never a deviation that could change a clinical reading or corrupt ` +\n `the wire: it can never make a bad checksum \"ok\" or a cancelled result read \"final.\"`,\n profileName,\n );\n }\n if (typeof t.rationale !== \"string\" || t.rationale.trim().length === 0) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' tolerate entry for '${t.code}' needs a non-empty 'rationale' ` +\n `documenting the real, grounded deviation it expects.`,\n profileName,\n );\n }\n }\n}\n","/**\n * `defineAstmProfile()`: the public factory for building immutable\n * {@link AstmProfile} objects with the safety rules enforced and `describe()`\n * attached. Mirrors the sibling `@cosyte/hl7` `defineProfile()` / `@cosyte/ccda`\n * `defineCcdaProfile()` shape (name / lineage / `extends`-merge / `describe`) while\n * modelling ASTM quirks (a tolerated warning code with provenance, plus the\n * optional raw-vs-framed transport override) rather than HL7 v2 Z-segments or\n * C-CDA template deviations.\n *\n * Zero runtime deps. No `any`; immutability at the return boundary via\n * `Object.freeze` (top-level, matching the sibling's boundary-freeze doctrine).\n */\n\nimport { buildDescribe } from \"./describe.js\";\nimport {\n mergeDescription,\n mergeLineage,\n mergeProvenance,\n mergeTolerations,\n mergeTransport,\n normaliseParents,\n} from \"./merge.js\";\nimport type { AstmProfile, DefineAstmProfileOptions } from \"./types.js\";\nimport {\n validateOptionKeys,\n validateProfileName,\n validateTolerations,\n validateTransport,\n} from \"./validate.js\";\n\n/**\n * Build a frozen {@link AstmProfile} from a validated options object. Throws\n * {@link AstmProfileDefinitionError} on a bad name, an unknown option key, an\n * invalid `transport`, or an invalid `tolerate` entry, including the **safety\n * rule**: a profile may never tolerate a safety-critical warning code (default-deny\n * across all three registries).\n *\n * `extends` composes profiles: lineage, `tolerate`, `transport`, `provenance`, and\n * `description` merge (parents left-to-right, then self; scalars are child-wins).\n * The merged `tolerate` set is re-validated so a safety-critical code cannot sneak\n * in via a hand-crafted parent.\n *\n * @param opts - The profile definition; see {@link DefineAstmProfileOptions}.\n * @returns A frozen, immutable profile with `describe()` attached.\n * @throws {@link AstmProfileDefinitionError} on any invalid definition.\n * @example\n * ```ts\n * import { defineAstmProfile } from \"@cosyte/astm\";\n * const site = defineAstmProfile({\n * name: \"acme-analyzer\",\n * description: \"Acme's inbound raw-TCP analyzer\",\n * transport: \"raw\",\n * tolerate: [\n * { code: \"ASTM_RECORD_UNKNOWN_TYPE\", rationale: \"emits a vendor QC record letter\" },\n * ],\n * provenance: { source: \"Acme host-interface manual\", reference: \"internal-2026\" },\n * });\n * site.lineage; // [\"acme-analyzer\"]\n * console.log(site.describe?.());\n * ```\n */\nexport function defineAstmProfile(opts: DefineAstmProfileOptions): AstmProfile {\n // Fail-fast on name so every downstream throw can name the profile.\n validateProfileName(opts);\n validateOptionKeys(opts);\n validateTransport(opts);\n\n const selfTolerate = opts.tolerate ?? [];\n // Pre-merge validation surfaces the offending profile's own name.\n validateTolerations(selfTolerate, opts.name);\n\n const parents = normaliseParents(opts.extends);\n const lineage = mergeLineage(parents, opts.name);\n const tolerate = mergeTolerations(parents, selfTolerate);\n const transport = mergeTransport(parents, opts.transport);\n const provenance = mergeProvenance(parents, opts.provenance);\n const description = mergeDescription(parents, opts.description);\n\n // Post-merge re-validation: a safety-critical code inherited from a hand-crafted\n // parent is refused here.\n validateTolerations(tolerate, opts.name);\n\n type Mutable<T> = { -readonly [K in keyof T]?: T[K] };\n const profile: Mutable<AstmProfile> = {\n name: opts.name,\n lineage,\n tolerate,\n };\n if (description !== undefined) profile.description = description;\n if (transport !== undefined) profile.transport = transport;\n if (provenance !== undefined) profile.provenance = provenance;\n\n const finalised = profile as AstmProfile;\n profile.describe = (): string => buildDescribe(finalised);\n\n return Object.freeze(profile) as AstmProfile;\n}\n","/**\n * The `default` conservative baseline profile. Tolerates **nothing** and forces no\n * transport: every deviation surfaces as its own warning, unmodified, and framing\n * is auto-detected. It exists so \"no profile\" and \"the default profile\" are the\n * same, explicit behaviour, and so a consumer can name the baseline when composing\n * (`extends: astmProfiles.default`) without importing a special sentinel. Absence of\n * a profile means this conservative default applied: not that a stream was fully\n * understood.\n *\n * Authored through the public `defineAstmProfile()` API: zero privileged coupling;\n * it is exactly what a user would write.\n */\n\nimport { defineAstmProfile } from \"./define.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * The conservative baseline profile: no tolerated quirks, no transport override.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.default });\n * // identical to parseAstmRecords(raw) with no profile.\n * ```\n */\nexport const defaultProfile: AstmProfile = defineAstmProfile({\n name: \"default\",\n description: \"Conservative baseline: tolerates nothing; every deviation surfaces as a warning.\",\n tolerate: [],\n});\n","/**\n * The `referenceCorpus` profile: a **non-vendor**, evidence-backed tolerance\n * grounded firsthand in the **redistributable OSS reference corpus**, not an\n * invented per-vendor deviation matrix.\n *\n * Grounding (firsthand-verified 2026-07-21; the corpus is permissively licensed):\n *\n * - **`kxepal/python-astm` `codec.py`** (BSD), its `decode_record` /\n * `decode_component` / `decode_repeated_component` split fields, components, and\n * repeats on the raw delimiters and **never un-escape** ASTM escape sequences: the\n * source contains zero handling of `&F&`/`&S&`/`&R&`/`&E&`, and the escape\n * character is treated as ordinary data. `senaite.astm` / `senaite.lis2a` inherit\n * the same escape-agnostic decode.\n * - Consequence, and what this profile expects: a stream produced or relayed by a\n * toolchain built on that escape-agnostic reference stack routinely carries a bare\n * or non-standard `&…&` body inside a free-text field (units, comments, test\n * names) that our **escape-aware** tokenizer flags as\n * `ASTM_UNKNOWN_ESCAPE_SEQUENCE`. We are deliberately stricter than the OSS\n * references here, so the warning is *expected*\n * noise when talking to that ecosystem: recognizable and non-clinical.\n *\n * **Why this is safe.** The escape codec **preserves an unrecognized `&…&` body\n * byte-for-byte** in the decoded value (`decodeEscapes` never guesses); the value a\n * consumer reads is identical whether or not this profile is active. The profile\n * only re-badges the *warning*: no value, flag, status, or identifier is altered\n * or dropped. And by the safety gate in `defineAstmProfile`, it **cannot** tolerate\n * a result value / flag / status / range / units, a patient or comment context, a\n * message-kind ambiguity, or any frame / LTP integrity warning: those always\n * surface, profile or not.\n *\n * **Honesty.** This is a *reference-corpus* profile, not a claim about any named\n * analyzer vendor. No per-vendor (\"cobas does X\") behaviour is asserted; a named\n * vendor profile awaits a firsthand vendor-attributed quirk document.\n */\n\nimport { defineAstmProfile } from \"./define.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * Tolerates the non-standard-escape noise that streams from the escape-agnostic OSS\n * reference stack (`python-astm` / `senaite`) carry: the value is preserved\n * verbatim; only the syntactic warning is expected.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles, WARNING_CODES } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.referenceCorpus });\n * // an unknown-escape deviation arrives as PROFILE_QUIRK_APPLIED (expected), not a bare warning:\n * const quirks = msg.warnings.filter((w) => w.code === WARNING_CODES.PROFILE_QUIRK_APPLIED);\n * ```\n */\nexport const referenceCorpus: AstmProfile = defineAstmProfile({\n name: \"referenceCorpus\",\n description:\n \"Non-standard-escape tolerance grounded firsthand in the escape-agnostic OSS reference corpus \" +\n \"(python-astm / senaite). Syntactic encoding noise only: the value is preserved verbatim, never \" +\n \"a safety-critical value.\",\n provenance: {\n source: \"kxepal/python-astm codec.py (BSD) + senaite.astm / senaite.lis2a\",\n reference:\n \"https://github.com/kxepal/python-astm/blob/master/astm/codec.py ; \" +\n \"https://github.com/senaite/senaite.astm\",\n retrieved: \"2026-07-21\",\n note: \"Firsthand-read: decode_record/decode_component/decode_repeated_component split on raw delimiters and never un-escape &F&/&S&/&R&/&E&.\",\n },\n tolerate: [\n {\n code: \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\",\n rationale:\n \"The OSS reference stack (python-astm codec.py, firsthand-verified) treats '&' as literal data \" +\n \"and never decodes escape sequences, so streams from that ecosystem carry non-standard '&…&' \" +\n \"bodies our escape-aware tokenizer flags. The body is preserved byte-for-byte in the value; \" +\n \"the deviation is recognizable, syntactic, and non-clinical.\",\n },\n ],\n});\n","/**\n * The profile **registry**: the named built-in set (with provenance) plus a\n * process-scoped default. Mirrors the sibling `@cosyte/hl7` / `@cosyte/ccda`\n * `set/getDefault*Profile` convention. `parseAstmRecords(raw)` with no explicit\n * `profile` option consults {@link getDefaultAstmProfile}; an explicit `profile`\n * option always wins, and `profile: null` opts out of the default for a single call.\n *\n * The single mutable module-scoped `let` is the only such state in the profile\n * subsystem: an intentional, documented trade-off (identical to the siblings).\n * Tests that set a default MUST clear it in teardown (`setDefaultAstmProfile(null)`)\n * to avoid cross-test bleed.\n */\n\nimport { defaultProfile } from \"./default.js\";\nimport { referenceCorpus } from \"./reference-corpus.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * The built-in profiles, keyed by name. Each carries its own provenance (except the\n * conservative `default` baseline).\n *\n * @internal\n */\nconst BUILT_INS: ReadonlyMap<string, AstmProfile> = new Map<string, AstmProfile>([\n [defaultProfile.name, defaultProfile],\n [referenceCorpus.name, referenceCorpus],\n]);\n\n/**\n * Look up a built-in profile by name. Returns `undefined` when no built-in has that\n * name (a user-defined profile is not in this registry, pass it directly).\n *\n * @param name - The built-in profile name.\n * @returns The profile, or `undefined`.\n * @example\n * ```ts\n * import { getAstmProfile } from \"@cosyte/astm\";\n * getAstmProfile(\"referenceCorpus\")?.provenance?.source;\n * ```\n */\nexport function getAstmProfile(name: string): AstmProfile | undefined {\n return BUILT_INS.get(name);\n}\n\n/**\n * The names of every built-in profile, in registration order.\n *\n * @returns The built-in names.\n * @example\n * ```ts\n * import { listAstmProfiles } from \"@cosyte/astm\";\n * listAstmProfiles(); // [\"default\", \"referenceCorpus\"]\n * ```\n */\nexport function listAstmProfiles(): readonly string[] {\n return Object.freeze([...BUILT_INS.keys()]);\n}\n\n/**\n * Process-scoped default profile. `undefined` means \"unset\": `parseAstmRecords`\n * applies no profile in that state.\n *\n * @internal\n */\nlet _defaultProfile: AstmProfile | undefined = undefined;\n\n/**\n * Register a process-scoped default profile that `parseAstmRecords(raw)` applies\n * when no explicit `profile` option is passed. Pass `null` (or `undefined`) to\n * clear. An explicit `parseAstmRecords(raw, { profile })` always wins;\n * `{ profile: null }` opts out of the default for a single call.\n *\n * **Test hygiene:** the only mutable module-scoped state here, tests that call this\n * MUST clear it in teardown or default-profile bleed infects later tests.\n *\n * @param profile - The profile to register as default, or `null` to clear.\n * @example\n * ```ts\n * import { setDefaultAstmProfile, astmProfiles, parseAstmRecords } from \"@cosyte/astm\";\n * setDefaultAstmProfile(astmProfiles.referenceCorpus);\n * const msg = parseAstmRecords(raw); // uses referenceCorpus\n * setDefaultAstmProfile(null); // clear\n * ```\n */\nexport function setDefaultAstmProfile(profile: AstmProfile | null): void {\n _defaultProfile = profile ?? undefined;\n}\n\n/**\n * Return the current process-scoped default profile, or `undefined` if none is\n * registered.\n *\n * @returns The default profile, or `undefined`.\n * @example\n * ```ts\n * import { getDefaultAstmProfile } from \"@cosyte/astm\";\n * const p = getDefaultAstmProfile();\n * if (p !== undefined) console.log(\"default profile:\", p.name);\n * ```\n */\nexport function getDefaultAstmProfile(): AstmProfile | undefined {\n return _defaultProfile;\n}\n","/**\n * The ASTM escape codec: the fix for a documented silent-misread class.\n *\n * ASTM escapes an embedded delimiter with the escape character on both sides of\n * a single mnemonic letter (the InterSystems mnemonics, cross-verified against\n * the OSS reference implementations):\n *\n * ```\n * &F& → field delimiter (default \"|\")\n * &S& → component delimiter (default \"^\")\n * &R& → repeat delimiter (default \"\\\")\n * &E& → escape character (default \"&\")\n * ```\n *\n * **Why this is a requirement, not a nicety.** A result value can legitimately\n * contain a component delimiter: e.g. a titre written `1^40`. On the wire that\n * is escaped as `1&S&40`. A parser that splits a field into components on the\n * raw component delimiter and *never decodes the escape* is safe here (the\n * escape body `&S&` contains no literal `^`), but a parser that **decodes first\n * and splits second** turns `1&S&40` into `1^40` and then mis-splits it into two\n * components: the silent misread `python-astm` / `Chistousov` exhibit. The\n * correct order, implemented here, is **escape-aware split, then decode each\n * leaf**: the escape sequence survives the split intact and only then decodes to\n * a single literal, so `1&S&40` reads as exactly one component.\n *\n * Re-escaping (the inverse, for spec-clean emit) lives in the serializer and is\n * deliberately not implemented here.\n */\n\nimport type { Delimiters } from \"./delimiters.js\";\n\n/**\n * A callback the codec calls when it encounters an escape sequence whose body is\n * not one of the four recognized mnemonics. The sequence is preserved verbatim;\n * the callback lets the parser surface a value-free `ASTM_UNKNOWN_ESCAPE_SEQUENCE`\n * warning. Optional so the codec can be used purely.\n */\nexport type UnknownEscapeSink = () => void;\n\n/**\n * Decode the four recognized ASTM escape mnemonics in a single already-split\n * leaf (a component string), substituting the *active* delimiters read from the\n * header. Unrecognized `&…&` bodies are preserved verbatim and reported through\n * `onUnknown` (never dropped, never guessed).\n *\n * This runs **after** splitting, so a decoded delimiter becomes ordinary literal\n * text and can never introduce a new split boundary.\n *\n * @param leaf - One component string, escape-aware split already applied.\n * @param d - The delimiters resolved from the header.\n * @param onUnknown - Called once per unrecognized escape body encountered.\n * @returns The decoded string.\n * @example\n * ```ts\n * import { decodeEscapes, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * decodeEscapes(\"1&S&40\", CANONICAL_DELIMITERS); // \"1^40\"\n * ```\n */\nexport function decodeEscapes(leaf: string, d: Delimiters, onUnknown?: UnknownEscapeSink): string {\n const esc = d.escape;\n if (!leaf.includes(esc)) return leaf;\n\n let out = \"\";\n let i = 0;\n while (i < leaf.length) {\n const ch = leaf.charAt(i);\n if (ch !== esc) {\n out += ch;\n i += 1;\n continue;\n }\n // Look for the closing escape char. An escape body is the single char between them.\n const close = leaf.indexOf(esc, i + 1);\n if (close === -1) {\n // A lone, unterminated escape char: preserve the remainder verbatim and stop.\n out += leaf.slice(i);\n break;\n }\n const body = leaf.slice(i + 1, close);\n const replacement = escapeBody(body, d);\n if (replacement === undefined) {\n // Unrecognized escape: preserve the whole `&…&` verbatim, surface it, never guess.\n out += esc + body + esc;\n onUnknown?.();\n } else {\n out += replacement;\n }\n i = close + 1;\n }\n return out;\n}\n\n/** Map one escape body (the char(s) between the escape delimiters) to its literal, or undefined. */\nfunction escapeBody(body: string, d: Delimiters): string | undefined {\n switch (body) {\n case \"F\":\n return d.field;\n case \"S\":\n return d.component;\n case \"R\":\n return d.repeat;\n case \"E\":\n return d.escape;\n default:\n return undefined;\n }\n}\n\n/**\n * Split `text` on `delimiter`, treating any escape sequence (`escape …\n * escape`) as an opaque atom so a delimiter that appears *inside* an escape body\n * never causes a split. Returns the raw (still-encoded) segments: decoding is\n * the caller's next step, per the escape-aware-split-then-decode contract.\n *\n * For the four canonical mnemonics this is belt-and-suspenders (their bodies are\n * letters, not delimiters), but it makes the \"an escaped delimiter is one token\"\n * guarantee hold for any declared delimiter set, including adversarial input.\n *\n * @param text - The field or repeat string to split.\n * @param delimiter - The delimiter to split on.\n * @param escape - The active escape character.\n * @returns The raw segments, in order.\n * @example\n * ```ts\n * import { splitEscapeAware } from \"@cosyte/astm\";\n * splitEscapeAware(\"a^b^c\", \"^\", \"&\"); // [\"a\", \"b\", \"c\"]\n * splitEscapeAware(\"1&S&40\", \"^\", \"&\"); // [\"1&S&40\"] (escape body is opaque)\n * ```\n */\nexport function splitEscapeAware(text: string, delimiter: string, escape: string): string[] {\n if (text.length === 0) return [\"\"];\n const out: string[] = [];\n let current = \"\";\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === escape) {\n // Copy the whole escape sequence verbatim; do not inspect it for delimiters.\n const close = text.indexOf(escape, i + 1);\n if (close === -1) {\n current += text.slice(i);\n break;\n }\n current += text.slice(i, close + 1);\n i = close + 1;\n continue;\n }\n if (ch === delimiter) {\n out.push(current);\n current = \"\";\n i += 1;\n continue;\n }\n current += ch;\n i += 1;\n }\n out.push(current);\n return out;\n}\n","/**\n * Record tokenizer: split one raw ASTM record into typed {@link AstmField}s.\n *\n * The split is **escape-aware first, decode second** (see `../common/escapes.ts`):\n * a field / repeat / component boundary is only recognized on an *unescaped*\n * delimiter, and each resulting component leaf is decoded afterwards. That\n * ordering is the whole reason an embedded escaped component delimiter (`&S&`)\n * reads as a single component instead of being mis-split.\n */\n\nimport type { Delimiters } from \"../common/delimiters.js\";\nimport { decodeEscapes, splitEscapeAware } from \"../common/escapes.js\";\nimport type { AstmField } from \"./types.js\";\n\n/**\n * Tokenize a single record string (its terminator already stripped) into its\n * fields. `fields[0]` is the type-letter field; ASTM data fields follow at\n * 1-based indices.\n *\n * @param record - The raw record text.\n * @param d - The delimiters resolved from the header.\n * @param onUnknownEscape - Called (with the 1-based field index) for each\n * unrecognized escape sequence encountered, so the caller can warn.\n * @returns The record's fields.\n * @example\n * ```ts\n * import { tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeRecord(\"R|1|^^^687|28.6|U/L\", CANONICAL_DELIMITERS);\n * fields[3].components[0]; // \"28.6\"\n * ```\n */\nexport function tokenizeRecord(\n record: string,\n d: Delimiters,\n onUnknownEscape?: (fieldIndex: number) => void,\n): AstmField[] {\n const rawFields = splitEscapeAware(record, d.field, d.escape);\n return rawFields.map((raw, fieldIndex) => toField(raw, d, () => onUnknownEscape?.(fieldIndex)));\n}\n\n/**\n * Tokenize an `H` (header) record into its fields.\n *\n * A header cannot go through {@link tokenizeRecord}: its second field is the\n * **delimiter declaration**, which carries the escape character *literally*\n * rather than as an escape sequence. The generic escape-aware split treats that\n * lone escape character as the opening of an unterminated escape and swallows\n * the whole remainder of the record into one field, so every data field after\n * the declaration is lost. This tokenizer instead takes the declaration verbatim\n * as one opaque field and applies the ordinary escape-aware tokenizer to the\n * data portion that follows it.\n *\n * `fields[0]` is the type-letter field and `fields[1]` is the delimiter\n * declaration (verbatim, never escape-decoded); the header's ASTM data fields\n * follow from `fields[2]`.\n *\n * @param record - The raw `H` record text (terminator already stripped).\n * @param d - The delimiters declared by this header.\n * @param onUnknownEscape - Called with the 0-based whole-record field index for\n * each unrecognized escape sequence in the data portion.\n * @returns The header's fields.\n * @example\n * ```ts\n * import { tokenizeHeader, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeHeader(\"H|\\\\^&|||sender\", CANONICAL_DELIMITERS);\n * fields[1].raw; // \"\\\\^&\"\n * fields[4].raw; // \"sender\"\n * ```\n */\nexport function tokenizeHeader(\n record: string,\n d: Delimiters,\n onUnknownEscape?: (fieldIndex: number) => void,\n): AstmField[] {\n // The delimiter-definition field runs from index 2 to the next field separator.\n const defEnd = record.indexOf(d.field, 2);\n const definition = defEnd === -1 ? record.slice(2) : record.slice(2, defEnd);\n const head = [opaqueField(record.slice(0, Math.min(1, record.length))), opaqueField(definition)];\n if (defEnd === -1) return head;\n // Data fields start at whole-record index 2, so shift the tokenizer's local index by 2.\n const data = tokenizeRecord(record.slice(defEnd + 1), d, (i) => onUnknownEscape?.(i + 2));\n return [...head, ...data];\n}\n\n/** A field whose text is structural, not data: surfaced verbatim as one component, never decoded. */\nfunction opaqueField(raw: string): AstmField {\n return { raw, components: [raw], repeats: [[raw]] };\n}\n\n/** Build one {@link AstmField} from its raw wire text: split into repeats → components, then decode. */\nfunction toField(raw: string, d: Delimiters, onUnknownEscape: () => void): AstmField {\n const rawRepeats = splitEscapeAware(raw, d.repeat, d.escape);\n const repeats = rawRepeats.map((rep) =>\n splitEscapeAware(rep, d.component, d.escape).map((comp) =>\n decodeEscapes(comp, d, onUnknownEscape),\n ),\n );\n // `splitEscapeAware` always returns at least one element, so `repeats[0]` is defined.\n const components = repeats[0] ?? [\"\"];\n return { raw, components, repeats };\n}\n\n/**\n * The primary scalar of a field: its first repeat's first component, decoded.\n * Returns `undefined` for a truly empty field so callers can distinguish\n * \"absent\" from a value: never defaulting a missing value.\n *\n * @example\n * ```ts\n * import { fieldScalar, tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const f = tokenizeRecord(\"R|1|^^^687|28.6\", CANONICAL_DELIMITERS);\n * fieldScalar(f[3]); // \"28.6\"\n * ```\n */\nexport function fieldScalar(field: AstmField | undefined): string | undefined {\n if (field === undefined) return undefined;\n const first = field.components[0];\n return first !== undefined && first.length > 0 ? first : undefined;\n}\n","/**\n * The record-layer entry point: {@link parseAstmRecords}.\n *\n * It assumes **already-de-framed** record bytes; {@link parseFramedAstm} composes\n * the E1381/LIS01 framing layer with this one. It reads the four delimiters from each\n * `H` record, tokenizes every record escape-aware, and builds the immutable\n * {@link AstmMessage}, lenient by default: vendor quirks become typed warnings, and\n * only three unrecoverable structural conditions are fatal.\n */\n\nimport { AstmParseError, FATAL_CODES } from \"../common/errors.js\";\nimport { deepFreeze } from \"../common/freeze.js\";\nimport { isNonStandard, readDelimiters, type Delimiters } from \"../common/delimiters.js\";\nimport { parseAstmDate, type AstmDate } from \"../common/dates.js\";\nimport { recognizeUniversalTestId } from \"../common/coding-system.js\";\nimport {\n ambiguousMessageKind,\n ambiguousValueSplit,\n delimitersRedeclared,\n nonStandardDelimiters,\n orphanComment,\n partialTimestamp,\n undefinedAbnormalFlag,\n undefinedResultStatus,\n uninterpretedQueryStatus,\n unitsAbsent,\n unknownEscapeSequence,\n unknownRecordType,\n unparseableReferenceRange,\n unreadableRedeclaration,\n type AstmRecordWarning,\n} from \"../common/warnings.js\";\nimport {\n interpretAbnormalFlag,\n interpretResultStatus,\n parseReferenceRange,\n} from \"./result-semantics.js\";\nimport { classifyMessage } from \"./host-query.js\";\nimport { applyAstmProfileToWarnings } from \"../profiles/apply.js\";\nimport { getDefaultAstmProfile } from \"../profiles/registry.js\";\nimport type { AstmProfile } from \"../profiles/types.js\";\nimport { fieldScalar, tokenizeHeader, tokenizeRecord } from \"./tokenize.js\";\nimport type {\n AstmField,\n AstmMessage,\n AstmParseOptions,\n AstmRecord,\n CommentRecord,\n HeaderRecord,\n ManufacturerRecord,\n OrderRecord,\n PatientName,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n ScientificRecord,\n TerminatorRecord,\n UnsupportedRecord,\n} from \"./types.js\";\n\n/**\n * Thrown by {@link parseAstmRecords} in `strict` mode when the lenient parser\n * would otherwise have accumulated one or more Tier-2 warnings. Carries the\n * warnings (code + position, never a value) so a caller can see every deviation.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, AstmStrictError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"H|\\\\^&\\rZ|1\\r\", { strict: true });\n * } catch (err) {\n * if (err instanceof AstmStrictError) err.warnings.length; // >= 1\n * }\n * ```\n */\nexport class AstmStrictError extends Error {\n public readonly warnings: readonly AstmRecordWarning[];\n /** @internal */\n public constructor(warnings: readonly AstmRecordWarning[]) {\n super(`Strict mode: ${String(warnings.length)} tolerated deviation(s) rejected.`);\n this.name = \"AstmStrictError\";\n this.warnings = warnings;\n }\n}\n\n/**\n * Parse an ASTM/CLSI-LIS02 record stream into an immutable {@link AstmMessage}.\n *\n * The stream is a sequence of records separated by `CR` (with `LF`/`CRLF`\n * tolerated); the first record must be an `H` header, which declares the\n * delimiters. Lenient by default: set `strict` to reject any tolerated deviation.\n *\n * **Several messages in one stream.** A message runs `H` … `L`, so a stream may carry\n * more than one, and each `H` declares the delimiters for the records that follow it.\n * A later header that declares a **different** set is honored from that header onward\n * and flagged `ASTM_RECORD_DELIMITERS_REDECLARED`; records already read keep the set\n * they were read with, so a redeclaration never reinterprets earlier bytes. A header\n * that merely restates the set in force is a no-op and warns nothing. A later header\n * that cannot declare a usable set keeps the delimiters already in force and warns\n * `ASTM_RECORD_UNREADABLE_REDECLARATION`: a set is never guessed, and no record is\n * dropped. Per-header sets are on each {@link HeaderRecord.delimiters};\n * {@link AstmMessage.delimiters} is the first header's.\n *\n * @param raw - The de-framed record bytes, as a string or `Uint8Array` (decoded\n * latin1 so byte values survive 1:1).\n * @param options - Parse options; lenient unless `strict` is set.\n * @returns The parsed, deeply-frozen message.\n * @throws {@link AstmParseError} on a Tier-3 fatal (`EMPTY_INPUT`,\n * `ASTM_RECORD_NO_HEADER`, `ASTM_RECORD_UNDECLARED_DELIMITERS`).\n * @throws {@link AstmStrictError} when `strict` is set and a deviation occurs.\n * @example\n * ```ts\n * import { parseAstmRecords, results } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rO|1|ACC\\rR|1|^^^687|28.6|U/L||N||F\\rL|1|N\\r\");\n * results(msg)[0]?.value; // \"28.6\"\n * ```\n */\nexport function parseAstmRecords(\n raw: string | Uint8Array,\n options: AstmParseOptions = {},\n): AstmMessage {\n const text = typeof raw === \"string\" ? raw : decodeBytes(raw);\n\n if (text.trim().length === 0) {\n throw new AstmParseError(FATAL_CODES.EMPTY_INPUT, \"Input is empty.\", { recordIndex: 0 });\n }\n\n // Records are CR-delimited (LF / CRLF tolerated). Phase 1 assumes de-framed record bytes.\n const lines = text.split(/\\r\\n|\\r|\\n/u).filter((l) => l.length > 0);\n\n const first = lines[0];\n if (first === undefined || first.charAt(0) !== \"H\") {\n const leadType = first?.charAt(0);\n throw new AstmParseError(\n FATAL_CODES.ASTM_RECORD_NO_HEADER,\n \"First record is not an H (header) record.\",\n leadType !== undefined && leadType.length > 0\n ? { recordIndex: 0, recordType: leadType }\n : { recordIndex: 0 },\n );\n }\n\n const delimiters = readDelimiters(first);\n if (delimiters === undefined) {\n throw new AstmParseError(\n FATAL_CODES.ASTM_RECORD_UNDECLARED_DELIMITERS,\n \"Header record is too short to declare the four delimiters.\",\n { recordIndex: 0, recordType: \"H\" },\n );\n }\n\n const warnings: AstmRecordWarning[] = [];\n if (isNonStandard(delimiters)) {\n warnings.push(nonStandardDelimiters({ recordIndex: 0, recordType: \"H\" }));\n }\n\n // Delimiters are scoped to a *message*, and a message runs `H` … `L`, so one stream may carry\n // several messages back to back, each header declaring its own set. The active set is therefore\n // re-read at every `H` and governs that header and the records that follow it. Reading the whole\n // stream with the FIRST header's set is what silently merged every field of a redeclared message\n // into one (ASTM-SECOND-HEADER-COLLAPSE): a lost result, with no warning.\n //\n // Forward scoping is also the only reading that never reinterprets bytes it has already consumed:\n // records before a redeclaration keep the set they were read with, so the two sets never disagree\n // about the same bytes. \"Last header wins retroactively\" would require re-reading records already\n // delivered; \"first header wins forever\" is the collapse itself.\n let active = delimiters;\n const built: AstmRecord[] = lines.map((line, recordIndex) => {\n if (recordIndex > 0 && line.charAt(0) === \"H\") {\n active = adoptRedeclaredDelimiters(line, active, recordIndex, warnings);\n }\n return buildRecord(line, recordIndex, active, warnings);\n });\n // Second pass: attach each comment to its immediately-preceding H/P/O/R parent (an orphan → root).\n const records = attachComments(built, warnings);\n\n // Host-query classification. `Q` dominates so a query is never read as a result set; a message that\n // carries BOTH a Q and an R is contradictory: classified host-query (still a request) and warned,\n // never silently treated as a result upload.\n const classification = classifyMessage(records);\n if (classification.hasQuery && classification.hasResults) {\n warnings.push(ambiguousMessageKind({ recordIndex: 0, recordType: \"H\" }));\n }\n\n // ── Phase 8: vendor profile tolerance. The profile transform runs LAST, over the\n // fully-accumulated warnings: it only ever re-badges a warning it *expects* to a\n // PROFILE_QUIRK_APPLIED (values, records, and delimiters are already built and are\n // never touched). A safety-critical deviation can never be tolerated (enforced at\n // defineAstmProfile time), so the transform can only quiet benign structural noise.\n const profile = resolveProfile(options);\n const finalWarnings = applyAstmProfileToWarnings(warnings, profile);\n\n // Strict mode escalates only the deviations that were NOT expected by a profile:\n // an expected PROFILE_QUIRK_APPLIED is known and benign, so it is recorded, not thrown.\n if (options.strict === true) {\n const escalating = finalWarnings.filter((w) => w.expected !== true);\n if (escalating.length > 0) {\n throw new AstmStrictError(escalating);\n }\n }\n\n const header = records[0] as HeaderRecord;\n const message: AstmMessage =\n profile !== undefined\n ? {\n header,\n records,\n delimiters,\n classification,\n warnings: finalWarnings,\n profile: { name: profile.name, lineage: profile.lineage },\n }\n : { header, records, delimiters, classification, warnings: finalWarnings };\n\n return deepFreeze(message);\n}\n\n/**\n * Resolve the effective profile for a parse. An explicit `options.profile` wins: an\n * {@link AstmProfile} is used as-is; `null` opts out of the process-scoped default\n * for this call. When `profile` is omitted, the process-scoped default\n * ({@link setDefaultAstmProfile}) applies, or `undefined` when none is set.\n */\nfunction resolveProfile(options: AstmParseOptions): AstmProfile | undefined {\n if (options.profile === null) return undefined;\n if (options.profile !== undefined) return options.profile;\n return getDefaultAstmProfile();\n}\n\n/**\n * Resolve the delimiter set a **subsequent** `H` record puts into force.\n *\n * Three outcomes, all fail-safe, a set is never guessed and no record is ever dropped:\n *\n * - **Unusable declaration** (too short, or the field separator also names one of the other three):\n * keep the set already in force and warn. The identical condition on the *first* header is the\n * `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal, because there is no earlier set to fall back to.\n * - **Same set restated**: a no-op. A stream carrying several messages in one delimiter set is\n * ordinary, so this warns nothing: warning would be pure noise on a conformant shape.\n * - **Different set**: adopt it, and warn. This is the only case where a reader still using the old\n * set would start merging fields, so it is the only case worth a signal.\n */\nfunction adoptRedeclaredDelimiters(\n line: string,\n active: Delimiters,\n recordIndex: number,\n warnings: AstmRecordWarning[],\n): Delimiters {\n const position = { recordIndex, recordType: \"H\" };\n const declared = readDelimiters(line);\n if (declared === undefined) {\n warnings.push(unreadableRedeclaration(position));\n return active;\n }\n if (sameDelimiters(declared, active)) return active;\n warnings.push(delimitersRedeclared(position));\n if (isNonStandard(declared)) warnings.push(nonStandardDelimiters(position));\n return declared;\n}\n\n/** Whether two delimiter sets agree on all four roles. */\nfunction sameDelimiters(a: Delimiters, b: Delimiters): boolean {\n return (\n a.field === b.field &&\n a.repeat === b.repeat &&\n a.component === b.component &&\n a.escape === b.escape\n );\n}\n\n/** Decode record bytes as latin1 so every byte survives to the string 1:1 (ASTM is byte-oriented). */\nfunction decodeBytes(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += String.fromCharCode(b);\n return out;\n}\n\n/** ASTM fields are 1-indexed; the type letter is field 1. Map an ASTM field number to the array slot. */\nfunction astmField(fields: readonly AstmField[], n: number): AstmField | undefined {\n return fields[n - 1];\n}\n\nfunction buildRecord(\n line: string,\n recordIndex: number,\n d: Delimiters,\n warnings: AstmRecordWarning[],\n): AstmRecord {\n const rawType = line.charAt(0);\n const onUnknownEscape = (fieldIndex: number): void => {\n warnings.push(\n unknownEscapeSequence({ recordIndex, recordType: rawType, fieldIndex: fieldIndex + 1 }),\n );\n };\n // The header needs its own tokenizer: the escape char sits literally inside the delimiter\n // declaration, and the generic escape-aware split reads it as an unterminated escape and\n // merges every following field into it.\n const fields =\n rawType === \"H\"\n ? tokenizeHeader(line, d, onUnknownEscape)\n : tokenizeRecord(line, d, onUnknownEscape);\n\n switch (rawType) {\n case \"H\":\n return {\n type: \"H\",\n recordIndex,\n fields,\n delimiters: d,\n rawLine: line,\n } satisfies HeaderRecord;\n case \"P\":\n return buildPatient(recordIndex, fields, warnings);\n case \"O\":\n return buildOrder(recordIndex, fields);\n case \"R\":\n return buildResult(recordIndex, fields, warnings);\n case \"C\":\n return buildComment(recordIndex, fields);\n case \"Q\":\n return buildQuery(recordIndex, fields, warnings);\n // `M` (manufacturer) and `S` (scientific) are vendor-defined free-form data surfaced VERBATIM and\n // NEVER interpreted into typed clinical fields: the exact wire line is preserved for round-trip.\n case \"M\":\n return { type: \"M\", recordIndex, fields, rawLine: line } satisfies ManufacturerRecord;\n case \"S\":\n return { type: \"S\", recordIndex, fields, rawLine: line } satisfies ScientificRecord;\n case \"L\":\n return { type: \"L\", recordIndex, fields } satisfies TerminatorRecord;\n default: {\n warnings.push(unknownRecordType({ recordIndex, recordType: rawType }));\n return { type: \"unsupported\", rawType, recordIndex, fields } satisfies UnsupportedRecord;\n }\n }\n}\n\nfunction buildPatient(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): PatientRecord {\n const nameField = astmField(fields, 6);\n const name: PatientName | undefined =\n nameField !== undefined && fieldScalar(nameField) !== undefined\n ? {\n raw: nameField.raw,\n ...definedString(\"last\", nameField.components[0]),\n ...definedString(\"first\", nameField.components[1]),\n ...definedString(\"middle\", nameField.components[2]),\n }\n : undefined;\n\n const birthDate = parseDateField(\n fieldScalar(astmField(fields, 8)),\n recordIndex,\n \"P\",\n 8,\n warnings,\n );\n\n return {\n type: \"P\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n // The three patient identifiers stay DISTINCT: practice-assigned (3), laboratory-assigned (4),\n // and a third ID (5) never collapse into one; conflating them is the primary misfiling path.\n ...definedString(\"practiceAssignedId\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"laboratoryAssignedId\", fieldScalar(astmField(fields, 4))),\n ...definedString(\"patientIdThree\", fieldScalar(astmField(fields, 5))),\n ...(name !== undefined ? { name } : {}),\n ...definedString(\"mothersMaidenName\", fieldScalar(astmField(fields, 7))),\n ...(birthDate !== undefined ? { birthDate } : {}),\n ...definedString(\"sex\", fieldScalar(astmField(fields, 9))),\n };\n}\n\nfunction buildOrder(recordIndex: number, fields: readonly AstmField[]): OrderRecord {\n const utidField = astmField(fields, 5);\n return {\n type: \"O\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...definedString(\"specimenId\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"instrumentSpecimenId\", fieldScalar(astmField(fields, 4))),\n ...(hasContent(utidField)\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n // Priority (6), action code (~12), and report type (~26) are surfaced verbatim. The `~` field\n // indices and the code sets are `[OSS-derived]` (paywalled), so they are never mapped to a\n // guessed meaning: see the JSDoc on `OrderRecord`.\n ...definedString(\"priority\", fieldScalar(astmField(fields, 6))),\n ...definedString(\"actionCode\", fieldScalar(astmField(fields, 12))),\n ...definedString(\"reportType\", fieldScalar(astmField(fields, 26))),\n };\n}\n\n/**\n * Whether a field carries any content at all: used to decide a Universal Test ID is present. The\n * scalar check is wrong here: a UTID's code lives in component 4 while component 1 (the LOINC slot)\n * is normally empty, so `fieldScalar` (first non-empty component) would miss `^^^687`.\n */\nfunction hasContent(field: AstmField | undefined): field is AstmField {\n return field !== undefined && field.raw.length > 0;\n}\n\nfunction buildResult(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): ResultRecord {\n const utidField = astmField(fields, 3);\n const valueField = astmField(fields, 4);\n const startedAt = parseDateField(\n fieldScalar(astmField(fields, 12)),\n recordIndex,\n \"R\",\n 12,\n warnings,\n );\n const completedAt = parseDateField(\n fieldScalar(astmField(fields, 13)),\n recordIndex,\n \"R\",\n 13,\n warnings,\n );\n\n // A value field with more than one component carried an *unescaped* component delimiter: an\n // ambiguity. Fail-safe: surface BOTH the full raw value (so nothing is truncated) AND the split,\n // and WARN. Never resolve it silently to a single (wrong, truncated) value.\n const ambiguous = valueField !== undefined && valueField.components.length > 1;\n const valueComponents = ambiguous ? valueField.components : undefined;\n // The primary `value` is the decoded scalar for the ordinary single-component case, but the FULL\n // raw field text when ambiguous, so `results(msg)[0].value` is never a truncated component.\n const value = ambiguous ? valueField.raw : fieldScalar(valueField);\n if (ambiguous) {\n warnings.push(ambiguousValueSplit({ recordIndex, recordType: \"R\", fieldIndex: 4 }));\n }\n\n // ── Phase 2: modeled, fail-safe result semantics (raw fields still surfaced alongside). ──\n const units = fieldScalar(astmField(fields, 5));\n // Reference range is surfaced from the FULL field text (not the first component), so a\n // component-delimited value (`low^high`) is preserved verbatim and read as `unparsed` + warned,\n // never truncated to a single bound. A present-but-empty field is treated as absent (no warn).\n const rangeRaw = fieldRaw(astmField(fields, 6));\n const flagRaw = fieldScalar(astmField(fields, 7));\n const statusRaw = fieldScalar(astmField(fields, 9));\n\n // Units: a *numeric* value with no units is the hazard (a bare magnitude is meaningless). Warn only\n // then: a qualitative result (e.g. \"POSITIVE\") legitimately has no units. Never default or guess.\n if (units === undefined && value !== undefined && isNumericValue(value)) {\n warnings.push(unitsAbsent({ recordIndex, recordType: \"R\", fieldIndex: 5 }));\n }\n\n const range = rangeRaw !== undefined ? parseReferenceRange(rangeRaw) : undefined;\n if (range?.kind === \"unparsed\") {\n warnings.push(unparseableReferenceRange({ recordIndex, recordType: \"R\", fieldIndex: 6 }));\n }\n\n const flag = flagRaw !== undefined ? interpretAbnormalFlag(flagRaw) : undefined;\n if (flag !== undefined && !flag.recognized) {\n warnings.push(undefinedAbnormalFlag({ recordIndex, recordType: \"R\", fieldIndex: 7 }));\n }\n\n // Status is ALWAYS modeled: an absent field 9 yields `unspecified` (never assumed `final`).\n const status = interpretResultStatus(statusRaw);\n if (statusRaw !== undefined && !status.recognized) {\n warnings.push(undefinedResultStatus({ recordIndex, recordType: \"R\", fieldIndex: 9 }));\n }\n\n return {\n type: \"R\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...(hasContent(utidField)\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n ...definedString(\"value\", value),\n ...(valueComponents !== undefined ? { valueComponents } : {}),\n ...definedString(\"units\", units),\n ...definedString(\"referenceRange\", rangeRaw),\n ...(range !== undefined ? { range } : {}),\n ...definedString(\"abnormalFlags\", flagRaw),\n ...(flag !== undefined ? { flag } : {}),\n ...definedString(\"resultStatus\", statusRaw),\n status,\n ...definedString(\"operator\", fieldScalar(astmField(fields, 11))),\n ...(startedAt !== undefined ? { startedAt } : {}),\n ...(completedAt !== undefined ? { completedAt } : {}),\n ...definedString(\"instrument\", fieldScalar(astmField(fields, 14))),\n };\n}\n\n/**\n * Build a `C` (comment) record from its fields. Parent attachment is resolved in a second pass\n * ({@link attachComments}); here `attachedToRoot` is a provisional `false` and `parentIndex` is unset.\n */\nfunction buildComment(recordIndex: number, fields: readonly AstmField[]): CommentRecord {\n const textField = astmField(fields, 4);\n // Comment text is component-capable: multiple components are a normal structured comment, not an\n // ambiguity, so no warning. Surface the FULL field text (never truncated) plus the component split.\n const textComponents =\n textField !== undefined && textField.components.length > 1 ? textField.components : undefined;\n const text = textField !== undefined && textField.raw.length > 0 ? textField.raw : undefined;\n\n return {\n type: \"C\",\n recordIndex,\n fields,\n attachedToRoot: false,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...definedString(\"source\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"text\", text),\n ...(textComponents !== undefined ? { textComponents } : {}),\n ...definedString(\"commentType\", fieldScalar(astmField(fields, 5))),\n };\n}\n\n/**\n * Build a `Q` (Request Information) record. The field *positions* are the public ASTM E1394 layout\n * (3 = starting range ID, 4 = ending range ID, 5 = Universal Test ID, 13 = request-info status); the\n * starting/ending range component structure, the `ALL` universal-query keyword, and the status code\n * set are all `[OSS-derived / paywalled]`, surfaced **verbatim** and **never interpreted or guessed**.\n * A present request-information status is flagged uninterpreted (the code set is paywalled).\n */\nfunction buildQuery(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): QueryRecord {\n const utidField = astmField(fields, 5);\n // The `ALL` universal-query keyword is recognized as a literal token only; its behavior is paywalled.\n const utidRaw = fieldRaw(utidField);\n const queriesAllTests = utidRaw !== undefined && utidRaw.trim().toUpperCase() === \"ALL\";\n\n // Surfaced from the FULL field text (fieldRaw, not fieldScalar) so it is literally verbatim: never\n // escape-decoded and never truncated to the first component, and so the uninterpreted-status warning\n // fires on ANY non-empty field-13 (matching the range-ID fields above). The status code set is\n // paywalled, so the value is passed through untouched, never interpreted.\n const requestInformationStatus = fieldRaw(astmField(fields, 13));\n if (requestInformationStatus !== undefined) {\n warnings.push(uninterpretedQueryStatus({ recordIndex, recordType: \"Q\", fieldIndex: 13 }));\n }\n\n return {\n type: \"Q\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n // Range IDs are surfaced from the FULL field text (never truncated to a component); their internal\n // caret structure is [OSS-derived]/paywalled and is not interpreted here.\n ...definedString(\"startingRangeId\", fieldRaw(astmField(fields, 3))),\n ...definedString(\"endingRangeId\", fieldRaw(astmField(fields, 4))),\n ...(hasContent(utidField) && !queriesAllTests\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n queriesAllTests,\n ...definedString(\"requestInformationStatus\", requestInformationStatus),\n };\n}\n\n/**\n * Attach every `C` (comment) record to its parent by position: the immediately-preceding\n * `H`/`P`/`O`/`R` record; consecutive comments share that parent. **Fail-safe:** a comment with no\n * valid preceding parent is an **orphan**, it is attached to the message root (`attachedToRoot:\n * true`) with an `ASTM_RECORD_ORPHAN_COMMENT` warning, **never dropped**. Returns a new array; the\n * non-comment records pass through unchanged.\n *\n * @param records - The built records, in wire order.\n * @param warnings - The accumulator an orphan comment warns onto.\n * @returns The records with each comment's `parentIndex` / `attachedToRoot` resolved.\n * @example\n * ```ts\n * import { attachComments } from \"@cosyte/astm\";\n * // A comment with no preceding H/P/O/R is an orphan attached to the root.\n * const warnings: import(\"@cosyte/astm\").AstmRecordWarning[] = [];\n * const out = attachComments(\n * [{ type: \"C\", recordIndex: 0, fields: [], attachedToRoot: false }],\n * warnings,\n * );\n * (out[0] as { attachedToRoot: boolean }).attachedToRoot; // true\n * ```\n */\nexport function attachComments(\n records: readonly AstmRecord[],\n warnings: AstmRecordWarning[],\n): AstmRecord[] {\n let lastParentIndex: number | undefined;\n return records.map((rec) => {\n if (rec.type === \"H\" || rec.type === \"P\" || rec.type === \"O\" || rec.type === \"R\") {\n lastParentIndex = rec.recordIndex;\n return rec;\n }\n if (rec.type !== \"C\") return rec;\n if (lastParentIndex === undefined) {\n warnings.push(orphanComment({ recordIndex: rec.recordIndex, recordType: \"C\" }));\n return { ...rec, attachedToRoot: true };\n }\n return { ...rec, parentIndex: lastParentIndex, attachedToRoot: false };\n });\n}\n\n/**\n * Parse a `YYYYMMDDHHMMSS` date field, emitting a value-free `ASTM_RECORD_PARTIAL_TIMESTAMP` warning\n * when the digit run truncates a component (an odd length that cuts a two-digit component in half).\n * The raw run is preserved and the structured value is never zero-filled into a fabricated time.\n */\nfunction parseDateField(\n raw: string | undefined,\n recordIndex: number,\n recordType: string,\n fieldIndex: number,\n warnings: AstmRecordWarning[],\n): AstmDate | undefined {\n if (raw === undefined) return undefined;\n const date = parseAstmDate(raw);\n if (date?.truncated === true) {\n warnings.push(partialTimestamp({ recordIndex, recordType, fieldIndex }));\n }\n return date;\n}\n\n/**\n * Whether a result value is purely numeric (optional comparator + sign + digits): the case where a\n * missing unit is a genuine hazard. A qualitative value (letters, e.g. `POSITIVE`) is excluded, so it\n * does not trip the units-absent warning.\n */\nfunction isNumericValue(value: string): boolean {\n return /^\\s*[<>]?\\s*-?\\d+(?:\\.\\d+)?\\s*$/u.test(value);\n}\n\n/** The full field text (all components), or `undefined` when the field is absent or empty. */\nfunction fieldRaw(field: AstmField | undefined): string | undefined {\n return field !== undefined && field.raw.length > 0 ? field.raw : undefined;\n}\n\n/** Spread helper: include `{ [key]: value }` only when `value` is a defined string. */\nfunction definedString(key: string, value: string | undefined): Record<string, string> {\n return value === undefined ? {} : { [key]: value };\n}\n","/**\n * The record-layer **emit** side: {@link serializeAstmRecords}.\n *\n * The inverse of `parseAstmRecords`. Where the parser is liberal (vendor quirks\n * become warnings), the serializer is **conservative**: it always emits a\n * spec-clean stream with the **canonical** delimiter set (`H|\\^&`), re-escapes\n * every embedded delimiter so an embedded `|`/`^`/`\\`/`&` in a value can never\n * break framing (the exact inverse of the escape codec), and terminates\n * each record with a `CR`.\n *\n * **Round-trip.** `parseAstmRecords(serializeAstmRecords(msg))` reproduces the modeled\n * records: the same components, the same typed accessors, and the canonical delimiter\n * set (a non-canonical source is normalized to `H|\\^&` by default). Normalizing sets the\n * four delimiter **roles**; it does not delete a declaration's surplus characters, so a\n * canonically-declared header that arrived carrying surplus keeps it on the default path\n * too.\n *\n * **One delimiter set for the whole stream.** Every record (including `H`, `M` and `S`)\n * is emitted against the delimiters being emitted with, and the header declares that same\n * set. `M` and `S` carry vendor free-form data and are reproduced **byte-for-byte** from\n * their preserved `rawLine` whenever a reader using those delimiters would recover exactly\n * the fields they model, which is the case for any stream already in the emit delimiter\n * set. When it is not, the record arrived under one delimiter set and is being emitted\n * under another, the record is re-encoded from its decoded tree instead, so its fields\n * survive as fields rather than collapsing into one on the next read.\n *\n * **Emit follows the model.** Every record type, `H` included, is emitted from its decoded\n * `AstmField.repeats` tree, so an edit to a record's `fields` is reflected on emit. The one\n * part of a record that is never taken from the model is the header's delimiter\n * declaration, and only in the three positions that carry a role: those always state the\n * repeat, component and escape characters actually in use, whatever the model says. Any\n * further characters the declaration carried, a reader assigns them no role, come from\n * the model like everything else, so a header that arrived as `H|\\^&#` goes back out as\n * `H|\\^&#` rather than losing the `#`. That holds on the **default** canonical path as\n * well as when a set is passed explicitly: the surplus of a canonically-declared header\n * survives normalization, because normalization is about the four roles and the surplus\n * has none.\n *\n * **Never break framing.** A component leaf that contains a record terminator\n * (`CR`/`LF`) cannot be escaped by the ASTM escape codec (only the four declared\n * delimiters have mnemonics), so emitting it would silently corrupt the wire.\n * The serializer refuses: an embedded `CR`/`LF` is a typed {@link AstmSerializeError},\n * never emitted raw.\n *\n * **Check the delimiter set before writing.** Three conditions are required for the\n * emitted bytes to read back as the records that produced them (each separator exactly\n * one character, no separator a `CR`/`LF`, no two separators the same character) and a\n * set failing any of them is a typed {@link AstmSerializeError}\n * (`ASTM_EMIT_INVALID_DELIMITERS`) rather than output this library's own parser would\n * reject or silently re-read with a different field tree. These are **necessary**\n * conditions, not a guarantee of readback: a set can pass all three and still produce a\n * stream that reads back wrong, a separator that collides with a record's type letter\n * is the known case, and it corrupts identically with or without this check.\n */\n\nimport { CANONICAL_DELIMITERS, type Delimiters } from \"../common/delimiters.js\";\nimport { tokenizeRecord } from \"./tokenize.js\";\nimport type {\n AstmField,\n AstmMessage,\n AstmRecord,\n HeaderRecord,\n ManufacturerRecord,\n ScientificRecord,\n} from \"./types.js\";\n\n/**\n * Thrown by the record/frame emit side when a value cannot be serialized into a\n * spec-clean stream: specifically when a component contains a record terminator\n * (`CR`/`LF`), which the ASTM escape codec cannot encode and which would break\n * framing if emitted raw. Carries a stable code + positional context, never the\n * offending value (PHI discipline).\n *\n * @example\n * ```ts\n * import { serializeAstmRecord, AstmSerializeError, parseAstmRecords } from \"@cosyte/astm\";\n * const rec = parseAstmRecords(\"H|\\\\^&\\rL|1\\r\").records[1]!;\n * try {\n * serializeAstmRecord(rec);\n * } catch (err) {\n * if (err instanceof AstmSerializeError) err.code; // \"ASTM_EMIT_UNENCODABLE_VALUE\"\n * }\n * ```\n */\nexport class AstmSerializeError extends Error {\n /**\n * Stable discriminant. `ASTM_EMIT_UNENCODABLE_VALUE` for a `CR`/`LF` in a value;\n * `ASTM_EMIT_INVALID_DELIMITERS` for a delimiter set that cannot be emitted\n * reversibly.\n */\n public readonly code: AstmSerializeErrorCode;\n /** 0-based ordinal of the record within the message, when known. */\n public readonly recordIndex?: number;\n /** @internal */\n public constructor(\n message: string,\n recordIndex?: number,\n code: AstmSerializeErrorCode = \"ASTM_EMIT_UNENCODABLE_VALUE\",\n ) {\n super(message);\n this.name = \"AstmSerializeError\";\n this.code = code;\n if (recordIndex !== undefined) this.recordIndex = recordIndex;\n }\n}\n\n/**\n * The reasons emit refuses rather than writing a stream that cannot be read back.\n *\n * - `ASTM_EMIT_UNENCODABLE_VALUE`: a component holds a record terminator\n * (`CR`/`LF`), which the escape codec has no mnemonic for.\n * - `ASTM_EMIT_INVALID_DELIMITERS`: the delimiter set to emit against failed one\n * of the three conditions readback requires (see {@link serializeAstmRecords}).\n * Those conditions are necessary rather than sufficient, so this code means a\n * set was rejected, not that every unreversible set is.\n *\n * @example\n * ```ts\n * import type { AstmSerializeErrorCode } from \"@cosyte/astm\";\n * const code: AstmSerializeErrorCode = \"ASTM_EMIT_INVALID_DELIMITERS\";\n * ```\n */\nexport type AstmSerializeErrorCode = \"ASTM_EMIT_UNENCODABLE_VALUE\" | \"ASTM_EMIT_INVALID_DELIMITERS\";\n\n/**\n * The three conditions a delimiter set must meet for emit to be reversible, in\n * the order they are checked. Each is a way the emitted bytes would otherwise\n * fail to read back as the records that produced them.\n */\nconst DELIMITER_ROLES = [\"field\", \"repeat\", \"component\", \"escape\"] as const;\n\n/**\n * Any C0 control character or `DEL`. Both the record layer and the frame layer\n * reserve characters from this range as structure (`CR`/`LF` end a record,\n * `STX`/`ETX`/`ETB` bound a frame) so none of them is ever carried through as\n * inert text.\n */\nconst CONTROL_CHARACTER = /[\\u0000-\\u001f\\u007f]/;\n\n/**\n * Refuse a delimiter set that cannot round-trip, before any bytes are written.\n *\n * **Why this refuses instead of warning.** Emit returns a bare `string` and has\n * no warning channel, so a warning here could only ever be ignored while the\n * unreadable stream still shipped. The three rules below are not style: each one\n * names a case where the emitted bytes provably do not read back as the records\n * that produced them, and every one of them was silent before, two of the six\n * shapes measured emitted a stream this library's own parser then re-read with a\n * **different** field tree and **no** warning at all, and the rest emitted a\n * stream it rejected outright. Refusing at the call is the only disposition that\n * reaches the caller.\n *\n * 1. **Each delimiter is exactly one character.** The reader takes each role from\n * a single position in the header's declaration, so a multi-character\n * separator is written but can never be read; an empty one matches everywhere\n * and shreds the values themselves.\n * 2. **No delimiter is a record terminator.** A `CR`/`LF` separator ends the\n * record instead of dividing it, truncating the stream.\n * 3. **The four are mutually distinct.** Two roles sharing a character makes the\n * boundary between them unrecoverable: a two-repeat field emitted with\n * `repeat` equal to `component` reads back as one repeat of two components,\n * losing structure with nothing to signal it.\n *\n * This is stricter than what the **parser** tolerates: a header may declare a set\n * where, say, `repeat` and `component` are the same character, and that stream\n * parses. Emitting against such a set is what cannot be undone, so a caller who\n * passes one through (`serializeAstmRecords(msg, msg.delimiters)` on such a\n * stream) now gets a typed error where it previously got silently lossy bytes.\n * That is a deliberate narrowing on a published package: the input it turns away\n * is exactly the input it was corrupting.\n *\n * **What this does not cover.** These three rules are necessary, not sufficient.\n * A set can satisfy all of them and still emit a stream that reads back wrong:\n * a separator equal to a record's type letter (`field` of `R`) is the known case,\n * because the type letter is then escaped away and the record re-reads as\n * unsupported. That corruption predates this check and is unchanged by it; it is\n * recorded as a known defect rather than fixed here, because the rule that would\n * catch it has to be derived rather than guessed, and deriving it while fixing\n * two unrelated gaps is how a fix outgrows the thing it fixes.\n *\n * The error never echoes the offending characters. A caller-supplied \"delimiter\"\n * that fails rule 1 can be arbitrary text, so quoting it back into a message\n * risks putting data into a log; naming the role is enough to fix the call.\n */\nfunction assertEmittableDelimiters(d: Delimiters, recordIndex?: number): void {\n const invalid = (reason: string): never => {\n throw new AstmSerializeError(\n `Cannot emit against these delimiters: ${reason}.`,\n recordIndex,\n \"ASTM_EMIT_INVALID_DELIMITERS\",\n );\n };\n\n // A caller reaching this from JavaScript can pass `null` where the types demand a\n // set, and `null` does not take the default parameter the way `undefined` does.\n // Checked first so that case is the documented typed error too.\n if (typeof d !== \"object\" || d === null) {\n invalid(\"a delimiter set is required\");\n }\n\n for (const role of DELIMITER_ROLES) {\n // The same caller can omit a member or pass a non-string. Checked before\n // `.length` so that case is the same typed error as any other unusable set,\n // rather than a `TypeError` from inside the serializer.\n const char: unknown = d[role];\n if (typeof char !== \"string\" || char.length !== 1) {\n invalid(`the ${role} delimiter must be exactly one character`);\n }\n if (char === \"\\r\" || char === \"\\n\") {\n invalid(`the ${role} delimiter must not be a record terminator (CR/LF)`);\n }\n }\n\n DELIMITER_ROLES.forEach((a, i) => {\n for (const b of DELIMITER_ROLES.slice(i + 1)) {\n if (d[a] === d[b]) {\n invalid(`the ${a} and ${b} delimiters must not be the same character`);\n }\n }\n });\n}\n\n/**\n * Escape-encode one component leaf for spec-clean emit: the inverse of\n * `decodeEscapes`. The **escape character itself is encoded first** (`&` → `&E&`)\n * so a later delimiter substitution can never double-encode the `&` it just\n * introduced; then the field / component / repeat delimiters map to their\n * mnemonics.\n *\n * A `CR`/`LF` in the leaf has no escape mnemonic and would break framing, so it\n * is rejected with an {@link AstmSerializeError} rather than emitted raw, as is a\n * delimiter set the result could not be read back with.\n *\n * @param leaf - One already-decoded component string.\n * @param d - The delimiters to emit against (canonical for spec-clean output).\n * @param recordIndex - The enclosing record's index, for error context.\n * @returns The escaped component text.\n * @throws {@link AstmSerializeError} for a `CR`/`LF` in the leaf\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or for a delimiter set failing one of the\n * three conditions readback requires (`ASTM_EMIT_INVALID_DELIMITERS`), which\n * are necessary, not sufficient, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { encodeComponent, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * encodeComponent(\"1^40\", CANONICAL_DELIMITERS); // \"1&S&40\"\n * ```\n */\nexport function encodeComponent(leaf: string, d: Delimiters, recordIndex?: number): string {\n assertEmittableDelimiters(d, recordIndex);\n return encodeLeaf(leaf, d, recordIndex);\n}\n\n/**\n * The escape encoder proper, for callers that have already checked `d`. Kept\n * separate so the delimiter check runs once per public call rather than once per\n * component leaf.\n */\nfunction encodeLeaf(leaf: string, d: Delimiters, recordIndex?: number): string {\n if (leaf.includes(\"\\r\") || leaf.includes(\"\\n\")) {\n throw new AstmSerializeError(\n \"A value contains a record terminator (CR/LF), which cannot be escaped without breaking framing.\",\n recordIndex,\n );\n }\n // Escape the escape char first, then the three structural delimiters.\n return leaf\n .split(d.escape)\n .join(d.escape + \"E\" + d.escape)\n .split(d.field)\n .join(d.escape + \"F\" + d.escape)\n .split(d.component)\n .join(d.escape + \"S\" + d.escape)\n .split(d.repeat)\n .join(d.escape + \"R\" + d.escape);\n}\n\n/** Encode one field from its decoded repeat/component tree, re-escaping each leaf. */\nfunction encodeField(\n repeats: readonly (readonly string[])[],\n d: Delimiters,\n recordIndex?: number,\n): string {\n return repeats\n .map((rep) => rep.map((c) => encodeLeaf(c, d, recordIndex)).join(d.component))\n .join(d.repeat);\n}\n\n/**\n * Serialize a single ASTM record to its spec-clean wire text (no trailing\n * terminator). Emits with the given delimiters, defaulting to the canonical set.\n *\n * The header (`H`) is special-cased: its delimiter-definition field is emitted as\n * the **literal** declaration of `d`, never escaped, escaping it would corrupt the\n * very declaration a reader depends on, followed by any characters the modeled\n * declaration carried beyond the three a reader takes its roles from. Manufacturer\n * (`M`) and scientific (`S`) records are reproduced **byte-identically** from their\n * preserved `rawLine` when they are already in `d`, and re-encoded from their fields\n * when they are not, so their fields never collapse into one on the next read.\n *\n * @param record - The record to serialize.\n * @param d - The delimiters to emit against; defaults to `H|\\^&`.\n * @returns The record's wire text, terminator excluded.\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { serializeAstmRecord, parseAstmRecords } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * serializeAstmRecord(msg.records[1]!); // \"R|1|^^^687|28.6|U/L||N||F\"\n * ```\n */\nexport function serializeAstmRecord(\n record: AstmRecord,\n d: Delimiters = CANONICAL_DELIMITERS,\n): string {\n assertEmittableDelimiters(d, record.recordIndex);\n return serializeRecordChecked(record, d);\n}\n\n/** {@link serializeAstmRecord} for callers that have already checked `d`. */\nfunction serializeRecordChecked(record: AstmRecord, d: Delimiters): string {\n if (record.type === \"M\" || record.type === \"S\") return serializeVerbatimRecord(record, d);\n\n if (record.type === \"H\") return serializeHeader(record, d);\n\n return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);\n}\n\n/**\n * Serialize an `M` (manufacturer) or `S` (scientific) record: the two vendor\n * free-form types the parser surfaces byte-for-byte.\n *\n * These records are re-emitted **verbatim from `rawLine` when, and only when, a\n * reader using the emit delimiters would recover exactly the fields the record\n * models**. When it would not, the record arrived under one delimiter set and\n * is being emitted under another, the record is re-encoded from its decoded\n * field tree instead, like every other record type.\n *\n * The choice being made here, and why. A blanket verbatim re-emit produced a\n * **mixed-delimiter stream**: the header declared one delimiter set while the\n * `M`/`S` rows still carried another, so re-parsing the output silently\n * collapsed every field of those rows into one, with no warning. On the\n * analyzer-to-LIS path a collapsed field is a lost result or a lost\n * patient/specimen identifier, and the caller had no signal. The two candidate\n * fixes were to re-encode the disagreeing records, or to refuse/warn on a\n * message whose records disagree. Re-encoding is chosen because (1) it is what\n * the serializer already promises: one spec-clean stream in the declared\n * delimiter set, and a mixed-delimiter stream is not spec-clean; (2) emit\n * returns a string and has no warning channel, so a warning could only be\n * ignored while the corrupt stream still shipped, and a refusal would reject\n * messages this library successfully parsed; and (3) the guard above means bytes\n * change for exactly the streams that were being corrupted and for no others.\n * The invariant that must hold either way is that a round-trip never silently\n * loses a field, in either direction.\n */\nfunction serializeVerbatimRecord(\n record: ManufacturerRecord | ScientificRecord,\n d: Delimiters,\n): string {\n if (recoversVerbatim(record, d)) return record.rawLine;\n return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);\n}\n\n/**\n * Whether re-tokenizing `record.rawLine` with the emit delimiters recovers the\n * exact field tree the record already models. True whenever the record's own\n * delimiters agree with the emit set (the ordinary case, where the raw bytes are\n * reproduced untouched), and true as well when the line simply contains no\n * delimiter either set would split on.\n */\nfunction recoversVerbatim(record: ManufacturerRecord | ScientificRecord, d: Delimiters): boolean {\n return sameFieldTree(tokenizeRecord(record.rawLine, d), record.fields);\n}\n\n/** Structural equality over two tokenized field trees, compared on decoded values. */\nfunction sameFieldTree(a: readonly AstmField[], b: readonly AstmField[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((fieldA, i) => {\n const fieldB = b[i];\n if (fieldB === undefined) return false;\n if (fieldA.repeats.length !== fieldB.repeats.length) return false;\n return fieldA.repeats.every((repA, r) => {\n const repB = fieldB.repeats[r];\n if (repB === undefined || repA.length !== repB.length) return false;\n return repA.every((comp, c) => comp === repB[c]);\n });\n });\n}\n\n/**\n * Serialize an `H` (header) record. The three delimiter roles the declaration\n * carries are emitted as the **literal** declaration of the delimiters being\n * emitted against, never escaped and never taken from the model: a header that\n * declares one delimiter set while the records around it use another is the exact\n * corruption this module refuses to produce. Every data field (field 3 onward) is\n * emitted from the header's tokenized {@link HeaderRecord.fields}, so an edit to\n * the model is reflected on emit, like every other record type.\n *\n * Anything the declaration carried **beyond** those three characters is carried\n * through: see {@link declarationResidual}.\n */\nfunction serializeHeader(header: HeaderRecord, d: Delimiters): string {\n const head = \"H\" + d.field + d.repeat + d.component + d.escape + declarationResidual(header, d);\n // fields[0] is the type letter and fields[1] is the delimiter declaration, both regenerated\n // above; the ASTM data fields start at fields[2]. No data fields ⇒ a bare `H|\\^&`.\n const dataFields = header.fields.slice(2);\n const rest = dataFields\n .map((f) => d.field + encodeField(f.repeats, d, header.recordIndex))\n .join(\"\");\n return head + rest;\n}\n\n/**\n * The bytes a header's delimiter declaration carries past the three it is read\n * for, `#` in a declaration of `\\^&#`, and whether they can be re-emitted.\n *\n * Only three characters of the declaration have a role: repeat, component and\n * escape, taken by position. A vendor may still declare more, and the reader\n * ignores the surplus rather than refusing the stream. Emit used to regenerate\n * the declaration from the three roles alone, so those surplus bytes were\n * **dropped with no signal** and a header that arrived as `H|\\^&#` went back out\n * as `H|\\^&`.\n *\n * The disposition chosen here is to **preserve** them, not to refuse and not to\n * report. Refusing would reject a stream the parser reads without complaint, on\n * a published package, over bytes it has already decided are inert; reporting is\n * not available at all, because emit returns a bare string with no warning\n * channel. Preserving costs nothing and is the only one of the three that makes\n * the round-trip byte-exact. What the surplus *means* remains unresolved, the\n * clauses that would settle it are not in the freely published material and were\n * not read, but carrying bytes through unread is a strictly smaller claim than\n * deleting them, and it keeps emit aligned with a reader that scopes delimiters\n * forward from every header: the re-read declaration resolves to the same four\n * roles either way.\n *\n * They are dropped whenever they could not be read back as surplus, which is one\n * of two things. Either the modeled declaration no longer begins with the three\n * roles being emitted (the header is being transcoded into a different set, and\n * the surplus belonged to the old declaration) or the surplus is not inert on\n * the wire: it contains the field delimiter, or **any control character**.\n *\n * The control-character rule is wider than the record layer alone needs, and\n * deliberately so. A `CR`/`LF` would end the record and shift every data field\n * along, but this text is also handed to the **frame** layer by\n * `serializeFramedAstm`, where `STX`, `ETX` and `ETB` are structural too: a\n * surplus carrying one of those truncates the frame body, and re-reading the\n * framed stream then drops the whole header record (its sender, its receiver,\n * its control ID) behind nothing but a checksum warning. Rather than enumerate\n * the bytes each layer happens to reserve, and re-derive that list every time a\n * layer is added, no control character is carried at all. The cost is bytes that\n * were unprintable inside a declaration whose meaning is already unresolved.\n *\n * **What this rule does not reach**, stated rather than left to be discovered.\n * It is keyed on the *character*, while the frame layer's structure is keyed on\n * the low byte: `src/frames/encode.ts` writes `charCodeAt(i) & 0xff`, so a\n * non-control character whose code point truncates onto `STX`/`ETX`/`ETB`\n * (`U+0102`, `U+0103`, `U+0117`) passes this guard and still breaks framing. It\n * fails loudly when it does (a typed parse error, or an unknown-record-type\n * warning), never silently, so the property this module holds is intact; the\n * truncation itself is a frame-layer defect that predates this rule and is\n * recorded separately. Nor does refusing a control character *here* imply one\n * cannot be a **delimiter role**: only `CR`/`LF` are refused as delimiters, so a\n * set declaring `STX` as its component separator is still accepted.\n *\n * Losing a field is a structural loss; dropping inert bytes is not.\n *\n * The surplus is read from `fields[1]`, the tokenized declaration, because\n * `fields` is the emit source for every record type: `rawLine` is provenance\n * and editing it has no effect on emit.\n */\nfunction declarationResidual(header: HeaderRecord, d: Delimiters): string {\n const declaration = header.fields[1]?.raw ?? \"\";\n const roles = d.repeat + d.component + d.escape;\n if (!declaration.startsWith(roles)) return \"\";\n const residual = declaration.slice(roles.length);\n if (residual.includes(d.field) || CONTROL_CHARACTER.test(residual)) return \"\";\n return residual;\n}\n\n/**\n * Serialize a whole ASTM message (or a bare record list) to a spec-clean,\n * `CR`-terminated record stream: the inverse of `parseAstmRecords`.\n *\n * Emit is **conservative**: the canonical `H|\\^&` delimiters, every embedded\n * delimiter re-escaped, each record closed with a `CR`. A message parsed with\n * non-canonical delimiters is **normalized** to the canonical set on emit: every\n * record, `M` and `S` included, so the emitted stream is in one delimiter set and\n * re-parsing it recovers every field. Passing `d` explicitly emits against that set\n * instead, and the header declares it. Normalization replaces the four delimiter\n * **roles**; a header declaration carrying characters beyond the three that hold a\n * role keeps them, on this path as on any other, rather than being truncated.\n *\n * **`d` is checked before anything is written.** Each of the four separators must be\n * exactly one character, none may be a record terminator, and no two may share a\n * character: otherwise the emitted bytes cannot be read back as the records that\n * produced them, and emit has no warning channel with which to say so. A set that\n * fails is an {@link AstmSerializeError} with code `ASTM_EMIT_INVALID_DELIMITERS`.\n * This is stricter than the parser, which reads some sets it cannot reverse, so\n * `serializeAstmRecords(msg, msg.delimiters)` can refuse a message that parsed:\n * in exactly the cases where it used to emit a stream that read back wrong. The\n * three conditions are necessary rather than sufficient: a set that passes them can\n * still read back wrong if a separator collides with a record's type letter, which\n * this check does not claim to catch.\n *\n * @param input - A parsed {@link AstmMessage} or a list of {@link AstmRecord}s.\n * @param d - The delimiters to emit against; defaults to the canonical `H|\\^&` set.\n * @returns The serialized record stream (`CR` after every record).\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { parseAstmRecords, serializeAstmRecords } from \"@cosyte/astm\";\n * const raw = \"H|\\\\^&\\rP|1\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\";\n * serializeAstmRecords(parseAstmRecords(raw)); // === raw\n * ```\n */\nexport function serializeAstmRecords(\n input: AstmMessage | readonly AstmRecord[],\n d: Delimiters = CANONICAL_DELIMITERS,\n): string {\n assertEmittableDelimiters(d);\n const records: readonly AstmRecord[] = Array.isArray(input)\n ? (input as readonly AstmRecord[])\n : (input as AstmMessage).records;\n return records.map((r) => serializeRecordChecked(r, d) + \"\\r\").join(\"\");\n}\n\n/**\n * Serialize a single {@link AstmField} to its spec-clean wire text, re-escaping\n * each component. A low-level helper for callers assembling a field outside a\n * whole record.\n *\n * @param field - The field to serialize.\n * @param d - The delimiters to emit against; defaults to `H|\\^&`.\n * @returns The escaped field text.\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { serializeField, tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeRecord(\"R|1|^^^687|1&S&40\", CANONICAL_DELIMITERS);\n * serializeField(fields[3]!); // \"1&S&40\"\n * ```\n */\nexport function serializeField(field: AstmField, d: Delimiters = CANONICAL_DELIMITERS): string {\n assertEmittableDelimiters(d);\n return encodeField(field.repeats, d);\n}\n","/**\n * The record-layer **builder**: {@link buildAstmMessage}.\n *\n * A builder produces spec-clean E1394 records **by construction** from typed\n * caller input, under two disciplines:\n *\n * - **Never fabricate.** A builder emits **only** the values the caller supplied.\n * An unsupplied field is left **empty**, never filled with a default clinical\n * value: no defaulted result status, abnormal flag, units, value, or patient\n * identifier. (Leaving a field empty is not a guess; inventing an `F` status or\n * an `N` flag would be.) A consumer that parses a built result whose status the\n * caller omitted reads `status.meaning === \"unspecified\"`, never `\"final\"`.\n * - **Compute, don't guess, the structure.** The record type letters, the\n * delimiter declaration (canonical `H|\\^&`), the message terminator (`L`), and\n * the per-record sequence counters are **structural**: computed by the builder,\n * not clinical values. The caller may override any sequence number.\n *\n * Every value is escape-encoded on emit (via the serializer), so an embedded\n * delimiter can never break framing, and a value carrying an unencodable `CR`/`LF`\n * is a typed {@link AstmSerializeError} rather than a corrupted wire.\n */\n\nimport { CANONICAL_DELIMITERS } from \"../common/delimiters.js\";\nimport { AstmSerializeError, encodeComponent } from \"./serialize.js\";\n\n/** A patient name split into its components; only the supplied parts are emitted. */\nexport interface PatientNameInput {\n readonly last?: string;\n readonly first?: string;\n readonly middle?: string;\n}\n\n/** Input for a `P` (patient) record. The three IDs stay distinct: none defaults from another. */\nexport interface PatientInput {\n readonly type: \"P\";\n /** Structural sequence number; auto-computed when omitted. */\n readonly seq?: string;\n /** Field 3: practice-assigned patient ID. */\n readonly practiceAssignedId?: string;\n /** Field 4: laboratory-assigned patient ID. */\n readonly laboratoryAssignedId?: string;\n /** Field 5: a third patient identifier. */\n readonly patientIdThree?: string;\n /** Field 6: patient name (`Last^First^Middle`). */\n readonly name?: PatientNameInput;\n /** Field 7: mother's maiden name. */\n readonly mothersMaidenName?: string;\n /** Field 8: birthdate (`YYYYMMDDHHMMSS`), emitted verbatim, never reformatted. */\n readonly birthDate?: string;\n /** Field 9: sex, emitted verbatim (never defaulted). */\n readonly sex?: string;\n}\n\n/** Input for an `O` (order) record. */\nexport interface OrderInput {\n readonly type: \"O\";\n readonly seq?: string;\n /** Field 3: specimen / accession ID. */\n readonly specimenId?: string;\n /** Field 4: instrument specimen ID. */\n readonly instrumentSpecimenId?: string;\n /** Field 5: Universal Test ID components (verbatim, in order; e.g. `[\"\", \"\", \"\", \"687\"]`). */\n readonly universalTestId?: readonly string[];\n /** Field 6: priority, emitted verbatim. */\n readonly priority?: string;\n /** Field 12: action code, emitted verbatim. */\n readonly actionCode?: string;\n /** Field 26: report type, emitted verbatim. */\n readonly reportType?: string;\n}\n\n/** Input for an `R` (result) record. No clinical field is defaulted; unsupplied ⇒ empty. */\nexport interface ResultInput {\n readonly type: \"R\";\n readonly seq?: string;\n /** Field 3: Universal Test ID components (verbatim). */\n readonly universalTestId?: readonly string[];\n /** Field 4: the measured value, emitted verbatim (never defaulted). */\n readonly value?: string;\n /** Field 5: units (vendor free text; never defaulted, guessed, or converted). */\n readonly units?: string;\n /** Field 6: reference range, emitted verbatim. */\n readonly referenceRange?: string;\n /** Field 7: abnormal flags, emitted verbatim (never defaulted to `N`). */\n readonly abnormalFlags?: string;\n /** Field 9: result status, emitted verbatim (never defaulted to `F`). */\n readonly resultStatus?: string;\n /** Field 11: operator. */\n readonly operator?: string;\n /** Field 12: test-started timestamp (`YYYYMMDDHHMMSS`), verbatim. */\n readonly startedAt?: string;\n /** Field 13: test-completed timestamp, verbatim. */\n readonly completedAt?: string;\n /** Field 14: instrument identifier. */\n readonly instrument?: string;\n}\n\n/** Input for a `C` (comment) record. */\nexport interface CommentInput {\n readonly type: \"C\";\n readonly seq?: string;\n /** Field 3: comment source. */\n readonly source?: string;\n /** Field 4: comment text (a single component). Use {@link CommentInput.textComponents} for a structured comment. */\n readonly text?: string;\n /** Field 4: comment text as explicit components (takes precedence over `text` when set). */\n readonly textComponents?: readonly string[];\n /** Field 5: comment type code, emitted verbatim. */\n readonly commentType?: string;\n}\n\n/** Input for a `Q` (request-information) record. */\nexport interface QueryInput {\n readonly type: \"Q\";\n readonly seq?: string;\n /** Field 3: starting range ID, emitted verbatim. */\n readonly startingRangeId?: string;\n /** Field 4: ending range ID, emitted verbatim. */\n readonly endingRangeId?: string;\n /** Field 5: Universal Test ID components; ignored when {@link QueryInput.queriesAllTests} is set. */\n readonly universalTestId?: readonly string[];\n /** Field 5: emit the literal `ALL` universal-query keyword instead of a Universal Test ID. */\n readonly queriesAllTests?: boolean;\n /** Field 13: request-information status, emitted verbatim. */\n readonly requestInformationStatus?: string;\n}\n\n/**\n * Input for an `M` (manufacturer) or `S` (scientific) record: vendor-defined\n * free-form data. Emitted verbatim from the caller's fields; never interpreted.\n */\nexport interface VerbatimInput {\n readonly type: \"M\" | \"S\";\n /** Data fields (after the type letter), emitted verbatim in order. */\n readonly fields: readonly string[];\n}\n\n/** Any record the message builder can emit (the terminator `L` is appended automatically). */\nexport type AstmRecordInput =\n | PatientInput\n | OrderInput\n | ResultInput\n | CommentInput\n | QueryInput\n | VerbatimInput;\n\n/** Optional header fields (after the delimiter declaration). */\nexport interface HeaderInput {\n /** Extra `H`-record fields (field 3 onward), emitted verbatim in order. Field 2 is always `\\^&`. */\n readonly fields?: readonly string[];\n}\n\n/** The message to build: an optional header, the body records, and an optional terminator code. */\nexport interface MessageInput {\n /** Header fields; the canonical `H|\\^&` declaration is always emitted. */\n readonly header?: HeaderInput;\n /** The body records, in order. `H` and `L` are supplied by the builder. */\n readonly records: readonly AstmRecordInput[];\n /**\n * Field 3 of the auto-appended `L` (terminator) record: the termination code\n * (e.g. `N` normal). Omitted by default (not defaulted to a value the caller\n * did not choose); the `L` seq is always emitted.\n */\n readonly terminationCode?: string;\n}\n\n/** Join a positional field array into a record line, trimming trailing empty fields. */\nfunction line(fields: readonly (readonly string[])[]): string {\n const d = CANONICAL_DELIMITERS;\n const encoded = fields.map((comps) => comps.map((c) => encodeComponent(c, d)).join(d.component));\n // Trim trailing empty fields: an absent trailing field and an empty one are equivalent on parse.\n let end = encoded.length;\n while (end > 0 && encoded[end - 1] === \"\") end -= 1;\n return encoded.slice(0, end).join(d.field);\n}\n\n/** Build the components of a Universal Test ID field from a verbatim component list. */\nfunction utid(components: readonly string[] | undefined): readonly string[] {\n return components === undefined ? [\"\"] : [...components];\n}\n\n/** Set a 1-based ASTM field position in a sparse builder array (index 0 is the type letter). */\nfunction at(fields: string[][], position1: number, value: string | undefined): void {\n if (value === undefined) return;\n fields[position1 - 1] = [value];\n}\n\nfunction buildPatientLine(input: PatientInput, seq: string): string {\n const f: string[][] = [[\"P\"], [seq]];\n at(f, 3, input.practiceAssignedId);\n at(f, 4, input.laboratoryAssignedId);\n at(f, 5, input.patientIdThree);\n if (input.name !== undefined) {\n f[5] = [input.name.last ?? \"\", input.name.first ?? \"\", input.name.middle ?? \"\"];\n }\n at(f, 7, input.mothersMaidenName);\n at(f, 8, input.birthDate);\n at(f, 9, input.sex);\n return line(padGaps(f));\n}\n\nfunction buildOrderLine(input: OrderInput, seq: string): string {\n const f: string[][] = [[\"O\"], [seq]];\n at(f, 3, input.specimenId);\n at(f, 4, input.instrumentSpecimenId);\n if (input.universalTestId !== undefined) f[4] = [...utid(input.universalTestId)];\n at(f, 6, input.priority);\n at(f, 12, input.actionCode);\n at(f, 26, input.reportType);\n return line(padGaps(f));\n}\n\nfunction buildResultLine(input: ResultInput, seq: string): string {\n const f: string[][] = [[\"R\"], [seq]];\n if (input.universalTestId !== undefined) f[2] = [...utid(input.universalTestId)];\n at(f, 4, input.value);\n at(f, 5, input.units);\n at(f, 6, input.referenceRange);\n at(f, 7, input.abnormalFlags);\n at(f, 9, input.resultStatus);\n at(f, 11, input.operator);\n at(f, 12, input.startedAt);\n at(f, 13, input.completedAt);\n at(f, 14, input.instrument);\n return line(padGaps(f));\n}\n\nfunction buildCommentLine(input: CommentInput, seq: string): string {\n const f: string[][] = [[\"C\"], [seq]];\n at(f, 3, input.source);\n if (input.textComponents !== undefined) f[3] = [...input.textComponents];\n else at(f, 4, input.text);\n at(f, 5, input.commentType);\n return line(padGaps(f));\n}\n\nfunction buildQueryLine(input: QueryInput, seq: string): string {\n const f: string[][] = [[\"Q\"], [seq]];\n at(f, 3, input.startingRangeId);\n at(f, 4, input.endingRangeId);\n if (input.queriesAllTests === true) f[4] = [\"ALL\"];\n else if (input.universalTestId !== undefined) f[4] = [...utid(input.universalTestId)];\n at(f, 13, input.requestInformationStatus);\n return line(padGaps(f));\n}\n\nfunction buildVerbatimLine(input: VerbatimInput, seq: string): string {\n // M / S records are surfaced VERBATIM by the parser, so the builder emits the caller's field text\n // byte-for-byte (never escaped, the caller owns the exact bytes). A CR/LF is still refused: it\n // cannot be represented inside a record and would break framing.\n return [input.type, seq, ...input.fields.map(guardWireSafe)].join(CANONICAL_DELIMITERS.field);\n}\n\n/** Reject a verbatim field carrying a record terminator (`CR`/`LF`), which would break framing. */\nfunction guardWireSafe(value: string): string {\n if (value.includes(\"\\r\") || value.includes(\"\\n\")) {\n throw new AstmSerializeError(\n \"A verbatim field contains a record terminator (CR/LF), which would break framing.\",\n );\n }\n return value;\n}\n\n/** Fill sparse gaps in a positional field array with a single empty component. */\nfunction padGaps(fields: string[][]): string[][] {\n const out: string[][] = [];\n for (let i = 0; i < fields.length; i += 1) out[i] = fields[i] ?? [\"\"];\n return out;\n}\n\n/**\n * Build a spec-clean ASTM/CLSI-LIS02 record stream from typed input.\n *\n * The builder emits the canonical `H|\\^&` header (plus any header fields), then\n * every body record with each supplied field at its correct 1-based position and\n * every value escape-encoded, then an `L` terminator. **Nothing clinical is\n * defaulted** (an unsupplied field is empty, never a guessed value) and the\n * structural pieces (delimiters, record types, sequence counters, terminator) are\n * computed. The result parses back to an equal message: build → parse fidelity by\n * construction.\n *\n * @param input - The message to build.\n * @returns The serialized, `CR`-terminated record stream.\n * @throws {@link AstmSerializeError} when a value contains an unencodable `CR`/`LF`.\n * @example\n * ```ts\n * import { buildAstmMessage, parseAstmRecords, results } from \"@cosyte/astm\";\n * const raw = buildAstmMessage({\n * records: [{ type: \"R\", universalTestId: [\"\", \"\", \"\", \"687\"], value: \"28.6\", units: \"U/L\" }],\n * });\n * results(parseAstmRecords(raw))[0]?.value; // \"28.6\"\n * ```\n */\nexport function buildAstmMessage(input: MessageInput): string {\n const lines: string[] = [];\n\n // Header: the canonical delimiter declaration is emitted LITERALLY (never escaped, escaping the\n // very declaration a reader depends on would corrupt it); any extra header fields are escaped.\n const d = CANONICAL_DELIMITERS;\n // Header data fields (sender name / version etc.) are emitted verbatim: they legitimately carry\n // component structure (`analyzer^cobas^1`); only a framing-breaking CR/LF is refused.\n const headerFields = input.header?.fields ?? [];\n const headerRest = headerFields.map((v) => d.field + guardWireSafe(v)).join(\"\");\n lines.push(\"H\" + d.field + d.repeat + d.component + d.escape + headerRest);\n\n // Per-record-type sequence counters (structural; the caller may override each seq).\n const counters = new Map<string, number>();\n const nextSeq = (type: string, override?: string): string => {\n if (override !== undefined) return override;\n const n = (counters.get(type) ?? 0) + 1;\n counters.set(type, n);\n return String(n);\n };\n\n for (const rec of input.records) {\n switch (rec.type) {\n case \"P\":\n lines.push(buildPatientLine(rec, nextSeq(\"P\", rec.seq)));\n break;\n case \"O\":\n lines.push(buildOrderLine(rec, nextSeq(\"O\", rec.seq)));\n break;\n case \"R\":\n lines.push(buildResultLine(rec, nextSeq(\"R\", rec.seq)));\n break;\n case \"C\":\n lines.push(buildCommentLine(rec, nextSeq(\"C\", rec.seq)));\n break;\n case \"Q\":\n lines.push(buildQueryLine(rec, nextSeq(\"Q\", rec.seq)));\n break;\n case \"M\":\n case \"S\":\n lines.push(buildVerbatimLine(rec, nextSeq(rec.type)));\n break;\n default: {\n // Exhaustiveness: an unrecognized record type is a structural error, not a guess.\n const bad = rec as { type?: unknown };\n throw new TypeError(`Unknown ASTM record type to build: ${String(bad.type)}`);\n }\n }\n }\n\n // Terminator: structural. Field 3 termination code only when the caller chose one.\n const term: string[][] = [[\"L\"], [\"1\"]];\n if (input.terminationCode !== undefined) term[2] = [input.terminationCode];\n lines.push(line(padGaps(term)));\n\n return lines.map((l) => l + \"\\r\").join(\"\");\n}\n","/**\n * Message grouping: the safe replacement for the stream-scoped extractors.\n *\n * A parsed {@link AstmMessage} models a whole **record stream**, and a stream may carry\n * several messages back to back: a message runs from an `H` header to its `L` terminator.\n * The flat extractors in `./extractors.js` read the whole stream, so on a multi-message\n * stream `patient(msg)` answered with the first `P` in the stream while `results(msg)`\n * answered with every `R` in it: pairing the two, which is exactly what the package's\n * one-line north star does, attributed one patient's results to another. It needed no\n * redeclaration and no unusual delimiters, and it warned about nothing.\n *\n * {@link messages} is the fix: it splits the stream into the messages it actually\n * contains, and each one carries only its own records, so a patient and a result are only\n * ever paired inside the message that carried both.\n *\n * **Where a message begins.** A new message opens at every `H` record and runs to the\n * record before the next `H` (or the end of the stream). That is deliberately the *same*\n * boundary the parser already uses to scope delimiters: the active delimiter set is\n * re-read at each `H` and governs that header and the records that follow it, so message\n * boundaries and delimiter scope cannot disagree. The `L` terminator **closes** a message\n * but does not open a scope, so anything between an `L` and the next `H` stays with the\n * message its header opened rather than being dropped. Grouping is therefore **total**:\n * every record of the stream lands in exactly one message, none duplicated, none lost.\n *\n * **What the standard grounds, and what it does not.** CLSI LIS02-A2 §2 is definitional\n * about the unit itself: a message is bounded by the `H` record at one end and the `L`\n * record at the other. That clause is in CLSI's free sample and is the whole of what is\n * claimed from the standard here. It does **not** settle which record *opens* a new scope\n * partway through a stream, so treating a second `H` as the start of the next message\n * (rather than, say, requiring an intervening `L`) is a reasoned choice, not a citation. It\n * is made this way because it is the boundary this parser already enforces for delimiters,\n * and because it never drops a record.\n *\n * **What is deliberately not modeled.** ASTM's within-message record hierarchy, which `P`\n * a given `R` files against when one message carries several patients, is not modeled\n * here. The clauses that would ground it (the message-level structure diagram, and the `P`\n * sequence-number rule) are withheld from the free sample and are paywalled, so rather than\n * guess a scoping rule this layer refuses to answer: a message carrying more than one `P`\n * leaves {@link AstmStreamMessage.patient} `undefined` and surfaces all of them on\n * {@link AstmStreamMessage.patients}. Multi-patient messages are real, at least one\n * openly-published vendor interface grammar makes the patient group repeatable on the\n * download direction, so this is a deferral with a known shape, not a claim that the case\n * does not arise, and not a claim that the standard is silent about it.\n *\n * **The OSS corpus was checked and offers no prior art.** It is informative here, unlike\n * for delimiter questions, but only negatively: the two commonly-cited reference\n * implementations both dissolve the message unit. One dispatches records to per-type\n * callbacks one at a time with no carried state, leaving the grouping to the integrator;\n * the other merges every message of a transport session into one envelope of flat\n * per-record-type buckets, from which the association cannot be recovered at all. A flat\n * model is the incumbent shape, and the misattribution above is what it costs.\n */\n\nimport type { Delimiters } from \"../common/delimiters.js\";\nimport type { AstmPosition } from \"../common/position.js\";\nimport type {\n AstmMessage,\n AstmRecord,\n CommentRecord,\n HeaderRecord,\n OrderRecord,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n} from \"./types.js\";\n\n/**\n * Stable codes for the two ways a flat, stream-scoped accessor can fail to name a single\n * answer. Renaming a code is a **breaking change**.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, patient, AMBIGUOUS_CODES, AstmAmbiguousStreamError } from \"@cosyte/astm\";\n * try {\n * patient(parseAstmRecords(raw));\n * } catch (err) {\n * if (err instanceof AstmAmbiguousStreamError) {\n * err.code === AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_MESSAGE;\n * }\n * }\n * ```\n */\nexport const AMBIGUOUS_CODES = {\n /** The stream carries more than one `H` … `L` message, so a stream-wide answer spans patients. */\n ASTM_AMBIGUOUS_MULTI_MESSAGE: \"ASTM_AMBIGUOUS_MULTI_MESSAGE\",\n /** The one message in the stream carries more than one `P`, so \"the patient\" is not determined. */\n ASTM_AMBIGUOUS_MULTI_PATIENT: \"ASTM_AMBIGUOUS_MULTI_PATIENT\",\n} as const;\n\n/** A value from {@link AMBIGUOUS_CODES}: the discriminant on {@link AstmAmbiguousStreamError}. */\nexport type AmbiguousCode = (typeof AMBIGUOUS_CODES)[keyof typeof AMBIGUOUS_CODES];\n\n/**\n * Thrown by a flat extractor when the stream it was handed does not determine a single\n * answer: either because it carries several messages, or because its one message carries\n * several patients.\n *\n * The throw **is** the fix. A caller who gets this error is strictly better off than one\n * who silently received another patient's results, and the remedy is mechanical: walk\n * {@link messages} and read each message's own records.\n *\n * **PHI:** carries a stable code, a position, and two counts. Never a field value, never a\n * patient identifier.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, messages, results, AstmAmbiguousStreamError } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw);\n * try {\n * results(msg);\n * } catch (err) {\n * if (err instanceof AstmAmbiguousStreamError) {\n * for (const m of messages(msg)) m.results.length;\n * }\n * }\n * ```\n */\nexport class AstmAmbiguousStreamError extends Error {\n /** The stable discriminant: see {@link AMBIGUOUS_CODES}. */\n public readonly code: AmbiguousCode;\n /** Where the ambiguity became visible: the second header, or the second `P`. Value-free. */\n public readonly position: AstmPosition;\n /** How many `H` … `L` messages the stream carries. */\n public readonly messageCount: number;\n /** How many `P` records the message in question carries. */\n public readonly patientCount: number;\n\n /** @internal */\n public constructor(\n code: AmbiguousCode,\n message: string,\n position: AstmPosition,\n counts: { readonly messageCount: number; readonly patientCount: number },\n ) {\n super(message);\n this.name = \"AstmAmbiguousStreamError\";\n this.code = code;\n this.position = position;\n this.messageCount = counts.messageCount;\n this.patientCount = counts.patientCount;\n }\n}\n\n/**\n * One `H` … `L` message inside a parsed stream, with its own records and nothing else.\n *\n * This is the unit the flat extractors were always assumed to be reading. `AstmMessage`\n * models the whole stream; this models a message within it.\n */\nexport interface AstmStreamMessage {\n /** 0-based ordinal of this message within the stream. */\n readonly index: number;\n /** This message's header record. */\n readonly header: HeaderRecord;\n /**\n * The delimiter set in force for this message: the header's own resolved set, which is\n * the set its records were actually read with. When a later header's declaration was\n * unusable, this is the set that stayed in force, never a guessed one.\n */\n readonly delimiters: Delimiters;\n /** Every record of this message in wire order, the header first. */\n readonly records: readonly AstmRecord[];\n /**\n * This message's patient, but **only when the message determines one**: the single `P`\n * when it carries exactly one, and `undefined` when it carries none **or** several.\n * `patients.length` distinguishes those two cases; there is no third meaning.\n *\n * A message carrying several patients is not answered with the first one, because\n * \"the first `P`\" is precisely the guess that files a result against the wrong person.\n */\n readonly patient: PatientRecord | undefined;\n /** Every `P` record in this message, in wire order (usually zero or one). */\n readonly patients: readonly PatientRecord[];\n /** Every `R` (result) record in this message, in wire order. */\n readonly results: readonly ResultRecord[];\n /** Every `O` (order) record in this message, in wire order. */\n readonly orders: readonly OrderRecord[];\n /** Every `C` (comment) record in this message, in wire order. */\n readonly comments: readonly CommentRecord[];\n /**\n * Every `Q` (request-information) record in this message, in wire order.\n *\n * For this message's host-query classification, call `classifyMessage(m.records)`. The\n * `classification` on the parsed model is folded over the **whole stream**, so it is not\n * per-message and is not mirrored here; deriving it from a message's own records is.\n */\n readonly queries: readonly QueryRecord[];\n}\n\n/**\n * Split a parsed stream into the `H` … `L` messages it carries.\n *\n * This is the safe path and it never throws: a single-message stream yields exactly one\n * entry, and pairing a patient with a result inside one entry is correct by construction.\n *\n * Returns an **array**, not an iterator: the stream is already fully parsed and frozen, so\n * there is no work to defer, and every other extractor in this package returns a\n * `readonly` array.\n *\n * @param msg - A parsed stream.\n * @returns The messages, in wire order; never empty for a parsed stream.\n * @example\n * ```ts\n * import { parseAstmRecords, messages } from \"@cosyte/astm\";\n * const stream = parseAstmRecords(\"H|\\\\^&\\rP|1|PRAC-1\\rR|1|^^^687|1.0|U/L||N||F\\rL|1\\r\");\n * for (const m of messages(stream)) {\n * m.patient?.practiceAssignedId; // \"PRAC-1\"\n * m.results[0]?.value; // \"1.0\"\n * }\n * ```\n */\nexport function messages(msg: AstmMessage): readonly AstmStreamMessage[] {\n const groups: AstmRecord[][] = [];\n let current: AstmRecord[] | undefined;\n for (const record of msg.records) {\n if (record.type === \"H\" || current === undefined) {\n current = [];\n groups.push(current);\n }\n current.push(record);\n }\n\n return Object.freeze(\n groups.map((records, index) => {\n const header = records.find((r): r is HeaderRecord => r.type === \"H\") ?? msg.header;\n const patients = records.filter((r): r is PatientRecord => r.type === \"P\");\n return Object.freeze({\n index,\n header,\n delimiters: header.delimiters,\n records: Object.freeze(records),\n patient: patients.length === 1 ? patients[0] : undefined,\n patients: Object.freeze(patients),\n results: Object.freeze(records.filter((r): r is ResultRecord => r.type === \"R\")),\n orders: Object.freeze(records.filter((r): r is OrderRecord => r.type === \"O\")),\n comments: Object.freeze(records.filter((r): r is CommentRecord => r.type === \"C\")),\n queries: Object.freeze(records.filter((r): r is QueryRecord => r.type === \"Q\")),\n });\n }),\n );\n}\n\n/**\n * How many `H` … `L` messages a stream carries, and where the second one starts.\n *\n * Counts `H` records directly rather than materializing the groups: the flat extractors\n * call this on every invocation, and the answer is the same by construction.\n *\n * @internal\n */\nfunction surveyMessages(msg: AstmMessage): {\n readonly count: number;\n readonly secondHeader: AstmRecord | undefined;\n} {\n let count = 0;\n let secondHeader: AstmRecord | undefined;\n for (const record of msg.records) {\n if (record.type !== \"H\") continue;\n count += 1;\n if (count === 2) secondHeader = record;\n }\n return { count, secondHeader };\n}\n\n/**\n * Guard for a flat, stream-scoped extractor: refuse to answer for a stream carrying more\n * than one message.\n *\n * @internal\n */\nexport function assertSingleMessage(msg: AstmMessage, accessor: string): void {\n const { count, secondHeader } = surveyMessages(msg);\n if (count <= 1) return;\n throw new AstmAmbiguousStreamError(\n AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_MESSAGE,\n `${accessor} is scoped to the whole stream, and this stream carries ${String(count)} messages, so its answer would span patients. Use messages() and read each message's own records.`,\n { recordIndex: secondHeader?.recordIndex ?? 0, recordType: \"H\" },\n { messageCount: count, patientCount: 0 },\n );\n}\n\n/**\n * Guard for {@link patient}: refuse to answer for a message carrying more than one `P`.\n *\n * The same harm as the multi-message case, one level down: \"the first `P`\" is a guess\n * about which patient a result files against, and this parser does not guess.\n *\n * @internal\n */\nexport function assertSinglePatient(msg: AstmMessage, accessor: string): void {\n const patients = msg.records.filter((r): r is PatientRecord => r.type === \"P\");\n if (patients.length <= 1) return;\n throw new AstmAmbiguousStreamError(\n AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_PATIENT,\n `${accessor} cannot name one patient: this message carries ${String(patients.length)} P records. Read them from messages()[n].patients and pair each with the records it owns.`,\n { recordIndex: patients[1]?.recordIndex ?? 0, recordType: \"P\" },\n { messageCount: 1, patientCount: patients.length },\n );\n}\n","/**\n * Typed extractors over an {@link AstmMessage}: the one-line \"pull the value out\"\n * surface that is the package's north star.\n *\n * They are thin and immutable: they read the already-parsed model and never re-parse,\n * never mutate, and never fabricate.\n *\n * **They are scoped to a single message, and they enforce it.** An `AstmMessage` models a\n * whole record **stream**, which may carry several `H` … `L` messages back to back. These\n * extractors read across the whole stream, so on a multi-message stream `patient()` used to\n * answer with the first `P` in the stream while `results()` answered with every `R` in it:\n * pairing the two attributed one patient's results to another, silently and with no warning.\n * They now **refuse** that stream: an {@link AstmAmbiguousStreamError} instead of a confident\n * wrong answer, which is the same rule the rest of this parser follows. {@link messages} is\n * the path that works on any stream, and `commentsFor()` keeps working on every stream\n * because its parent record already names the message.\n *\n * @see {@link messages}\n */\n\nimport { assertSingleMessage, assertSinglePatient } from \"./messages.js\";\nimport type {\n AstmMessage,\n AstmRecord,\n CommentRecord,\n OrderRecord,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n} from \"./types.js\";\n\n/**\n * Every result (`R`) record in the message, in wire order.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The result records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, results } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * results(msg)[0]?.units; // \"U/L\"\n * ```\n */\nexport function results(msg: AstmMessage): readonly ResultRecord[] {\n assertSingleMessage(msg, \"results()\");\n return msg.records.filter((r): r is ResultRecord => r.type === \"R\");\n}\n\n/**\n * The message's patient (`P`) record, or `undefined` when the message carries none.\n *\n * This is the identity a result files against, so it is answered only when the stream\n * determines exactly one. It **refuses** the two shapes where \"the patient\" is a guess: a\n * stream carrying several messages, and a single message carrying several `P` records. In\n * both, the old behaviour, the first `P` in the stream, is the wrong-patient path itself.\n *\n * @param msg - A parsed single-message stream carrying at most one patient.\n * @returns The patient record, or `undefined` when the message carries none.\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message, or (`ASTM_AMBIGUOUS_MULTI_PATIENT`) when its one message\n * carries more than one `P`. Use {@link messages} and read `patients` in both cases.\n * @example\n * ```ts\n * import { parseAstmRecords, patient } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1|PRAC|LAB\\rL|1\\r\");\n * patient(msg)?.practiceAssignedId; // \"PRAC\"\n * ```\n */\nexport function patient(msg: AstmMessage): PatientRecord | undefined {\n assertSingleMessage(msg, \"patient()\");\n assertSinglePatient(msg, \"patient()\");\n return msg.records.find((r): r is PatientRecord => r.type === \"P\");\n}\n\n/**\n * Every order (`O`) record in the message, in wire order.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The order records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, orders } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rO|1|ACC-42||^^^687|R\\rL|1\\r\");\n * orders(msg)[0]?.specimenId; // \"ACC-42\"\n * ```\n */\nexport function orders(msg: AstmMessage): readonly OrderRecord[] {\n assertSingleMessage(msg, \"orders()\");\n return msg.records.filter((r): r is OrderRecord => r.type === \"O\");\n}\n\n/**\n * Every comment (`C`) record in the message, in wire order. Each carries the\n * `parentIndex` of the `H`/`P`/`O`/`R` it attaches to (or `attachedToRoot` when\n * it is an orphan): use {@link commentsFor} to get the comments of one record.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The comment records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, comments } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|5|U/L||||F\\rC|1|I|checked|G\\rL|1\\r\");\n * comments(msg)[0]?.text; // \"checked\"\n * ```\n */\nexport function comments(msg: AstmMessage): readonly CommentRecord[] {\n assertSingleMessage(msg, \"comments()\");\n return msg.records.filter((r): r is CommentRecord => r.type === \"C\");\n}\n\n/**\n * The comment (`C`) records attached to a given parent record, in wire order.\n *\n * Unlike the other extractors this works on **any** stream, single- or multi-message: the\n * parent record it is handed already names the message, so there is nothing to disambiguate.\n * Returns the comments whose `parentIndex` is that record's `recordIndex`, so a\n * comment carrying (e.g.) QC context is read against the record it modifies:\n * never floated to the wrong one.\n *\n * @param msg - A parsed message.\n * @param parent - The `H`/`P`/`O`/`R` record whose comments to collect.\n * @returns The attached comment records (possibly empty).\n * @example\n * ```ts\n * import { parseAstmRecords, results, commentsFor } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|5|U/L||||F\\rC|1|I|checked|G\\rL|1\\r\");\n * commentsFor(msg, results(msg)[0]!)[0]?.text; // \"checked\"\n * ```\n */\nexport function commentsFor(msg: AstmMessage, parent: AstmRecord): readonly CommentRecord[] {\n return msg.records.filter(\n (r): r is CommentRecord => r.type === \"C\" && r.parentIndex === parent.recordIndex,\n );\n}\n\n/**\n * Every request-information (`Q`) record in the message, in wire order. A non-empty\n * result means the message is a **host-query request**, not a result set: see\n * {@link AstmMessage.classification} (`isHostQueryRequest`).\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The query records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, query } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rQ|1|^SPEC-7||ALL\\rL|1\\r\");\n * query(msg)[0]?.queriesAllTests; // true\n * ```\n */\nexport function query(msg: AstmMessage): readonly QueryRecord[] {\n assertSingleMessage(msg, \"query()\");\n return msg.records.filter((r): r is QueryRecord => r.type === \"Q\");\n}\n","/**\n * Public barrel for the `@cosyte/astm` vendor-profile subsystem. Assembles the\n * `astmProfiles` namespace object (the built-ins) and re-exports the public profile\n * API: `defineAstmProfile`, the registry accessors, the apply helpers, the safety\n * set, the definition-time error, and the profile types.\n *\n * The individual built-ins are reached via `astmProfiles.referenceCorpus` etc., not\n * as top-level named exports: mirrors the sibling `profiles` namespace convention\n * (\"default\" is too generic for a top-level export).\n */\n\nexport { defineAstmProfile } from \"./define.js\";\nexport { AstmProfileDefinitionError } from \"./errors.js\";\nexport type {\n AstmProfile,\n DefineAstmProfileOptions,\n AstmQuirkTolerance,\n AstmQuirkMatch,\n AstmProfileProvenance,\n AnyAstmWarningCode,\n} from \"./types.js\";\nexport {\n getAstmProfile,\n listAstmProfiles,\n setDefaultAstmProfile,\n getDefaultAstmProfile,\n} from \"./registry.js\";\nexport { applyAstmProfile, applyAstmProfileToWarnings, resolveProfileTransport } from \"./apply.js\";\nexport {\n SAFETY_CRITICAL_CODES,\n TOLERABLE_CODES,\n ALL_ASTM_WARNING_CODES,\n isSafetyCriticalCode,\n} from \"./safety.js\";\n\nimport { defaultProfile } from \"./default.js\";\nimport { referenceCorpus } from \"./reference-corpus.js\";\n\n/**\n * Namespace object exposing the built-in profiles: the conservative `default`\n * baseline plus the evidence-backed `referenceCorpus` (grounded firsthand in the\n * redistributable OSS reference corpus), each authored via the public\n * `defineAstmProfile()` API and carrying its cited provenance. Named per-vendor\n * profiles (cobas / Sysmex / …) are **deferred** pending a firsthand\n * vendor-attributed quirk document: the engine fully supports them; we do not ship\n * ungrounded ones.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.referenceCorpus });\n * msg.profile?.name; // \"referenceCorpus\"\n * ```\n */\nexport const astmProfiles = Object.freeze({\n default: defaultProfile,\n referenceCorpus,\n}) as {\n readonly default: typeof defaultProfile;\n readonly referenceCorpus: typeof referenceCorpus;\n};\n","/**\n * The **consumer-supplied** LIVD catalog: a vendor-test-code → LOINC index built\n * from an IICC LIVD (\"LOINC to Vendor IVD\") mapping the consumer provides.\n *\n * **Bring-your-own, by design.** `@cosyte/astm` bundles\n * **no** LOINC, SNOMED, or LIVD data: LOINC is © Regenstrief (redistributable\n * only with its attribution notice) and the public CDC LIVD publication is a\n * SARS-CoV-2-specific file that also carries SNOMED CT (separately licensed), not\n * a general-analyte, public-domain catalog. So the package stays a **structural\n * recognizer, not a dictionary**: it recognizes the Universal Test ID's LOINC slot\n * and surfaces vendor codes verbatim, and this module lets a consumer\n * *supply* their own LIVD file to map those codes, the terminology data, and its\n * license obligations, are the consumer's.\n *\n * **Grounded firsthand on the IICC LIVD digital format / HL7 LIVD IG.** The\n * mapping key is the **Vendor Analyte Code**: the vendor *transmission code* the\n * instrument sends for an automated test, which is exactly the local code an ASTM\n * analyzer puts in the Universal Test ID (`R`/`O` field, component 4). The mapping\n * target is the **LOINC Code** (with the LOINC Long Common Name as an optional\n * human-readable label). Manufacturer/Model are optional provenance a consumer can\n * carry for their own auditing; this catalog keys on the vendor code alone and,\n * crucially, **refuses to choose** when one code maps to more than one LOINC.\n *\n * **Scope the catalog to the source device fleet.** The ASTM Universal Test ID\n * carries no manufacturer to disambiguate against, so two different instruments that\n * reuse the same transmission code for different analytes would both match a single\n * catalog entry. Supply a catalog built for the analyzers you actually receive from;\n * conflicting entries *within* one catalog are caught and surfaced as `ambiguous`\n * (never resolved to a guess), but cross-device code reuse the catalog cannot see is\n * a catalog-hygiene concern the consumer owns.\n */\n\nimport { deepFreeze } from \"../common/freeze.js\";\n\n/**\n * One LIVD mapping row a consumer supplies: a vendor test code and the LOINC it\n * maps to, plus optional human-readable / provenance fields. Modeled on the IICC\n * LIVD digital format's data elements.\n *\n * @example\n * ```ts\n * import type { LivdEntry } from \"@cosyte/astm\";\n * const e: LivdEntry = { vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" };\n * ```\n */\nexport interface LivdEntry {\n /**\n * The **Vendor Analyte Code**: the vendor transmission code the instrument sends (the local code\n * in an ASTM Universal Test ID, component 4). The mapping key; compared **verbatim** (exact,\n * case-sensitive) against the reported code, never normalized or fuzzy-matched.\n */\n readonly vendorCode: string;\n /**\n * The **LOINC Code** this vendor code maps to (e.g. `\"1920-8\"`). Taken from the consumer's catalog\n * as-is and **never validated, altered, or invented**: the parser does not ship a LOINC table and\n * cannot check it; it only carries what the catalog says.\n */\n readonly loinc: string;\n /** The **LOINC Long Common Name**, when the catalog supplies it: an optional human-readable label. */\n readonly loincLongName?: string;\n /** The **Vendor Analyte Name**: the vendor's human-readable analyte label, when supplied. */\n readonly vendorAnalyteName?: string;\n /** The device **Manufacturer**, when the catalog scopes the mapping to a device (optional provenance). */\n readonly manufacturer?: string;\n /** The device **Model**, when the catalog scopes the mapping to a device (optional provenance). */\n readonly model?: string;\n}\n\n/**\n * The outcome of looking a vendor code up in a {@link LivdCatalog}. A distinct\n * value per safe disposition: a hit is `mapped`; a miss is `unmapped`; a code that\n * matched more than one **distinct** LOINC is `ambiguous` with the candidates\n * surfaced but **none chosen**. There is deliberately no \"guessed\" case.\n */\nexport type LivdLookup =\n /** Exactly one LOINC (one entry, or several entries that all agree on the same LOINC). */\n | {\n readonly status: \"mapped\";\n readonly loinc: string;\n readonly loincLongName?: string;\n }\n /** No entry for this code: a miss. The code stays verbatim; no LOINC is invented. */\n | { readonly status: \"unmapped\" }\n /** More than one distinct LOINC: surfaced for inspection, never resolved to one. */\n | { readonly status: \"ambiguous\"; readonly candidates: readonly string[] };\n\n/**\n * An immutable, consumer-supplied LIVD catalog. Look a vendor code up with\n * {@link LivdCatalog.lookup}; the catalog **never** picks between conflicting\n * LOINCs and **never** mutates. Build one with {@link defineLivdCatalog}.\n */\nexport interface LivdCatalog {\n /** The number of distinct vendor codes indexed (not the number of input rows). */\n readonly size: number;\n /**\n * Look a vendor code up, verbatim (exact, case-sensitive). Returns `mapped` on a\n * single-LOINC hit, `unmapped` on a miss, and `ambiguous` when the code carries\n * more than one distinct LOINC: never a guess.\n *\n * @param vendorCode - The reported vendor/local test code.\n * @returns The lookup outcome.\n */\n lookup(vendorCode: string): LivdLookup;\n}\n\n/**\n * Build a {@link LivdCatalog} from LIVD entries a consumer supplies. Entries are\n * indexed by their `vendorCode` (verbatim). When several entries share a vendor\n * code:\n *\n * - all agreeing on the same `loinc` → a single `mapped` result (the first entry's\n * optional `loincLongName` is kept);\n * - disagreeing (two distinct LOINCs) → an `ambiguous` result carrying every\n * distinct candidate, and **no** choice between them.\n *\n * The returned catalog is deeply frozen; nothing is mutated after construction.\n *\n * @param entries - The consumer's LIVD mapping rows.\n * @returns An immutable catalog.\n * @example\n * ```ts\n * import { defineLivdCatalog } from \"@cosyte/astm\";\n * const catalog = defineLivdCatalog([\n * { vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" },\n * { vendorCode: \"690\", loinc: \"1742-6\", loincLongName: \"ALT\" },\n * ]);\n * catalog.lookup(\"687\"); // { status: \"mapped\", loinc: \"1920-8\", loincLongName: \"AST\" }\n * catalog.lookup(\"999\"); // { status: \"unmapped\" }\n * ```\n */\nexport function defineLivdCatalog(entries: readonly LivdEntry[]): LivdCatalog {\n const index = new Map<string, LivdEntry[]>();\n for (const entry of entries) {\n const bucket = index.get(entry.vendorCode);\n if (bucket) bucket.push(entry);\n else index.set(entry.vendorCode, [entry]);\n }\n\n const catalog: LivdCatalog = {\n size: index.size,\n lookup(vendorCode: string): LivdLookup {\n const bucket = index.get(vendorCode);\n if (!bucket || bucket.length === 0) return { status: \"unmapped\" };\n\n const distinct = [...new Set(bucket.map((e) => e.loinc))];\n if (distinct.length > 1) {\n return deepFreeze({ status: \"ambiguous\", candidates: distinct });\n }\n\n const [first] = bucket;\n if (first === undefined) return { status: \"unmapped\" };\n return deepFreeze({\n status: \"mapped\",\n loinc: first.loinc,\n ...(first.loincLongName !== undefined ? { loincLongName: first.loincLongName } : {}),\n });\n },\n };\n return Object.freeze(catalog);\n}\n","/**\n * The **terminology** warning registry (`ASTM_LIVD_*`) for the LIVD-aware LOINC\n * recognition layer: the fourth of the package's registries,\n * alongside the record layer's `ASTM_RECORD_*`, the frame codec's `ASTM_FRAME_*`,\n * and the protocol reducer's `ASTM_LTP_*`.\n *\n * A terminology warning is the record of a **safe non-mapping**: a reported\n * vendor test code that a consumer-supplied LIVD catalog did not map to a single\n * LOINC. It never carries a LOINC the catalog did not vouch for: the whole point\n * of the layer is that an unmapped or ambiguous code is surfaced as *unmapped*,\n * **never a guessed LOINC** (mis-identifying a test is safety-critical). Every\n * warning carries a stable code plus positional context ({@link AstmPosition}) and\n * **never** a field value. Consumers compare `warning.code ===\n * LIVD_WARNING_CODES.<CODE>`; renaming a code is a **breaking change**.\n *\n * These codes live in their own registry and are deliberately **not** part of the\n * profile safety gate's universe (`ALL_ASTM_WARNING_CODES`): a LIVD non-mapping is\n * a post-parse advisory produced by the opt-in {@link applyLivd} helper, not a\n * parse-time deviation a vendor profile could ever \"tolerate\". A profile referencing\n * one is rejected as an unknown code: correctly, since there is nothing here to\n * quiet.\n */\n\nimport type { AstmPosition } from \"../common/position.js\";\n\n/**\n * Stable string codes for every terminology (LIVD) warning. `key === value` so\n * `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { LIVD_WARNING_CODES } from \"@cosyte/astm\";\n * LIVD_WARNING_CODES.ASTM_LIVD_UNMAPPED_CODE; // \"ASTM_LIVD_UNMAPPED_CODE\"\n * ```\n */\nexport const LIVD_WARNING_CODES = {\n /**\n * A record carried a vendor/local test code, but the consumer-supplied LIVD catalog held **no**\n * entry for it. The code stays surfaced verbatim and the mapping is `unmapped`: a LOINC is\n * **never** guessed. Purely advisory: the raw code and value are untouched.\n */\n ASTM_LIVD_UNMAPPED_CODE: \"ASTM_LIVD_UNMAPPED_CODE\",\n /**\n * A vendor/local test code matched **more than one distinct LOINC** in the catalog (e.g. the same\n * transmission code used by two devices for different analytes). The mapping is `ambiguous` and the\n * candidate LOINCs are surfaced for inspection, but **none is chosen**: refusing to pick is the\n * fail-safe, since guessing wrong mis-identifies the test.\n */\n ASTM_LIVD_AMBIGUOUS_MAPPING: \"ASTM_LIVD_AMBIGUOUS_MAPPING\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmLivdWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link LIVD_WARNING_CODES}.\n */\nexport type LivdWarningCode = (typeof LIVD_WARNING_CODES)[keyof typeof LIVD_WARNING_CODES];\n\n/**\n * A single terminology (LIVD) warning: a stable code, a value-free human-readable\n * message, and positional context. Never carries a test code, a value, or a LOINC.\n *\n * @example\n * ```ts\n * import type { AstmLivdWarning } from \"@cosyte/astm\";\n * const w: AstmLivdWarning = {\n * code: \"ASTM_LIVD_UNMAPPED_CODE\",\n * message: \"Reported test code had no LIVD mapping, surfaced unmapped, never a guessed LOINC.\",\n * position: { recordIndex: 3, recordType: \"R\" },\n * };\n * ```\n */\nexport interface AstmLivdWarning {\n readonly code: LivdWarningCode;\n /** Human-readable detail for logs. Never contains a test code, a value, or a LOINC. */\n readonly message: string;\n readonly position: AstmPosition;\n}\n\n/**\n * Build an `ASTM_LIVD_UNMAPPED_CODE` warning. The reported code stays verbatim and\n * the mapping is `unmapped`; no LOINC is fabricated.\n *\n * @param position - Where the unmapped code was seen (record ordinal + type; never the code).\n * @returns The warning.\n * @example\n * ```ts\n * import { livdUnmappedCode } from \"@cosyte/astm\";\n * livdUnmappedCode({ recordIndex: 3, recordType: \"R\" });\n * ```\n */\nexport function livdUnmappedCode(position: AstmPosition): AstmLivdWarning {\n return {\n code: LIVD_WARNING_CODES.ASTM_LIVD_UNMAPPED_CODE,\n message: \"Reported test code had no LIVD mapping, surfaced unmapped, never a guessed LOINC.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_LIVD_AMBIGUOUS_MAPPING` warning. The code matched multiple distinct\n * LOINCs; none is chosen (refusing to pick is the fail-safe).\n *\n * @param position - Where the ambiguous code was seen (record ordinal + type; never the code).\n * @returns The warning.\n * @example\n * ```ts\n * import { livdAmbiguousMapping } from \"@cosyte/astm\";\n * livdAmbiguousMapping({ recordIndex: 4, recordType: \"O\" });\n * ```\n */\nexport function livdAmbiguousMapping(position: AstmPosition): AstmLivdWarning {\n return {\n code: LIVD_WARNING_CODES.ASTM_LIVD_AMBIGUOUS_MAPPING,\n message:\n \"Reported test code mapped to multiple distinct LOINCs, surfaced ambiguous, never one guessed.\",\n position,\n };\n}\n","/**\n * The **additive, advisory** LIVD annotation layer. {@link applyLivd}\n * reads a parsed {@link AstmMessage} and a consumer-supplied {@link LivdCatalog}\n * and returns a **separate** layer of per-record LOINC annotations: it **never\n * mutates, alters, or drops** the raw reported code or value. Recognition is an\n * annotation, not a rewrite: the wire stays exactly as parsed.\n *\n * **The never-fabricate rule (the safety-critical point).** A vendor code with no\n * single LIVD mapping surfaces as a typed `unmapped`/`ambiguous`/`no-code`\n * annotation: **never** a guessed LOINC. A wrong LOINC mis-identifies a test, so\n * the layer only ever reports a LOINC the wire itself carried (an inline LOINC\n * candidate) or one the consumer's catalog vouched for (labeled `derived`), and\n * otherwise reports nothing but the miss.\n */\n\nimport { primaryCode, recognizeUniversalTestId } from \"../common/coding-system.js\";\nimport { deepFreeze } from \"../common/freeze.js\";\nimport type { UniversalTestId, UniversalTestIdProvenance } from \"../common/coding-system.js\";\nimport type { AstmMessage, OrderRecord, ResultRecord } from \"../records/types.js\";\n\nimport type { LivdCatalog } from \"./catalog.js\";\nimport { livdAmbiguousMapping, livdUnmappedCode } from \"./warnings.js\";\nimport type { AstmLivdWarning } from \"./warnings.js\";\n\n/**\n * The outcome of annotating one record's Universal Test ID against a LIVD catalog.\n * A distinct case per disposition; there is no case in which a LOINC is guessed.\n *\n * - `mapped`, the vendor/local code resolved to a single LOINC **via the catalog**\n * (labeled `derived: true`, `source: \"livd\"`).\n * - `inline-loinc`, the wire itself carried a LOINC in the Universal Test ID's slot\n * (component 1); surfaced `source: \"wire\"`, **not** derived and **not** validated.\n * - `unmapped`: a vendor/local code with no catalog entry.\n * - `ambiguous`: a vendor/local code matching more than one distinct LOINC; the\n * candidates are surfaced but **none is chosen**.\n * - `no-code`: the record carried no usable test code at all (name-only/empty), so\n * there was nothing to map.\n */\nexport type LivdMapping =\n | {\n readonly status: \"mapped\";\n readonly loinc: string;\n readonly loincLongName?: string;\n readonly source: \"livd\";\n readonly derived: true;\n }\n | { readonly status: \"inline-loinc\"; readonly loinc: string; readonly source: \"wire\" }\n | { readonly status: \"unmapped\" }\n | { readonly status: \"ambiguous\"; readonly candidates: readonly string[] }\n | { readonly status: \"no-code\" };\n\n/**\n * One record's LIVD annotation, which record, the code that was looked up\n * (verbatim), how it was recognized, and the mapping outcome. Additive: it points\n * at the record by index and never replaces it.\n *\n * @example\n * ```ts\n * import type { LivdAnnotation } from \"@cosyte/astm\";\n * const a: LivdAnnotation = {\n * recordIndex: 3,\n * recordType: \"R\",\n * reportedCode: \"687\",\n * provenance: \"local-code\",\n * mapping: { status: \"mapped\", loinc: \"1920-8\", source: \"livd\", derived: true },\n * };\n * ```\n */\nexport interface LivdAnnotation {\n /** The `recordIndex` of the annotated `R`/`O` record. */\n readonly recordIndex: number;\n /** The annotated record's type. */\n readonly recordType: \"R\" | \"O\";\n /** The reported primary code that was looked up, verbatim; absent when the record carried no code. */\n readonly reportedCode?: string;\n /** How the reported code was recognized in the Universal Test ID (provenance only, never a lookup). */\n readonly provenance: UniversalTestIdProvenance;\n /** The mapping outcome: never a guessed LOINC. */\n readonly mapping: LivdMapping;\n}\n\n/**\n * The result of {@link applyLivd}: the per-record annotations (one per `R`/`O`\n * record) and the value-free warnings for every unmapped or ambiguous code. Both\n * arrays are deeply frozen; the source message is untouched.\n */\nexport interface LivdResult {\n /** One annotation per `R`/`O` record, in wire order. */\n readonly annotations: readonly LivdAnnotation[];\n /** A value-free warning per `unmapped`/`ambiguous` code: never per `mapped`/`inline-loinc`/`no-code`. */\n readonly warnings: readonly AstmLivdWarning[];\n}\n\n/** Map a recognized Universal Test ID against the catalog: the pure core of the annotation. */\nfunction mapTestId(uid: UniversalTestId, catalog: LivdCatalog): LivdMapping {\n // The wire already carries a candidate LOINC in component 1: surface it as-is,\n // from the wire, never derived and never validated. It is the primary code.\n if (uid.loincCandidate !== undefined) {\n return { status: \"inline-loinc\", loinc: uid.loincCandidate, source: \"wire\" };\n }\n\n const code = primaryCode(uid);\n if (code === undefined) return { status: \"no-code\" };\n\n const hit = catalog.lookup(code);\n switch (hit.status) {\n case \"mapped\":\n return {\n status: \"mapped\",\n loinc: hit.loinc,\n ...(hit.loincLongName !== undefined ? { loincLongName: hit.loincLongName } : {}),\n source: \"livd\",\n derived: true,\n };\n case \"ambiguous\":\n return { status: \"ambiguous\", candidates: hit.candidates };\n case \"unmapped\":\n return { status: \"unmapped\" };\n }\n}\n\n/** Recognize a record's Universal Test ID whether it arrived pre-recognized or as raw components. */\nfunction testIdOf(record: ResultRecord | OrderRecord): UniversalTestId | undefined {\n return record.universalTestId;\n}\n\n/**\n * Annotate one `R` or `O` record against a LIVD catalog. The building block of\n * {@link applyLivd}; useful when a consumer holds a single record. Never mutates\n * the record and never fabricates a LOINC.\n *\n * @param record - The result/order record to annotate.\n * @param catalog - The consumer-supplied LIVD catalog.\n * @returns The record's annotation.\n * @example\n * ```ts\n * import { parseAstmRecords, results, defineLivdCatalog, lookupLivdForRecord } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const catalog = defineLivdCatalog([{ vendorCode: \"687\", loinc: \"1920-8\" }]);\n * lookupLivdForRecord(results(msg)[0]!, catalog).mapping.status; // \"mapped\"\n * ```\n */\nexport function lookupLivdForRecord(\n record: ResultRecord | OrderRecord,\n catalog: LivdCatalog,\n): LivdAnnotation {\n const uid = testIdOf(record);\n const recognized = uid ?? recognizeUniversalTestId([]);\n const mapping = mapTestId(recognized, catalog);\n const reportedCode = primaryCode(recognized);\n return {\n recordIndex: record.recordIndex,\n recordType: record.type,\n ...(reportedCode !== undefined ? { reportedCode } : {}),\n provenance: recognized.provenance,\n mapping,\n };\n}\n\n/**\n * Apply a consumer-supplied LIVD catalog to a parsed message, producing an\n * **additive, advisory** layer of LOINC annotations over its `R` (result) and `O`\n * (order) records. The source message is never mutated; the raw reported codes and\n * values stay exactly as parsed.\n *\n * Fail-safe: with no matching entry a code is `unmapped` (+ an\n * `ASTM_LIVD_UNMAPPED_CODE` warning); with a conflicting mapping it is `ambiguous`\n * (+ an `ASTM_LIVD_AMBIGUOUS_MAPPING` warning); a LOINC is **never** guessed. When\n * the wire already carried an inline LOINC candidate, it is surfaced from the wire\n * (never overwritten by the catalog).\n *\n * @param msg - A parsed ASTM message.\n * @param catalog - The consumer-supplied LIVD catalog (build with {@link defineLivdCatalog}).\n * @returns The annotations and value-free warnings (deeply frozen).\n * @example\n * ```ts\n * import { parseAstmRecords, defineLivdCatalog, applyLivd } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const catalog = defineLivdCatalog([{ vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" }]);\n * const { annotations } = applyLivd(msg, catalog);\n * annotations[0]?.mapping; // { status: \"mapped\", loinc: \"1920-8\", loincLongName: \"AST\", source: \"livd\", derived: true }\n * ```\n */\nexport function applyLivd(msg: AstmMessage, catalog: LivdCatalog): LivdResult {\n const annotations: LivdAnnotation[] = [];\n const warnings: AstmLivdWarning[] = [];\n\n for (const record of msg.records) {\n if (record.type !== \"R\" && record.type !== \"O\") continue;\n const annotation = lookupLivdForRecord(record, catalog);\n annotations.push(annotation);\n\n const position = { recordIndex: record.recordIndex, recordType: record.type };\n if (annotation.mapping.status === \"unmapped\") {\n warnings.push(livdUnmappedCode(position));\n } else if (annotation.mapping.status === \"ambiguous\") {\n warnings.push(livdAmbiguousMapping(position));\n }\n }\n\n return deepFreeze({ annotations, warnings });\n}\n","/**\n * The four LTP (low-level transfer protocol) control bytes for ASTM/CLSI-LIS01\n * (was E1381), plus the bare-record-letter set the transport detector keys on.\n *\n * The frame codec (`../frames`) owns the *inside* of a frame (`STX FN text\n * ETB/ETX CS CRLF`); this module owns the *handshake around* frames, the\n * establishment/transfer/termination signalling that the {@link ltpReduce}\n * reducer consumes. These are the raw byte values; the reducer and the transport\n * detector are the only consumers.\n */\n\n/** Enquiry (`0x05`): the sender's request to establish a transfer. */\nexport const ENQ = 0x05;\n/** Acknowledge (`0x06`): the receiver accepted the last establishment or frame. */\nexport const ACK = 0x06;\n/** Negative acknowledge (`0x15`): the receiver rejected the last frame; retransmit, do not accept. */\nexport const NAK = 0x15;\n/** End of transmission (`0x04`): the sender terminated the transfer; the line returns to neutral. */\nexport const EOT = 0x04;\n\n/**\n * The ASTM record-type letters that can legally *lead a de-framed record stream*.\n * A raw-TCP stream (framing dropped) begins with a bare record letter, in\n * practice `H` (message header), where a framed stream begins with `STX` or\n * `ENQ`. The transport detector uses membership here as the \"this is raw record\n * bytes, not a frame\" signal.\n *\n * The set is the nine ASTM record types (`H`/`P`/`O`/`R`/`C`/`Q`/`M`/`S`/`L`); a\n * conformant record stream always opens with `H`, but tolerating any record\n * letter keeps the detector robust to a capture that starts mid-message.\n */\nexport const RECORD_TYPE_LETTERS: ReadonlySet<number> = new Set(\n [...\"HPORCQMSL\"].map((c) => c.charCodeAt(0)),\n);\n","/**\n * Byte constants and limits for the ASTM/CLSI-LIS01 (was E1381) frame codec.\n *\n * A frame on the wire is `<STX> FN text <ETB|ETX> C1 C2 <CR> <LF>`: an `STX`\n * start byte, a single ASCII frame-number digit `FN` (`0`–`7`), up to 240 bytes\n * of record text, an `ETB` (intermediate) or `ETX` (final) terminator, a\n * two-hex-char modulo-256 checksum, and a `CR`+`LF` tail. These are the raw byte\n * values; the decoder in `./decode.ts` is the only consumer.\n */\n\n/** Start of a frame (`0x02`). */\nexport const STX = 0x02;\n/** Final-frame terminator (`0x03`): the last frame of a record. */\nexport const ETX = 0x03;\n/** Intermediate-frame terminator (`0x17`): the record continues in the next frame. */\nexport const ETB = 0x17;\n/** Carriage return (`0x0D`): closes a frame after the checksum. */\nexport const CR = 0x0d;\n/** Line feed (`0x0A`): follows the `CR` at the end of a frame. */\nexport const LF = 0x0a;\n\n/** ASCII `0` (`0x30`): the low end of the frame-number digit range. */\nexport const FN_ZERO = 0x30;\n/** ASCII `7` (`0x37`): the high end of the frame-number digit range. */\nexport const FN_SEVEN = 0x37;\n\n/**\n * The maximum record-text length in a single frame (240 bytes). The seven framing\n * control bytes (`STX`, `FN`, the terminator, the two checksum chars, `CR`, `LF`)\n * are **not** counted toward this limit: only the record text between `FN` and the\n * terminator is. Text longer than this without a split is an oversize deviation.\n */\nexport const MAX_FRAME_TEXT = 240;\n\n/** The frame-number sequence is modulo 8 (`0`–`7`), and by convention starts at `1`. */\nexport const FRAME_NUMBER_MODULUS = 8;\n/** The frame number of the first frame of a transfer (`1`); the sequence rolls over `7 → 0 → 1`. */\nexport const FIRST_FRAME_NUMBER = 1;\n","/**\n * Framed-vs-raw transport auto-detection for ASTM/CLSI-LIS01.\n *\n * ASTM transport is not uniform, and that is what breaks naive parsers. **Serial**\n * (RS-232) always uses full E1381 framing. Over **TCP it varies within a single\n * vendor**: the Roche cobas 4800 and iNTERFACEWARE Iguana retain the full\n * `ENQ`/`ACK` + `STX`/checksum framing over TCP (**framed-TCP**), while the cobas\n * b121 drops all low-level framing, \"TCP itself ensures correctness\", and streams\n * de-framed record bytes directly (**raw-TCP**). A reader must handle both and must\n * not assume the handshake is present.\n *\n * {@link detectFraming} decides which it is from the stream's leading byte: `STX`\n * or `ENQ` ⇒ framed (feed it to the frame codec + {@link ltpReduce}); a bare record\n * letter ⇒ raw (feed it straight to `parseAstmRecords`). Anything else is\n * **ambiguous**, and the fail-safe is to **default to framed** and warn, never to\n * guess silently into data loss, with an explicit override for a profile that\n * knows better.\n */\n\nimport { ENQ, RECORD_TYPE_LETTERS } from \"./constants.js\";\nimport { STX } from \"../frames/constants.js\";\nimport { ltpAmbiguousTransport, type AstmLtpWarning } from \"./warnings.js\";\n\n/**\n * The transport framing of an ASTM byte stream: `\"framed\"` (E1381 `STX`/checksum\n * frames, the serial and framed-TCP realities) or `\"raw\"` (framing dropped,\n * de-framed record bytes streamed directly, the raw-TCP reality).\n */\nexport type AstmFraming = \"framed\" | \"raw\";\n\n/**\n * Options for {@link detectFraming}.\n */\nexport interface DetectFramingOptions {\n /**\n * A profile-supplied override. When set, detection is bypassed entirely and this\n * value is returned with no warning: the way a vendor profile forces raw for\n * a cobas b121 or framed for a cobas 4800 regardless of the leading byte.\n */\n readonly override?: AstmFraming;\n}\n\n/**\n * The result of {@link detectFraming}: the decided framing plus any warning\n * (exactly one `ASTM_LTP_AMBIGUOUS_TRANSPORT` when the lead was unrecognizable and\n * the mode was defaulted; empty otherwise).\n */\nexport interface DetectFramingResult {\n /** The decided transport framing. */\n readonly framing: AstmFraming;\n /** `true` when the lead byte was unrecognizable and `framing` was defaulted (not inferred). */\n readonly defaulted: boolean;\n /** The detection warnings (a single ambiguity warning, or none). */\n readonly warnings: readonly AstmLtpWarning[];\n}\n\n/**\n * Detect whether an ASTM byte stream is framed or raw from its leading byte.\n *\n * - Leading `STX` (`0x02`) or `ENQ` (`0x05`) ⇒ `\"framed\"`.\n * - A leading bare record letter (`H`/`P`/`O`/`R`/`C`/`Q`/`M`/`S`/`L`) ⇒ `\"raw\"`.\n * - Anything else (including an empty stream) is **ambiguous** ⇒ defaults to\n * `\"framed\"` and emits one `ASTM_LTP_AMBIGUOUS_TRANSPORT` warning.\n *\n * An `override` short-circuits all of the above, returning the forced mode with no\n * warning.\n *\n * @param bytes - The stream to inspect (only the leading byte is read).\n * @param options - Detection options; an `override` forces the result.\n * @returns The decided framing, whether it was defaulted, and any warning.\n * @example\n * ```ts\n * import { detectFraming } from \"@cosyte/astm\";\n * detectFraming(new Uint8Array([0x02])).framing; // \"framed\" (STX)\n * detectFraming(new Uint8Array([0x48])).framing; // \"raw\" (leading \"H\")\n * detectFraming(new Uint8Array([0x2a])).framing; // \"framed\" (ambiguous → defaulted, + warning)\n * detectFraming(new Uint8Array([0x48]), { override: \"framed\" }).framing; // \"framed\" (forced)\n * ```\n */\nexport function detectFraming(\n bytes: Uint8Array,\n options: DetectFramingOptions = {},\n): DetectFramingResult {\n if (options.override !== undefined) {\n return { framing: options.override, defaulted: false, warnings: [] };\n }\n\n const lead = bytes[0];\n if (lead === STX || lead === ENQ) {\n return { framing: \"framed\", defaulted: false, warnings: [] };\n }\n if (lead !== undefined && RECORD_TYPE_LETTERS.has(lead)) {\n return { framing: \"raw\", defaulted: false, warnings: [] };\n }\n\n // Unrecognizable (or empty): default to framed, the safer assumption, since treating a framed\n // stream as raw would feed control/checksum bytes to the record parser and corrupt fields.\n return { framing: \"framed\", defaulted: true, warnings: [ltpAmbiguousTransport()] };\n}\n","/**\n * The pure LTP protocol reducer: {@link ltpReduce} and {@link ltpInitialState}.\n *\n * A deterministic, socket-free receiver-side state machine for ASTM/CLSI-LIS01\n * (LIS01-A2). The consumer owns the wire (it reads bytes, decodes frames with\n * `decodeAstmFrames`, and drives this reducer one {@link LtpEvent} at a time) and\n * the reducer decides, purely, what happens next: whether to `ACK` or `NAK`, when a\n * record is complete, and when the session returns to neutral.\n *\n * **The one inviolable rule (borrowed from `mllp`'s ACK-failsafe discipline):** a\n * frame the codec did not vouch for (a bad checksum, an unterminated frame, or one\n * out of sequence) is answered with `NAK`, **never** a fabricated positive `ACK`,\n * and its bytes are **never** appended to a record or delivered. A `NAK` drives\n * retransmit, not acceptance. This is the protocol-level twin of the frame codec's\n * \"a bad-checksum frame is never merged into a record.\"\n */\n\nimport { FRAME_NUMBER_MODULUS, FIRST_FRAME_NUMBER } from \"../frames/constants.js\";\nimport type { AstmFrame } from \"../frames/types.js\";\nimport { ltpFrameRejected, ltpUnexpectedEvent, type AstmLtpWarning } from \"./warnings.js\";\nimport type { LtpAction, LtpEvent, LtpState, LtpTransition } from \"./types.js\";\n\nconst EMPTY = new Uint8Array(0);\n\n/**\n * The neutral starting state for a fresh session: line idle, no record in flight,\n * nothing delivered yet. A consumer creates one of these when a connection opens\n * and folds inbound events through {@link ltpReduce} from here.\n *\n * @returns A frozen neutral {@link LtpState}.\n * @example\n * ```ts\n * import { ltpInitialState, ltpReduce } from \"@cosyte/astm\";\n * let state = ltpInitialState();\n * const step = ltpReduce(state, { type: \"enq\" });\n * step.actions; // [{ type: \"sendAck\" }]\n * state = step.state;\n * ```\n */\nexport function ltpInitialState(): LtpState {\n return freezeState({\n phase: \"neutral\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: [],\n recordOpen: false,\n openRecord: EMPTY,\n });\n}\n\n/**\n * Advance the LTP session by one inbound event: a pure function, no I/O.\n *\n * Behaviour by phase:\n * - **neutral**: `enq` accepts establishment (`sendAck`, enter transfer); `eot` is\n * a benign line reset (no-op); `ack`/`nak` are unexpected at a receiver (surfaced,\n * never read as acceptance); a `frame` before establishment is tolerated\n * (Postel's Law), the session auto-establishes and processes it, with a warning.\n * - **transfer**: a `frame` is accepted (`sendAck`, appended, sequence advanced) only\n * when it is trusted *and* carries the expected number; a duplicate of the last\n * accepted frame is idempotently re-`ACK`ed without re-appending; a bad or\n * out-of-sequence frame is `NAK`ed and dropped. `eot` terminates the transfer and\n * returns to neutral (a record left open on an `ETB` is **not** delivered). `enq`\n * restarts establishment. `ack`/`nak` are unexpected.\n *\n * @param state - The current session state.\n * @param event - The inbound event.\n * @returns The next state, the actions to take, and any warnings.\n * @example\n * ```ts\n * import { ltpInitialState, ltpReduce } from \"@cosyte/astm\";\n * // A bad-checksum frame is NAKed, never ACKed, and never appended.\n * const est = ltpReduce(ltpInitialState(), { type: \"enq\" }).state;\n * const bad = { trusted: false } as never;\n * ltpReduce(est, { type: \"frame\", frame: bad }).actions; // [{ type: \"sendNak\" }]\n * ```\n */\nexport function ltpReduce(state: LtpState, event: LtpEvent): LtpTransition {\n switch (event.type) {\n case \"enq\":\n return establish(state);\n case \"eot\":\n return terminate(state);\n case \"ack\":\n case \"nak\":\n // A receiver sends ACK/NAK; receiving one is a role/protocol violation. Surface it and hold:\n // an inbound ACK is NEVER read as acceptance of data (that is the sender's signal, not ours).\n return { state, actions: [], warnings: [ltpUnexpectedEvent()] };\n case \"frame\":\n return onFrame(state, event.frame);\n }\n}\n\n/** `neutral --enq--> transfer`, or a mid-transfer `enq` restart. Establishment is accepted with an ACK. */\nfunction establish(state: LtpState): LtpTransition {\n const warnings: AstmLtpWarning[] = state.phase === \"transfer\" ? [ltpUnexpectedEvent()] : [];\n // Enter (or restart) transfer: reset the frame counters and drop any half-built record, a partial\n // reassembly is never carried across an establishment boundary, never delivered as if whole.\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions: [{ type: \"sendAck\" }], warnings };\n}\n\n/** `--eot--> neutral`. Terminating discards any open (unterminated) record: no partial is invented. */\nfunction terminate(state: LtpState): LtpTransition {\n const next = freezeState({\n phase: \"neutral\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions: [], warnings: [] };\n}\n\n/** A frame arrived. In neutral, tolerate the missing handshake (auto-establish + warn), then process. */\nfunction onFrame(state: LtpState, frame: AstmFrame): LtpTransition {\n if (state.phase === \"neutral\") {\n const established = freezeState({\n phase: \"transfer\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n const step = processTransferFrame(established, frame);\n return {\n state: step.state,\n actions: step.actions,\n warnings: [ltpUnexpectedEvent(frame.frameNumber), ...step.warnings],\n };\n }\n return processTransferFrame(state, frame);\n}\n\n/**\n * The safety-critical core: decide `ACK` vs `NAK` for a frame received in transfer.\n *\n * A frame is accepted (`sendAck`, text appended, sequence advanced, and (on `ETX`)\n * the completed record delivered) **only** when it is trusted (codec-vouched\n * checksum + terminator) *and* carries the expected sequence number. A duplicate of\n * the last accepted frame is re-`ACK`ed idempotently. Everything else (bad checksum,\n * unterminated, out of sequence) is `NAK`ed and dropped: never appended, never\n * delivered, sequence never advanced.\n */\nfunction processTransferFrame(state: LtpState, frame: AstmFrame): LtpTransition {\n // ── Fail-safe: an untrusted frame is NAKed, full stop. No ACK is ever fabricated for it. ──\n if (!frame.trusted) {\n return reject(state, frame.frameNumber);\n }\n\n const fn = frame.frameNumber;\n // A trusted frame is terminated with a valid checksum, so its frame number was read; guard anyway.\n if (fn === undefined) {\n return reject(state, undefined);\n }\n\n // ── Duplicate retransmit: the sender re-sent an already-accepted frame (it missed our ACK). ──\n // Re-ACK idempotently; do NOT re-append or advance. (Not a deviation, normal recovery.)\n if (state.lastAcceptedFrame !== undefined && fn === state.lastAcceptedFrame) {\n return { state, actions: [{ type: \"sendAck\" }], warnings: [] };\n }\n\n // ── Out of sequence: a trusted frame carrying the wrong number. Never bridge the gap: NAK it. ──\n if (fn !== state.expectedFrame) {\n return reject(state, fn);\n }\n\n return acceptFrame(state, frame, fn);\n}\n\n/**\n * Accept a trusted, in-sequence frame: `sendAck`, append its text to the open\n * record, deliver on `ETX`, and advance the sequence counter.\n */\nfunction acceptFrame(state: LtpState, frame: AstmFrame, fn: number): LtpTransition {\n const nextExpected = (fn + 1) % FRAME_NUMBER_MODULUS;\n const accumulated = concatBytes([state.openRecord, frame.text]);\n const actions: LtpAction[] = [{ type: \"sendAck\" }];\n\n if (frame.terminator === \"ETX\") {\n // Record complete: the accumulated bytes (prior ETB frames + this final text) are one record.\n // Hand the action its own copy so the delivered bytes never alias the entry stored in\n // `state.records`: mutating one must never retroactively mutate the other.\n actions.push({ type: \"deliverRecord\", record: accumulated.slice() });\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: nextExpected,\n lastAcceptedFrame: fn,\n records: [...state.records, accumulated],\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions, warnings: [] };\n }\n\n // ETB: the record continues in a later frame; carry the accumulated bytes forward.\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: nextExpected,\n lastAcceptedFrame: fn,\n records: state.records,\n recordOpen: true,\n openRecord: accumulated,\n });\n return { state: next, actions, warnings: [] };\n}\n\n/** Reject a frame: `sendNak`, no append, no advance, plus a frame-rejected warning. */\nfunction reject(state: LtpState, frameNumber: number | undefined): LtpTransition {\n return {\n state,\n actions: [{ type: \"sendNak\" }],\n warnings: [ltpFrameRejected(frameNumber)],\n };\n}\n\n/**\n * Build a frozen {@link LtpState}. `lastAcceptedFrame` is omitted (not set to\n * `undefined`) when absent, keeping `exactOptionalPropertyTypes` happy. The\n * `records` array is copied and frozen; the byte payloads are copies the caller owns\n * (a typed array with elements cannot itself be `Object.freeze`d).\n */\nfunction freezeState(fields: {\n readonly phase: LtpState[\"phase\"];\n readonly expectedFrame: number;\n readonly lastAcceptedFrame?: number;\n readonly records: readonly Uint8Array[];\n readonly recordOpen: boolean;\n readonly openRecord: Uint8Array;\n}): LtpState {\n const state: LtpState = {\n phase: fields.phase,\n expectedFrame: fields.expectedFrame,\n ...(fields.lastAcceptedFrame !== undefined\n ? { lastAcceptedFrame: fields.lastAcceptedFrame }\n : {}),\n records: Object.freeze([...fields.records]),\n recordOpen: fields.recordOpen,\n openRecord: fields.openRecord,\n };\n return Object.freeze(state);\n}\n\n/** Concatenate byte chunks into one `Uint8Array` (the reassembled record bytes). */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * The ASTM/CLSI-LIS01 modulo-256 frame checksum.\n *\n * The checksum is the sum, modulo 256, of every byte **after** the `STX` up to\n * and **including** the `ETB`/`ETX` terminator: i.e. the frame number, the record\n * text, and the terminator byte. It is carried on the wire as **two hexadecimal\n * characters**. This module computes it (`computeChecksum`), formats it for emit\n * (`toChecksumHex`, always **uppercase**), and reads it back leniently\n * (`parseChecksumHex`, **accepts lowercase**), matching the real-vendor quirk that\n * some analyzers emit a lowercase checksum.\n */\n\n/**\n * Sum bytes `[start, endInclusive]` of `bytes` modulo 256: the ASTM frame\n * checksum span (frame number through the terminator, inclusive).\n *\n * @param bytes - The full decoded byte stream.\n * @param start - First index to include (the frame-number byte, one past `STX`).\n * @param endInclusive - Last index to include (the `ETB`/`ETX` terminator byte).\n * @returns The checksum in `0`–`255`.\n * @example\n * ```ts\n * import { computeChecksum } from \"@cosyte/astm\";\n * // bytes: STX '1' 'A' ETX → sum '1' + 'A' + ETX, mod 256.\n * computeChecksum(new Uint8Array([0x02, 0x31, 0x41, 0x03]), 1, 3); // 0x75\n * ```\n */\nexport function computeChecksum(bytes: Uint8Array, start: number, endInclusive: number): number {\n let sum = 0;\n for (let i = start; i <= endInclusive; i++) {\n sum = (sum + (bytes[i] ?? 0)) & 0xff;\n }\n return sum;\n}\n\n/**\n * Format a checksum byte as the two **uppercase** hex characters ASTM puts on the\n * wire (the conservative-emit form). Decode accepts lowercase; emit is uppercase.\n *\n * @param checksum - A value in `0`–`255`.\n * @returns Two uppercase hex characters, zero-padded (e.g. `\"0A\"`, `\"75\"`).\n * @example\n * ```ts\n * import { toChecksumHex } from \"@cosyte/astm\";\n * toChecksumHex(0x0a); // \"0A\"\n * ```\n */\nexport function toChecksumHex(checksum: number): string {\n return (checksum & 0xff).toString(16).toUpperCase().padStart(2, \"0\");\n}\n\n/** ASCII code for a single hex digit (`0`–`9`/`A`–`F`/`a`–`f`), or `undefined`. */\nfunction hexNibble(code: number | undefined): number | undefined {\n if (code === undefined) return undefined;\n if (code >= 0x30 && code <= 0x39) return code - 0x30; // 0-9\n if (code >= 0x41 && code <= 0x46) return code - 0x41 + 10; // A-F\n if (code >= 0x61 && code <= 0x66) return code - 0x61 + 10; // a-f (lenient: lowercase accepted)\n return undefined;\n}\n\n/**\n * Read the two-hex-char checksum at `bytes[i0]`/`bytes[i1]`, **case-insensitively**\n * (a lowercase checksum is a tolerated real-vendor quirk). Returns `undefined` when\n * either position is out of range or not a hex digit: the caller then treats the\n * frame as having no readable declared checksum, never as a match.\n *\n * @param bytes - The full decoded byte stream.\n * @param i0 - Index of the high hex nibble (immediately after the terminator).\n * @param i1 - Index of the low hex nibble.\n * @returns The declared checksum in `0`–`255`, or `undefined` if unreadable.\n * @example\n * ```ts\n * import { parseChecksumHex } from \"@cosyte/astm\";\n * parseChecksumHex(new Uint8Array([0x37, 0x35]), 0, 1); // 0x75 (\"75\")\n * parseChecksumHex(new Uint8Array([0x37, 0x35]), 5, 6); // undefined (out of range)\n * ```\n */\nexport function parseChecksumHex(bytes: Uint8Array, i0: number, i1: number): number | undefined {\n const hi = hexNibble(bytes[i0]);\n const lo = hexNibble(bytes[i1]);\n if (hi === undefined || lo === undefined) return undefined;\n return (hi << 4) | lo;\n}\n","/**\n * The frame codec's thrown error: the `strict`-mode counterpart to its lenient\n * warnings.\n *\n * The frame layer is lenient by default (byte-level deviations become\n * {@link AstmFrameWarning}s), and shares the single {@link FATAL_CODES.EMPTY_INPUT}\n * fatal with the record layer (thrown as an {@link AstmParseError} on an empty\n * stream, in both modes). In `strict` mode every tolerated deviation is rejected\n * instead: {@link decodeAstmFrames} throws {@link AstmFrameStrictError} carrying the\n * warnings it would otherwise have accumulated (code + position, never a value).\n */\n\nimport type { AstmFrameWarning } from \"./warnings.js\";\n\n/**\n * Thrown by {@link decodeAstmFrames} in `strict` mode when the lenient codec would\n * otherwise have accumulated one or more frame warnings (a bad checksum, a\n * sequence gap, an unterminated frame, or an oversize frame). Carries every\n * warning (code + position, never the frame's record bytes) so a caller can see\n * each deviation.\n *\n * @example\n * ```ts\n * import { decodeAstmFrames, AstmFrameStrictError } from \"@cosyte/astm\";\n * try {\n * decodeAstmFrames(someFramedBytes, { strict: true });\n * } catch (err) {\n * if (err instanceof AstmFrameStrictError) err.warnings.length; // >= 1\n * }\n * ```\n */\nexport class AstmFrameStrictError extends Error {\n public readonly warnings: readonly AstmFrameWarning[];\n /** @internal */\n public constructor(warnings: readonly AstmFrameWarning[]) {\n super(`Strict mode: ${String(warnings.length)} frame deviation(s) rejected.`);\n this.name = \"AstmFrameStrictError\";\n this.warnings = warnings;\n }\n}\n","/**\n * The frame-layer entry point: {@link decodeAstmFrames}.\n *\n * Decode a framed ASTM/CLSI-LIS01 byte stream (`<STX> FN text <ETB|ETX> CS <CR>\n * <LF>` frames) into a list of frames plus the reassembled record bytes of the\n * frames that were trusted and contiguous. Lenient by default: byte-level\n * deviations become {@link AstmFrameWarning}s, and the only fatal is the shared\n * `EMPTY_INPUT` (an empty stream). The two safety-critical rules are absolute in\n * **both** modes: a bad-checksum frame is surfaced flagged untrusted and **never**\n * merged into a record, and a sequence gap is **never** silently bridged.\n *\n * This is the framing layer only: it does not parse record grammar. Hand a\n * reassembled record to `parseAstmRecords`, or use `parseFramedAstm` to compose\n * both layers at the edge.\n */\n\nimport { AstmParseError, FATAL_CODES } from \"../common/errors.js\";\nimport {\n CR,\n ETB,\n ETX,\n FN_SEVEN,\n FN_ZERO,\n FRAME_NUMBER_MODULUS,\n FIRST_FRAME_NUMBER,\n LF,\n MAX_FRAME_TEXT,\n STX,\n} from \"./constants.js\";\nimport { computeChecksum, parseChecksumHex } from \"./checksum.js\";\nimport { AstmFrameStrictError } from \"./errors.js\";\nimport {\n frameBadChecksum,\n frameOversize,\n frameSequenceGap,\n frameUnterminated,\n type AstmFrameWarning,\n} from \"./warnings.js\";\nimport type {\n AstmFrame,\n DecodeAstmFramesResult,\n FrameChecksum,\n FrameOptions,\n FrameTerminator,\n} from \"./types.js\";\n\n/**\n * Decode a framed ASTM byte stream into frames + reassembled record bytes.\n *\n * Bytes outside a frame (before the first `STX`, or between a frame's trailing\n * `LF` and the next `STX`) are skipped: in ASTM they are low-level transfer\n * control (`ENQ`/`ACK`/`NAK`/`EOT`), never record content, so skipping them is\n * not data loss.\n *\n * @param bytes - The framed byte stream.\n * @param options - Decode options; lenient unless `strict` is set.\n * @returns The decoded frames, the reassembled trusted record bytes, and any warnings.\n * @throws {@link AstmParseError} with `EMPTY_INPUT` when `bytes` is empty (both modes).\n * @throws {@link AstmFrameStrictError} when `strict` is set and any deviation occurred.\n * @example\n * ```ts\n * import { decodeAstmFrames } from \"@cosyte/astm\";\n * // One final frame carrying the record text \"R|1|\": checksum \"AF\" over FN..ETX (mod 256).\n * const bytes = new Uint8Array([0x02, 0x31, 0x52, 0x7c, 0x31, 0x7c, 0x03, 0x41, 0x46, 0x0d, 0x0a]);\n * const { records, frames } = decodeAstmFrames(bytes);\n * frames[0]?.frameNumber; // 1\n * records.length; // 1\n * ```\n */\nexport function decodeAstmFrames(\n bytes: Uint8Array,\n options: FrameOptions = {},\n): DecodeAstmFramesResult {\n if (bytes.length === 0) {\n throw new AstmParseError(FATAL_CODES.EMPTY_INPUT, \"Framed input is empty.\", { recordIndex: 0 });\n }\n\n const frames: AstmFrame[] = [];\n const records: Uint8Array[] = [];\n const warnings: AstmFrameWarning[] = [];\n\n // Reassembly state for the current (possibly multi-frame) record.\n let recordChunks: Uint8Array[] = [];\n let recordTainted = false;\n let recordOpen = false; // a frame has been accumulated toward a record not yet closed by ETX\n\n // Sequence state: the next frame number expected. ASTM starts at 1 and rolls over 7 → 0 → 1.\n let expected = FIRST_FRAME_NUMBER;\n\n const n = bytes.length;\n let i = 0;\n while (i < n) {\n if (bytes[i] !== STX) {\n i++; // inter-frame / transfer-control byte: not record content, skip it\n continue;\n }\n\n const stxOffset = i;\n const fnIndex = i + 1;\n const frameNumber = fnIndex < n ? readFrameNumber(bytes[fnIndex]) : undefined;\n\n // Scan forward for the terminator (ETB/ETX). A second STX or end-of-stream first ⇒ unterminated.\n let termIndex = -1;\n let oversize = false;\n for (let k = i + 2; k < n; k++) {\n const b = bytes[k];\n if (b === ETB || b === ETX) {\n termIndex = k;\n break;\n }\n if (b === STX) break; // next frame started before this one terminated\n if (k - (i + 2) + 1 > MAX_FRAME_TEXT) oversize = true; // keep scanning; flagged below\n }\n\n // ── Unterminated: no terminator, or the checksum bytes are truncated past end-of-stream. ──\n const checksumTruncated = termIndex !== -1 && termIndex + 2 >= n;\n if (termIndex === -1 || checksumTruncated) {\n // Payload ends at the terminator (checksum truncated) or the next STX / end-of-stream.\n const textEnd = termIndex !== -1 ? termIndex : nextStxOrEnd(bytes, i + 2, n);\n const text = bytes.slice(Math.min(i + 2, textEnd), textEnd);\n frames.push({\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n text,\n byteOffset: stxOffset,\n checksum: { computed: 0, valid: false },\n trusted: false,\n unterminated: true,\n oversize,\n });\n warnings.push(frameUnterminated(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n recordOpen = false; // this attempt ended in error; do not double-report at end-of-stream\n // Resume at the next STX (if the stream continued) or finish.\n i = termIndex === -1 ? textEnd : n;\n continue;\n }\n\n // ── Terminated frame: verify checksum over [FN .. terminator] inclusive. ──\n const termKind: FrameTerminator = bytes[termIndex] === ETB ? \"ETB\" : \"ETX\";\n const computed = computeChecksum(bytes, fnIndex, termIndex);\n const declared = parseChecksumHex(bytes, termIndex + 1, termIndex + 2);\n const valid = declared !== undefined && declared === computed;\n const checksum: FrameChecksum =\n declared !== undefined ? { computed, declared, valid } : { computed, valid };\n const text = bytes.slice(i + 2, termIndex);\n\n const frame: AstmFrame = {\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n terminator: termKind,\n text,\n byteOffset: stxOffset,\n checksum,\n trusted: valid,\n unterminated: false,\n oversize,\n };\n frames.push(frame);\n\n // Sequence check: never bridge a gap silently. An out-of-range FN (not 0–7) is a gap by\n // definition. On a valid, in-sequence number, advance the expected counter (mod 8).\n if (frameNumber !== undefined && isFrameDigit(frameNumber) && frameNumber === expected) {\n expected = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n } else {\n warnings.push(frameSequenceGap(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n // Resync to what we actually received so a single drop does not cascade warnings.\n if (frameNumber !== undefined && isFrameDigit(frameNumber)) {\n expected = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n }\n }\n\n if (oversize) warnings.push(frameOversize(framePosition(frameNumber, stxOffset)));\n\n if (!valid) {\n // Bad checksum: surfaced untrusted (above), warned, and NEVER merged into the record.\n warnings.push(frameBadChecksum(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n } else {\n recordChunks.push(text);\n }\n\n if (termKind === \"ETX\") {\n // Record complete. Emit ONLY a clean reassembly; a tainted record is dropped (its frames\n // remain surfaced), never emitted as if whole.\n if (!recordTainted) records.push(concatBytes(recordChunks));\n recordChunks = [];\n recordTainted = false;\n recordOpen = false;\n } else {\n recordOpen = true; // ETB: the record continues in a later frame\n }\n\n // Advance past the two checksum chars and an optional CR/LF tail.\n i = termIndex + 3;\n if (bytes[i] === CR) i++;\n if (bytes[i] === LF) i++;\n }\n\n // A record left open on an intermediate ETB with no final ETX is unterminated: no partial\n // record is invented; its frames are already surfaced.\n if (recordOpen) {\n warnings.push(frameUnterminated({ byteOffset: n }));\n }\n\n if (options.strict === true && warnings.length > 0) {\n throw new AstmFrameStrictError(warnings);\n }\n\n return freezeResult({ records, frames, warnings });\n}\n\n/**\n * Freeze the decoded result so the model rejects mutation: the frame objects,\n * their checksum sub-objects, the warnings, and the three container arrays. The\n * `Uint8Array` payloads are deliberately **not** frozen: `Object.freeze` throws on\n * a typed array that has elements, and the byte buffers are copies the caller owns.\n */\nfunction freezeResult(result: DecodeAstmFramesResult): DecodeAstmFramesResult {\n for (const f of result.frames) {\n Object.freeze(f.checksum);\n Object.freeze(f);\n }\n for (const w of result.warnings) {\n Object.freeze(w.position);\n Object.freeze(w);\n }\n Object.freeze(result.records);\n Object.freeze(result.frames);\n Object.freeze(result.warnings);\n return Object.freeze(result);\n}\n\n/** Read a frame-number byte into its numeric value; `undefined` only when the byte is absent. */\nfunction readFrameNumber(fnByte: number | undefined): number | undefined {\n if (fnByte === undefined) return undefined;\n // Surface the digit value even when out of 0–7 range (a corruption flagged as a sequence gap).\n return fnByte - FN_ZERO;\n}\n\n/** Whether a decoded frame number is a valid ASTM `0`–`7` sequence digit. */\nfunction isFrameDigit(frameNumber: number): boolean {\n return frameNumber >= 0 && frameNumber <= FN_SEVEN - FN_ZERO;\n}\n\n/** Build a value-free frame position (frame number + byte offset). */\nfunction framePosition(\n frameNumber: number | undefined,\n byteOffset: number,\n): { frameNumber?: number; byteOffset: number } {\n return frameNumber !== undefined ? { frameNumber, byteOffset } : { byteOffset };\n}\n\n/** Index of the next `STX` at or after `from`, or `end` if none: bounds a partial (unterminated) frame. */\nfunction nextStxOrEnd(bytes: Uint8Array, from: number, end: number): number {\n for (let k = from; k < end; k++) {\n if (bytes[k] === STX) return k;\n }\n return end;\n}\n\n/** Concatenate byte chunks into one `Uint8Array` (the reassembled record bytes). */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * The frame-layer **emit** side: {@link composeAstmFrames}.\n *\n * The exact inverse of {@link decodeAstmFrames}. Given the reassembled record\n * byte-strings (one entry per complete record, the same shape `decodeAstmFrames`\n * *produces*), it wraps each into one or more `<STX> FN text <ETB|ETX> CS <CR><LF>`\n * frames, so `decodeAstmFrames(composeAstmFrames(records)).records` reproduces the\n * input records exactly.\n *\n * Everything structural is **computed, never accepted-as-given**:\n * - the **modulo-256 checksum** over `FN … terminator` (inclusive), emitted as two\n * **uppercase** hex chars (the conservative-emit form the decoder accepts either\n * case of);\n * - the **frame number** `0`–`7`, starting at `1` and rolling over `7 → 0 → 1`,\n * continuous across every frame in the stream (as the decoder's sequence check\n * expects);\n * - the **240-byte text split**: a record longer than 240 bytes is split across\n * frames closed by `ETB` (intermediate) with a final `ETX`; the seven framing\n * control bytes are never counted toward the 240.\n *\n * A record must carry bytes: an empty record (or an empty record list) is a typed\n * {@link AstmFrameEncodeError}, never an empty frame.\n */\n\nimport { computeChecksum, toChecksumHex } from \"./checksum.js\";\nimport {\n CR,\n ETB,\n ETX,\n FN_ZERO,\n FRAME_NUMBER_MODULUS,\n FIRST_FRAME_NUMBER,\n LF,\n MAX_FRAME_TEXT,\n STX,\n} from \"./constants.js\";\n\n/**\n * Thrown by {@link composeAstmFrames} when the input cannot be framed into a\n * spec-clean stream: an empty record list, or a record with no bytes (a frame\n * must carry a record; an empty one is a structural error, never an empty frame).\n * Carries a stable code + the offending record index, never record bytes.\n *\n * @example\n * ```ts\n * import { composeAstmFrames, AstmFrameEncodeError } from \"@cosyte/astm\";\n * try {\n * composeAstmFrames([]);\n * } catch (err) {\n * if (err instanceof AstmFrameEncodeError) err.code; // \"ASTM_FRAME_EMPTY_RECORD\"\n * }\n * ```\n */\nexport class AstmFrameEncodeError extends Error {\n /** Stable discriminant. */\n public readonly code: \"ASTM_FRAME_EMPTY_RECORD\";\n /** Index of the offending record within the input, when applicable. */\n public readonly recordIndex?: number;\n /** @internal */\n public constructor(message: string, recordIndex?: number) {\n super(message);\n this.name = \"AstmFrameEncodeError\";\n this.code = \"ASTM_FRAME_EMPTY_RECORD\";\n if (recordIndex !== undefined) this.recordIndex = recordIndex;\n }\n}\n\n/** Options for {@link composeAstmFrames}. */\nexport interface ComposeFramesOptions {\n /**\n * The frame number to start the sequence at (`0`–`7`). Defaults to\n * {@link FIRST_FRAME_NUMBER} (`1`): the ASTM convention.\n */\n readonly startFrameNumber?: number;\n}\n\n/** Latin1 bytes of a string (each char → its byte), so `\\r` etc. survive 1:1. */\nfunction toBytes(input: Uint8Array | string): Uint8Array {\n if (typeof input !== \"string\") return input;\n const out = new Uint8Array(input.length);\n for (let i = 0; i < input.length; i += 1) out[i] = input.charCodeAt(i) & 0xff;\n return out;\n}\n\n/** Build one frame's bytes: `STX FN text (ETB|ETX) C1 C2 CR LF`, checksum over `FN … terminator`. */\nfunction encodeFrame(text: Uint8Array, frameNumber: number, isFinal: boolean): number[] {\n const term = isFinal ? ETX : ETB;\n // Assemble STX FN text term, then compute the checksum over FN..term inclusive.\n const bytes = [STX, FN_ZERO + frameNumber, ...text, term];\n const cs = computeChecksum(Uint8Array.from(bytes), 1, bytes.length - 1);\n const hex = toChecksumHex(cs);\n return [...bytes, hex.charCodeAt(0), hex.charCodeAt(1), CR, LF];\n}\n\n/**\n * Frame reassembled record bytes into a spec-clean ASTM/CLSI-LIS01 byte stream:\n * the inverse of {@link decodeAstmFrames}.\n *\n * Each record is split at 240 text bytes into `ETB`-closed intermediate frames\n * and a final `ETX` frame; frame numbers run `1, 2, … 7, 0, 1 …` continuously\n * across the whole stream; every frame's modulo-256 checksum is computed and\n * emitted uppercase.\n *\n * @param records - The reassembled record byte-strings (`Uint8Array` or latin1\n * `string`), one entry per complete record.\n * @param options - Encode options.\n * @returns The framed byte stream.\n * @throws {@link AstmFrameEncodeError} when the list is empty or a record has no bytes.\n * @example\n * ```ts\n * import { composeAstmFrames, decodeAstmFrames } from \"@cosyte/astm\";\n * const records = [new TextEncoder().encode(\"H|\\\\^&\\r\"), new TextEncoder().encode(\"L|1\\r\")];\n * const bytes = composeAstmFrames(records);\n * decodeAstmFrames(bytes).records.length; // 2\n * ```\n */\nexport function composeAstmFrames(\n records: readonly (Uint8Array | string)[],\n options: ComposeFramesOptions = {},\n): Uint8Array {\n if (records.length === 0) {\n throw new AstmFrameEncodeError(\"Cannot frame an empty record list: nothing to transmit.\");\n }\n\n const out: number[] = [];\n let frameNumber = options.startFrameNumber ?? FIRST_FRAME_NUMBER;\n\n records.forEach((record, recordIndex) => {\n const bytes = toBytes(record);\n if (bytes.length === 0) {\n throw new AstmFrameEncodeError(\n \"Cannot frame an empty record: a frame must carry record bytes.\",\n recordIndex,\n );\n }\n // Split the record text into <=240-byte chunks; the last chunk of the record closes with ETX.\n for (let offset = 0; offset < bytes.length; offset += MAX_FRAME_TEXT) {\n const chunk = bytes.subarray(offset, offset + MAX_FRAME_TEXT);\n const isFinal = offset + MAX_FRAME_TEXT >= bytes.length;\n out.push(...encodeFrame(chunk, frameNumber, isFinal));\n frameNumber = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n }\n });\n\n return Uint8Array.from(out);\n}\n","/**\n * Composing the two layers at the edge: {@link parseFramedAstm}.\n *\n * The framing layer ({@link decodeAstmFrames}) and the record layer\n * (`parseAstmRecords`) share nothing but the payload boundary: a frame carries\n * record bytes; a record knows nothing about frames. This helper joins them at the\n * one point they meet: decode a framed stream into trusted, reassembled record\n * bytes, then parse those bytes into an {@link AstmMessage}. It is deliberately\n * thin: a consumer that already holds de-framed record bytes calls\n * `parseAstmRecords` directly, and a consumer that only wants frames calls\n * `decodeAstmFrames` directly.\n */\n\nimport { parseAstmRecords } from \"../records/parse.js\";\nimport { serializeAstmRecord } from \"../records/serialize.js\";\nimport type { AstmMessage, AstmParseOptions, AstmRecord } from \"../records/types.js\";\nimport { composeAstmFrames, type ComposeFramesOptions } from \"./encode.js\";\nimport { decodeAstmFrames } from \"./decode.js\";\nimport type { AstmFrame, FrameOptions } from \"./types.js\";\nimport type { AstmFrameWarning } from \"./warnings.js\";\n\n/**\n * The result of {@link parseFramedAstm}: the parsed message, plus the frame-layer\n * detail (every decoded frame and the frame warnings) so a consumer keeps full\n * visibility into the transport below the records.\n */\nexport interface FramedAstmResult {\n /** The message parsed from the trusted, reassembled record bytes. */\n readonly message: AstmMessage;\n /** Every decoded frame, trusted or not, in wire order. */\n readonly frames: readonly AstmFrame[];\n /** The frame-layer warnings (bad checksum, sequence gap, unterminated, oversize). */\n readonly frameWarnings: readonly AstmFrameWarning[];\n}\n\n/**\n * Decode a framed ASTM byte stream and parse its reassembled records in one call.\n *\n * Only **trusted, contiguous** frames are reassembled (a bad-checksum frame, a\n * sequence gap, or an unterminated frame is surfaced in `frames`/`frameWarnings`\n * but never fed to the record parser), so the parsed `message` reflects only bytes\n * the framing layer vouched for.\n *\n * @param bytes - The framed byte stream.\n * @param options - Frame decode options **and** record parse options (both layers\n * honor a shared `strict`).\n * @returns The parsed message plus the frame-layer detail.\n * @throws {@link AstmParseError} `EMPTY_INPUT` when the stream is empty, or when no\n * trusted records could be reassembled (nothing to parse).\n * @throws {@link AstmFrameStrictError} / {@link AstmStrictError} in `strict` mode on\n * a frame or record deviation, respectively.\n * @example\n * ```ts\n * import { parseFramedAstm } from \"@cosyte/astm\";\n * // A single final frame carrying \"H|\\^&\\r\": checksum \"E5\" over FN..ETX (mod 256).\n * const bytes = new Uint8Array([\n * 0x02, 0x31, 0x48, 0x7c, 0x5c, 0x5e, 0x26, 0x0d, 0x03, 0x45, 0x35, 0x0d, 0x0a,\n * ]);\n * const { message, frames } = parseFramedAstm(bytes);\n * message.header.delimiters.field; // \"|\"\n * frames.length; // 1\n * ```\n */\nexport function parseFramedAstm(\n bytes: Uint8Array,\n options: FrameOptions & AstmParseOptions = {},\n): FramedAstmResult {\n const { records, frames, warnings } = decodeAstmFrames(bytes, options);\n const joined = concatBytes(records);\n const message = parseAstmRecords(joined, options);\n return { message, frames, frameWarnings: warnings };\n}\n\n/**\n * Serialize an ASTM message (or a bare record list) and frame it into a spec-clean\n * byte stream in one call: the inverse of {@link parseFramedAstm}, composing the\n * two emit layers at the edge.\n *\n * Each record is serialized to spec-clean, `CR`-terminated wire text (canonical\n * delimiters, embedded delimiters re-escaped) and then framed **independently**\n * (one record per `ETX`-closed frame run), so the framing exactly mirrors what\n * {@link decodeAstmFrames} reassembles: `parseFramedAstm(serializeFramedAstm(msg))`\n * yields an equal message.\n *\n * @param input - A parsed {@link AstmMessage} or a list of {@link AstmRecord}s.\n * @param options - Frame-encode options.\n * @returns The framed byte stream.\n * @throws {@link AstmSerializeError} when a value contains an unencodable `CR`/`LF`.\n * @throws {@link AstmFrameEncodeError} when there are no records to frame.\n * @example\n * ```ts\n * import { parseAstmRecords, serializeFramedAstm, parseFramedAstm } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const bytes = serializeFramedAstm(msg);\n * parseFramedAstm(bytes).message.records.length; // 3\n * ```\n */\nexport function serializeFramedAstm(\n input: AstmMessage | readonly AstmRecord[],\n options: ComposeFramesOptions = {},\n): Uint8Array {\n const records: readonly AstmRecord[] = Array.isArray(input)\n ? (input as readonly AstmRecord[])\n : (input as AstmMessage).records;\n // Frame each record independently (record text + its CR terminator) so decode reassembles per record.\n const recordBytes = records.map((r) => serializeAstmRecord(r) + \"\\r\");\n return composeAstmFrames(recordBytes, options);\n}\n\n/** Concatenate the reassembled record byte-strings into one de-framed record stream. */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * Public entry point for the `@cosyte/astm` package: the ASTM/CLSI-LIS02\n * **record** layer.\n *\n * The north star: hand a de-framed ASTM record stream to {@link parseAstmRecords}\n * and pull a result's value, units, and flag out in one line. Parsing is lenient\n * (vendor quirks become typed warnings, never silent loss) and, the whole point,\n * **never returns a confident wrong value**. Delimiters are read from each header,\n * embedded escapes are decoded before a value is split, the practice- and\n * laboratory-assigned patient IDs stay distinct, and every deviation is a stable,\n * value-free warning. Result flag/status semantics, patient/order identity\n * depth + the `C` comment record + partial-timestamp hardening, and the\n * request-information (`Q`) record + host-query classification + verbatim `M`/`S`\n * records are all modeled: the **record-content layer is now feature-complete**.\n *\n * A parsed model is a whole record **stream**, and a stream may carry several `H` … `L`\n * messages. {@link messages} splits it into them, so a patient and a result are only ever\n * paired inside the message that carried both; the flat extractors ({@link patient},\n * {@link results}, …) read the whole stream and therefore **refuse** one they cannot answer\n * for, with an {@link AstmAmbiguousStreamError}, rather than answering across patients.\n *\n * The E1381/CLSI-LIS01 **framing** layer lives alongside the record layer and\n * shares nothing but the payload boundary: {@link decodeAstmFrames} decodes a framed\n * byte stream (`<STX> FN text <ETB|ETX> CS <CR><LF>`) into frames + reassembled\n * record bytes: verifying the modulo-256 checksum (a bad frame is surfaced\n * untrusted, never merged), tracking frame-number sequencing (a gap is never\n * silently bridged), and reassembling the 240-byte-limited multi-frame records.\n * {@link parseFramedAstm} composes the two layers at the edge.\n *\n * The LTP **protocol** layer sits above the frame codec: {@link detectFraming}\n * auto-detects the transport reality from the stream's leading byte, framed\n * (serial / cobas 4800 / Iguana) vs raw (cobas b121, framing dropped).\n * {@link ltpReduce} is a **pure, socket-free** receiver-side state machine over\n * `ENQ`/`ACK`/`NAK`/`EOT` + frame-received events. The consumer owns the wire; the\n * reducer decides. Its inviolable rule mirrors `mllp`'s ACK-failsafe: a frame the\n * codec did not vouch for is answered with `NAK`, **never** a fabricated positive\n * `ACK`, and never merged into a record, a `NAK` drives retransmit, not acceptance.\n *\n * The **emit** half is the conservative inverse of the record and framing layers.\n * {@link buildAstmMessage} constructs records from typed caller input, emitting only\n * the values the caller supplied; {@link serializeAstmRecords} writes them out with the\n * canonical `H|\\^&` delimiters by default, re-escaping every embedded delimiter in a\n * decoded component leaf (every record type is emitted from its decoded `fields`, except that\n * `M`/`S` bytes pass through untouched when they already read correctly under the emit set, so\n * an edit to the model is reflected on emit and the whole stream goes out in the one\n * delimiter set the header declares); {@link composeAstmFrames} wraps them into frames whose\n * modulo-256 checksum is\n * computed rather than accepted; and {@link serializeFramedAstm} composes the last two.\n * The protocol layer has no emit inverse by design: {@link ltpReduce} returns the actions\n * to take, and the consumer owns the wire.\n */\n\n/**\n * Library version string, kept in lockstep with `package.json#version`.\n *\n * Changesets owns the bump and rewrites `package.json` only, so the release `version` script runs\n * `scripts/sync-version.mjs` to rewrite this declaration in the same commit, and\n * `test/sanity.test.ts` compares the two so a skipped sync goes red instead of shipping a version\n * string that lies.\n *\n * @example\n * ```ts\n * import { VERSION } from \"@cosyte/astm\";\n * console.log(VERSION);\n * ```\n */\nexport const VERSION: string = \"0.0.7\";\n\nexport { parseAstmRecords, AstmStrictError, attachComments } from \"./records/parse.js\";\nexport {\n serializeAstmRecords,\n serializeAstmRecord,\n serializeField,\n encodeComponent,\n AstmSerializeError,\n} from \"./records/serialize.js\";\nexport type { AstmSerializeErrorCode } from \"./records/serialize.js\";\nexport { buildAstmMessage } from \"./records/build.js\";\nexport type {\n AstmRecordInput,\n MessageInput,\n HeaderInput,\n PatientInput,\n PatientNameInput,\n OrderInput,\n ResultInput,\n CommentInput,\n QueryInput,\n VerbatimInput,\n} from \"./records/build.js\";\nexport { results, patient, orders, comments, commentsFor, query } from \"./records/extractors.js\";\nexport { messages, AMBIGUOUS_CODES, AstmAmbiguousStreamError } from \"./records/messages.js\";\nexport type { AstmStreamMessage, AmbiguousCode } from \"./records/messages.js\";\nexport { classifyMessage } from \"./records/host-query.js\";\nexport { fieldScalar, tokenizeHeader, tokenizeRecord } from \"./records/tokenize.js\";\nexport {\n interpretAbnormalFlag,\n interpretResultStatus,\n parseReferenceRange,\n} from \"./records/result-semantics.js\";\nexport type {\n AbnormalFlag,\n AbnormalFlagCode,\n AbnormalFlagMeaning,\n ReferenceRange,\n ReferenceRangeKind,\n ResultStatus,\n ResultStatusCode,\n ResultStatusMeaning,\n} from \"./records/result-semantics.js\";\nexport type {\n AstmField,\n AstmMessage,\n AstmMessageClassification,\n AstmMessageKind,\n AstmParseOptions,\n AstmRecord,\n HeaderRecord,\n PatientRecord,\n PatientName,\n OrderRecord,\n ResultRecord,\n CommentRecord,\n QueryRecord,\n ManufacturerRecord,\n ScientificRecord,\n TerminatorRecord,\n UnsupportedRecord,\n} from \"./records/types.js\";\n\nexport { FATAL_CODES, AstmParseError } from \"./common/errors.js\";\nexport type { FatalCode } from \"./common/errors.js\";\nexport {\n WARNING_CODES,\n unknownRecordType,\n nonStandardDelimiters,\n unknownEscapeSequence,\n ambiguousValueSplit,\n undefinedAbnormalFlag,\n undefinedResultStatus,\n unparseableReferenceRange,\n unitsAbsent,\n orphanComment,\n partialTimestamp,\n uninterpretedQueryStatus,\n ambiguousMessageKind,\n delimitersRedeclared,\n unreadableRedeclaration,\n profileQuirkApplied,\n} from \"./common/warnings.js\";\nexport type { WarningCode, AstmRecordWarning } from \"./common/warnings.js\";\nexport type { AstmPosition } from \"./common/position.js\";\n\nexport { CANONICAL_DELIMITERS, readDelimiters, isNonStandard } from \"./common/delimiters.js\";\nexport type { Delimiters } from \"./common/delimiters.js\";\nexport { decodeEscapes, splitEscapeAware } from \"./common/escapes.js\";\nexport type { UnknownEscapeSink } from \"./common/escapes.js\";\nexport { parseAstmDate, astmDateToLocalISO } from \"./common/dates.js\";\nexport type { AstmDate, AstmDatePrecision } from \"./common/dates.js\";\nexport { recognizeUniversalTestId, primaryCode } from \"./common/coding-system.js\";\nexport type { UniversalTestId, UniversalTestIdProvenance } from \"./common/coding-system.js\";\nexport { deepFreeze } from \"./common/freeze.js\";\n\n// ── The vendor profile system (P8): the engine + registry + tolerance transform ──\n// `defineAstmProfile` builds an immutable, provenance-backed profile with a\n// definition-time safety gate (a profile can never tolerate a safety-critical\n// deviation, and never alters an extracted value: it only re-badges an expected\n// warning to PROFILE_QUIRK_APPLIED and can force the raw-vs-framed transport). The\n// built-in registry ships `default` + the corpus-grounded `referenceCorpus`; named\n// vendor profiles are deferred (REAL-CORPUS) but fully supported by the engine.\nexport {\n defineAstmProfile,\n AstmProfileDefinitionError,\n astmProfiles,\n getAstmProfile,\n listAstmProfiles,\n setDefaultAstmProfile,\n getDefaultAstmProfile,\n applyAstmProfile,\n applyAstmProfileToWarnings,\n resolveProfileTransport,\n SAFETY_CRITICAL_CODES,\n TOLERABLE_CODES,\n ALL_ASTM_WARNING_CODES,\n isSafetyCriticalCode,\n} from \"./profiles/index.js\";\nexport type {\n AstmProfile,\n DefineAstmProfileOptions,\n AstmQuirkTolerance,\n AstmQuirkMatch,\n AstmProfileProvenance,\n AnyAstmWarningCode,\n} from \"./profiles/index.js\";\n\n// ── The terminology layer (P9): LIVD-aware LOINC recognition (bring-your-own) ──\n// `@cosyte/astm` bundles no LOINC/SNOMED/LIVD data (roadmap §5): LOINC is © Regenstrief\n// (redistributable only with its attribution notice) and the public CDC LIVD file is\n// SARS-CoV-2-specific and carries separately-licensed SNOMED: not a general public-domain\n// catalog. So a consumer supplies their own IICC LIVD catalog (`defineLivdCatalog`) and\n// `applyLivd` produces an additive, advisory LOINC annotation layer that never mutates the\n// raw code/value and never guesses a LOINC: an unmapped/ambiguous code surfaces as such.\nexport {\n defineLivdCatalog,\n applyLivd,\n lookupLivdForRecord,\n LIVD_WARNING_CODES,\n livdUnmappedCode,\n livdAmbiguousMapping,\n} from \"./terminology/index.js\";\nexport type {\n LivdCatalog,\n LivdEntry,\n LivdLookup,\n LivdAnnotation,\n LivdMapping,\n LivdResult,\n AstmLivdWarning,\n LivdWarningCode,\n} from \"./terminology/index.js\";\n\n// ── The LTP protocol layer (P6): transport detection + pure session reducer ──\nexport { detectFraming } from \"./ltp/transport.js\";\nexport type { AstmFraming, DetectFramingOptions, DetectFramingResult } from \"./ltp/transport.js\";\nexport { ltpInitialState, ltpReduce } from \"./ltp/reducer.js\";\nexport {\n LTP_WARNING_CODES,\n ltpAmbiguousTransport,\n ltpUnexpectedEvent,\n ltpFrameRejected,\n} from \"./ltp/warnings.js\";\nexport type { LtpWarningCode, AstmLtpWarning } from \"./ltp/warnings.js\";\nexport {\n ENQ as ASTM_ENQ,\n ACK as ASTM_ACK,\n NAK as ASTM_NAK,\n EOT as ASTM_EOT,\n} from \"./ltp/constants.js\";\nexport type { LtpPhase, LtpState, LtpEvent, LtpAction, LtpTransition } from \"./ltp/types.js\";\n\n// ── The E1381 / CLSI-LIS01 framing layer (P5) ──\nexport { decodeAstmFrames } from \"./frames/decode.js\";\nexport { composeAstmFrames, AstmFrameEncodeError } from \"./frames/encode.js\";\nexport type { ComposeFramesOptions } from \"./frames/encode.js\";\nexport { parseFramedAstm, serializeFramedAstm } from \"./frames/compose.js\";\nexport type { FramedAstmResult } from \"./frames/compose.js\";\nexport { computeChecksum, toChecksumHex, parseChecksumHex } from \"./frames/checksum.js\";\nexport { AstmFrameStrictError } from \"./frames/errors.js\";\nexport {\n FRAME_WARNING_CODES,\n frameBadChecksum,\n frameSequenceGap,\n frameUnterminated,\n frameOversize,\n} from \"./frames/warnings.js\";\nexport type { FrameWarningCode, AstmFrameWarning } from \"./frames/warnings.js\";\nexport type { AstmFramePosition } from \"./frames/position.js\";\nexport type {\n AstmFrame,\n FrameChecksum,\n FrameTerminator,\n FrameOptions,\n DecodeAstmFramesResult,\n} from \"./frames/types.js\";\n"]}
1
+ {"version":3,"sources":["../src/common/errors.ts","../src/common/freeze.ts","../src/common/delimiters.ts","../src/common/dates.ts","../src/common/coding-system.ts","../src/common/warnings.ts","../src/records/result-semantics.ts","../src/records/host-query.ts","../src/frames/warnings.ts","../src/ltp/warnings.ts","../src/profiles/safety.ts","../src/profiles/apply.ts","../src/profiles/describe.ts","../src/profiles/merge.ts","../src/profiles/errors.ts","../src/profiles/validate.ts","../src/profiles/define.ts","../src/profiles/default.ts","../src/profiles/reference-corpus.ts","../src/profiles/registry.ts","../src/common/escapes.ts","../src/records/tokenize.ts","../src/records/parse.ts","../src/records/serialize.ts","../src/records/build.ts","../src/records/messages.ts","../src/records/extractors.ts","../src/profiles/index.ts","../src/terminology/catalog.ts","../src/terminology/warnings.ts","../src/terminology/apply.ts","../src/ltp/constants.ts","../src/frames/constants.ts","../src/ltp/transport.ts","../src/ltp/reducer.ts","../src/frames/checksum.ts","../src/frames/errors.ts","../src/frames/decode.ts","../src/frames/encode.ts","../src/frames/compose.ts","../src/index.ts"],"names":["line","next","at","text","concatBytes"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Fatal error taxonomy for the `@cosyte/astm` record parser.\n *\n * Tier-3 **fatal** codes mark input the parser cannot recover into a structured\n * `AstmMessage`; anything less severe is a Tier-2 warning (see `./warnings.ts`).\n * `AstmParseError` is thrown directly and consumers narrow via the `code`\n * discriminant. {@link FatalCode} is **closed**: these three are all of it. The frame\n * codec reuses `EMPTY_INPUT` (an empty stream is fatal in both its lenient and strict\n * modes) but does not widen the union, its own thrown errors are separate types with\n * their own discriminants: {@link AstmFrameEncodeError} carries\n * `ASTM_FRAME_EMPTY_RECORD`, and {@link AstmFrameStrictError} carries the rejected\n * warnings rather than a `code`. So narrowing an `AstmParseError` on `code` will only\n * ever see a value from this set.\n */\n\nimport type { AstmPosition } from \"./position.js\";\n\n/**\n * Stable string codes for every Tier-3 fatal the record parser may throw.\n * Consumers narrow on `err.code` to react to specific structural failures.\n * Renaming a code is a **breaking change**.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, FATAL_CODES, AstmParseError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"\");\n * } catch (err) {\n * if (err instanceof AstmParseError && err.code === FATAL_CODES.EMPTY_INPUT) {\n * // handle empty input\n * }\n * }\n * ```\n */\nexport const FATAL_CODES = {\n /** Input was empty or whitespace-only: there is nothing to parse. Shared across layers. */\n EMPTY_INPUT: \"EMPTY_INPUT\",\n /** The first record is not an `H` (header) record: an ASTM message must lead with `H`. */\n ASTM_RECORD_NO_HEADER: \"ASTM_RECORD_NO_HEADER\",\n /** The `H` record is too short to declare the four delimiters (field/repeat/component/escape). */\n ASTM_RECORD_UNDECLARED_DELIMITERS: \"ASTM_RECORD_UNDECLARED_DELIMITERS\",\n} as const;\n\n/**\n * A value from {@link FATAL_CODES}: the type carried by a thrown {@link AstmParseError}.\n */\nexport type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];\n\n/**\n * Thrown by {@link parseAstmRecords} when the input violates one of the Tier-3\n * unrecoverable structural rules (empty input, no leading `H`, or an `H` that\n * cannot declare its delimiters). Carries positional context so consumers can\n * log an actionable error.\n *\n * **PHI:** the error carries a stable code + position only, never a field value.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, AstmParseError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"P|1\");\n * } catch (err) {\n * if (err instanceof AstmParseError && err.code === \"ASTM_RECORD_NO_HEADER\") {\n * // err.position is available; err carries no field value\n * }\n * }\n * ```\n */\nexport class AstmParseError extends Error {\n public readonly code: FatalCode;\n public readonly position: AstmPosition;\n\n /**\n * Construct a new `AstmParseError`. Both fields are required so every thrower\n * populates positional context and no PHI-bearing snippet is ever attached.\n *\n * @internal\n */\n public constructor(code: FatalCode, message: string, position: AstmPosition) {\n super(message);\n this.name = \"AstmParseError\";\n this.code = code;\n this.position = position;\n }\n}\n","/**\n * Deep-freeze helper for the immutable ASTM model.\n *\n * The parser returns plain readonly data; `deepFreeze` makes that immutability\n * enforced at runtime (a stray `msg.records[0].fields[0] = …` throws in strict\n * mode and is a no-op otherwise), which the shared `immutabilityProperty`\n * invariant asserts. Zero-dep, Node stdlib only.\n */\n\n/**\n * Recursively freeze an object graph (objects and arrays), returning the same\n * reference typed as deeply readonly. Cyclic graphs are not produced by the\n * parser, so a simple recursive walk suffices.\n *\n * @param value - The value to freeze in place.\n * @returns The same value, now deeply frozen.\n * @example\n * ```ts\n * import { deepFreeze } from \"@cosyte/astm\";\n * const frozen = deepFreeze({ a: [1, 2] });\n * Object.isFrozen(frozen.a); // true\n * ```\n */\nexport function deepFreeze<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (Object.isFrozen(value)) return value;\n Object.freeze(value);\n for (const key of Object.keys(value)) {\n deepFreeze((value as Record<string, unknown>)[key]);\n }\n return value;\n}\n","/**\n * The ASTM delimiter model and its self-declaration reader.\n *\n * ASTM/CLSI-LIS02 messages are **self-describing**: every `H` (header) record\n * declares the four delimiters it uses, in the bytes immediately after the `H`.\n * A parser MUST read them from the record and MUST NOT hardcode them: a vendor\n * is free to declare non-standard delimiters and a conformant reader follows.\n *\n * The canonical declaration is `H|\\^&`:\n *\n * | position | char | role |\n * |--------------------------|------|-------------------------|\n * | immediately after `H` | `\\|` | **field** delimiter |\n * | delimiter-definition [0] | `\\` | **repeat** delimiter |\n * | delimiter-definition [1] | `^` | **component** delimiter |\n * | delimiter-definition [2] | `&` | **escape** delimiter |\n *\n * Note ASTM differs from HL7 v2 here: ASTM's `\\` is the **repeat** delimiter and\n * `&` is the **escape** delimiter, where HL7 uses `\\` for escape and `~` for\n * repetition. Getting this mapping wrong silently mis-reads values, so the roles\n * are read positionally from the record, never assumed.\n */\n\n/**\n * The four ASTM delimiters resolved from an `H` record. Immutable; carried on\n * the parsed message as delimiter provenance.\n *\n * @example\n * ```ts\n * import { readDelimiters } from \"@cosyte/astm\";\n * const d = readDelimiters(\"H|\\\\^&\");\n * d.field; // \"|\"\n * d.repeat; // \"\\\\\"\n * d.component; // \"^\"\n * d.escape; // \"&\"\n * ```\n */\nexport interface Delimiters {\n /** Field separator: the char immediately after `H` in the header. */\n readonly field: string;\n /** Repeat (repetition) separator: ASTM `\\` by default. */\n readonly repeat: string;\n /** Component separator: ASTM `^` by default. */\n readonly component: string;\n /** Escape character: ASTM `&` by default (introduces `&F&`/`&S&`/`&R&`/`&E&`). */\n readonly escape: string;\n}\n\n/**\n * The canonical ASTM delimiter set (`H|\\^&`). Used only as documentation and as\n * a comparison baseline for the non-standard-delimiter warning: never as a\n * parse-time default (delimiters are always read from the header).\n *\n * @example\n * ```ts\n * import { CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * CANONICAL_DELIMITERS.repeat; // \"\\\\\"\n * ```\n */\nexport const CANONICAL_DELIMITERS: Delimiters = {\n field: \"|\",\n repeat: \"\\\\\",\n component: \"^\",\n escape: \"&\",\n};\n\n/** The result of reading delimiters from a header record: either resolved or a declared failure. */\nexport type DelimiterReadResult =\n | { readonly ok: true; readonly delimiters: Delimiters }\n | { readonly ok: false };\n\n/**\n * Read the four delimiters from a header record's raw text (a single `H`\n * record, its terminator already stripped).\n *\n * Returns `undefined` when the record cannot declare all four delimiters: it is\n * shorter than `H` + a field separator + a 3-char delimiter definition, or the\n * field separator it names also appears among the other three.\n *\n * What the caller does with that depends on **which** header it is. On the first\n * header it is the `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal, because there is no\n * earlier set to fall back to. On any later header (a stream may carry several\n * messages, each declaring its own set) the delimiters already in force are kept\n * and an `ASTM_RECORD_UNREADABLE_REDECLARATION` warning is raised instead; a set is\n * never guessed and no record is dropped.\n *\n * This function does not throw: delimiter resolution and the escalation decision are\n * kept separate so the reader stays pure and testable.\n *\n * @param headerRecord - The raw `H` record text (no trailing CR/LF).\n * @returns The resolved delimiters, or a declared failure.\n * @example\n * ```ts\n * import { readDelimiters } from \"@cosyte/astm\";\n * const d = readDelimiters(\"H|\\\\^&|||sender\");\n * d.field; // \"|\"\n * ```\n */\nexport function readDelimiters(headerRecord: string): Delimiters | undefined {\n // Need \"H\" + field-sep + at least the 3-char delimiter definition (repeat/component/escape).\n if (headerRecord.length < 5) return undefined;\n if (headerRecord.charAt(0) !== \"H\") return undefined;\n\n const field = headerRecord.charAt(1);\n // The delimiter-definition field runs from index 2 up to the next field separator.\n const defEnd = headerRecord.indexOf(field, 2);\n const definition = headerRecord.slice(2, defEnd === -1 ? headerRecord.length : defEnd);\n if (definition.length < 3) return undefined;\n\n const repeat = definition.charAt(0);\n const component = definition.charAt(1);\n const escape = definition.charAt(2);\n\n // A field separator that also appears among the other three delimiters is not a coherent\n // declaration: the four roles must be distinguishable. Refuse rather than mis-split.\n if (field === repeat || field === component || field === escape) return undefined;\n\n return { field, repeat, component, escape };\n}\n\n/**\n * Whether a resolved delimiter set differs from the canonical `H|\\^&`. A `true`\n * result is a tolerated vendor quirk, surfaced as a value-free warning so a\n * consumer can notice a non-standard stream without the parser refusing it.\n *\n * @example\n * ```ts\n * import { isNonStandard, readDelimiters } from \"@cosyte/astm\";\n * isNonStandard(readDelimiters(\"H|\\\\^&\")!); // false\n * ```\n */\nexport function isNonStandard(d: Delimiters): boolean {\n return (\n d.field !== CANONICAL_DELIMITERS.field ||\n d.repeat !== CANONICAL_DELIMITERS.repeat ||\n d.component !== CANONICAL_DELIMITERS.component ||\n d.escape !== CANONICAL_DELIMITERS.escape\n );\n}\n","/**\n * The ASTM date/time value: `YYYYMMDDHHMMSS`, precision-preserving, no timezone.\n *\n * ASTM timestamps are a run of digits, most-significant first, truncated at\n * whatever precision the instrument reports. There is **no timezone field**:\n * the value is local to the instrument and this parser never assumes UTC. A\n * date-only value (`YYYYMMDD`) is a normal, complete value at day precision:\n * **not** an error and never zero-filled into a fake time.\n */\n\n/** The precision to which an {@link AstmDate} is populated. */\nexport type AstmDatePrecision = \"year\" | \"month\" | \"day\" | \"hour\" | \"minute\" | \"second\";\n\n/**\n * A parsed ASTM date/time. Immutable plain data; the populated fields extend\n * exactly as far as {@link AstmDate.precision}. Absent components are left\n * `undefined` rather than defaulted, so a consumer can tell \"midnight\" from\n * \"no time given\". No timezone is modeled: the value is instrument-local.\n *\n * @example\n * ```ts\n * import { parseAstmDate } from \"@cosyte/astm\";\n * const d = parseAstmDate(\"20240315\");\n * d?.precision; // \"day\"\n * d?.hour; // undefined (not 0)\n * ```\n */\nexport interface AstmDate {\n /** The raw digit string as it appeared on the wire. */\n readonly raw: string;\n readonly year: number;\n readonly month?: number;\n readonly day?: number;\n readonly hour?: number;\n readonly minute?: number;\n readonly second?: number;\n /** How far the components are populated. */\n readonly precision: AstmDatePrecision;\n /**\n * `true` when the digit run does **not** align to a whole-component boundary: an odd number of\n * digits that cuts a two-digit component (month/day/hour/minute/second) in half (lengths 5, 7, 9,\n * 11, 13). The full run is preserved in {@link AstmDate.raw} and the structured value is truncated\n * to the last **complete** component: the dangling digit is **never zero-filled into a fabricated\n * time**. Absent (never `false`) for a clean value. A caller surfaces this as a value-free\n * `ASTM_RECORD_PARTIAL_TIMESTAMP` warning.\n */\n readonly truncated?: true;\n}\n\n/**\n * Parse an ASTM `YYYYMMDDHHMMSS` value, preserving whatever precision is\n * present. Returns `undefined` for a value that is not a usable timestamp (fewer\n * than four leading digits for the year, or non-digit content): a caller keeps\n * the raw field text either way, so nothing is lost. A partial value is parsed,\n * never rejected.\n *\n * Extra trailing digits beyond seconds (fractional seconds some vendors append)\n * are ignored for the structured value; the full input remains in {@link AstmDate.raw}.\n *\n * @param raw - The raw field text.\n * @returns The parsed date, or `undefined` when it is not a timestamp.\n * @example\n * ```ts\n * import { parseAstmDate } from \"@cosyte/astm\";\n * parseAstmDate(\"20240315093000\")?.precision; // \"second\"\n * parseAstmDate(\"202403\")?.precision; // \"month\"\n * ```\n */\nexport function parseAstmDate(raw: string): AstmDate | undefined {\n const s = raw.trim();\n if (!/^\\d{4,}$/u.test(s)) return undefined;\n\n const year = Number(s.slice(0, 4));\n // An odd length below the full 14 digits cuts a two-digit component in half: the value is\n // truncated mid-component. The raw run is preserved verbatim; the dangling digit is dropped from\n // the structured value rather than zero-filled into a fabricated time, and the flag lets a caller\n // warn. Extra digits beyond 14 (fractional seconds some vendors append) are not \"truncated\".\n const truncated = s.length < 14 && s.length % 2 === 1;\n const base = { raw: s, year, ...(truncated ? { truncated: true as const } : {}) };\n\n if (s.length < 6) return { ...base, precision: \"year\" };\n const month = Number(s.slice(4, 6));\n if (s.length < 8) return { ...base, month, precision: \"month\" };\n const day = Number(s.slice(6, 8));\n if (s.length < 10) return { ...base, month, day, precision: \"day\" };\n const hour = Number(s.slice(8, 10));\n if (s.length < 12) return { ...base, month, day, hour, precision: \"hour\" };\n const minute = Number(s.slice(10, 12));\n if (s.length < 14) return { ...base, month, day, hour, minute, precision: \"minute\" };\n const second = Number(s.slice(12, 14));\n return { ...base, month, day, hour, minute, second, precision: \"second\" };\n}\n\n/**\n * Render an {@link AstmDate} as an ISO-8601-*like* string truncated to its\n * precision, with **no** `Z` and **no** offset: because ASTM carries no\n * timezone and appending one would fabricate information. A consumer that knows\n * the instrument's zone can attach it; this function never assumes UTC.\n *\n * @param d - The date to render.\n * @returns e.g. `\"2024-03-15T09:30\"` (minute precision) or `\"2024-03\"` (month).\n * @example\n * ```ts\n * import { astmDateToLocalISO, parseAstmDate } from \"@cosyte/astm\";\n * astmDateToLocalISO(parseAstmDate(\"20240315\")!); // \"2024-03-15\"\n * ```\n */\nexport function astmDateToLocalISO(d: AstmDate): string {\n const p2 = (n: number): string => String(n).padStart(2, \"0\");\n let out = String(d.year).padStart(4, \"0\");\n if (d.month !== undefined) out += `-${p2(d.month)}`;\n if (d.day !== undefined) out += `-${p2(d.day)}`;\n if (d.hour !== undefined) out += `T${p2(d.hour)}`;\n if (d.minute !== undefined) out += `:${p2(d.minute)}`;\n if (d.second !== undefined) out += `:${p2(d.second)}`;\n return out;\n}\n","/**\n * Code-system **provenance recognition** for the ASTM Universal Test ID.\n *\n * ASTM carries a test identifier as a caret-component field (`^^^687`): the\n * spec's component order is LOINC-slot / test-name / coding-scheme / local-code.\n * In the field the way real analyzers emit it:\n *\n * - **component 1** is a LOINC slot: *almost always empty*; a few vendors place\n * an inline LOINC when one exists;\n * - **component 4** is the vendor/local code: the identifier that is actually\n * present, and therefore the **primary** identifier here.\n *\n * Following the sibling parsers' conservative posture, this module is a\n * **structural recognizer, not a dictionary**: it tags where a code came from\n * (its provenance) and surfaces every code verbatim. It does not look codes up,\n * does not validate them, and bundles no LOINC/SNOMED tables.\n */\n\n/** Where a Universal Test ID's usable identifier came from. */\nexport type UniversalTestIdProvenance =\n /** Component 1 (the LOINC slot) is populated: a candidate LOINC, recognized not validated. */\n | \"inline-loinc-candidate\"\n /** Component 4 (the vendor/local code) carries the identifier. */\n | \"local-code\"\n /** Only the test name (component 2) is present: no code. */\n | \"name-only\"\n /** Nothing usable in the field. */\n | \"empty\";\n\n/**\n * A recognized ASTM Universal Test ID. All components are surfaced verbatim\n * (already escape-decoded by the tokenizer); nothing is looked up.\n *\n * @example\n * ```ts\n * import { recognizeUniversalTestId } from \"@cosyte/astm\";\n * const u = recognizeUniversalTestId([\"\", \"\", \"\", \"687\"]);\n * u.localCode; // \"687\"\n * u.provenance; // \"local-code\"\n * ```\n */\nexport interface UniversalTestId {\n /** The field's components, verbatim and in order. */\n readonly components: readonly string[];\n /** Component 1 when populated: a *candidate* LOINC (provenance only, never validated). */\n readonly loincCandidate?: string;\n /** Component 2: the test / battery name, when present. */\n readonly testName?: string;\n /** Component 3: the coding-scheme selector, when present. */\n readonly codingScheme?: string;\n /** Component 4, the vendor/local code: the primary identifier when no inline LOINC is given. */\n readonly localCode?: string;\n /** Where the primary identifier came from. */\n readonly provenance: UniversalTestIdProvenance;\n}\n\n/**\n * Recognize a Universal Test ID from a field's already-decoded components.\n *\n * @param components - The component strings (escape-decoded), in order.\n * @returns The recognized, provenance-tagged Universal Test ID.\n * @example\n * ```ts\n * import { recognizeUniversalTestId, primaryCode } from \"@cosyte/astm\";\n * primaryCode(recognizeUniversalTestId([\"\", \"Glucose\", \"L\", \"687\"])); // \"687\"\n * ```\n */\nexport function recognizeUniversalTestId(components: readonly string[]): UniversalTestId {\n const nonEmpty = (i: number): string | undefined => {\n const c = components[i];\n return c !== undefined && c.length > 0 ? c : undefined;\n };\n const loincCandidate = nonEmpty(0);\n const testName = nonEmpty(1);\n const codingScheme = nonEmpty(2);\n const localCode = nonEmpty(3);\n\n let provenance: UniversalTestIdProvenance;\n if (loincCandidate !== undefined) provenance = \"inline-loinc-candidate\";\n else if (localCode !== undefined) provenance = \"local-code\";\n else if (testName !== undefined) provenance = \"name-only\";\n else provenance = \"empty\";\n\n const base = { components, provenance };\n return {\n ...base,\n ...(loincCandidate !== undefined ? { loincCandidate } : {}),\n ...(testName !== undefined ? { testName } : {}),\n ...(codingScheme !== undefined ? { codingScheme } : {}),\n ...(localCode !== undefined ? { localCode } : {}),\n };\n}\n\n/**\n * The primary code to key a result on: the inline LOINC candidate when a vendor\n * supplied one, otherwise the local (vendor) code. Returns `undefined` when the\n * field carries no code at all (name-only or empty): never a guess.\n *\n * @param u - A recognized Universal Test ID.\n * @returns The primary code, or `undefined`.\n * @example\n * ```ts\n * import { primaryCode, recognizeUniversalTestId } from \"@cosyte/astm\";\n * primaryCode(recognizeUniversalTestId([\"2345-7\", \"Glucose\", \"LN\", \"687\"])); // \"2345-7\"\n * ```\n */\nexport function primaryCode(u: UniversalTestId): string | undefined {\n return u.loincCandidate ?? u.localCode;\n}\n","/**\n * Tier-2 warning registry and factories for the `@cosyte/astm` record parser.\n *\n * A warning is the lenient parser's record of a tolerated deviation: it never\n * throws, never drops data, and never fabricates a value. Consumers compare\n * `warning.code === WARNING_CODES.<CODE>` to react; renaming a code is a\n * **breaking change**. Every warning carries a stable code plus a\n * {@link AstmPosition} and **never** a field value (PHI discipline).\n */\n\nimport type { AstmPosition } from \"./position.js\";\n\n/**\n * Stable string codes for every Tier-2 warning the record parser may emit.\n * `key === value` so `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, WARNING_CODES } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rL|1\\r\");\n * msg.warnings.some((w) => w.code === WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE);\n * ```\n */\nexport const WARNING_CODES = {\n /**\n * A record's type letter is not one of the modeled types: surfaced as an unsupported record,\n * never dropped. Treat it as a possible lost message boundary, because a header the reader did\n * not recognize as one does not open a new message.\n */\n ASTM_RECORD_UNKNOWN_TYPE: \"ASTM_RECORD_UNKNOWN_TYPE\",\n /**\n * The delimiters in force found **no field separator at all** in a record that carries content\n * beyond its type letter, so the whole record read back as a single field and none of its modeled\n * fields could be recovered. The raw line is surfaced intact and nothing is dropped, but on a\n * result record it means the value, the units and the status are all absent from the parsed model.\n *\n * This is one signature of a record being read with a delimiter set that does not belong to it,\n * which is how a delimiter-scoping mistake turns into lost values. It is reported rather than\n * repaired, because recovering the fields would mean guessing which set the sender meant.\n *\n * **Its absence is not evidence that a record was read in its own set.** This tests one of the\n * four delimiter roles, the field separator, and only in its total form, where that separator\n * occurs in the line (unescaped). Two whole classes of the same loss are outside it: a foreign set\n * whose field separator happens to occur somewhere in the line still splits (on the wrong\n * boundaries, silently, and this can happen to one record inside a run of these warnings); and a\n * set differing in the repeat, component or escape role usually splits into fields normally,\n * where a mis-split component can cost a test identity while the value survives, and where an\n * `&X&` sequence whose body is a delimiter is an opaque atom, so that delimiter does not split\n * and every field after it shifts. The escape role's worst case has narrowed and not\n * disappeared: an escape character heading no sequence is now read as a literal and reported\n * under {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER} rather than merging the rest of the\n * record, but a delimiter swallowed inside an `&X&` body is reported only by\n * {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, which is tolerable. Treat this code as a\n * report that one record definitely lost its fields, never as a sweep that would have fired if\n * any had.\n */\n ASTM_RECORD_FIELDS_UNSEPARATED: \"ASTM_RECORD_FIELDS_UNSEPARATED\",\n /** The header declared delimiters other than the canonical `H|\\^&`: tolerated, noted. */\n ASTM_NONSTANDARD_DELIMITERS: \"ASTM_NONSTANDARD_DELIMITERS\",\n /** An escape sequence body was not one of `&F&`/`&S&`/`&R&`/`&E&`: preserved verbatim. */\n ASTM_UNKNOWN_ESCAPE_SEQUENCE: \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\",\n /**\n * An escape character appeared where no escape sequence starts (an escape sequence is the escape\n * character, one body character, and the escape character again). It is read as the **literal\n * character it is** and kept byte-for-byte in the decoded value. Nothing is dropped and no byte is\n * invented: this flags that the sender did not write the character the spec-clean way, which\n * is `&E&`.\n *\n * **What it replaced is the reason it exists.** The codec used to read such a character as the\n * opening of a sequence that never closed and merge the whole remainder of the record into the\n * field it sat in, reporting nothing at all. One `&` in a result value cost the units, the\n * abnormal flag and the status together and left the status reading `unspecified` rather than\n * `final`; one in a surname cost the patient's birth date and sex. Emit then re-escaped the merged\n * text into a spec-clean-looking line that read back as the same wrong value, so the mis-read\n * survived a round trip without ever surfacing.\n *\n * **This code is not a statement about the rest of the record.** It reports one character. A\n * *different* escape character in the same record may still head a real three-character sequence,\n * and if that sequence's body happens to be a delimiter (`&|&` under the canonical set) the atom\n * rule means that delimiter does not split. That case is reported separately, under\n * {@link WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE}, and it can still cost a field boundary.\n */\n ASTM_UNPAIRED_ESCAPE_CHARACTER: \"ASTM_UNPAIRED_ESCAPE_CHARACTER\",\n /**\n * A result value field carried an *unescaped* component delimiter, so it split into more than one\n * component. Both the full raw value and the split are surfaced and this warning fires: the\n * ambiguity is never resolved silently into a truncated value.\n */\n ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT: \"ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT\",\n /**\n * A result's abnormal-flag field (`R` field 7) carried a letter outside HL7 Table 0078. The flag is\n * surfaced as `undefined`: never dropped, and **never coerced to `normal`** (a clinical error).\n */\n ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG: \"ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG\",\n /**\n * A result's status field (`R` field 9) carried a letter that is not a recognized status. It is\n * surfaced as `undefined` and, like every non-`F` status, never reads as active-final.\n */\n ASTM_RECORD_UNDEFINED_RESULT_STATUS: \"ASTM_RECORD_UNDEFINED_RESULT_STATUS\",\n /**\n * A result's reference-range field (`R` field 6) did not match a recognized form (`low-high`,\n * `<high`, `>low`). The text is surfaced verbatim as `unparsed`: a bound is **never fabricated**.\n */\n ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE: \"ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE\",\n /**\n * A result carried a numeric value but no units (`R` field 5 empty). Units are vendor free text\n * (not UCUM); a missing unit is flagged here and **never defaulted, guessed, or converted**.\n */\n ASTM_RECORD_UNITS_ABSENT: \"ASTM_RECORD_UNITS_ABSENT\",\n /**\n * A `C` (comment) record had no valid preceding `H`/`P`/`O`/`R` parent: an **orphan**. The comment\n * is attached to the message root (`attachedToRoot: true`) and surfaced, **never dropped**.\n */\n ASTM_RECORD_ORPHAN_COMMENT: \"ASTM_RECORD_ORPHAN_COMMENT\",\n /**\n * A `YYYYMMDDHHMMSS` timestamp had an odd digit run that truncates a two-digit component in half\n * (e.g. a partial day/hour). The raw run is preserved and the structured value stops at the last\n * **complete** component: the dangling digit is **never zero-filled into a fabricated time**.\n */\n ASTM_RECORD_PARTIAL_TIMESTAMP: \"ASTM_RECORD_PARTIAL_TIMESTAMP\",\n /**\n * A `Q` (request-information) record carried a request-information status code (field 13). The code\n * *set* is `[OSS-derived / paywalled]` with no publicly-groundable enumeration, so the parser\n * interprets **none** of them: the status is surfaced verbatim and this value-free warning flags that\n * it was passed through **uninterpreted**, never mapped to a guessed meaning.\n */\n ASTM_RECORD_UNINTERPRETED_QUERY_STATUS: \"ASTM_RECORD_UNINTERPRETED_QUERY_STATUS\",\n /**\n * A message carried **both** a `Q` (request) and an `R` (result) record: a contradictory shape. The\n * message is classified `host-query` (the `Q` **dominates**, so it is never read as a result set) and\n * this warning flags the anomaly. Positional context only; no field value.\n */\n ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND: \"ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND\",\n /**\n * A later `H` (header) record declared a **different** delimiter set from the one in force, and the\n * parser **followed it**: that header and every record after it are read with the newly-declared\n * delimiters, until the next `H`. Records already read keep the set that was in force when they were\n * read: a redeclaration never reinterprets bytes that have already been consumed.\n *\n * A stream carrying several messages back to back is ordinary, and a header that simply **repeats**\n * the delimiters already in force is a no-op that warns nothing. This code fires only when the set\n * actually changes, because that is the point at which a reader still using the old set would begin\n * merging fields together.\n */\n ASTM_RECORD_DELIMITERS_REDECLARED: \"ASTM_RECORD_DELIMITERS_REDECLARED\",\n /**\n * A later `H` (header) record could not declare a usable delimiter set: it was too short, or the\n * field separator it named also appeared among the other three, leaving the four roles\n * indistinguishable. The delimiters **already in force are kept** and every record is still surfaced;\n * a set is never guessed and no record is dropped.\n *\n * The same condition on the *first* header is unrecoverable and remains the\n * `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal: there is no earlier set to fall back to.\n */\n ASTM_RECORD_UNREADABLE_REDECLARATION: \"ASTM_RECORD_UNREADABLE_REDECLARATION\",\n /**\n * The downgraded form an active vendor {@link AstmProfile} produces from a deviation it *expects*\n * (see `src/profiles/`). The original warning is **never dropped**: its code moves to\n * {@link AstmRecordWarning.toleratedCode}, the warning is re-badged `PROFILE_QUIRK_APPLIED` with\n * `expected: true` and the tolerating profile named, so a consumer can filter known, grounded noise\n * while the fact of the deviation, and where it was, survives. A profile can only ever reach this\n * path for a **non-safety-critical** code (enforced at profile-definition time); a safety-critical\n * deviation (a result value, flag, status, patient identifier, code system, or a frame-integrity\n * warning) can **never** be tolerated, so it can never be re-badged here.\n */\n PROFILE_QUIRK_APPLIED: \"PROFILE_QUIRK_APPLIED\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmRecordWarning.code}. Narrowing by this code\n * lets consumers write exhaustive `switch` blocks and guarantees a typo-free\n * comparison against {@link WARNING_CODES}.\n */\nexport type WarningCode = (typeof WARNING_CODES)[keyof typeof WARNING_CODES];\n\n/**\n * A single Tier-2 warning: a stable code, a value-free human-readable message,\n * and positional context. Plain data, accumulated onto `AstmMessage.warnings`.\n *\n * @example\n * ```ts\n * import type { AstmRecordWarning } from \"@cosyte/astm\";\n * const w: AstmRecordWarning = {\n * code: \"ASTM_RECORD_UNKNOWN_TYPE\",\n * message: \"Unknown record type.\",\n * position: { recordIndex: 2, recordType: \"Z\" },\n * };\n * ```\n */\nexport interface AstmRecordWarning {\n readonly code: WarningCode;\n /** Human-readable detail for logs. Never contains a field value. */\n readonly message: string;\n readonly position: AstmPosition;\n /**\n * `true` when an active vendor {@link AstmProfile} *expected* this deviation and re-badged it as a\n * {@link WARNING_CODES.PROFILE_QUIRK_APPLIED}. An `expected` warning does **not** escalate to a\n * thrown `AstmStrictError` in strict mode (the whole point of the profile is that this deviation is\n * known and benign): it is still recorded, so nothing is hidden. Absent on an untolerated warning.\n */\n readonly expected?: boolean;\n /** The name of the {@link AstmProfile} that tolerated this warning, when `expected`. */\n readonly profile?: string;\n /**\n * When `code` is {@link WARNING_CODES.PROFILE_QUIRK_APPLIED}, the original warning code the profile\n * tolerated, so a consumer can still see *which* deviation was re-badged as expected.\n */\n readonly toleratedCode?: WarningCode;\n}\n\n/**\n * Build an `ASTM_RECORD_UNKNOWN_TYPE` warning. The record is still surfaced (as\n * an unsupported record), never dropped.\n *\n * **This one is not cosmetic.** Message grouping decides where a message starts by\n * reading each record's type letter, so an unrecognized letter may be a header the\n * reader failed to see, and two messages then read as one. A profile is therefore\n * not permitted to tolerate this code.\n *\n * @example\n * ```ts\n * import { unknownRecordType } from \"@cosyte/astm\";\n * unknownRecordType({ recordIndex: 3, recordType: \"Z\" });\n * ```\n */\nexport function unknownRecordType(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNKNOWN_TYPE,\n message:\n \"Unrecognized record type, surfaced verbatim as an unsupported record. \" +\n \"If this record was a header, message grouping did not open a new message here.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_FIELDS_UNSEPARATED` warning. The raw line is surfaced\n * intact; what is lost is every **modeled** field of the record, because the\n * delimiters in force never split it.\n *\n * **This one is not cosmetic either.** A record with content but no field\n * separator is a record being read with a delimiter set that does not belong to\n * it, and on a result record that costs the value, the units and the status in\n * one go. The fields are not reconstructed, because doing so would mean guessing\n * which set the sender meant, so a profile is not permitted to tolerate this code.\n *\n * **It is a report, not a sweep.** See {@link WARNING_CODES} for the two classes\n * of the same loss it does not see: its absence never certifies a record split\n * correctly.\n *\n * @example\n * ```ts\n * import { fieldsUnseparated } from \"@cosyte/astm\";\n * fieldsUnseparated({ recordIndex: 4, recordType: \"R\" });\n * ```\n */\nexport function fieldsUnseparated(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_FIELDS_UNSEPARATED,\n message:\n \"The delimiters in force found no field separator in this record, so its whole content read \" +\n \"back as one field and none of its modeled fields could be recovered. The raw line is \" +\n \"surfaced intact, and no field is reconstructed, because the set the sender used is unknown.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_NONSTANDARD_DELIMITERS` warning. The declared delimiters are\n * used as-is; this only flags that they differ from the canonical set.\n *\n * @example\n * ```ts\n * import { nonStandardDelimiters } from \"@cosyte/astm\";\n * nonStandardDelimiters({ recordIndex: 0, recordType: \"H\" });\n * ```\n */\nexport function nonStandardDelimiters(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS,\n message: \"Header declared non-canonical delimiters, read from the header and honored.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_DELIMITERS_REDECLARED` warning. Emitted when a later `H`\n * record declares a delimiter set different from the one in force; the new set is\n * honored from that header onward. Positional context only: never the delimiters\n * themselves.\n *\n * @example\n * ```ts\n * import { delimitersRedeclared } from \"@cosyte/astm\";\n * delimitersRedeclared({ recordIndex: 5, recordType: \"H\" });\n * ```\n */\nexport function delimitersRedeclared(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_DELIMITERS_REDECLARED,\n message:\n \"A later header declared different delimiters, honored from that header onward; earlier records keep the set they were read with.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNREADABLE_REDECLARATION` warning. Emitted when a later\n * `H` record cannot declare a usable delimiter set; the set already in force is\n * kept and every record is still surfaced.\n *\n * @example\n * ```ts\n * import { unreadableRedeclaration } from \"@cosyte/astm\";\n * unreadableRedeclaration({ recordIndex: 2, recordType: \"H\" });\n * ```\n */\nexport function unreadableRedeclaration(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNREADABLE_REDECLARATION,\n message:\n \"A later header could not declare a usable delimiter set, the delimiters already in force were kept.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_UNKNOWN_ESCAPE_SEQUENCE` warning. The sequence is preserved\n * verbatim in the decoded value; the warning body carries neither the sequence\n * nor its surrounding text.\n *\n * @example\n * ```ts\n * import { unknownEscapeSequence } from \"@cosyte/astm\";\n * unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 4 });\n * ```\n */\nexport function unknownEscapeSequence(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE,\n message: \"Unrecognized escape sequence preserved verbatim.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_UNPAIRED_ESCAPE_CHARACTER` warning. The character is preserved\n * verbatim as a literal in the decoded value and opens no atom, so it does not\n * merge the rest of the record; the warning body carries neither the character's\n * surroundings nor any field value.\n *\n * **It is a statement about that one character, not about the record.** A\n * different escape character in the same record may head a real three-character\n * sequence, and if that sequence's body is a delimiter, that delimiter does not\n * split. See {@link WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER}.\n *\n * A profile **may** tolerate this code: the value it reports is byte-identical\n * with the warning and without it, because reading the character as a literal is\n * the parse, not a consequence of the warning.\n *\n * @example\n * ```ts\n * import { unpairedEscapeCharacter } from \"@cosyte/astm\";\n * unpairedEscapeCharacter({ recordIndex: 4, recordType: \"R\", fieldIndex: 4 });\n * ```\n */\nexport function unpairedEscapeCharacter(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER,\n message:\n \"An escape character headed no escape sequence, read as the literal character it is \" +\n \"rather than opening a sequence that never closes. The spec-clean form is the escaped one.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT` warning. Emitted when a result\n * value field split on an unescaped component delimiter: the full raw value and\n * the split are both surfaced, never a silent truncation.\n *\n * @example\n * ```ts\n * import { ambiguousValueSplit } from \"@cosyte/astm\";\n * ambiguousValueSplit({ recordIndex: 3, recordType: \"R\", fieldIndex: 4 });\n * ```\n */\nexport function ambiguousValueSplit(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT,\n message:\n \"Result value contained an unescaped component delimiter, full raw value and split both surfaced.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG` warning. The flag is surfaced as\n * `undefined` (never coerced to `normal`); the warning carries only the position.\n *\n * @example\n * ```ts\n * import { undefinedAbnormalFlag } from \"@cosyte/astm\";\n * undefinedAbnormalFlag({ recordIndex: 4, recordType: \"R\", fieldIndex: 7 });\n * ```\n */\nexport function undefinedAbnormalFlag(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG,\n message: \"Abnormal flag is not in HL7 Table 0078, surfaced as undefined, never as normal.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNDEFINED_RESULT_STATUS` warning. The status is surfaced\n * as `undefined` and, like every non-final status, never reads as active-final.\n *\n * @example\n * ```ts\n * import { undefinedResultStatus } from \"@cosyte/astm\";\n * undefinedResultStatus({ recordIndex: 4, recordType: \"R\", fieldIndex: 9 });\n * ```\n */\nexport function undefinedResultStatus(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNDEFINED_RESULT_STATUS,\n message: \"Result status is not a recognized status letter, surfaced as undefined.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE` warning. The range text is\n * surfaced verbatim as `unparsed`; no bound is fabricated.\n *\n * @example\n * ```ts\n * import { unparseableReferenceRange } from \"@cosyte/astm\";\n * unparseableReferenceRange({ recordIndex: 4, recordType: \"R\", fieldIndex: 6 });\n * ```\n */\nexport function unparseableReferenceRange(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE,\n message:\n \"Reference range did not match a recognized form, surfaced verbatim, no bound invented.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNITS_ABSENT` warning. Emitted when a result carries a\n * numeric value but no units; units are never defaulted, guessed, or converted.\n *\n * @example\n * ```ts\n * import { unitsAbsent } from \"@cosyte/astm\";\n * unitsAbsent({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * ```\n */\nexport function unitsAbsent(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNITS_ABSENT,\n message: \"Numeric result value carried no units, never defaulted, guessed, or converted.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_ORPHAN_COMMENT` warning. Emitted when a `C` record had no\n * valid preceding `H`/`P`/`O`/`R` parent; the comment is attached to the message\n * root and surfaced, never dropped.\n *\n * @example\n * ```ts\n * import { orphanComment } from \"@cosyte/astm\";\n * orphanComment({ recordIndex: 5, recordType: \"C\" });\n * ```\n */\nexport function orphanComment(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_ORPHAN_COMMENT,\n message: \"Comment had no valid preceding parent, attached to the message root, never dropped.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_PARTIAL_TIMESTAMP` warning. Emitted when a\n * `YYYYMMDDHHMMSS` value had an odd digit run that truncates a component; the raw\n * run is preserved and no time is fabricated.\n *\n * @example\n * ```ts\n * import { partialTimestamp } from \"@cosyte/astm\";\n * partialTimestamp({ recordIndex: 2, recordType: \"P\", fieldIndex: 8 });\n * ```\n */\nexport function partialTimestamp(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_PARTIAL_TIMESTAMP,\n message: \"Timestamp digit run truncates a component, preserved verbatim, never zero-filled.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS` warning. Emitted when a `Q`\n * record carries a request-information status code; the code set is paywalled, so\n * the status is surfaced verbatim and never mapped to a guessed meaning.\n *\n * @example\n * ```ts\n * import { uninterpretedQueryStatus } from \"@cosyte/astm\";\n * uninterpretedQueryStatus({ recordIndex: 2, recordType: \"Q\", fieldIndex: 13 });\n * ```\n */\nexport function uninterpretedQueryStatus(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_UNINTERPRETED_QUERY_STATUS,\n message:\n \"Query request-information status surfaced verbatim, code set paywalled, never interpreted.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND` warning. Emitted when a message\n * carries both a `Q` (request) and an `R` (result) record; the message is\n * classified as a host-query request (the `Q` dominates) and the anomaly is\n * flagged.\n *\n * @example\n * ```ts\n * import { ambiguousMessageKind } from \"@cosyte/astm\";\n * ambiguousMessageKind({ recordIndex: 0, recordType: \"H\" });\n * ```\n */\nexport function ambiguousMessageKind(position: AstmPosition): AstmRecordWarning {\n return {\n code: WARNING_CODES.ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND,\n message:\n \"Message carried both a Q (request) and an R (result) record, classified host-query; Q dominates.\",\n position,\n };\n}\n\n/**\n * Build a `PROFILE_QUIRK_APPLIED` warning: the downgraded form an active vendor profile produces from\n * a deviation it *expects*. The original warning is **not dropped**: its `code` moves to\n * `toleratedCode`, the warning is re-badged `PROFILE_QUIRK_APPLIED`, `expected` is set, and the\n * tolerating profile is named. The original `position` and `message` are preserved (both PHI-free by\n * the same construction as every other factory), so a consumer can filter known, grounded noise while\n * the fact of the deviation, and where it was, survive. A profile can only ever reach this path for a\n * **non-safety-critical** code (enforced at profile-definition time by the safety gate).\n *\n * @param original - The warning the profile tolerated.\n * @param profileName - The name of the tolerating profile.\n * @returns The re-badged, still-informative warning.\n * @example\n * ```ts\n * import { profileQuirkApplied, unknownEscapeSequence } from \"@cosyte/astm\";\n * const original = unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * const w = profileQuirkApplied(original, \"referenceCorpus\");\n * w.code; // \"PROFILE_QUIRK_APPLIED\"\n * w.toleratedCode; // \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"\n * ```\n */\nexport function profileQuirkApplied(\n original: AstmRecordWarning,\n profileName: string,\n): AstmRecordWarning {\n return {\n code: WARNING_CODES.PROFILE_QUIRK_APPLIED,\n message: `Profile \"${profileName}\" expected ${original.code}: ${original.message}`,\n position: original.position,\n expected: true,\n profile: profileName,\n toleratedCode: original.code,\n };\n}\n","/**\n * Safety-critical result semantics for the ASTM `R` (result) record.\n *\n * The parser surfaces the `R` record's flag, status, units, and reference-range\n * fields **raw**. This module turns those raw letters into modeled, **fail-safe**\n * semantics, under one rule: **never a confident wrong value.**\n *\n * - An **abnormal flag** (field 7) is recognized against HL7 Table 0078; an\n * unrecognized flag is surfaced as `undefined` (with a warning) and is **never\n * coerced to `normal`**.\n * - A **result status** (field 9) is modeled so a **correction (`C`)** or a\n * **cancellation (`X`)** can never read as an active-final result, and an\n * **absent** status is a typed `unspecified`: **never assumed `F` (final)**.\n * - A **reference range** (field 6) is parsed into a low/high (or open-ended)\n * pair; an unparseable range is surfaced **verbatim**, a bound is **never\n * fabricated**.\n *\n * Every bound and code is surfaced verbatim; nothing is collapsed, reconciled,\n * or converted. Units are handled at the record layer (a numeric value without\n * units warns; units are never defaulted, guessed, or converted, see\n * `./parse.ts`).\n */\n\n/* ────────────────────────────── abnormal flags ────────────────────────────── */\n\n/**\n * The recognized abnormal-flag letters, from **HL7 v2.2 Table 0078** (the flag\n * *value* set, a published fact set, not CLSI prose). Read in the context of an\n * `R` record's field 7: here `S`/`R`/`I` are the microbiology susceptibility\n * codes and `R` is **not** the repeat delimiter.\n */\nexport type AbnormalFlagCode =\n | \"L\"\n | \"H\"\n | \"LL\"\n | \"HH\"\n | \"<\"\n | \">\"\n | \"N\"\n | \"A\"\n | \"AA\"\n | \"U\"\n | \"D\"\n | \"B\"\n | \"W\"\n | \"S\"\n | \"R\"\n | \"I\";\n\n/**\n * The modeled meaning of a recognized {@link AbnormalFlagCode}, plus the two\n * fail-safe sentinels: `undefined` (a flag was present but is not in Table 0078),\n * which is **never** collapsed to `normal`.\n */\nexport type AbnormalFlagMeaning =\n | \"below-normal\"\n | \"above-normal\"\n | \"critically-below-normal\"\n | \"critically-above-normal\"\n | \"below-scale\"\n | \"above-scale\"\n | \"normal\"\n | \"abnormal\"\n | \"very-abnormal\"\n | \"significant-change-up\"\n | \"significant-change-down\"\n | \"better\"\n | \"worse\"\n | \"susceptible\"\n | \"resistant\"\n | \"intermediate\"\n | \"undefined\";\n\n/**\n * HL7 Table 0078 flag letter → modeled meaning. `U`/`D` are **directional**\n * significant-change flags (up / down), *not* units or a delta magnitude, a\n * distinction that misreads a trend if collapsed. `LL`/`HH` are panic (critical)\n * low/high; `<`/`>` are off-scale low/high; `AA` is very-abnormal.\n */\nconst ABNORMAL_FLAG_MEANINGS: Readonly<Record<AbnormalFlagCode, AbnormalFlagMeaning>> = {\n L: \"below-normal\",\n H: \"above-normal\",\n LL: \"critically-below-normal\",\n HH: \"critically-above-normal\",\n \"<\": \"below-scale\",\n \">\": \"above-scale\",\n N: \"normal\",\n A: \"abnormal\",\n AA: \"very-abnormal\",\n U: \"significant-change-up\",\n D: \"significant-change-down\",\n B: \"better\",\n W: \"worse\",\n S: \"susceptible\",\n R: \"resistant\",\n I: \"intermediate\",\n};\n\n/**\n * A recognized (or explicitly *un*recognized) abnormal flag. The raw field text\n * is always preserved; `recognized` is `false` and `meaning` is `\"undefined\"`\n * for any letter outside Table 0078, the flag is surfaced, never dropped, and\n * **never coerced to `normal`**.\n *\n * @example\n * ```ts\n * import { interpretAbnormalFlag } from \"@cosyte/astm\";\n * const f = interpretAbnormalFlag(\"HH\");\n * f.meaning; // \"critically-above-normal\"\n * f.recognized; // true\n * ```\n */\nexport interface AbnormalFlag {\n /** The verbatim field text, exactly as received. */\n readonly raw: string;\n /** The Table 0078 code, present only when the raw text is a recognized flag. */\n readonly code?: AbnormalFlagCode;\n /** The modeled meaning; `\"undefined\"` (never `\"normal\"`) for an unrecognized flag. */\n readonly meaning: AbnormalFlagMeaning;\n /** Whether the raw text matched a Table 0078 flag. */\n readonly recognized: boolean;\n}\n\n/**\n * Interpret an `R`-record abnormal-flag field (field 7) against HL7 Table 0078.\n *\n * Leading/trailing whitespace is ignored for the lookup but the `raw` text is\n * preserved verbatim. An unrecognized flag yields `{ recognized: false, meaning:\n * \"undefined\" }`, surfaced, never dropped, and **never `\"normal\"`**.\n *\n * @param raw - The verbatim field-7 text.\n * @returns The interpreted flag.\n * @example\n * ```ts\n * import { interpretAbnormalFlag } from \"@cosyte/astm\";\n * interpretAbnormalFlag(\"U\").meaning; // \"significant-change-up\"\n * interpretAbnormalFlag(\"ZZ\").meaning; // \"undefined\" (never \"normal\")\n * ```\n */\nexport function interpretAbnormalFlag(raw: string): AbnormalFlag {\n const key = raw.trim();\n if (isAbnormalFlagCode(key)) {\n return { raw, code: key, meaning: ABNORMAL_FLAG_MEANINGS[key], recognized: true };\n }\n return { raw, meaning: \"undefined\", recognized: false };\n}\n\nfunction isAbnormalFlagCode(value: string): value is AbnormalFlagCode {\n return Object.prototype.hasOwnProperty.call(ABNORMAL_FLAG_MEANINGS, value);\n}\n\n/* ────────────────────────────── result status ────────────────────────────── */\n\n/**\n * The recognized result-status letters (`R`-record field 9). The clinically\n * load-bearing members are **`C` (correction, supersedes a previously\n * transmitted value)** and **`X` (cannot be done / cancelled)**.\n */\nexport type ResultStatusCode = \"F\" | \"C\" | \"P\" | \"R\" | \"S\" | \"I\" | \"X\";\n\n/**\n * The modeled meaning of a result status, plus two fail-safe sentinels:\n * `unspecified` (the field was absent, **never assumed `final`**) and\n * `undefined` (a letter was present but is not a recognized status).\n */\nexport type ResultStatusMeaning =\n | \"final\"\n | \"correction\"\n | \"preliminary\"\n | \"previously-transmitted\"\n | \"partial\"\n | \"pending\"\n | \"cancelled\"\n | \"unspecified\"\n | \"undefined\";\n\nconst RESULT_STATUS_MEANINGS: Readonly<Record<ResultStatusCode, ResultStatusMeaning>> = {\n F: \"final\",\n C: \"correction\",\n P: \"preliminary\",\n R: \"previously-transmitted\",\n S: \"partial\",\n I: \"pending\",\n X: \"cancelled\",\n};\n\n/**\n * A modeled result status. The three booleans are the safety surface a consumer\n * reads instead of string-matching the code:\n *\n * - **`isActiveFinal`** is `true` **only** for a plain `F` (final). It is `false`\n * for a correction (`C`), a cancellation (`X`), a preliminary/partial/pending\n * result, an absent status, and an unrecognized one, so a superseded or\n * cancelled result can **never** read as current/final.\n * - **`supersedes`** is `true` for `C`: this value replaces a previously\n * transmitted one.\n * - **`cancelled`** is `true` for `X`: the result cannot be done / was cancelled.\n *\n * @example\n * ```ts\n * import { interpretResultStatus } from \"@cosyte/astm\";\n * interpretResultStatus(\"C\").isActiveFinal; // false (a correction is not active-final)\n * interpretResultStatus(\"X\").cancelled; // true\n * interpretResultStatus(undefined).meaning; // \"unspecified\" (never \"final\")\n * ```\n */\nexport interface ResultStatus {\n /** The verbatim field text, present only when field 9 carried a value. */\n readonly raw?: string;\n /** The recognized status code, present only when the raw text is a known status. */\n readonly code?: ResultStatusCode;\n /** The modeled meaning; `\"unspecified\"` when absent, `\"undefined\"` when unrecognized. */\n readonly meaning: ResultStatusMeaning;\n /** Whether the raw text matched a recognized status letter. */\n readonly recognized: boolean;\n /** `true` **only** for a plain `F` (final): never for `C`, `X`, absent, or unrecognized. */\n readonly isActiveFinal: boolean;\n /** `true` for `C`: this value supersedes a previously transmitted result. */\n readonly supersedes: boolean;\n /** `true` for `X`: the result cannot be done / was cancelled. */\n readonly cancelled: boolean;\n}\n\n/**\n * Interpret an `R`-record result-status field (field 9).\n *\n * An **absent** status (`undefined` or empty) is typed `unspecified`: **never\n * assumed `final`**. A **present but unrecognized** letter is `undefined`\n * (recognized `false`). In every non-`F` case `isActiveFinal` is `false`, so a\n * correction, a cancellation, or an unknown status can never read as current.\n *\n * @param raw - The verbatim field-9 text, or `undefined`/empty when absent.\n * @returns The modeled status.\n * @example\n * ```ts\n * import { interpretResultStatus } from \"@cosyte/astm\";\n * const s = interpretResultStatus(\"F\");\n * s.isActiveFinal; // true\n * s.supersedes; // false\n * ```\n */\nexport function interpretResultStatus(raw: string | undefined): ResultStatus {\n if (raw === undefined || raw.trim().length === 0) {\n return {\n meaning: \"unspecified\",\n recognized: false,\n isActiveFinal: false,\n supersedes: false,\n cancelled: false,\n };\n }\n const key = raw.trim();\n if (isResultStatusCode(key)) {\n return {\n raw,\n code: key,\n meaning: RESULT_STATUS_MEANINGS[key],\n recognized: true,\n isActiveFinal: key === \"F\",\n supersedes: key === \"C\",\n cancelled: key === \"X\",\n };\n }\n return {\n raw,\n meaning: \"undefined\",\n recognized: false,\n isActiveFinal: false,\n supersedes: false,\n cancelled: false,\n };\n}\n\nfunction isResultStatusCode(value: string): value is ResultStatusCode {\n return Object.prototype.hasOwnProperty.call(RESULT_STATUS_MEANINGS, value);\n}\n\n/* ────────────────────────────── reference range ────────────────────────────── */\n\n/**\n * The shape of a parsed reference range.\n *\n * - `closed`: both a low and a high bound (`3.5-5.0`).\n * - `open-low`: an upper bound only (`<5`), so everything at or below `high`.\n * - `open-high`: a lower bound only (`>10`), so everything at or above `low`.\n * - `unparsed`: the text did not match a recognized form; both bounds are\n * absent and the raw text is surfaced. **A bound is never fabricated.**\n */\nexport type ReferenceRangeKind = \"closed\" | \"open-low\" | \"open-high\" | \"unparsed\";\n\n/**\n * A parsed reference range. Bounds are surfaced as the **verbatim numeric text**\n * (never coerced to a float, never rounded, never converted) so nothing is lost\n * or fabricated.\n *\n * **`[OSS-derived]`: the exact lower/upper delimiter (`-`) and the open-ended\n * `<x`/`>x` forms are taken from the permissively-licensed OSS reference parsers\n * and cross-verified vendor transcripts; they are not confirmed against the\n * purchased CLSI LIS02-A2. Anything that does not match these\n * forms is surfaced verbatim as `unparsed`, never guessed into a bound.**\n *\n * @example\n * ```ts\n * import { parseReferenceRange } from \"@cosyte/astm\";\n * const r = parseReferenceRange(\"3.5-5.0\");\n * r.kind; // \"closed\"\n * r.low; // \"3.5\"\n * r.high; // \"5.0\"\n * ```\n */\nexport interface ReferenceRange {\n /** The verbatim field text, exactly as received. */\n readonly raw: string;\n /** The lower bound, verbatim numeric text (for `closed` and `open-high`). */\n readonly low?: string;\n /** The upper bound, verbatim numeric text (for `closed` and `open-low`). */\n readonly high?: string;\n /** The recognized shape, or `\"unparsed\"` when the text matched no known form. */\n readonly kind: ReferenceRangeKind;\n}\n\n/** Matches a single numeric literal: optional sign, digits, optional fraction. */\nconst NUMBER = \"-?\\\\d+(?:\\\\.\\\\d+)?\";\nconst OPEN_LOW = new RegExp(`^<\\\\s*(${NUMBER})$`, \"u\");\nconst OPEN_HIGH = new RegExp(`^>\\\\s*(${NUMBER})$`, \"u\");\nconst CLOSED = new RegExp(`^(${NUMBER})\\\\s*-\\\\s*(${NUMBER})$`, \"u\");\n\n/**\n * Parse an `R`-record reference-range field (field 6) into a low/high (or\n * open-ended) pair.\n *\n * Recognized forms are `low-high` (closed), `<high` (open-low), and `>low`\n * (open-high). Anything else, including an ambiguous multi-dash string or a\n * bare non-numeric token, is returned as `kind: \"unparsed\"` with the raw text\n * preserved and **no bound invented**. Bounds are surfaced as verbatim text, not\n * coerced to numbers.\n *\n * @param raw - The verbatim field-6 text.\n * @returns The parsed reference range.\n * @example\n * ```ts\n * import { parseReferenceRange } from \"@cosyte/astm\";\n * parseReferenceRange(\"<5\").kind; // \"open-low\"\n * parseReferenceRange(\">10\").low; // \"10\"\n * parseReferenceRange(\"weird\").kind; // \"unparsed\" (never a fabricated bound)\n * ```\n */\nexport function parseReferenceRange(raw: string): ReferenceRange {\n const text = raw.trim();\n\n const low = OPEN_LOW.exec(text);\n if (low?.[1] !== undefined) return { raw, high: low[1], kind: \"open-low\" };\n\n const high = OPEN_HIGH.exec(text);\n if (high?.[1] !== undefined) return { raw, low: high[1], kind: \"open-high\" };\n\n const closed = CLOSED.exec(text);\n if (closed?.[1] !== undefined && closed[2] !== undefined) {\n return { raw, low: closed[1], high: closed[2], kind: \"closed\" };\n }\n\n return { raw, kind: \"unparsed\" };\n}\n","/**\n * The **host-query flow**: classify a message as a request, a result upload, an\n * order download, or indeterminate.\n *\n * On real analyzers the host-query mode is first-class and sometimes mandatory\n * (on the Roche cobas 4800 there is *no* results-sending-only option): the\n * analyzer sends an `H/P/Q/L` **request** and the LIS answers with an `H/P/O/L`\n * **response**. Misreading a query as a result upload (or the reverse) breaks the\n * order flow, so this module makes the distinction **explicit**:\n *\n * **The `Q` record dominates.** Any message carrying a `Q` (request-information)\n * record is classified `host-query`, a request, and is **never** treated as a\n * result set, even if a result record is also present (an anomaly, which the\n * parser flags separately with `ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND`). This is the\n * whole point: a `Q`-bearing message can never silently read as a result upload.\n *\n * **A letter the reader could not recognize blocks a positive answer.** The rule\n * above is stated on a count of type letters, so it only holds while every letter\n * was legible. A record whose type is unsupported may be the very `Q` the rule\n * exists to honor, and reading such a message as a result set is exactly the\n * outcome the rule forbids. So an unsupported record, with no `Q` to settle the\n * question, yields `indeterminate` rather than `results` or `orders`: the honest\n * answer is that the kind is not knowable, and the parser reports the unreadable\n * letter separately with `ASTM_RECORD_UNKNOWN_TYPE`. A `Q` that *was* read still\n * dominates, because an unreadable letter can only ever add a kind, never remove\n * the query that is already there.\n */\n\nimport type { AstmMessageClassification, AstmRecord } from \"./types.js\";\n\n/**\n * Classify a record stream by the host-query flow. Pure and total: it only reads\n * the record type letters, never a field value.\n *\n * `Q` dominates: a message with any `Q` record is a `host-query` request even when\n * a result (`R`) record is also present, so a query is never misread as a result\n * upload. (The `Q`+`R` anomaly is separately warned at parse time.)\n *\n * An **unsupported** record letter with no `Q` alongside it yields `indeterminate`,\n * never `results` or `orders`: an unreadable letter may have been the `Q`, and\n * claiming a positive kind over it is how a query comes to read as a result set.\n * {@link AstmMessageClassification.hasUnrecognized} reports why the answer is\n * withheld, and the `has*` counts stay truthful throughout.\n *\n * @param records - The parsed records, in wire order.\n * @returns The message classification.\n * @example\n * ```ts\n * import { classifyMessage, parseAstmRecords } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rQ|1|^SPEC-7||ALL\\rL|1\\r\");\n * classifyMessage(msg.records).kind; // \"host-query\"\n * ```\n */\nexport function classifyMessage(records: readonly AstmRecord[]): AstmMessageClassification {\n let hasQuery = false;\n let hasResults = false;\n let hasOrders = false;\n let hasUnrecognized = false;\n for (const r of records) {\n if (r.type === \"Q\") hasQuery = true;\n else if (r.type === \"R\") hasResults = true;\n else if (r.type === \"O\") hasOrders = true;\n else if (r.type === \"unsupported\") hasUnrecognized = true;\n }\n\n // `Q` dominates so a query is never read as a result set; then results, then orders.\n //\n // An unrecognized letter sits between the two: it cannot cancel a `Q` that was read (the query is\n // there on the wire either way), but with no `Q` read it leaves the kind genuinely unknown, and\n // answering `results` over it is precisely the misreading the `Q`-dominates rule exists to\n // prevent. Withholding the positive answer keeps that guarantee true of a mangled letter as well\n // as a legible one, and costs only a claim the reader was not entitled to make.\n let kind: AstmMessageClassification[\"kind\"];\n if (hasQuery) kind = \"host-query\";\n else if (hasUnrecognized) kind = \"indeterminate\";\n else if (hasResults) kind = \"results\";\n else if (hasOrders) kind = \"orders\";\n else kind = \"indeterminate\";\n\n return {\n kind,\n hasQuery,\n hasResults,\n hasOrders,\n hasUnrecognized,\n isHostQueryRequest: kind === \"host-query\",\n };\n}\n","/**\n * The **frame** warning registry (`ASTM_FRAME_*`) for the ASTM/CLSI-LIS01 codec:\n * the second of the package's two registries, alongside the record layer's\n * `ASTM_RECORD_*` (both share only the `EMPTY_INPUT` fatal).\n *\n * A frame warning is the lenient codec's record of a tolerated byte-level\n * deviation: it never crashes, never invents a value, and, critically, never\n * lets an untrusted frame be silently merged into a record. Every warning carries\n * a stable code plus an {@link AstmFramePosition} (frame number + byte offset) and\n * **never** the record bytes a frame carries (PHI discipline). Consumers compare\n * `warning.code === FRAME_WARNING_CODES.<CODE>`; renaming a code is a **breaking\n * change**.\n */\n\nimport type { AstmFramePosition } from \"./position.js\";\n\n/**\n * Stable string codes for every frame-codec warning. `key === value` so\n * `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { decodeAstmFrames, FRAME_WARNING_CODES } from \"@cosyte/astm\";\n * const bytes = new Uint8Array([]); // ...a framed stream...\n * void bytes;\n * FRAME_WARNING_CODES.ASTM_FRAME_BAD_CHECKSUM; // \"ASTM_FRAME_BAD_CHECKSUM\"\n * ```\n */\nexport const FRAME_WARNING_CODES = {\n /**\n * A frame's two-hex-char checksum did not match the modulo-256 sum recomputed over its bytes. The\n * frame is surfaced with `trusted: false` and its text is **never merged** into a reassembled\n * record (default **warn** in lenient mode, escalated to a thrown error in `strict`). Corruption is\n * never silently trusted: the \"checksums are routinely not validated\" claim was refuted; we\n * validate.\n */\n ASTM_FRAME_BAD_CHECKSUM: \"ASTM_FRAME_BAD_CHECKSUM\",\n /**\n * A frame's sequence number was not the expected next value (`1 → … → 7 → 0 → …`): a frame was\n * possibly dropped. The stream is **never silently concatenated across the gap** as if contiguous;\n * the in-progress record is tainted and not emitted as a clean reassembly.\n */\n ASTM_FRAME_SEQUENCE_GAP: \"ASTM_FRAME_SEQUENCE_GAP\",\n /**\n * A frame opened with `STX` but no valid terminator + checksum was found before the stream ended\n * (or before the next `STX`), or a record's frames ended on an intermediate `ETB` with no final\n * `ETX`. The partial bytes are surfaced flagged untrusted and **no partial record is invented**\n * (warn in lenient, thrown in `strict`).\n */\n ASTM_FRAME_UNTERMINATED: \"ASTM_FRAME_UNTERMINATED\",\n /**\n * A frame's record text exceeded the 240-byte limit without a split. The frame is still surfaced\n * (and, if its checksum validates, reassembled): the deviation is flagged, not silently dropped\n * (warn in lenient, thrown in `strict`).\n */\n ASTM_FRAME_OVERSIZE: \"ASTM_FRAME_OVERSIZE\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmFrameWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link FRAME_WARNING_CODES}.\n */\nexport type FrameWarningCode = (typeof FRAME_WARNING_CODES)[keyof typeof FRAME_WARNING_CODES];\n\n/**\n * A single frame-codec warning: a stable code, a value-free human-readable\n * message, and positional context (frame number + byte offset).\n *\n * @example\n * ```ts\n * import type { AstmFrameWarning } from \"@cosyte/astm\";\n * const w: AstmFrameWarning = {\n * code: \"ASTM_FRAME_BAD_CHECKSUM\",\n * message: \"Frame checksum mismatch.\",\n * position: { frameNumber: 2, byteOffset: 251 },\n * };\n * ```\n */\nexport interface AstmFrameWarning {\n readonly code: FrameWarningCode;\n /** Human-readable detail for logs. Never contains the frame's record bytes. */\n readonly message: string;\n readonly position: AstmFramePosition;\n}\n\n/**\n * Build an `ASTM_FRAME_BAD_CHECKSUM` warning. The frame is surfaced flagged\n * untrusted and never merged into a record; the warning carries position only.\n *\n * @example\n * ```ts\n * import { frameBadChecksum } from \"@cosyte/astm\";\n * frameBadChecksum({ frameNumber: 2, byteOffset: 251 });\n * ```\n */\nexport function frameBadChecksum(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_BAD_CHECKSUM,\n message: \"Frame checksum mismatch, surfaced untrusted, never merged into a record.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_SEQUENCE_GAP` warning. A frame was possibly dropped; the\n * record is not concatenated across the gap as if contiguous.\n *\n * @example\n * ```ts\n * import { frameSequenceGap } from \"@cosyte/astm\";\n * frameSequenceGap({ frameNumber: 3, byteOffset: 502 });\n * ```\n */\nexport function frameSequenceGap(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_SEQUENCE_GAP,\n message: \"Frame number out of sequence, possible dropped frame, never silently bridged.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_UNTERMINATED` warning. The partial bytes are surfaced\n * flagged untrusted; no partial record is invented.\n *\n * @example\n * ```ts\n * import { frameUnterminated } from \"@cosyte/astm\";\n * frameUnterminated({ byteOffset: 730 });\n * ```\n */\nexport function frameUnterminated(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_UNTERMINATED,\n message: \"Unterminated frame, partial bytes surfaced untrusted, no partial record invented.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_FRAME_OVERSIZE` warning. The 240-byte text limit was exceeded\n * without a split; the frame is flagged, not silently dropped.\n *\n * @example\n * ```ts\n * import { frameOversize } from \"@cosyte/astm\";\n * frameOversize({ frameNumber: 1, byteOffset: 0 });\n * ```\n */\nexport function frameOversize(position: AstmFramePosition): AstmFrameWarning {\n return {\n code: FRAME_WARNING_CODES.ASTM_FRAME_OVERSIZE,\n message: \"Frame text exceeded the 240-byte limit without a split, flagged, never dropped.\",\n position,\n };\n}\n","/**\n * The **LTP** warning registry (`ASTM_LTP_*`) for the ASTM/CLSI-LIS01 protocol\n * layer: the third of the package's registries, alongside the record layer's\n * `ASTM_RECORD_*` and the frame codec's `ASTM_FRAME_*`.\n *\n * An LTP warning is the protocol reducer's (or the transport detector's) record\n * of a tolerated session-level deviation: an ambiguous framed/raw stream, a\n * control event that arrived in a state that did not expect it, or a frame the\n * receiver had to reject (`NAK`) rather than accept. Every warning carries a\n * stable code plus, at most, a **frame number**: never the record bytes a frame\n * carries (PHI discipline). Consumers compare `warning.code ===\n * LTP_WARNING_CODES.<CODE>`; renaming a code is a **breaking change**.\n */\n\n/**\n * Stable string codes for every LTP protocol / transport warning. `key === value`\n * so `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { LTP_WARNING_CODES } from \"@cosyte/astm\";\n * LTP_WARNING_CODES.ASTM_LTP_FRAME_REJECTED; // \"ASTM_LTP_FRAME_REJECTED\"\n * ```\n */\nexport const LTP_WARNING_CODES = {\n /**\n * The transport detector could not tell a framed stream from a raw (unframed) one from its leading\n * byte: it was neither `STX`/`ENQ` nor a bare record letter. It **defaults to framed** and warns,\n * never guessing silently into data loss; a profile override forces the mode. (cobas b121 drops\n * framing over TCP; cobas 4800 / Iguana retain it, both realities exist, so an unrecognizable lead\n * is defaulted, not assumed.)\n */\n ASTM_LTP_AMBIGUOUS_TRANSPORT: \"ASTM_LTP_AMBIGUOUS_TRANSPORT\",\n /**\n * A control event arrived in a protocol state that did not expect it: e.g. an inbound `ACK`/`NAK`\n * at a receiver (which sends, never receives, those), or an `ENQ` mid-transfer. The event is\n * surfaced and handled defensively (an unexpected `ACK`/`NAK` is **never** read as acceptance of\n * data); it never advances the transfer as if valid.\n */\n ASTM_LTP_UNEXPECTED_EVENT: \"ASTM_LTP_UNEXPECTED_EVENT\",\n /**\n * The receiver rejected a received frame with a `NAK` instead of accepting it: because its\n * checksum failed, it was unterminated, or its frame number was out of sequence. The frame's text\n * is **never** appended to the record and the transfer does **not** advance; the receiver awaits the\n * sender's retransmit. This is the protocol-level face of the frame codec's fail-safe: a bad frame\n * drives retransmit, not acceptance.\n */\n ASTM_LTP_FRAME_REJECTED: \"ASTM_LTP_FRAME_REJECTED\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmLtpWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link LTP_WARNING_CODES}.\n */\nexport type LtpWarningCode = (typeof LTP_WARNING_CODES)[keyof typeof LTP_WARNING_CODES];\n\n/**\n * A single LTP protocol / transport warning: a stable code, a value-free\n * human-readable message, and, for a frame-scoped deviation, the frame number\n * only. Never carries a frame's record bytes.\n *\n * @example\n * ```ts\n * import type { AstmLtpWarning } from \"@cosyte/astm\";\n * const w: AstmLtpWarning = {\n * code: \"ASTM_LTP_FRAME_REJECTED\",\n * message: \"Frame rejected, NAK sent, retransmit expected.\",\n * frameNumber: 2,\n * };\n * ```\n */\nexport interface AstmLtpWarning {\n readonly code: LtpWarningCode;\n /** Human-readable detail for logs. Never contains the frame's record bytes. */\n readonly message: string;\n /** The frame's sequence number when the warning is frame-scoped; absent otherwise. */\n readonly frameNumber?: number;\n}\n\n/**\n * Build an `ASTM_LTP_AMBIGUOUS_TRANSPORT` warning. The detector defaulted to\n * framed; a profile override can force raw.\n *\n * @example\n * ```ts\n * import { ltpAmbiguousTransport } from \"@cosyte/astm\";\n * ltpAmbiguousTransport();\n * ```\n */\nexport function ltpAmbiguousTransport(): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_AMBIGUOUS_TRANSPORT,\n message:\n \"Leading byte was neither a frame start (STX/ENQ) nor a record letter, defaulted to framed; override with a profile to force raw.\",\n };\n}\n\n/**\n * Build an `ASTM_LTP_UNEXPECTED_EVENT` warning. The event was surfaced and handled\n * defensively; it never advanced the transfer as if valid.\n *\n * @param frameNumber - The frame number, when the unexpected event was a frame; omitted otherwise.\n * @example\n * ```ts\n * import { ltpUnexpectedEvent } from \"@cosyte/astm\";\n * ltpUnexpectedEvent();\n * ```\n */\nexport function ltpUnexpectedEvent(frameNumber?: number): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_UNEXPECTED_EVENT,\n message:\n \"Control event arrived in a state that did not expect it, handled defensively, never read as acceptance.\",\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n };\n}\n\n/**\n * Build an `ASTM_LTP_FRAME_REJECTED` warning. The receiver sent a `NAK`; the\n * frame's text was never appended and the transfer did not advance.\n *\n * @param frameNumber - The rejected frame's sequence number, when it could be read.\n * @example\n * ```ts\n * import { ltpFrameRejected } from \"@cosyte/astm\";\n * ltpFrameRejected(2);\n * ```\n */\nexport function ltpFrameRejected(frameNumber?: number): AstmLtpWarning {\n return {\n code: LTP_WARNING_CODES.ASTM_LTP_FRAME_REJECTED,\n message:\n \"Frame rejected (bad checksum, unterminated, or out of sequence), NAK sent, retransmit expected, never accepted.\",\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n };\n}\n","/**\n * The safety spine of the profile subsystem: the set of warning codes a profile is\n * **forbidden** to tolerate. A vendor profile exists to quiet known, benign\n * structural noise: never to hide a deviation that could change a clinical\n * reading, lose track of where one message ends, or corrupt the wire.\n *\n * **Design: default-deny.** Rather than enumerate the *forbidden* codes (a list you\n * can forget to extend when a new warning ships, the exact incompleteness a\n * safety gate must not have), this module enumerates the tiny set of **explicitly\n * tolerable** codes and treats *every other* code, across all three registries\n * (record `ASTM_RECORD_*`, frame `ASTM_FRAME_*`, protocol `ASTM_LTP_*`), as\n * safety-critical. A warning code added later is therefore\n * safety-critical **by default** until someone deliberately, reviewably adds it to\n * the allow-list below. Widening the allow-list can only ever be an explicit,\n * argued act; forgetting to touch this file can never *weaken* the gate.\n *\n * **The test for admitting a code has two parts, and the second one is easy to\n * miss.** A code may sit on the allow-list only while\n *\n * 1. it is structural or syntactic noise that **cannot alter, drop, or fabricate\n * an extracted value** (a profile re-badges a *warning*, it never re-parses, so\n * the underlying datum is surfaced identically with or without the profile);\n * **and**\n * 2. **nothing else in this package reads the condition the warning reports.**\n *\n * Part 2 is a claim about the whole library, not about the warning, and it is the\n * part that can stop being true without anyone touching this file. A code admitted\n * as benign stops being benign the moment a later feature starts making a decision\n * out of the very condition the warning is reporting, and adding that feature does\n * not force this file to be revisited. That is not hypothetical: it happened once\n * here, to `ASTM_RECORD_UNKNOWN_TYPE`, and the consequence was a wrong-patient\n * path.\n *\n * **A record's type letter acquired two load-bearing readers, and the code was\n * admitted before either existed.** Message grouping reads the letter to decide\n * where one message ends and the next begins, so an unrecognized header opens no\n * message and two messages are read as one, pairing a patient with results that\n * arrived under a different header. Message classification counts `Q` / `R` / `O`\n * records by the same letter, so an unrecognized `Q` cost a host-query request its\n * `Q`-dominates guarantee: it read as a result set, and the\n * `ASTM_RECORD_AMBIGUOUS_MESSAGE_KIND` warning that flags that contradiction went\n * with it. In both cases `ASTM_RECORD_UNKNOWN_TYPE` is the only warning left on the\n * stream, while the allow-list still described it as harmless. Tolerating it\n * re-badged the single report that the reader had lost its place, and a strict parse\n * then accepted the stream. It is off the list for that reason, and its removal is\n * what part 2 above is written down to prevent a repeat of.\n *\n * The classifier no longer answers `results` over a letter it could not read (it\n * withholds the kind instead), so that particular misreading is closed at the\n * source as well as reported. The allow-list conclusion is unchanged either way:\n * part 2 asks whether anything **reads** the reported condition, and the classifier\n * still does, now to decide whether it may answer at all.\n *\n * **A third reader of record structure landed with\n * `ASTM_RECORD_FIELDS_UNSEPARATED`, and the allow-list was re-derived against it.**\n * That code reports a record the delimiters in force could not split at all, which\n * is one signature of a record being read in a set that is not its own. It is\n * safety-critical (it reports lost values, so it fails part 1 outright) and it is\n * on the forbidden side by construction, because that side is computed rather than\n * listed. What it required was re-reading the survivors below against it:\n * none of them is a statement about whether a record split, so none of them is now\n * hiding it. Note the code is deliberately **partial** (see its own docs): it does\n * not fire wherever a field went missing to a foreign delimiter set, only where the\n * field separator was absent outright, so it is a reader of record structure that\n * cannot be relied on as a sweep.\n *\n * **So the ones that remain are recorded with the reading each one survives**, not\n * merely with the value it preserves. Each was re-derived against both readers of\n * record structure above, plus the single-message / single-patient guards:\n *\n * - `ASTM_NONSTANDARD_DELIMITERS`: the header's own declared delimiters are read\n * and honored either way, and this notes only that they differ from the canonical\n * set. No reader sees them: the stream is cut into records on a line break,\n * and a record's type letter is the first character of its line, both taken before\n * any delimiter-driven tokenization runs. It survives the split reader too, and\n * the distinction is worth stating precisely: this code says the declared set is\n * unusual, `ASTM_RECORD_FIELDS_UNSEPARATED` says a record did not match the set in\n * force. A stream may be wholly non-canonical and split perfectly, and a wholly\n * canonical stream can still carry a record that does not split, so tolerating the\n * first can never quiet the second.\n * - `ASTM_UNKNOWN_ESCAPE_SEQUENCE`: an unrecognized escape body is **preserved\n * byte-for-byte** in the decoded value (the escape codec does not guess at one),\n * so the value is identical with or without the profile. No reader sees it\n * either: a decoded field value is never cut back into records, the type\n * letter is read before decoding, and the split reader counts fields, which the\n * escape-aware tokenizer has already finished dividing before any body is decoded.\n *\n * **This entry is on the list and is recorded as questionable, deliberately.**\n * The argument above is about the *decoded value*, and it holds. What it does not\n * cover is that the escape-aware split itself treats an `&X&` triple as opaque, so\n * where `X` is a delimiter that delimiter never became a boundary: the split the\n * argument says has \"already finished dividing\" divided one time too few, and this\n * code is the only report of it. Measured on the canonical set:\n * `R|1|^^^687|28.6&|&U/L||||F` yields a value of `28.6&|&U/L`, no units, and status\n * `unspecified` rather than `final`, with this as the sole warning, so a profile\n * tolerating it (the shipped `referenceCorpus` does) lets `{ strict: true }` accept\n * it. That fails part 1 of the two-clause test on the reading, not on the value.\n * It is **left on the list on purpose**: removing it would change behavior for\n * every profile naming it, and what should report a swallowed boundary instead, and\n * at what severity, is a question this file cannot settle on its own. Do not read\n * this bullet as an endorsement; read it as the open question it is, and do not\n * close it by narrowing the atom, which is what keeps `&F&` one token under a\n * delimiter set that names `F` as a delimiter.\n * - `ASTM_UNPAIRED_ESCAPE_CHARACTER`: an escape character heading no escape\n * sequence, read as the **literal character it is** and kept byte-for-byte in the\n * decoded value, so the value is identical with or without the profile. Note what\n * makes this admissible is a property of the *parse*, not of the warning: reading\n * the character as a literal is unconditional, and tolerating the code changes\n * nothing about how the record splits. No reader sees it, on the same grounds as\n * the entry above: the type letter is read before any decoding, the split reader\n * counts fields the escape-aware tokenizer has already finished dividing, and a\n * decoded value is never cut back into records. Contrast the condition it\n * replaced, which **would** have failed part 1: reading the same character as an\n * unterminated sequence merged every field after it into one, and a code reporting\n * that would have been a report of lost values.\n * - `ASTM_RECORD_UNINTERPRETED_QUERY_STATUS`: a request-information status carried\n * verbatim as a leaf field on a `Q` record; the code set is paywalled and is not\n * interpreted, profile or not. Classification reads whether a `Q` record is\n * *present*, never what this field says; grouping does not read `Q` at all; and the\n * split reader is a count of fields, not a reading of any one of them, so a record\n * carrying this warning has by definition already split.\n *\n * Everything a wrong value could hide from: a result value split ambiguity, an\n * undefined abnormal flag or result status, an unparseable reference range, absent\n * units, a mis-attached comment, a partial timestamp, a query-vs-result ambiguity,\n * an unrecognized record type, a record the delimiters in force could not split, a\n * bad frame checksum / sequence gap / unterminated / oversize frame, an ambiguous\n * transport, an unexpected protocol event, or a rejected frame: is forbidden.\n *\n * **What this file cannot do for you.** Part 2 is a review obligation, not a\n * mechanical one. There is no honest automatic check for \"nothing load-bearing\n * reads this\", because deciding whether a future reader of record structure has put\n * an allow-listed code back in the path takes reading the reader. What is\n * mechanical is the direction of the failure: default-deny means a *new* code is\n * refused until argued in, so the residual risk is confined to the small, explicit\n * list below. Re-derive that list whenever something new starts reading record\n * structure.\n */\n\nimport { WARNING_CODES } from \"../common/warnings.js\";\nimport { FRAME_WARNING_CODES } from \"../frames/warnings.js\";\nimport { LTP_WARNING_CODES } from \"../ltp/warnings.js\";\n\nimport type { AnyAstmWarningCode } from \"./types.js\";\n\n/**\n * The **only** warning codes a profile may list in its `tolerate` set: benign\n * structural or syntactic vendor noise that cannot alter, drop, or fabricate an\n * extracted value, **and** whose reported condition nothing else in this package\n * reads: not to decide where one message ends and the next begins, and not to\n * decide what kind of message it is. Frozen so it cannot be mutated at runtime to\n * smuggle a code in. Adding to this set is a deliberate, reviewable act, and an\n * addition has to satisfy both halves of that test, not just the first.\n *\n * `ASTM_RECORD_UNKNOWN_TYPE` is **not** on this list, and used to be. An\n * unrecognized record type may be a header the reader did not recognize as one, in\n * which case two messages have been read as one, or a `Q` it did not recognize, in\n * which case the message kind is no longer knowable. Tolerating it can quiet the\n * only report either happened.\n *\n * `ASTM_RECORD_FIELDS_UNSEPARATED` is not on this list either, and never was. It\n * reports a record the delimiters in force could not split, so every modeled field\n * of that record is missing: it fails the first half of the test outright.\n *\n * @example\n * ```ts\n * import { TOLERABLE_CODES } from \"@cosyte/astm\";\n * TOLERABLE_CODES.has(\"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"); // true\n * TOLERABLE_CODES.has(\"ASTM_RECORD_UNKNOWN_TYPE\"); // false\n * TOLERABLE_CODES.has(\"ASTM_FRAME_BAD_CHECKSUM\"); // false\n * ```\n */\nexport const TOLERABLE_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>([\n WARNING_CODES.ASTM_NONSTANDARD_DELIMITERS,\n WARNING_CODES.ASTM_UNKNOWN_ESCAPE_SEQUENCE,\n WARNING_CODES.ASTM_UNPAIRED_ESCAPE_CHARACTER,\n WARNING_CODES.ASTM_RECORD_UNINTERPRETED_QUERY_STATUS,\n ]),\n);\n\n/**\n * Every real warning code across the three registries: the universe the safety\n * gate reasons over. `PROFILE_QUIRK_APPLIED` is included (it is a real record code)\n * so a profile can never tolerate the marker itself.\n *\n * @internal\n */\nconst ALL_WARNING_CODES: readonly AnyAstmWarningCode[] = [\n ...Object.values(WARNING_CODES),\n ...Object.values(FRAME_WARNING_CODES),\n ...Object.values(LTP_WARNING_CODES),\n];\n\n/**\n * The set of every real warning code, for O(1) membership checks (used by the\n * validator to distinguish \"unknown code\" from \"known but forbidden\"). Frozen.\n *\n * @example\n * ```ts\n * import { ALL_ASTM_WARNING_CODES } from \"@cosyte/astm\";\n * ALL_ASTM_WARNING_CODES.has(\"ASTM_LTP_FRAME_REJECTED\"); // true\n * ```\n */\nexport const ALL_ASTM_WARNING_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>(ALL_WARNING_CODES),\n);\n\n/**\n * The forbidden set: computed as **every known code minus the tolerable\n * allow-list**, so it is complete by construction. Frozen. A code appears here iff\n * it is a real warning code that is not in {@link TOLERABLE_CODES}.\n *\n * @example\n * ```ts\n * import { SAFETY_CRITICAL_CODES } from \"@cosyte/astm\";\n * SAFETY_CRITICAL_CODES.has(\"ASTM_RECORD_UNDEFINED_RESULT_STATUS\"); // true\n * SAFETY_CRITICAL_CODES.has(\"ASTM_RECORD_UNKNOWN_TYPE\"); // true\n * SAFETY_CRITICAL_CODES.has(\"ASTM_FRAME_BAD_CHECKSUM\"); // true\n * ```\n */\nexport const SAFETY_CRITICAL_CODES: ReadonlySet<AnyAstmWarningCode> = Object.freeze(\n new Set<AnyAstmWarningCode>(ALL_WARNING_CODES.filter((c) => !TOLERABLE_CODES.has(c))),\n);\n\n/**\n * True when `code` is a known warning code that is **not** tolerable: i.e. a\n * profile may never list it. A code that is not a real warning code at all returns\n * `false` here (the validator reports \"unknown code\" separately, a distinct\n * failure with a distinct message).\n *\n * @param code - The warning code to test.\n * @returns `true` iff the code is a real code outside the tolerable allow-list.\n * @example\n * ```ts\n * import { isSafetyCriticalCode } from \"@cosyte/astm\";\n * isSafetyCriticalCode(\"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"); // false (tolerable)\n * isSafetyCriticalCode(\"ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT\"); // true\n * isSafetyCriticalCode(\"ASTM_RECORD_UNKNOWN_TYPE\"); // true (it can mean a lost message boundary)\n * ```\n */\nexport function isSafetyCriticalCode(code: string): boolean {\n return SAFETY_CRITICAL_CODES.has(code as AnyAstmWarningCode);\n}\n","/**\n * The runtime side of the profile subsystem: turning an active {@link AstmProfile}\n * into a warning transform the record parser runs over its accumulated warnings,\n * plus the transport-override accessor the framing detector consumes.\n *\n * A profile **never touches an extracted value**. The warning transform operates\n * purely at the warning layer: downgrading a deviation it *expects* into a\n * `PROFILE_QUIRK_APPLIED` warning (flagged `expected`, carrying the original\n * `toleratedCode`) while leaving every un-expected warning untouched. Because the\n * transform only ever rewrites a warning object (never a record, field, or value),\n * a profile cannot change how any byte parses: a spec-clean message parses\n * identically with or without a profile.\n */\n\nimport { profileQuirkApplied, type AstmRecordWarning } from \"../common/warnings.js\";\nimport type { AstmFraming } from \"../ltp/transport.js\";\n\nimport { isSafetyCriticalCode } from \"./safety.js\";\nimport type { AstmProfile, AstmQuirkTolerance } from \"./types.js\";\n\n/**\n * Does `tolerance` apply to `warning`? The codes must match, and every structural\n * key present in the tolerance's `match` must equal the warning's position: a\n * tolerance with no `match` applies to every warning of its code. Matching is on\n * PHI-free structural identifiers only (record-type letter, 1-based field index).\n *\n * @internal\n */\nfunction toleranceApplies(tolerance: AstmQuirkTolerance, warning: AstmRecordWarning): boolean {\n if (tolerance.code !== warning.code) return false;\n const match = tolerance.match;\n if (match === undefined) return true;\n if (match.recordType !== undefined && warning.position.recordType !== match.recordType) {\n return false;\n }\n if (match.fieldIndex !== undefined && warning.position.fieldIndex !== match.fieldIndex) {\n return false;\n }\n return true;\n}\n\n/**\n * Apply a profile to a single warning. Returns a downgraded `PROFILE_QUIRK_APPLIED`\n * warning when the profile expects this deviation; otherwise returns the original\n * warning unchanged (referential identity preserved, so an un-tolerated warning is\n * never reallocated). A warning that is already `expected` (e.g. re-processed) is\n * passed through untouched.\n *\n * **The safety gate is re-checked here, and that is not redundant.**\n * {@link defineAstmProfile} refuses a safety-critical code at definition time, but\n * {@link AstmProfile} is a plain interface: a hand-authored object literal\n * type-checks, and `parseAstmRecords(raw, { profile })` and `setDefaultAstmProfile`\n * both accept one without re-running the factory. A definition-time-only gate\n * therefore guards a door that has a second entrance. A safety-critical code is\n * **not downgraded here whatever the profile says**, so the original warning\n * survives and `strict` still escalates it. Silently declining to downgrade, rather\n * than throwing, is the fail-safe direction: the signal is preserved either way, and\n * a parse is not turned into an exception by a profile the caller built by hand.\n *\n * @param profile - The active profile.\n * @param warning - One accumulated warning.\n * @returns The re-badged warning when tolerated, else the original.\n * @example\n * ```ts\n * import { applyAstmProfile, astmProfiles, unknownEscapeSequence } from \"@cosyte/astm\";\n * const w = unknownEscapeSequence({ recordIndex: 4, recordType: \"R\", fieldIndex: 5 });\n * const out = applyAstmProfile(astmProfiles.referenceCorpus, w);\n * out.code; // \"PROFILE_QUIRK_APPLIED\"\n * out.toleratedCode; // \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\"\n * ```\n */\nexport function applyAstmProfile(\n profile: AstmProfile,\n warning: AstmRecordWarning,\n): AstmRecordWarning {\n if (warning.expected === true) return warning;\n if (isSafetyCriticalCode(warning.code)) return warning;\n for (const tolerance of profile.tolerate) {\n if (toleranceApplies(tolerance, warning)) {\n return profileQuirkApplied(warning, profile.name);\n }\n }\n return warning;\n}\n\n/**\n * Apply a profile across a whole warning list, returning a NEW array (the input is\n * never mutated). Returned unchanged (same reference contents, a shallow copy) when\n * `profile` is `undefined`, so the no-profile path pays only a copy. Every tolerated\n * warning is re-badged; every other warning passes through by identity, nothing is\n * dropped, reordered, or reallocated beyond the re-badge.\n *\n * @param warnings - The accumulated record warnings.\n * @param profile - The active profile, or `undefined` for no transform.\n * @returns A new array with tolerated warnings downgraded.\n * @example\n * ```ts\n * import { applyAstmProfileToWarnings, astmProfiles } from \"@cosyte/astm\";\n * const out = applyAstmProfileToWarnings(msgWarnings, astmProfiles.referenceCorpus);\n * ```\n */\nexport function applyAstmProfileToWarnings(\n warnings: readonly AstmRecordWarning[],\n profile: AstmProfile | undefined,\n): AstmRecordWarning[] {\n if (profile === undefined) return [...warnings];\n return warnings.map((w) => applyAstmProfile(profile, w));\n}\n\n/**\n * The profile's transport override, if any: the value a consumer feeds to\n * `detectFraming(bytes, { override })` to force framed/raw and bypass leading-byte\n * auto-detection. `undefined` means \"let detection decide.\"\n *\n * @param profile - The active profile, or `undefined`.\n * @returns `\"framed\"` / `\"raw\"` when the profile forces one, else `undefined`.\n * @example\n * ```ts\n * import { resolveProfileTransport, detectFraming, astmProfiles } from \"@cosyte/astm\";\n * const override = resolveProfileTransport(myRawTcpProfile);\n * const { framing } = detectFraming(bytes, override !== undefined ? { override } : {});\n * ```\n */\nexport function resolveProfileTransport(profile: AstmProfile | undefined): AstmFraming | undefined {\n return profile?.transport;\n}\n","/**\n * Build the multi-line `describe()` output for an {@link AstmProfile}. Omits lines\n * for absent fields; lineage renders as `a → b → c`. Guaranteed non-empty and\n * always starts with `Profile '<name>'` so the \"contains the profile name\" contract\n * holds regardless of which lines are omitted.\n *\n * @internal\n */\n\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * Format an {@link AstmProfile} as a human-readable multi-line description.\n *\n * @internal\n */\nexport function buildDescribe(p: AstmProfile): string {\n const lines: string[] = [`Profile '${p.name}'`];\n if (p.description !== undefined) {\n lines.push(` description: ${p.description}`);\n }\n // `describe()` is only ever attached by the factory, which always fills `lineage`\n // with at least the profile's own name, so it is non-empty here.\n lines.push(` lineage: ${p.lineage.join(\" → \")}`);\n if (p.transport !== undefined) {\n lines.push(` transport: forces ${p.transport} framing (detection override)`);\n }\n if (p.provenance !== undefined) {\n lines.push(` grounded in: ${p.provenance.source} (${p.provenance.reference})`);\n }\n if (p.tolerate.length === 0) {\n lines.push(\" tolerates: nothing (conservative baseline)\");\n } else {\n lines.push(` tolerates ${String(p.tolerate.length)} quirk(s):`);\n for (const t of p.tolerate) {\n const scope =\n t.match?.recordType !== undefined\n ? ` @record ${t.match.recordType}${\n t.match.fieldIndex !== undefined ? `.${String(t.match.fieldIndex)}` : \"\"\n }`\n : t.match?.fieldIndex !== undefined\n ? ` @field ${String(t.match.fieldIndex)}`\n : \"\";\n lines.push(` - ${t.code}${scope}: ${t.rationale}`);\n }\n }\n return lines.join(\"\\n\");\n}\n","/**\n * Pure merge helpers consumed by `defineAstmProfile` when `opts.extends` is\n * supplied. Every helper takes a `readonly parents[]` + a self value and returns\n * the merged result; none mutate input. Mirrors the sibling `@cosyte/hl7` /\n * `@cosyte/ccda` profile merge semantics: lineage first-occurrence dedupe,\n * `tolerate` concat + dedupe (parents before self, last-wins on rationale), scalar\n * last-wins (child, else the last parent that carries a value).\n *\n * Zero runtime deps. Post-merge safety re-validation (a tolerated code must not be\n * safety-critical) is the CALLER's responsibility: these helpers are pure reducers.\n *\n * @internal\n */\n\nimport type { AstmFraming } from \"../ltp/transport.js\";\nimport type { AstmProfile, AstmProfileProvenance, AstmQuirkTolerance } from \"./types.js\";\n\n/**\n * Normalise the `extends` input to a readonly array. Accepts a single profile or an\n * array; returns `[]` for `undefined` so callers treat \"no parents\" identically to\n * \"zero parents\".\n *\n * @internal\n */\nexport function normaliseParents(\n ext: AstmProfile | readonly AstmProfile[] | undefined,\n): readonly AstmProfile[] {\n if (ext === undefined) return [];\n if (Array.isArray(ext)) return ext as readonly AstmProfile[];\n return [ext as AstmProfile];\n}\n\n/**\n * Compute lineage: flatten parent lineages (or `[parent.name]` when a parent has\n * none), append `selfName`, dedupe preserving first occurrence.\n *\n * @internal\n */\nexport function mergeLineage(parents: readonly AstmProfile[], selfName: string): readonly string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const p of parents) {\n const parentLineage = p.lineage.length > 0 ? p.lineage : [p.name];\n for (const n of parentLineage) {\n if (!seen.has(n)) {\n seen.add(n);\n out.push(n);\n }\n }\n }\n if (!seen.has(selfName)) out.push(selfName);\n return Object.freeze(out);\n}\n\n/**\n * Stable signature for a tolerance, used for concat-dedupe: the code plus the\n * optional structural match keys. Two tolerances with the same signature collapse\n * to the last (child-wins on rationale).\n *\n * @internal\n */\nfunction toleranceKey(t: AstmQuirkTolerance): string {\n return `${t.code} ${t.match?.recordType ?? \"\"} ${String(t.match?.fieldIndex ?? \"\")}`;\n}\n\n/**\n * Merge `tolerate` sets: every parent (left-to-right) then self, deduped by\n * {@link toleranceKey} with the **last** occurrence winning (so a child can refine\n * a parent's rationale for the same code+match). Order of first appearance is\n * preserved.\n *\n * @internal\n */\nexport function mergeTolerations(\n parents: readonly AstmProfile[],\n self: readonly AstmQuirkTolerance[],\n): readonly AstmQuirkTolerance[] {\n const order: string[] = [];\n const byKey = new Map<string, AstmQuirkTolerance>();\n const layer = (list: readonly AstmQuirkTolerance[]): void => {\n for (const t of list) {\n const key = toleranceKey(t);\n if (!byKey.has(key)) order.push(key);\n byKey.set(key, t);\n }\n };\n for (const p of parents) layer(p.tolerate);\n layer(self);\n return Object.freeze(order.map((k) => byKey.get(k) as AstmQuirkTolerance));\n}\n\n/**\n * Merge the `provenance` scalar: child wins when provided; otherwise the LAST parent\n * that carries one; otherwise `undefined`.\n *\n * @internal\n */\nexport function mergeProvenance(\n parents: readonly AstmProfile[],\n self: AstmProfileProvenance | undefined,\n): AstmProfileProvenance | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.provenance !== undefined) return p.provenance;\n }\n return undefined;\n}\n\n/**\n * Merge the `description` scalar: child wins when provided; otherwise the LAST\n * parent that carries one; otherwise `undefined`.\n *\n * @internal\n */\nexport function mergeDescription(\n parents: readonly AstmProfile[],\n self: string | undefined,\n): string | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.description !== undefined) return p.description;\n }\n return undefined;\n}\n\n/**\n * Merge the `transport` override scalar: child wins when provided; otherwise the\n * LAST parent that carries one; otherwise `undefined` (let detection decide).\n *\n * @internal\n */\nexport function mergeTransport(\n parents: readonly AstmProfile[],\n self: AstmFraming | undefined,\n): AstmFraming | undefined {\n if (self !== undefined) return self;\n for (let i = parents.length - 1; i >= 0; i--) {\n const p = parents[i];\n if (p?.transport !== undefined) return p.transport;\n }\n return undefined;\n}\n","/**\n * The definition-time error for the `@cosyte/astm` vendor-profile subsystem.\n *\n * A profile is validated when it is *defined*, not when it is applied, so a\n * misconfigured profile (a bad name, an unknown option key, a tolerated code that\n * is unknown or **safety-critical**) fails loudly at construction, long before it\n * could quiet a real deviation. This is the class that failure throws. It is a\n * programming error (a bad profile literal), distinct from `AstmParseError` (bad\n * input), so consumers narrow on it separately.\n */\n\n/**\n * Thrown by `defineAstmProfile` when a profile definition is invalid: a missing or\n * empty `name`, an unknown option key, an invalid `transport` value, or a\n * `tolerate` entry whose code is unknown, whose `rationale` is empty, or, the\n * load-bearing safety rule, whose code is **safety-critical** (a result value,\n * abnormal flag, result status, reference range, units, patient/comment context,\n * message-kind, code system, an unrecognized record type, or any frame/LTP\n * integrity warning). Carries the\n * offending profile's `name` when it is known, so a multi-profile build names the\n * culprit.\n *\n * @example\n * ```ts\n * import { defineAstmProfile, AstmProfileDefinitionError } from \"@cosyte/astm\";\n * try {\n * defineAstmProfile({\n * name: \"unsafe\",\n * tolerate: [{ code: \"ASTM_FRAME_BAD_CHECKSUM\", rationale: \"no\" }],\n * });\n * } catch (err) {\n * if (err instanceof AstmProfileDefinitionError) err.profileName; // \"unsafe\"\n * }\n * ```\n */\nexport class AstmProfileDefinitionError extends Error {\n /** The offending profile's `name`, when it could be read. */\n public readonly profileName?: string;\n\n /**\n * Construct a new `AstmProfileDefinitionError`.\n *\n * @param message - A human-readable explanation of what was invalid.\n * @param profileName - The offending profile's name, when known.\n * @internal\n */\n public constructor(message: string, profileName?: string) {\n super(message);\n this.name = \"AstmProfileDefinitionError\";\n if (profileName !== undefined) this.profileName = profileName;\n }\n}\n","/**\n * Validation helpers for `defineAstmProfile`. Every validator returns `void` on\n * success and throws {@link AstmProfileDefinitionError} on failure. The name\n * validator is split out so the factory can call it FIRST and pass `opts.name` to\n * every subsequent throw site (a bad-tolerance error should name the profile it\n * came from).\n *\n * The load-bearing check is {@link validateTolerations}: it refuses any `tolerate`\n * entry whose code is unknown, whose rationale is empty, or, the safety rule,\n * whose code is **safety-critical** (default-deny; see `src/profiles/safety.ts`).\n * That refusal is what lets the rest of the system treat \"an active profile\" as\n * safe by construction: it can only ever quiet a benign, value-preserving warning.\n *\n * Zero runtime deps: inlined Levenshtein for the \"did you mean?\" hint.\n *\n * @internal\n */\n\nimport { AstmProfileDefinitionError } from \"./errors.js\";\nimport { ALL_ASTM_WARNING_CODES, isSafetyCriticalCode } from \"./safety.js\";\nimport type { AstmQuirkTolerance, DefineAstmProfileOptions } from \"./types.js\";\n\n/**\n * Known top-level option keys accepted by `defineAstmProfile`. Any key outside this\n * list throws with an optional Levenshtein-based \"did you mean?\" hint.\n *\n * @internal\n */\nconst KNOWN_OPTION_KEYS: readonly string[] = [\n \"name\",\n \"description\",\n \"tolerate\",\n \"transport\",\n \"provenance\",\n \"extends\",\n];\n\n/** The two legal {@link AstmFraming} values a profile's `transport` may take. */\nconst KNOWN_TRANSPORTS: readonly string[] = [\"framed\", \"raw\"];\n\n/**\n * Iterative DP Levenshtein distance. Zero-dep, ≤15 LoC; used only for the\n * unknown-option-key hint.\n *\n * @internal\n */\nfunction levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n let prev: number[] = [];\n for (let j = 0; j <= b.length; j++) prev.push(j);\n for (let i = 1; i <= a.length; i++) {\n const curr: number[] = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;\n curr.push(Math.min((prev[j] ?? 0) + 1, (curr[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost));\n }\n prev = curr;\n }\n return prev[b.length] ?? 0;\n}\n\n/**\n * Validate the profile NAME (fail-fast). Throws on null/undefined opts, a\n * non-string name, or an empty/whitespace-only name.\n *\n * @internal\n */\nexport function validateProfileName(opts: DefineAstmProfileOptions): void {\n if (opts === null || opts === undefined) {\n throw new AstmProfileDefinitionError(\n `defineAstmProfile: options is required and must be an object. Received: ${String(opts)}.`,\n );\n }\n if (typeof opts.name !== \"string\") {\n throw new AstmProfileDefinitionError(\n \"defineAstmProfile: 'name' is required and must be a non-empty string. \" +\n `Received: ${JSON.stringify((opts as { name?: unknown }).name)}.`,\n );\n }\n if (opts.name.trim().length === 0) {\n throw new AstmProfileDefinitionError(\n \"defineAstmProfile: 'name' is required and must be a non-empty string. \" +\n `Received: ${JSON.stringify(opts.name)}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate TOP-LEVEL option keys. Throws on any unknown key with a\n * Levenshtein-based hint when the edit distance to a known key is ≤ 2.\n *\n * @internal\n */\nexport function validateOptionKeys(opts: DefineAstmProfileOptions): void {\n for (const key of Object.keys(opts)) {\n if (KNOWN_OPTION_KEYS.includes(key)) continue;\n let hint: string | undefined;\n for (const known of KNOWN_OPTION_KEYS) {\n if (levenshtein(key, known) <= 2) {\n hint = known;\n break;\n }\n }\n throw new AstmProfileDefinitionError(\n `Profile '${opts.name}' has unknown option key '${key}'. ` +\n (hint !== undefined ? `Did you mean '${hint}'? ` : \"\") +\n `Known keys: ${KNOWN_OPTION_KEYS.join(\", \")}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate the optional `transport` override: absent, or exactly `\"framed\"` /\n * `\"raw\"`. A profile that forces framing must force a real framing.\n *\n * @internal\n */\nexport function validateTransport(opts: DefineAstmProfileOptions): void {\n if (opts.transport === undefined) return;\n if (typeof opts.transport !== \"string\" || !KNOWN_TRANSPORTS.includes(opts.transport)) {\n throw new AstmProfileDefinitionError(\n `Profile '${opts.name}' has an invalid 'transport' ${JSON.stringify(opts.transport)}. ` +\n `It must be one of: ${KNOWN_TRANSPORTS.join(\", \")}.`,\n opts.name,\n );\n }\n}\n\n/**\n * Validate a `tolerate` list: every entry's `code` must be a real warning code,\n * must **not** be safety-critical (default-deny), and must carry a non-empty\n * `rationale` (a tolerated deviation without a stated, grounded reason is exactly\n * the \"invented quirk\" the anti-invention rule forbids). Runs post-merge so a\n * tolerance inherited from a rogue parent is caught too.\n *\n * @internal\n */\nexport function validateTolerations(\n tolerate: readonly AstmQuirkTolerance[],\n profileName: string,\n): void {\n for (const t of tolerate) {\n if (typeof t.code !== \"string\" || !ALL_ASTM_WARNING_CODES.has(t.code)) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' tolerate entry has unknown warning code ${JSON.stringify(t.code)}. ` +\n `Only real codes from the record / frame / LTP registries may be named.`,\n profileName,\n );\n }\n if (isSafetyCriticalCode(t.code)) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' may not tolerate '${t.code}': it is a safety-critical warning ` +\n `code (a result value / flag / status / range / units, patient or comment context, ` +\n `message-kind ambiguity, code system, an unrecognized record type, or a frame / LTP ` +\n `integrity warning). A profile quiets benign structural noise, never a deviation that could ` +\n `change a clinical reading, report a lost message boundary, or corrupt the wire: it can ` +\n `never make a bad checksum \"ok\", a cancelled result read \"final\", or quiet the warning ` +\n `that says two messages were read as one.`,\n profileName,\n );\n }\n if (typeof t.rationale !== \"string\" || t.rationale.trim().length === 0) {\n throw new AstmProfileDefinitionError(\n `Profile '${profileName}' tolerate entry for '${t.code}' needs a non-empty 'rationale' ` +\n `documenting the real, grounded deviation it expects.`,\n profileName,\n );\n }\n }\n}\n","/**\n * `defineAstmProfile()`: the public factory for building immutable\n * {@link AstmProfile} objects with the safety rules enforced and `describe()`\n * attached. Mirrors the sibling `@cosyte/hl7` `defineProfile()` / `@cosyte/ccda`\n * `defineCcdaProfile()` shape (name / lineage / `extends`-merge / `describe`) while\n * modelling ASTM quirks (a tolerated warning code with provenance, plus the\n * optional raw-vs-framed transport override) rather than HL7 v2 Z-segments or\n * C-CDA template deviations.\n *\n * Zero runtime deps. No `any`; immutability at the return boundary via\n * `Object.freeze` (top-level, matching the sibling's boundary-freeze doctrine).\n */\n\nimport { buildDescribe } from \"./describe.js\";\nimport {\n mergeDescription,\n mergeLineage,\n mergeProvenance,\n mergeTolerations,\n mergeTransport,\n normaliseParents,\n} from \"./merge.js\";\nimport type { AstmProfile, DefineAstmProfileOptions } from \"./types.js\";\nimport {\n validateOptionKeys,\n validateProfileName,\n validateTolerations,\n validateTransport,\n} from \"./validate.js\";\n\n/**\n * Build a frozen {@link AstmProfile} from a validated options object. Throws\n * {@link AstmProfileDefinitionError} on a bad name, an unknown option key, an\n * invalid `transport`, or an invalid `tolerate` entry, including the **safety\n * rule**: a profile may never tolerate a safety-critical warning code (default-deny\n * across all three registries).\n *\n * `extends` composes profiles: lineage, `tolerate`, `transport`, `provenance`, and\n * `description` merge (parents left-to-right, then self; scalars are child-wins).\n * The merged `tolerate` set is re-validated so a safety-critical code cannot sneak\n * in via a hand-crafted parent.\n *\n * @param opts - The profile definition; see {@link DefineAstmProfileOptions}.\n * @returns A frozen, immutable profile with `describe()` attached.\n * @throws {@link AstmProfileDefinitionError} on any invalid definition.\n * @example\n * ```ts\n * import { defineAstmProfile } from \"@cosyte/astm\";\n * const site = defineAstmProfile({\n * name: \"acme-analyzer\",\n * description: \"Acme's inbound raw-TCP analyzer\",\n * transport: \"raw\",\n * tolerate: [\n * { code: \"ASTM_NONSTANDARD_DELIMITERS\", rationale: \"declares its own delimiter set\" },\n * ],\n * provenance: { source: \"Acme host-interface manual\", reference: \"internal-2026\" },\n * });\n * site.lineage; // [\"acme-analyzer\"]\n * console.log(site.describe?.());\n * ```\n */\nexport function defineAstmProfile(opts: DefineAstmProfileOptions): AstmProfile {\n // Fail-fast on name so every downstream throw can name the profile.\n validateProfileName(opts);\n validateOptionKeys(opts);\n validateTransport(opts);\n\n const selfTolerate = opts.tolerate ?? [];\n // Pre-merge validation surfaces the offending profile's own name.\n validateTolerations(selfTolerate, opts.name);\n\n const parents = normaliseParents(opts.extends);\n const lineage = mergeLineage(parents, opts.name);\n const tolerate = mergeTolerations(parents, selfTolerate);\n const transport = mergeTransport(parents, opts.transport);\n const provenance = mergeProvenance(parents, opts.provenance);\n const description = mergeDescription(parents, opts.description);\n\n // Post-merge re-validation: a safety-critical code inherited from a hand-crafted\n // parent is refused here.\n validateTolerations(tolerate, opts.name);\n\n type Mutable<T> = { -readonly [K in keyof T]?: T[K] };\n const profile: Mutable<AstmProfile> = {\n name: opts.name,\n lineage,\n tolerate,\n };\n if (description !== undefined) profile.description = description;\n if (transport !== undefined) profile.transport = transport;\n if (provenance !== undefined) profile.provenance = provenance;\n\n const finalised = profile as AstmProfile;\n profile.describe = (): string => buildDescribe(finalised);\n\n return Object.freeze(profile) as AstmProfile;\n}\n","/**\n * The `default` conservative baseline profile. Tolerates **nothing** and forces no\n * transport: every deviation surfaces as its own warning, unmodified, and framing\n * is auto-detected. It exists so \"no profile\" and \"the default profile\" are the\n * same, explicit behaviour, and so a consumer can name the baseline when composing\n * (`extends: astmProfiles.default`) without importing a special sentinel. Absence of\n * a profile means this conservative default applied: not that a stream was fully\n * understood.\n *\n * Authored through the public `defineAstmProfile()` API: zero privileged coupling;\n * it is exactly what a user would write.\n */\n\nimport { defineAstmProfile } from \"./define.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * The conservative baseline profile: no tolerated quirks, no transport override.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.default });\n * // identical to parseAstmRecords(raw) with no profile.\n * ```\n */\nexport const defaultProfile: AstmProfile = defineAstmProfile({\n name: \"default\",\n description: \"Conservative baseline: tolerates nothing; every deviation surfaces as a warning.\",\n tolerate: [],\n});\n","/**\n * The `referenceCorpus` profile: a **non-vendor**, evidence-backed tolerance\n * grounded firsthand in the **redistributable OSS reference corpus**, not an\n * invented per-vendor deviation matrix.\n *\n * Grounding (firsthand-verified 2026-07-21; the corpus is permissively licensed):\n *\n * - **`kxepal/python-astm` `codec.py`** (BSD), its `decode_record` /\n * `decode_component` / `decode_repeated_component` split fields, components, and\n * repeats on the raw delimiters and **never un-escape** ASTM escape sequences: the\n * source contains zero handling of `&F&`/`&S&`/`&R&`/`&E&`, and the escape\n * character is treated as ordinary data. `senaite.astm` / `senaite.lis2a` inherit\n * the same escape-agnostic decode.\n * - Consequence, and what this profile expects: a stream produced or relayed by a\n * toolchain built on that escape-agnostic reference stack routinely carries a bare\n * or non-standard `&…&` body inside a free-text field (units, comments, test\n * names) that our **escape-aware** tokenizer flags as\n * `ASTM_UNKNOWN_ESCAPE_SEQUENCE`. We are deliberately stricter than the OSS\n * references here, so the warning is *expected*\n * noise when talking to that ecosystem: recognizable and non-clinical.\n *\n * **Why this is safe.** The escape codec **preserves an unrecognized `&…&` body\n * byte-for-byte** in the decoded value (`decodeEscapes` never guesses); the value a\n * consumer reads is identical whether or not this profile is active. The profile\n * only re-badges the *warning*: no value, flag, status, or identifier is altered\n * or dropped. And by the safety gate in `defineAstmProfile`, it **cannot** tolerate\n * a result value / flag / status / range / units, a patient or comment context, a\n * message-kind ambiguity, an unrecognized record type, or any frame / LTP integrity\n * warning: those always surface, profile or not. The unrecognized-record-type case\n * matters because message grouping reads a record's type letter, so a letter the\n * reader does not recognize may be a header, and a header read as something else\n * joins two messages into one.\n *\n * **Honesty.** This is a *reference-corpus* profile, not a claim about any named\n * analyzer vendor. No per-vendor (\"cobas does X\") behaviour is asserted; a named\n * vendor profile awaits a firsthand vendor-attributed quirk document.\n */\n\nimport { defineAstmProfile } from \"./define.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * Tolerates the non-standard-escape noise that streams from the escape-agnostic OSS\n * reference stack (`python-astm` / `senaite`) carry: the value is preserved\n * verbatim; only the syntactic warning is expected.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles, WARNING_CODES } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.referenceCorpus });\n * // an unknown-escape deviation arrives as PROFILE_QUIRK_APPLIED (expected), not a bare warning:\n * const quirks = msg.warnings.filter((w) => w.code === WARNING_CODES.PROFILE_QUIRK_APPLIED);\n * ```\n */\nexport const referenceCorpus: AstmProfile = defineAstmProfile({\n name: \"referenceCorpus\",\n description:\n \"Non-standard-escape tolerance grounded firsthand in the escape-agnostic OSS reference corpus \" +\n \"(python-astm / senaite). Mostly syntactic encoding noise, with one measured exception: an \" +\n \"escape sequence whose body is itself a delimiter is an opaque atom, so that delimiter does \" +\n \"not split and a result can lose its units and its status. Read the values; do not rely on \" +\n \"this profile leaving only cosmetic deviations behind.\",\n provenance: {\n source: \"kxepal/python-astm codec.py (BSD) + senaite.astm / senaite.lis2a\",\n reference:\n \"https://github.com/kxepal/python-astm/blob/master/astm/codec.py ; \" +\n \"https://github.com/senaite/senaite.astm\",\n retrieved: \"2026-07-21\",\n note: \"Firsthand-read: decode_record/decode_component/decode_repeated_component split on raw delimiters and never un-escape &F&/&S&/&R&/&E&.\",\n },\n tolerate: [\n {\n code: \"ASTM_UNKNOWN_ESCAPE_SEQUENCE\",\n rationale:\n \"The OSS reference stack (python-astm codec.py, firsthand-verified) treats '&' as literal data \" +\n \"and never decodes escape sequences, so streams from that ecosystem carry non-standard '&…&' \" +\n \"bodies our escape-aware tokenizer flags. The body is preserved byte-for-byte in the value. \" +\n \"NOT always non-clinical, and this is measured rather than assumed: where the body is a \" +\n \"delimiter, that delimiter never became a field boundary, so `28.6&|&U/L` reads as one \" +\n \"value with no units and no final status. Tolerating this code hides that too.\",\n },\n ],\n});\n","/**\n * The profile **registry**: the named built-in set (with provenance) plus a\n * process-scoped default. Mirrors the sibling `@cosyte/hl7` / `@cosyte/ccda`\n * `set/getDefault*Profile` convention. `parseAstmRecords(raw)` with no explicit\n * `profile` option consults {@link getDefaultAstmProfile}; an explicit `profile`\n * option always wins, and `profile: null` opts out of the default for a single call.\n *\n * The single mutable module-scoped `let` is the only such state in the profile\n * subsystem: an intentional, documented trade-off (identical to the siblings).\n * Tests that set a default MUST clear it in teardown (`setDefaultAstmProfile(null)`)\n * to avoid cross-test bleed.\n */\n\nimport { defaultProfile } from \"./default.js\";\nimport { referenceCorpus } from \"./reference-corpus.js\";\nimport type { AstmProfile } from \"./types.js\";\n\n/**\n * The built-in profiles, keyed by name. Each carries its own provenance (except the\n * conservative `default` baseline).\n *\n * @internal\n */\nconst BUILT_INS: ReadonlyMap<string, AstmProfile> = new Map<string, AstmProfile>([\n [defaultProfile.name, defaultProfile],\n [referenceCorpus.name, referenceCorpus],\n]);\n\n/**\n * Look up a built-in profile by name. Returns `undefined` when no built-in has that\n * name (a user-defined profile is not in this registry, pass it directly).\n *\n * @param name - The built-in profile name.\n * @returns The profile, or `undefined`.\n * @example\n * ```ts\n * import { getAstmProfile } from \"@cosyte/astm\";\n * getAstmProfile(\"referenceCorpus\")?.provenance?.source;\n * ```\n */\nexport function getAstmProfile(name: string): AstmProfile | undefined {\n return BUILT_INS.get(name);\n}\n\n/**\n * The names of every built-in profile, in registration order.\n *\n * @returns The built-in names.\n * @example\n * ```ts\n * import { listAstmProfiles } from \"@cosyte/astm\";\n * listAstmProfiles(); // [\"default\", \"referenceCorpus\"]\n * ```\n */\nexport function listAstmProfiles(): readonly string[] {\n return Object.freeze([...BUILT_INS.keys()]);\n}\n\n/**\n * Process-scoped default profile. `undefined` means \"unset\": `parseAstmRecords`\n * applies no profile in that state.\n *\n * @internal\n */\nlet _defaultProfile: AstmProfile | undefined = undefined;\n\n/**\n * Register a process-scoped default profile that `parseAstmRecords(raw)` applies\n * when no explicit `profile` option is passed. Pass `null` (or `undefined`) to\n * clear. An explicit `parseAstmRecords(raw, { profile })` always wins;\n * `{ profile: null }` opts out of the default for a single call.\n *\n * **Test hygiene:** the only mutable module-scoped state here, tests that call this\n * MUST clear it in teardown or default-profile bleed infects later tests.\n *\n * @param profile - The profile to register as default, or `null` to clear.\n * @example\n * ```ts\n * import { setDefaultAstmProfile, astmProfiles, parseAstmRecords } from \"@cosyte/astm\";\n * setDefaultAstmProfile(astmProfiles.referenceCorpus);\n * const msg = parseAstmRecords(raw); // uses referenceCorpus\n * setDefaultAstmProfile(null); // clear\n * ```\n */\nexport function setDefaultAstmProfile(profile: AstmProfile | null): void {\n _defaultProfile = profile ?? undefined;\n}\n\n/**\n * Return the current process-scoped default profile, or `undefined` if none is\n * registered.\n *\n * @returns The default profile, or `undefined`.\n * @example\n * ```ts\n * import { getDefaultAstmProfile } from \"@cosyte/astm\";\n * const p = getDefaultAstmProfile();\n * if (p !== undefined) console.log(\"default profile:\", p.name);\n * ```\n */\nexport function getDefaultAstmProfile(): AstmProfile | undefined {\n return _defaultProfile;\n}\n","/**\n * The ASTM escape codec: the fix for a documented silent-misread class.\n *\n * ASTM escapes an embedded delimiter with the escape character on both sides of\n * a single mnemonic letter (the InterSystems mnemonics, cross-verified against\n * the OSS reference implementations):\n *\n * ```\n * &F& → field delimiter (default \"|\")\n * &S& → component delimiter (default \"^\")\n * &R& → repeat delimiter (default \"\\\")\n * &E& → escape character (default \"&\")\n * ```\n *\n * **Why this is a requirement, not a nicety.** A result value can legitimately\n * contain a component delimiter: e.g. a titre written `1^40`. On the wire that\n * is escaped as `1&S&40`. A parser that splits a field into components on the\n * raw component delimiter and *never decodes the escape* is safe here (the\n * escape body `&S&` contains no literal `^`), but a parser that **decodes first\n * and splits second** turns `1&S&40` into `1^40` and then mis-splits it into two\n * components: the silent misread `python-astm` / `Chistousov` exhibit. The\n * correct order, implemented here, is **escape-aware split, then decode each\n * leaf**: the escape sequence survives the split intact and only then decodes to\n * a single literal, so `1&S&40` reads as exactly one component.\n *\n * **An escape sequence is exactly three characters** (the escape character, one\n * body character, and the escape character again), and both functions here agree\n * on that, because they have to: a split that thinks `&X…&` is one atom while the\n * decoder thinks otherwise is the same class of mis-read the ordering above\n * exists to prevent. An escape character that does not sit at the head of such a\n * triple is not an escape at all: it is read as the **literal character it is**,\n * it opens no atom, and the parse layer reports it\n * (`ASTM_UNPAIRED_ESCAPE_CHARACTER`). Both functions used to scan forward for the\n * next escape character with no bound, which meant a single unescaped `&` in a\n * value merged every field after it into that value, in silence.\n *\n * **The atom rule is unchanged and still costs a boundary in one case.** A triple\n * is opaque by design, so where its body is itself a delimiter (`&|&` under the\n * canonical set) that delimiter does not split. That is deliberate: it is what\n * keeps `&F&` one token under a declared set that names `F` as a delimiter. Where\n * the body is not a recognized mnemonic the parse layer reports it as\n * `ASTM_UNKNOWN_ESCAPE_SEQUENCE`, so it is never silent, but that code is\n * tolerable and a field boundary has still gone. Do not \"fix\" that by narrowing\n * the atom: the narrowing would break the guarantee the atom exists for.\n *\n * Re-escaping (the inverse, for spec-clean emit) lives in the serializer and is\n * deliberately not implemented here.\n */\n\nimport type { Delimiters } from \"./delimiters.js\";\n\n/**\n * A callback the codec calls when it encounters an escape sequence whose body is\n * not one of the four recognized mnemonics. The sequence is preserved verbatim;\n * the callback lets the parser surface a value-free `ASTM_UNKNOWN_ESCAPE_SEQUENCE`\n * warning. Optional so the codec can be used purely.\n */\nexport type UnknownEscapeSink = () => void;\n\n/**\n * A callback the codec calls when it encounters an escape character that does not\n * head a three-character escape sequence. The character is preserved verbatim as a\n * literal; the callback lets the parser surface a value-free\n * `ASTM_UNPAIRED_ESCAPE_CHARACTER` warning. Optional so the codec can be used purely.\n */\nexport type UnpairedEscapeSink = () => void;\n\n/**\n * Whether an escape sequence starts at `i`: the escape character, exactly one body\n * character, and the escape character again.\n *\n * **The bound is the point.** Scanning forward for the next escape character with\n * no bound lets an escape character that was never meant as one reach across an\n * arbitrary amount of the record and turn every delimiter it spans into ordinary\n * text. The four mnemonic bodies are single characters, so one is all a real\n * sequence ever needs, and reading no further keeps the decision local: whether a\n * field splits can no longer depend on some later field happening to carry an\n * escape character too.\n *\n * `charAt` returns `\"\"` past the end, which is never equal to a delimiter, so an\n * escape character in the last two positions of `text` is correctly not a sequence.\n */\nfunction isEscapeSequenceAt(text: string, i: number, escape: string): boolean {\n return text.charAt(i) === escape && text.charAt(i + 2) === escape;\n}\n\n/**\n * Decode the four recognized ASTM escape mnemonics in a single already-split\n * leaf (a component string), substituting the *active* delimiters read from the\n * header. Unrecognized `&X&` bodies are preserved verbatim and reported through\n * `onUnknown` (never dropped, never guessed), and an escape character that heads\n * no sequence at all is preserved as a literal and reported through `onUnpaired`.\n *\n * This runs **after** splitting, so a decoded delimiter becomes ordinary literal\n * text and can never introduce a new split boundary.\n *\n * @param leaf - One component string, escape-aware split already applied.\n * @param d - The delimiters resolved from the header.\n * @param onUnknown - Called once per unrecognized escape body encountered.\n * @param onUnpaired - Called once per escape character that heads no sequence.\n * @returns The decoded string.\n * @example\n * ```ts\n * import { decodeEscapes, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * decodeEscapes(\"1&S&40\", CANONICAL_DELIMITERS); // \"1^40\"\n * decodeEscapes(\"O&Brien\", CANONICAL_DELIMITERS); // \"O&Brien\" (literal, reported)\n * ```\n */\nexport function decodeEscapes(\n leaf: string,\n d: Delimiters,\n onUnknown?: UnknownEscapeSink,\n onUnpaired?: UnpairedEscapeSink,\n): string {\n const esc = d.escape;\n if (!leaf.includes(esc)) return leaf;\n\n let out = \"\";\n let i = 0;\n while (i < leaf.length) {\n const ch = leaf.charAt(i);\n if (ch !== esc) {\n out += ch;\n i += 1;\n continue;\n }\n if (!isEscapeSequenceAt(leaf, i, esc)) {\n // An escape character heading no sequence: keep it as the literal character it is,\n // report it, and carry on. Never swallow the remainder of the leaf behind it.\n out += ch;\n i += 1;\n onUnpaired?.();\n continue;\n }\n const body = leaf.charAt(i + 1);\n const replacement = escapeBody(body, d);\n if (replacement === undefined) {\n // Unrecognized escape: preserve the whole `&X&` verbatim, surface it, never guess.\n out += esc + body + esc;\n onUnknown?.();\n } else {\n out += replacement;\n }\n i += 3;\n }\n return out;\n}\n\n/** Map one escape body (the char(s) between the escape delimiters) to its literal, or undefined. */\nfunction escapeBody(body: string, d: Delimiters): string | undefined {\n switch (body) {\n case \"F\":\n return d.field;\n case \"S\":\n return d.component;\n case \"R\":\n return d.repeat;\n case \"E\":\n return d.escape;\n default:\n return undefined;\n }\n}\n\n/**\n * Split `text` on `delimiter`, treating an escape sequence (escape, one body\n * character, escape) as an opaque atom so a delimiter that appears *inside* an\n * escape body never causes a split. Returns the raw (still-encoded) segments:\n * decoding is the caller's next step, per the escape-aware-split-then-decode\n * contract.\n *\n * For the four canonical mnemonics the opacity is belt-and-suspenders (their\n * bodies are letters, not delimiters), but it makes the \"an escaped delimiter is\n * one token\" guarantee hold for any declared delimiter set, including adversarial\n * input. A single body character is all that guarantee needs.\n *\n * An escape character that heads no sequence is **not** an escape: it is ordinary\n * text, and it opens no atom. Reading it as the opening of a sequence that never\n * closes is what used to merge the whole remainder of a record into one field.\n * Decoding the resulting leaf is what reports it, so this function stays a pure\n * split. Note the two rules together: a delimiter after such a character does\n * split, and a delimiter sitting inside a real three-character atom does not.\n *\n * @param text - The field or repeat string to split.\n * @param delimiter - The delimiter to split on.\n * @param escape - The active escape character.\n * @returns The raw segments, in order.\n * @example\n * ```ts\n * import { splitEscapeAware } from \"@cosyte/astm\";\n * splitEscapeAware(\"a^b^c\", \"^\", \"&\"); // [\"a\", \"b\", \"c\"]\n * splitEscapeAware(\"1&S&40\", \"^\", \"&\"); // [\"1&S&40\"] (escape body is opaque)\n * splitEscapeAware(\"O&Brien^John\", \"^\", \"&\"); // [\"O&Brien\", \"John\"] (unpaired: literal)\n * ```\n */\nexport function splitEscapeAware(text: string, delimiter: string, escape: string): string[] {\n if (text.length === 0) return [\"\"];\n const out: string[] = [];\n let current = \"\";\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === escape && isEscapeSequenceAt(text, i, escape)) {\n // Copy the whole escape sequence verbatim; do not inspect it for delimiters.\n current += text.slice(i, i + 3);\n i += 3;\n continue;\n }\n if (ch === delimiter) {\n out.push(current);\n current = \"\";\n i += 1;\n continue;\n }\n current += ch;\n i += 1;\n }\n out.push(current);\n return out;\n}\n","/**\n * Record tokenizer: split one raw ASTM record into typed {@link AstmField}s.\n *\n * The split is **escape-aware first, decode second** (see `../common/escapes.ts`):\n * a field / repeat / component boundary is only recognized on an *unescaped*\n * delimiter, and each resulting component leaf is decoded afterwards. That\n * ordering is the whole reason an embedded escaped component delimiter (`&S&`)\n * reads as a single component instead of being mis-split.\n */\n\nimport type { Delimiters } from \"../common/delimiters.js\";\nimport { decodeEscapes, splitEscapeAware } from \"../common/escapes.js\";\nimport type { AstmField } from \"./types.js\";\n\n/**\n * Tokenize a single record string (its terminator already stripped) into its\n * fields. `fields[0]` is the type-letter field; ASTM data fields follow at\n * 1-based indices.\n *\n * @param record - The raw record text.\n * @param d - The delimiters resolved from the header.\n * @param onUnknownEscape - Called (with the 0-based field index) for each\n * unrecognized escape sequence encountered, so the caller can warn.\n * @param onUnpairedEscape - Called (with the 0-based field index) for each escape\n * character that heads no escape sequence and was therefore read as a literal.\n * @returns The record's fields.\n * @example\n * ```ts\n * import { tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeRecord(\"R|1|^^^687|28.6|U/L\", CANONICAL_DELIMITERS);\n * fields[3].components[0]; // \"28.6\"\n * ```\n */\nexport function tokenizeRecord(\n record: string,\n d: Delimiters,\n onUnknownEscape?: (fieldIndex: number) => void,\n onUnpairedEscape?: (fieldIndex: number) => void,\n): AstmField[] {\n const rawFields = splitEscapeAware(record, d.field, d.escape);\n return rawFields.map((raw, fieldIndex) =>\n toField(\n raw,\n d,\n () => onUnknownEscape?.(fieldIndex),\n () => onUnpairedEscape?.(fieldIndex),\n ),\n );\n}\n\n/**\n * Tokenize an `H` (header) record into its fields.\n *\n * A header cannot go through {@link tokenizeRecord}: its second field is the\n * **delimiter declaration**, which carries all three non-field delimiters\n * *literally* rather than as escape sequences. Run through the generic tokenizer\n * the declaration would be split on its own repeat and component characters and\n * its escape character would be decoded and reported as unpaired, so what the\n * header declares would come back as fragments plus a spurious warning. This\n * tokenizer instead takes the declaration verbatim as one opaque field, never\n * decoded, and applies the ordinary escape-aware tokenizer to the data portion\n * that follows it.\n *\n * `fields[0]` is the type-letter field and `fields[1]` is the delimiter\n * declaration (verbatim, never escape-decoded); the header's ASTM data fields\n * follow from `fields[2]`.\n *\n * @param record - The raw `H` record text (terminator already stripped).\n * @param d - The delimiters declared by this header.\n * @param onUnknownEscape - Called with the 0-based whole-record field index for\n * each unrecognized escape sequence in the data portion.\n * @param onUnpairedEscape - Called with the 0-based whole-record field index for\n * each unpaired escape character in the data portion. The declaration itself is\n * opaque, so the escape character it names never reports here.\n * @returns The header's fields.\n * @example\n * ```ts\n * import { tokenizeHeader, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeHeader(\"H|\\\\^&|||sender\", CANONICAL_DELIMITERS);\n * fields[1].raw; // \"\\\\^&\"\n * fields[4].raw; // \"sender\"\n * ```\n */\nexport function tokenizeHeader(\n record: string,\n d: Delimiters,\n onUnknownEscape?: (fieldIndex: number) => void,\n onUnpairedEscape?: (fieldIndex: number) => void,\n): AstmField[] {\n // The delimiter-definition field runs from index 2 to the next field separator.\n const defEnd = record.indexOf(d.field, 2);\n const definition = defEnd === -1 ? record.slice(2) : record.slice(2, defEnd);\n const head = [opaqueField(record.slice(0, Math.min(1, record.length))), opaqueField(definition)];\n if (defEnd === -1) return head;\n // Data fields start at whole-record index 2, so shift the tokenizer's local index by 2.\n const data = tokenizeRecord(\n record.slice(defEnd + 1),\n d,\n (i) => onUnknownEscape?.(i + 2),\n (i) => onUnpairedEscape?.(i + 2),\n );\n return [...head, ...data];\n}\n\n/** A field whose text is structural, not data: surfaced verbatim as one component, never decoded. */\nfunction opaqueField(raw: string): AstmField {\n return { raw, components: [raw], repeats: [[raw]] };\n}\n\n/** Build one {@link AstmField} from its raw wire text: split into repeats → components, then decode. */\nfunction toField(\n raw: string,\n d: Delimiters,\n onUnknownEscape: () => void,\n onUnpairedEscape: () => void,\n): AstmField {\n const rawRepeats = splitEscapeAware(raw, d.repeat, d.escape);\n const repeats = rawRepeats.map((rep) =>\n splitEscapeAware(rep, d.component, d.escape).map((comp) =>\n decodeEscapes(comp, d, onUnknownEscape, onUnpairedEscape),\n ),\n );\n // `splitEscapeAware` always returns at least one element, so `repeats[0]` is defined.\n const components = repeats[0] ?? [\"\"];\n return { raw, components, repeats };\n}\n\n/**\n * The primary scalar of a field: its first repeat's first component, decoded.\n * Returns `undefined` for a truly empty field so callers can distinguish\n * \"absent\" from a value: never defaulting a missing value.\n *\n * @example\n * ```ts\n * import { fieldScalar, tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const f = tokenizeRecord(\"R|1|^^^687|28.6\", CANONICAL_DELIMITERS);\n * fieldScalar(f[3]); // \"28.6\"\n * ```\n */\nexport function fieldScalar(field: AstmField | undefined): string | undefined {\n if (field === undefined) return undefined;\n const first = field.components[0];\n return first !== undefined && first.length > 0 ? first : undefined;\n}\n","/**\n * The record-layer entry point: {@link parseAstmRecords}.\n *\n * It assumes **already-de-framed** record bytes; {@link parseFramedAstm} composes\n * the E1381/LIS01 framing layer with this one. It reads the four delimiters from each\n * `H` record, tokenizes every record escape-aware, and builds the immutable\n * {@link AstmMessage}, lenient by default: vendor quirks become typed warnings, and\n * only three unrecoverable structural conditions are fatal.\n */\n\nimport { AstmParseError, FATAL_CODES } from \"../common/errors.js\";\nimport { deepFreeze } from \"../common/freeze.js\";\nimport { isNonStandard, readDelimiters, type Delimiters } from \"../common/delimiters.js\";\nimport { parseAstmDate, type AstmDate } from \"../common/dates.js\";\nimport { recognizeUniversalTestId } from \"../common/coding-system.js\";\nimport {\n ambiguousMessageKind,\n ambiguousValueSplit,\n delimitersRedeclared,\n fieldsUnseparated,\n nonStandardDelimiters,\n orphanComment,\n partialTimestamp,\n undefinedAbnormalFlag,\n undefinedResultStatus,\n uninterpretedQueryStatus,\n unitsAbsent,\n unknownEscapeSequence,\n unpairedEscapeCharacter,\n unknownRecordType,\n unparseableReferenceRange,\n unreadableRedeclaration,\n type AstmRecordWarning,\n} from \"../common/warnings.js\";\nimport {\n interpretAbnormalFlag,\n interpretResultStatus,\n parseReferenceRange,\n} from \"./result-semantics.js\";\nimport { classifyMessage } from \"./host-query.js\";\nimport { applyAstmProfileToWarnings } from \"../profiles/apply.js\";\nimport { getDefaultAstmProfile } from \"../profiles/registry.js\";\nimport type { AstmProfile } from \"../profiles/types.js\";\nimport { fieldScalar, tokenizeHeader, tokenizeRecord } from \"./tokenize.js\";\nimport type {\n AstmField,\n AstmMessage,\n AstmParseOptions,\n AstmRecord,\n CommentRecord,\n HeaderRecord,\n ManufacturerRecord,\n OrderRecord,\n PatientName,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n ScientificRecord,\n TerminatorRecord,\n UnsupportedRecord,\n} from \"./types.js\";\n\n/**\n * Thrown by {@link parseAstmRecords} in `strict` mode when the lenient parser\n * would otherwise have accumulated one or more Tier-2 warnings. Carries the\n * warnings (code + position, never a value) so a caller can see every deviation.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, AstmStrictError } from \"@cosyte/astm\";\n * try {\n * parseAstmRecords(\"H|\\\\^&\\rZ|1\\r\", { strict: true });\n * } catch (err) {\n * if (err instanceof AstmStrictError) err.warnings.length; // >= 1\n * }\n * ```\n */\nexport class AstmStrictError extends Error {\n public readonly warnings: readonly AstmRecordWarning[];\n /** @internal */\n public constructor(warnings: readonly AstmRecordWarning[]) {\n super(`Strict mode: ${String(warnings.length)} tolerated deviation(s) rejected.`);\n this.name = \"AstmStrictError\";\n this.warnings = warnings;\n }\n}\n\n/**\n * Parse an ASTM/CLSI-LIS02 record stream into an immutable {@link AstmMessage}.\n *\n * The stream is a sequence of records separated by `CR` (with `LF`/`CRLF`\n * tolerated); the first record must be an `H` header, which declares the\n * delimiters. Lenient by default: set `strict` to reject any tolerated deviation.\n *\n * **Several messages in one stream.** A message runs `H` … `L`, so a stream may carry\n * more than one, and each `H` declares the delimiters for the records that follow it.\n * A later header that declares a **different** set is honored from that header onward\n * and flagged `ASTM_RECORD_DELIMITERS_REDECLARED`; records already read keep the set\n * they were read with, so a redeclaration never reinterprets earlier bytes. A header\n * that merely restates the set in force is a no-op and warns nothing. A later header\n * that cannot declare a usable set keeps the delimiters already in force and warns\n * `ASTM_RECORD_UNREADABLE_REDECLARATION`: a set is never guessed, and no record is\n * dropped. Per-header sets are on each {@link HeaderRecord.delimiters};\n * {@link AstmMessage.delimiters} is the first header's.\n *\n * @param raw - The de-framed record bytes, as a string or `Uint8Array` (decoded\n * latin1 so byte values survive 1:1).\n * @param options - Parse options; lenient unless `strict` is set.\n * @returns The parsed, deeply-frozen message.\n * @throws {@link AstmParseError} on a Tier-3 fatal (`EMPTY_INPUT`,\n * `ASTM_RECORD_NO_HEADER`, `ASTM_RECORD_UNDECLARED_DELIMITERS`).\n * @throws {@link AstmStrictError} when `strict` is set and a deviation occurs.\n * @example\n * ```ts\n * import { parseAstmRecords, results } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rO|1|ACC\\rR|1|^^^687|28.6|U/L||N||F\\rL|1|N\\r\");\n * results(msg)[0]?.value; // \"28.6\"\n * ```\n */\nexport function parseAstmRecords(\n raw: string | Uint8Array,\n options: AstmParseOptions = {},\n): AstmMessage {\n const text = typeof raw === \"string\" ? raw : decodeBytes(raw);\n\n if (text.trim().length === 0) {\n throw new AstmParseError(FATAL_CODES.EMPTY_INPUT, \"Input is empty.\", { recordIndex: 0 });\n }\n\n // Records are CR-delimited (LF / CRLF tolerated). Phase 1 assumes de-framed record bytes.\n const lines = text.split(/\\r\\n|\\r|\\n/u).filter((l) => l.length > 0);\n\n const first = lines[0];\n if (first === undefined || first.charAt(0) !== \"H\") {\n const leadType = first?.charAt(0);\n throw new AstmParseError(\n FATAL_CODES.ASTM_RECORD_NO_HEADER,\n \"First record is not an H (header) record.\",\n leadType !== undefined && leadType.length > 0\n ? { recordIndex: 0, recordType: leadType }\n : { recordIndex: 0 },\n );\n }\n\n const delimiters = readDelimiters(first);\n if (delimiters === undefined) {\n throw new AstmParseError(\n FATAL_CODES.ASTM_RECORD_UNDECLARED_DELIMITERS,\n \"Header record is too short to declare the four delimiters.\",\n { recordIndex: 0, recordType: \"H\" },\n );\n }\n\n const warnings: AstmRecordWarning[] = [];\n if (isNonStandard(delimiters)) {\n warnings.push(nonStandardDelimiters({ recordIndex: 0, recordType: \"H\" }));\n }\n\n // Delimiters are scoped to a *message*, and a message runs `H` … `L`, so one stream may carry\n // several messages back to back, each header declaring its own set. The active set is therefore\n // re-read at every `H` and governs that header and the records that follow it. Reading the whole\n // stream with the FIRST header's set is what silently merged every field of a redeclared message\n // into one (ASTM-SECOND-HEADER-COLLAPSE): a lost result, with no warning.\n //\n // Forward scoping is also the only reading that never reinterprets bytes it has already consumed:\n // records before a redeclaration keep the set they were read with, so the two sets never disagree\n // about the same bytes. \"Last header wins retroactively\" would require re-reading records already\n // delivered; \"first header wins forever\" is the collapse itself.\n let active = delimiters;\n const built: AstmRecord[] = lines.map((line, recordIndex) => {\n if (recordIndex > 0 && line.charAt(0) === \"H\") {\n active = adoptRedeclaredDelimiters(line, active, recordIndex, warnings);\n }\n return buildRecord(line, recordIndex, active, warnings);\n });\n // Second pass: attach each comment to its immediately-preceding H/P/O/R parent (an orphan → root).\n const records = attachComments(built, warnings);\n\n // Host-query classification. `Q` dominates so a query is never read as a result set; a message that\n // carries BOTH a Q and an R is contradictory: classified host-query (still a request) and warned,\n // never silently treated as a result upload.\n const classification = classifyMessage(records);\n if (classification.hasQuery && classification.hasResults) {\n warnings.push(ambiguousMessageKind({ recordIndex: 0, recordType: \"H\" }));\n }\n\n // ── Phase 8: vendor profile tolerance. The profile transform runs LAST, over the\n // fully-accumulated warnings: it only ever re-badges a warning it *expects* to a\n // PROFILE_QUIRK_APPLIED (values, records, and delimiters are already built and are\n // never touched). A safety-critical deviation can never be tolerated (enforced at\n // defineAstmProfile time), so the transform can only quiet benign structural noise.\n const profile = resolveProfile(options);\n const finalWarnings = applyAstmProfileToWarnings(warnings, profile);\n\n // Strict mode escalates only the deviations that were NOT expected by a profile:\n // an expected PROFILE_QUIRK_APPLIED is known and benign, so it is recorded, not thrown.\n if (options.strict === true) {\n const escalating = finalWarnings.filter((w) => w.expected !== true);\n if (escalating.length > 0) {\n throw new AstmStrictError(escalating);\n }\n }\n\n const header = records[0] as HeaderRecord;\n const message: AstmMessage =\n profile !== undefined\n ? {\n header,\n records,\n delimiters,\n classification,\n warnings: finalWarnings,\n profile: { name: profile.name, lineage: profile.lineage },\n }\n : { header, records, delimiters, classification, warnings: finalWarnings };\n\n return deepFreeze(message);\n}\n\n/**\n * Resolve the effective profile for a parse. An explicit `options.profile` wins: an\n * {@link AstmProfile} is used as-is; `null` opts out of the process-scoped default\n * for this call. When `profile` is omitted, the process-scoped default\n * ({@link setDefaultAstmProfile}) applies, or `undefined` when none is set.\n */\nfunction resolveProfile(options: AstmParseOptions): AstmProfile | undefined {\n if (options.profile === null) return undefined;\n if (options.profile !== undefined) return options.profile;\n return getDefaultAstmProfile();\n}\n\n/**\n * Resolve the delimiter set a **subsequent** `H` record puts into force.\n *\n * Three outcomes, all fail-safe, a set is never guessed and no record is ever dropped:\n *\n * - **Unusable declaration** (too short, or the field separator also names one of the other three):\n * keep the set already in force and warn. The identical condition on the *first* header is the\n * `ASTM_RECORD_UNDECLARED_DELIMITERS` fatal, because there is no earlier set to fall back to.\n * - **Same set restated**: a no-op. A stream carrying several messages in one delimiter set is\n * ordinary, so this warns nothing: warning would be pure noise on a conformant shape.\n * - **Different set**: adopt it, and warn. This is the only case where a reader still using the old\n * set would start merging fields, so it is the only case worth a signal.\n */\nfunction adoptRedeclaredDelimiters(\n line: string,\n active: Delimiters,\n recordIndex: number,\n warnings: AstmRecordWarning[],\n): Delimiters {\n const position = { recordIndex, recordType: \"H\" };\n const declared = readDelimiters(line);\n if (declared === undefined) {\n warnings.push(unreadableRedeclaration(position));\n return active;\n }\n if (sameDelimiters(declared, active)) return active;\n warnings.push(delimitersRedeclared(position));\n if (isNonStandard(declared)) warnings.push(nonStandardDelimiters(position));\n return declared;\n}\n\n/** Whether two delimiter sets agree on all four roles. */\nfunction sameDelimiters(a: Delimiters, b: Delimiters): boolean {\n return (\n a.field === b.field &&\n a.repeat === b.repeat &&\n a.component === b.component &&\n a.escape === b.escape\n );\n}\n\n/** Decode record bytes as latin1 so every byte survives to the string 1:1 (ASTM is byte-oriented). */\nfunction decodeBytes(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) out += String.fromCharCode(b);\n return out;\n}\n\n/** ASTM fields are 1-indexed; the type letter is field 1. Map an ASTM field number to the array slot. */\nfunction astmField(fields: readonly AstmField[], n: number): AstmField | undefined {\n return fields[n - 1];\n}\n\nfunction buildRecord(\n line: string,\n recordIndex: number,\n d: Delimiters,\n warnings: AstmRecordWarning[],\n): AstmRecord {\n const rawType = line.charAt(0);\n const onUnknownEscape = (fieldIndex: number): void => {\n warnings.push(\n unknownEscapeSequence({ recordIndex, recordType: rawType, fieldIndex: fieldIndex + 1 }),\n );\n };\n const onUnpairedEscape = (fieldIndex: number): void => {\n warnings.push(\n unpairedEscapeCharacter({ recordIndex, recordType: rawType, fieldIndex: fieldIndex + 1 }),\n );\n };\n // The header needs its own tokenizer: all three non-field delimiters sit literally inside the\n // delimiter declaration, so the generic tokenizer would split the declaration on its own repeat\n // and component characters and report its escape character as unpaired.\n const fields =\n rawType === \"H\"\n ? tokenizeHeader(line, d, onUnknownEscape, onUnpairedEscape)\n : tokenizeRecord(line, d, onUnknownEscape, onUnpairedEscape);\n\n // A record that carries content beyond its type letter but yields exactly ONE field contains no\n // field separator at all, so the delimiters in force are not the set this record was written\n // with. Every modeled field of it is then absent: on an `R` that is the value, the units and the\n // status at once, which is the shape a delimiter-scoping mistake takes when it reaches a reader.\n //\n // Reported, never repaired. Re-splitting on a set the header did not declare would mean guessing\n // at bytes the sender did not send, which is the guess this package declines to make; what it\n // will not do is lose those fields in silence. Keying the check on the OBSERVED collapse rather\n // than on any one cause of it is what lets it cover routes that are not a redeclaring header at\n // all: a type letter the reader could not recognize does not re-scope delimiters either.\n //\n // ── WHAT THIS DOES NOT CATCH, which is most of the space and is why it is a report and not a\n // guarantee. It is one test on ONE of the four delimiter roles, and only in its total form:\n //\n // * A foreign set whose FIELD separator happens to occur anywhere in the line still splits, so\n // the count is 2 and nothing fires, while the fields land on the wrong boundaries. One stray\n // `|` in an otherwise `*`-separated record is enough, and the value is lost just the same.\n // This can happen to one record INSIDE a run of these warnings, so a run is not a sweep of\n // the records it spans.\n // * A set differing in the REPEAT, COMPONENT or ESCAPE role usually splits into fields normally,\n // and nothing here sees it. A mis-split component can cost a test identity while the value\n // survives. The ESCAPE role's worst case has NARROWED, not gone: an escape character heading\n // no `&X&` sequence is now a literal and is reported (`ASTM_UNPAIRED_ESCAPE_CHARACTER`)\n // instead of merging the rest of the record, but an `&X&` whose body IS a delimiter is still\n // an opaque atom, so that delimiter does not split and every field after it shifts. That one\n // is reported only by `ASTM_UNKNOWN_ESCAPE_SEQUENCE`, which a profile may tolerate.\n //\n // So the absence of this warning is NOT evidence that a record was read in its own set. Widening\n // it would mean deciding which set a record \"should\" have had, which is the same guess again.\n // The honest scope is stated on the warning code and in the shipped docs, and it is stated as a\n // limit rather than left to be inferred.\n //\n // Whitespace-only trailing content is excluded: with no non-blank byte after the type letter\n // there is no field content that could have been lost, so a padded terminator is not a report.\n //\n // A header is exempt by construction, not by exception: `tokenizeHeader` always yields the type\n // letter plus the delimiter declaration, and a header is read with the set it declares itself.\n if (fields.length === 1 && line.slice(1).trim().length > 0) {\n warnings.push(fieldsUnseparated({ recordIndex, recordType: rawType }));\n }\n\n switch (rawType) {\n case \"H\":\n return {\n type: \"H\",\n recordIndex,\n fields,\n delimiters: d,\n rawLine: line,\n } satisfies HeaderRecord;\n case \"P\":\n return buildPatient(recordIndex, fields, warnings);\n case \"O\":\n return buildOrder(recordIndex, fields);\n case \"R\":\n return buildResult(recordIndex, fields, warnings);\n case \"C\":\n return buildComment(recordIndex, fields);\n case \"Q\":\n return buildQuery(recordIndex, fields, warnings);\n // `M` (manufacturer) and `S` (scientific) are vendor-defined free-form data surfaced VERBATIM and\n // NEVER interpreted into typed clinical fields: the exact wire line is preserved for round-trip.\n case \"M\":\n return { type: \"M\", recordIndex, fields, rawLine: line } satisfies ManufacturerRecord;\n case \"S\":\n return { type: \"S\", recordIndex, fields, rawLine: line } satisfies ScientificRecord;\n case \"L\":\n return { type: \"L\", recordIndex, fields } satisfies TerminatorRecord;\n default: {\n warnings.push(unknownRecordType({ recordIndex, recordType: rawType }));\n return { type: \"unsupported\", rawType, recordIndex, fields } satisfies UnsupportedRecord;\n }\n }\n}\n\nfunction buildPatient(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): PatientRecord {\n const nameField = astmField(fields, 6);\n const name: PatientName | undefined =\n nameField !== undefined && fieldScalar(nameField) !== undefined\n ? {\n raw: nameField.raw,\n ...definedString(\"last\", nameField.components[0]),\n ...definedString(\"first\", nameField.components[1]),\n ...definedString(\"middle\", nameField.components[2]),\n }\n : undefined;\n\n const birthDate = parseDateField(\n fieldScalar(astmField(fields, 8)),\n recordIndex,\n \"P\",\n 8,\n warnings,\n );\n\n return {\n type: \"P\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n // The three patient identifiers stay DISTINCT: practice-assigned (3), laboratory-assigned (4),\n // and a third ID (5) never collapse into one; conflating them is the primary misfiling path.\n ...definedString(\"practiceAssignedId\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"laboratoryAssignedId\", fieldScalar(astmField(fields, 4))),\n ...definedString(\"patientIdThree\", fieldScalar(astmField(fields, 5))),\n ...(name !== undefined ? { name } : {}),\n ...definedString(\"mothersMaidenName\", fieldScalar(astmField(fields, 7))),\n ...(birthDate !== undefined ? { birthDate } : {}),\n ...definedString(\"sex\", fieldScalar(astmField(fields, 9))),\n };\n}\n\nfunction buildOrder(recordIndex: number, fields: readonly AstmField[]): OrderRecord {\n const utidField = astmField(fields, 5);\n return {\n type: \"O\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...definedString(\"specimenId\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"instrumentSpecimenId\", fieldScalar(astmField(fields, 4))),\n ...(hasContent(utidField)\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n // Priority (6), action code (~12), and report type (~26) are surfaced verbatim. The `~` field\n // indices and the code sets are `[OSS-derived]` (paywalled), so they are never mapped to a\n // guessed meaning: see the JSDoc on `OrderRecord`.\n ...definedString(\"priority\", fieldScalar(astmField(fields, 6))),\n ...definedString(\"actionCode\", fieldScalar(astmField(fields, 12))),\n ...definedString(\"reportType\", fieldScalar(astmField(fields, 26))),\n };\n}\n\n/**\n * Whether a field carries any content at all: used to decide a Universal Test ID is present. The\n * scalar check is wrong here: a UTID's code lives in component 4 while component 1 (the LOINC slot)\n * is normally empty, so `fieldScalar` (first non-empty component) would miss `^^^687`.\n */\nfunction hasContent(field: AstmField | undefined): field is AstmField {\n return field !== undefined && field.raw.length > 0;\n}\n\nfunction buildResult(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): ResultRecord {\n const utidField = astmField(fields, 3);\n const valueField = astmField(fields, 4);\n const startedAt = parseDateField(\n fieldScalar(astmField(fields, 12)),\n recordIndex,\n \"R\",\n 12,\n warnings,\n );\n const completedAt = parseDateField(\n fieldScalar(astmField(fields, 13)),\n recordIndex,\n \"R\",\n 13,\n warnings,\n );\n\n // A value field with more than one component carried an *unescaped* component delimiter: an\n // ambiguity. Fail-safe: surface BOTH the full raw value (so nothing is truncated) AND the split,\n // and WARN. Never resolve it silently to a single (wrong, truncated) value.\n const ambiguous = valueField !== undefined && valueField.components.length > 1;\n const valueComponents = ambiguous ? valueField.components : undefined;\n // The primary `value` is the decoded scalar for the ordinary single-component case, but the FULL\n // raw field text when ambiguous, so `results(msg)[0].value` is never a truncated component.\n const value = ambiguous ? valueField.raw : fieldScalar(valueField);\n if (ambiguous) {\n warnings.push(ambiguousValueSplit({ recordIndex, recordType: \"R\", fieldIndex: 4 }));\n }\n\n // ── Phase 2: modeled, fail-safe result semantics (raw fields still surfaced alongside). ──\n const units = fieldScalar(astmField(fields, 5));\n // Reference range is surfaced from the FULL field text (not the first component), so a\n // component-delimited value (`low^high`) is preserved verbatim and read as `unparsed` + warned,\n // never truncated to a single bound. A present-but-empty field is treated as absent (no warn).\n const rangeRaw = fieldRaw(astmField(fields, 6));\n const flagRaw = fieldScalar(astmField(fields, 7));\n const statusRaw = fieldScalar(astmField(fields, 9));\n\n // Units: a *numeric* value with no units is the hazard (a bare magnitude is meaningless). Warn only\n // then: a qualitative result (e.g. \"POSITIVE\") legitimately has no units. Never default or guess.\n if (units === undefined && value !== undefined && isNumericValue(value)) {\n warnings.push(unitsAbsent({ recordIndex, recordType: \"R\", fieldIndex: 5 }));\n }\n\n const range = rangeRaw !== undefined ? parseReferenceRange(rangeRaw) : undefined;\n if (range?.kind === \"unparsed\") {\n warnings.push(unparseableReferenceRange({ recordIndex, recordType: \"R\", fieldIndex: 6 }));\n }\n\n const flag = flagRaw !== undefined ? interpretAbnormalFlag(flagRaw) : undefined;\n if (flag !== undefined && !flag.recognized) {\n warnings.push(undefinedAbnormalFlag({ recordIndex, recordType: \"R\", fieldIndex: 7 }));\n }\n\n // Status is ALWAYS modeled: an absent field 9 yields `unspecified` (never assumed `final`).\n const status = interpretResultStatus(statusRaw);\n if (statusRaw !== undefined && !status.recognized) {\n warnings.push(undefinedResultStatus({ recordIndex, recordType: \"R\", fieldIndex: 9 }));\n }\n\n return {\n type: \"R\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...(hasContent(utidField)\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n ...definedString(\"value\", value),\n ...(valueComponents !== undefined ? { valueComponents } : {}),\n ...definedString(\"units\", units),\n ...definedString(\"referenceRange\", rangeRaw),\n ...(range !== undefined ? { range } : {}),\n ...definedString(\"abnormalFlags\", flagRaw),\n ...(flag !== undefined ? { flag } : {}),\n ...definedString(\"resultStatus\", statusRaw),\n status,\n ...definedString(\"operator\", fieldScalar(astmField(fields, 11))),\n ...(startedAt !== undefined ? { startedAt } : {}),\n ...(completedAt !== undefined ? { completedAt } : {}),\n ...definedString(\"instrument\", fieldScalar(astmField(fields, 14))),\n };\n}\n\n/**\n * Build a `C` (comment) record from its fields. Parent attachment is resolved in a second pass\n * ({@link attachComments}); here `attachedToRoot` is a provisional `false` and `parentIndex` is unset.\n */\nfunction buildComment(recordIndex: number, fields: readonly AstmField[]): CommentRecord {\n const textField = astmField(fields, 4);\n // Comment text is component-capable: multiple components are a normal structured comment, not an\n // ambiguity, so no warning. Surface the FULL field text (never truncated) plus the component split.\n const textComponents =\n textField !== undefined && textField.components.length > 1 ? textField.components : undefined;\n const text = textField !== undefined && textField.raw.length > 0 ? textField.raw : undefined;\n\n return {\n type: \"C\",\n recordIndex,\n fields,\n attachedToRoot: false,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n ...definedString(\"source\", fieldScalar(astmField(fields, 3))),\n ...definedString(\"text\", text),\n ...(textComponents !== undefined ? { textComponents } : {}),\n ...definedString(\"commentType\", fieldScalar(astmField(fields, 5))),\n };\n}\n\n/**\n * Build a `Q` (Request Information) record. The field *positions* are the public ASTM E1394 layout\n * (3 = starting range ID, 4 = ending range ID, 5 = Universal Test ID, 13 = request-info status); the\n * starting/ending range component structure, the `ALL` universal-query keyword, and the status code\n * set are all `[OSS-derived / paywalled]`, surfaced **verbatim** and **never interpreted or guessed**.\n * A present request-information status is flagged uninterpreted (the code set is paywalled).\n */\nfunction buildQuery(\n recordIndex: number,\n fields: readonly AstmField[],\n warnings: AstmRecordWarning[],\n): QueryRecord {\n const utidField = astmField(fields, 5);\n // The `ALL` universal-query keyword is recognized as a literal token only; its behavior is paywalled.\n const utidRaw = fieldRaw(utidField);\n const queriesAllTests = utidRaw !== undefined && utidRaw.trim().toUpperCase() === \"ALL\";\n\n // Surfaced from the FULL field text (fieldRaw, not fieldScalar) so it is literally verbatim: never\n // escape-decoded and never truncated to the first component, and so the uninterpreted-status warning\n // fires on ANY non-empty field-13 (matching the range-ID fields above). The status code set is\n // paywalled, so the value is passed through untouched, never interpreted.\n const requestInformationStatus = fieldRaw(astmField(fields, 13));\n if (requestInformationStatus !== undefined) {\n warnings.push(uninterpretedQueryStatus({ recordIndex, recordType: \"Q\", fieldIndex: 13 }));\n }\n\n return {\n type: \"Q\",\n recordIndex,\n fields,\n ...definedString(\"seq\", fieldScalar(astmField(fields, 2))),\n // Range IDs are surfaced from the FULL field text (never truncated to a component); their internal\n // caret structure is [OSS-derived]/paywalled and is not interpreted here.\n ...definedString(\"startingRangeId\", fieldRaw(astmField(fields, 3))),\n ...definedString(\"endingRangeId\", fieldRaw(astmField(fields, 4))),\n ...(hasContent(utidField) && !queriesAllTests\n ? { universalTestId: recognizeUniversalTestId(utidField.components) }\n : {}),\n queriesAllTests,\n ...definedString(\"requestInformationStatus\", requestInformationStatus),\n };\n}\n\n/**\n * Attach every `C` (comment) record to its parent by position: the immediately-preceding\n * `H`/`P`/`O`/`R` record; consecutive comments share that parent. **Fail-safe:** a comment with no\n * valid preceding parent is an **orphan**, it is attached to the message root (`attachedToRoot:\n * true`) with an `ASTM_RECORD_ORPHAN_COMMENT` warning, **never dropped**. Returns a new array; the\n * non-comment records pass through unchanged.\n *\n * @param records - The built records, in wire order.\n * @param warnings - The accumulator an orphan comment warns onto.\n * @returns The records with each comment's `parentIndex` / `attachedToRoot` resolved.\n * @example\n * ```ts\n * import { attachComments } from \"@cosyte/astm\";\n * // A comment with no preceding H/P/O/R is an orphan attached to the root.\n * const warnings: import(\"@cosyte/astm\").AstmRecordWarning[] = [];\n * const out = attachComments(\n * [{ type: \"C\", recordIndex: 0, fields: [], attachedToRoot: false }],\n * warnings,\n * );\n * (out[0] as { attachedToRoot: boolean }).attachedToRoot; // true\n * ```\n */\nexport function attachComments(\n records: readonly AstmRecord[],\n warnings: AstmRecordWarning[],\n): AstmRecord[] {\n let lastParentIndex: number | undefined;\n return records.map((rec) => {\n if (rec.type === \"H\" || rec.type === \"P\" || rec.type === \"O\" || rec.type === \"R\") {\n lastParentIndex = rec.recordIndex;\n return rec;\n }\n if (rec.type !== \"C\") return rec;\n if (lastParentIndex === undefined) {\n warnings.push(orphanComment({ recordIndex: rec.recordIndex, recordType: \"C\" }));\n return { ...rec, attachedToRoot: true };\n }\n return { ...rec, parentIndex: lastParentIndex, attachedToRoot: false };\n });\n}\n\n/**\n * Parse a `YYYYMMDDHHMMSS` date field, emitting a value-free `ASTM_RECORD_PARTIAL_TIMESTAMP` warning\n * when the digit run truncates a component (an odd length that cuts a two-digit component in half).\n * The raw run is preserved and the structured value is never zero-filled into a fabricated time.\n */\nfunction parseDateField(\n raw: string | undefined,\n recordIndex: number,\n recordType: string,\n fieldIndex: number,\n warnings: AstmRecordWarning[],\n): AstmDate | undefined {\n if (raw === undefined) return undefined;\n const date = parseAstmDate(raw);\n if (date?.truncated === true) {\n warnings.push(partialTimestamp({ recordIndex, recordType, fieldIndex }));\n }\n return date;\n}\n\n/**\n * Whether a result value is purely numeric (optional comparator + sign + digits): the case where a\n * missing unit is a genuine hazard. A qualitative value (letters, e.g. `POSITIVE`) is excluded, so it\n * does not trip the units-absent warning.\n */\nfunction isNumericValue(value: string): boolean {\n return /^\\s*[<>]?\\s*-?\\d+(?:\\.\\d+)?\\s*$/u.test(value);\n}\n\n/** The full field text (all components), or `undefined` when the field is absent or empty. */\nfunction fieldRaw(field: AstmField | undefined): string | undefined {\n return field !== undefined && field.raw.length > 0 ? field.raw : undefined;\n}\n\n/** Spread helper: include `{ [key]: value }` only when `value` is a defined string. */\nfunction definedString(key: string, value: string | undefined): Record<string, string> {\n return value === undefined ? {} : { [key]: value };\n}\n","/**\n * The record-layer **emit** side: {@link serializeAstmRecords}.\n *\n * The inverse of `parseAstmRecords`. Where the parser is liberal (vendor quirks\n * become warnings), the serializer is **conservative**: it always emits a\n * spec-clean stream with the **canonical** delimiter set (`H|\\^&`), re-escapes\n * every embedded delimiter so an embedded `|`/`^`/`\\`/`&` in a value can never\n * break framing (the exact inverse of the escape codec), and terminates\n * each record with a `CR`.\n *\n * **Round-trip.** `parseAstmRecords(serializeAstmRecords(msg))` reproduces the modeled\n * records: the same components, the same typed accessors, and the canonical delimiter\n * set (a non-canonical source is normalized to `H|\\^&` by default). Normalizing sets the\n * four delimiter **roles**; it does not delete a declaration's surplus characters, so a\n * canonically-declared header that arrived carrying surplus keeps it on the default path\n * too.\n *\n * **One delimiter set for the whole stream.** Every record (including `H`, `M` and `S`)\n * is emitted against the delimiters being emitted with, and the header declares that same\n * set. `M` and `S` carry vendor free-form data and are reproduced **byte-for-byte** from\n * their preserved `rawLine` whenever a reader using those delimiters would recover exactly\n * the fields they model, which is the case for any stream already in the emit delimiter\n * set. When it is not, the record arrived under one delimiter set and is being emitted\n * under another, the record is re-encoded from its decoded tree instead, so its fields\n * survive as fields rather than collapsing into one on the next read.\n *\n * **Emit follows the model.** Every record type, `H` included, is emitted from its decoded\n * `AstmField.repeats` tree, so an edit to a record's `fields` is reflected on emit. The one\n * part of a record that is never taken from the model is the header's delimiter\n * declaration, and only in the three positions that carry a role: those always state the\n * repeat, component and escape characters actually in use, whatever the model says. Any\n * further characters the declaration carried, a reader assigns them no role, come from\n * the model like everything else, so a header that arrived as `H|\\^&#` goes back out as\n * `H|\\^&#` rather than losing the `#`. That holds on the **default** canonical path as\n * well as when a set is passed explicitly: the surplus of a canonically-declared header\n * survives normalization, because normalization is about the four roles and the surplus\n * has none.\n *\n * **Never break framing.** A component leaf that contains a record terminator\n * (`CR`/`LF`) cannot be escaped by the ASTM escape codec (only the four declared\n * delimiters have mnemonics), so emitting it would silently corrupt the wire.\n * The serializer refuses: an embedded `CR`/`LF` is a typed {@link AstmSerializeError},\n * never emitted raw.\n *\n * **Check the delimiter set before writing.** Three conditions are required for the\n * emitted bytes to read back as the records that produced them (each separator exactly\n * one character, no separator a `CR`/`LF`, no two separators the same character) and a\n * set failing any of them is a typed {@link AstmSerializeError}\n * (`ASTM_EMIT_INVALID_DELIMITERS`) rather than output this library's own parser would\n * reject or silently re-read with a different field tree. These are **necessary**\n * conditions, not a guarantee of readback: a set can pass all three and still produce a\n * stream that reads back wrong, a separator that collides with a record's type letter\n * is the known case, and it corrupts identically with or without this check.\n */\n\nimport { CANONICAL_DELIMITERS, type Delimiters } from \"../common/delimiters.js\";\nimport { tokenizeRecord } from \"./tokenize.js\";\nimport type {\n AstmField,\n AstmMessage,\n AstmRecord,\n HeaderRecord,\n ManufacturerRecord,\n ScientificRecord,\n} from \"./types.js\";\n\n/**\n * Thrown by the record/frame emit side when a value cannot be serialized into a\n * spec-clean stream: specifically when a component contains a record terminator\n * (`CR`/`LF`), which the ASTM escape codec cannot encode and which would break\n * framing if emitted raw. Carries a stable code + positional context, never the\n * offending value (PHI discipline).\n *\n * @example\n * ```ts\n * import { serializeAstmRecord, AstmSerializeError, parseAstmRecords } from \"@cosyte/astm\";\n * const rec = parseAstmRecords(\"H|\\\\^&\\rL|1\\r\").records[1]!;\n * try {\n * serializeAstmRecord(rec);\n * } catch (err) {\n * if (err instanceof AstmSerializeError) err.code; // \"ASTM_EMIT_UNENCODABLE_VALUE\"\n * }\n * ```\n */\nexport class AstmSerializeError extends Error {\n /**\n * Stable discriminant. `ASTM_EMIT_UNENCODABLE_VALUE` for a `CR`/`LF` in a value;\n * `ASTM_EMIT_INVALID_DELIMITERS` for a delimiter set that cannot be emitted\n * reversibly.\n */\n public readonly code: AstmSerializeErrorCode;\n /** 0-based ordinal of the record within the message, when known. */\n public readonly recordIndex?: number;\n /** @internal */\n public constructor(\n message: string,\n recordIndex?: number,\n code: AstmSerializeErrorCode = \"ASTM_EMIT_UNENCODABLE_VALUE\",\n ) {\n super(message);\n this.name = \"AstmSerializeError\";\n this.code = code;\n if (recordIndex !== undefined) this.recordIndex = recordIndex;\n }\n}\n\n/**\n * The reasons emit refuses rather than writing a stream that cannot be read back.\n *\n * - `ASTM_EMIT_UNENCODABLE_VALUE`: a component holds a record terminator\n * (`CR`/`LF`), which the escape codec has no mnemonic for.\n * - `ASTM_EMIT_INVALID_DELIMITERS`: the delimiter set to emit against failed one\n * of the three conditions readback requires (see {@link serializeAstmRecords}).\n * Those conditions are necessary rather than sufficient, so this code means a\n * set was rejected, not that every unreversible set is.\n *\n * @example\n * ```ts\n * import type { AstmSerializeErrorCode } from \"@cosyte/astm\";\n * const code: AstmSerializeErrorCode = \"ASTM_EMIT_INVALID_DELIMITERS\";\n * ```\n */\nexport type AstmSerializeErrorCode = \"ASTM_EMIT_UNENCODABLE_VALUE\" | \"ASTM_EMIT_INVALID_DELIMITERS\";\n\n/**\n * The three conditions a delimiter set must meet for emit to be reversible, in\n * the order they are checked. Each is a way the emitted bytes would otherwise\n * fail to read back as the records that produced them.\n */\nconst DELIMITER_ROLES = [\"field\", \"repeat\", \"component\", \"escape\"] as const;\n\n/**\n * Any C0 control character or `DEL`. Both the record layer and the frame layer\n * reserve characters from this range as structure (`CR`/`LF` end a record,\n * `STX`/`ETX`/`ETB` bound a frame) so none of them is ever carried through as\n * inert text.\n */\nconst CONTROL_CHARACTER = /[\\u0000-\\u001f\\u007f]/;\n\n/**\n * Refuse a delimiter set that cannot round-trip, before any bytes are written.\n *\n * **Why this refuses instead of warning.** Emit returns a bare `string` and has\n * no warning channel, so a warning here could only ever be ignored while the\n * unreadable stream still shipped. The three rules below are not style: each one\n * names a case where the emitted bytes provably do not read back as the records\n * that produced them, and every one of them was silent before, two of the six\n * shapes measured emitted a stream this library's own parser then re-read with a\n * **different** field tree and **no** warning at all, and the rest emitted a\n * stream it rejected outright. Refusing at the call is the only disposition that\n * reaches the caller.\n *\n * 1. **Each delimiter is exactly one character.** The reader takes each role from\n * a single position in the header's declaration, so a multi-character\n * separator is written but can never be read; an empty one matches everywhere\n * and shreds the values themselves.\n * 2. **No delimiter is a record terminator.** A `CR`/`LF` separator ends the\n * record instead of dividing it, truncating the stream.\n * 3. **The four are mutually distinct.** Two roles sharing a character makes the\n * boundary between them unrecoverable: a two-repeat field emitted with\n * `repeat` equal to `component` reads back as one repeat of two components,\n * losing structure with nothing to signal it.\n *\n * This is stricter than what the **parser** tolerates: a header may declare a set\n * where, say, `repeat` and `component` are the same character, and that stream\n * parses. Emitting against such a set is what cannot be undone, so a caller who\n * passes one through (`serializeAstmRecords(msg, msg.delimiters)` on such a\n * stream) now gets a typed error where it previously got silently lossy bytes.\n * That is a deliberate narrowing on a published package: the input it turns away\n * is exactly the input it was corrupting.\n *\n * **What this does not cover.** These three rules are necessary, not sufficient.\n * A set can satisfy all of them and still emit a stream that reads back wrong:\n * a separator equal to a record's type letter (`field` of `R`) is the known case,\n * because the type letter is then escaped away and the record re-reads as\n * unsupported. That corruption predates this check and is unchanged by it; it is\n * recorded as a known defect rather than fixed here, because the rule that would\n * catch it has to be derived rather than guessed, and deriving it while fixing\n * two unrelated gaps is how a fix outgrows the thing it fixes.\n *\n * The error never echoes the offending characters. A caller-supplied \"delimiter\"\n * that fails rule 1 can be arbitrary text, so quoting it back into a message\n * risks putting data into a log; naming the role is enough to fix the call.\n */\nfunction assertEmittableDelimiters(d: Delimiters, recordIndex?: number): void {\n const invalid = (reason: string): never => {\n throw new AstmSerializeError(\n `Cannot emit against these delimiters: ${reason}.`,\n recordIndex,\n \"ASTM_EMIT_INVALID_DELIMITERS\",\n );\n };\n\n // A caller reaching this from JavaScript can pass `null` where the types demand a\n // set, and `null` does not take the default parameter the way `undefined` does.\n // Checked first so that case is the documented typed error too.\n if (typeof d !== \"object\" || d === null) {\n invalid(\"a delimiter set is required\");\n }\n\n for (const role of DELIMITER_ROLES) {\n // The same caller can omit a member or pass a non-string. Checked before\n // `.length` so that case is the same typed error as any other unusable set,\n // rather than a `TypeError` from inside the serializer.\n const char: unknown = d[role];\n if (typeof char !== \"string\" || char.length !== 1) {\n invalid(`the ${role} delimiter must be exactly one character`);\n }\n if (char === \"\\r\" || char === \"\\n\") {\n invalid(`the ${role} delimiter must not be a record terminator (CR/LF)`);\n }\n }\n\n DELIMITER_ROLES.forEach((a, i) => {\n for (const b of DELIMITER_ROLES.slice(i + 1)) {\n if (d[a] === d[b]) {\n invalid(`the ${a} and ${b} delimiters must not be the same character`);\n }\n }\n });\n}\n\n/**\n * Escape-encode one component leaf for spec-clean emit: the inverse of\n * `decodeEscapes`. The **escape character itself is encoded first** (`&` → `&E&`)\n * so a later delimiter substitution can never double-encode the `&` it just\n * introduced; then the field / component / repeat delimiters map to their\n * mnemonics.\n *\n * A `CR`/`LF` in the leaf has no escape mnemonic and would break framing, so it\n * is rejected with an {@link AstmSerializeError} rather than emitted raw, as is a\n * delimiter set the result could not be read back with.\n *\n * @param leaf - One already-decoded component string.\n * @param d - The delimiters to emit against (canonical for spec-clean output).\n * @param recordIndex - The enclosing record's index, for error context.\n * @returns The escaped component text.\n * @throws {@link AstmSerializeError} for a `CR`/`LF` in the leaf\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or for a delimiter set failing one of the\n * three conditions readback requires (`ASTM_EMIT_INVALID_DELIMITERS`), which\n * are necessary, not sufficient, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { encodeComponent, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * encodeComponent(\"1^40\", CANONICAL_DELIMITERS); // \"1&S&40\"\n * ```\n */\nexport function encodeComponent(leaf: string, d: Delimiters, recordIndex?: number): string {\n assertEmittableDelimiters(d, recordIndex);\n return encodeLeaf(leaf, d, recordIndex);\n}\n\n/**\n * The escape encoder proper, for callers that have already checked `d`. Kept\n * separate so the delimiter check runs once per public call rather than once per\n * component leaf.\n */\nfunction encodeLeaf(leaf: string, d: Delimiters, recordIndex?: number): string {\n if (leaf.includes(\"\\r\") || leaf.includes(\"\\n\")) {\n throw new AstmSerializeError(\n \"A value contains a record terminator (CR/LF), which cannot be escaped without breaking framing.\",\n recordIndex,\n );\n }\n // Escape the escape char first, then the three structural delimiters.\n return leaf\n .split(d.escape)\n .join(d.escape + \"E\" + d.escape)\n .split(d.field)\n .join(d.escape + \"F\" + d.escape)\n .split(d.component)\n .join(d.escape + \"S\" + d.escape)\n .split(d.repeat)\n .join(d.escape + \"R\" + d.escape);\n}\n\n/** Encode one field from its decoded repeat/component tree, re-escaping each leaf. */\nfunction encodeField(\n repeats: readonly (readonly string[])[],\n d: Delimiters,\n recordIndex?: number,\n): string {\n return repeats\n .map((rep) => rep.map((c) => encodeLeaf(c, d, recordIndex)).join(d.component))\n .join(d.repeat);\n}\n\n/**\n * Serialize a single ASTM record to its spec-clean wire text (no trailing\n * terminator). Emits with the given delimiters, defaulting to the canonical set.\n *\n * The header (`H`) is special-cased: its delimiter-definition field is emitted as\n * the **literal** declaration of `d`, never escaped, escaping it would corrupt the\n * very declaration a reader depends on, followed by any characters the modeled\n * declaration carried beyond the three a reader takes its roles from. Manufacturer\n * (`M`) and scientific (`S`) records are reproduced **byte-identically** from their\n * preserved `rawLine` when they are already in `d`, and re-encoded from their fields\n * when they are not, so their fields never collapse into one on the next read.\n *\n * @param record - The record to serialize.\n * @param d - The delimiters to emit against; defaults to `H|\\^&`.\n * @returns The record's wire text, terminator excluded.\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { serializeAstmRecord, parseAstmRecords } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * serializeAstmRecord(msg.records[1]!); // \"R|1|^^^687|28.6|U/L||N||F\"\n * ```\n */\nexport function serializeAstmRecord(\n record: AstmRecord,\n d: Delimiters = CANONICAL_DELIMITERS,\n): string {\n assertEmittableDelimiters(d, record.recordIndex);\n return serializeRecordChecked(record, d);\n}\n\n/** {@link serializeAstmRecord} for callers that have already checked `d`. */\nfunction serializeRecordChecked(record: AstmRecord, d: Delimiters): string {\n if (record.type === \"M\" || record.type === \"S\") return serializeVerbatimRecord(record, d);\n\n if (record.type === \"H\") return serializeHeader(record, d);\n\n return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);\n}\n\n/**\n * Serialize an `M` (manufacturer) or `S` (scientific) record: the two vendor\n * free-form types the parser surfaces byte-for-byte.\n *\n * These records are re-emitted **verbatim from `rawLine` when, and only when, a\n * reader using the emit delimiters would recover exactly the fields the record\n * models**. When it would not, the record arrived under one delimiter set and\n * is being emitted under another, the record is re-encoded from its decoded\n * field tree instead, like every other record type.\n *\n * The choice being made here, and why. A blanket verbatim re-emit produced a\n * **mixed-delimiter stream**: the header declared one delimiter set while the\n * `M`/`S` rows still carried another, so re-parsing the output silently\n * collapsed every field of those rows into one, with no warning. On the\n * analyzer-to-LIS path a collapsed field is a lost result or a lost\n * patient/specimen identifier, and the caller had no signal. The two candidate\n * fixes were to re-encode the disagreeing records, or to refuse/warn on a\n * message whose records disagree. Re-encoding is chosen because (1) it is what\n * the serializer already promises: one spec-clean stream in the declared\n * delimiter set, and a mixed-delimiter stream is not spec-clean; (2) emit\n * returns a string and has no warning channel, so a warning could only be\n * ignored while the corrupt stream still shipped, and a refusal would reject\n * messages this library successfully parsed; and (3) the guard above means bytes\n * change for exactly the streams that were being corrupted and for no others.\n * The invariant that must hold either way is that a round-trip never silently\n * loses a field, in either direction.\n */\nfunction serializeVerbatimRecord(\n record: ManufacturerRecord | ScientificRecord,\n d: Delimiters,\n): string {\n if (recoversVerbatim(record, d)) return record.rawLine;\n return record.fields.map((f) => encodeField(f.repeats, d, record.recordIndex)).join(d.field);\n}\n\n/**\n * Whether re-tokenizing `record.rawLine` with the emit delimiters recovers the\n * exact field tree the record already models. True whenever the record's own\n * delimiters agree with the emit set (the ordinary case, where the raw bytes are\n * reproduced untouched), and true as well when the line simply contains no\n * delimiter either set would split on.\n */\nfunction recoversVerbatim(record: ManufacturerRecord | ScientificRecord, d: Delimiters): boolean {\n return sameFieldTree(tokenizeRecord(record.rawLine, d), record.fields);\n}\n\n/** Structural equality over two tokenized field trees, compared on decoded values. */\nfunction sameFieldTree(a: readonly AstmField[], b: readonly AstmField[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((fieldA, i) => {\n const fieldB = b[i];\n if (fieldB === undefined) return false;\n if (fieldA.repeats.length !== fieldB.repeats.length) return false;\n return fieldA.repeats.every((repA, r) => {\n const repB = fieldB.repeats[r];\n if (repB === undefined || repA.length !== repB.length) return false;\n return repA.every((comp, c) => comp === repB[c]);\n });\n });\n}\n\n/**\n * Serialize an `H` (header) record. The three delimiter roles the declaration\n * carries are emitted as the **literal** declaration of the delimiters being\n * emitted against, never escaped and never taken from the model: a header that\n * declares one delimiter set while the records around it use another is the exact\n * corruption this module refuses to produce. Every data field (field 3 onward) is\n * emitted from the header's tokenized {@link HeaderRecord.fields}, so an edit to\n * the model is reflected on emit, like every other record type.\n *\n * Anything the declaration carried **beyond** those three characters is carried\n * through: see {@link declarationResidual}.\n */\nfunction serializeHeader(header: HeaderRecord, d: Delimiters): string {\n const head = \"H\" + d.field + d.repeat + d.component + d.escape + declarationResidual(header, d);\n // fields[0] is the type letter and fields[1] is the delimiter declaration, both regenerated\n // above; the ASTM data fields start at fields[2]. No data fields ⇒ a bare `H|\\^&`.\n const dataFields = header.fields.slice(2);\n const rest = dataFields\n .map((f) => d.field + encodeField(f.repeats, d, header.recordIndex))\n .join(\"\");\n return head + rest;\n}\n\n/**\n * The bytes a header's delimiter declaration carries past the three it is read\n * for, `#` in a declaration of `\\^&#`, and whether they can be re-emitted.\n *\n * Only three characters of the declaration have a role: repeat, component and\n * escape, taken by position. A vendor may still declare more, and the reader\n * ignores the surplus rather than refusing the stream. Emit used to regenerate\n * the declaration from the three roles alone, so those surplus bytes were\n * **dropped with no signal** and a header that arrived as `H|\\^&#` went back out\n * as `H|\\^&`.\n *\n * The disposition chosen here is to **preserve** them, not to refuse and not to\n * report. Refusing would reject a stream the parser reads without complaint, on\n * a published package, over bytes it has already decided are inert; reporting is\n * not available at all, because emit returns a bare string with no warning\n * channel. Preserving costs nothing and is the only one of the three that makes\n * the round-trip byte-exact. What the surplus *means* remains unresolved, the\n * clauses that would settle it are not in the freely published material and were\n * not read, but carrying bytes through unread is a strictly smaller claim than\n * deleting them, and it keeps emit aligned with a reader that scopes delimiters\n * forward from every header: the re-read declaration resolves to the same four\n * roles either way.\n *\n * They are dropped whenever they could not be read back as surplus, which is one\n * of two things. Either the modeled declaration no longer begins with the three\n * roles being emitted (the header is being transcoded into a different set, and\n * the surplus belonged to the old declaration) or the surplus is not inert on\n * the wire: it contains the field delimiter, or **any control character**.\n *\n * The control-character rule is wider than the record layer alone needs, and\n * deliberately so. A `CR`/`LF` would end the record and shift every data field\n * along, but this text is also handed to the **frame** layer by\n * `serializeFramedAstm`, where `STX`, `ETX` and `ETB` are structural too: a\n * surplus carrying one of those truncates the frame body, and re-reading the\n * framed stream then drops the whole header record (its sender, its receiver,\n * its control ID) behind nothing but a checksum warning. Rather than enumerate\n * the bytes each layer happens to reserve, and re-derive that list every time a\n * layer is added, no control character is carried at all. The cost is bytes that\n * were unprintable inside a declaration whose meaning is already unresolved.\n *\n * **What this rule does not reach**, stated rather than left to be discovered.\n * It is keyed on the *character*, while the frame layer's structure is keyed on\n * the low byte: `src/frames/encode.ts` writes `charCodeAt(i) & 0xff`, so a\n * non-control character whose code point truncates onto `STX`/`ETX`/`ETB`\n * (`U+0102`, `U+0103`, `U+0117`) passes this guard and still breaks framing. It\n * fails loudly when it does (a typed parse error, or an unknown-record-type\n * warning), never silently, so the property this module holds is intact; the\n * truncation itself is a frame-layer defect that predates this rule and is\n * recorded separately. Nor does refusing a control character *here* imply one\n * cannot be a **delimiter role**: only `CR`/`LF` are refused as delimiters, so a\n * set declaring `STX` as its component separator is still accepted.\n *\n * Losing a field is a structural loss; dropping inert bytes is not.\n *\n * The surplus is read from `fields[1]`, the tokenized declaration, because\n * `fields` is the emit source for every record type: `rawLine` is provenance\n * and editing it has no effect on emit.\n */\nfunction declarationResidual(header: HeaderRecord, d: Delimiters): string {\n const declaration = header.fields[1]?.raw ?? \"\";\n const roles = d.repeat + d.component + d.escape;\n if (!declaration.startsWith(roles)) return \"\";\n const residual = declaration.slice(roles.length);\n if (residual.includes(d.field) || CONTROL_CHARACTER.test(residual)) return \"\";\n return residual;\n}\n\n/**\n * Serialize a whole ASTM message (or a bare record list) to a spec-clean,\n * `CR`-terminated record stream: the inverse of `parseAstmRecords`.\n *\n * Emit is **conservative**: the canonical `H|\\^&` delimiters, every embedded\n * delimiter re-escaped, each record closed with a `CR`. A message parsed with\n * non-canonical delimiters is **normalized** to the canonical set on emit: every\n * record, `M` and `S` included, so the emitted stream is in one delimiter set and\n * re-parsing it recovers every field. Passing `d` explicitly emits against that set\n * instead, and the header declares it. Normalization replaces the four delimiter\n * **roles**; a header declaration carrying characters beyond the three that hold a\n * role keeps them, on this path as on any other, rather than being truncated.\n *\n * **`d` is checked before anything is written.** Each of the four separators must be\n * exactly one character, none may be a record terminator, and no two may share a\n * character: otherwise the emitted bytes cannot be read back as the records that\n * produced them, and emit has no warning channel with which to say so. A set that\n * fails is an {@link AstmSerializeError} with code `ASTM_EMIT_INVALID_DELIMITERS`.\n * This is stricter than the parser, which reads some sets it cannot reverse, so\n * `serializeAstmRecords(msg, msg.delimiters)` can refuse a message that parsed:\n * in exactly the cases where it used to emit a stream that read back wrong. The\n * three conditions are necessary rather than sufficient: a set that passes them can\n * still read back wrong if a separator collides with a record's type letter, which\n * this check does not claim to catch.\n *\n * @param input - A parsed {@link AstmMessage} or a list of {@link AstmRecord}s.\n * @param d - The delimiters to emit against; defaults to the canonical `H|\\^&` set.\n * @returns The serialized record stream (`CR` after every record).\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { parseAstmRecords, serializeAstmRecords } from \"@cosyte/astm\";\n * const raw = \"H|\\\\^&\\rP|1\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\";\n * serializeAstmRecords(parseAstmRecords(raw)); // === raw\n * ```\n */\nexport function serializeAstmRecords(\n input: AstmMessage | readonly AstmRecord[],\n d: Delimiters = CANONICAL_DELIMITERS,\n): string {\n assertEmittableDelimiters(d);\n const records: readonly AstmRecord[] = Array.isArray(input)\n ? (input as readonly AstmRecord[])\n : (input as AstmMessage).records;\n return records.map((r) => serializeRecordChecked(r, d) + \"\\r\").join(\"\");\n}\n\n/**\n * Serialize a single {@link AstmField} to its spec-clean wire text, re-escaping\n * each component. A low-level helper for callers assembling a field outside a\n * whole record.\n *\n * @param field - The field to serialize.\n * @param d - The delimiters to emit against; defaults to `H|\\^&`.\n * @returns The escaped field text.\n * @throws {@link AstmSerializeError} when a component contains an unencodable `CR`/`LF`\n * (`ASTM_EMIT_UNENCODABLE_VALUE`), or when `d` fails one of the three conditions\n * readback requires (`ASTM_EMIT_INVALID_DELIMITERS`): necessary conditions, not\n * sufficient ones, so a set can pass them and still not reverse.\n * @example\n * ```ts\n * import { serializeField, tokenizeRecord, CANONICAL_DELIMITERS } from \"@cosyte/astm\";\n * const fields = tokenizeRecord(\"R|1|^^^687|1&S&40\", CANONICAL_DELIMITERS);\n * serializeField(fields[3]!); // \"1&S&40\"\n * ```\n */\nexport function serializeField(field: AstmField, d: Delimiters = CANONICAL_DELIMITERS): string {\n assertEmittableDelimiters(d);\n return encodeField(field.repeats, d);\n}\n","/**\n * The record-layer **builder**: {@link buildAstmMessage}.\n *\n * A builder produces spec-clean E1394 records **by construction** from typed\n * caller input, under two disciplines:\n *\n * - **Never fabricate.** A builder emits **only** the values the caller supplied.\n * An unsupplied field is left **empty**, never filled with a default clinical\n * value: no defaulted result status, abnormal flag, units, value, or patient\n * identifier. (Leaving a field empty is not a guess; inventing an `F` status or\n * an `N` flag would be.) A consumer that parses a built result whose status the\n * caller omitted reads `status.meaning === \"unspecified\"`, never `\"final\"`.\n * - **Compute, don't guess, the structure.** The record type letters, the\n * delimiter declaration (canonical `H|\\^&`), the message terminator (`L`), and\n * the per-record sequence counters are **structural**: computed by the builder,\n * not clinical values. The caller may override any sequence number.\n *\n * Every value is escape-encoded on emit (via the serializer), so an embedded\n * delimiter can never break framing, and a value carrying an unencodable `CR`/`LF`\n * is a typed {@link AstmSerializeError} rather than a corrupted wire.\n */\n\nimport { CANONICAL_DELIMITERS } from \"../common/delimiters.js\";\nimport { AstmSerializeError, encodeComponent } from \"./serialize.js\";\n\n/** A patient name split into its components; only the supplied parts are emitted. */\nexport interface PatientNameInput {\n readonly last?: string;\n readonly first?: string;\n readonly middle?: string;\n}\n\n/** Input for a `P` (patient) record. The three IDs stay distinct: none defaults from another. */\nexport interface PatientInput {\n readonly type: \"P\";\n /** Structural sequence number; auto-computed when omitted. */\n readonly seq?: string;\n /** Field 3: practice-assigned patient ID. */\n readonly practiceAssignedId?: string;\n /** Field 4: laboratory-assigned patient ID. */\n readonly laboratoryAssignedId?: string;\n /** Field 5: a third patient identifier. */\n readonly patientIdThree?: string;\n /** Field 6: patient name (`Last^First^Middle`). */\n readonly name?: PatientNameInput;\n /** Field 7: mother's maiden name. */\n readonly mothersMaidenName?: string;\n /** Field 8: birthdate (`YYYYMMDDHHMMSS`), emitted verbatim, never reformatted. */\n readonly birthDate?: string;\n /** Field 9: sex, emitted verbatim (never defaulted). */\n readonly sex?: string;\n}\n\n/** Input for an `O` (order) record. */\nexport interface OrderInput {\n readonly type: \"O\";\n readonly seq?: string;\n /** Field 3: specimen / accession ID. */\n readonly specimenId?: string;\n /** Field 4: instrument specimen ID. */\n readonly instrumentSpecimenId?: string;\n /** Field 5: Universal Test ID components (verbatim, in order; e.g. `[\"\", \"\", \"\", \"687\"]`). */\n readonly universalTestId?: readonly string[];\n /** Field 6: priority, emitted verbatim. */\n readonly priority?: string;\n /** Field 12: action code, emitted verbatim. */\n readonly actionCode?: string;\n /** Field 26: report type, emitted verbatim. */\n readonly reportType?: string;\n}\n\n/** Input for an `R` (result) record. No clinical field is defaulted; unsupplied ⇒ empty. */\nexport interface ResultInput {\n readonly type: \"R\";\n readonly seq?: string;\n /** Field 3: Universal Test ID components (verbatim). */\n readonly universalTestId?: readonly string[];\n /** Field 4: the measured value, emitted verbatim (never defaulted). */\n readonly value?: string;\n /** Field 5: units (vendor free text; never defaulted, guessed, or converted). */\n readonly units?: string;\n /** Field 6: reference range, emitted verbatim. */\n readonly referenceRange?: string;\n /** Field 7: abnormal flags, emitted verbatim (never defaulted to `N`). */\n readonly abnormalFlags?: string;\n /** Field 9: result status, emitted verbatim (never defaulted to `F`). */\n readonly resultStatus?: string;\n /** Field 11: operator. */\n readonly operator?: string;\n /** Field 12: test-started timestamp (`YYYYMMDDHHMMSS`), verbatim. */\n readonly startedAt?: string;\n /** Field 13: test-completed timestamp, verbatim. */\n readonly completedAt?: string;\n /** Field 14: instrument identifier. */\n readonly instrument?: string;\n}\n\n/** Input for a `C` (comment) record. */\nexport interface CommentInput {\n readonly type: \"C\";\n readonly seq?: string;\n /** Field 3: comment source. */\n readonly source?: string;\n /** Field 4: comment text (a single component). Use {@link CommentInput.textComponents} for a structured comment. */\n readonly text?: string;\n /** Field 4: comment text as explicit components (takes precedence over `text` when set). */\n readonly textComponents?: readonly string[];\n /** Field 5: comment type code, emitted verbatim. */\n readonly commentType?: string;\n}\n\n/** Input for a `Q` (request-information) record. */\nexport interface QueryInput {\n readonly type: \"Q\";\n readonly seq?: string;\n /** Field 3: starting range ID, emitted verbatim. */\n readonly startingRangeId?: string;\n /** Field 4: ending range ID, emitted verbatim. */\n readonly endingRangeId?: string;\n /** Field 5: Universal Test ID components; ignored when {@link QueryInput.queriesAllTests} is set. */\n readonly universalTestId?: readonly string[];\n /** Field 5: emit the literal `ALL` universal-query keyword instead of a Universal Test ID. */\n readonly queriesAllTests?: boolean;\n /** Field 13: request-information status, emitted verbatim. */\n readonly requestInformationStatus?: string;\n}\n\n/**\n * Input for an `M` (manufacturer) or `S` (scientific) record: vendor-defined\n * free-form data. Emitted verbatim from the caller's fields; never interpreted.\n */\nexport interface VerbatimInput {\n readonly type: \"M\" | \"S\";\n /** Data fields (after the type letter), emitted verbatim in order. */\n readonly fields: readonly string[];\n}\n\n/** Any record the message builder can emit (the terminator `L` is appended automatically). */\nexport type AstmRecordInput =\n | PatientInput\n | OrderInput\n | ResultInput\n | CommentInput\n | QueryInput\n | VerbatimInput;\n\n/** Optional header fields (after the delimiter declaration). */\nexport interface HeaderInput {\n /** Extra `H`-record fields (field 3 onward), emitted verbatim in order. Field 2 is always `\\^&`. */\n readonly fields?: readonly string[];\n}\n\n/** The message to build: an optional header, the body records, and an optional terminator code. */\nexport interface MessageInput {\n /** Header fields; the canonical `H|\\^&` declaration is always emitted. */\n readonly header?: HeaderInput;\n /** The body records, in order. `H` and `L` are supplied by the builder. */\n readonly records: readonly AstmRecordInput[];\n /**\n * Field 3 of the auto-appended `L` (terminator) record: the termination code\n * (e.g. `N` normal). Omitted by default (not defaulted to a value the caller\n * did not choose); the `L` seq is always emitted.\n */\n readonly terminationCode?: string;\n}\n\n/** Join a positional field array into a record line, trimming trailing empty fields. */\nfunction line(fields: readonly (readonly string[])[]): string {\n const d = CANONICAL_DELIMITERS;\n const encoded = fields.map((comps) => comps.map((c) => encodeComponent(c, d)).join(d.component));\n // Trim trailing empty fields: an absent trailing field and an empty one are equivalent on parse.\n let end = encoded.length;\n while (end > 0 && encoded[end - 1] === \"\") end -= 1;\n return encoded.slice(0, end).join(d.field);\n}\n\n/** Build the components of a Universal Test ID field from a verbatim component list. */\nfunction utid(components: readonly string[] | undefined): readonly string[] {\n return components === undefined ? [\"\"] : [...components];\n}\n\n/** Set a 1-based ASTM field position in a sparse builder array (index 0 is the type letter). */\nfunction at(fields: string[][], position1: number, value: string | undefined): void {\n if (value === undefined) return;\n fields[position1 - 1] = [value];\n}\n\nfunction buildPatientLine(input: PatientInput, seq: string): string {\n const f: string[][] = [[\"P\"], [seq]];\n at(f, 3, input.practiceAssignedId);\n at(f, 4, input.laboratoryAssignedId);\n at(f, 5, input.patientIdThree);\n if (input.name !== undefined) {\n f[5] = [input.name.last ?? \"\", input.name.first ?? \"\", input.name.middle ?? \"\"];\n }\n at(f, 7, input.mothersMaidenName);\n at(f, 8, input.birthDate);\n at(f, 9, input.sex);\n return line(padGaps(f));\n}\n\nfunction buildOrderLine(input: OrderInput, seq: string): string {\n const f: string[][] = [[\"O\"], [seq]];\n at(f, 3, input.specimenId);\n at(f, 4, input.instrumentSpecimenId);\n if (input.universalTestId !== undefined) f[4] = [...utid(input.universalTestId)];\n at(f, 6, input.priority);\n at(f, 12, input.actionCode);\n at(f, 26, input.reportType);\n return line(padGaps(f));\n}\n\nfunction buildResultLine(input: ResultInput, seq: string): string {\n const f: string[][] = [[\"R\"], [seq]];\n if (input.universalTestId !== undefined) f[2] = [...utid(input.universalTestId)];\n at(f, 4, input.value);\n at(f, 5, input.units);\n at(f, 6, input.referenceRange);\n at(f, 7, input.abnormalFlags);\n at(f, 9, input.resultStatus);\n at(f, 11, input.operator);\n at(f, 12, input.startedAt);\n at(f, 13, input.completedAt);\n at(f, 14, input.instrument);\n return line(padGaps(f));\n}\n\nfunction buildCommentLine(input: CommentInput, seq: string): string {\n const f: string[][] = [[\"C\"], [seq]];\n at(f, 3, input.source);\n if (input.textComponents !== undefined) f[3] = [...input.textComponents];\n else at(f, 4, input.text);\n at(f, 5, input.commentType);\n return line(padGaps(f));\n}\n\nfunction buildQueryLine(input: QueryInput, seq: string): string {\n const f: string[][] = [[\"Q\"], [seq]];\n at(f, 3, input.startingRangeId);\n at(f, 4, input.endingRangeId);\n if (input.queriesAllTests === true) f[4] = [\"ALL\"];\n else if (input.universalTestId !== undefined) f[4] = [...utid(input.universalTestId)];\n at(f, 13, input.requestInformationStatus);\n return line(padGaps(f));\n}\n\nfunction buildVerbatimLine(input: VerbatimInput, seq: string): string {\n // M / S records are surfaced VERBATIM by the parser, so the builder emits the caller's field text\n // byte-for-byte (never escaped, the caller owns the exact bytes). A CR/LF is still refused: it\n // cannot be represented inside a record and would break framing.\n return [input.type, seq, ...input.fields.map(guardWireSafe)].join(CANONICAL_DELIMITERS.field);\n}\n\n/** Reject a verbatim field carrying a record terminator (`CR`/`LF`), which would break framing. */\nfunction guardWireSafe(value: string): string {\n if (value.includes(\"\\r\") || value.includes(\"\\n\")) {\n throw new AstmSerializeError(\n \"A verbatim field contains a record terminator (CR/LF), which would break framing.\",\n );\n }\n return value;\n}\n\n/** Fill sparse gaps in a positional field array with a single empty component. */\nfunction padGaps(fields: string[][]): string[][] {\n const out: string[][] = [];\n for (let i = 0; i < fields.length; i += 1) out[i] = fields[i] ?? [\"\"];\n return out;\n}\n\n/**\n * Build a spec-clean ASTM/CLSI-LIS02 record stream from typed input.\n *\n * The builder emits the canonical `H|\\^&` header (plus any header fields), then\n * every body record with each supplied field at its correct 1-based position and\n * every value escape-encoded, then an `L` terminator. **Nothing clinical is\n * defaulted** (an unsupplied field is empty, never a guessed value) and the\n * structural pieces (delimiters, record types, sequence counters, terminator) are\n * computed. The result parses back to an equal message: build → parse fidelity by\n * construction.\n *\n * @param input - The message to build.\n * @returns The serialized, `CR`-terminated record stream.\n * @throws {@link AstmSerializeError} when a value contains an unencodable `CR`/`LF`.\n * @example\n * ```ts\n * import { buildAstmMessage, parseAstmRecords, results } from \"@cosyte/astm\";\n * const raw = buildAstmMessage({\n * records: [{ type: \"R\", universalTestId: [\"\", \"\", \"\", \"687\"], value: \"28.6\", units: \"U/L\" }],\n * });\n * results(parseAstmRecords(raw))[0]?.value; // \"28.6\"\n * ```\n */\nexport function buildAstmMessage(input: MessageInput): string {\n const lines: string[] = [];\n\n // Header: the canonical delimiter declaration is emitted LITERALLY (never escaped, escaping the\n // very declaration a reader depends on would corrupt it); any extra header fields are escaped.\n const d = CANONICAL_DELIMITERS;\n // Header data fields (sender name / version etc.) are emitted verbatim: they legitimately carry\n // component structure (`analyzer^cobas^1`); only a framing-breaking CR/LF is refused.\n const headerFields = input.header?.fields ?? [];\n const headerRest = headerFields.map((v) => d.field + guardWireSafe(v)).join(\"\");\n lines.push(\"H\" + d.field + d.repeat + d.component + d.escape + headerRest);\n\n // Per-record-type sequence counters (structural; the caller may override each seq).\n const counters = new Map<string, number>();\n const nextSeq = (type: string, override?: string): string => {\n if (override !== undefined) return override;\n const n = (counters.get(type) ?? 0) + 1;\n counters.set(type, n);\n return String(n);\n };\n\n for (const rec of input.records) {\n switch (rec.type) {\n case \"P\":\n lines.push(buildPatientLine(rec, nextSeq(\"P\", rec.seq)));\n break;\n case \"O\":\n lines.push(buildOrderLine(rec, nextSeq(\"O\", rec.seq)));\n break;\n case \"R\":\n lines.push(buildResultLine(rec, nextSeq(\"R\", rec.seq)));\n break;\n case \"C\":\n lines.push(buildCommentLine(rec, nextSeq(\"C\", rec.seq)));\n break;\n case \"Q\":\n lines.push(buildQueryLine(rec, nextSeq(\"Q\", rec.seq)));\n break;\n case \"M\":\n case \"S\":\n lines.push(buildVerbatimLine(rec, nextSeq(rec.type)));\n break;\n default: {\n // Exhaustiveness: an unrecognized record type is a structural error, not a guess.\n const bad = rec as { type?: unknown };\n throw new TypeError(`Unknown ASTM record type to build: ${String(bad.type)}`);\n }\n }\n }\n\n // Terminator: structural. Field 3 termination code only when the caller chose one.\n const term: string[][] = [[\"L\"], [\"1\"]];\n if (input.terminationCode !== undefined) term[2] = [input.terminationCode];\n lines.push(line(padGaps(term)));\n\n return lines.map((l) => l + \"\\r\").join(\"\");\n}\n","/**\n * Message grouping: the safe replacement for the stream-scoped extractors.\n *\n * A parsed {@link AstmMessage} models a whole **record stream**, and a stream may carry\n * several messages back to back: a message runs from an `H` header to its `L` terminator.\n * The flat extractors in `./extractors.js` read the whole stream, so on a multi-message\n * stream `patient(msg)` answered with the first `P` in the stream while `results(msg)`\n * answered with every `R` in it: pairing the two, which is exactly what the package's\n * one-line north star does, attributed one patient's results to another. It needed no\n * redeclaration and no unusual delimiters, and it warned about nothing.\n *\n * {@link messages} is the fix: it splits the stream into the messages it actually\n * contains, and each one carries only its own records, so a patient and a result are only\n * ever paired inside the message that carried both.\n *\n * **Where a message begins.** A new message opens at every `H` record and runs to the\n * record before the next `H` (or the end of the stream). That is deliberately the *same*\n * boundary the parser already uses to scope delimiters: the active delimiter set is\n * re-read at each `H` and governs that header and the records that follow it, so message\n * boundaries and delimiter scope cannot disagree. The `L` terminator **closes** a message\n * but does not open a scope, so anything between an `L` and the next `H` stays with the\n * message its header opened rather than being dropped. Grouping is therefore **total**:\n * every record of the stream lands in exactly one message, none duplicated, none lost.\n *\n * **What the standard grounds, and what it does not.** CLSI LIS02-A2 §2 is definitional\n * about the unit itself: a message is bounded by the `H` record at one end and the `L`\n * record at the other. That clause is in CLSI's free sample and is the whole of what is\n * claimed from the standard here. It does **not** settle which record *opens* a new scope\n * partway through a stream, so treating a second `H` as the start of the next message\n * (rather than, say, requiring an intervening `L`) is a reasoned choice, not a citation. It\n * is made this way because it is the boundary this parser already enforces for delimiters,\n * and because it never drops a record.\n *\n * **What that costs, and where the cost is paid.** Grouping reads a record's **type letter**,\n * so it is only as good as the reader's ability to recognize one. A header carrying a stray\n * leading byte reads as an unsupported record, opens no message, and the two messages either\n * side of it merge: a patient from the first is then paired with results from the second, which\n * is the precise pairing {@link messages} exists to prevent. The parser does report it, as an\n * `ASTM_RECORD_UNKNOWN_TYPE` warning against that record, and a strict parse refuses the stream\n * outright. The cost is not only misattribution: delimiters are re-read at each `H` too, so an\n * unrecognized header does not re-scope them either, and where the sets differ the merged tail is\n * tokenized with the previous header's delimiters and its fields are lost rather than merely\n * misfiled. Because that warning is the only report the merge produces, a profile is **not**\n * permitted to tolerate the code and quiet it; the safety gate refuses the code when a profile is\n * defined, and declines to downgrade the warning whatever profile is in force. Read an\n * `ASTM_RECORD_UNKNOWN_TYPE` on a stream you are about to group as \"a message boundary may be\n * missing here\", not as cosmetic noise.\n *\n * Recognizing a mangled header *as* a header is deliberately not attempted. Deciding that an\n * unsupported record was \"really\" an `H` means guessing at a byte the sender did not send, and\n * that guess would silently split a stream a different way; a warning that the reader lost its\n * place is the honest answer, and refusing the stream in strict mode is the safe one.\n *\n * **What is deliberately not modeled.** ASTM's within-message record hierarchy, which `P`\n * a given `R` files against when one message carries several patients, is not modeled\n * here. The clauses that would ground it (the message-level structure diagram, and the `P`\n * sequence-number rule) are withheld from the free sample and are paywalled, so rather than\n * guess a scoping rule this layer refuses to answer: a message carrying more than one `P`\n * leaves {@link AstmStreamMessage.patient} `undefined` and surfaces all of them on\n * {@link AstmStreamMessage.patients}. Multi-patient messages are real, at least one\n * openly-published vendor interface grammar makes the patient group repeatable on the\n * download direction, so this is a deferral with a known shape, not a claim that the case\n * does not arise, and not a claim that the standard is silent about it.\n *\n * **The OSS corpus was checked and offers no prior art.** It is informative here, unlike\n * for delimiter questions, but only negatively: the two commonly-cited reference\n * implementations both dissolve the message unit. One dispatches records to per-type\n * callbacks one at a time with no carried state, leaving the grouping to the integrator;\n * the other merges every message of a transport session into one envelope of flat\n * per-record-type buckets, from which the association cannot be recovered at all. A flat\n * model is the incumbent shape, and the misattribution above is what it costs.\n */\n\nimport type { Delimiters } from \"../common/delimiters.js\";\nimport type { AstmPosition } from \"../common/position.js\";\nimport type {\n AstmMessage,\n AstmRecord,\n CommentRecord,\n HeaderRecord,\n OrderRecord,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n} from \"./types.js\";\n\n/**\n * Stable codes for the two ways a flat, stream-scoped accessor can fail to name a single\n * answer. Renaming a code is a **breaking change**.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, patient, AMBIGUOUS_CODES, AstmAmbiguousStreamError } from \"@cosyte/astm\";\n * try {\n * patient(parseAstmRecords(raw));\n * } catch (err) {\n * if (err instanceof AstmAmbiguousStreamError) {\n * err.code === AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_MESSAGE;\n * }\n * }\n * ```\n */\nexport const AMBIGUOUS_CODES = {\n /** The stream carries more than one `H` … `L` message, so a stream-wide answer spans patients. */\n ASTM_AMBIGUOUS_MULTI_MESSAGE: \"ASTM_AMBIGUOUS_MULTI_MESSAGE\",\n /** The one message in the stream carries more than one `P`, so \"the patient\" is not determined. */\n ASTM_AMBIGUOUS_MULTI_PATIENT: \"ASTM_AMBIGUOUS_MULTI_PATIENT\",\n} as const;\n\n/** A value from {@link AMBIGUOUS_CODES}: the discriminant on {@link AstmAmbiguousStreamError}. */\nexport type AmbiguousCode = (typeof AMBIGUOUS_CODES)[keyof typeof AMBIGUOUS_CODES];\n\n/**\n * Thrown by a flat extractor when the stream it was handed does not determine a single\n * answer: either because it carries several messages, or because its one message carries\n * several patients.\n *\n * The throw **is** the fix. A caller who gets this error is strictly better off than one\n * who silently received another patient's results, and the remedy is mechanical: walk\n * {@link messages} and read each message's own records.\n *\n * **PHI:** carries a stable code, a position, and two counts. Never a field value, never a\n * patient identifier.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, messages, results, AstmAmbiguousStreamError } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw);\n * try {\n * results(msg);\n * } catch (err) {\n * if (err instanceof AstmAmbiguousStreamError) {\n * for (const m of messages(msg)) m.results.length;\n * }\n * }\n * ```\n */\nexport class AstmAmbiguousStreamError extends Error {\n /** The stable discriminant: see {@link AMBIGUOUS_CODES}. */\n public readonly code: AmbiguousCode;\n /** Where the ambiguity became visible: the second header, or the second `P`. Value-free. */\n public readonly position: AstmPosition;\n /** How many `H` … `L` messages the stream carries. */\n public readonly messageCount: number;\n /** How many `P` records the message in question carries. */\n public readonly patientCount: number;\n\n /** @internal */\n public constructor(\n code: AmbiguousCode,\n message: string,\n position: AstmPosition,\n counts: { readonly messageCount: number; readonly patientCount: number },\n ) {\n super(message);\n this.name = \"AstmAmbiguousStreamError\";\n this.code = code;\n this.position = position;\n this.messageCount = counts.messageCount;\n this.patientCount = counts.patientCount;\n }\n}\n\n/**\n * One `H` … `L` message inside a parsed stream, with its own records and nothing else.\n *\n * This is the unit the flat extractors were always assumed to be reading. `AstmMessage`\n * models the whole stream; this models a message within it.\n */\nexport interface AstmStreamMessage {\n /** 0-based ordinal of this message within the stream. */\n readonly index: number;\n /** This message's header record. */\n readonly header: HeaderRecord;\n /**\n * The delimiter set in force for this message: the header's own resolved set, which is\n * the set its records were actually read with. When a later header's declaration was\n * unusable, this is the set that stayed in force, never a guessed one.\n */\n readonly delimiters: Delimiters;\n /** Every record of this message in wire order, the header first. */\n readonly records: readonly AstmRecord[];\n /**\n * This message's patient, but **only when the message determines one**: the single `P`\n * when it carries exactly one, and `undefined` when it carries none **or** several.\n * `patients.length` distinguishes those two cases; there is no third meaning.\n *\n * A message carrying several patients is not answered with the first one, because\n * \"the first `P`\" is precisely the guess that files a result against the wrong person.\n */\n readonly patient: PatientRecord | undefined;\n /** Every `P` record in this message, in wire order (usually zero or one). */\n readonly patients: readonly PatientRecord[];\n /** Every `R` (result) record in this message, in wire order. */\n readonly results: readonly ResultRecord[];\n /** Every `O` (order) record in this message, in wire order. */\n readonly orders: readonly OrderRecord[];\n /** Every `C` (comment) record in this message, in wire order. */\n readonly comments: readonly CommentRecord[];\n /**\n * Every `Q` (request-information) record in this message, in wire order.\n *\n * For this message's host-query classification, call `classifyMessage(m.records)`. The\n * `classification` on the parsed model is folded over the **whole stream**, so it is not\n * per-message and is not mirrored here; deriving it from a message's own records is.\n */\n readonly queries: readonly QueryRecord[];\n}\n\n/**\n * Split a parsed stream into the `H` … `L` messages it carries.\n *\n * This is the safe path and it never throws: a single-message stream yields exactly one\n * entry, and pairing a patient with a result inside one entry is correct by construction.\n *\n * Returns an **array**, not an iterator: the stream is already fully parsed and frozen, so\n * there is no work to defer, and every other extractor in this package returns a\n * `readonly` array.\n *\n * @param msg - A parsed stream.\n * @returns The messages, in wire order; never empty for a parsed stream.\n * @example\n * ```ts\n * import { parseAstmRecords, messages } from \"@cosyte/astm\";\n * const stream = parseAstmRecords(\"H|\\\\^&\\rP|1|PRAC-1\\rR|1|^^^687|1.0|U/L||N||F\\rL|1\\r\");\n * for (const m of messages(stream)) {\n * m.patient?.practiceAssignedId; // \"PRAC-1\"\n * m.results[0]?.value; // \"1.0\"\n * }\n * ```\n */\nexport function messages(msg: AstmMessage): readonly AstmStreamMessage[] {\n const groups: AstmRecord[][] = [];\n let current: AstmRecord[] | undefined;\n for (const record of msg.records) {\n if (record.type === \"H\" || current === undefined) {\n current = [];\n groups.push(current);\n }\n current.push(record);\n }\n\n return Object.freeze(\n groups.map((records, index) => {\n const header = records.find((r): r is HeaderRecord => r.type === \"H\") ?? msg.header;\n const patients = records.filter((r): r is PatientRecord => r.type === \"P\");\n return Object.freeze({\n index,\n header,\n delimiters: header.delimiters,\n records: Object.freeze(records),\n patient: patients.length === 1 ? patients[0] : undefined,\n patients: Object.freeze(patients),\n results: Object.freeze(records.filter((r): r is ResultRecord => r.type === \"R\")),\n orders: Object.freeze(records.filter((r): r is OrderRecord => r.type === \"O\")),\n comments: Object.freeze(records.filter((r): r is CommentRecord => r.type === \"C\")),\n queries: Object.freeze(records.filter((r): r is QueryRecord => r.type === \"Q\")),\n });\n }),\n );\n}\n\n/**\n * How many `H` … `L` messages a stream carries, and where the second one starts.\n *\n * Counts `H` records directly rather than materializing the groups: the flat extractors\n * call this on every invocation, and the answer is the same by construction.\n *\n * @internal\n */\nfunction surveyMessages(msg: AstmMessage): {\n readonly count: number;\n readonly secondHeader: AstmRecord | undefined;\n} {\n let count = 0;\n let secondHeader: AstmRecord | undefined;\n for (const record of msg.records) {\n if (record.type !== \"H\") continue;\n count += 1;\n if (count === 2) secondHeader = record;\n }\n return { count, secondHeader };\n}\n\n/**\n * Guard for a flat, stream-scoped extractor: refuse to answer for a stream carrying more\n * than one message.\n *\n * @internal\n */\nexport function assertSingleMessage(msg: AstmMessage, accessor: string): void {\n const { count, secondHeader } = surveyMessages(msg);\n if (count <= 1) return;\n throw new AstmAmbiguousStreamError(\n AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_MESSAGE,\n `${accessor} is scoped to the whole stream, and this stream carries ${String(count)} messages, so its answer would span patients. Use messages() and read each message's own records.`,\n { recordIndex: secondHeader?.recordIndex ?? 0, recordType: \"H\" },\n { messageCount: count, patientCount: 0 },\n );\n}\n\n/**\n * Guard for {@link patient}: refuse to answer for a message carrying more than one `P`.\n *\n * The same harm as the multi-message case, one level down: \"the first `P`\" is a guess\n * about which patient a result files against, and this parser does not guess.\n *\n * @internal\n */\nexport function assertSinglePatient(msg: AstmMessage, accessor: string): void {\n const patients = msg.records.filter((r): r is PatientRecord => r.type === \"P\");\n if (patients.length <= 1) return;\n throw new AstmAmbiguousStreamError(\n AMBIGUOUS_CODES.ASTM_AMBIGUOUS_MULTI_PATIENT,\n `${accessor} cannot name one patient: this message carries ${String(patients.length)} P records. Read them from messages()[n].patients and pair each with the records it owns.`,\n { recordIndex: patients[1]?.recordIndex ?? 0, recordType: \"P\" },\n { messageCount: 1, patientCount: patients.length },\n );\n}\n","/**\n * Typed extractors over an {@link AstmMessage}: the one-line \"pull the value out\"\n * surface that is the package's north star.\n *\n * They are thin and immutable: they read the already-parsed model and never re-parse,\n * never mutate, and never fabricate.\n *\n * **They are scoped to a single message, and they enforce it.** An `AstmMessage` models a\n * whole record **stream**, which may carry several `H` … `L` messages back to back. These\n * extractors read across the whole stream, so on a multi-message stream `patient()` used to\n * answer with the first `P` in the stream while `results()` answered with every `R` in it:\n * pairing the two attributed one patient's results to another, silently and with no warning.\n * They now **refuse** that stream: an {@link AstmAmbiguousStreamError} instead of a confident\n * wrong answer, which is the same rule the rest of this parser follows. {@link messages} is\n * the path that works on any stream, and `commentsFor()` keeps working on every stream\n * because its parent record already names the message.\n *\n * @see {@link messages}\n */\n\nimport { assertSingleMessage, assertSinglePatient } from \"./messages.js\";\nimport type {\n AstmMessage,\n AstmRecord,\n CommentRecord,\n OrderRecord,\n PatientRecord,\n QueryRecord,\n ResultRecord,\n} from \"./types.js\";\n\n/**\n * Every result (`R`) record in the message, in wire order.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The result records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, results } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * results(msg)[0]?.units; // \"U/L\"\n * ```\n */\nexport function results(msg: AstmMessage): readonly ResultRecord[] {\n assertSingleMessage(msg, \"results()\");\n return msg.records.filter((r): r is ResultRecord => r.type === \"R\");\n}\n\n/**\n * The message's patient (`P`) record, or `undefined` when the message carries none.\n *\n * This is the identity a result files against, so it is answered only when the stream\n * determines exactly one. It **refuses** the two shapes where \"the patient\" is a guess: a\n * stream carrying several messages, and a single message carrying several `P` records. In\n * both, the old behaviour, the first `P` in the stream, is the wrong-patient path itself.\n *\n * @param msg - A parsed single-message stream carrying at most one patient.\n * @returns The patient record, or `undefined` when the message carries none.\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message, or (`ASTM_AMBIGUOUS_MULTI_PATIENT`) when its one message\n * carries more than one `P`. Use {@link messages} and read `patients` in both cases.\n * @example\n * ```ts\n * import { parseAstmRecords, patient } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1|PRAC|LAB\\rL|1\\r\");\n * patient(msg)?.practiceAssignedId; // \"PRAC\"\n * ```\n */\nexport function patient(msg: AstmMessage): PatientRecord | undefined {\n assertSingleMessage(msg, \"patient()\");\n assertSinglePatient(msg, \"patient()\");\n return msg.records.find((r): r is PatientRecord => r.type === \"P\");\n}\n\n/**\n * Every order (`O`) record in the message, in wire order.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The order records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, orders } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rO|1|ACC-42||^^^687|R\\rL|1\\r\");\n * orders(msg)[0]?.specimenId; // \"ACC-42\"\n * ```\n */\nexport function orders(msg: AstmMessage): readonly OrderRecord[] {\n assertSingleMessage(msg, \"orders()\");\n return msg.records.filter((r): r is OrderRecord => r.type === \"O\");\n}\n\n/**\n * Every comment (`C`) record in the message, in wire order. Each carries the\n * `parentIndex` of the `H`/`P`/`O`/`R` it attaches to (or `attachedToRoot` when\n * it is an orphan): use {@link commentsFor} to get the comments of one record.\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The comment records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, comments } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|5|U/L||||F\\rC|1|I|checked|G\\rL|1\\r\");\n * comments(msg)[0]?.text; // \"checked\"\n * ```\n */\nexport function comments(msg: AstmMessage): readonly CommentRecord[] {\n assertSingleMessage(msg, \"comments()\");\n return msg.records.filter((r): r is CommentRecord => r.type === \"C\");\n}\n\n/**\n * The comment (`C`) records attached to a given parent record, in wire order.\n *\n * Unlike the other extractors this works on **any** stream, single- or multi-message: the\n * parent record it is handed already names the message, so there is nothing to disambiguate.\n * Returns the comments whose `parentIndex` is that record's `recordIndex`, so a\n * comment carrying (e.g.) QC context is read against the record it modifies:\n * never floated to the wrong one.\n *\n * @param msg - A parsed message.\n * @param parent - The `H`/`P`/`O`/`R` record whose comments to collect.\n * @returns The attached comment records (possibly empty).\n * @example\n * ```ts\n * import { parseAstmRecords, results, commentsFor } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|5|U/L||||F\\rC|1|I|checked|G\\rL|1\\r\");\n * commentsFor(msg, results(msg)[0]!)[0]?.text; // \"checked\"\n * ```\n */\nexport function commentsFor(msg: AstmMessage, parent: AstmRecord): readonly CommentRecord[] {\n return msg.records.filter(\n (r): r is CommentRecord => r.type === \"C\" && r.parentIndex === parent.recordIndex,\n );\n}\n\n/**\n * Every request-information (`Q`) record in the message, in wire order. A non-empty\n * result means the message is a **host-query request**, not a result set: see\n * {@link AstmMessage.classification} (`isHostQueryRequest`).\n *\n * @param msg - A parsed **single-message** stream.\n * @returns The query records (possibly empty).\n * @throws {@link AstmAmbiguousStreamError} (`ASTM_AMBIGUOUS_MULTI_MESSAGE`) when the stream\n * carries more than one message: see {@link messages}.\n * @example\n * ```ts\n * import { parseAstmRecords, query } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rP|1\\rQ|1|^SPEC-7||ALL\\rL|1\\r\");\n * query(msg)[0]?.queriesAllTests; // true\n * ```\n */\nexport function query(msg: AstmMessage): readonly QueryRecord[] {\n assertSingleMessage(msg, \"query()\");\n return msg.records.filter((r): r is QueryRecord => r.type === \"Q\");\n}\n","/**\n * Public barrel for the `@cosyte/astm` vendor-profile subsystem. Assembles the\n * `astmProfiles` namespace object (the built-ins) and re-exports the public profile\n * API: `defineAstmProfile`, the registry accessors, the apply helpers, the safety\n * set, the definition-time error, and the profile types.\n *\n * The individual built-ins are reached via `astmProfiles.referenceCorpus` etc., not\n * as top-level named exports: mirrors the sibling `profiles` namespace convention\n * (\"default\" is too generic for a top-level export).\n */\n\nexport { defineAstmProfile } from \"./define.js\";\nexport { AstmProfileDefinitionError } from \"./errors.js\";\nexport type {\n AstmProfile,\n DefineAstmProfileOptions,\n AstmQuirkTolerance,\n AstmQuirkMatch,\n AstmProfileProvenance,\n AnyAstmWarningCode,\n} from \"./types.js\";\nexport {\n getAstmProfile,\n listAstmProfiles,\n setDefaultAstmProfile,\n getDefaultAstmProfile,\n} from \"./registry.js\";\nexport { applyAstmProfile, applyAstmProfileToWarnings, resolveProfileTransport } from \"./apply.js\";\nexport {\n SAFETY_CRITICAL_CODES,\n TOLERABLE_CODES,\n ALL_ASTM_WARNING_CODES,\n isSafetyCriticalCode,\n} from \"./safety.js\";\n\nimport { defaultProfile } from \"./default.js\";\nimport { referenceCorpus } from \"./reference-corpus.js\";\n\n/**\n * Namespace object exposing the built-in profiles: the conservative `default`\n * baseline plus the evidence-backed `referenceCorpus` (grounded firsthand in the\n * redistributable OSS reference corpus), each authored via the public\n * `defineAstmProfile()` API and carrying its cited provenance. Named per-vendor\n * profiles (cobas / Sysmex / …) are **deferred** pending a firsthand\n * vendor-attributed quirk document: the engine fully supports them; we do not ship\n * ungrounded ones.\n *\n * @example\n * ```ts\n * import { parseAstmRecords, astmProfiles } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(raw, { profile: astmProfiles.referenceCorpus });\n * msg.profile?.name; // \"referenceCorpus\"\n * ```\n */\nexport const astmProfiles = Object.freeze({\n default: defaultProfile,\n referenceCorpus,\n}) as {\n readonly default: typeof defaultProfile;\n readonly referenceCorpus: typeof referenceCorpus;\n};\n","/**\n * The **consumer-supplied** LIVD catalog: a vendor-test-code → LOINC index built\n * from an IICC LIVD (\"LOINC to Vendor IVD\") mapping the consumer provides.\n *\n * **Bring-your-own, by design.** `@cosyte/astm` bundles\n * **no** LOINC, SNOMED, or LIVD data: LOINC is © Regenstrief (redistributable\n * only with its attribution notice) and the public CDC LIVD publication is a\n * SARS-CoV-2-specific file that also carries SNOMED CT (separately licensed), not\n * a general-analyte, public-domain catalog. So the package stays a **structural\n * recognizer, not a dictionary**: it recognizes the Universal Test ID's LOINC slot\n * and surfaces vendor codes verbatim, and this module lets a consumer\n * *supply* their own LIVD file to map those codes, the terminology data, and its\n * license obligations, are the consumer's.\n *\n * **Grounded firsthand on the IICC LIVD digital format / HL7 LIVD IG.** The\n * mapping key is the **Vendor Analyte Code**: the vendor *transmission code* the\n * instrument sends for an automated test, which is exactly the local code an ASTM\n * analyzer puts in the Universal Test ID (`R`/`O` field, component 4). The mapping\n * target is the **LOINC Code** (with the LOINC Long Common Name as an optional\n * human-readable label). Manufacturer/Model are optional provenance a consumer can\n * carry for their own auditing; this catalog keys on the vendor code alone and,\n * crucially, **refuses to choose** when one code maps to more than one LOINC.\n *\n * **Scope the catalog to the source device fleet.** The ASTM Universal Test ID\n * carries no manufacturer to disambiguate against, so two different instruments that\n * reuse the same transmission code for different analytes would both match a single\n * catalog entry. Supply a catalog built for the analyzers you actually receive from;\n * conflicting entries *within* one catalog are caught and surfaced as `ambiguous`\n * (never resolved to a guess), but cross-device code reuse the catalog cannot see is\n * a catalog-hygiene concern the consumer owns.\n */\n\nimport { deepFreeze } from \"../common/freeze.js\";\n\n/**\n * One LIVD mapping row a consumer supplies: a vendor test code and the LOINC it\n * maps to, plus optional human-readable / provenance fields. Modeled on the IICC\n * LIVD digital format's data elements.\n *\n * @example\n * ```ts\n * import type { LivdEntry } from \"@cosyte/astm\";\n * const e: LivdEntry = { vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" };\n * ```\n */\nexport interface LivdEntry {\n /**\n * The **Vendor Analyte Code**: the vendor transmission code the instrument sends (the local code\n * in an ASTM Universal Test ID, component 4). The mapping key; compared **verbatim** (exact,\n * case-sensitive) against the reported code, never normalized or fuzzy-matched.\n */\n readonly vendorCode: string;\n /**\n * The **LOINC Code** this vendor code maps to (e.g. `\"1920-8\"`). Taken from the consumer's catalog\n * as-is and **never validated, altered, or invented**: the parser does not ship a LOINC table and\n * cannot check it; it only carries what the catalog says.\n */\n readonly loinc: string;\n /** The **LOINC Long Common Name**, when the catalog supplies it: an optional human-readable label. */\n readonly loincLongName?: string;\n /** The **Vendor Analyte Name**: the vendor's human-readable analyte label, when supplied. */\n readonly vendorAnalyteName?: string;\n /** The device **Manufacturer**, when the catalog scopes the mapping to a device (optional provenance). */\n readonly manufacturer?: string;\n /** The device **Model**, when the catalog scopes the mapping to a device (optional provenance). */\n readonly model?: string;\n}\n\n/**\n * The outcome of looking a vendor code up in a {@link LivdCatalog}. A distinct\n * value per safe disposition: a hit is `mapped`; a miss is `unmapped`; a code that\n * matched more than one **distinct** LOINC is `ambiguous` with the candidates\n * surfaced but **none chosen**. There is deliberately no \"guessed\" case.\n */\nexport type LivdLookup =\n /** Exactly one LOINC (one entry, or several entries that all agree on the same LOINC). */\n | {\n readonly status: \"mapped\";\n readonly loinc: string;\n readonly loincLongName?: string;\n }\n /** No entry for this code: a miss. The code stays verbatim; no LOINC is invented. */\n | { readonly status: \"unmapped\" }\n /** More than one distinct LOINC: surfaced for inspection, never resolved to one. */\n | { readonly status: \"ambiguous\"; readonly candidates: readonly string[] };\n\n/**\n * An immutable, consumer-supplied LIVD catalog. Look a vendor code up with\n * {@link LivdCatalog.lookup}; the catalog **never** picks between conflicting\n * LOINCs and **never** mutates. Build one with {@link defineLivdCatalog}.\n */\nexport interface LivdCatalog {\n /** The number of distinct vendor codes indexed (not the number of input rows). */\n readonly size: number;\n /**\n * Look a vendor code up, verbatim (exact, case-sensitive). Returns `mapped` on a\n * single-LOINC hit, `unmapped` on a miss, and `ambiguous` when the code carries\n * more than one distinct LOINC: never a guess.\n *\n * @param vendorCode - The reported vendor/local test code.\n * @returns The lookup outcome.\n */\n lookup(vendorCode: string): LivdLookup;\n}\n\n/**\n * Build a {@link LivdCatalog} from LIVD entries a consumer supplies. Entries are\n * indexed by their `vendorCode` (verbatim). When several entries share a vendor\n * code:\n *\n * - all agreeing on the same `loinc` → a single `mapped` result (the first entry's\n * optional `loincLongName` is kept);\n * - disagreeing (two distinct LOINCs) → an `ambiguous` result carrying every\n * distinct candidate, and **no** choice between them.\n *\n * The returned catalog is deeply frozen; nothing is mutated after construction.\n *\n * @param entries - The consumer's LIVD mapping rows.\n * @returns An immutable catalog.\n * @example\n * ```ts\n * import { defineLivdCatalog } from \"@cosyte/astm\";\n * const catalog = defineLivdCatalog([\n * { vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" },\n * { vendorCode: \"690\", loinc: \"1742-6\", loincLongName: \"ALT\" },\n * ]);\n * catalog.lookup(\"687\"); // { status: \"mapped\", loinc: \"1920-8\", loincLongName: \"AST\" }\n * catalog.lookup(\"999\"); // { status: \"unmapped\" }\n * ```\n */\nexport function defineLivdCatalog(entries: readonly LivdEntry[]): LivdCatalog {\n const index = new Map<string, LivdEntry[]>();\n for (const entry of entries) {\n const bucket = index.get(entry.vendorCode);\n if (bucket) bucket.push(entry);\n else index.set(entry.vendorCode, [entry]);\n }\n\n const catalog: LivdCatalog = {\n size: index.size,\n lookup(vendorCode: string): LivdLookup {\n const bucket = index.get(vendorCode);\n if (!bucket || bucket.length === 0) return { status: \"unmapped\" };\n\n const distinct = [...new Set(bucket.map((e) => e.loinc))];\n if (distinct.length > 1) {\n return deepFreeze({ status: \"ambiguous\", candidates: distinct });\n }\n\n const [first] = bucket;\n if (first === undefined) return { status: \"unmapped\" };\n return deepFreeze({\n status: \"mapped\",\n loinc: first.loinc,\n ...(first.loincLongName !== undefined ? { loincLongName: first.loincLongName } : {}),\n });\n },\n };\n return Object.freeze(catalog);\n}\n","/**\n * The **terminology** warning registry (`ASTM_LIVD_*`) for the LIVD-aware LOINC\n * recognition layer: the fourth of the package's registries,\n * alongside the record layer's `ASTM_RECORD_*`, the frame codec's `ASTM_FRAME_*`,\n * and the protocol reducer's `ASTM_LTP_*`.\n *\n * A terminology warning is the record of a **safe non-mapping**: a reported\n * vendor test code that a consumer-supplied LIVD catalog did not map to a single\n * LOINC. It never carries a LOINC the catalog did not vouch for: the whole point\n * of the layer is that an unmapped or ambiguous code is surfaced as *unmapped*,\n * **never a guessed LOINC** (mis-identifying a test is safety-critical). Every\n * warning carries a stable code plus positional context ({@link AstmPosition}) and\n * **never** a field value. Consumers compare `warning.code ===\n * LIVD_WARNING_CODES.<CODE>`; renaming a code is a **breaking change**.\n *\n * These codes live in their own registry and are deliberately **not** part of the\n * profile safety gate's universe (`ALL_ASTM_WARNING_CODES`): a LIVD non-mapping is\n * a post-parse advisory produced by the opt-in {@link applyLivd} helper, not a\n * parse-time deviation a vendor profile could ever \"tolerate\". A profile referencing\n * one is rejected as an unknown code: correctly, since there is nothing here to\n * quiet.\n */\n\nimport type { AstmPosition } from \"../common/position.js\";\n\n/**\n * Stable string codes for every terminology (LIVD) warning. `key === value` so\n * `Object.values(...)` yields a stable snapshot set.\n *\n * @example\n * ```ts\n * import { LIVD_WARNING_CODES } from \"@cosyte/astm\";\n * LIVD_WARNING_CODES.ASTM_LIVD_UNMAPPED_CODE; // \"ASTM_LIVD_UNMAPPED_CODE\"\n * ```\n */\nexport const LIVD_WARNING_CODES = {\n /**\n * A record carried a vendor/local test code, but the consumer-supplied LIVD catalog held **no**\n * entry for it. The code stays surfaced verbatim and the mapping is `unmapped`: a LOINC is\n * **never** guessed. Purely advisory: the raw code and value are untouched.\n */\n ASTM_LIVD_UNMAPPED_CODE: \"ASTM_LIVD_UNMAPPED_CODE\",\n /**\n * A vendor/local test code matched **more than one distinct LOINC** in the catalog (e.g. the same\n * transmission code used by two devices for different analytes). The mapping is `ambiguous` and the\n * candidate LOINCs are surfaced for inspection, but **none is chosen**: refusing to pick is the\n * fail-safe, since guessing wrong mis-identifies the test.\n */\n ASTM_LIVD_AMBIGUOUS_MAPPING: \"ASTM_LIVD_AMBIGUOUS_MAPPING\",\n} as const;\n\n/**\n * Discriminant type for {@link AstmLivdWarning.code}. Narrowing by this code lets\n * consumers write exhaustive `switch` blocks against {@link LIVD_WARNING_CODES}.\n */\nexport type LivdWarningCode = (typeof LIVD_WARNING_CODES)[keyof typeof LIVD_WARNING_CODES];\n\n/**\n * A single terminology (LIVD) warning: a stable code, a value-free human-readable\n * message, and positional context. Never carries a test code, a value, or a LOINC.\n *\n * @example\n * ```ts\n * import type { AstmLivdWarning } from \"@cosyte/astm\";\n * const w: AstmLivdWarning = {\n * code: \"ASTM_LIVD_UNMAPPED_CODE\",\n * message: \"Reported test code had no LIVD mapping, surfaced unmapped, never a guessed LOINC.\",\n * position: { recordIndex: 3, recordType: \"R\" },\n * };\n * ```\n */\nexport interface AstmLivdWarning {\n readonly code: LivdWarningCode;\n /** Human-readable detail for logs. Never contains a test code, a value, or a LOINC. */\n readonly message: string;\n readonly position: AstmPosition;\n}\n\n/**\n * Build an `ASTM_LIVD_UNMAPPED_CODE` warning. The reported code stays verbatim and\n * the mapping is `unmapped`; no LOINC is fabricated.\n *\n * @param position - Where the unmapped code was seen (record ordinal + type; never the code).\n * @returns The warning.\n * @example\n * ```ts\n * import { livdUnmappedCode } from \"@cosyte/astm\";\n * livdUnmappedCode({ recordIndex: 3, recordType: \"R\" });\n * ```\n */\nexport function livdUnmappedCode(position: AstmPosition): AstmLivdWarning {\n return {\n code: LIVD_WARNING_CODES.ASTM_LIVD_UNMAPPED_CODE,\n message: \"Reported test code had no LIVD mapping, surfaced unmapped, never a guessed LOINC.\",\n position,\n };\n}\n\n/**\n * Build an `ASTM_LIVD_AMBIGUOUS_MAPPING` warning. The code matched multiple distinct\n * LOINCs; none is chosen (refusing to pick is the fail-safe).\n *\n * @param position - Where the ambiguous code was seen (record ordinal + type; never the code).\n * @returns The warning.\n * @example\n * ```ts\n * import { livdAmbiguousMapping } from \"@cosyte/astm\";\n * livdAmbiguousMapping({ recordIndex: 4, recordType: \"O\" });\n * ```\n */\nexport function livdAmbiguousMapping(position: AstmPosition): AstmLivdWarning {\n return {\n code: LIVD_WARNING_CODES.ASTM_LIVD_AMBIGUOUS_MAPPING,\n message:\n \"Reported test code mapped to multiple distinct LOINCs, surfaced ambiguous, never one guessed.\",\n position,\n };\n}\n","/**\n * The **additive, advisory** LIVD annotation layer. {@link applyLivd}\n * reads a parsed {@link AstmMessage} and a consumer-supplied {@link LivdCatalog}\n * and returns a **separate** layer of per-record LOINC annotations: it **never\n * mutates, alters, or drops** the raw reported code or value. Recognition is an\n * annotation, not a rewrite: the wire stays exactly as parsed.\n *\n * **The never-fabricate rule (the safety-critical point).** A vendor code with no\n * single LIVD mapping surfaces as a typed `unmapped`/`ambiguous`/`no-code`\n * annotation: **never** a guessed LOINC. A wrong LOINC mis-identifies a test, so\n * the layer only ever reports a LOINC the wire itself carried (an inline LOINC\n * candidate) or one the consumer's catalog vouched for (labeled `derived`), and\n * otherwise reports nothing but the miss.\n */\n\nimport { primaryCode, recognizeUniversalTestId } from \"../common/coding-system.js\";\nimport { deepFreeze } from \"../common/freeze.js\";\nimport type { UniversalTestId, UniversalTestIdProvenance } from \"../common/coding-system.js\";\nimport type { AstmMessage, OrderRecord, ResultRecord } from \"../records/types.js\";\n\nimport type { LivdCatalog } from \"./catalog.js\";\nimport { livdAmbiguousMapping, livdUnmappedCode } from \"./warnings.js\";\nimport type { AstmLivdWarning } from \"./warnings.js\";\n\n/**\n * The outcome of annotating one record's Universal Test ID against a LIVD catalog.\n * A distinct case per disposition; there is no case in which a LOINC is guessed.\n *\n * - `mapped`, the vendor/local code resolved to a single LOINC **via the catalog**\n * (labeled `derived: true`, `source: \"livd\"`).\n * - `inline-loinc`, the wire itself carried a LOINC in the Universal Test ID's slot\n * (component 1); surfaced `source: \"wire\"`, **not** derived and **not** validated.\n * - `unmapped`: a vendor/local code with no catalog entry.\n * - `ambiguous`: a vendor/local code matching more than one distinct LOINC; the\n * candidates are surfaced but **none is chosen**.\n * - `no-code`: the record carried no usable test code at all (name-only/empty), so\n * there was nothing to map.\n */\nexport type LivdMapping =\n | {\n readonly status: \"mapped\";\n readonly loinc: string;\n readonly loincLongName?: string;\n readonly source: \"livd\";\n readonly derived: true;\n }\n | { readonly status: \"inline-loinc\"; readonly loinc: string; readonly source: \"wire\" }\n | { readonly status: \"unmapped\" }\n | { readonly status: \"ambiguous\"; readonly candidates: readonly string[] }\n | { readonly status: \"no-code\" };\n\n/**\n * One record's LIVD annotation, which record, the code that was looked up\n * (verbatim), how it was recognized, and the mapping outcome. Additive: it points\n * at the record by index and never replaces it.\n *\n * @example\n * ```ts\n * import type { LivdAnnotation } from \"@cosyte/astm\";\n * const a: LivdAnnotation = {\n * recordIndex: 3,\n * recordType: \"R\",\n * reportedCode: \"687\",\n * provenance: \"local-code\",\n * mapping: { status: \"mapped\", loinc: \"1920-8\", source: \"livd\", derived: true },\n * };\n * ```\n */\nexport interface LivdAnnotation {\n /** The `recordIndex` of the annotated `R`/`O` record. */\n readonly recordIndex: number;\n /** The annotated record's type. */\n readonly recordType: \"R\" | \"O\";\n /** The reported primary code that was looked up, verbatim; absent when the record carried no code. */\n readonly reportedCode?: string;\n /** How the reported code was recognized in the Universal Test ID (provenance only, never a lookup). */\n readonly provenance: UniversalTestIdProvenance;\n /** The mapping outcome: never a guessed LOINC. */\n readonly mapping: LivdMapping;\n}\n\n/**\n * The result of {@link applyLivd}: the per-record annotations (one per `R`/`O`\n * record) and the value-free warnings for every unmapped or ambiguous code. Both\n * arrays are deeply frozen; the source message is untouched.\n */\nexport interface LivdResult {\n /** One annotation per `R`/`O` record, in wire order. */\n readonly annotations: readonly LivdAnnotation[];\n /** A value-free warning per `unmapped`/`ambiguous` code: never per `mapped`/`inline-loinc`/`no-code`. */\n readonly warnings: readonly AstmLivdWarning[];\n}\n\n/** Map a recognized Universal Test ID against the catalog: the pure core of the annotation. */\nfunction mapTestId(uid: UniversalTestId, catalog: LivdCatalog): LivdMapping {\n // The wire already carries a candidate LOINC in component 1: surface it as-is,\n // from the wire, never derived and never validated. It is the primary code.\n if (uid.loincCandidate !== undefined) {\n return { status: \"inline-loinc\", loinc: uid.loincCandidate, source: \"wire\" };\n }\n\n const code = primaryCode(uid);\n if (code === undefined) return { status: \"no-code\" };\n\n const hit = catalog.lookup(code);\n switch (hit.status) {\n case \"mapped\":\n return {\n status: \"mapped\",\n loinc: hit.loinc,\n ...(hit.loincLongName !== undefined ? { loincLongName: hit.loincLongName } : {}),\n source: \"livd\",\n derived: true,\n };\n case \"ambiguous\":\n return { status: \"ambiguous\", candidates: hit.candidates };\n case \"unmapped\":\n return { status: \"unmapped\" };\n }\n}\n\n/** Recognize a record's Universal Test ID whether it arrived pre-recognized or as raw components. */\nfunction testIdOf(record: ResultRecord | OrderRecord): UniversalTestId | undefined {\n return record.universalTestId;\n}\n\n/**\n * Annotate one `R` or `O` record against a LIVD catalog. The building block of\n * {@link applyLivd}; useful when a consumer holds a single record. Never mutates\n * the record and never fabricates a LOINC.\n *\n * @param record - The result/order record to annotate.\n * @param catalog - The consumer-supplied LIVD catalog.\n * @returns The record's annotation.\n * @example\n * ```ts\n * import { parseAstmRecords, results, defineLivdCatalog, lookupLivdForRecord } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const catalog = defineLivdCatalog([{ vendorCode: \"687\", loinc: \"1920-8\" }]);\n * lookupLivdForRecord(results(msg)[0]!, catalog).mapping.status; // \"mapped\"\n * ```\n */\nexport function lookupLivdForRecord(\n record: ResultRecord | OrderRecord,\n catalog: LivdCatalog,\n): LivdAnnotation {\n const uid = testIdOf(record);\n const recognized = uid ?? recognizeUniversalTestId([]);\n const mapping = mapTestId(recognized, catalog);\n const reportedCode = primaryCode(recognized);\n return {\n recordIndex: record.recordIndex,\n recordType: record.type,\n ...(reportedCode !== undefined ? { reportedCode } : {}),\n provenance: recognized.provenance,\n mapping,\n };\n}\n\n/**\n * Apply a consumer-supplied LIVD catalog to a parsed message, producing an\n * **additive, advisory** layer of LOINC annotations over its `R` (result) and `O`\n * (order) records. The source message is never mutated; the raw reported codes and\n * values stay exactly as parsed.\n *\n * Fail-safe: with no matching entry a code is `unmapped` (+ an\n * `ASTM_LIVD_UNMAPPED_CODE` warning); with a conflicting mapping it is `ambiguous`\n * (+ an `ASTM_LIVD_AMBIGUOUS_MAPPING` warning); a LOINC is **never** guessed. When\n * the wire already carried an inline LOINC candidate, it is surfaced from the wire\n * (never overwritten by the catalog).\n *\n * @param msg - A parsed ASTM message.\n * @param catalog - The consumer-supplied LIVD catalog (build with {@link defineLivdCatalog}).\n * @returns The annotations and value-free warnings (deeply frozen).\n * @example\n * ```ts\n * import { parseAstmRecords, defineLivdCatalog, applyLivd } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const catalog = defineLivdCatalog([{ vendorCode: \"687\", loinc: \"1920-8\", loincLongName: \"AST\" }]);\n * const { annotations } = applyLivd(msg, catalog);\n * annotations[0]?.mapping; // { status: \"mapped\", loinc: \"1920-8\", loincLongName: \"AST\", source: \"livd\", derived: true }\n * ```\n */\nexport function applyLivd(msg: AstmMessage, catalog: LivdCatalog): LivdResult {\n const annotations: LivdAnnotation[] = [];\n const warnings: AstmLivdWarning[] = [];\n\n for (const record of msg.records) {\n if (record.type !== \"R\" && record.type !== \"O\") continue;\n const annotation = lookupLivdForRecord(record, catalog);\n annotations.push(annotation);\n\n const position = { recordIndex: record.recordIndex, recordType: record.type };\n if (annotation.mapping.status === \"unmapped\") {\n warnings.push(livdUnmappedCode(position));\n } else if (annotation.mapping.status === \"ambiguous\") {\n warnings.push(livdAmbiguousMapping(position));\n }\n }\n\n return deepFreeze({ annotations, warnings });\n}\n","/**\n * The four LTP (low-level transfer protocol) control bytes for ASTM/CLSI-LIS01\n * (was E1381), plus the bare-record-letter set the transport detector keys on.\n *\n * The frame codec (`../frames`) owns the *inside* of a frame (`STX FN text\n * ETB/ETX CS CRLF`); this module owns the *handshake around* frames, the\n * establishment/transfer/termination signalling that the {@link ltpReduce}\n * reducer consumes. These are the raw byte values; the reducer and the transport\n * detector are the only consumers.\n */\n\n/** Enquiry (`0x05`): the sender's request to establish a transfer. */\nexport const ENQ = 0x05;\n/** Acknowledge (`0x06`): the receiver accepted the last establishment or frame. */\nexport const ACK = 0x06;\n/** Negative acknowledge (`0x15`): the receiver rejected the last frame; retransmit, do not accept. */\nexport const NAK = 0x15;\n/** End of transmission (`0x04`): the sender terminated the transfer; the line returns to neutral. */\nexport const EOT = 0x04;\n\n/**\n * The ASTM record-type letters that can legally *lead a de-framed record stream*.\n * A raw-TCP stream (framing dropped) begins with a bare record letter, in\n * practice `H` (message header), where a framed stream begins with `STX` or\n * `ENQ`. The transport detector uses membership here as the \"this is raw record\n * bytes, not a frame\" signal.\n *\n * The set is the nine ASTM record types (`H`/`P`/`O`/`R`/`C`/`Q`/`M`/`S`/`L`); a\n * conformant record stream always opens with `H`, but tolerating any record\n * letter keeps the detector robust to a capture that starts mid-message.\n */\nexport const RECORD_TYPE_LETTERS: ReadonlySet<number> = new Set(\n [...\"HPORCQMSL\"].map((c) => c.charCodeAt(0)),\n);\n","/**\n * Byte constants and limits for the ASTM/CLSI-LIS01 (was E1381) frame codec.\n *\n * A frame on the wire is `<STX> FN text <ETB|ETX> C1 C2 <CR> <LF>`: an `STX`\n * start byte, a single ASCII frame-number digit `FN` (`0`–`7`), up to 240 bytes\n * of record text, an `ETB` (intermediate) or `ETX` (final) terminator, a\n * two-hex-char modulo-256 checksum, and a `CR`+`LF` tail. These are the raw byte\n * values; the decoder in `./decode.ts` is the only consumer.\n */\n\n/** Start of a frame (`0x02`). */\nexport const STX = 0x02;\n/** Final-frame terminator (`0x03`): the last frame of a record. */\nexport const ETX = 0x03;\n/** Intermediate-frame terminator (`0x17`): the record continues in the next frame. */\nexport const ETB = 0x17;\n/** Carriage return (`0x0D`): closes a frame after the checksum. */\nexport const CR = 0x0d;\n/** Line feed (`0x0A`): follows the `CR` at the end of a frame. */\nexport const LF = 0x0a;\n\n/** ASCII `0` (`0x30`): the low end of the frame-number digit range. */\nexport const FN_ZERO = 0x30;\n/** ASCII `7` (`0x37`): the high end of the frame-number digit range. */\nexport const FN_SEVEN = 0x37;\n\n/**\n * The maximum record-text length in a single frame (240 bytes). The seven framing\n * control bytes (`STX`, `FN`, the terminator, the two checksum chars, `CR`, `LF`)\n * are **not** counted toward this limit: only the record text between `FN` and the\n * terminator is. Text longer than this without a split is an oversize deviation.\n */\nexport const MAX_FRAME_TEXT = 240;\n\n/** The frame-number sequence is modulo 8 (`0`–`7`), and by convention starts at `1`. */\nexport const FRAME_NUMBER_MODULUS = 8;\n/** The frame number of the first frame of a transfer (`1`); the sequence rolls over `7 → 0 → 1`. */\nexport const FIRST_FRAME_NUMBER = 1;\n","/**\n * Framed-vs-raw transport auto-detection for ASTM/CLSI-LIS01.\n *\n * ASTM transport is not uniform, and that is what breaks naive parsers. **Serial**\n * (RS-232) always uses full E1381 framing. Over **TCP it varies within a single\n * vendor**: the Roche cobas 4800 and iNTERFACEWARE Iguana retain the full\n * `ENQ`/`ACK` + `STX`/checksum framing over TCP (**framed-TCP**), while the cobas\n * b121 drops all low-level framing, \"TCP itself ensures correctness\", and streams\n * de-framed record bytes directly (**raw-TCP**). A reader must handle both and must\n * not assume the handshake is present.\n *\n * {@link detectFraming} decides which it is from the stream's leading byte: `STX`\n * or `ENQ` ⇒ framed (feed it to the frame codec + {@link ltpReduce}); a bare record\n * letter ⇒ raw (feed it straight to `parseAstmRecords`). Anything else is\n * **ambiguous**, and the fail-safe is to **default to framed** and warn, never to\n * guess silently into data loss, with an explicit override for a profile that\n * knows better.\n */\n\nimport { ENQ, RECORD_TYPE_LETTERS } from \"./constants.js\";\nimport { STX } from \"../frames/constants.js\";\nimport { ltpAmbiguousTransport, type AstmLtpWarning } from \"./warnings.js\";\n\n/**\n * The transport framing of an ASTM byte stream: `\"framed\"` (E1381 `STX`/checksum\n * frames, the serial and framed-TCP realities) or `\"raw\"` (framing dropped,\n * de-framed record bytes streamed directly, the raw-TCP reality).\n */\nexport type AstmFraming = \"framed\" | \"raw\";\n\n/**\n * Options for {@link detectFraming}.\n */\nexport interface DetectFramingOptions {\n /**\n * A profile-supplied override. When set, detection is bypassed entirely and this\n * value is returned with no warning: the way a vendor profile forces raw for\n * a cobas b121 or framed for a cobas 4800 regardless of the leading byte.\n */\n readonly override?: AstmFraming;\n}\n\n/**\n * The result of {@link detectFraming}: the decided framing plus any warning\n * (exactly one `ASTM_LTP_AMBIGUOUS_TRANSPORT` when the lead was unrecognizable and\n * the mode was defaulted; empty otherwise).\n */\nexport interface DetectFramingResult {\n /** The decided transport framing. */\n readonly framing: AstmFraming;\n /** `true` when the lead byte was unrecognizable and `framing` was defaulted (not inferred). */\n readonly defaulted: boolean;\n /** The detection warnings (a single ambiguity warning, or none). */\n readonly warnings: readonly AstmLtpWarning[];\n}\n\n/**\n * Detect whether an ASTM byte stream is framed or raw from its leading byte.\n *\n * - Leading `STX` (`0x02`) or `ENQ` (`0x05`) ⇒ `\"framed\"`.\n * - A leading bare record letter (`H`/`P`/`O`/`R`/`C`/`Q`/`M`/`S`/`L`) ⇒ `\"raw\"`.\n * - Anything else (including an empty stream) is **ambiguous** ⇒ defaults to\n * `\"framed\"` and emits one `ASTM_LTP_AMBIGUOUS_TRANSPORT` warning.\n *\n * An `override` short-circuits all of the above, returning the forced mode with no\n * warning.\n *\n * @param bytes - The stream to inspect (only the leading byte is read).\n * @param options - Detection options; an `override` forces the result.\n * @returns The decided framing, whether it was defaulted, and any warning.\n * @example\n * ```ts\n * import { detectFraming } from \"@cosyte/astm\";\n * detectFraming(new Uint8Array([0x02])).framing; // \"framed\" (STX)\n * detectFraming(new Uint8Array([0x48])).framing; // \"raw\" (leading \"H\")\n * detectFraming(new Uint8Array([0x2a])).framing; // \"framed\" (ambiguous → defaulted, + warning)\n * detectFraming(new Uint8Array([0x48]), { override: \"framed\" }).framing; // \"framed\" (forced)\n * ```\n */\nexport function detectFraming(\n bytes: Uint8Array,\n options: DetectFramingOptions = {},\n): DetectFramingResult {\n if (options.override !== undefined) {\n return { framing: options.override, defaulted: false, warnings: [] };\n }\n\n const lead = bytes[0];\n if (lead === STX || lead === ENQ) {\n return { framing: \"framed\", defaulted: false, warnings: [] };\n }\n if (lead !== undefined && RECORD_TYPE_LETTERS.has(lead)) {\n return { framing: \"raw\", defaulted: false, warnings: [] };\n }\n\n // Unrecognizable (or empty): default to framed, the safer assumption, since treating a framed\n // stream as raw would feed control/checksum bytes to the record parser and corrupt fields.\n return { framing: \"framed\", defaulted: true, warnings: [ltpAmbiguousTransport()] };\n}\n","/**\n * The pure LTP protocol reducer: {@link ltpReduce} and {@link ltpInitialState}.\n *\n * A deterministic, socket-free receiver-side state machine for ASTM/CLSI-LIS01\n * (LIS01-A2). The consumer owns the wire (it reads bytes, decodes frames with\n * `decodeAstmFrames`, and drives this reducer one {@link LtpEvent} at a time) and\n * the reducer decides, purely, what happens next: whether to `ACK` or `NAK`, when a\n * record is complete, and when the session returns to neutral.\n *\n * **The one inviolable rule (borrowed from `mllp`'s ACK-failsafe discipline):** a\n * frame the codec did not vouch for (a bad checksum, an unterminated frame, or one\n * out of sequence) is answered with `NAK`, **never** a fabricated positive `ACK`,\n * and its bytes are **never** appended to a record or delivered. A `NAK` drives\n * retransmit, not acceptance. This is the protocol-level twin of the frame codec's\n * \"a bad-checksum frame is never merged into a record.\"\n */\n\nimport { FRAME_NUMBER_MODULUS, FIRST_FRAME_NUMBER } from \"../frames/constants.js\";\nimport type { AstmFrame } from \"../frames/types.js\";\nimport { ltpFrameRejected, ltpUnexpectedEvent, type AstmLtpWarning } from \"./warnings.js\";\nimport type { LtpAction, LtpEvent, LtpState, LtpTransition } from \"./types.js\";\n\nconst EMPTY = new Uint8Array(0);\n\n/**\n * The neutral starting state for a fresh session: line idle, no record in flight,\n * nothing delivered yet. A consumer creates one of these when a connection opens\n * and folds inbound events through {@link ltpReduce} from here.\n *\n * @returns A frozen neutral {@link LtpState}.\n * @example\n * ```ts\n * import { ltpInitialState, ltpReduce } from \"@cosyte/astm\";\n * let state = ltpInitialState();\n * const step = ltpReduce(state, { type: \"enq\" });\n * step.actions; // [{ type: \"sendAck\" }]\n * state = step.state;\n * ```\n */\nexport function ltpInitialState(): LtpState {\n return freezeState({\n phase: \"neutral\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: [],\n recordOpen: false,\n openRecord: EMPTY,\n });\n}\n\n/**\n * Advance the LTP session by one inbound event: a pure function, no I/O.\n *\n * Behaviour by phase:\n * - **neutral**: `enq` accepts establishment (`sendAck`, enter transfer); `eot` is\n * a benign line reset (no-op); `ack`/`nak` are unexpected at a receiver (surfaced,\n * never read as acceptance); a `frame` before establishment is tolerated\n * (Postel's Law), the session auto-establishes and processes it, with a warning.\n * - **transfer**: a `frame` is accepted (`sendAck`, appended, sequence advanced) only\n * when it is trusted *and* carries the expected number; a duplicate of the last\n * accepted frame is idempotently re-`ACK`ed without re-appending; a bad or\n * out-of-sequence frame is `NAK`ed and dropped. `eot` terminates the transfer and\n * returns to neutral (a record left open on an `ETB` is **not** delivered). `enq`\n * restarts establishment. `ack`/`nak` are unexpected.\n *\n * @param state - The current session state.\n * @param event - The inbound event.\n * @returns The next state, the actions to take, and any warnings.\n * @example\n * ```ts\n * import { ltpInitialState, ltpReduce } from \"@cosyte/astm\";\n * // A bad-checksum frame is NAKed, never ACKed, and never appended.\n * const est = ltpReduce(ltpInitialState(), { type: \"enq\" }).state;\n * const bad = { trusted: false } as never;\n * ltpReduce(est, { type: \"frame\", frame: bad }).actions; // [{ type: \"sendNak\" }]\n * ```\n */\nexport function ltpReduce(state: LtpState, event: LtpEvent): LtpTransition {\n switch (event.type) {\n case \"enq\":\n return establish(state);\n case \"eot\":\n return terminate(state);\n case \"ack\":\n case \"nak\":\n // A receiver sends ACK/NAK; receiving one is a role/protocol violation. Surface it and hold:\n // an inbound ACK is NEVER read as acceptance of data (that is the sender's signal, not ours).\n return { state, actions: [], warnings: [ltpUnexpectedEvent()] };\n case \"frame\":\n return onFrame(state, event.frame);\n }\n}\n\n/** `neutral --enq--> transfer`, or a mid-transfer `enq` restart. Establishment is accepted with an ACK. */\nfunction establish(state: LtpState): LtpTransition {\n const warnings: AstmLtpWarning[] = state.phase === \"transfer\" ? [ltpUnexpectedEvent()] : [];\n // Enter (or restart) transfer: reset the frame counters and drop any half-built record, a partial\n // reassembly is never carried across an establishment boundary, never delivered as if whole.\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions: [{ type: \"sendAck\" }], warnings };\n}\n\n/** `--eot--> neutral`. Terminating discards any open (unterminated) record: no partial is invented. */\nfunction terminate(state: LtpState): LtpTransition {\n const next = freezeState({\n phase: \"neutral\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions: [], warnings: [] };\n}\n\n/** A frame arrived. In neutral, tolerate the missing handshake (auto-establish + warn), then process. */\nfunction onFrame(state: LtpState, frame: AstmFrame): LtpTransition {\n if (state.phase === \"neutral\") {\n const established = freezeState({\n phase: \"transfer\",\n expectedFrame: FIRST_FRAME_NUMBER,\n records: state.records,\n recordOpen: false,\n openRecord: EMPTY,\n });\n const step = processTransferFrame(established, frame);\n return {\n state: step.state,\n actions: step.actions,\n warnings: [ltpUnexpectedEvent(frame.frameNumber), ...step.warnings],\n };\n }\n return processTransferFrame(state, frame);\n}\n\n/**\n * The safety-critical core: decide `ACK` vs `NAK` for a frame received in transfer.\n *\n * A frame is accepted (`sendAck`, text appended, sequence advanced, and (on `ETX`)\n * the completed record delivered) **only** when it is trusted (codec-vouched\n * checksum + terminator) *and* carries the expected sequence number. A duplicate of\n * the last accepted frame is re-`ACK`ed idempotently. Everything else (bad checksum,\n * unterminated, out of sequence) is `NAK`ed and dropped: never appended, never\n * delivered, sequence never advanced.\n */\nfunction processTransferFrame(state: LtpState, frame: AstmFrame): LtpTransition {\n // ── Fail-safe: an untrusted frame is NAKed, full stop. No ACK is ever fabricated for it. ──\n if (!frame.trusted) {\n return reject(state, frame.frameNumber);\n }\n\n const fn = frame.frameNumber;\n // A trusted frame is terminated with a valid checksum, so its frame number was read; guard anyway.\n if (fn === undefined) {\n return reject(state, undefined);\n }\n\n // ── Duplicate retransmit: the sender re-sent an already-accepted frame (it missed our ACK). ──\n // Re-ACK idempotently; do NOT re-append or advance. (Not a deviation, normal recovery.)\n if (state.lastAcceptedFrame !== undefined && fn === state.lastAcceptedFrame) {\n return { state, actions: [{ type: \"sendAck\" }], warnings: [] };\n }\n\n // ── Out of sequence: a trusted frame carrying the wrong number. Never bridge the gap: NAK it. ──\n if (fn !== state.expectedFrame) {\n return reject(state, fn);\n }\n\n return acceptFrame(state, frame, fn);\n}\n\n/**\n * Accept a trusted, in-sequence frame: `sendAck`, append its text to the open\n * record, deliver on `ETX`, and advance the sequence counter.\n */\nfunction acceptFrame(state: LtpState, frame: AstmFrame, fn: number): LtpTransition {\n const nextExpected = (fn + 1) % FRAME_NUMBER_MODULUS;\n const accumulated = concatBytes([state.openRecord, frame.text]);\n const actions: LtpAction[] = [{ type: \"sendAck\" }];\n\n if (frame.terminator === \"ETX\") {\n // Record complete: the accumulated bytes (prior ETB frames + this final text) are one record.\n // Hand the action its own copy so the delivered bytes never alias the entry stored in\n // `state.records`: mutating one must never retroactively mutate the other.\n actions.push({ type: \"deliverRecord\", record: accumulated.slice() });\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: nextExpected,\n lastAcceptedFrame: fn,\n records: [...state.records, accumulated],\n recordOpen: false,\n openRecord: EMPTY,\n });\n return { state: next, actions, warnings: [] };\n }\n\n // ETB: the record continues in a later frame; carry the accumulated bytes forward.\n const next = freezeState({\n phase: \"transfer\",\n expectedFrame: nextExpected,\n lastAcceptedFrame: fn,\n records: state.records,\n recordOpen: true,\n openRecord: accumulated,\n });\n return { state: next, actions, warnings: [] };\n}\n\n/** Reject a frame: `sendNak`, no append, no advance, plus a frame-rejected warning. */\nfunction reject(state: LtpState, frameNumber: number | undefined): LtpTransition {\n return {\n state,\n actions: [{ type: \"sendNak\" }],\n warnings: [ltpFrameRejected(frameNumber)],\n };\n}\n\n/**\n * Build a frozen {@link LtpState}. `lastAcceptedFrame` is omitted (not set to\n * `undefined`) when absent, keeping `exactOptionalPropertyTypes` happy. The\n * `records` array is copied and frozen; the byte payloads are copies the caller owns\n * (a typed array with elements cannot itself be `Object.freeze`d).\n */\nfunction freezeState(fields: {\n readonly phase: LtpState[\"phase\"];\n readonly expectedFrame: number;\n readonly lastAcceptedFrame?: number;\n readonly records: readonly Uint8Array[];\n readonly recordOpen: boolean;\n readonly openRecord: Uint8Array;\n}): LtpState {\n const state: LtpState = {\n phase: fields.phase,\n expectedFrame: fields.expectedFrame,\n ...(fields.lastAcceptedFrame !== undefined\n ? { lastAcceptedFrame: fields.lastAcceptedFrame }\n : {}),\n records: Object.freeze([...fields.records]),\n recordOpen: fields.recordOpen,\n openRecord: fields.openRecord,\n };\n return Object.freeze(state);\n}\n\n/** Concatenate byte chunks into one `Uint8Array` (the reassembled record bytes). */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * The ASTM/CLSI-LIS01 modulo-256 frame checksum.\n *\n * The checksum is the sum, modulo 256, of every byte **after** the `STX` up to\n * and **including** the `ETB`/`ETX` terminator: i.e. the frame number, the record\n * text, and the terminator byte. It is carried on the wire as **two hexadecimal\n * characters**. This module computes it (`computeChecksum`), formats it for emit\n * (`toChecksumHex`, always **uppercase**), and reads it back leniently\n * (`parseChecksumHex`, **accepts lowercase**), matching the real-vendor quirk that\n * some analyzers emit a lowercase checksum.\n */\n\n/**\n * Sum bytes `[start, endInclusive]` of `bytes` modulo 256: the ASTM frame\n * checksum span (frame number through the terminator, inclusive).\n *\n * @param bytes - The full decoded byte stream.\n * @param start - First index to include (the frame-number byte, one past `STX`).\n * @param endInclusive - Last index to include (the `ETB`/`ETX` terminator byte).\n * @returns The checksum in `0`–`255`.\n * @example\n * ```ts\n * import { computeChecksum } from \"@cosyte/astm\";\n * // bytes: STX '1' 'A' ETX → sum '1' + 'A' + ETX, mod 256.\n * computeChecksum(new Uint8Array([0x02, 0x31, 0x41, 0x03]), 1, 3); // 0x75\n * ```\n */\nexport function computeChecksum(bytes: Uint8Array, start: number, endInclusive: number): number {\n let sum = 0;\n for (let i = start; i <= endInclusive; i++) {\n sum = (sum + (bytes[i] ?? 0)) & 0xff;\n }\n return sum;\n}\n\n/**\n * Format a checksum byte as the two **uppercase** hex characters ASTM puts on the\n * wire (the conservative-emit form). Decode accepts lowercase; emit is uppercase.\n *\n * @param checksum - A value in `0`–`255`.\n * @returns Two uppercase hex characters, zero-padded (e.g. `\"0A\"`, `\"75\"`).\n * @example\n * ```ts\n * import { toChecksumHex } from \"@cosyte/astm\";\n * toChecksumHex(0x0a); // \"0A\"\n * ```\n */\nexport function toChecksumHex(checksum: number): string {\n return (checksum & 0xff).toString(16).toUpperCase().padStart(2, \"0\");\n}\n\n/** ASCII code for a single hex digit (`0`–`9`/`A`–`F`/`a`–`f`), or `undefined`. */\nfunction hexNibble(code: number | undefined): number | undefined {\n if (code === undefined) return undefined;\n if (code >= 0x30 && code <= 0x39) return code - 0x30; // 0-9\n if (code >= 0x41 && code <= 0x46) return code - 0x41 + 10; // A-F\n if (code >= 0x61 && code <= 0x66) return code - 0x61 + 10; // a-f (lenient: lowercase accepted)\n return undefined;\n}\n\n/**\n * Read the two-hex-char checksum at `bytes[i0]`/`bytes[i1]`, **case-insensitively**\n * (a lowercase checksum is a tolerated real-vendor quirk). Returns `undefined` when\n * either position is out of range or not a hex digit: the caller then treats the\n * frame as having no readable declared checksum, never as a match.\n *\n * @param bytes - The full decoded byte stream.\n * @param i0 - Index of the high hex nibble (immediately after the terminator).\n * @param i1 - Index of the low hex nibble.\n * @returns The declared checksum in `0`–`255`, or `undefined` if unreadable.\n * @example\n * ```ts\n * import { parseChecksumHex } from \"@cosyte/astm\";\n * parseChecksumHex(new Uint8Array([0x37, 0x35]), 0, 1); // 0x75 (\"75\")\n * parseChecksumHex(new Uint8Array([0x37, 0x35]), 5, 6); // undefined (out of range)\n * ```\n */\nexport function parseChecksumHex(bytes: Uint8Array, i0: number, i1: number): number | undefined {\n const hi = hexNibble(bytes[i0]);\n const lo = hexNibble(bytes[i1]);\n if (hi === undefined || lo === undefined) return undefined;\n return (hi << 4) | lo;\n}\n","/**\n * The frame codec's thrown error: the `strict`-mode counterpart to its lenient\n * warnings.\n *\n * The frame layer is lenient by default (byte-level deviations become\n * {@link AstmFrameWarning}s), and shares the single {@link FATAL_CODES.EMPTY_INPUT}\n * fatal with the record layer (thrown as an {@link AstmParseError} on an empty\n * stream, in both modes). In `strict` mode every tolerated deviation is rejected\n * instead: {@link decodeAstmFrames} throws {@link AstmFrameStrictError} carrying the\n * warnings it would otherwise have accumulated (code + position, never a value).\n */\n\nimport type { AstmFrameWarning } from \"./warnings.js\";\n\n/**\n * Thrown by {@link decodeAstmFrames} in `strict` mode when the lenient codec would\n * otherwise have accumulated one or more frame warnings (a bad checksum, a\n * sequence gap, an unterminated frame, or an oversize frame). Carries every\n * warning (code + position, never the frame's record bytes) so a caller can see\n * each deviation.\n *\n * @example\n * ```ts\n * import { decodeAstmFrames, AstmFrameStrictError } from \"@cosyte/astm\";\n * try {\n * decodeAstmFrames(someFramedBytes, { strict: true });\n * } catch (err) {\n * if (err instanceof AstmFrameStrictError) err.warnings.length; // >= 1\n * }\n * ```\n */\nexport class AstmFrameStrictError extends Error {\n public readonly warnings: readonly AstmFrameWarning[];\n /** @internal */\n public constructor(warnings: readonly AstmFrameWarning[]) {\n super(`Strict mode: ${String(warnings.length)} frame deviation(s) rejected.`);\n this.name = \"AstmFrameStrictError\";\n this.warnings = warnings;\n }\n}\n","/**\n * The frame-layer entry point: {@link decodeAstmFrames}.\n *\n * Decode a framed ASTM/CLSI-LIS01 byte stream (`<STX> FN text <ETB|ETX> CS <CR>\n * <LF>` frames) into a list of frames plus the reassembled record bytes of the\n * frames that were trusted and contiguous. Lenient by default: byte-level\n * deviations become {@link AstmFrameWarning}s, and the only fatal is the shared\n * `EMPTY_INPUT` (an empty stream). The two safety-critical rules are absolute in\n * **both** modes: a bad-checksum frame is surfaced flagged untrusted and **never**\n * merged into a record, and a sequence gap is **never** silently bridged.\n *\n * This is the framing layer only: it does not parse record grammar. Hand a\n * reassembled record to `parseAstmRecords`, or use `parseFramedAstm` to compose\n * both layers at the edge.\n */\n\nimport { AstmParseError, FATAL_CODES } from \"../common/errors.js\";\nimport {\n CR,\n ETB,\n ETX,\n FN_SEVEN,\n FN_ZERO,\n FRAME_NUMBER_MODULUS,\n FIRST_FRAME_NUMBER,\n LF,\n MAX_FRAME_TEXT,\n STX,\n} from \"./constants.js\";\nimport { computeChecksum, parseChecksumHex } from \"./checksum.js\";\nimport { AstmFrameStrictError } from \"./errors.js\";\nimport {\n frameBadChecksum,\n frameOversize,\n frameSequenceGap,\n frameUnterminated,\n type AstmFrameWarning,\n} from \"./warnings.js\";\nimport type {\n AstmFrame,\n DecodeAstmFramesResult,\n FrameChecksum,\n FrameOptions,\n FrameTerminator,\n} from \"./types.js\";\n\n/**\n * Decode a framed ASTM byte stream into frames + reassembled record bytes.\n *\n * Bytes outside a frame (before the first `STX`, or between a frame's trailing\n * `LF` and the next `STX`) are skipped: in ASTM they are low-level transfer\n * control (`ENQ`/`ACK`/`NAK`/`EOT`), never record content, so skipping them is\n * not data loss.\n *\n * @param bytes - The framed byte stream.\n * @param options - Decode options; lenient unless `strict` is set.\n * @returns The decoded frames, the reassembled trusted record bytes, and any warnings.\n * @throws {@link AstmParseError} with `EMPTY_INPUT` when `bytes` is empty (both modes).\n * @throws {@link AstmFrameStrictError} when `strict` is set and any deviation occurred.\n * @example\n * ```ts\n * import { decodeAstmFrames } from \"@cosyte/astm\";\n * // One final frame carrying the record text \"R|1|\": checksum \"AF\" over FN..ETX (mod 256).\n * const bytes = new Uint8Array([0x02, 0x31, 0x52, 0x7c, 0x31, 0x7c, 0x03, 0x41, 0x46, 0x0d, 0x0a]);\n * const { records, frames } = decodeAstmFrames(bytes);\n * frames[0]?.frameNumber; // 1\n * records.length; // 1\n * ```\n */\nexport function decodeAstmFrames(\n bytes: Uint8Array,\n options: FrameOptions = {},\n): DecodeAstmFramesResult {\n if (bytes.length === 0) {\n throw new AstmParseError(FATAL_CODES.EMPTY_INPUT, \"Framed input is empty.\", { recordIndex: 0 });\n }\n\n const frames: AstmFrame[] = [];\n const records: Uint8Array[] = [];\n const warnings: AstmFrameWarning[] = [];\n\n // Reassembly state for the current (possibly multi-frame) record.\n let recordChunks: Uint8Array[] = [];\n let recordTainted = false;\n let recordOpen = false; // a frame has been accumulated toward a record not yet closed by ETX\n\n // Sequence state: the next frame number expected. ASTM starts at 1 and rolls over 7 → 0 → 1.\n let expected = FIRST_FRAME_NUMBER;\n\n const n = bytes.length;\n let i = 0;\n while (i < n) {\n if (bytes[i] !== STX) {\n i++; // inter-frame / transfer-control byte: not record content, skip it\n continue;\n }\n\n const stxOffset = i;\n const fnIndex = i + 1;\n const frameNumber = fnIndex < n ? readFrameNumber(bytes[fnIndex]) : undefined;\n\n // Scan forward for the terminator (ETB/ETX). A second STX or end-of-stream first ⇒ unterminated.\n let termIndex = -1;\n let oversize = false;\n for (let k = i + 2; k < n; k++) {\n const b = bytes[k];\n if (b === ETB || b === ETX) {\n termIndex = k;\n break;\n }\n if (b === STX) break; // next frame started before this one terminated\n if (k - (i + 2) + 1 > MAX_FRAME_TEXT) oversize = true; // keep scanning; flagged below\n }\n\n // ── Unterminated: no terminator, or the checksum bytes are truncated past end-of-stream. ──\n const checksumTruncated = termIndex !== -1 && termIndex + 2 >= n;\n if (termIndex === -1 || checksumTruncated) {\n // Payload ends at the terminator (checksum truncated) or the next STX / end-of-stream.\n const textEnd = termIndex !== -1 ? termIndex : nextStxOrEnd(bytes, i + 2, n);\n const text = bytes.slice(Math.min(i + 2, textEnd), textEnd);\n frames.push({\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n text,\n byteOffset: stxOffset,\n checksum: { computed: 0, valid: false },\n trusted: false,\n unterminated: true,\n oversize,\n });\n warnings.push(frameUnterminated(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n recordOpen = false; // this attempt ended in error; do not double-report at end-of-stream\n // Resume at the next STX (if the stream continued) or finish.\n i = termIndex === -1 ? textEnd : n;\n continue;\n }\n\n // ── Terminated frame: verify checksum over [FN .. terminator] inclusive. ──\n const termKind: FrameTerminator = bytes[termIndex] === ETB ? \"ETB\" : \"ETX\";\n const computed = computeChecksum(bytes, fnIndex, termIndex);\n const declared = parseChecksumHex(bytes, termIndex + 1, termIndex + 2);\n const valid = declared !== undefined && declared === computed;\n const checksum: FrameChecksum =\n declared !== undefined ? { computed, declared, valid } : { computed, valid };\n const text = bytes.slice(i + 2, termIndex);\n\n const frame: AstmFrame = {\n ...(frameNumber !== undefined ? { frameNumber } : {}),\n terminator: termKind,\n text,\n byteOffset: stxOffset,\n checksum,\n trusted: valid,\n unterminated: false,\n oversize,\n };\n frames.push(frame);\n\n // Sequence check: never bridge a gap silently. An out-of-range FN (not 0–7) is a gap by\n // definition. On a valid, in-sequence number, advance the expected counter (mod 8).\n if (frameNumber !== undefined && isFrameDigit(frameNumber) && frameNumber === expected) {\n expected = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n } else {\n warnings.push(frameSequenceGap(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n // Resync to what we actually received so a single drop does not cascade warnings.\n if (frameNumber !== undefined && isFrameDigit(frameNumber)) {\n expected = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n }\n }\n\n if (oversize) warnings.push(frameOversize(framePosition(frameNumber, stxOffset)));\n\n if (!valid) {\n // Bad checksum: surfaced untrusted (above), warned, and NEVER merged into the record.\n warnings.push(frameBadChecksum(framePosition(frameNumber, stxOffset)));\n recordTainted = true;\n } else {\n recordChunks.push(text);\n }\n\n if (termKind === \"ETX\") {\n // Record complete. Emit ONLY a clean reassembly; a tainted record is dropped (its frames\n // remain surfaced), never emitted as if whole.\n if (!recordTainted) records.push(concatBytes(recordChunks));\n recordChunks = [];\n recordTainted = false;\n recordOpen = false;\n } else {\n recordOpen = true; // ETB: the record continues in a later frame\n }\n\n // Advance past the two checksum chars and an optional CR/LF tail.\n i = termIndex + 3;\n if (bytes[i] === CR) i++;\n if (bytes[i] === LF) i++;\n }\n\n // A record left open on an intermediate ETB with no final ETX is unterminated: no partial\n // record is invented; its frames are already surfaced.\n if (recordOpen) {\n warnings.push(frameUnterminated({ byteOffset: n }));\n }\n\n if (options.strict === true && warnings.length > 0) {\n throw new AstmFrameStrictError(warnings);\n }\n\n return freezeResult({ records, frames, warnings });\n}\n\n/**\n * Freeze the decoded result so the model rejects mutation: the frame objects,\n * their checksum sub-objects, the warnings, and the three container arrays. The\n * `Uint8Array` payloads are deliberately **not** frozen: `Object.freeze` throws on\n * a typed array that has elements, and the byte buffers are copies the caller owns.\n */\nfunction freezeResult(result: DecodeAstmFramesResult): DecodeAstmFramesResult {\n for (const f of result.frames) {\n Object.freeze(f.checksum);\n Object.freeze(f);\n }\n for (const w of result.warnings) {\n Object.freeze(w.position);\n Object.freeze(w);\n }\n Object.freeze(result.records);\n Object.freeze(result.frames);\n Object.freeze(result.warnings);\n return Object.freeze(result);\n}\n\n/** Read a frame-number byte into its numeric value; `undefined` only when the byte is absent. */\nfunction readFrameNumber(fnByte: number | undefined): number | undefined {\n if (fnByte === undefined) return undefined;\n // Surface the digit value even when out of 0–7 range (a corruption flagged as a sequence gap).\n return fnByte - FN_ZERO;\n}\n\n/** Whether a decoded frame number is a valid ASTM `0`–`7` sequence digit. */\nfunction isFrameDigit(frameNumber: number): boolean {\n return frameNumber >= 0 && frameNumber <= FN_SEVEN - FN_ZERO;\n}\n\n/** Build a value-free frame position (frame number + byte offset). */\nfunction framePosition(\n frameNumber: number | undefined,\n byteOffset: number,\n): { frameNumber?: number; byteOffset: number } {\n return frameNumber !== undefined ? { frameNumber, byteOffset } : { byteOffset };\n}\n\n/** Index of the next `STX` at or after `from`, or `end` if none: bounds a partial (unterminated) frame. */\nfunction nextStxOrEnd(bytes: Uint8Array, from: number, end: number): number {\n for (let k = from; k < end; k++) {\n if (bytes[k] === STX) return k;\n }\n return end;\n}\n\n/** Concatenate byte chunks into one `Uint8Array` (the reassembled record bytes). */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * The frame-layer **emit** side: {@link composeAstmFrames}.\n *\n * The exact inverse of {@link decodeAstmFrames}. Given the reassembled record\n * byte-strings (one entry per complete record, the same shape `decodeAstmFrames`\n * *produces*), it wraps each into one or more `<STX> FN text <ETB|ETX> CS <CR><LF>`\n * frames, so `decodeAstmFrames(composeAstmFrames(records)).records` reproduces the\n * input records exactly.\n *\n * Everything structural is **computed, never accepted-as-given**:\n * - the **modulo-256 checksum** over `FN … terminator` (inclusive), emitted as two\n * **uppercase** hex chars (the conservative-emit form the decoder accepts either\n * case of);\n * - the **frame number** `0`–`7`, starting at `1` and rolling over `7 → 0 → 1`,\n * continuous across every frame in the stream (as the decoder's sequence check\n * expects);\n * - the **240-byte text split**: a record longer than 240 bytes is split across\n * frames closed by `ETB` (intermediate) with a final `ETX`; the seven framing\n * control bytes are never counted toward the 240.\n *\n * A record must carry bytes: an empty record (or an empty record list) is a typed\n * {@link AstmFrameEncodeError}, never an empty frame.\n */\n\nimport { computeChecksum, toChecksumHex } from \"./checksum.js\";\nimport {\n CR,\n ETB,\n ETX,\n FN_ZERO,\n FRAME_NUMBER_MODULUS,\n FIRST_FRAME_NUMBER,\n LF,\n MAX_FRAME_TEXT,\n STX,\n} from \"./constants.js\";\n\n/**\n * Thrown by {@link composeAstmFrames} when the input cannot be framed into a\n * spec-clean stream: an empty record list, or a record with no bytes (a frame\n * must carry a record; an empty one is a structural error, never an empty frame).\n * Carries a stable code + the offending record index, never record bytes.\n *\n * @example\n * ```ts\n * import { composeAstmFrames, AstmFrameEncodeError } from \"@cosyte/astm\";\n * try {\n * composeAstmFrames([]);\n * } catch (err) {\n * if (err instanceof AstmFrameEncodeError) err.code; // \"ASTM_FRAME_EMPTY_RECORD\"\n * }\n * ```\n */\nexport class AstmFrameEncodeError extends Error {\n /** Stable discriminant. */\n public readonly code: \"ASTM_FRAME_EMPTY_RECORD\";\n /** Index of the offending record within the input, when applicable. */\n public readonly recordIndex?: number;\n /** @internal */\n public constructor(message: string, recordIndex?: number) {\n super(message);\n this.name = \"AstmFrameEncodeError\";\n this.code = \"ASTM_FRAME_EMPTY_RECORD\";\n if (recordIndex !== undefined) this.recordIndex = recordIndex;\n }\n}\n\n/** Options for {@link composeAstmFrames}. */\nexport interface ComposeFramesOptions {\n /**\n * The frame number to start the sequence at (`0`–`7`). Defaults to\n * {@link FIRST_FRAME_NUMBER} (`1`): the ASTM convention.\n */\n readonly startFrameNumber?: number;\n}\n\n/** Latin1 bytes of a string (each char → its byte), so `\\r` etc. survive 1:1. */\nfunction toBytes(input: Uint8Array | string): Uint8Array {\n if (typeof input !== \"string\") return input;\n const out = new Uint8Array(input.length);\n for (let i = 0; i < input.length; i += 1) out[i] = input.charCodeAt(i) & 0xff;\n return out;\n}\n\n/** Build one frame's bytes: `STX FN text (ETB|ETX) C1 C2 CR LF`, checksum over `FN … terminator`. */\nfunction encodeFrame(text: Uint8Array, frameNumber: number, isFinal: boolean): number[] {\n const term = isFinal ? ETX : ETB;\n // Assemble STX FN text term, then compute the checksum over FN..term inclusive.\n const bytes = [STX, FN_ZERO + frameNumber, ...text, term];\n const cs = computeChecksum(Uint8Array.from(bytes), 1, bytes.length - 1);\n const hex = toChecksumHex(cs);\n return [...bytes, hex.charCodeAt(0), hex.charCodeAt(1), CR, LF];\n}\n\n/**\n * Frame reassembled record bytes into a spec-clean ASTM/CLSI-LIS01 byte stream:\n * the inverse of {@link decodeAstmFrames}.\n *\n * Each record is split at 240 text bytes into `ETB`-closed intermediate frames\n * and a final `ETX` frame; frame numbers run `1, 2, … 7, 0, 1 …` continuously\n * across the whole stream; every frame's modulo-256 checksum is computed and\n * emitted uppercase.\n *\n * @param records - The reassembled record byte-strings (`Uint8Array` or latin1\n * `string`), one entry per complete record.\n * @param options - Encode options.\n * @returns The framed byte stream.\n * @throws {@link AstmFrameEncodeError} when the list is empty or a record has no bytes.\n * @example\n * ```ts\n * import { composeAstmFrames, decodeAstmFrames } from \"@cosyte/astm\";\n * const records = [new TextEncoder().encode(\"H|\\\\^&\\r\"), new TextEncoder().encode(\"L|1\\r\")];\n * const bytes = composeAstmFrames(records);\n * decodeAstmFrames(bytes).records.length; // 2\n * ```\n */\nexport function composeAstmFrames(\n records: readonly (Uint8Array | string)[],\n options: ComposeFramesOptions = {},\n): Uint8Array {\n if (records.length === 0) {\n throw new AstmFrameEncodeError(\"Cannot frame an empty record list: nothing to transmit.\");\n }\n\n const out: number[] = [];\n let frameNumber = options.startFrameNumber ?? FIRST_FRAME_NUMBER;\n\n records.forEach((record, recordIndex) => {\n const bytes = toBytes(record);\n if (bytes.length === 0) {\n throw new AstmFrameEncodeError(\n \"Cannot frame an empty record: a frame must carry record bytes.\",\n recordIndex,\n );\n }\n // Split the record text into <=240-byte chunks; the last chunk of the record closes with ETX.\n for (let offset = 0; offset < bytes.length; offset += MAX_FRAME_TEXT) {\n const chunk = bytes.subarray(offset, offset + MAX_FRAME_TEXT);\n const isFinal = offset + MAX_FRAME_TEXT >= bytes.length;\n out.push(...encodeFrame(chunk, frameNumber, isFinal));\n frameNumber = (frameNumber + 1) % FRAME_NUMBER_MODULUS;\n }\n });\n\n return Uint8Array.from(out);\n}\n","/**\n * Composing the two layers at the edge: {@link parseFramedAstm}.\n *\n * The framing layer ({@link decodeAstmFrames}) and the record layer\n * (`parseAstmRecords`) share nothing but the payload boundary: a frame carries\n * record bytes; a record knows nothing about frames. This helper joins them at the\n * one point they meet: decode a framed stream into trusted, reassembled record\n * bytes, then parse those bytes into an {@link AstmMessage}. It is deliberately\n * thin: a consumer that already holds de-framed record bytes calls\n * `parseAstmRecords` directly, and a consumer that only wants frames calls\n * `decodeAstmFrames` directly.\n */\n\nimport { parseAstmRecords } from \"../records/parse.js\";\nimport { serializeAstmRecord } from \"../records/serialize.js\";\nimport type { AstmMessage, AstmParseOptions, AstmRecord } from \"../records/types.js\";\nimport { composeAstmFrames, type ComposeFramesOptions } from \"./encode.js\";\nimport { decodeAstmFrames } from \"./decode.js\";\nimport type { AstmFrame, FrameOptions } from \"./types.js\";\nimport type { AstmFrameWarning } from \"./warnings.js\";\n\n/**\n * The result of {@link parseFramedAstm}: the parsed message, plus the frame-layer\n * detail (every decoded frame and the frame warnings) so a consumer keeps full\n * visibility into the transport below the records.\n */\nexport interface FramedAstmResult {\n /** The message parsed from the trusted, reassembled record bytes. */\n readonly message: AstmMessage;\n /** Every decoded frame, trusted or not, in wire order. */\n readonly frames: readonly AstmFrame[];\n /** The frame-layer warnings (bad checksum, sequence gap, unterminated, oversize). */\n readonly frameWarnings: readonly AstmFrameWarning[];\n}\n\n/**\n * Decode a framed ASTM byte stream and parse its reassembled records in one call.\n *\n * Only **trusted, contiguous** frames are reassembled (a bad-checksum frame, a\n * sequence gap, or an unterminated frame is surfaced in `frames`/`frameWarnings`\n * but never fed to the record parser), so the parsed `message` reflects only bytes\n * the framing layer vouched for.\n *\n * @param bytes - The framed byte stream.\n * @param options - Frame decode options **and** record parse options (both layers\n * honor a shared `strict`).\n * @returns The parsed message plus the frame-layer detail.\n * @throws {@link AstmParseError} `EMPTY_INPUT` when the stream is empty, or when no\n * trusted records could be reassembled (nothing to parse).\n * @throws {@link AstmFrameStrictError} / {@link AstmStrictError} in `strict` mode on\n * a frame or record deviation, respectively.\n * @example\n * ```ts\n * import { parseFramedAstm } from \"@cosyte/astm\";\n * // A single final frame carrying \"H|\\^&\\r\": checksum \"E5\" over FN..ETX (mod 256).\n * const bytes = new Uint8Array([\n * 0x02, 0x31, 0x48, 0x7c, 0x5c, 0x5e, 0x26, 0x0d, 0x03, 0x45, 0x35, 0x0d, 0x0a,\n * ]);\n * const { message, frames } = parseFramedAstm(bytes);\n * message.header.delimiters.field; // \"|\"\n * frames.length; // 1\n * ```\n */\nexport function parseFramedAstm(\n bytes: Uint8Array,\n options: FrameOptions & AstmParseOptions = {},\n): FramedAstmResult {\n const { records, frames, warnings } = decodeAstmFrames(bytes, options);\n const joined = concatBytes(records);\n const message = parseAstmRecords(joined, options);\n return { message, frames, frameWarnings: warnings };\n}\n\n/**\n * Serialize an ASTM message (or a bare record list) and frame it into a spec-clean\n * byte stream in one call: the inverse of {@link parseFramedAstm}, composing the\n * two emit layers at the edge.\n *\n * Each record is serialized to spec-clean, `CR`-terminated wire text (canonical\n * delimiters, embedded delimiters re-escaped) and then framed **independently**\n * (one record per `ETX`-closed frame run), so the framing exactly mirrors what\n * {@link decodeAstmFrames} reassembles: `parseFramedAstm(serializeFramedAstm(msg))`\n * yields an equal message.\n *\n * @param input - A parsed {@link AstmMessage} or a list of {@link AstmRecord}s.\n * @param options - Frame-encode options.\n * @returns The framed byte stream.\n * @throws {@link AstmSerializeError} when a value contains an unencodable `CR`/`LF`.\n * @throws {@link AstmFrameEncodeError} when there are no records to frame.\n * @example\n * ```ts\n * import { parseAstmRecords, serializeFramedAstm, parseFramedAstm } from \"@cosyte/astm\";\n * const msg = parseAstmRecords(\"H|\\\\^&\\rR|1|^^^687|28.6|U/L||N||F\\rL|1\\r\");\n * const bytes = serializeFramedAstm(msg);\n * parseFramedAstm(bytes).message.records.length; // 3\n * ```\n */\nexport function serializeFramedAstm(\n input: AstmMessage | readonly AstmRecord[],\n options: ComposeFramesOptions = {},\n): Uint8Array {\n const records: readonly AstmRecord[] = Array.isArray(input)\n ? (input as readonly AstmRecord[])\n : (input as AstmMessage).records;\n // Frame each record independently (record text + its CR terminator) so decode reassembles per record.\n const recordBytes = records.map((r) => serializeAstmRecord(r) + \"\\r\");\n return composeAstmFrames(recordBytes, options);\n}\n\n/** Concatenate the reassembled record byte-strings into one de-framed record stream. */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) {\n out.set(c, at);\n at += c.length;\n }\n return out;\n}\n","/**\n * Public entry point for the `@cosyte/astm` package: the ASTM/CLSI-LIS02\n * **record** layer.\n *\n * The north star: hand a de-framed ASTM record stream to {@link parseAstmRecords}\n * and pull a result's value, units, and flag out in one line. Parsing is lenient\n * (vendor quirks become typed warnings, never silent loss) and, the whole point,\n * **never returns a confident wrong value**. Delimiters are read from each header,\n * embedded escapes are decoded before a value is split, the practice- and\n * laboratory-assigned patient IDs stay distinct, and every deviation is a stable,\n * value-free warning. Result flag/status semantics, patient/order identity\n * depth + the `C` comment record + partial-timestamp hardening, and the\n * request-information (`Q`) record + host-query classification + verbatim `M`/`S`\n * records are all modeled: the **record-content layer is now feature-complete**.\n *\n * A parsed model is a whole record **stream**, and a stream may carry several `H` … `L`\n * messages. {@link messages} splits it into them, so a patient and a result are only ever\n * paired inside the message that carried both; the flat extractors ({@link patient},\n * {@link results}, …) read the whole stream and therefore **refuse** one they cannot answer\n * for, with an {@link AstmAmbiguousStreamError}, rather than answering across patients.\n *\n * The E1381/CLSI-LIS01 **framing** layer lives alongside the record layer and\n * shares nothing but the payload boundary: {@link decodeAstmFrames} decodes a framed\n * byte stream (`<STX> FN text <ETB|ETX> CS <CR><LF>`) into frames + reassembled\n * record bytes: verifying the modulo-256 checksum (a bad frame is surfaced\n * untrusted, never merged), tracking frame-number sequencing (a gap is never\n * silently bridged), and reassembling the 240-byte-limited multi-frame records.\n * {@link parseFramedAstm} composes the two layers at the edge.\n *\n * The LTP **protocol** layer sits above the frame codec: {@link detectFraming}\n * auto-detects the transport reality from the stream's leading byte, framed\n * (serial / cobas 4800 / Iguana) vs raw (cobas b121, framing dropped).\n * {@link ltpReduce} is a **pure, socket-free** receiver-side state machine over\n * `ENQ`/`ACK`/`NAK`/`EOT` + frame-received events. The consumer owns the wire; the\n * reducer decides. Its inviolable rule mirrors `mllp`'s ACK-failsafe: a frame the\n * codec did not vouch for is answered with `NAK`, **never** a fabricated positive\n * `ACK`, and never merged into a record, a `NAK` drives retransmit, not acceptance.\n *\n * The **emit** half is the conservative inverse of the record and framing layers.\n * {@link buildAstmMessage} constructs records from typed caller input, emitting only\n * the values the caller supplied; {@link serializeAstmRecords} writes them out with the\n * canonical `H|\\^&` delimiters by default, re-escaping every embedded delimiter in a\n * decoded component leaf (every record type is emitted from its decoded `fields`, except that\n * `M`/`S` bytes pass through untouched when they already read correctly under the emit set, so\n * an edit to the model is reflected on emit and the whole stream goes out in the one\n * delimiter set the header declares); {@link composeAstmFrames} wraps them into frames whose\n * modulo-256 checksum is\n * computed rather than accepted; and {@link serializeFramedAstm} composes the last two.\n * The protocol layer has no emit inverse by design: {@link ltpReduce} returns the actions\n * to take, and the consumer owns the wire.\n */\n\n/**\n * Library version string, kept in lockstep with `package.json#version`.\n *\n * Changesets owns the bump and rewrites `package.json` only, so the release `version` script runs\n * `scripts/sync-version.mjs` to rewrite this declaration in the same commit, and\n * `test/sanity.test.ts` compares the two so a skipped sync goes red instead of shipping a version\n * string that lies.\n *\n * @example\n * ```ts\n * import { VERSION } from \"@cosyte/astm\";\n * console.log(VERSION);\n * ```\n */\nexport const VERSION: string = \"0.0.9\";\n\nexport { parseAstmRecords, AstmStrictError, attachComments } from \"./records/parse.js\";\nexport {\n serializeAstmRecords,\n serializeAstmRecord,\n serializeField,\n encodeComponent,\n AstmSerializeError,\n} from \"./records/serialize.js\";\nexport type { AstmSerializeErrorCode } from \"./records/serialize.js\";\nexport { buildAstmMessage } from \"./records/build.js\";\nexport type {\n AstmRecordInput,\n MessageInput,\n HeaderInput,\n PatientInput,\n PatientNameInput,\n OrderInput,\n ResultInput,\n CommentInput,\n QueryInput,\n VerbatimInput,\n} from \"./records/build.js\";\nexport { results, patient, orders, comments, commentsFor, query } from \"./records/extractors.js\";\nexport { messages, AMBIGUOUS_CODES, AstmAmbiguousStreamError } from \"./records/messages.js\";\nexport type { AstmStreamMessage, AmbiguousCode } from \"./records/messages.js\";\nexport { classifyMessage } from \"./records/host-query.js\";\nexport { fieldScalar, tokenizeHeader, tokenizeRecord } from \"./records/tokenize.js\";\nexport {\n interpretAbnormalFlag,\n interpretResultStatus,\n parseReferenceRange,\n} from \"./records/result-semantics.js\";\nexport type {\n AbnormalFlag,\n AbnormalFlagCode,\n AbnormalFlagMeaning,\n ReferenceRange,\n ReferenceRangeKind,\n ResultStatus,\n ResultStatusCode,\n ResultStatusMeaning,\n} from \"./records/result-semantics.js\";\nexport type {\n AstmField,\n AstmMessage,\n AstmMessageClassification,\n AstmMessageKind,\n AstmParseOptions,\n AstmRecord,\n HeaderRecord,\n PatientRecord,\n PatientName,\n OrderRecord,\n ResultRecord,\n CommentRecord,\n QueryRecord,\n ManufacturerRecord,\n ScientificRecord,\n TerminatorRecord,\n UnsupportedRecord,\n} from \"./records/types.js\";\n\nexport { FATAL_CODES, AstmParseError } from \"./common/errors.js\";\nexport type { FatalCode } from \"./common/errors.js\";\nexport {\n WARNING_CODES,\n unknownRecordType,\n fieldsUnseparated,\n nonStandardDelimiters,\n unknownEscapeSequence,\n unpairedEscapeCharacter,\n ambiguousValueSplit,\n undefinedAbnormalFlag,\n undefinedResultStatus,\n unparseableReferenceRange,\n unitsAbsent,\n orphanComment,\n partialTimestamp,\n uninterpretedQueryStatus,\n ambiguousMessageKind,\n delimitersRedeclared,\n unreadableRedeclaration,\n profileQuirkApplied,\n} from \"./common/warnings.js\";\nexport type { WarningCode, AstmRecordWarning } from \"./common/warnings.js\";\nexport type { AstmPosition } from \"./common/position.js\";\n\nexport { CANONICAL_DELIMITERS, readDelimiters, isNonStandard } from \"./common/delimiters.js\";\nexport type { Delimiters } from \"./common/delimiters.js\";\nexport { decodeEscapes, splitEscapeAware } from \"./common/escapes.js\";\nexport type { UnknownEscapeSink, UnpairedEscapeSink } from \"./common/escapes.js\";\nexport { parseAstmDate, astmDateToLocalISO } from \"./common/dates.js\";\nexport type { AstmDate, AstmDatePrecision } from \"./common/dates.js\";\nexport { recognizeUniversalTestId, primaryCode } from \"./common/coding-system.js\";\nexport type { UniversalTestId, UniversalTestIdProvenance } from \"./common/coding-system.js\";\nexport { deepFreeze } from \"./common/freeze.js\";\n\n// ── The vendor profile system (P8): the engine + registry + tolerance transform ──\n// `defineAstmProfile` builds an immutable, provenance-backed profile with a\n// safety gate enforced both at definition time and again when a warning would be\n// downgraded (a profile can never tolerate a safety-critical\n// deviation, and never alters an extracted value: it only re-badges an expected\n// warning to PROFILE_QUIRK_APPLIED and can force the raw-vs-framed transport). The\n// built-in registry ships `default` + the corpus-grounded `referenceCorpus`; named\n// vendor profiles are deferred (REAL-CORPUS) but fully supported by the engine.\nexport {\n defineAstmProfile,\n AstmProfileDefinitionError,\n astmProfiles,\n getAstmProfile,\n listAstmProfiles,\n setDefaultAstmProfile,\n getDefaultAstmProfile,\n applyAstmProfile,\n applyAstmProfileToWarnings,\n resolveProfileTransport,\n SAFETY_CRITICAL_CODES,\n TOLERABLE_CODES,\n ALL_ASTM_WARNING_CODES,\n isSafetyCriticalCode,\n} from \"./profiles/index.js\";\nexport type {\n AstmProfile,\n DefineAstmProfileOptions,\n AstmQuirkTolerance,\n AstmQuirkMatch,\n AstmProfileProvenance,\n AnyAstmWarningCode,\n} from \"./profiles/index.js\";\n\n// ── The terminology layer (P9): LIVD-aware LOINC recognition (bring-your-own) ──\n// `@cosyte/astm` bundles no LOINC/SNOMED/LIVD data (roadmap §5): LOINC is © Regenstrief\n// (redistributable only with its attribution notice) and the public CDC LIVD file is\n// SARS-CoV-2-specific and carries separately-licensed SNOMED: not a general public-domain\n// catalog. So a consumer supplies their own IICC LIVD catalog (`defineLivdCatalog`) and\n// `applyLivd` produces an additive, advisory LOINC annotation layer that never mutates the\n// raw code/value and never guesses a LOINC: an unmapped/ambiguous code surfaces as such.\nexport {\n defineLivdCatalog,\n applyLivd,\n lookupLivdForRecord,\n LIVD_WARNING_CODES,\n livdUnmappedCode,\n livdAmbiguousMapping,\n} from \"./terminology/index.js\";\nexport type {\n LivdCatalog,\n LivdEntry,\n LivdLookup,\n LivdAnnotation,\n LivdMapping,\n LivdResult,\n AstmLivdWarning,\n LivdWarningCode,\n} from \"./terminology/index.js\";\n\n// ── The LTP protocol layer (P6): transport detection + pure session reducer ──\nexport { detectFraming } from \"./ltp/transport.js\";\nexport type { AstmFraming, DetectFramingOptions, DetectFramingResult } from \"./ltp/transport.js\";\nexport { ltpInitialState, ltpReduce } from \"./ltp/reducer.js\";\nexport {\n LTP_WARNING_CODES,\n ltpAmbiguousTransport,\n ltpUnexpectedEvent,\n ltpFrameRejected,\n} from \"./ltp/warnings.js\";\nexport type { LtpWarningCode, AstmLtpWarning } from \"./ltp/warnings.js\";\nexport {\n ENQ as ASTM_ENQ,\n ACK as ASTM_ACK,\n NAK as ASTM_NAK,\n EOT as ASTM_EOT,\n} from \"./ltp/constants.js\";\nexport type { LtpPhase, LtpState, LtpEvent, LtpAction, LtpTransition } from \"./ltp/types.js\";\n\n// ── The E1381 / CLSI-LIS01 framing layer (P5) ──\nexport { decodeAstmFrames } from \"./frames/decode.js\";\nexport { composeAstmFrames, AstmFrameEncodeError } from \"./frames/encode.js\";\nexport type { ComposeFramesOptions } from \"./frames/encode.js\";\nexport { parseFramedAstm, serializeFramedAstm } from \"./frames/compose.js\";\nexport type { FramedAstmResult } from \"./frames/compose.js\";\nexport { computeChecksum, toChecksumHex, parseChecksumHex } from \"./frames/checksum.js\";\nexport { AstmFrameStrictError } from \"./frames/errors.js\";\nexport {\n FRAME_WARNING_CODES,\n frameBadChecksum,\n frameSequenceGap,\n frameUnterminated,\n frameOversize,\n} from \"./frames/warnings.js\";\nexport type { FrameWarningCode, AstmFrameWarning } from \"./frames/warnings.js\";\nexport type { AstmFramePosition } from \"./frames/position.js\";\nexport type {\n AstmFrame,\n FrameChecksum,\n FrameTerminator,\n FrameOptions,\n DecodeAstmFramesResult,\n} from \"./frames/types.js\";\n"]}