@cosyte/dicom 0.0.13 → 0.0.15

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.cts CHANGED
@@ -114,6 +114,18 @@ interface UidEntry {
114
114
  * throw `TypeError` in strict mode (which all of `@cosyte/dicom`'s emitted code
115
115
  * runs under, given `"use strict"` is implicit for ES modules).
116
116
  *
117
+ * @example
118
+ * ```ts
119
+ * import { Dictionary } from "@cosyte/dicom";
120
+ *
121
+ * // Tag or keyword, either way round, and `undefined` rather than a throw on a miss.
122
+ * Dictionary.lookup("00100020")?.name; // "Patient ID"
123
+ * Dictionary.byKeyword("Modality")?.tag; // "00080060"
124
+ *
125
+ * // UIDs resolve to their registered name, so nobody hard-codes a SOP Class table.
126
+ * Dictionary.uid("1.2.840.10008.5.1.4.1.1.2")?.name; // "CT Image Storage"
127
+ * ```
128
+ *
117
129
  * @module
118
130
  */
119
131
 
@@ -317,8 +329,31 @@ interface ParseOptions {
317
329
  *
318
330
  * 🛑 **THE ESCALATED DIAGNOSTIC CARRIES SOURCE BYTES THAT THE WARNING DOES
319
331
  * NOT.** A `DicomParseWarning.message` is a frozen registry string with only
320
- * structural tokens filled in, so it is safe to log whole; the
321
- * `DicomParseError` this option raises in its place also carries `snippet`,
332
+ * structural tokens filled in: `{tag}` renders only a tag PS3.6's element
333
+ * registry carries a literal row for, `{vr}` only one of the 34 VRs PS3.5
334
+ * section 6.2 defines, and a raw length or byte value a header carries is
335
+ * bound out of the factory signature rather than rendered. That now holds on
336
+ * the `deidentify()` codes too:
337
+ * `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` and
338
+ * `DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE` rendered `Element.rawBytes.length`
339
+ * through `0.0.14`, which equals the declared Value Length and was reachable
340
+ * from a fabricated header, and both slots are gone. So is
341
+ * `DICOM_ITEM_CROSSES_SEQUENCE_END`'s remaining-bytes count, because **a raw
342
+ * number shifted by a constant the reader can compute is that raw number**: it
343
+ * was the enclosing sequence's declared Value Length less the bytes of that
344
+ * sequence already consumed. **The exceptions are named in one place that is
345
+ * not a record of a past change, and are deliberately not restated here** -
346
+ * the `WARNING_MESSAGES` docblock in `./warnings.ts`, which this JSDoc used to
347
+ * carry a copy of.
348
+ * **This is a statement about `w.message` and about nothing else**: the two
349
+ * byte counts still exist on `report.undefinedVrElements[].byteLength` and
350
+ * `report.unauditableSequences[].byteLength`, model fields on a type whose own
351
+ * docs say it is not a value-free surface. **No
352
+ * safe-to-log verdict is stated here** - that sentence was corrected twice and
353
+ * is deleted rather than tried a third time; the mechanism is above and the
354
+ * treatment is in the package's troubleshooting docs. The `DicomParseError`
355
+ * this option raises in its place is a different and larger surface: it also
356
+ * carries `snippet`,
322
357
  * **16 raw bytes, unredacted** (D-10), read at the warning's own `byteOffset`.
323
358
  * A message-only PHI review of the lenient path therefore does not transfer to
324
359
  * the strict one. Log `err.code`, `err.byteOffset` and `err.message`; treat
@@ -921,6 +956,21 @@ declare class Element {
921
956
  * `(0002,0102)` Private Information - is captured here as raw bytes (the
922
957
  * on-wire value, even-length per PS3.5 §6.2) rather than dropped. `value` is a
923
958
  * defensive copy, so the view never aliases the parsed input buffer.
959
+ *
960
+ * @example
961
+ * ```ts
962
+ * import { parseDicom } from "@cosyte/dicom";
963
+ * const ds = parseDicom(buf);
964
+ * // Anything the typed FileMeta fields do not model, in ascending tag order.
965
+ * for (const raw of ds.fileMeta?.extraElements ?? []) {
966
+ * raw.tag; // e.g. "00020017" Sending Application Entity Title
967
+ * raw.vr; // the VR the source wrote (File Meta is always Explicit VR LE)
968
+ * raw.value.length; // even, per PS3.5 2026c section 7.1.1
969
+ * }
970
+ * ```
971
+ *
972
+ * `(0002,0016)` Source Application Entity Title is deliberately NOT the example: it is one of the
973
+ * typed fields, so it never reaches this array.
924
974
  */
925
975
  interface FileMetaRawElement {
926
976
  /** 8-char uppercase hex tag, e.g. `"00020100"`. */
@@ -2448,15 +2498,29 @@ interface DeidentifiedAttribute {
2448
2498
  * (PS3.15 §E.1 "all instances"; §E.3.5 is the standard's own precedent for
2449
2499
  * removing identifying information embedded inside a string attribute).
2450
2500
  *
2451
- * `tag` and `vr` are the carrier's own and are structural. **`hidden` is not,
2452
- * and calling this finding "safe to log" was wrong.** Each entry is composed
2453
- * from four bytes that were sitting *inside* the carrier's value - that is the
2454
- * whole reason the field exists - so on any file that populates it those bytes
2455
- * are document content. Measured: a `CS` carrier over-declaring over the bytes
2456
- * `"SABC"` reports `hidden: ["41534342"]`, which is `"SABC"` in wire order.
2457
- * `PRE-EXISTING` and identical on every release that has shipped the field.
2458
- * Treat `hidden` at the sensitivity of the file, and see
2459
- * {@link DeidentifyReport} for the other fields in this class.
2501
+ * `tag` and `vr` are the carrier's own and are structural. **`hidden` was not,
2502
+ * and it is bound now.** Every tag in an embedded run is composed from four
2503
+ * bytes that were sitting *inside* the carrier's value - that is the whole
2504
+ * reason this type exists - and a run needs only ONE actionable attribute to be
2505
+ * reported, so through `0.0.13` the rest of the run was listed beside it.
2506
+ * Measured: a `CS` carrier over-declaring across a fabricated `"SMIT"` header
2507
+ * beside a genuine `(0010,0020)` reported `hidden: ["4D535449", "00100020"]`,
2508
+ * and `4D535449` is `"SMIT"` in wire order.
2509
+ *
2510
+ * **An entry is now one of the 652 literal rows of PS3.15 Table E.1-1 that this
2511
+ * run's options left actionable.** That is a **membership** bound rather than a
2512
+ * shape one - the posture this package already takes for a VR and for a Private
2513
+ * Creator - so what survives names a published table entry rather than a
2514
+ * document byte, the same trade rendering a VR makes with the 34. **A
2515
+ * repeating-group mask hit is NOT in that set and is excluded**: `(50xx,xxxx)`
2516
+ * Curve Data leaves the whole 16-bit element number free, so a mask match proves
2517
+ * a rule exists without making the membership finite. A graded pass caught a
2518
+ * draft of this filter that admitted it.
2519
+ *
2520
+ * **🛑 THAT IS NOT AN ALL-CLEAR OVER THIS TYPE.** `contextPath` below is
2521
+ * unbound and unchanged, `hidden` is uncapped, and
2522
+ * {@link DeidentifyReport} names the report's other value-bearing fields. A
2523
+ * `DeidentifyReport` is still not safe to log whole.
2460
2524
  *
2461
2525
  * @example
2462
2526
  * ```ts
@@ -2472,7 +2536,18 @@ interface EmbeddedAttributeFinding {
2472
2536
  readonly tag: Tag;
2473
2537
  /** The carrier's VR. Always one of the string VRs; binary VRs are not scanned. */
2474
2538
  readonly vr: VR;
2475
- /** The tags of the Data Elements found inside the carrier's value, in wire order. */
2539
+ /**
2540
+ * The tags found inside the carrier's value **that this run acts on and that a
2541
+ * published table names**, in wire order. **Not every tag in the run** - see
2542
+ * this type's own remarks.
2543
+ *
2544
+ * **🩺 IT MAY BE EMPTY, AND EMPTY DOES NOT MEAN "NOTHING WAS HIDDEN HERE".**
2545
+ * A run whose only actionable members are private attributes, Curve Data or
2546
+ * Overlay elements reports a finding with no tags: the carrier was still
2547
+ * emptied, and the accompanying `DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED`
2548
+ * warning still counts the whole run. The presence of the finding is the fact;
2549
+ * this list is the part of it that can be named.
2550
+ */
2476
2551
  readonly hidden: readonly Tag[];
2477
2552
  /**
2478
2553
  * Tag/index chain when the carrier is inside a sequence item; omitted at the
@@ -2649,12 +2724,21 @@ interface UndefinedVrFinding {
2649
2724
  *
2650
2725
  * **An input-derived count, and on this finding specifically that is not a
2651
2726
  * formality.** For the sibling findings the length came from a header the
2652
- * sender wrote; here the two length bytes can themselves be value bytes, like
2653
- * the tag bytes. What is published is the *number* they decode to, never the
2654
- * bytes - the same footing as every `{n}` in the warning registry, which is
2655
- * documented there as "an input-derived count". A number is not a rendering,
2656
- * and the reach is at most a character or so, but do not describe this field
2657
- * as "structural, never a value" the way its siblings are described.
2727
+ * sender wrote; here the four length bytes can themselves be value bytes, like
2728
+ * the tag bytes. What is published is the *number* they decode to, and one
2729
+ * `readUInt32LE` puts the bytes back: a fabricated header reading `"SO\0\0"`
2730
+ * publishes `20307`, two letters of a surname. Do not describe this field as
2731
+ * "structural, never a value" the way its siblings are described.
2732
+ *
2733
+ * **🩺 IT IS NO LONGER "the same footing as every `{n}` in the warning
2734
+ * registry", AND THAT SENTENCE WAS THE ONE THIS FIELD SHIPPED WITH.**
2735
+ * `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` rendered exactly this number and
2736
+ * does not any more - it is bound out of `undefinedVrNotAuditable`'s
2737
+ * signature. So this field is now a **model field**, on the same standing
2738
+ * exception as `removedPrivateTags`, `unauditableSequences[].tag`, `uidMap`
2739
+ * and `contextPath`: narrowing it is a product call, because a bound empties
2740
+ * it on every well-formed file, where it is exactly the audit number the field
2741
+ * exists to carry. Deliberately unchanged; see this type's own summary.
2658
2742
  */
2659
2743
  readonly byteLength: number;
2660
2744
  /**
@@ -2697,13 +2781,18 @@ interface UndefinedVrFinding {
2697
2781
  * and still answers it whenever the fabricated VR is outside the 34 PS3.5
2698
2782
  * §6.2 defines. It cannot when the fabricated VR is one of them, because those
2699
2783
  * two files are byte-identical.
2700
- * - **`embeddedAttributes[].hidden`** - see {@link EmbeddedAttributeFinding}.
2701
- * Each entry is a tag composed from four bytes that were sitting **inside**
2702
- * a value, which is the position this type exists to distrust, so on a file
2703
- * that populates the field at all they are document content by
2704
- * construction. `PRE-EXISTING` and identical on every release that has
2705
- * shipped the field; it is disclosed here and its remedy is its own slice,
2706
- * alongside the cap that field is also missing.
2784
+ * - **`undefinedVrElements[].byteLength` and
2785
+ * `unauditableSequences[].byteLength`** - the declared Value Length read off
2786
+ * the element header, so on a fabricated header it is four document bytes
2787
+ * wearing a decimal: `"SO\0\0"` publishes `20307`, two letters of a surname,
2788
+ * put back with one `readUInt32LE`. **They JOINED this list rather than
2789
+ * always having been on it.** Through `0.0.14` the two
2790
+ * `DICOM_DEIDENT_*_NOT_AUDITABLE` **messages** rendered the same number;
2791
+ * binding it out of those factory signatures left these model fields as its
2792
+ * only publisher, which is a smaller surface and not a closed one. Narrowing
2793
+ * them is a product call rather than a defect fix: a bound empties the field
2794
+ * on every well-formed file, where the number is exactly the audit
2795
+ * information it exists to carry.
2707
2796
  * - **`contextPath`, on all four findings that carry one** - see
2708
2797
  * {@link DeidentifiedAttribute.contextPath}, which holds the measurement. The
2709
2798
  * segment tags come off the wire with no table behind them, so a fabricated
@@ -2714,8 +2803,18 @@ interface UndefinedVrFinding {
2714
2803
  * same file the de-identified object itself re-emits the fabricated header, so
2715
2804
  * redacting this field does not make the object safe.**
2716
2805
  *
2806
+ * `embeddedAttributes[].hidden` **left that list** in
2807
+ * `DICOM-DIAGNOSTIC-PHI-RESIDUALS` - an entry is now a literal PS3.15 Table
2808
+ * E.1-1 row, so it is not value-bearing. Its own disclosure had been reworded
2809
+ * twice by then, and this repo deletes a disclosure at that point rather than
2810
+ * writing a third; what is true of the field is stated once, on
2811
+ * {@link EmbeddedAttributeFinding}. The field is **still uncapped**.
2812
+ *
2717
2813
  * So "the report is safe to log apart from `uidMap`" is **not** an accurate
2718
2814
  * description of this type, and was corrected rather than kept convenient.
2815
+ * **This is the only copy of that list.** Two others lived in module docstrings,
2816
+ * still naming `hidden` after it left and never naming `contextPath` at all; a
2817
+ * graded pass found them and they were deleted rather than resynced.
2719
2818
  *
2720
2819
  * @example
2721
2820
  * ```ts
@@ -2994,9 +3093,8 @@ declare class DeidentifyError extends Error {
2994
3093
  * action map ({@link annexE}). It is a **pure** function: the input {@link Dataset}
2995
3094
  * is never mutated; a fresh `Dataset` (with a rebuilt element map and File Meta)
2996
3095
  * is returned alongside a {@link DeidentifyReport} whose non-value-free fields
2997
- * are named on that type: `uidMap`, `removedPrivateTags`,
2998
- * `unauditableSequences[].tag` and `embeddedAttributes[].hidden`. Read the list
2999
- * there, never a count quoted elsewhere.
3096
+ * are named on that type. **The list is deliberately not copied here** - see
3097
+ * `./index.ts` for what a third copy of it cost.
3000
3098
  *
3001
3099
  * **What it does**
3002
3100
  * - Resolves each attribute's action (basic profile, overridden by an active
package/dist/index.d.ts CHANGED
@@ -114,6 +114,18 @@ interface UidEntry {
114
114
  * throw `TypeError` in strict mode (which all of `@cosyte/dicom`'s emitted code
115
115
  * runs under, given `"use strict"` is implicit for ES modules).
116
116
  *
117
+ * @example
118
+ * ```ts
119
+ * import { Dictionary } from "@cosyte/dicom";
120
+ *
121
+ * // Tag or keyword, either way round, and `undefined` rather than a throw on a miss.
122
+ * Dictionary.lookup("00100020")?.name; // "Patient ID"
123
+ * Dictionary.byKeyword("Modality")?.tag; // "00080060"
124
+ *
125
+ * // UIDs resolve to their registered name, so nobody hard-codes a SOP Class table.
126
+ * Dictionary.uid("1.2.840.10008.5.1.4.1.1.2")?.name; // "CT Image Storage"
127
+ * ```
128
+ *
117
129
  * @module
118
130
  */
119
131
 
@@ -317,8 +329,31 @@ interface ParseOptions {
317
329
  *
318
330
  * 🛑 **THE ESCALATED DIAGNOSTIC CARRIES SOURCE BYTES THAT THE WARNING DOES
319
331
  * NOT.** A `DicomParseWarning.message` is a frozen registry string with only
320
- * structural tokens filled in, so it is safe to log whole; the
321
- * `DicomParseError` this option raises in its place also carries `snippet`,
332
+ * structural tokens filled in: `{tag}` renders only a tag PS3.6's element
333
+ * registry carries a literal row for, `{vr}` only one of the 34 VRs PS3.5
334
+ * section 6.2 defines, and a raw length or byte value a header carries is
335
+ * bound out of the factory signature rather than rendered. That now holds on
336
+ * the `deidentify()` codes too:
337
+ * `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` and
338
+ * `DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE` rendered `Element.rawBytes.length`
339
+ * through `0.0.14`, which equals the declared Value Length and was reachable
340
+ * from a fabricated header, and both slots are gone. So is
341
+ * `DICOM_ITEM_CROSSES_SEQUENCE_END`'s remaining-bytes count, because **a raw
342
+ * number shifted by a constant the reader can compute is that raw number**: it
343
+ * was the enclosing sequence's declared Value Length less the bytes of that
344
+ * sequence already consumed. **The exceptions are named in one place that is
345
+ * not a record of a past change, and are deliberately not restated here** -
346
+ * the `WARNING_MESSAGES` docblock in `./warnings.ts`, which this JSDoc used to
347
+ * carry a copy of.
348
+ * **This is a statement about `w.message` and about nothing else**: the two
349
+ * byte counts still exist on `report.undefinedVrElements[].byteLength` and
350
+ * `report.unauditableSequences[].byteLength`, model fields on a type whose own
351
+ * docs say it is not a value-free surface. **No
352
+ * safe-to-log verdict is stated here** - that sentence was corrected twice and
353
+ * is deleted rather than tried a third time; the mechanism is above and the
354
+ * treatment is in the package's troubleshooting docs. The `DicomParseError`
355
+ * this option raises in its place is a different and larger surface: it also
356
+ * carries `snippet`,
322
357
  * **16 raw bytes, unredacted** (D-10), read at the warning's own `byteOffset`.
323
358
  * A message-only PHI review of the lenient path therefore does not transfer to
324
359
  * the strict one. Log `err.code`, `err.byteOffset` and `err.message`; treat
@@ -921,6 +956,21 @@ declare class Element {
921
956
  * `(0002,0102)` Private Information - is captured here as raw bytes (the
922
957
  * on-wire value, even-length per PS3.5 §6.2) rather than dropped. `value` is a
923
958
  * defensive copy, so the view never aliases the parsed input buffer.
959
+ *
960
+ * @example
961
+ * ```ts
962
+ * import { parseDicom } from "@cosyte/dicom";
963
+ * const ds = parseDicom(buf);
964
+ * // Anything the typed FileMeta fields do not model, in ascending tag order.
965
+ * for (const raw of ds.fileMeta?.extraElements ?? []) {
966
+ * raw.tag; // e.g. "00020017" Sending Application Entity Title
967
+ * raw.vr; // the VR the source wrote (File Meta is always Explicit VR LE)
968
+ * raw.value.length; // even, per PS3.5 2026c section 7.1.1
969
+ * }
970
+ * ```
971
+ *
972
+ * `(0002,0016)` Source Application Entity Title is deliberately NOT the example: it is one of the
973
+ * typed fields, so it never reaches this array.
924
974
  */
925
975
  interface FileMetaRawElement {
926
976
  /** 8-char uppercase hex tag, e.g. `"00020100"`. */
@@ -2448,15 +2498,29 @@ interface DeidentifiedAttribute {
2448
2498
  * (PS3.15 §E.1 "all instances"; §E.3.5 is the standard's own precedent for
2449
2499
  * removing identifying information embedded inside a string attribute).
2450
2500
  *
2451
- * `tag` and `vr` are the carrier's own and are structural. **`hidden` is not,
2452
- * and calling this finding "safe to log" was wrong.** Each entry is composed
2453
- * from four bytes that were sitting *inside* the carrier's value - that is the
2454
- * whole reason the field exists - so on any file that populates it those bytes
2455
- * are document content. Measured: a `CS` carrier over-declaring over the bytes
2456
- * `"SABC"` reports `hidden: ["41534342"]`, which is `"SABC"` in wire order.
2457
- * `PRE-EXISTING` and identical on every release that has shipped the field.
2458
- * Treat `hidden` at the sensitivity of the file, and see
2459
- * {@link DeidentifyReport} for the other fields in this class.
2501
+ * `tag` and `vr` are the carrier's own and are structural. **`hidden` was not,
2502
+ * and it is bound now.** Every tag in an embedded run is composed from four
2503
+ * bytes that were sitting *inside* the carrier's value - that is the whole
2504
+ * reason this type exists - and a run needs only ONE actionable attribute to be
2505
+ * reported, so through `0.0.13` the rest of the run was listed beside it.
2506
+ * Measured: a `CS` carrier over-declaring across a fabricated `"SMIT"` header
2507
+ * beside a genuine `(0010,0020)` reported `hidden: ["4D535449", "00100020"]`,
2508
+ * and `4D535449` is `"SMIT"` in wire order.
2509
+ *
2510
+ * **An entry is now one of the 652 literal rows of PS3.15 Table E.1-1 that this
2511
+ * run's options left actionable.** That is a **membership** bound rather than a
2512
+ * shape one - the posture this package already takes for a VR and for a Private
2513
+ * Creator - so what survives names a published table entry rather than a
2514
+ * document byte, the same trade rendering a VR makes with the 34. **A
2515
+ * repeating-group mask hit is NOT in that set and is excluded**: `(50xx,xxxx)`
2516
+ * Curve Data leaves the whole 16-bit element number free, so a mask match proves
2517
+ * a rule exists without making the membership finite. A graded pass caught a
2518
+ * draft of this filter that admitted it.
2519
+ *
2520
+ * **🛑 THAT IS NOT AN ALL-CLEAR OVER THIS TYPE.** `contextPath` below is
2521
+ * unbound and unchanged, `hidden` is uncapped, and
2522
+ * {@link DeidentifyReport} names the report's other value-bearing fields. A
2523
+ * `DeidentifyReport` is still not safe to log whole.
2460
2524
  *
2461
2525
  * @example
2462
2526
  * ```ts
@@ -2472,7 +2536,18 @@ interface EmbeddedAttributeFinding {
2472
2536
  readonly tag: Tag;
2473
2537
  /** The carrier's VR. Always one of the string VRs; binary VRs are not scanned. */
2474
2538
  readonly vr: VR;
2475
- /** The tags of the Data Elements found inside the carrier's value, in wire order. */
2539
+ /**
2540
+ * The tags found inside the carrier's value **that this run acts on and that a
2541
+ * published table names**, in wire order. **Not every tag in the run** - see
2542
+ * this type's own remarks.
2543
+ *
2544
+ * **🩺 IT MAY BE EMPTY, AND EMPTY DOES NOT MEAN "NOTHING WAS HIDDEN HERE".**
2545
+ * A run whose only actionable members are private attributes, Curve Data or
2546
+ * Overlay elements reports a finding with no tags: the carrier was still
2547
+ * emptied, and the accompanying `DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED`
2548
+ * warning still counts the whole run. The presence of the finding is the fact;
2549
+ * this list is the part of it that can be named.
2550
+ */
2476
2551
  readonly hidden: readonly Tag[];
2477
2552
  /**
2478
2553
  * Tag/index chain when the carrier is inside a sequence item; omitted at the
@@ -2649,12 +2724,21 @@ interface UndefinedVrFinding {
2649
2724
  *
2650
2725
  * **An input-derived count, and on this finding specifically that is not a
2651
2726
  * formality.** For the sibling findings the length came from a header the
2652
- * sender wrote; here the two length bytes can themselves be value bytes, like
2653
- * the tag bytes. What is published is the *number* they decode to, never the
2654
- * bytes - the same footing as every `{n}` in the warning registry, which is
2655
- * documented there as "an input-derived count". A number is not a rendering,
2656
- * and the reach is at most a character or so, but do not describe this field
2657
- * as "structural, never a value" the way its siblings are described.
2727
+ * sender wrote; here the four length bytes can themselves be value bytes, like
2728
+ * the tag bytes. What is published is the *number* they decode to, and one
2729
+ * `readUInt32LE` puts the bytes back: a fabricated header reading `"SO\0\0"`
2730
+ * publishes `20307`, two letters of a surname. Do not describe this field as
2731
+ * "structural, never a value" the way its siblings are described.
2732
+ *
2733
+ * **🩺 IT IS NO LONGER "the same footing as every `{n}` in the warning
2734
+ * registry", AND THAT SENTENCE WAS THE ONE THIS FIELD SHIPPED WITH.**
2735
+ * `DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE` rendered exactly this number and
2736
+ * does not any more - it is bound out of `undefinedVrNotAuditable`'s
2737
+ * signature. So this field is now a **model field**, on the same standing
2738
+ * exception as `removedPrivateTags`, `unauditableSequences[].tag`, `uidMap`
2739
+ * and `contextPath`: narrowing it is a product call, because a bound empties
2740
+ * it on every well-formed file, where it is exactly the audit number the field
2741
+ * exists to carry. Deliberately unchanged; see this type's own summary.
2658
2742
  */
2659
2743
  readonly byteLength: number;
2660
2744
  /**
@@ -2697,13 +2781,18 @@ interface UndefinedVrFinding {
2697
2781
  * and still answers it whenever the fabricated VR is outside the 34 PS3.5
2698
2782
  * §6.2 defines. It cannot when the fabricated VR is one of them, because those
2699
2783
  * two files are byte-identical.
2700
- * - **`embeddedAttributes[].hidden`** - see {@link EmbeddedAttributeFinding}.
2701
- * Each entry is a tag composed from four bytes that were sitting **inside**
2702
- * a value, which is the position this type exists to distrust, so on a file
2703
- * that populates the field at all they are document content by
2704
- * construction. `PRE-EXISTING` and identical on every release that has
2705
- * shipped the field; it is disclosed here and its remedy is its own slice,
2706
- * alongside the cap that field is also missing.
2784
+ * - **`undefinedVrElements[].byteLength` and
2785
+ * `unauditableSequences[].byteLength`** - the declared Value Length read off
2786
+ * the element header, so on a fabricated header it is four document bytes
2787
+ * wearing a decimal: `"SO\0\0"` publishes `20307`, two letters of a surname,
2788
+ * put back with one `readUInt32LE`. **They JOINED this list rather than
2789
+ * always having been on it.** Through `0.0.14` the two
2790
+ * `DICOM_DEIDENT_*_NOT_AUDITABLE` **messages** rendered the same number;
2791
+ * binding it out of those factory signatures left these model fields as its
2792
+ * only publisher, which is a smaller surface and not a closed one. Narrowing
2793
+ * them is a product call rather than a defect fix: a bound empties the field
2794
+ * on every well-formed file, where the number is exactly the audit
2795
+ * information it exists to carry.
2707
2796
  * - **`contextPath`, on all four findings that carry one** - see
2708
2797
  * {@link DeidentifiedAttribute.contextPath}, which holds the measurement. The
2709
2798
  * segment tags come off the wire with no table behind them, so a fabricated
@@ -2714,8 +2803,18 @@ interface UndefinedVrFinding {
2714
2803
  * same file the de-identified object itself re-emits the fabricated header, so
2715
2804
  * redacting this field does not make the object safe.**
2716
2805
  *
2806
+ * `embeddedAttributes[].hidden` **left that list** in
2807
+ * `DICOM-DIAGNOSTIC-PHI-RESIDUALS` - an entry is now a literal PS3.15 Table
2808
+ * E.1-1 row, so it is not value-bearing. Its own disclosure had been reworded
2809
+ * twice by then, and this repo deletes a disclosure at that point rather than
2810
+ * writing a third; what is true of the field is stated once, on
2811
+ * {@link EmbeddedAttributeFinding}. The field is **still uncapped**.
2812
+ *
2717
2813
  * So "the report is safe to log apart from `uidMap`" is **not** an accurate
2718
2814
  * description of this type, and was corrected rather than kept convenient.
2815
+ * **This is the only copy of that list.** Two others lived in module docstrings,
2816
+ * still naming `hidden` after it left and never naming `contextPath` at all; a
2817
+ * graded pass found them and they were deleted rather than resynced.
2719
2818
  *
2720
2819
  * @example
2721
2820
  * ```ts
@@ -2994,9 +3093,8 @@ declare class DeidentifyError extends Error {
2994
3093
  * action map ({@link annexE}). It is a **pure** function: the input {@link Dataset}
2995
3094
  * is never mutated; a fresh `Dataset` (with a rebuilt element map and File Meta)
2996
3095
  * is returned alongside a {@link DeidentifyReport} whose non-value-free fields
2997
- * are named on that type: `uidMap`, `removedPrivateTags`,
2998
- * `unauditableSequences[].tag` and `embeddedAttributes[].hidden`. Read the list
2999
- * there, never a count quoted elsewhere.
3096
+ * are named on that type. **The list is deliberately not copied here** - see
3097
+ * `./index.ts` for what a third copy of it cost.
3000
3098
  *
3001
3099
  * **What it does**
3002
3100
  * - Resolves each attribute's action (basic profile, overridden by an active