@ubercode/dcmtk 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { R as Result } from './types-Cgumy1N4.js';
2
- import { D as DicomJsonElement, T as TagModification } from './dcmodify-BvaIeyJg.js';
2
+ import { D as DicomJsonElement, a as DicomJsonModel, T as TagModification } from './dcmodify-CHvwChFu.js';
3
3
 
4
4
  /**
5
5
  * Branded types for domain primitives.
@@ -142,6 +142,47 @@ declare function createPort(input: number): Result<Port>;
142
142
  */
143
143
  declare function tag(input: string): DicomTagPath;
144
144
 
145
+ /**
146
+ * Utility for iterating all DICOM tags in a JSON Model dataset.
147
+ *
148
+ * Supports VR filtering and bounded recursion into sequences.
149
+ *
150
+ * @module dicom/walkTags
151
+ */
152
+
153
+ /** Information about a visited DICOM tag. */
154
+ interface WalkTagEntry {
155
+ /** 8-char hex tag key (e.g., '00100010'). */
156
+ readonly tag: string;
157
+ /** The DICOM JSON element with vr, Value, etc. */
158
+ readonly element: DicomJsonElement;
159
+ /** VR code (e.g., 'PN', 'US', 'SQ'). */
160
+ readonly vr: string;
161
+ /** Nesting depth (0 = top-level). */
162
+ readonly depth: number;
163
+ /** Dot-separated path (e.g., '00081115[0].0020000E'). */
164
+ readonly path: string;
165
+ }
166
+ /** Options for walkTags. */
167
+ interface WalkTagsOptions {
168
+ /** Only visit tags with these VR codes. Omit to visit all. */
169
+ readonly vrFilter?: ReadonlyArray<string> | undefined;
170
+ /** Max recursion depth into sequences. Default: 16. */
171
+ readonly maxDepth?: number | undefined;
172
+ }
173
+ /**
174
+ * Walks all tags in a DICOM JSON Model dataset.
175
+ *
176
+ * Iterates every tag, optionally filtering by VR and recursing into sequences.
177
+ * Sequences are always recursed into (even if SQ is filtered out) so nested
178
+ * tags can match the VR filter.
179
+ *
180
+ * @param data - A DICOM JSON Model object
181
+ * @param options - Optional VR filter and max depth
182
+ * @returns A readonly array of all matching tag entries
183
+ */
184
+ declare function walkTags(data: DicomJsonModel, options?: WalkTagsOptions): ReadonlyArray<WalkTagEntry>;
185
+
145
186
  /**
146
187
  * Immutable DICOM dataset wrapper with type-safe accessors.
147
188
  *
@@ -253,6 +294,13 @@ declare class DicomDataset {
253
294
  * @returns A readonly array of all matching values (may be empty)
254
295
  */
255
296
  findValues(path: DicomTagPath): ReadonlyArray<unknown>;
297
+ /**
298
+ * Walks all tags in this dataset, optionally filtering by VR and recursing into sequences.
299
+ *
300
+ * @param options - Optional VR filter and max depth
301
+ * @returns A readonly array of all matching tag entries
302
+ */
303
+ walkTags(options?: WalkTagsOptions): ReadonlyArray<WalkTagEntry>;
256
304
  /** Accession Number (0008,0050). */
257
305
  get accession(): string;
258
306
  /** Patient's Name (0010,0010). */
@@ -622,4 +670,4 @@ declare class DicomInstance {
622
670
  getMetadata(key: string): unknown;
623
671
  }
624
672
 
