@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/CHANGELOG.md +414 -25
- package/README.md +57 -48
- package/TRADEMARKS.md +32 -0
- package/dist/index.cjs +651 -200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +158 -133
- package/dist/index.d.ts +158 -133
- package/dist/index.mjs +651 -200
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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`
|
|
95
|
+
* Public Dictionary namespace for `@cosyte/dicom` - Phase 1 deliverable.
|
|
96
96
|
*
|
|
97
97
|
* D-10:
|
|
98
98
|
*
|
|
99
|
-
* - {@link lookup}
|
|
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}
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
172
|
-
* - D-03
|
|
173
|
-
* - D-07
|
|
174
|
-
* - D-45
|
|
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
|
|
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`
|
|
208
|
+
* - `escalations` - Tier-2 warning codes promoted to a thrown
|
|
209
209
|
* `DicomParseError` (a stricter posture for known-unsafe deviations).
|
|
210
|
-
* - `suppressions`
|
|
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`
|
|
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
|
|
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)`
|
|
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)`
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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`
|
|
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`
|
|
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
|
|
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`
|
|
514
|
-
* - `text`
|
|
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`
|
|
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`
|
|
520
|
-
* - `numbers`
|
|
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`
|
|
522
|
+
* - `bigints` - 64-bit binary VRs `SV UV` (kept as `bigint` so no
|
|
523
523
|
* precision is lost above 2^53).
|
|
524
|
-
* - `attributeTags`
|
|
525
|
-
* - `decimalString`
|
|
526
|
-
* - `integerString`
|
|
527
|
-
* - `dates`
|
|
528
|
-
* - `times`
|
|
529
|
-
* - `dateTimes`
|
|
530
|
-
* - `binary`
|
|
531
|
-
* not interpreted in v1
|
|
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`
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)`
|
|
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
|
|
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"
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
1378
|
-
* - `NOT_DICOM_PART_10`
|
|
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`
|
|
1380
|
+
* - `INVALID_FILE_META` - File Meta is truncated or `(0002,0010)`
|
|
1381
1381
|
* Transfer Syntax UID is missing.
|
|
1382
|
-
* - `UNSUPPORTED_TRANSFER_SYNTAX`
|
|
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`
|
|
1424
|
+
* Phase 2 structural `Sequence` - wrapper around an SQ element's items.
|
|
1425
1425
|
*
|
|
1426
|
-
* Per `02-CONTEXT.md` D-04: structural surface only
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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`
|
|
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`
|
|
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`
|
|
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
|
|
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
|
|
1919
|
-
* syntax
|
|
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
|
|
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`
|
|
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
|
|
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`
|
|
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`
|
|
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()`
|
|
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`
|
|
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`)
|
|
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
|
|
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
|
|
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
|
|
2212
|
-
* E.3.2 `CleanRecognizableVisual`
|
|
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
|
|
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
|
|
2291
|
+
* What `deidentify` actually did to one attribute - the concrete outcome of the
|
|
2279
2292
|
* resolved Annex E action.
|
|
2280
2293
|
*
|
|
2281
|
-
* - `removed`
|
|
2282
|
-
* - `emptied`
|
|
2283
|
-
* - `dummied`
|
|
2284
|
-
* - `uid-remapped`
|
|
2285
|
-
* - `cleaned`
|
|
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`
|
|
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
|
|
2300
|
-
* the resolved Annex E action code, and the SQ context path
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2366
|
-
* derived
|
|
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)
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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*
|
|
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
|
|
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
|
|
2521
|
-
* any shared state
|
|
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)
|
|
2557
|
+
* DICOM `2.25` UUID-derived root (PS3.5 §B.2) - no registration required.
|
|
2533
2558
|
*
|
|
2534
2559
|
* @example
|
|
2535
2560
|
* ```ts
|