@cosyte/dicom 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ declare const VERSION: string;
19
19
  *
20
20
  * Frozen surface. Phase 2 (parser), Phase 3 (dataset/VR), and Phase 7
21
21
  * (validate) all depend on this
22
- * shape — schema-breaking changes here cascade through the codebase.
22
+ * shape - schema-breaking changes here cascade through the codebase.
23
23
  *
24
24
  * @module
25
25
  */
@@ -28,7 +28,7 @@ declare const VERSION: string;
28
28
  *
29
29
  * For repeating-group attribute families (`(50xx,xxxx)` curves, `(60xx,xxxx)`
30
30
  * overlays, etc.), the tag string preserves lowercase `x` placeholders verbatim
31
- * — those entries cannot be looked up by concrete tag and are flagged via
31
+ * - those entries cannot be looked up by concrete tag and are flagged via
32
32
  * {@link DictionaryEntry.repeatingGroup}.
33
33
  *
34
34
  * @example
@@ -40,7 +40,7 @@ type Tag = string;
40
40
  * additions (`OV`, `SV`, `UV`) introduced in DICOM 2018.
41
41
  *
42
42
  * Note that some attributes in the data dictionary list MULTIPLE possible VRs
43
- * — see {@link DictionaryEntry.vr} (which is always an array, possibly empty
43
+ * - see {@link DictionaryEntry.vr} (which is always an array, possibly empty
44
44
  * for retired entries with no VR or special "See Note" entries).
45
45
  *
46
46
  * @example
@@ -66,7 +66,7 @@ interface DictionaryEntry {
66
66
  readonly vm: string;
67
67
  readonly retired: boolean;
68
68
  /**
69
- * `true` for repeating-group families (`(50xx,xxxx)`, `(60xx,xxxx)` —
69
+ * `true` for repeating-group families (`(50xx,xxxx)`, `(60xx,xxxx)`:
70
70
  * curves/overlays); the {@link DictionaryEntry.tag} field for these contains
71
71
  * lowercase `x` placeholders, NOT a concrete 8-hex-char tag. Repeating-group
72
72
  * entries are not surfaced by `Dictionary.lookup(tag)` for concrete tags.
@@ -92,19 +92,19 @@ interface UidEntry {
92
92
  }
93
93
 
94
94
  /**
95
- * Public Dictionary namespace for `@cosyte/dicom` — Phase 1 deliverable.
95
+ * Public Dictionary namespace for `@cosyte/dicom` - Phase 1 deliverable.
96
96
  *
97
97
  * D-10:
98
98
  *
99
- * - {@link lookup} — accepts either an 8-char hex tag (`"00100010"`) or a
99
+ * - {@link lookup} - accepts either an 8-char hex tag (`"00100010"`) or a
100
100
  * keyword (`"PatientName"`) and returns the typed {@link DictionaryEntry}
101
101
  * or `undefined`.
102
- * - {@link byKeyword} — keyword-only lookup; for cases where the caller has
102
+ * - {@link byKeyword} - keyword-only lookup; for cases where the caller has
103
103
  * already validated the input shape and wants a narrower call.
104
- * - {@link uid} — UID lookup (DICT-06).
104
+ * - {@link uid} - UID lookup (DICT-06).
105
105
  *
106
106
  * No function throws on miss (D-10: "Returns the typed entry or `undefined`.
107
- * No throws on miss."). Returned entries are deeply frozen — mutation attempts
107
+ * No throws on miss."). Returned entries are deeply frozen - mutation attempts
108
108
  * throw `TypeError` in strict mode (which all of `@cosyte/dicom`'s emitted code
109
109
  * runs under, given `"use strict"` is implicit for ES modules).
110
110
  *
@@ -117,7 +117,7 @@ interface UidEntry {
117
117
  * Hex tag input is normalized to uppercase; keyword input is case-sensitive.
118
118
  * Returns `undefined` for unknown tags, unknown keywords, malformed input, or
119
119
  * tags from repeating-group families (those resolve via the family entry's
120
- * `repeatingGroup` flag — see {@link DictionaryEntry}).
120
+ * `repeatingGroup` flag - see {@link DictionaryEntry}).
121
121
  *
122
122
  * @example
123
123
  * import { Dictionary } from "@cosyte/dicom";
@@ -168,10 +168,10 @@ declare namespace index {
168
168
  * Shared parser-pipeline types for `@cosyte/dicom`.
169
169
  *
170
170
  * Phase 2 core-parser context:
171
- * - D-02 — `ParseOptions` shape (Phase 2 only; no `profile` field).
172
- * - D-03 — `OnWarningCallback` ordering contract (invoked AFTER push to `ctx.warnings`).
173
- * - D-07 — `DicomPosition` shape (`byteOffset`, optional `fileMeta` / `deflated` / `contextPath`).
174
- * - D-45 — `ParseContext.profile?: unknown` is reserved for Phase 6; Phase 2 never sets it.
171
+ * - D-02 - `ParseOptions` shape (Phase 2 only; no `profile` field).
172
+ * - D-03 - `OnWarningCallback` ordering contract (invoked AFTER push to `ctx.warnings`).
173
+ * - D-07 - `DicomPosition` shape (`byteOffset`, optional `fileMeta` / `deflated` / `contextPath`).
174
+ * - D-45 - `ParseContext.profile?: unknown` is reserved for Phase 6; Phase 2 never sets it.
175
175
  *
176
176
  * Public types (exported via `src/index.ts`): `DicomPosition`, `ParseOptions`, `OnWarningCallback`.
177
177
  * Internal type (NOT exported from `src/index.ts`): `ParseContext`.
@@ -202,14 +202,14 @@ interface PrivateTagDefinition {
202
202
  }
203
203
  /**
204
204
  * A source/vendor tolerance preset (Phase 6). A `Profile` bundles three
205
- * things that only ever **tighten or annotate** a parse — never loosen it
205
+ * things that only ever **tighten or annotate** a parse - never loosen it
206
206
  * past the Postel's-Law default:
207
207
  *
208
- * - `escalations` — Tier-2 warning codes promoted to a thrown
208
+ * - `escalations` - Tier-2 warning codes promoted to a thrown
209
209
  * `DicomParseError` (a stricter posture for known-unsafe deviations).
210
- * - `suppressions` — Tier-2 warning codes silenced because they are a
210
+ * - `suppressions` - Tier-2 warning codes silenced because they are a
211
211
  * documented, benign quirk of the named source (annotation, not loss).
212
- * - `privateDictionary` — a private-creator-keyed overlay resolving the
212
+ * - `privateDictionary` - a private-creator-keyed overlay resolving the
213
213
  * Implicit-VR of vendor private data elements via the file's **live**
214
214
  * private-creator string (never a hard-coded block number).
215
215
  *
@@ -287,7 +287,7 @@ type OnWarningCallback = (warning: DicomParseWarning) => void;
287
287
  /**
288
288
  * Options accepted by `parseDicom`.
289
289
  *
290
- * Per `02-CONTEXT.md` D-02 — Phase 2 form only. No `profile` field; Phase 6
290
+ * Per `02-CONTEXT.md` D-02 - Phase 2 form only. No `profile` field; Phase 6
291
291
  * adds it. With `exactOptionalPropertyTypes: true`, callers omit unset keys
292
292
  * rather than passing `undefined` for any field below.
293
293
  *
@@ -329,9 +329,9 @@ interface ParseOptions {
329
329
  */
330
330
  readonly onWarning?: OnWarningCallback;
331
331
  /**
332
- * When `true`, every `Element.rawBytes` is `Buffer.from(slice)` — copying
332
+ * When `true`, every `Element.rawBytes` is `Buffer.from(slice)` - copying
333
333
  * each value out so the source buffer can be released. When `false` (the
334
- * default), `Element.rawBytes` is `Buffer.subarray(slice)` — a zero-copy
334
+ * default), `Element.rawBytes` is `Buffer.subarray(slice)` - a zero-copy
335
335
  * view that pins the source ArrayBuffer until every Element is GC'd.
336
336
  *
337
337
  * Per D-16 / MODEL-03. Omit to use the default.
@@ -341,7 +341,7 @@ interface ParseOptions {
341
341
  * Source/vendor tolerance preset (Phase 6, D-45). Applies the profile's
342
342
  * `escalations` / `suppressions` to warning emission and its
343
343
  * `privateDictionary` to Implicit-VR resolution of private data elements.
344
- * A profile only tightens or annotates — it never makes the default
344
+ * A profile only tightens or annotates - it never makes the default
345
345
  * lenient parse throw outside the four Tier-3 fatals, and a private
346
346
  * creator the profile does not recognize degrades to generic UN handling
347
347
  * plus a `DICOM_PRIVATE_CREATOR_UNKNOWN` warning, never a wrong decode.
@@ -356,11 +356,11 @@ interface ParseOptions {
356
356
  * pipeline.
357
357
  *
358
358
  * Phase 2 core-parser context:
359
- * - D-08 — `WARNING_CODES` is a frozen `as const` registry with every code
359
+ * - D-08 - `WARNING_CODES` is a frozen `as const` registry with every code
360
360
  * in the TOL-03 catalog (≥25 entries). Phase 2 actively emits 13;
361
361
  * 7 VR-decode-time codes are reserved for Phase 3; 2 charset codes are
362
362
  * reserved for Phase 4; 2 codes are reserved for Phase 6 / Phase 7.
363
- * - D-12 — Exactly one named factory per actively-emitted code; each
363
+ * - D-12 - Exactly one named factory per actively-emitted code; each
364
364
  * factory carries its own JSDoc + `@example` and returns a typed
365
365
  * `DicomParseWarning`.
366
366
  *
@@ -375,7 +375,7 @@ interface ParseOptions {
375
375
  * Stable string codes for every Tier-2 warning the parser may emit.
376
376
  *
377
377
  * The registry is frozen via `as const` so TypeScript infers the exact
378
- * string-literal union for `WarningCode` — there is zero runtime cost and
378
+ * string-literal union for `WarningCode` - there is zero runtime cost and
379
379
  * no magic-string comparisons for consumers. Reserved-but-not-emitted
380
380
  * codes carry inline comments documenting which phase activates them
381
381
  * (Phase 2 declares the union so the schema is stable for downstream
@@ -439,7 +439,7 @@ type WarningCode = (typeof WARNING_CODES)[keyof typeof WARNING_CODES];
439
439
  * subclass) so they can be safely accumulated into `Dataset.warnings` and
440
440
  * passed to `onWarning` callbacks.
441
441
  *
442
- * Per D-07 there is intentionally NO `snippet` field on warnings —
442
+ * Per D-07 there is intentionally NO `snippet` field on warnings:
443
443
  * real-world files routinely produce 50+ warnings and a per-warning
444
444
  * snippet would balloon retained memory. Snippets appear only on
445
445
  * `DicomParseError` (the strict-mode escalation path).
@@ -461,7 +461,7 @@ interface DicomParseWarning {
461
461
  }
462
462
 
463
463
  /**
464
- * Phase 2 structural `Item` — a single `(FFFE,E000)`-delimited Item
464
+ * Phase 2 structural `Item` - a single `(FFFE,E000)`-delimited Item
465
465
  * inside a Sequence, carrying a nested `Dataset`.
466
466
  *
467
467
  * Per `02-CONTEXT.md` D-04: structural surface only; navigation methods
@@ -471,7 +471,7 @@ interface DicomParseWarning {
471
471
  */
472
472
 
473
473
  /**
474
- * Initialiser shape for an `Item` — extends `DatasetInit` with `index`,
474
+ * Initialiser shape for an `Item` - extends `DatasetInit` with `index`,
475
475
  * the 0-based position of the item inside its parent `Sequence`.
476
476
  *
477
477
  * @internal
@@ -505,32 +505,32 @@ declare class Item extends Dataset {
505
505
  }
506
506
 
507
507
  /**
508
- * Decoded-value model for Phase 3 — the typed result of `Element.value`.
508
+ * Decoded-value model for Phase 3 - the typed result of `Element.value`.
509
509
  *
510
510
  * `DicomValue` is a discriminated union keyed on `kind`; each variant maps
511
511
  * to a family of VRs that share a decode shape (per PS3.5 §6.2 Table 6.2-1):
512
512
  *
513
- * - `empty` — zero-length value (any VR).
514
- * - `text` — single-value text VRs `LT ST UT UR` (no backslash
513
+ * - `empty` - zero-length value (any VR).
514
+ * - `text` - single-value text VRs `LT ST UT UR` (no backslash
515
515
  * multiplicity; charset-decoded except `UR`).
516
- * - `strings` — backslash-multi-valued string VRs
516
+ * - `strings` - backslash-multi-valued string VRs
517
517
  * `AE AS CS LO SH UC UI` (charset-decoded for
518
518
  * `LO SH UC`; ASCII for the rest).
519
- * - `personName` — `PN`, decoded to its 3-group / 5-component structure.
520
- * - `numbers` — binary numeric VRs `US UL SS SL FL FD` (signedness
519
+ * - `personName` - `PN`, decoded to its 3-group / 5-component structure.
520
+ * - `numbers` - binary numeric VRs `US UL SS SL FL FD` (signedness
521
521
  * from the VR, never guessed).
522
- * - `bigints` — 64-bit binary VRs `SV UV` (kept as `bigint` so no
522
+ * - `bigints` - 64-bit binary VRs `SV UV` (kept as `bigint` so no
523
523
  * precision is lost above 2^53).
524
- * - `attributeTags` — `AT`, decoded to 8-hex tag strings.
525
- * - `decimalString` — `DS`, parsed to `number | null` (non-numeric → null).
526
- * - `integerString` — `IS`, parsed to `number | null` (non-integer → null).
527
- * - `dates` — `DA`, tolerant temporal decode.
528
- * - `times` — `TM`, tolerant temporal decode.
529
- * - `dateTimes` — `DT`, tolerant temporal decode.
530
- * - `binary` — bulk byte VRs `OB OD OF OL OV OW UN` (raw preserved;
531
- * not interpreted in v1 — avoids the giant-typed-array
524
+ * - `attributeTags` - `AT`, decoded to 8-hex tag strings.
525
+ * - `decimalString` - `DS`, parsed to `number | null` (non-numeric → null).
526
+ * - `integerString` - `IS`, parsed to `number | null` (non-integer → null).
527
+ * - `dates` - `DA`, tolerant temporal decode.
528
+ * - `times` - `TM`, tolerant temporal decode.
529
+ * - `dateTimes` - `DT`, tolerant temporal decode.
530
+ * - `binary` - bulk byte VRs `OB OD OF OL OV OW UN` (raw preserved;
531
+ * not interpreted in v1 - avoids the giant-typed-array
532
532
  * footgun for pixel / LUT payloads).
533
- * - `sequence` — `SQ`, the parsed `Item[]`.
533
+ * - `sequence` - `SQ`, the parsed `Item[]`.
534
534
  *
535
535
  * Every variant whose decode may flag a tolerated deviation carries an
536
536
  * optional `warnings` array (decode is lazy and post-parse, so these cannot
@@ -564,7 +564,7 @@ interface PersonNameGroup {
564
564
  readonly nameSuffix: string;
565
565
  }
566
566
  /**
567
- * A decoded `PN` value — up to three component groups separated on-wire by
567
+ * A decoded `PN` value - up to three component groups separated on-wire by
568
568
  * `=` (PS3.5 §6.2.1.1). `alphabetic` is always present; `ideographic` and
569
569
  * `phonetic` are present only when the value supplied them.
570
570
  *
@@ -584,7 +584,7 @@ interface PersonName {
584
584
  }
585
585
  /**
586
586
  * A tolerantly-decoded `DA` (Date) value. `raw` always carries the on-wire
587
- * string (PHI-safe — a date is not an identifier on its own, but is
587
+ * string (PHI-safe - a date is not an identifier on its own, but is
588
588
  * preserved verbatim regardless). `valid` is `true` only when the string
589
589
  * parsed cleanly to a `YYYYMMDD` calendar date; otherwise the numeric
590
590
  * fields are omitted and `raw` is the source of truth.
@@ -710,11 +710,11 @@ type DicomValue = {
710
710
  };
711
711
 
712
712
  /**
713
- * Phase 2 structural `Element` — leaf wrapper over an on-wire DICOM
713
+ * Phase 2 structural `Element` - leaf wrapper over an on-wire DICOM
714
714
  * Data Element.
715
715
  *
716
716
  * Per `02-CONTEXT.md` D-04 + D-42: structural fields ONLY. No `.value`
717
- * getter, no decoders, no `.items` navigation — those land in Phase 3,
717
+ * getter, no decoders, no `.items` navigation - those land in Phase 3,
718
718
  * which extends this class.
719
719
  *
720
720
  * Per D-16: `rawBytes` is a `Buffer.subarray()` view over the source
@@ -755,7 +755,7 @@ interface ElementInit {
755
755
  /**
756
756
  * Byte order of the value bytes, set by the parser per transfer syntax
757
757
  * (`true` for Implicit/Explicit VR LE + Deflated; `false` for Explicit VR
758
- * BE). Phase 3 numeric decoders read this — signedness comes from the VR,
758
+ * BE). Phase 3 numeric decoders read this - signedness comes from the VR,
759
759
  * endianness from here.
760
760
  */
761
761
  readonly littleEndian: boolean;
@@ -839,7 +839,7 @@ declare class Element {
839
839
  constructor(init: ElementInit);
840
840
  /**
841
841
  * The decoded value of this element, by VR. Decode is lazy (the structural
842
- * parse stays eager; field decode runs on first access) and memoized — the
842
+ * parse stays eager; field decode runs on first access) and memoized - the
843
843
  * documented ~30× win on large studies where most fields are never read.
844
844
  * Fail-safe: never throws; a malformed value surfaces as typed-absent with
845
845
  * the deviation on the returned value's `warnings`.
@@ -859,7 +859,7 @@ declare class Element {
859
859
  * Phase 2 structural `FileMeta` view-object shape.
860
860
  *
861
861
  * Per `02-CONTEXT.md` D-04 + D-17 + FM-02: a plain interface (not a class)
862
- * since Phase 2 has no methods. Only `transferSyntaxUID` is required —
862
+ * since Phase 2 has no methods. Only `transferSyntaxUID` is required;
863
863
  * the rest are populated by `parseFileMeta` (plan 02-02) when present
864
864
  * but never enforced (Phase 7's `validate()` enforces FM Type-1 fields
865
865
  * per D-19). Phase 3 may promote to a class if helpers are added.
@@ -872,9 +872,9 @@ declare class Element {
872
872
  * serializer can re-emit an exotic File Meta group byte-for-byte.
873
873
  *
874
874
  * The typed {@link FileMeta} fields cover the common Type-1/Type-3 elements;
875
- * anything else a source file carried — e.g. `(0002,0017)`/`(0002,0018)`
875
+ * anything else a source file carried - e.g. `(0002,0017)`/`(0002,0018)`
876
876
  * Sending/Receiving AE Title, `(0002,0100)` Private Information Creator UID,
877
- * `(0002,0102)` Private Information — is captured here as raw bytes (the
877
+ * `(0002,0102)` Private Information - is captured here as raw bytes (the
878
878
  * on-wire value, even-length per PS3.5 §6.2) rather than dropped. `value` is a
879
879
  * defensive copy, so the view never aliases the parsed input buffer.
880
880
  */
@@ -929,11 +929,11 @@ interface FileMeta {
929
929
  * a missing value is **typed-absent** (`undefined`), never a substituted
930
930
  * default; a malformed numeric component stays `null` inside its array
931
931
  * rather than being coerced; raw bytes are always still reachable via the
932
- * underlying `Element`. These views *interpret* nothing about pixels — they
932
+ * underlying `Element`. These views *interpret* nothing about pixels - they
933
933
  * surface the metadata a downstream renderer needs to interpret them
934
934
  * correctly.
935
935
  *
936
- * The dangerous DICOM failure is the confident, wrong image — so the
936
+ * The dangerous DICOM failure is the confident, wrong image - so the
937
937
  * deliberate omissions are as load-bearing as the fields: there is no
938
938
  * `signed` boolean unless `(0028,0103)` was actually present, no rescale
939
939
  * slope unless `(0028,1053)` was, and the three pixel-spacing tags are
@@ -943,7 +943,7 @@ interface FileMeta {
943
943
  */
944
944
 
945
945
  /**
946
- * One entry of Other Patient IDs Sequence `(0010,1002)` — a `{id, issuer,
946
+ * One entry of Other Patient IDs Sequence `(0010,1002)` - a `{id, issuer,
947
947
  * typeCode}` triple (PS3.3 §10.15, the DICOM analogue of an HL7 v2 CX
948
948
  * repetition). Surfaced so a caller never matches on a bare `(0010,0020)`.
949
949
  *
@@ -959,7 +959,7 @@ interface OtherPatientId {
959
959
  readonly typeCode?: string;
960
960
  }
961
961
  /**
962
- * Patient & study identity (§4.1 — the wrong-patient failure class).
962
+ * Patient & study identity (§4.1 - the wrong-patient failure class).
963
963
  *
964
964
  * `id` is **not globally unique**; correct cross-system matching needs the
965
965
  * `{id, issuerOfId, issuerQualifiers}` tuple plus `otherIds`. `name` keeps
@@ -969,7 +969,7 @@ interface OtherPatientId {
969
969
  * ```ts
970
970
  * import { parseDicom } from "@cosyte/dicom";
971
971
  * const p = parseDicom(buf).patient;
972
- * p.id; // "MRN-42" — meaningless without the issuer
972
+ * p.id; // "MRN-42" - meaningless without the issuer
973
973
  * p.issuerOfId; // "HOSP_A"
974
974
  * p.name?.alphabetic.familyName; // "Doe"
975
975
  * ```
@@ -1047,7 +1047,7 @@ interface CodedConcept {
1047
1047
  readonly schemeUid?: string;
1048
1048
  }
1049
1049
  /**
1050
- * A Real World Value Mapping (§4.5) — slope/intercept bound atomically to
1050
+ * A Real World Value Mapping (§4.5) - slope/intercept bound atomically to
1051
1051
  * its UCUM measurement-units code, so a number is never detached from its
1052
1052
  * units. From Real World Value Mapping Sequence `(0040,9096)`.
1053
1053
  *
@@ -1067,7 +1067,7 @@ interface RealWorldValueMap {
1067
1067
  * single frame Per-Frame-else-Shared (§4.4, PS3.3 §C.7.6.16). Each macro is
1068
1068
  * typed-absent when present in neither the per-frame nor the shared group
1069
1069
  * (the three *geometry* macros are treated as required for an enhanced
1070
- * object — see {@link ImageView.frame}).
1070
+ * object - see {@link ImageView.frame}).
1071
1071
  *
1072
1072
  * @example
1073
1073
  * ```ts
@@ -1101,7 +1101,7 @@ interface FrameFunctionalGroups {
1101
1101
  };
1102
1102
  }
1103
1103
  /**
1104
- * Pixel-interpretation + geometry metadata (§4.2 / §4.3 / §4.4 / §4.5) — the
1104
+ * Pixel-interpretation + geometry metadata (§4.2 / §4.3 / §4.4 / §4.5) - the
1105
1105
  * "wrong pixels look fine" and "looks fine, measures wrong" classes. v1 does
1106
1106
  * not decode pixels; this view surfaces exactly what a renderer needs so it
1107
1107
  * does not have to guess.
@@ -1119,7 +1119,7 @@ interface FrameFunctionalGroups {
1119
1119
  * const img = parseDicom(buf).image;
1120
1120
  * img.rescaleSlope; // undefined ⇒ caller MUST NOT assume 1
1121
1121
  * img.signed; // undefined ⇒ signedness unknown, never guess
1122
- * img.pixelSpacing; // patient-plane mm — distinct from imagerPixelSpacing
1122
+ * img.pixelSpacing; // patient-plane mm - distinct from imagerPixelSpacing
1123
1123
  * ```
1124
1124
  */
1125
1125
  interface ImageView {
@@ -1166,7 +1166,7 @@ interface ImageView {
1166
1166
  }
1167
1167
 
1168
1168
  /**
1169
- * Phase 2 structural `Dataset` — top-level container produced by
1169
+ * Phase 2 structural `Dataset` - top-level container produced by
1170
1170
  * `parseDicom`.
1171
1171
  *
1172
1172
  * Per `02-CONTEXT.md` D-04 + D-42: only `fileMeta` and `warnings` are
@@ -1186,7 +1186,7 @@ interface ImageView {
1186
1186
  * Initialiser shape accepted by the `Dataset` (and `Item`) constructor.
1187
1187
  *
1188
1188
  * `fileMeta` is optional because nested `Item` datasets do not carry
1189
- * their own File Meta — only the root `Dataset` returned by
1189
+ * their own File Meta - only the root `Dataset` returned by
1190
1190
  * `parseDicom` does.
1191
1191
  *
1192
1192
  * @internal
@@ -1197,7 +1197,7 @@ interface DatasetInit {
1197
1197
  readonly elements: ReadonlyMap<Tag, Element>;
1198
1198
  }
1199
1199
  /**
1200
- * One parsed DICOM dataset — the structural shell shipped by Phase 2.
1200
+ * One parsed DICOM dataset - the structural shell shipped by Phase 2.
1201
1201
  *
1202
1202
  * Phase 2 public surface: `fileMeta`, `warnings`. The internal element
1203
1203
  * map is stored on `_elements` (protected) and Phase 3 promotes it to
@@ -1276,7 +1276,7 @@ declare class Dataset {
1276
1276
  /**
1277
1277
  * All elements matching a tag as an array (never `undefined`). A dataset
1278
1278
  * holds at most one element per tag, so this returns a 0- or 1-length
1279
- * array — the convenience complement of {@link Dataset.get} for callers
1279
+ * array - the convenience complement of {@link Dataset.get} for callers
1280
1280
  * that prefer an always-array shape.
1281
1281
  *
1282
1282
  * @example
@@ -1289,7 +1289,7 @@ declare class Dataset {
1289
1289
  getAll(tag: Tag): readonly Element[];
1290
1290
  /**
1291
1291
  * Patient-identity view (§4.1). Fail-safe typed-absent fields; `id` is
1292
- * **not** globally unique — match on the `{id, issuerOfId, ...}` tuple
1292
+ * **not** globally unique - match on the `{id, issuerOfId, ...}` tuple
1293
1293
  * plus `otherIds`, never on a bare `(0010,0020)`. Memoised on first read.
1294
1294
  *
1295
1295
  * @example
@@ -1342,7 +1342,7 @@ declare class Dataset {
1342
1342
  }
1343
1343
 
1344
1344
  /**
1345
- * Public parser entry — `parseDicom`.
1345
+ * Public parser entry - `parseDicom`.
1346
1346
  *
1347
1347
  * Pipeline (Phase 2 plan 02-02):
1348
1348
  * 1. Dual EMPTY_INPUT check (raw input + post-normalize) per CONTEXT D-13.
@@ -1355,7 +1355,7 @@ declare class Dataset {
1355
1355
  * unsupported UIDs throw `UNSUPPORTED_TRANSFER_SYNTAX` carrying the
1356
1356
  * `Dictionary.uid(uid)?.name` in `err.snippet`.
1357
1357
  * 6. The chosen strategy returns a `ReadonlyMap<Tag, Element>` (Phase 2
1358
- * stubs — real bodies arrive in plans 02-03 / 02-04 / 02-05).
1358
+ * stubs - real bodies arrive in plans 02-03 / 02-04 / 02-05).
1359
1359
  * 7. The result is assembled into a structural {@link Dataset}.
1360
1360
  *
1361
1361
  * Phase 6 (D-45) wires a source/vendor `Profile` via `ParseOptions.profile`:
@@ -1369,17 +1369,17 @@ declare class Dataset {
1369
1369
  /**
1370
1370
  * Parse a DICOM Part 10 buffer into a structural {@link Dataset}.
1371
1371
  *
1372
- * Lenient by default — recoverable deviations (missing preamble, File Meta
1372
+ * Lenient by default - recoverable deviations (missing preamble, File Meta
1373
1373
  * group-length mismatch, odd-length value, etc.) are pushed into
1374
1374
  * `ds.warnings` with stable codes from `WARNING_CODES`. Four unrecoverable
1375
1375
  * structural failures throw `DicomParseError`:
1376
1376
  *
1377
- * - `EMPTY_INPUT` — empty `Buffer | Uint8Array | ArrayBuffer`.
1378
- * - `NOT_DICOM_PART_10` — input lacks both `DICM` magic at offset 128 and
1377
+ * - `EMPTY_INPUT` - empty `Buffer | Uint8Array | ArrayBuffer`.
1378
+ * - `NOT_DICOM_PART_10` - input lacks both `DICM` magic at offset 128 and
1379
1379
  * a recognizable `(0002,0000)` File Meta Group Length at offset 0.
1380
- * - `INVALID_FILE_META` — File Meta is truncated or `(0002,0010)`
1380
+ * - `INVALID_FILE_META` - File Meta is truncated or `(0002,0010)`
1381
1381
  * Transfer Syntax UID is missing.
1382
- * - `UNSUPPORTED_TRANSFER_SYNTAX` — Transfer Syntax UID is not one of the
1382
+ * - `UNSUPPORTED_TRANSFER_SYNTAX` - Transfer Syntax UID is not one of the
1383
1383
  * four v1 UIDs (`1.2.840.10008.1.2`, `…1.2.1`, `…1.2.2`, `…1.2.1.99`).
1384
1384
  *
1385
1385
  * Pass `{ strict: true }` to escalate every Tier-2 warning to a thrown
@@ -1421,9 +1421,9 @@ declare function parseDicom(input: Buffer | Uint8Array | ArrayBuffer): Dataset;
1421
1421
  declare function parseDicom(input: Buffer | Uint8Array | ArrayBuffer, options: ParseOptions): Dataset;
1422
1422
 
1423
1423
  /**
1424
- * Phase 2 structural `Sequence` — wrapper around an SQ element's items.
1424
+ * Phase 2 structural `Sequence` - wrapper around an SQ element's items.
1425
1425
  *
1426
- * Per `02-CONTEXT.md` D-04: structural surface only — `items` and
1426
+ * Per `02-CONTEXT.md` D-04: structural surface only - `items` and
1427
1427
  * `length`. No navigation methods. Phase 3 adds `Sequence.items[N].get(...)`
1428
1428
  * via `Item`'s `Dataset`-superclass extension per D-42.
1429
1429
  *
@@ -1459,9 +1459,9 @@ declare class Sequence {
1459
1459
  * Fatal error taxonomy for the `@cosyte/dicom` parser pipeline.
1460
1460
  *
1461
1461
  * Phase 2 core-parser context:
1462
- * - D-09 — `FATAL_CODES` is a frozen `as const` registry with EXACTLY
1462
+ * - D-09 - `FATAL_CODES` is a frozen `as const` registry with EXACTLY
1463
1463
  * four codes; anything less severe MUST be a Tier-2 warning.
1464
- * - D-10 — `DicomParseError` carries `code`, `byteOffset`, `snippet`
1464
+ * - D-10 - `DicomParseError` carries `code`, `byteOffset`, `snippet`
1465
1465
  * (up to 16 source bytes, space-separated lowercase hex), and an
1466
1466
  * optional `contextPath`. The thrown `Error.message` is formatted
1467
1467
  * `[CODE] msg (offset=N)` with `… in path/segments` appended when
@@ -1522,7 +1522,7 @@ declare const FATAL_CODES: {
1522
1522
  type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];
1523
1523
  /**
1524
1524
  * Thrown by `parseDicom` when the input violates one of the four
1525
- * unrecoverable Tier-3 structural rules — or, under `{ strict: true }`,
1525
+ * unrecoverable Tier-3 structural rules - or, under `{ strict: true }`,
1526
1526
  * when any Tier-2 warning is escalated through the single `emit`
1527
1527
  * chokepoint (D-35). Carries byte-offset positional context plus a short
1528
1528
  * source snippet so consumers can log actionable errors.
@@ -1531,7 +1531,7 @@ type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];
1531
1531
  * when `contextPath` is provided.
1532
1532
  *
1533
1533
  * @remarks
1534
- * Snippets may contain PHI when parsing real clinical files — redact at
1534
+ * Snippets may contain PHI when parsing real clinical files - redact at
1535
1535
  * the call site if required by your compliance posture. The library does
1536
1536
  * not redact snippets itself.
1537
1537
  *
@@ -1562,7 +1562,7 @@ declare class DicomParseError extends Error {
1562
1562
  }
1563
1563
 
1564
1564
  /**
1565
- * VR-aware value dispatch — the body behind `Element.value`.
1565
+ * VR-aware value dispatch - the body behind `Element.value`.
1566
1566
  *
1567
1567
  * `decodeElementValue` maps an `Element`'s `vr` + raw bytes to a typed
1568
1568
  * {@link DicomValue}, applying the fail-safe rules from PS3.5 §6.2:
@@ -1613,7 +1613,7 @@ declare function decodeElementValue(element: Element): DicomValue;
1613
1613
  *
1614
1614
  * Term-list corrections verified against PS3.3 §C.12.1.1.2 for this build
1615
1615
  * (the easy-to-get-wrong ones):
1616
- * - There is **no `ISO_IR 14`** — only `ISO_IR 13` (JIS X 0201 Katakana).
1616
+ * - There is **no `ISO_IR 14`** - only `ISO_IR 13` (JIS X 0201 Katakana).
1617
1617
  * - `IR 87` / `IR 159` are **code-extension-only**: the bare `ISO_IR 87` /
1618
1618
  * `ISO_IR 159` terms do NOT exist; only `ISO 2022 IR 87` / `ISO 2022 IR
1619
1619
  * 159` are valid (so they are absent from the single-byte term list).
@@ -1646,7 +1646,7 @@ declare function parseSpecificCharacterSet(bytes: Buffer): readonly string[];
1646
1646
  * ```ts
1647
1647
  * import { isKnownCharsetTerm } from "@cosyte/dicom";
1648
1648
  * isKnownCharsetTerm("ISO_IR 192"); // true
1649
- * isKnownCharsetTerm("ISO_IR 14"); // false — does not exist
1649
+ * isKnownCharsetTerm("ISO_IR 14"); // false - does not exist
1650
1650
  * ```
1651
1651
  */
1652
1652
  declare function isKnownCharsetTerm(term: string): boolean;
@@ -1707,7 +1707,7 @@ declare function parsePersonName(value: string): PersonName;
1707
1707
  * Tolerant temporal decoders for `DA` / `TM` / `DT` (PS3.5 §6.2).
1708
1708
  *
1709
1709
  * Every decoder is fail-safe: a malformed value yields `valid: false` with
1710
- * the raw string preserved and the numeric fields omitted — it NEVER throws
1710
+ * the raw string preserved and the numeric fields omitted - it NEVER throws
1711
1711
  * and NEVER coerces to a plausible-but-wrong date. Tolerated legacy forms
1712
1712
  * (retired dotted `YYYY.MM.DD`, partial precision, non-standard offsets) are
1713
1713
  * decoded best-effort and flagged to the caller so it can emit the matching
@@ -1768,7 +1768,7 @@ declare function parseDateTime(raw: string): {
1768
1768
  * A coded concept is the triplet `Code Value (0008,0100)` /
1769
1769
  * `Coding Scheme Designator (0008,0102)` / `Code Meaning (0008,0104)`. This
1770
1770
  * module **surfaces** the triplet and resolves the canonical scheme OID for
1771
- * the four standard designators — it performs **no lookup, validation, or
1771
+ * the four standard designators - it performs **no lookup, validation, or
1772
1772
  * mapping** (deferred). Detaching a code value from its scheme is unsafe, so
1773
1773
  * the three parts always travel together.
1774
1774
  *
@@ -1803,7 +1803,7 @@ declare const CODING_SCHEME_OIDS: {
1803
1803
  * ```ts
1804
1804
  * import { codingSchemeOid } from "@cosyte/dicom";
1805
1805
  * codingSchemeOid("UCUM"); // "2.16.840.1.113883.6.8"
1806
- * codingSchemeOid("SRT"); // undefined — not SCT (CP-730)
1806
+ * codingSchemeOid("SRT"); // undefined - not SCT (CP-730)
1807
1807
  * ```
1808
1808
  */
1809
1809
  declare function codingSchemeOid(designator: string | undefined): string | undefined;
@@ -1826,15 +1826,15 @@ declare function readCode(item: Dataset): CodedConcept;
1826
1826
  *
1827
1827
  * This is **separate** from the parser's four-code `FATAL_CODES` /
1828
1828
  * `DicomParseError` (locked to unrecoverable Part 10 structural corruption
1829
- * — see `../../parser/errors.ts`). The §4 helpers are otherwise fail-safe:
1829
+ * - see `../../parser/errors.ts`). The §4 helpers are otherwise fail-safe:
1830
1830
  * a missing *value* is typed-absent (`undefined`), never an exception. The
1831
1831
  * helper layer throws only for a **structural contract violation** the
1832
1832
  * caller asked it to resolve and that cannot be answered safely:
1833
1833
  *
1834
- * - `FRAME_INDEX_OUT_OF_RANGE` — `image.frame(i)` called with an index
1834
+ * - `FRAME_INDEX_OUT_OF_RANGE` - `image.frame(i)` called with an index
1835
1835
  * outside `[0, numberOfFrames)`. Returning a guessed frame would be a
1836
1836
  * silent wrong-image, so we refuse.
1837
- * - `MISSING_REQUIRED_FUNCTIONAL_GROUP` — an Enhanced multi-frame object
1837
+ * - `MISSING_REQUIRED_FUNCTIONAL_GROUP` - an Enhanced multi-frame object
1838
1838
  * lacks a required geometry macro (Pixel Measures / Plane Position /
1839
1839
  * Plane Orientation) in *both* the Per-Frame and Shared groups, so the
1840
1840
  * frame cannot be spatially placed.
@@ -1905,7 +1905,7 @@ declare class DicomValueError extends Error {
1905
1905
  }
1906
1906
 
1907
1907
  /**
1908
- * `serializeDicom` — the Phase 5 Part 10 writer (the conservative half of
1908
+ * `serializeDicom` - the Phase 5 Part 10 writer (the conservative half of
1909
1909
  * Postel's Law).
1910
1910
  *
1911
1911
  * Takes a {@link Dataset} (from `parseDicom`, or hand-built) and emits a
@@ -1913,10 +1913,10 @@ declare class DicomValueError extends Error {
1913
1913
  *
1914
1914
  * 1. 128-byte zero preamble + `DICM` magic (PS3.10 §7.1).
1915
1915
  * 2. File Meta group `0002`, always Explicit VR LE, with a correct
1916
- * `(0002,0000)` group length — see {@link encodeFileMeta}.
1916
+ * `(0002,0000)` group length - see {@link encodeFileMeta}.
1917
1917
  * 3. The dataset body, in the dataset's own transfer syntax (**no
1918
- * transcode**): Implicit VR LE, Explicit VR LE/BE, or — for the Deflated
1919
- * syntax — an Explicit VR LE body run through RFC 1951 raw deflate
1918
+ * transcode**): Implicit VR LE, Explicit VR LE/BE, or - for the Deflated
1919
+ * syntax - an Explicit VR LE body run through RFC 1951 raw deflate
1920
1920
  * (`zlib.deflateRawSync`), symmetric to the parser's `inflateRawSync`.
1921
1921
  *
1922
1922
  * Conservative behaviour (PS3.5): scalar values are padded to even length on
@@ -1934,7 +1934,7 @@ declare class DicomValueError extends Error {
1934
1934
  * The dataset's transfer syntax is preserved (no transcoding): pixel-data
1935
1935
  * fragments and nested sequences are written back byte-for-byte, while scalar
1936
1936
  * values are re-emitted with correct even-length padding and File Meta group
1937
- * length. Pure function — the input `Dataset` is never mutated.
1937
+ * length. Pure function - the input `Dataset` is never mutated.
1938
1938
  *
1939
1939
  * **Input contract.** The writer is designed for a {@link Dataset} produced by
1940
1940
  * `parseDicom`: it relies on the parser's `Element.rawBytes` representation
@@ -1948,7 +1948,7 @@ declare class DicomValueError extends Error {
1948
1948
  * {@link "../dataset/file-meta".FileMeta} fields plus any non-modeled File Meta
1949
1949
  * elements preserved on `extraElements`), not a byte-exact copy of the original
1950
1950
  * file: the 128-byte preamble is normalized to zeros, the File Meta group is
1951
- * rebuilt in ascending tag order (modeled fields + `extraElements` — see
1951
+ * rebuilt in ascending tag order (modeled fields + `extraElements` - see
1952
1952
  * {@link encodeFileMeta}), odd-length values are padded even, and retired
1953
1953
  * `(gggg,0000)` group lengths are dropped.
1954
1954
  *
@@ -1972,14 +1972,14 @@ declare function serializeDicom(ds: Dataset): Buffer;
1972
1972
  * Like the Phase 4 {@link "../dataset/helpers/errors".DicomValueError}, this
1973
1973
  * is **separate** from the parser's four-code `FATAL_CODES` /
1974
1974
  * `DicomParseError` (which are locked to *read*-side Part 10 structural
1975
- * corruption — see `../parser/errors.ts`). The serializer throws only when
1975
+ * corruption - see `../parser/errors.ts`). The serializer throws only when
1976
1976
  * it is asked to emit a buffer it cannot make spec-clean:
1977
1977
  *
1978
- * - `MISSING_TRANSFER_SYNTAX` — the `Dataset` has no `fileMeta`, or its
1978
+ * - `MISSING_TRANSFER_SYNTAX` - the `Dataset` has no `fileMeta`, or its
1979
1979
  * `fileMeta.transferSyntaxUID` is empty. The Transfer Syntax UID is the
1980
1980
  * dispatch input that decides every byte of the encoding, so there is no
1981
1981
  * safe default to fall back to.
1982
- * - `UNSUPPORTED_TRANSFER_SYNTAX` — the Transfer Syntax UID is not one of
1982
+ * - `UNSUPPORTED_TRANSFER_SYNTAX` - the Transfer Syntax UID is not one of
1983
1983
  * the four v1 syntaxes the writer (and parser) support. The writer never
1984
1984
  * transcodes, so it cannot emit a syntax it does not understand.
1985
1985
  *
@@ -2052,7 +2052,7 @@ declare class DicomSerializeError extends Error {
2052
2052
  }
2053
2053
 
2054
2054
  /**
2055
- * `defineProfile()` — the public factory for building immutable {@link Profile}
2055
+ * `defineProfile()` - the public factory for building immutable {@link Profile}
2056
2056
  * objects (Phase 6). Mirrors the `@cosyte/hl7` sibling's profile factory: a
2057
2057
  * validated options object in, a frozen `Profile` with a `describe()` method
2058
2058
  * out. Invalid input throws {@link ProfileDefinitionError} with an actionable
@@ -2132,14 +2132,14 @@ interface DefineProfileOptions {
2132
2132
  declare function defineProfile(opts: DefineProfileOptions): Profile;
2133
2133
 
2134
2134
  /**
2135
- * `ProfileDefinitionError` — the single typed failure surface for
2135
+ * `ProfileDefinitionError` - the single typed failure surface for
2136
2136
  * `defineProfile()`. Thrown (never returned) when profile-author input is
2137
2137
  * malformed: a bad name, an unknown option key, an unknown warning code, an
2138
2138
  * invalid VR, or a private-tag key that does not match the canonical
2139
2139
  * `GGGGxxEE` notation.
2140
2140
  *
2141
2141
  * This is distinct from the parse-time taxonomy (`DicomParseError` /
2142
- * `DicomValueError` / `DicomSerializeError`) — a profile is built once, at
2142
+ * `DicomValueError` / `DicomSerializeError`) - a profile is built once, at
2143
2143
  * author time, long before any byte is parsed, so its errors never carry a
2144
2144
  * byte offset.
2145
2145
  *
@@ -2195,21 +2195,21 @@ declare const profiles: Readonly<{
2195
2195
  }>;
2196
2196
 
2197
2197
  /**
2198
- * PS3.15 Annex E attribute-action table — DICOM Basic Application Confidentiality
2198
+ * PS3.15 Annex E attribute-action table - DICOM Basic Application Confidentiality
2199
2199
  * Profile + 11 retention/clean option sets.
2200
2200
  *
2201
2201
  * Phase 1 deliverable (D-08 / D-09 / D-14). Consumed by Phase 7 `anonymize()`
2202
2202
  * (ANON-01..ANON-10).
2203
2203
  *
2204
2204
  * NOTE: This module is NOT re-exported from the package's `src/index.ts` (per D-10 +
2205
- * D-27 — Phase 1's external surface is `Dictionary.{lookup,byKeyword,uid}` + `VERSION`
2205
+ * D-27 - Phase 1's external surface is `Dictionary.{lookup,byKeyword,uid}` + `VERSION`
2206
2206
  * only). Phase 7 imports `annexE` via the `@cosyte/dicom/dictionary/annex-e` internal
2207
2207
  * path, which the `package.json` `exports` map will admit when Phase 7 lands its plans.
2208
2208
  *
2209
2209
  * The 9 *metadata-affecting* PS3.15 Annex E option-set columns (E.3.3–E.3.11 plus the
2210
2210
  * collapsed E.3.6 "RetainLongitudinalTemporal") populate `AnnexEAction.optionSet`
2211
- * keys per attribute. The two *pixel-level* options — E.3.1 `CleanPixelData` and
2212
- * E.3.2 `CleanRecognizableVisual` — are not represented per-attribute (PS3.15 Table
2211
+ * keys per attribute. The two *pixel-level* options - E.3.1 `CleanPixelData` and
2212
+ * E.3.2 `CleanRecognizableVisual` - are not represented per-attribute (PS3.15 Table
2213
2213
  * E.1-1 has no column for them); Phase 7 enforces them at the pixel-decode layer.
2214
2214
  * Both names remain in `AnnexEOption` for completeness and for Phase 7's API.
2215
2215
  */
@@ -2253,13 +2253,26 @@ type AnnexEOption = "CleanPixelData" | "CleanRecognizableVisual" | "CleanGraphic
2253
2253
  */
2254
2254
 
2255
2255
  /**
2256
- * The PS3.15 Annex E option sets `deidentify` honours — the nine
2256
+ * The PS3.15 Annex E option sets `deidentify` honours - the nine
2257
2257
  * *metadata-affecting* columns of Table E.1-1. The two pixel-level options
2258
2258
  * (`CleanPixelData` §E.3.1, `CleanRecognizableVisual` §E.3.2) are deliberately
2259
2259
  * excluded: this is a metadata-only de-identifier and cannot inspect pixels
2260
2260
  * (deferred to `@cosyte/dicom-pixel`). When pixel data is present it always
2261
2261
  * warns rather than claiming the image is clean.
2262
2262
  *
2263
+ * **`RetainLongitudinalTemporal` gives you the full-dates branch.** PS3.15
2264
+ * §E.3.6 is *two* options, and Table E.1-1 gives them separate columns:
2265
+ * `Rtn. Long. Full Dates` (keep dates and times as they are) and
2266
+ * `Rtn. Long. Modif. Dates` (keep them only as modified/shifted values). One
2267
+ * name here covers both, and it carries the **full-dates** column - the *less*
2268
+ * protective branch. That is not a rounding difference: the two columns disagree
2269
+ * on **169** rows, and on every one of them full-dates says `K` (keep the real
2270
+ * value) where modified-dates says `C` (clean it). Activate it only when real
2271
+ * dates are genuinely required; leave it off and the Basic Profile action
2272
+ * applies, which removes or empties them. Date *shifting* is not implemented at
2273
+ * this layer - a caller who needs the modified-dates behaviour shifts the values
2274
+ * themselves after the call.
2275
+ *
2263
2276
  * @example
2264
2277
  * const retain: DeidentifyOption[] = ["RetainLongitudinalTemporal", "RetainSafePrivate"];
2265
2278
  */
@@ -2275,16 +2288,16 @@ type DeidentifyOption = Exclude<AnnexEOption, "CleanPixelData" | "CleanRecogniza
2275
2288
  */
2276
2289
  declare const DEIDENTIFY_OPTIONS: readonly DeidentifyOption[];
2277
2290
  /**
2278
- * What `deidentify` actually did to one attribute — the concrete outcome of the
2291
+ * What `deidentify` actually did to one attribute - the concrete outcome of the
2279
2292
  * resolved Annex E action.
2280
2293
  *
2281
- * - `removed` — the element was deleted (`X`).
2282
- * - `emptied` — replaced with a zero-length value (`Z`).
2283
- * - `dummied` — replaced with a non-identifying dummy of compatible VR (`D`).
2284
- * - `uid-remapped` — UID(s) replaced with internally-consistent UIDs (`U`).
2285
- * - `cleaned` — conservatively blanked because a safe similar-meaning value
2294
+ * - `removed` - the element was deleted (`X`).
2295
+ * - `emptied` - replaced with a zero-length value (`Z`).
2296
+ * - `dummied` - replaced with a non-identifying dummy of compatible VR (`D`).
2297
+ * - `uid-remapped` - UID(s) replaced with internally-consistent UIDs (`U`).
2298
+ * - `cleaned` - conservatively blanked because a safe similar-meaning value
2286
2299
  * cannot be synthesised at the metadata layer (`C`; see known limitations).
2287
- * - `kept` — retained, either by an active Retain option or because the SQ was
2300
+ * - `kept` - retained, either by an active Retain option or because the SQ was
2288
2301
  * kept and its items cleaned recursively.
2289
2302
  *
2290
2303
  * @example
@@ -2296,8 +2309,8 @@ declare const DEIDENTIFY_OPTIONS: readonly DeidentifyOption[];
2296
2309
  */
2297
2310
  type AppliedAction = "removed" | "emptied" | "dummied" | "uid-remapped" | "cleaned" | "kept";
2298
2311
  /**
2299
- * One audited attribute outcome. Carries only structural facts — tag, keyword,
2300
- * the resolved Annex E action code, and the SQ context path — **never** a
2312
+ * One audited attribute outcome. Carries only structural facts - tag, keyword,
2313
+ * the resolved Annex E action code, and the SQ context path - **never** a
2301
2314
  * decoded value, so a report is always safe to log.
2302
2315
  *
2303
2316
  * @example
@@ -2305,7 +2318,7 @@ type AppliedAction = "removed" | "emptied" | "dummied" | "uid-remapped" | "clean
2305
2318
  * import { deidentify, parseDicom, type DeidentifiedAttribute } from "@cosyte/dicom";
2306
2319
  * const { report } = deidentify(parseDicom(buf));
2307
2320
  * report.attributes.forEach((a: DeidentifiedAttribute) => {
2308
- * console.log(a.keyword, a.action, a.applied); // structural facts only — safe to log
2321
+ * console.log(a.keyword, a.action, a.applied); // structural facts only - safe to log
2309
2322
  * });
2310
2323
  * ```
2311
2324
  */
@@ -2317,10 +2330,17 @@ interface DeidentifiedAttribute {
2317
2330
  readonly applied: AppliedAction;
2318
2331
  /** Tag/index chain for an attribute inside a sequence; omitted at the root. */
2319
2332
  readonly contextPath?: readonly string[];
2333
+ /**
2334
+ * Present when the action came from a Table E.1-1 row that names a
2335
+ * repeating-group family rather than this single tag: the mask that matched,
2336
+ * e.g. `"60xx4000"` for Overlay Comments in any overlay plane. `tag` is always
2337
+ * the concrete tag that was in the file. Absent for every exact-tag row.
2338
+ */
2339
+ readonly repeatingGroup?: string;
2320
2340
  }
2321
2341
  /**
2322
2342
  * The audit trail returned alongside the de-identified dataset. Contains no
2323
- * decoded values — only tags, keywords, action codes, and the UID map (whose
2343
+ * decoded values - only tags, keywords, action codes, and the UID map (whose
2324
2344
  * keys/values are UIDs, not patient data).
2325
2345
  *
2326
2346
  * @example
@@ -2338,13 +2358,13 @@ interface DeidentifyReport {
2338
2358
  readonly removedPrivateTags: readonly Tag[];
2339
2359
  /** Source UID → replacement UID, for cross-file consistency. */
2340
2360
  readonly uidMap: ReadonlyMap<string, string>;
2341
- /** Safety warnings — notably burned-in-pixel annotation that cannot be cleaned. */
2361
+ /** Safety warnings - notably burned-in-pixel annotation that cannot be cleaned. */
2342
2362
  readonly warnings: readonly DicomParseWarning[];
2343
2363
  /** The Retain/Clean options that were active for this run. */
2344
2364
  readonly retained: readonly DeidentifyOption[];
2345
2365
  }
2346
2366
  /**
2347
- * Options controlling a de-identification run. All optional — the default is
2367
+ * Options controlling a de-identification run. All optional - the default is
2348
2368
  * the Basic Application Level Confidentiality Profile with no Retain options.
2349
2369
  *
2350
2370
  * @example
@@ -2362,8 +2382,8 @@ interface DeidentifyOptions {
2362
2382
  /**
2363
2383
  * A caller-owned source→replacement UID cache. Pass one shared map across a
2364
2384
  * whole study/archive to make UID remapping consistent by construction even
2365
- * across separate calls (it is consistent anyway — the mapping is content-
2366
- * derived — but a shared map also makes repeats O(1)).
2385
+ * across separate calls (it is consistent anyway - the mapping is content-
2386
+ * derived - but a shared map also makes repeats O(1)).
2367
2387
  */
2368
2388
  readonly uidMap?: Map<string, string>;
2369
2389
  /**
@@ -2420,14 +2440,14 @@ type DeidentifyErrorCode = (typeof DEIDENTIFY_ERROR_CODES)[keyof typeof DEIDENTI
2420
2440
  * Thrown for an author-time misconfiguration of {@link deidentify} (an unknown
2421
2441
  * Retain option, a malformed UID root). Distinct from the parser's fatal codes,
2422
2442
  * the value layer's `DicomValueError`, and the serializer's `DicomSerializeError`.
2423
- * The message carries only structural facts (option names, the UID root) — never
2443
+ * The message carries only structural facts (option names, the UID root) - never
2424
2444
  * a decoded value.
2425
2445
  *
2426
2446
  * @example
2427
2447
  * ```ts
2428
2448
  * import { deidentify, DeidentifyError } from "@cosyte/dicom";
2429
2449
  * try {
2430
- * // @ts-expect-error — not a valid option
2450
+ * // @ts-expect-error - not a valid option
2431
2451
  * deidentify(ds, { retain: ["RetainEverything"] });
2432
2452
  * } catch (e) {
2433
2453
  * if (e instanceof DeidentifyError) console.error(e.code); // "INVALID_OPTIONS"
@@ -2444,7 +2464,7 @@ declare class DeidentifyError extends Error {
2444
2464
  }
2445
2465
 
2446
2466
  /**
2447
- * `deidentify` — PS3.15 Annex E metadata-level de-identification.
2467
+ * `deidentify` - PS3.15 Annex E metadata-level de-identification.
2448
2468
  *
2449
2469
  * Applies the **Basic Application Level Confidentiality Profile** plus any of the
2450
2470
  * nine *metadata-affecting* Annex E Options, driven by the generated Table E.1-1
@@ -2458,8 +2478,13 @@ declare class DeidentifyError extends Error {
2458
2478
  * (see {@link resolveAction}), and applies it: `X` remove, `Z` zero-length, `D`
2459
2479
  * VR-consistent dummy (falling back to `Z` where no safe dummy exists), `C`
2460
2480
  * conservative blank, `U` deterministic consistent-UID remap, `K` keep.
2481
+ * - Resolves the three Table E.1-1 rows the standard states as a repeating-group
2482
+ * mask (`(50xx,xxxx)` Curve Data, `(60xx,3000)` Overlay Data, `(60xx,4000)`
2483
+ * Overlay Comments) across the groups PS3.5 §7.6 bounds them to, on an
2484
+ * exact-tag miss. A match is removed *and* reported, with
2485
+ * `DeidentifiedAttribute.repeatingGroup` naming the mask.
2461
2486
  * - Recurses into kept sequences and **re-encodes** them so nested PHI is removed
2462
- * in the *serialized* bytes too — not just the object model (the Phase 5 writer
2487
+ * in the *serialized* bytes too - not just the object model (the Phase 5 writer
2463
2488
  * blits `SQ` spans verbatim, so a rebuilt `items` array alone would not survive
2464
2489
  * serialization). Rebuilt sequences are normalized to defined length.
2465
2490
  * - Removes all private attributes by default; with `RetainSafePrivate` + a
@@ -2469,18 +2494,18 @@ declare class DeidentifyError extends Error {
2469
2494
  * `RetainUIDs`), writes `(0012,0062)` Patient Identity Removed = `YES` and
2470
2495
  * `(0012,0063)` De-identification Method, and warns
2471
2496
  * (`DICOM_BURNED_IN_ANNOTATION_NOT_REMOVED`) when Pixel Data is present and not
2472
- * marked free of burned-in annotation — this metadata-only pass cannot clean
2497
+ * marked free of burned-in annotation - this metadata-only pass cannot clean
2473
2498
  * pixels (deferred to `@cosyte/dicom-pixel`).
2474
2499
  *
2475
2500
  * **Known limitations** (documented, fail-safe toward *more* removal):
2476
2501
  * - No IOD Type-1 conformance analysis, so conditional codes always take the
2477
- * most-protective leftmost branch — a Type-1 attribute that strictly needed a
2502
+ * most-protective leftmost branch - a Type-1 attribute that strictly needed a
2478
2503
  * dummy is instead removed/emptied.
2479
2504
  * - `C` (clean) is a conservative blank, not a meaning-preserving structured
2480
2505
  * replacement (which needs domain context the metadata layer lacks).
2481
2506
  * - Pixel-level options (`CleanPixelData`, `CleanRecognizableVisual`) are out of
2482
2507
  * scope; burned-in text is warned, never cleaned.
2483
- * - A private data element kept under `RetainSafePrivate` is kept *verbatim* — if
2508
+ * - A private data element kept under `RetainSafePrivate` is kept *verbatim* - if
2484
2509
  * it is itself a sequence carrying standard PHI attributes, that nested content
2485
2510
  * is not recursed. The profile vouches the element is safe; nest accordingly.
2486
2511
  * - A sequence whose `items` the parser did not materialize (e.g. an
@@ -2491,7 +2516,7 @@ declare class DeidentifyError extends Error {
2491
2516
  */
2492
2517
 
2493
2518
  /**
2494
- * De-identify a {@link Dataset} per PS3.15 Annex E — the Basic Application Level
2519
+ * De-identify a {@link Dataset} per PS3.15 Annex E - the Basic Application Level
2495
2520
  * Confidentiality Profile, plus any Retain/Clean Options passed in `retain`.
2496
2521
  *
2497
2522
  * Pure: `ds` is never mutated. Returns a fresh dataset and a value-free
@@ -2517,19 +2542,19 @@ declare function deidentify(ds: Dataset, options?: DeidentifyOptions): Deidentif
2517
2542
  * The replacement is a pure function of the source UID and the chosen root:
2518
2543
  * `sha256(sourceUid)` reduced to a decimal string under `<root>.…`. Because it
2519
2544
  * is content-derived, the *same* source UID maps to the *same* replacement
2520
- * across separate `deidentify` calls and across files in a study set — without
2521
- * any shared state — so cross-instance referential integrity (Study ⇄ Series ⇄
2545
+ * across separate `deidentify` calls and across files in a study set - without
2546
+ * any shared state - so cross-instance referential integrity (Study ⇄ Series ⇄
2522
2547
  * SOP, Frame of Reference, referenced instances) is preserved. A per-call cache
2523
2548
  * `Map` makes repeats within one call O(1) and lets a caller thread one map
2524
2549
  * through a whole archive if they prefer explicit sharing.
2525
2550
  *
2526
- * Default root `2.25` is the DICOM-sanctioned UUID-derived arc (PS3.5 §B.2) —
2551
+ * Default root `2.25` is the DICOM-sanctioned UUID-derived arc (PS3.5 §B.2):
2527
2552
  * a globally-unique root that needs no registration.
2528
2553
  *
2529
2554
  * @module
2530
2555
  */
2531
2556
  /**
2532
- * DICOM `2.25` UUID-derived root (PS3.5 §B.2) — no registration required.
2557
+ * DICOM `2.25` UUID-derived root (PS3.5 §B.2) - no registration required.
2533
2558
  *
2534
2559
  * @example
2535
2560
  * ```ts