625
- export { type AETitle as A, type Brand as B, ChangeSet as C, type DicomTag as D, type Port as P, type SOPClassUID as S, type TransferSyntaxUID as T, type DicomTagPath as a, DicomDataset as b, type DicomFilePath as c, DicomInstance as d, createAETitle as e, createDicomFilePath as f, createDicomTag as g, createDicomTagPath as h, createPort as i, createSOPClassUID as j, createTransferSyntaxUID as k, tag as t };
673
+ export { type AETitle as A, type Brand as B, ChangeSet as C, type DicomTag as D, type Port as P, type SOPClassUID as S, type TransferSyntaxUID as T, type WalkTagEntry as W, type DicomTagPath as a, DicomDataset as b, type DicomFilePath as c, DicomInstance as d, type WalkTagsOptions as e, createAETitle as f, createDicomFilePath as g, createDicomTag as h, createDicomTagPath as i, createPort as j, createSOPClassUID as k, createTransferSyntaxUID as l, tag as t, walkTags as w };
@@ -1,5 +1,5 @@
1
1
  import { R as Result } from './types-Cgumy1N4.cjs';
2
- import { D as DicomJsonElement, T as TagModification } from './dcmodify-B9js5K1f.cjs';
2
+ import { D as DicomJsonElement, a as DicomJsonModel, T as TagModification } from './dcmodify-Cf-RPHF3.cjs';
3
3
 
4
4
  /**
5
5
  * Branded types for domain primitives.
@@ -142,6 +142,47 @@ declare function createPort(input: number): Result<Port>;
142
142
  */
143
143
  declare function tag(input: string): DicomTagPath;
144
144
 
145
+ /**
146
+ * Utility for iterating all DICOM tags in a JSON Model dataset.
147
+ *
148
+ * Supports VR filtering and bounded recursion into sequences.
149
+ *
150
+ * @module dicom/walkTags
151
+ */
152
+
153
+ /** Information about a visited DICOM tag. */
154
+ interface WalkTagEntry {
155
+ /** 8-char hex tag key (e.g., '00100010'). */
156
+ readonly tag: string;
157
+ /** The DICOM JSON element with vr, Value, etc. */
158
+ readonly element: DicomJsonElement;
159
+ /** VR code (e.g., 'PN', 'US', 'SQ'). */
160
+ readonly vr: string;
161
+ /** Nesting depth (0 = top-level). */
162
+ readonly depth: number;
163
+ /** Dot-separated path (e.g., '00081115[0].0020000E'). */
164
+ readonly path: string;
165
+ }
166
+ /** Options for walkTags. */
167
+ interface WalkTagsOptions {
168
+ /** Only visit tags with these VR codes. Omit to visit all. */
169
+ readonly vrFilter?: ReadonlyArray<string> | undefined;
170
+ /** Max recursion depth into sequences. Default: 16. */
171
+ readonly maxDepth?: number | undefined;
172
+ }
173
+ /**
174
+ * Walks all tags in a DICOM JSON Model dataset.
175
+ *
176
+ * Iterates every tag, optionally filtering by VR and recursing into sequences.
177
+ * Sequences are always recursed into (even if SQ is filtered out) so nested
178
+ * tags can match the VR filter.
179
+ *
180
+ * @param data - A DICOM JSON Model object
181
+ * @param options - Optional VR filter and max depth
182
+ * @returns A readonly array of all matching tag entries
183
+ */
184
+ declare function walkTags(data: DicomJsonModel, options?: WalkTagsOptions): ReadonlyArray<WalkTagEntry>;
185
+
145
186
  /**
146
187
  * Immutable DICOM dataset wrapper with type-safe accessors.
147
188
  *
@@ -253,6 +294,13 @@ declare class DicomDataset {
253
294
  * @returns A readonly array of all matching values (may be empty)
254
295
  */
255
296
  findValues(path: DicomTagPath): ReadonlyArray<unknown>;
297
+ /**
298
+ * Walks all tags in this dataset, optionally filtering by VR and recursing into sequences.
299
+ *
300
+ * @param options - Optional VR filter and max depth
301
+ * @returns A readonly array of all matching tag entries
302
+ */
303
+ walkTags(options?: WalkTagsOptions): ReadonlyArray<WalkTagEntry>;
256
304
  /** Accession Number (0008,0050). */
257
305
  get accession(): string;
258
306
  /** Patient's Name (0010,0010). */
@@ -622,4 +670,4 @@ declare class DicomInstance {
622
670
  getMetadata(key: string): unknown;
623
671
  }
624
672
 
