@ubercode/dcmtk 0.1.1 → 0.2.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.
Files changed (41) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +18 -15
  3. package/dist/DicomInstance-By9zd7GM.d.cts +625 -0
  4. package/dist/DicomInstance-CQEIuF_x.d.ts +625 -0
  5. package/dist/{dcmodify-CTXBWKU9.d.cts → dcmodify-B-_uUIKB.d.ts} +4 -2
  6. package/dist/{dcmodify-Daeafqrm.d.ts → dcmodify-Gds9u5Vj.d.cts} +4 -2
  7. package/dist/dicom.cjs +329 -51
  8. package/dist/dicom.cjs.map +1 -1
  9. package/dist/dicom.d.cts +368 -3
  10. package/dist/dicom.d.ts +368 -3
  11. package/dist/dicom.js +329 -51
  12. package/dist/dicom.js.map +1 -1
  13. package/dist/index.cjs +1460 -419
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +8 -7
  16. package/dist/index.d.ts +8 -7
  17. package/dist/index.js +1432 -413
  18. package/dist/index.js.map +1 -1
  19. package/dist/servers.cjs +2379 -196
  20. package/dist/servers.cjs.map +1 -1
  21. package/dist/servers.d.cts +1654 -3
  22. package/dist/servers.d.ts +1654 -3
  23. package/dist/servers.js +2305 -145
  24. package/dist/servers.js.map +1 -1
  25. package/dist/tools.cjs +97 -50
  26. package/dist/tools.cjs.map +1 -1
  27. package/dist/tools.d.cts +21 -4
  28. package/dist/tools.d.ts +21 -4
  29. package/dist/tools.js +97 -51
  30. package/dist/tools.js.map +1 -1
  31. package/dist/{types-zHhxS7d2.d.cts → types-Cgumy1N4.d.cts} +1 -24
  32. package/dist/{types-zHhxS7d2.d.ts → types-Cgumy1N4.d.ts} +1 -24
  33. package/dist/utils.cjs.map +1 -1
  34. package/dist/utils.d.cts +1 -1
  35. package/dist/utils.d.ts +1 -1
  36. package/dist/utils.js.map +1 -1
  37. package/package.json +8 -8
  38. package/dist/index-BZxi4104.d.ts +0 -826
  39. package/dist/index-CapkWqxy.d.ts +0 -1295
  40. package/dist/index-DX4C3zbo.d.cts +0 -826
  41. package/dist/index-r7AvpkCE.d.cts +0 -1295
package/dist/dicom.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { normalize, join } from 'path';
2
2
  import { stderr, tryCatch } from 'stderr-lib';
3
- import { copyFile, unlink, stat } from 'fs/promises';
4
- import { z } from 'zod';
5
3
  import { spawn, execSync } from 'child_process';
6
4
  import kill from 'tree-kill';
7
5
  import { existsSync } from 'fs';
6
+ import { z } from 'zod';
8
7
  import { XMLParser } from 'fast-xml-parser';
8
+ import { copyFile, unlink, stat } from 'fs/promises';
9
9
 
10
10
  var __defProp = Object.defineProperty;
11
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -30123,6 +30123,18 @@ function buildMergedModifications(base, other, erasures) {
30123
30123
  }
30124
30124
  return merged;
30125
30125
  }
