@cosyte/dicom 0.0.16 → 0.0.18

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.mjs CHANGED
@@ -9,7 +9,7 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/version.ts
12
- var VERSION = "0.0.16";
12
+ var VERSION = "0.0.18";
13
13
 
14
14
  // src/dictionary/index.ts
15
15
  var dictionary_exports = {};
@@ -11666,23 +11666,52 @@ var FATAL_CODES = {
11666
11666
  UNSUPPORTED_TRANSFER_SYNTAX: "UNSUPPORTED_TRANSFER_SYNTAX",
11667
11667
  EMPTY_INPUT: "EMPTY_INPUT"
11668
11668
  };
11669
+ var OFFSET_FRAMES = {
11670
+ /**
11671
+ * Byte 0 is byte 0 of the buffer handed to `parseDicom`. The only frame in
11672
+ * which indexing the caller's own input by `byteOffset` is meaningful.
11673
+ */
11674
+ INPUT: "input",
11675
+ /**
11676
+ * Byte 0 is byte 0 of the inflated Data Set of a Deflated Explicit VR LE
11677
+ * object (`1.2.840.10008.1.2.1.99`). The compressed input holds no such
11678
+ * byte, so the offset does not index it at any scale.
11679
+ */
11680
+ INFLATED_DATASET: "inflated-dataset",
11681
+ /**
11682
+ * Byte 0 is byte 0 of a slice this parser cut from inside a Value Field: a
11683
+ * defined-length Sequence Item's value, or an `SQ`/`UN` value handed to a
11684
+ * descent. **Where that slice begins is deliberately not published** - see
11685
+ * this table's own note.
11686
+ */
11687
+ VALUE_SLICE: "value-slice"
11688
+ };
11669
11689
  var DicomParseError = class extends Error {
11670
11690
  code;
11671
11691
  byteOffset;
11692
+ /**
11693
+ * The coordinate system {@link DicomParseError.byteOffset} is counted in.
11694
+ * See {@link OFFSET_FRAMES}.
11695
+ */
11696
+ offsetFrame;
11672
11697
  snippet;
11673
11698
  contextPath;
11674
11699
  /**
11675
11700
  * Construct a new `DicomParseError`. All fields except `contextPath` are
11676
- * required so every thrower populates positional context per `TOL-02`.
11701
+ * required so every thrower populates positional context per `TOL-02` - and
11702
+ * `offsetFrame` is required for the same reason `byteOffset` is, because an
11703
+ * offset whose frame is optional is an offset whose frame is usually
11704
+ * missing.
11677
11705
  *
11678
11706
  * @internal
11679
11707
  */
11680
- constructor(code, message, byteOffset, snippet, contextPath) {
11681
- const formatted = `[${code}] ${message} (offset=${String(byteOffset)})` + (contextPath !== void 0 && contextPath.length > 0 ? ` \u2026 in ${contextPath.join("/")}` : "");
11708
+ constructor(code, message, byteOffset, offsetFrame, snippet, contextPath) {
11709
+ const formatted = `[${code}] ${message} (offset=${String(byteOffset)} frame=${offsetFrame})` + (contextPath !== void 0 && contextPath.length > 0 ? ` \u2026 in ${contextPath.join("/")}` : "");
11682
11710
  super(formatted);
11683
11711
  this.name = "DicomParseError";
11684
11712
  this.code = code;
11685
11713
  this.byteOffset = byteOffset;
11714
+ this.offsetFrame = offsetFrame;
11686
11715
  this.snippet = snippet;
11687
11716
  this.contextPath = contextPath;
11688
11717
  }
@@ -11708,7 +11737,12 @@ function makeEmitter(ctx) {
11708
11737
  w.code,
11709
11738
  w.message,
11710
11739
  w.position.byteOffset,
11711
- buildSnippet(ctx.buffer, w.position.byteOffset),
11740
+ // Both come off the SAME frame object, so an escalation cannot name one
11741
+ // frame and cut its bytes from another. `DicomParseWarning.position`
11742
+ // itself carries no frame - that is `PRE-EXISTING` and stated on
11743
+ // `DicomPosition`, not closed here.
11744
+ ctx.frame.name,
11745
+ buildSnippet(ctx.frame.buffer, w.position.byteOffset),
11712
11746
  w.position.contextPath
11713
11747
  );
11714
11748
  };
@@ -11834,8 +11868,11 @@ var FATAL_MESSAGES = Object.freeze({
11834
11868
  message: "File Meta is truncated."
11835
11869
  },
11836
11870
  // The declared group length is deliberately absent. It is four bytes of the
11837
- // document read as a 32-bit integer, and this code fires precisely when those
11838
- // four bytes are not what they claim to be.
11871
+ // document read as a 32-bit integer. **NOT because this code fires only when
11872
+ // those four bytes are fabricated** - an honestly truncated file raises it
11873
+ // with a perfectly correct group length, measured - but because they are four
11874
+ // bytes a sender wrote in either reading, and in the other one they are part
11875
+ // of somebody's value.
11839
11876
  FILE_META_GROUP_LENGTH_OVERRUNS: {
11840
11877
  code: FATAL_CODES.INVALID_FILE_META,
11841
11878
  message: "(0002,0000) FileMetaInformationGroupLength declares more bytes than remain in the input. The declared length and the count of bytes that remain are withheld; the byte offset locates the group."
@@ -11920,88 +11957,89 @@ function renderTransferSyntax(ts) {
11920
11957
  function renderZlibCode(code) {
11921
11958
  return code !== void 0 && ZLIB_CODES.has(code) ? code : WITHHELD;
11922
11959
  }
11923
- function build(key, byteOffset, snippet, tokens = {}) {
11960
+ function build(key, byteOffset, offsetFrame, snippet, tokens = {}) {
11924
11961
  const entry = FATAL_MESSAGES[key];
11925
11962
  const message = entry.message.replace("{vr}", renderVr(tokens.vr)).replace("{ts}", renderTransferSyntax(tokens.ts)).replace("{zlib}", renderZlibCode(tokens.zlib)).replace("{n}", String(tokens.n ?? 0));
11926
- return new DicomParseError(entry.code, message, byteOffset, snippet);
11963
+ return new DicomParseError(entry.code, message, byteOffset, offsetFrame, snippet);
11927
11964
  }
11928
- function at(key, buffer, offset, tokens = {}) {
11929
- return build(key, offset, buildSnippet(buffer, offset), tokens);
11965
+ function at(key, frame, offset, tokens = {}) {
11966
+ return build(key, offset, frame.name, buildSnippet(frame.buffer, offset), tokens);
11930
11967
  }
11931
11968
  function emptyInput() {
11932
- return build("EMPTY_INPUT", 0, "");
11969
+ return build("EMPTY_INPUT", 0, OFFSET_FRAMES.INPUT, "");
11933
11970
  }
11934
11971
  function emptyInputAfterNormalization() {
11935
- return build("EMPTY_INPUT_AFTER_NORMALIZATION", 0, "");
11972
+ return build("EMPTY_INPUT_AFTER_NORMALIZATION", 0, OFFSET_FRAMES.INPUT, "");
11936
11973
  }
11937
- function notDicomPart10Required(buffer, offset) {
11938
- return at("NOT_DICOM_PART_10_REQUIRED", buffer, offset);
11974
+ function notDicomPart10Required(frame, offset) {
11975
+ return at("NOT_DICOM_PART_10_REQUIRED", frame, offset);
11939
11976
  }
11940
- function notDicomPart10(buffer, offset) {
11941
- return at("NOT_DICOM_PART_10", buffer, offset);
11977
+ function notDicomPart10(frame, offset) {
11978
+ return at("NOT_DICOM_PART_10", frame, offset);
11942
11979
  }
11943
- function unsupportedTransferSyntax(buffer, offset, ts) {
11980
+ function unsupportedTransferSyntax(frame, offset, ts) {
11944
11981
  const name = uid(ts)?.name ?? "";
11945
11982
  return build(
11946
11983
  "UNSUPPORTED_TRANSFER_SYNTAX",
11947
11984
  offset,
11948
- name.length > 0 ? name : buildSnippet(buffer, offset),
11985
+ frame.name,
11986
+ name.length > 0 ? name : buildSnippet(frame.buffer, offset),
11949
11987
  { ts }
11950
11988
  );
11951
11989
  }
11952
- function fileMetaTruncated(buffer, offset) {
11953
- return at("FILE_META_TRUNCATED", buffer, offset);
11990
+ function fileMetaTruncated(frame, offset) {
11991
+ return at("FILE_META_TRUNCATED", frame, offset);
11954
11992
  }
11955
- function fileMetaGroupLengthOverruns(buffer, offset) {
11956
- return at("FILE_META_GROUP_LENGTH_OVERRUNS", buffer, offset);
11993
+ function fileMetaGroupLengthOverruns(frame, offset) {
11994
+ return at("FILE_META_GROUP_LENGTH_OVERRUNS", frame, offset);
11957
11995
  }
11958
- function fileMetaTransferSyntaxMissing(buffer, offset) {
11959
- return at("FILE_META_TRANSFER_SYNTAX_MISSING", buffer, offset);
11996
+ function fileMetaTransferSyntaxMissing(frame, offset) {
11997
+ return at("FILE_META_TRANSFER_SYNTAX_MISSING", frame, offset);
11960
11998
  }
11961
- function truncatedDatasetHeader(buffer, offset) {
11962
- return at("TRUNCATED_DATASET_HEADER", buffer, offset);
11999
+ function truncatedDatasetHeader(frame, offset) {
12000
+ return at("TRUNCATED_DATASET_HEADER", frame, offset);
11963
12001
  }
11964
- function truncatedFffeHeader(buffer, offset) {
11965
- return at("TRUNCATED_FFFE_HEADER", buffer, offset);
12002
+ function truncatedFffeHeader(frame, offset) {
12003
+ return at("TRUNCATED_FFFE_HEADER", frame, offset);
11966
12004
  }
11967
- function truncatedExplicitVrHeader(buffer, offset) {
11968
- return at("TRUNCATED_EXPLICIT_VR_HEADER", buffer, offset);
12005
+ function truncatedExplicitVrHeader(frame, offset) {
12006
+ return at("TRUNCATED_EXPLICIT_VR_HEADER", frame, offset);
11969
12007
  }
11970
- function sqItemHeaderTruncated(buffer, offset) {
11971
- return at("SQ_ITEM_HEADER_TRUNCATED", buffer, offset);
12008
+ function sqItemHeaderTruncated(frame, offset) {
12009
+ return at("SQ_ITEM_HEADER_TRUNCATED", frame, offset);
11972
12010
  }
11973
- function unexpectedFffeAtDatasetLevel(buffer, offset) {
11974
- return at("UNEXPECTED_FFFE_AT_DATASET_LEVEL", buffer, offset);
12011
+ function unexpectedFffeAtDatasetLevel(frame, offset) {
12012
+ return at("UNEXPECTED_FFFE_AT_DATASET_LEVEL", frame, offset);
11975
12013
  }
11976
- function unexpectedFffeAtDatasetRoot(buffer, offset) {
11977
- return at("UNEXPECTED_FFFE_AT_DATASET_ROOT", buffer, offset);
12014
+ function unexpectedFffeAtDatasetRoot(frame, offset) {
12015
+ return at("UNEXPECTED_FFFE_AT_DATASET_ROOT", frame, offset);
11978
12016
  }
11979
- function undefinedLengthOnNonSqExplicit(buffer, offset, vr) {
11980
- return at("UNDEFINED_LENGTH_ON_NON_SQ_EXPLICIT", buffer, offset, { vr });
12017
+ function undefinedLengthOnNonSqExplicit(frame, offset, vr) {
12018
+ return at("UNDEFINED_LENGTH_ON_NON_SQ_EXPLICIT", frame, offset, { vr });
11981
12019
  }
11982
- function undefinedLengthOnNonSqImplicit(buffer, offset, vr) {
11983
- return at("UNDEFINED_LENGTH_ON_NON_SQ_IMPLICIT", buffer, offset, { vr });
12020
+ function undefinedLengthOnNonSqImplicit(frame, offset, vr) {
12021
+ return at("UNDEFINED_LENGTH_ON_NON_SQ_IMPLICIT", frame, offset, { vr });
11984
12022
  }
11985
- function elementLengthExceedsBuffer(buffer, offset) {
11986
- return at("ELEMENT_LENGTH_EXCEEDS_BUFFER", buffer, offset);
12023
+ function elementLengthExceedsBuffer(frame, offset) {
12024
+ return at("ELEMENT_LENGTH_EXCEEDS_BUFFER", frame, offset);
11987
12025
  }
11988
- function unexpectedTagInsideSequence(buffer, offset) {
11989
- return at("UNEXPECTED_TAG_INSIDE_SEQUENCE", buffer, offset);
12026
+ function unexpectedTagInsideSequence(frame, offset) {
12027
+ return at("UNEXPECTED_TAG_INSIDE_SEQUENCE", frame, offset);
11990
12028
  }
11991
- function encapsulatedFragmentExceedsBuffer(buffer, offset) {
11992
- return at("ENCAPSULATED_FRAGMENT_EXCEEDS_BUFFER", buffer, offset);
12029
+ function encapsulatedFragmentExceedsBuffer(frame, offset) {
12030
+ return at("ENCAPSULATED_FRAGMENT_EXCEEDS_BUFFER", frame, offset);
11993
12031
  }
11994
- function itemLengthExceedsBuffer(buffer, offset) {
11995
- return at("ITEM_LENGTH_EXCEEDS_BUFFER", buffer, offset);
12032
+ function itemLengthExceedsBuffer(frame, offset) {
12033
+ return at("ITEM_LENGTH_EXCEEDS_BUFFER", frame, offset);
11996
12034
  }
11997
- function sqNestingDepthExceeded(buffer, offset, limit) {
11998
- return at("SQ_NESTING_DEPTH_EXCEEDED", buffer, offset, { n: limit });
12035
+ function sqNestingDepthExceeded(frame, offset, limit) {
12036
+ return at("SQ_NESTING_DEPTH_EXCEEDED", frame, offset, { n: limit });
11999
12037
  }
12000
- function inflatedPayloadExceedsCap(buffer, offset, cap) {
12001
- return at("INFLATED_PAYLOAD_EXCEEDS_CAP", buffer, offset, { n: cap });
12038
+ function inflatedPayloadExceedsCap(frame, offset, cap) {
12039
+ return at("INFLATED_PAYLOAD_EXCEEDS_CAP", frame, offset, { n: cap });
12002
12040
  }
12003
- function inflateFailed(buffer, offset, code) {
12004
- return at("INFLATE_FAILED", buffer, offset, ...code !== void 0 ? [{ zlib: code }] : []);
12041
+ function inflateFailed(frame, offset, code) {
12042
+ return at("INFLATE_FAILED", frame, offset, ...code !== void 0 ? [{ zlib: code }] : []);
12005
12043
  }
12006
12044
  function copyValueBytes(src) {
12007
12045
  const out = Buffer.allocUnsafeSlow(src.length);
@@ -12197,6 +12235,8 @@ var WARNING_CODES = {
12197
12235
  // emitted by deidentify(), never by the parser
12198
12236
  DICOM_DEIDENT_METHOD_PRIOR_RETAINED: "DICOM_DEIDENT_METHOD_PRIOR_RETAINED",
12199
12237
  // emitted by deidentify(), never by the parser
12238
+ DICOM_DEIDENT_METHOD_VALUE_OVER_LENGTH: "DICOM_DEIDENT_METHOD_VALUE_OVER_LENGTH",
12239
+ // emitted by deidentify(), never by the parser
12200
12240
  DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE: "DICOM_DEIDENT_SEQUENCE_NOT_AUDITABLE",
12201
12241
  // emitted by deidentify(), never by the parser
12202
12242
  DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE: "DICOM_DEIDENT_UNDEFINED_VR_NOT_AUDITABLE",
@@ -12275,6 +12315,20 @@ var WARNING_MESSAGES = Object.freeze({
12275
12315
  // No value and no length, for the same reason as the code above: the retained
12276
12316
  // text is the file's own. The tag is a constant of this code.
12277
12317
  DICOM_DEIDENT_METHOD_PRIOR_RETAINED: "A De-identification Method (0012,0063) value the source file already carried was kept beside the one this run recorded, as PS3.15 E.1.1 requires. That attribute is not in Table E.1-1, so no rule in this run inspected, audited or redacted those bytes: if the sender wrote identifying text there it is in the de-identified output, under (0012,0062) = YES. The text is withheld from this message.",
12318
+ // No value, no length, no count AND NO ORIGIN. 64 is a constant of the VR, not
12319
+ // a measurement over input; the offending Value's own length IS a measurement
12320
+ // over input and is the number DICOM-DIAGNOSTIC-PHI-RESIDUALS bound out of six
12321
+ // other messages. How many Values are over is the same kind of number, and
12322
+ // which one of them the caller supplied versus which the file did is not
12323
+ // decidable when the two are equal.
12324
+ //
12325
+ // 🛑 AND IT SAYS "LONGER THAN 64 BYTES", NOT "OVER THE MAXIMUM". A graded pass
12326
+ // refuted the draft that called 64 bytes "the largest an LO Value may be under
12327
+ // a single-byte repertoire": PS3.5 6.2 specifies these lengths "in characters
12328
+ // rather than bytes" and excludes Code Extension escape sequences from the
12329
+ // count, so a conformant 64-character Value can exceed 64 bytes under a
12330
+ // single-byte repertoire too. The message states what was measured.
12331
+ DICOM_DEIDENT_METHOD_VALUE_OVER_LENGTH: "The De-identification Method (0012,0063) this run wrote carries a Value longer than 64 bytes. PS3.5 Table 6.2-1 bounds an LO Value at 64 characters, per Value and not per Value Field, and PS3.5 6.2 specifies that length in characters rather than bytes and excludes Code Extension escape sequences from the count: a Value of 64 bytes or fewer can never carry more than 64 characters, but a longer one is not necessarily over the maximum. This run never shortens, splits or truncates a Value it did not compose: a caller-supplied method, and a value the source file already carried, are both written through as given. A receiver that enforces the VR may reject the attribute that records the de-identification. The value, its length, how many Values are over and which of them came from where are all withheld from this message.",
12278
12332
  // BOTH of the disagreeing length fields are deliberately absent, and the
12279
12333
  // second one is the eighth instance of `DICOM-DIAGNOSTIC-PHI-RESIDUALS`: the
12280
12334
  // remaining-bytes count this string used to carry was the enclosing sequence's
@@ -12403,6 +12457,9 @@ function deidentMethodNotLo(position) {
12403
12457
  function deidentMethodPriorRetained(position) {
12404
12458
  return build2(WARNING_CODES.DICOM_DEIDENT_METHOD_PRIOR_RETAINED, position);
12405
12459
  }
12460
+ function deidentMethodValueOverLength(position) {
12461
+ return build2(WARNING_CODES.DICOM_DEIDENT_METHOD_VALUE_OVER_LENGTH, position);
12462
+ }
12406
12463
  function embeddedAttributeRemoved(position, tag, vr, count) {
12407
12464
  return build2(WARNING_CODES.DICOM_DEIDENT_EMBEDDED_ATTRIBUTE_REMOVED, position, {
12408
12465
  tag,
@@ -12615,7 +12672,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
12615
12672
  firstElement = readExplicitLeElement(cursor);
12616
12673
  } catch (err) {
12617
12674
  if (err instanceof RangeError) {
12618
- throw fileMetaTruncated(buffer, fmStart);
12675
+ throw fileMetaTruncated(ctx.frame, fmStart);
12619
12676
  }
12620
12677
  throw err;
12621
12678
  }
@@ -12625,7 +12682,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
12625
12682
  if (firstElement.tag === "00020000" && firstElement.vr === "UL" && firstElement.value.length === 4) {
12626
12683
  declaredFmLength = firstElement.value.readUInt32LE(0);
12627
12684
  if (cursor.position + declaredFmLength > buffer.length) {
12628
- throw fileMetaGroupLengthOverruns(buffer, fmStart);
12685
+ throw fileMetaGroupLengthOverruns(ctx.frame, fmStart);
12629
12686
  }
12630
12687
  } else {
12631
12688
  emit(fileMetaGroupLengthMissing({ byteOffset: fmStart, fileMeta: true }));
@@ -12642,7 +12699,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
12642
12699
  next = readExplicitLeElement(cursor);
12643
12700
  } catch (err) {
12644
12701
  if (err instanceof RangeError) {
12645
- throw fileMetaTruncated(buffer, fmStart);
12702
+ throw fileMetaTruncated(ctx.frame, fmStart);
12646
12703
  }
12647
12704
  throw err;
12648
12705
  }
@@ -12666,7 +12723,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
12666
12723
  next = readExplicitLeElement(cursor);
12667
12724
  } catch (err) {
12668
12725
  if (err instanceof RangeError) {
12669
- throw fileMetaTruncated(buffer, fmStart);
12726
+ throw fileMetaTruncated(ctx.frame, fmStart);
12670
12727
  }
12671
12728
  throw err;
12672
12729
  }
@@ -12685,7 +12742,7 @@ function parseFileMeta(buffer, datasetStart, ctx, emit) {
12685
12742
  }
12686
12743
  const tsElement = fmElements.find((e) => e.tag === "00020010");
12687
12744
  if (tsElement === void 0 || tsElement.vr !== "UI") {
12688
- throw fileMetaTransferSyntaxMissing(buffer, fmStart);
12745
+ throw fileMetaTransferSyntaxMissing(ctx.frame, fmStart);
12689
12746
  }
12690
12747
  const extraElements = fmElements.filter((e) => !MODELED_FM_TAGS.has(e.tag)).map((e) => Object.freeze({ tag: e.tag, vr: e.vr, value: Buffer.from(e.value) }));
12691
12748
  const fileMeta = {
@@ -12769,13 +12826,13 @@ function parsePart10Header(buffer, ctx, emit) {
12769
12826
  return { datasetStart: 132, hadPreamble: true };
12770
12827
  }
12771
12828
  if (ctx.stripPreamble === "require") {
12772
- throw notDicomPart10Required(buffer, 0);
12829
+ throw notDicomPart10Required(ctx.frame, 0);
12773
12830
  }
12774
12831
  if (looksLikeFileMetaGroupLengthAtOffsetZero(buffer)) {
12775
12832
  emit(missingPreamble({ byteOffset: 0 }));
12776
12833
  return { datasetStart: 0, hadPreamble: false };
12777
12834
  }
12778
- throw notDicomPart10(buffer, 0);
12835
+ throw notDicomPart10(ctx.frame, 0);
12779
12836
  }
12780
12837
  function looksLikeFileMetaGroupLengthAtOffsetZero(buffer) {
12781
12838
  if (buffer.length < 12) return false;
@@ -13186,7 +13243,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
13186
13243
  ctx.nestingDepth += 1;
13187
13244
  if (ctx.nestingDepth > NESTING_DEPTH_LIMIT) {
13188
13245
  ctx.nestingDepth -= 1;
13189
- throw sqNestingDepthExceeded(buffer, valueStart, NESTING_DEPTH_LIMIT);
13246
+ throw sqNestingDepthExceeded(ctx.frame, valueStart, NESTING_DEPTH_LIMIT);
13190
13247
  }
13191
13248
  ctx.encodingContextStack.push(
13192
13249
  opts.encapsulatedPixelData === true ? "EncapsulatedPixelData" : "SqItem"
@@ -13222,7 +13279,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
13222
13279
  itemLength = cursor.readUInt32();
13223
13280
  } catch (err) {
13224
13281
  if (err instanceof RangeError) {
13225
- throw sqItemHeaderTruncated(buffer, itemHeaderStart);
13282
+ throw sqItemHeaderTruncated(ctx.frame, itemHeaderStart);
13226
13283
  }
13227
13284
  throw err;
13228
13285
  }
@@ -13231,7 +13288,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
13231
13288
  return { items, endOffset: cursor.position };
13232
13289
  }
13233
13290
  if (itemTag !== ITEM_TAG) {
13234
- throw unexpectedTagInsideSequence(buffer, itemHeaderStart);
13291
+ throw unexpectedTagInsideSequence(ctx.frame, itemHeaderStart);
13235
13292
  }
13236
13293
  if (itemLength === 0) {
13237
13294
  emit(emptyItemInSequence({ byteOffset: itemHeaderStart }, itemTag));
@@ -13247,7 +13304,7 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
13247
13304
  }
13248
13305
  if (opts.encapsulatedPixelData === true) {
13249
13306
  if (cursor.position + itemLength > buffer.length) {
13250
- throw encapsulatedFragmentExceedsBuffer(buffer, itemHeaderStart);
13307
+ throw encapsulatedFragmentExceedsBuffer(ctx.frame, itemHeaderStart);
13251
13308
  }
13252
13309
  cursor.position += itemLength;
13253
13310
  items.push(new Item({ warnings: [], elements: /* @__PURE__ */ new Map(), index: itemIndex }));
@@ -13267,16 +13324,16 @@ function parseSequence(buffer, valueStart, ctx, emit, opts) {
13267
13324
  emit(itemCrossesSequenceEnd({ byteOffset: itemHeaderStart }, itemTag));
13268
13325
  }
13269
13326
  if (cursor.position + itemLength > buffer.length) {
13270
- throw itemLengthExceedsBuffer(buffer, itemHeaderStart);
13327
+ throw itemLengthExceedsBuffer(ctx.frame, itemHeaderStart);
13271
13328
  }
13272
13329
  const itemSlice = buffer.subarray(cursor.position, cursor.position + itemLength);
13273
- const enclosingFrame = ctx.buffer;
13330
+ const enclosingFrame = ctx.frame;
13274
13331
  let inner;
13275
13332
  try {
13276
- ctx.buffer = itemSlice;
13333
+ ctx.frame = { buffer: itemSlice, name: OFFSET_FRAMES.VALUE_SLICE };
13277
13334
  inner = opts.innerStrategy(itemSlice, 0, ctx, emit);
13278
13335
  } finally {
13279
- ctx.buffer = enclosingFrame;
13336
+ ctx.frame = enclosingFrame;
13280
13337
  }
13281
13338
  items.push(new Item({ warnings: [], elements: inner.elements, index: itemIndex }));
13282
13339
  cursor.position += itemLength;
@@ -13301,17 +13358,17 @@ function tryParseDefinedLengthSQ(buffer, valueStart, valueLength, ctx, emit, imp
13301
13358
  const savedWarningsLen = ctx.warnings.length;
13302
13359
  try {
13303
13360
  const slice = buffer.subarray(valueStart, valueStart + valueLength);
13304
- const enclosingFrame = ctx.buffer;
13361
+ const enclosingFrame = ctx.frame;
13305
13362
  let result;
13306
13363
  try {
13307
- ctx.buffer = slice;
13364
+ ctx.frame = { buffer: slice, name: OFFSET_FRAMES.VALUE_SLICE };
13308
13365
  result = parseSequence(slice, 0, ctx, emit, {
13309
13366
  explicitLength: valueLength,
13310
13367
  littleEndian: true,
13311
13368
  innerStrategy: implicitLeInner
13312
13369
  });
13313
13370
  } finally {
13314
- ctx.buffer = enclosingFrame;
13371
+ ctx.frame = enclosingFrame;
13315
13372
  }
13316
13373
  return { success: true, items: result.items };
13317
13374
  } catch (err) {
@@ -13339,13 +13396,13 @@ function tryParseUnAsSQ(buffer, valueStart, valueLength, ctx, emit, implicitLeIn
13339
13396
  // CP-246: Implicit VR LE inner per D-30.
13340
13397
  innerStrategy: implicitLeInner
13341
13398
  };
13342
- const enclosingFrame = ctx.buffer;
13399
+ const enclosingFrame = ctx.frame;
13343
13400
  let result;
13344
13401
  try {
13345
- ctx.buffer = slice;
13402
+ ctx.frame = { buffer: slice, name: OFFSET_FRAMES.VALUE_SLICE };
13346
13403
  result = parseSequence(slice, 0, ctx, emit, opts);
13347
13404
  } finally {
13348
- ctx.buffer = enclosingFrame;
13405
+ ctx.frame = enclosingFrame;
13349
13406
  }
13350
13407
  emit(unParsedAsSQ({ byteOffset: valueStart }, tag));
13351
13408
  return {
@@ -13383,7 +13440,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
13383
13440
  length = cursor.readUInt32();
13384
13441
  } catch (err) {
13385
13442
  if (err instanceof RangeError) {
13386
- throw truncatedDatasetHeader(buffer, headerStart);
13443
+ throw truncatedDatasetHeader(ctx.frame, headerStart);
13387
13444
  }
13388
13445
  throw err;
13389
13446
  }
@@ -13392,7 +13449,7 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
13392
13449
  if (opts.stopOnItemDelim === true && tag === "FFFEE00D") {
13393
13450
  return { elements, endOffset: cursor.position };
13394
13451
  }
13395
- throw unexpectedFffeAtDatasetRoot(buffer, headerStart);
13452
+ throw unexpectedFffeAtDatasetRoot(ctx.frame, headerStart);
13396
13453
  }
13397
13454
  const position = { byteOffset: headerStart };
13398
13455
  const vr = resolveImplicitVR(tag, ctx, emit, position);
@@ -13459,10 +13516,10 @@ function parseImplicitLE(buffer, datasetStart, ctx, emit, opts = {}) {
13459
13516
  continue;
13460
13517
  }
13461
13518
  }
13462
- throw undefinedLengthOnNonSqImplicit(buffer, headerStart, vr);
13519
+ throw undefinedLengthOnNonSqImplicit(ctx.frame, headerStart, vr);
13463
13520
  }
13464
13521
  if (cursor.position + length > buffer.length) {
13465
- throw elementLengthExceedsBuffer(buffer, headerStart);
13522
+ throw elementLengthExceedsBuffer(ctx.frame, headerStart);
13466
13523
  }
13467
13524
  const valueStart = cursor.position;
13468
13525
  const valueEnd = valueStart + length;
@@ -13538,7 +13595,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
13538
13595
  peekGroup = cursor.readUInt16At(cursor.position);
13539
13596
  } catch (err) {
13540
13597
  if (err instanceof RangeError) {
13541
- throw truncatedDatasetHeader(buffer, headerStart);
13598
+ throw truncatedDatasetHeader(ctx.frame, headerStart);
13542
13599
  }
13543
13600
  throw err;
13544
13601
  }
@@ -13551,7 +13608,7 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
13551
13608
  cursor.readUInt32();
13552
13609
  } catch (err) {
13553
13610
  if (err instanceof RangeError) {
13554
- throw truncatedFffeHeader(buffer, headerStart);
13611
+ throw truncatedFffeHeader(ctx.frame, headerStart);
13555
13612
  }
13556
13613
  throw err;
13557
13614
  }
@@ -13559,14 +13616,14 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
13559
13616
  if (opts.stopOnItemDelim === true && fffeTag === "FFFEE00D") {
13560
13617
  return { elements, endOffset: cursor.position };
13561
13618
  }
13562
- throw unexpectedFffeAtDatasetLevel(buffer, headerStart);
13619
+ throw unexpectedFffeAtDatasetLevel(ctx.frame, headerStart);
13563
13620
  }
13564
13621
  let header;
13565
13622
  try {
13566
13623
  header = readExplicitElementHeader(cursor, ctx, emit);
13567
13624
  } catch (err) {
13568
13625
  if (err instanceof RangeError) {
13569
- throw truncatedExplicitVrHeader(buffer, headerStart);
13626
+ throw truncatedExplicitVrHeader(ctx.frame, headerStart);
13570
13627
  }
13571
13628
  throw err;
13572
13629
  }
@@ -13706,10 +13763,10 @@ function _parseExplicit(buffer, datasetStart, ctx, emit, mode, opts = {}) {
13706
13763
  continue;
13707
13764
  }
13708
13765
  if (length === UNDEFINED_LENGTH2) {
13709
- throw undefinedLengthOnNonSqExplicit(buffer, headerStart, vr);
13766
+ throw undefinedLengthOnNonSqExplicit(ctx.frame, headerStart, vr);
13710
13767
  }
13711
13768
  if (cursor.position + length > buffer.length) {
13712
- throw elementLengthExceedsBuffer(buffer, headerStart);
13769
+ throw elementLengthExceedsBuffer(ctx.frame, headerStart);
13713
13770
  }
13714
13771
  const valueStart = cursor.position;
13715
13772
  const valueEnd = valueStart + length;
@@ -13770,11 +13827,14 @@ function parseDeflatedLEWithCap(buffer, datasetStart, ctx, emit, maxInflatedByte
13770
13827
  const code = errAny.code;
13771
13828
  const message = errAny.message ?? String(err);
13772
13829
  if (code === "ERR_BUFFER_TOO_LARGE" || err instanceof RangeError && /maxOutputLength|too large/i.test(message)) {
13773
- throw inflatedPayloadExceedsCap(buffer, datasetStart, maxInflatedBytes);
13830
+ throw inflatedPayloadExceedsCap(ctx.frame, datasetStart, maxInflatedBytes);
13774
13831
  }
13775
- throw inflateFailed(buffer, datasetStart, code);
13832
+ throw inflateFailed(ctx.frame, datasetStart, code);
13776
13833
  }
13777
- const innerCtx = { ...ctx, buffer: inflated };
13834
+ const innerCtx = {
13835
+ ...ctx,
13836
+ frame: { buffer: inflated, name: OFFSET_FRAMES.INFLATED_DATASET }
13837
+ };
13778
13838
  const innerChokepoint = makeEmitter(innerCtx);
13779
13839
  const innerEmit = (w) => {
13780
13840
  const wrapped = {
@@ -13824,7 +13884,7 @@ function parseDicom(input, options = {}) {
13824
13884
  const tsUid = fileMeta.transferSyntaxUID;
13825
13885
  const strategy = TRANSFER_SYNTAX_PARSERS[tsUid];
13826
13886
  if (strategy === void 0) {
13827
- throw unsupportedTransferSyntax(buffer, fileMetaEnd, tsUid);
13887
+ throw unsupportedTransferSyntax(ctx.frame, fileMetaEnd, tsUid);
13828
13888
  }
13829
13889
  const { elements } = strategy(buffer, fileMetaEnd, ctx, emit);
13830
13890
  return new Dataset({ fileMeta, warnings: ctx.warnings, elements });
@@ -13842,7 +13902,9 @@ function normalizeInput(input) {
13842
13902
  }
13843
13903
  function buildContext(buffer, options, warnings) {
13844
13904
  const base = {
13845
- buffer,
13905
+ // The root frame. Every nested frame is entered from here by a swap that
13906
+ // moves the bytes and the name together; see `ParseFrame`.
13907
+ frame: { buffer, name: OFFSET_FRAMES.INPUT },
13846
13908
  strict: options.strict === true,
13847
13909
  stripPreamble: options.stripPreamble ?? "tolerate",
13848
13910
  warnings,
@@ -15749,6 +15811,10 @@ function defaultMethod(active) {
15749
15811
  }
15750
15812
  var VALUE_DELIMITER = 92;
15751
15813
  var MAX_SHORT_FORM_VALUE_BYTES = 65534;
15814
+ var LO_VALUE_MAX_CHARS = 64;
15815
+ function hasValueOverLoMaximum(field) {
15816
+ return splitValues(field).some((value) => value.length > LO_VALUE_MAX_CHARS);
15817
+ }
15752
15818
  function addDeidentificationMethod(existing, method) {
15753
15819
  const added = trimTrailingPad(Buffer.from(method, "latin1"));
15754
15820
  const none = { replacedPrior: false, retainedPrior: false, replacedNonLoPrior: false };
@@ -15846,6 +15912,11 @@ function deidentify(ds, options = {}) {
15846
15912
  deidentMethodNotLo({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
15847
15913
  );
15848
15914
  }
15915
+ if (hasValueOverLoMaximum(deidentMethod.value)) {
15916
+ warnings.push(
15917
+ deidentMethodValueOverLength({ byteOffset: priorMethod?.byteOffset ?? 0, fileMeta: false })
15918
+ );
15919
+ }
15849
15920
  if (hasUncleanedBurnedIn(ds)) {
15850
15921
  const offset = ds.get(TAG_PIXEL_DATA)?.byteOffset ?? 0;
15851
15922
  warnings.push(burnedInAnnotationNotRemoved({ byteOffset: offset, fileMeta: false }));
@@ -15870,6 +15941,6 @@ function deidentify(ds, options = {}) {
15870
15941
  return { dataset, report };
15871
15942
  }
15872
15943
 
15873
- export { CODING_SCHEME_OIDS, DEFAULT_UID_ROOT, DEIDENTIFY_ERROR_CODES, DEIDENTIFY_OPTIONS, Dataset, DeidentifyError, DicomParseError, DicomSerializeError, DicomValueError, dictionary_exports as Dictionary, Element, FATAL_CODES, Item, ProfileDefinitionError, SERIALIZE_ERROR_CODES, Sequence, VALUE_ERROR_CODES, VERSION, WARNING_CODES, codingSchemeOid, decodeElementValue, decodeText, defineProfile, deidentify, isKnownCharsetTerm, makeUidRemapper, parseDate, parseDateTime, parseDicom, parsePersonName, parseSpecificCharacterSet, parseTime, profiles, readCode, resolveDecoderLabel, serializeDicom };
15944
+ export { CODING_SCHEME_OIDS, DEFAULT_UID_ROOT, DEIDENTIFY_ERROR_CODES, DEIDENTIFY_OPTIONS, Dataset, DeidentifyError, DicomParseError, DicomSerializeError, DicomValueError, dictionary_exports as Dictionary, Element, FATAL_CODES, Item, OFFSET_FRAMES, ProfileDefinitionError, SERIALIZE_ERROR_CODES, Sequence, VALUE_ERROR_CODES, VERSION, WARNING_CODES, codingSchemeOid, decodeElementValue, decodeText, defineProfile, deidentify, isKnownCharsetTerm, makeUidRemapper, parseDate, parseDateTime, parseDicom, parsePersonName, parseSpecificCharacterSet, parseTime, profiles, readCode, resolveDecoderLabel, serializeDicom };
15874
15945
  //# sourceMappingURL=index.mjs.map
15875
15946
  //# sourceMappingURL=index.mjs.map