625
- export { type AETitle as A, type Brand as B, ChangeSet as C, type DicomTag as D, type Port as P, type SOPClassUID as S, type TransferSyntaxUID as T, type DicomTagPath as a, DicomDataset as b, type DicomFilePath as c, DicomInstance as d, createAETitle as e, createDicomFilePath as f, createDicomTag as g, createDicomTagPath as h, createPort as i, createSOPClassUID as j, createTransferSyntaxUID as k, tag as t };
673
+ export { type AETitle as A, type Brand as B, ChangeSet as C, type DicomTag as D, type Port as P, type SOPClassUID as S, type TransferSyntaxUID as T, type WalkTagEntry as W, type DicomTagPath as a, DicomDataset as b, type DicomFilePath as c, DicomInstance as d, type WalkTagsOptions as e, createAETitle as f, createDicomFilePath as g, createDicomTag as h, createDicomTagPath as i, createPort as j, createSOPClassUID as k, createTransferSyntaxUID as l, tag as t, walkTags as w };
@@ -123,4 +123,4 @@ interface DcmodifyResult {
123
123
  */
124
124
  declare function dcmodify(inputPath: string, options: DcmodifyOptions): Promise<Result<DcmodifyResult>>;
125
125
 
126
- export { type DicomJsonElement as D, type TagModification as T, type DcmodifyOptions as a, type DcmodifyResult as b, type DicomJsonModel as c, type ToolBaseOptions as d, dcmodify as e, xmlToJson as x };
126
+ export { type DicomJsonElement as D, type TagModification as T, type DicomJsonModel as a, type DcmodifyOptions as b, type DcmodifyResult as c, type ToolBaseOptions as d, dcmodify as e, xmlToJson as x };
@@ -123,4 +123,4 @@ interface DcmodifyResult {
123
123
  */
124
124
  declare function dcmodify(inputPath: string, options: DcmodifyOptions): Promise<Result<DcmodifyResult>>;
125
125
 
126
- export { type DicomJsonElement as D, type TagModification as T, type DcmodifyOptions as a, type DcmodifyResult as b, type DicomJsonModel as c, type ToolBaseOptions as d, dcmodify as e, xmlToJson as x };
126
+ export { type DicomJsonElement as D, type TagModification as T, type DicomJsonModel as a, type DcmodifyOptions as b, type DcmodifyResult as c, type ToolBaseOptions as d, dcmodify as e, xmlToJson as x };
package/dist/dicom.cjs CHANGED
@@ -29725,6 +29725,45 @@ function segmentsToModifyPath(segments) {
29725
29725
  function segmentsToString(segments) {
29726
29726
  return segmentsToModifyPath(segments);
29727
29727
  }
29728
+
29729
+ // src/dicom/walkTags.ts
29730
+ var DEFAULT_MAX_DEPTH = 16;
29731
+ function walkTags(data, options) {
29732
+ const ctx = {
29733
+ maxDepth: options?.maxDepth ?? DEFAULT_MAX_DEPTH,
29734
+ vrSet: options?.vrFilter !== void 0 ? new Set(options.vrFilter) : void 0,
29735
+ results: []
29736
+ };
29737
+ walkLevel(data, 0, "", ctx);
29738
+ return ctx.results;
29739
+ }
29740
+ function walkLevel(data, depth, pathPrefix, ctx) {
29741
+ const keys = Object.keys(data);
29742
+ for (let i = 0; i < keys.length; i++) {
29743
+ const tag = keys[i];
29744
+ if (tag === void 0) continue;
29745
+ const element = data[tag];
29746
+ if (element === void 0) continue;
29747
+ const vr = element.vr;
29748
+ const path = pathPrefix.length > 0 ? `${pathPrefix}.${tag}` : tag;
29749
+ if (ctx.vrSet === void 0 || ctx.vrSet.has(vr)) {
29750
+ ctx.results.push({ tag, element, vr, depth, path });
29751
+ }
29752
+ if (vr === "SQ" && element.Value !== void 0 && depth < ctx.maxDepth) {
29753
+ walkSequenceItems(element.Value, depth, path, ctx);
29754
+ }
29755
+ }
29756
+ }
29757
+ function walkSequenceItems(items, depth, parentPath, ctx) {
29758
+ for (let idx = 0; idx < items.length; idx++) {
29759
+ const item = items[idx];
29760
+ if (typeof item !== "object" || item === null) continue;
29761
+ const itemPath = `${parentPath}[${idx}]`;
29762
+ walkLevel(item, depth + 1, itemPath, ctx);
29763
+ }
29764
+ }
29765
+
29766
+ // src/dicom/DicomDataset.ts
29728
29767
  var HEX_TAG_KEY = /^[0-9A-Fa-f]{8}$/;
29729
29768
  function isDicomJsonElement(value) {
29730
29769
  return typeof value === "object" && value !== null && "vr" in value && typeof value["vr"] === "string";
@@ -30053,6 +30092,15 @@ var DicomDataset = class _DicomDataset {
30053
30092
  const result = collectWildcard(this.data, segments);
30054
30093
  return result.values;
30055
30094
  }
30095
+ /**
30096
+ * Walks all tags in this dataset, optionally filtering by VR and recursing into sequences.
30097
+ *
30098
+ * @param options - Optional VR filter and max depth
30099
+ * @returns A readonly array of all matching tag entries
30100
+ */
30101
+ walkTags(options) {
30102
+ return walkTags(this.data, options);
30103
+ }
30056
30104
  // -----------------------------------------------------------------------
30057
30105
  // Convenience readonly getters
30058
30106
  // -----------------------------------------------------------------------
@@ -30614,15 +30662,32 @@ function convertSequence(attr, element) {
30614
30662
  }
30615
30663
  if (values.length > 0) element.Value = values;
30616
30664
  }
30617
- function convertRegularValue(attr, element) {
30665
+ var NUMERIC_JSON_VRS = /* @__PURE__ */ new Set(["DS", "FL", "FD", "IS", "SL", "SS", "SV", "UL", "US", "UV"]);
30666
+ function unwrapValue(v) {
30667
+ if (typeof v !== "object" || v === null) return v;
30668
+ const obj = v;
30669
+ if ("#text" in obj) return obj["#text"];
30670
+ const keys = Object.keys(obj);
30671
+ if (keys.length === 1 && keys[0] !== void 0 && keys[0].startsWith("@_")) {
30672
+ return obj[keys[0]];
30673
+ }
30674
+ return v;
30675
+ }
30676
+ function coerceNumeric(value) {
30677
+ if (typeof value === "number") return value;
30678
+ if (typeof value !== "string") return value;
30679
+ const trimmed = value.trim();
30680
+ if (trimmed.length === 0) return value;
30681
+ const num = Number(trimmed);
30682
+ return Number.isNaN(num) ? value : num;
30683
+ }
30684
+ function convertRegularValue(attr, element, vr) {
30618
30685
  const valArray = toArray(attr.Value);
30619
30686
  const values = [];
30687
+ const isNumeric = NUMERIC_JSON_VRS.has(vr);
30620
30688
  for (const v of valArray) {
30621
- if (typeof v === "object" && v !== null && "#text" in v) {
30622
- values.push(v["#text"]);
30623
- } else {
30624
- values.push(v);
30625
- }
30689
+ const unwrapped = unwrapValue(v);
30690
+ values.push(isNumeric ? coerceNumeric(unwrapped) : unwrapped);
30626
30691
  }
30627
30692
  if (values.length > 0) element.Value = values;
30628
30693
  }
@@ -30639,7 +30704,7 @@ function convertElement(attr) {
30639
30704
  } else if (element.vr === "SQ" && attr.Item !== void 0) {
30640
30705
  convertSequence(attr, element);
30641
30706
  } else if (attr.Value !== void 0) {
30642
- convertRegularValue(attr, element);
30707
+ convertRegularValue(attr, element, vr);
30643
30708
  }
30644
30709
  return Object.freeze(element);
30645
30710
  }