30126
+ function applyBatchEntries(initial, entries) {
30127
+ const keys = Object.keys(entries);
30128
+ let cs = initial;
30129
+ for (let i = 0; i < keys.length; i++) {
30130
+ const key = keys[i];
30131
+ if (key === void 0) continue;
30132
+ const value = entries[key];
30133
+ if (value === void 0) continue;
30134
+ cs = cs.setTag(key, value);
30135
+ }
30136
+ return cs;
30137
+ }
30126
30138
  var ChangeSet = class _ChangeSet {
30127
30139
  constructor(mods, erasures) {
30128
30140
  __publicField(this, "mods");
@@ -30140,7 +30152,7 @@ var ChangeSet = class _ChangeSet {
30140
30152
  * Control characters (except LF/CR) are stripped from the value.
30141
30153
  * If the tag was previously erased, it is removed from the erasure set.
30142
30154
  *
30143
- * @param path - The DICOM tag path to set
30155
+ * @param path - The DICOM tag path to set (e.g. `'(0010,0010)'`)
30144
30156
  * @param value - The new value for the tag
30145
30157
  * @returns A new ChangeSet with the modification applied
30146
30158
  * @throws Error if operation count would exceed MAX_CHANGESET_OPERATIONS
@@ -30163,7 +30175,7 @@ var ChangeSet = class _ChangeSet {
30163
30175
  *
30164
30176
  * If the tag was previously set, the modification is removed.
30165
30177
  *
30166
- * @param path - The DICOM tag path to erase
30178
+ * @param path - The DICOM tag path to erase (e.g. `'(0010,0010)'`)
30167
30179
  * @returns A new ChangeSet with the erasure applied
30168
30180
  * @throws Error if operation count would exceed MAX_CHANGESET_OPERATIONS
30169
30181
  */
@@ -30194,6 +30206,50 @@ var ChangeSet = class _ChangeSet {
30194
30206
  newErasures.add(ERASE_PRIVATE_SENTINEL);
30195
30207
  return new _ChangeSet(new Map(this.mods), newErasures);
30196
30208
  }
30209
+ // -----------------------------------------------------------------------
30210
+ // Convenience setters for common DICOM tags
30211
+ // -----------------------------------------------------------------------
30212
+ /** Sets Patient's Name (0010,0010). */
30213
+ setPatientName(value) {
30214
+ return this.setTag("(0010,0010)", value);
30215
+ }
30216
+ /** Sets Patient ID (0010,0020). */
30217
+ setPatientID(value) {
30218
+ return this.setTag("(0010,0020)", value);
30219
+ }
30220
+ /** Sets Study Date (0008,0020). */
30221
+ setStudyDate(value) {
30222
+ return this.setTag("(0008,0020)", value);
30223
+ }
30224
+ /** Sets Modality (0008,0060). */
30225
+ setModality(value) {
30226
+ return this.setTag("(0008,0060)", value);
30227
+ }
30228
+ /** Sets Accession Number (0008,0050). */
30229
+ setAccessionNumber(value) {
30230
+ return this.setTag("(0008,0050)", value);
30231
+ }
30232
+ /** Sets Study Description (0008,1030). */
30233
+ setStudyDescription(value) {
30234
+ return this.setTag("(0008,1030)", value);
30235
+ }
30236
+ /** Sets Series Description (0008,103E). */
30237
+ setSeriesDescription(value) {
30238
+ return this.setTag("(0008,103E)", value);
30239
+ }
30240
+ /** Sets Institution Name (0008,0080). */
30241
+ setInstitutionName(value) {
30242
+ return this.setTag("(0008,0080)", value);
30243
+ }
30244
+ /**
30245
+ * Sets multiple tags at once, returning a new ChangeSet.
30246
+ *
30247
+ * @param entries - A record of tag path → value pairs
30248
+ * @returns A new ChangeSet with all modifications applied
30249
+ */
30250
+ setBatch(entries) {
30251
+ return applyBatchEntries(this, entries);
30252
+ }
30197
30253
  /** All pending tag modifications as a readonly map of path → value. */
30198
30254
  get modifications() {
30199
30255
  return this.mods;
@@ -30724,7 +30780,8 @@ var DcmodifyOptionsSchema = z.object({
30724
30780
  erasures: z.array(z.string()).optional(),
30725
30781
  erasePrivateTags: z.boolean().optional(),
30726
30782
  noBackup: z.boolean().optional(),
30727
- insertIfMissing: z.boolean().optional()
30783
+ insertIfMissing: z.boolean().optional(),
30784
+ ignoreMissingTags: z.boolean().optional()
30728
30785
  }).strict().refine((data) => data.modifications.length > 0 || data.erasures !== void 0 && data.erasures.length > 0 || data.erasePrivateTags === true, {
30729
30786
  message: "At least one of modifications, erasures, or erasePrivateTags is required"
30730
30787
  });
@@ -30733,6 +30790,9 @@ function buildArgs(inputPath, options) {
30733
30790
  if (options.noBackup !== false) {
30734
30791
  args.push("-nb");
30735
30792
  }
30793
+ if (options.ignoreMissingTags === true) {
30794
+ args.push("-imt");
30795
+ }
30736
30796
  const flag = options.insertIfMissing === true ? "-i" : "-m";
30737
30797
  const modifications = options.modifications ?? [];
30738
30798
  for (const mod of modifications) {
@@ -30752,7 +30812,7 @@ function buildArgs(inputPath, options) {
30752
30812
  async function dcmodify(inputPath, options) {
30753
30813
  const validation = DcmodifyOptionsSchema.safeParse(options);
30754
30814
  if (!validation.success) {
30755
- return err(new Error(`dcmodify: invalid options: ${validation.error.message}`));
30815
+ return err(createValidationError("dcmodify", validation.error));
30756
30816
  }
30757
30817
  const binaryResult = resolveBinary("dcmodify");
30758
30818
  if (!binaryResult.ok) {
@@ -30772,16 +30832,16 @@ async function dcmodify(inputPath, options) {
30772
30832
  }
30773
30833
  return ok({ filePath: inputPath });
30774
30834
  }
30775
-
30776
- // src/dicom/DicomFile.ts
30777
30835
  async function applyModifications(filePath, changeset, options) {
30778
30836
  const modifications = changeset.toModifications();
30779
30837
  const erasures = changeset.toErasureArgs();
30838
+ const hasErasures = erasures.length > 0 || changeset.erasePrivate;
30780
30839
  const result = await dcmodify(filePath, {
30781
30840
  modifications: modifications.length > 0 ? modifications : void 0,
30782
30841
  erasures: erasures.length > 0 ? erasures : void 0,
30783
30842
  erasePrivateTags: changeset.erasePrivate || void 0,
30784
30843
  insertIfMissing: true,
30844
+ ignoreMissingTags: hasErasures || void 0,
30785
30845
  timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
30786
30846
  signal: options.signal
30787
30847
  });
@@ -30806,24 +30866,28 @@ async function unlinkFile(path) {
30806
30866
  (e) => new Error(`Failed to delete file: ${e.message}`)
30807
30867
  );
30808
30868
  }
30809
- var DicomFile = class _DicomFile {
30810
- constructor(dataset, filePath, changes) {
30811
- /** The immutable DICOM dataset read from the file. */
30812
- __publicField(this, "dataset");
30813
- /** The branded file path. */
30814
- __publicField(this, "filePath");
30815
- /** The accumulated pending changes. */
30816
- __publicField(this, "changes");
30817
- this.dataset = dataset;
30818
- this.filePath = filePath;
30819
- this.changes = changes;
30869
+
30870
+ // src/dicom/DicomInstance.ts
30871
+ var DicomInstance = class _DicomInstance {
30872
+ constructor(dataset, changes, filePath, metadata) {
30873
+ __publicField(this, "dicomDataset");
30874
+ __publicField(this, "changeSet");
30875
+ __publicField(this, "filepath");
30876
+ __publicField(this, "meta");
30877
+ this.dicomDataset = dataset;
30878
+ this.changeSet = changes;
30879
+ this.filepath = filePath;
30880
+ this.meta = metadata;
30820
30881
  }
30882
+ // -----------------------------------------------------------------------
30883
+ // Factories
30884
+ // -----------------------------------------------------------------------
30821
30885
  /**
30822
- * Opens a DICOM file and reads its dataset.
30886
+ * Opens a DICOM file and creates a DicomInstance.
30823
30887
  *
30824
30888
  * @param path - Filesystem path to the DICOM file
30825
30889
  * @param options - Timeout and abort options
30826
- * @returns A Result containing the DicomFile or an error
30890
+ * @returns A Result containing the DicomInstance or an error
30827
30891
  */
30828
30892
  static async open(path, options) {
30829
30893
  const filePathResult = createDicomFilePath(path);
@@ -30835,64 +30899,250 @@ var DicomFile = class _DicomFile {
30835
30899
  if (!jsonResult.ok) return err(jsonResult.error);
30836
30900
  const datasetResult = DicomDataset.fromJson(jsonResult.value.data);
30837
30901
  if (!datasetResult.ok) return err(datasetResult.error);
30838
- return ok(new _DicomFile(datasetResult.value, filePathResult.value, ChangeSet.empty()));
30902
+ return ok(new _DicomInstance(datasetResult.value, ChangeSet.empty(), filePathResult.value, /* @__PURE__ */ new Map()));
30839
30903
  }
30840
30904
  /**
30841
- * Returns a new DicomFile with the given changes merged into the pending changes.
30905
+ * Creates a DicomInstance from an existing DicomDataset.
30906
+ *
30907
+ * @param dataset - The DicomDataset to wrap
30908
+ * @param filePath - Optional file path (e.g. if the dataset came from a file)
30909
+ * @returns A Result containing the DicomInstance
30910
+ */
30911
+ static fromDataset(dataset, filePath) {
30912
+ let fp;
30913
+ if (filePath !== void 0) {
30914
+ const fpResult = createDicomFilePath(filePath);
30915
+ if (!fpResult.ok) return err(fpResult.error);
30916
+ fp = fpResult.value;
30917
+ }
30918
+ return ok(new _DicomInstance(dataset, ChangeSet.empty(), fp, /* @__PURE__ */ new Map()));
30919
+ }
30920
+ // -----------------------------------------------------------------------
30921
+ // Read accessors (delegate to DicomDataset)
30922
+ // -----------------------------------------------------------------------
30923
+ /** The underlying immutable DICOM dataset. */
30924
+ get dataset() {
30925
+ return this.dicomDataset;
30926
+ }
30927
+ /** The pending change set. */
30928
+ get changes() {
30929
+ return this.changeSet;
30930
+ }
30931
+ /** Whether there are unsaved changes. */
30932
+ get hasUnsavedChanges() {
30933
+ return !this.changeSet.isEmpty;
30934
+ }
30935
+ /** The file path, or undefined if this instance has no associated file. */
30936
+ get filePath() {
30937
+ return this.filepath;
30938
+ }
30939
+ /** Patient's Name (0010,0010). */
30940
+ get patientName() {
30941
+ return this.dicomDataset.patientName;
30942
+ }
30943
+ /** Patient ID (0010,0020). */
30944
+ get patientID() {
30945
+ return this.dicomDataset.patientID;
30946
+ }
30947
+ /** Study Date (0008,0020). */
30948
+ get studyDate() {
30949
+ return this.dicomDataset.studyDate;
30950
+ }
30951
+ /** Modality (0008,0060). */
30952
+ get modality() {
30953
+ return this.dicomDataset.modality;
30954
+ }
30955
+ /** Accession Number (0008,0050). */
30956
+ get accession() {
30957
+ return this.dicomDataset.accession;
30958
+ }
30959
+ /** SOP Class UID (0008,0016). */
30960
+ get sopClassUID() {
30961
+ return this.dicomDataset.sopClassUID;
30962
+ }
30963
+ /** Study Instance UID (0020,000D). */
30964
+ get studyInstanceUID() {
30965
+ return this.dicomDataset.studyInstanceUID;
30966
+ }
30967
+ /** Series Instance UID (0020,000E). */
30968
+ get seriesInstanceUID() {
30969
+ return this.dicomDataset.seriesInstanceUID;
30970
+ }
30971
+ /** SOP Instance UID (0008,0018). */
30972
+ get sopInstanceUID() {
30973
+ return this.dicomDataset.sopInstanceUID;
30974
+ }
30975
+ /** Transfer Syntax UID (0002,0010). */
30976
+ get transferSyntaxUID() {
30977
+ return this.dicomDataset.transferSyntaxUID;
30978
+ }
30979
+ /**
30980
+ * Gets a tag value as a string with optional fallback.
30981
+ *
30982
+ * @param tag - A DICOM tag, e.g. `'(0010,0010)'` or `'00100010'`
30983
+ * @param fallback - Value to return if tag is missing (default: `''`)
30984
+ */
30985
+ getString(tag, fallback = "") {
30986
+ return this.dicomDataset.getString(tag, fallback);
30987
+ }
30988
+ /**
30989
+ * Gets a tag value as a number.
30990
+ *
30991
+ * @param tag - A DICOM tag, e.g. `'(0020,0013)'`
30992
+ */
30993
+ getNumber(tag) {
30994
+ return this.dicomDataset.getNumber(tag);
30995
+ }
30996
+ /** Checks whether a tag exists in the dataset. */
30997
+ hasTag(tag) {
30998
+ return this.dicomDataset.hasTag(tag);
30999
+ }
31000
+ /**
31001
+ * Finds all values matching a wildcard path.
31002
+ *
31003
+ * @param path - A DicomTagPath with optional wildcard indices
31004
+ */
31005
+ findValues(path) {
31006
+ return this.dicomDataset.findValues(path);
31007
+ }
31008
+ // -----------------------------------------------------------------------
31009
+ // Write methods (return new instance)
31010
+ // -----------------------------------------------------------------------
31011
+ /**
31012
+ * Sets a tag value, returning a new DicomInstance.
31013
+ *
31014
+ * @param path - The DICOM tag path (e.g. `'(0010,0010)'`)
31015
+ * @param value - The new value
31016
+ */
31017
+ setTag(path, value) {
31018
+ return new _DicomInstance(this.dicomDataset, this.changeSet.setTag(path, value), this.filepath, this.meta);
31019
+ }
31020
+ /**
31021
+ * Erases a tag, returning a new DicomInstance.
31022
+ *
31023
+ * @param path - The DICOM tag path to erase
31024
+ */
31025
+ eraseTag(path) {
31026
+ return new _DicomInstance(this.dicomDataset, this.changeSet.eraseTag(path), this.filepath, this.meta);
31027
+ }
31028
+ /** Erases all private tags, returning a new DicomInstance. */
31029
+ erasePrivateTags() {
31030
+ return new _DicomInstance(this.dicomDataset, this.changeSet.erasePrivateTags(), this.filepath, this.meta);
31031
+ }
31032
+ /** Sets Patient's Name (0010,0010). */
31033
+ setPatientName(value) {
31034
+ return this.setTag("(0010,0010)", value);
31035
+ }
31036
+ /** Sets Patient ID (0010,0020). */
31037
+ setPatientID(value) {
31038
+ return this.setTag("(0010,0020)", value);
31039
+ }
31040
+ /** Sets Study Date (0008,0020). */
31041
+ setStudyDate(value) {
31042
+ return this.setTag("(0008,0020)", value);
31043
+ }
31044
+ /** Sets Modality (0008,0060). */
31045
+ setModality(value) {
31046
+ return this.setTag("(0008,0060)", value);
31047
+ }
31048
+ /** Sets Accession Number (0008,0050). */
31049
+ setAccessionNumber(value) {
31050
+ return this.setTag("(0008,0050)", value);
31051
+ }
31052
+ /** Sets Study Description (0008,1030). */
31053
+ setStudyDescription(value) {
31054
+ return this.setTag("(0008,1030)", value);
31055
+ }
31056
+ /** Sets Series Description (0008,103E). */
31057
+ setSeriesDescription(value) {
31058
+ return this.setTag("(0008,103E)", value);
31059
+ }
31060
+ /** Sets Institution Name (0008,0080). */
31061
+ setInstitutionName(value) {
31062
+ return this.setTag("(0008,0080)", value);
31063
+ }
31064
+ /**
31065
+ * Transforms a tag value using a function.
31066
+ *
31067
+ * The function receives the current string value (or undefined if tag is missing)
31068
+ * and returns the new value. Returns a new DicomInstance with the modification.
31069
+ *
31070
+ * @param path - The DICOM tag path
31071
+ * @param fn - Transform function receiving the current value
31072
+ */
31073
+ transformTag(path, fn) {
31074
+ const current = this.dicomDataset.getString(path);
31075
+ const newValue = fn(current.length > 0 ? current : void 0);
31076
+ return this.setTag(path, newValue);
31077
+ }
31078
+ /**
31079
+ * Sets multiple tags at once, returning a new DicomInstance.
31080
+ *
31081
+ * @param entries - A record of tag path → value pairs
31082
+ */
31083
+ setBatch(entries) {
31084
+ return new _DicomInstance(this.dicomDataset, this.changeSet.setBatch(entries), this.filepath, this.meta);
31085
+ }
31086
+ /**
31087
+ * Returns a new DicomInstance with the given changes merged into pending changes.
30842
31088
  *
30843
31089
  * @param changes - A ChangeSet to merge with existing pending changes
30844
- * @returns A new DicomFile with accumulated changes
31090
+ * @returns A new DicomInstance with accumulated changes
30845
31091
  */
30846
31092
  withChanges(changes) {
30847
- return new _DicomFile(this.dataset, this.filePath, this.changes.merge(changes));
31093
+ return new _DicomInstance(this.dicomDataset, this.changeSet.merge(changes), this.filepath, this.meta);
30848
31094
  }
30849
31095
  /**
30850
- * Returns a new DicomFile with a different file path.
31096
+ * Returns a new DicomInstance pointing to a different file path.
30851
31097
  *
30852
- * Preserves the dataset and pending changes.
31098
+ * Preserves the dataset, pending changes, and metadata.
30853
31099
  *
30854
- * @param newPath - The new branded file path
30855
- * @returns A new DicomFile pointing to the new path
31100
+ * @param newPath - The new filesystem path (validated via createDicomFilePath)
31101
+ * @returns A new DicomInstance with the updated path
31102
+ * @throws If the path is invalid
30856
31103
  */
30857
31104
  withFilePath(newPath) {
30858
- return new _DicomFile(this.dataset, newPath, this.changes);
31105
+ const result = createDicomFilePath(newPath);
31106
+ if (!result.ok) throw result.error;
31107
+ return new _DicomInstance(this.dicomDataset, this.changeSet, result.value, this.meta);
30859
31108
  }
31109
+ // -----------------------------------------------------------------------
31110
+ // File I/O
31111
+ // -----------------------------------------------------------------------
30860
31112
  /**
30861
- * Applies pending changes to the file in-place using dcmodify.
31113
+ * Applies pending changes to the file in-place.
30862
31114
  *
30863
- * If there are no pending changes, this is a no-op that returns success.
30864
- * After applying, the dataset is NOT refreshed — call {@link DicomFile.open}
30865
- * again if you need fresh data.
31115
+ * Requires that the instance has an associated file path.
30866
31116
  *
30867
31117
  * @param options - Timeout and abort options
30868
- * @returns A Result indicating success or failure
30869
31118
  */
30870
31119
  async applyChanges(options) {
30871
- if (this.changes.isEmpty) return ok(void 0);
30872
- return applyModifications(this.filePath, this.changes, options ?? {});
31120
+ if (this.filepath === void 0) return err(new Error("No file path associated with this instance"));
31121
+ if (this.changeSet.isEmpty) return ok(void 0);
31122
+ return applyModifications(this.filepath, this.changeSet, options ?? {});
30873
31123
  }
30874
31124
  /**
30875
31125
  * Copies the file to a new path and applies pending changes to the copy.
30876
31126
  *
30877
- * If there are no pending changes, only the copy is performed.
30878
- * On dcmodify failure, the copy is cleaned up.
31127
+ * Returns a new DicomInstance pointing to the output path.
30879
31128
  *
30880
31129
  * @param outputPath - Destination filesystem path
30881
31130
  * @param options - Timeout and abort options
30882
- * @returns A Result containing the branded output path or an error
30883
31131
  */
30884
31132
  async writeAs(outputPath, options) {
31133
+ if (this.filepath === void 0) return err(new Error("No file path associated with this instance"));
30885
31134
  const outPathResult = createDicomFilePath(outputPath);
30886
31135
  if (!outPathResult.ok) return err(outPathResult.error);
30887
- const copyResult = await copyFileSafe(this.filePath, outputPath);
31136
+ const copyResult = await copyFileSafe(this.filepath, outputPath);
30888
31137
  if (!copyResult.ok) return err(copyResult.error);
30889
- if (this.changes.isEmpty) return ok(outPathResult.value);
30890
- const applyResult = await applyModifications(outPathResult.value, this.changes, options ?? {});
30891
- if (!applyResult.ok) {
30892
- await unlinkFile(outputPath);
30893
- return err(applyResult.error);
31138
+ if (!this.changeSet.isEmpty) {
31139
+ const applyResult = await applyModifications(outPathResult.value, this.changeSet, options ?? {});
31140
+ if (!applyResult.ok) {
31141
+ await unlinkFile(outputPath);
31142
+ return err(applyResult.error);
31143
+ }
30894
31144
  }
30895
- return ok(outPathResult.value);
31145
+ return ok(new _DicomInstance(this.dicomDataset, ChangeSet.empty(), outPathResult.value, this.meta));
30896
31146
  }
30897
31147
  /**
30898
31148
  * Gets the file size in bytes.
@@ -30900,15 +31150,43 @@ var DicomFile = class _DicomFile {
30900
31150
  * @returns A Result containing the size or an error
30901
31151
  */
30902
31152
  async fileSize() {
30903
- return statFileSize(this.filePath);
31153
+ if (this.filepath === void 0) return err(new Error("No file path associated with this instance"));
31154
+ return statFileSize(this.filepath);
30904
31155
  }
30905
31156
  /**
30906
- * Deletes the file from the filesystem.
31157
+ * Deletes the associated file from the filesystem.
30907
31158
  *
30908
31159
  * @returns A Result indicating success or failure
30909
31160
  */
30910
31161
  async unlink() {
30911
- return unlinkFile(this.filePath);
31162
+ if (this.filepath === void 0) return err(new Error("No file path associated with this instance"));
31163
+ return unlinkFile(this.filepath);
31164
+ }
31165
+ // -----------------------------------------------------------------------
31166
+ // Metadata (non-DICOM app context)
31167
+ // -----------------------------------------------------------------------
31168
+ /**
31169
+ * Returns a new DicomInstance with application metadata attached.
31170
+ *
31171
+ * Metadata is not stored in the DICOM file — it's for application context
31172
+ * (e.g. tracking source association, processing status, etc.).
31173
+ *
31174
+ * @param key - Metadata key
31175
+ * @param value - Metadata value
31176
+ */
31177
+ withMetadata(key, value) {
31178
+ const newMeta = new Map(this.meta);
31179
+ newMeta.set(key, value);
31180
+ return new _DicomInstance(this.dicomDataset, this.changeSet, this.filepath, newMeta);
31181
+ }
31182
+ /**
31183
+ * Gets application metadata by key.
31184
+ *
31185
+ * @param key - Metadata key
31186
+ * @returns The metadata value or undefined
31187
+ */
31188
+ getMetadata(key) {
31189
+ return this.meta.get(key);
30912
31190
  }
30913
31191
  };
30914
31192
 
@@ -31026,6 +31304,6 @@ function sopClassNameFromUID(uid) {
31026
31304
  return uidToNameMap.get(uid);
31027
31305
  }
31028
31306
 
31029
- export { ChangeSet, DicomDataset, DicomFile, SOP_CLASSES, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, getVRCategory, isBinaryVR, isNumericVR, isStringVR, lookupTag, lookupTagByKeyword, lookupTagByName, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, tagPathToSegments, xmlToJson };
31307
+ export { ChangeSet, DicomDataset, DicomInstance, SOP_CLASSES, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, getVRCategory, isBinaryVR, isNumericVR, isStringVR, lookupTag, lookupTagByKeyword, lookupTagByName, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, tagPathToSegments, xmlToJson };
31030
31308
  //# sourceMappingURL=dicom.js.map
31031
31309
  //# sourceMappingURL=dicom.js.map