@ubercode/dcmtk 1.0.0-rc.1 → 1.0.0-rc.2
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/{DicomInstance-DGWB2mfD.d.ts → DicomInstance-BFFzieOp.d.ts} +8 -2
- package/dist/{DicomInstance-BGXtON9T.d.cts → DicomInstance-C0ntZyxC.d.cts} +8 -2
- package/dist/dicom.cjs +556 -21
- package/dist/dicom.cjs.map +1 -1
- package/dist/dicom.d.cts +2 -2
- package/dist/dicom.d.ts +2 -2
- package/dist/dicom.js +556 -22
- package/dist/dicom.js.map +1 -1
- package/dist/{index-D1msvaAt.d.cts → index-CCHr7MUH.d.cts} +1 -1
- package/dist/{index-C6dWOhHS.d.ts → index-DxjEslJ4.d.ts} +1 -1
- package/dist/index.cjs +574 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +573 -28
- package/dist/index.js.map +1 -1
- package/dist/servers.cjs +29979 -21
- package/dist/servers.cjs.map +1 -1
- package/dist/servers.d.cts +2 -2
- package/dist/servers.d.ts +2 -2
- package/dist/servers.js +29979 -22
- package/dist/servers.js.map +1 -1
- package/dist/tools.cjs +29979 -14
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +79 -2
- package/dist/tools.d.ts +79 -2
- package/dist/tools.js +29978 -16
- package/dist/tools.js.map +1 -1
- package/package.json +3 -1
package/dist/dicom.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DicomTag, a as DicomTagPath } from './DicomInstance-
|
|
2
|
-
export { C as ChangeSet, b as DicomDataset, d as DicomInstance, e as DicomOpenOptions } from './DicomInstance-
|
|
1
|
+
import { D as DicomTag, a as DicomTagPath } from './DicomInstance-C0ntZyxC.cjs';
|
|
2
|
+
export { C as ChangeSet, b as DicomDataset, d as DicomInstance, e as DicomOpenOptions } from './DicomInstance-C0ntZyxC.cjs';
|
|
3
3
|
export { x as xmlToJson } from './dcmodify-Cf-RPHF3.cjs';
|
|
4
4
|
import './types-Cgumy1N4.cjs';
|
|
5
5
|
|
package/dist/dicom.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DicomTag, a as DicomTagPath } from './DicomInstance-
|
|
2
|
-
export { C as ChangeSet, b as DicomDataset, d as DicomInstance, e as DicomOpenOptions } from './DicomInstance-
|
|
1
|
+
import { D as DicomTag, a as DicomTagPath } from './DicomInstance-BFFzieOp.js';
|
|
2
|
+
export { C as ChangeSet, b as DicomDataset, d as DicomInstance, e as DicomOpenOptions } from './DicomInstance-BFFzieOp.js';
|
|
3
3
|
export { x as xmlToJson } from './dcmodify-CHvwChFu.js';
|
|
4
4
|
import './types-Cgumy1N4.js';
|
|
5
5
|
|
package/dist/dicom.js
CHANGED
|
@@ -3,10 +3,12 @@ import { stderr, tryCatch } from 'stderr-lib';
|
|
|
3
3
|
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import kill from 'tree-kill';
|
|
5
5
|
import { existsSync } from 'fs';
|
|
6
|
-
import { copyFile, unlink, stat, rm, mkdtemp } from 'fs/promises';
|
|
6
|
+
import { copyFile, unlink, stat, rm, mkdtemp, readFile } from 'fs/promises';
|
|
7
7
|
import { tmpdir } from 'os';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { XMLParser } from 'fast-xml-parser';
|
|
10
|
+
import { inflateRawSync } from 'zlib';
|
|
11
|
+
import dicomParser from 'dicom-parser';
|
|
10
12
|
|
|
11
13
|
var __defProp = Object.defineProperty;
|
|
12
14
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -30386,9 +30388,9 @@ var ToolExecutionError = class extends Error {
|
|
|
30386
30388
|
this.exitCode = details.exitCode;
|
|
30387
30389
|
}
|
|
30388
30390
|
};
|
|
30389
|
-
function createToolError(toolName, args, exitCode,
|
|
30391
|
+
function createToolError(toolName, args, exitCode, stderr5, stdout = "") {
|
|
30390
30392
|
const argsStr = truncate(args.join(" "), MAX_ARGS_LENGTH);
|
|
30391
|
-
const stderrStr = truncate(
|
|
30393
|
+
const stderrStr = truncate(stderr5.trim(), MAX_STDERR_LENGTH);
|
|
30392
30394
|
const parts = [`${toolName} failed (exit code ${String(exitCode)})`];
|
|
30393
30395
|
if (argsStr.length > 0) {
|
|
30394
30396
|
parts.push(`args: ${argsStr}`);
|
|
@@ -30396,7 +30398,7 @@ function createToolError(toolName, args, exitCode, stderr4, stdout = "") {
|
|
|
30396
30398
|
if (stderrStr.length > 0) {
|
|
30397
30399
|
parts.push(`stderr: ${stderrStr}`);
|
|
30398
30400
|
}
|
|
30399
|
-
return new ToolExecutionError(parts.join(" | "), { stdout, stderr:
|
|
30401
|
+
return new ToolExecutionError(parts.join(" | "), { stdout, stderr: stderr5, exitCode });
|
|
30400
30402
|
}
|
|
30401
30403
|
function createValidationError(toolName, zodError) {
|
|
30402
30404
|
const parts = [];
|
|
@@ -30428,7 +30430,7 @@ async function execCommand(binary, args, options) {
|
|
|
30428
30430
|
}
|
|
30429
30431
|
function wireSpawnListeners(binary, child, timeoutMs, resolve) {
|
|
30430
30432
|
let stdout = "";
|
|
30431
|
-
let
|
|
30433
|
+
let stderr5 = "";
|
|
30432
30434
|
let settled = false;
|
|
30433
30435
|
const name = binary.split("/").pop() ?? binary;
|
|
30434
30436
|
const settle = (result) => {
|
|
@@ -30443,14 +30445,14 @@ function wireSpawnListeners(binary, child, timeoutMs, resolve) {
|
|
|
30443
30445
|
}, timeoutMs);
|
|
30444
30446
|
child.stdout?.on("data", (chunk) => {
|
|
30445
30447
|
stdout += String(chunk);
|
|
30446
|
-
if (stdout.length +
|
|
30448
|
+
if (stdout.length + stderr5.length > MAX_OUTPUT_BYTES) {
|
|
30447
30449
|
if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
|
|
30448
30450
|
settle(err(new Error(`${name} output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
|
|
30449
30451
|
}
|
|
30450
30452
|
});
|
|
30451
30453
|
child.stderr?.on("data", (chunk) => {
|
|
30452
|
-
|
|
30453
|
-
if (stdout.length +
|
|
30454
|
+
stderr5 += String(chunk);
|
|
30455
|
+
if (stdout.length + stderr5.length > MAX_OUTPUT_BYTES) {
|
|
30454
30456
|
if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
|
|
30455
30457
|
settle(err(new Error(`${name} output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
|
|
30456
30458
|
}
|
|
@@ -30460,7 +30462,7 @@ function wireSpawnListeners(binary, child, timeoutMs, resolve) {
|
|
|
30460
30462
|
settle(err(new Error(`${name} error: ${error.message}`)));
|
|
30461
30463
|
});
|
|
30462
30464
|
child.on("close", (code) => {
|
|
30463
|
-
settle(ok({ stdout, stderr:
|
|
30465
|
+
settle(ok({ stdout, stderr: stderr5, exitCode: code ?? 1 }));
|
|
30464
30466
|
});
|
|
30465
30467
|
}
|
|
30466
30468
|
async function spawnCommand(binary, args, options) {
|
|
@@ -30682,8 +30684,8 @@ function unwrapValue(v) {
|
|
|
30682
30684
|
const obj = v;
|
|
30683
30685
|
if ("#text" in obj) return obj["#text"];
|
|
30684
30686
|
const keys = Object.keys(obj);
|
|
30685
|
-
if (keys.
|
|
30686
|
-
return
|
|
30687
|
+
if (keys.every((k) => k.startsWith("@_"))) {
|
|
30688
|
+
return "";
|
|
30687
30689
|
}
|
|
30688
30690
|
return v;
|
|
30689
30691
|
}
|
|
@@ -30889,16 +30891,542 @@ async function dcm2json(inputPath, options) {
|
|
|
30889
30891
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
30890
30892
|
const signal = options?.signal;
|
|
30891
30893
|
const startTime = Date.now();
|
|
30892
|
-
const remaining = () => Math.max(
|
|
30894
|
+
const remaining = () => Math.max(0, timeoutMs - (Date.now() - startTime));
|
|
30893
30895
|
const verbosity = options?.verbosity;
|
|
30894
30896
|
if (options?.directOnly === true) {
|
|
30895
|
-
return tryDirectPath(inputPath,
|
|
30897
|
+
return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
|
|
30896
30898
|
}
|
|
30897
|
-
const xmlResult = await tryXmlPath(inputPath,
|
|
30899
|
+
const xmlResult = await tryXmlPath(inputPath, timeoutMs, signal, buildXmlOpts(options));
|
|
30898
30900
|
if (xmlResult.ok) {
|
|
30899
30901
|
return xmlResult;
|
|
30900
30902
|
}
|
|
30901
|
-
return
|
|
30903
|
+
return fallbackToDirect(inputPath, xmlResult.error, { budget: remaining(), timeoutMs, signal, verbosity });
|
|
30904
|
+
}
|
|
30905
|
+
async function fallbackToDirect(inputPath, xmlError, context) {
|
|
30906
|
+
if (context.budget === 0) {
|
|
30907
|
+
return err(
|
|
30908
|
+
new Error(
|
|
30909
|
+
`dcm2json: XML path failed and timeout budget (${String(context.timeoutMs)}ms) is exhausted (skipping direct fallback) | xml: ${xmlError.message}`
|
|
30910
|
+
)
|
|
30911
|
+
);
|
|
30912
|
+
}
|
|
30913
|
+
const directResult = await tryDirectPath(inputPath, context.budget, context.signal, context.verbosity);
|
|
30914
|
+
if (directResult.ok) {
|
|
30915
|
+
return directResult;
|
|
30916
|
+
}
|
|
30917
|
+
return err(new Error(`dcm2json: both paths failed | xml: ${xmlError.message} | direct: ${directResult.error.message}`));
|
|
30918
|
+
}
|
|
30919
|
+
|
|
30920
|
+
// src/tools/_charset.ts
|
|
30921
|
+
var LATIN1 = { kind: "latin1" };
|
|
30922
|
+
function label(name) {
|
|
30923
|
+
return { kind: "label", label: name };
|
|
30924
|
+
}
|
|
30925
|
+
var CHARSET_DECODERS = {
|
|
30926
|
+
"ISO_IR 6": LATIN1,
|
|
30927
|
+
"ISO_IR 100": LATIN1,
|
|
30928
|
+
"ISO_IR 101": label("iso-8859-2"),
|
|
30929
|
+
"ISO_IR 109": label("iso-8859-3"),
|
|
30930
|
+
"ISO_IR 110": label("iso-8859-4"),
|
|
30931
|
+
"ISO_IR 144": label("iso-8859-5"),
|
|
30932
|
+
"ISO_IR 127": label("iso-8859-6"),
|
|
30933
|
+
"ISO_IR 126": label("iso-8859-7"),
|
|
30934
|
+
"ISO_IR 138": label("iso-8859-8"),
|
|
30935
|
+
"ISO_IR 148": label("iso-8859-9"),
|
|
30936
|
+
"ISO_IR 203": label("iso-8859-15"),
|
|
30937
|
+
"ISO_IR 13": label("shift_jis"),
|
|
30938
|
+
"ISO_IR 166": label("windows-874"),
|
|
30939
|
+
"ISO_IR 192": label("utf-8"),
|
|
30940
|
+
GB18030: label("gb18030"),
|
|
30941
|
+
GBK: label("gbk")
|
|
30942
|
+
};
|
|
30943
|
+
var ISO2022_INITIAL = {
|
|
30944
|
+
"ISO 2022 IR 6": LATIN1,
|
|
30945
|
+
"ISO 2022 IR 13": label("shift_jis"),
|
|
30946
|
+
"ISO 2022 IR 87": LATIN1,
|
|
30947
|
+
"ISO 2022 IR 159": LATIN1,
|
|
30948
|
+
"ISO 2022 IR 149": label("euc-kr"),
|
|
30949
|
+
"ISO 2022 IR 58": label("gbk"),
|
|
30950
|
+
"ISO 2022 IR 100": LATIN1,
|
|
30951
|
+
"ISO 2022 IR 101": label("iso-8859-2"),
|
|
30952
|
+
"ISO 2022 IR 109": label("iso-8859-3"),
|
|
30953
|
+
"ISO 2022 IR 110": label("iso-8859-4"),
|
|
30954
|
+
"ISO 2022 IR 144": label("iso-8859-5"),
|
|
30955
|
+
"ISO 2022 IR 127": label("iso-8859-6"),
|
|
30956
|
+
"ISO 2022 IR 126": label("iso-8859-7"),
|
|
30957
|
+
"ISO 2022 IR 138": label("iso-8859-8"),
|
|
30958
|
+
"ISO 2022 IR 148": label("iso-8859-9"),
|
|
30959
|
+
"ISO 2022 IR 166": label("windows-874"),
|
|
30960
|
+
"ISO 2022 IR 203": label("iso-8859-15")
|
|
30961
|
+
};
|
|
30962
|
+
var ESCAPE_DECODERS = {
|
|
30963
|
+
"(B": LATIN1,
|
|
30964
|
+
// G0 = ASCII
|
|
30965
|
+
"(J": LATIN1,
|
|
30966
|
+
// G0 = JIS X 0201 romaji
|
|
30967
|
+
")I": { kind: "katakana" },
|
|
30968
|
+
// G1 = JIS X 0201 katakana
|
|
30969
|
+
"$@": { kind: "iso2022jp", escape: [27, 36, 64] },
|
|
30970
|
+
// G0 = JIS X 0208-1978
|
|
30971
|
+
$B: { kind: "iso2022jp", escape: [27, 36, 66] },
|
|
30972
|
+
// G0 = JIS X 0208
|
|
30973
|
+
"$(D": { kind: "iso2022jp", escape: [27, 36, 40, 68] },
|
|
30974
|
+
// G0 = JIS X 0212
|
|
30975
|
+
"$)C": label("euc-kr"),
|
|
30976
|
+
// G1 = KS X 1001
|
|
30977
|
+
"$)A": label("gbk"),
|
|
30978
|
+
// G1 = GB 2312
|
|
30979
|
+
"-A": LATIN1,
|
|
30980
|
+
// G1 = ISO-8859-1
|
|
30981
|
+
"-B": label("iso-8859-2"),
|
|
30982
|
+
"-C": label("iso-8859-3"),
|
|
30983
|
+
"-D": label("iso-8859-4"),
|
|
30984
|
+
"-F": label("iso-8859-7"),
|
|
30985
|
+
"-G": label("iso-8859-6"),
|
|
30986
|
+
"-H": label("iso-8859-8"),
|
|
30987
|
+
"-L": label("iso-8859-5"),
|
|
30988
|
+
"-M": label("iso-8859-9"),
|
|
30989
|
+
"-T": label("windows-874"),
|
|
30990
|
+
"-b": label("iso-8859-15")
|
|
30991
|
+
};
|
|
30992
|
+
var CHARSET_ALIASES = {
|
|
30993
|
+
ascii: "ISO_IR 6",
|
|
30994
|
+
"latin-1": "ISO_IR 100",
|
|
30995
|
+
latin1: "ISO_IR 100",
|
|
30996
|
+
"iso-8859-1": "ISO_IR 100",
|
|
30997
|
+
"latin-2": "ISO_IR 101",
|
|
30998
|
+
"latin-3": "ISO_IR 109",
|
|
30999
|
+
"latin-4": "ISO_IR 110",
|
|
31000
|
+
"latin-5": "ISO_IR 148",
|
|
31001
|
+
"latin-9": "ISO_IR 203",
|
|
31002
|
+
cyrillic: "ISO_IR 144",
|
|
31003
|
+
arabic: "ISO_IR 127",
|
|
31004
|
+
greek: "ISO_IR 126",
|
|
31005
|
+
hebrew: "ISO_IR 138",
|
|
31006
|
+
thai: "ISO_IR 166",
|
|
31007
|
+
"shift-jis": "ISO_IR 13",
|
|
31008
|
+
shift_jis: "ISO_IR 13",
|
|
31009
|
+
"utf-8": "ISO_IR 192",
|
|
31010
|
+
utf8: "ISO_IR 192",
|
|
31011
|
+
gb18030: "GB18030",
|
|
31012
|
+
gbk: "GBK"
|
|
31013
|
+
};
|
|
31014
|
+
var decoderCache = /* @__PURE__ */ new Map();
|
|
31015
|
+
function getTextDecoder(labelName) {
|
|
31016
|
+
if (decoderCache.has(labelName)) {
|
|
31017
|
+
return decoderCache.get(labelName);
|
|
31018
|
+
}
|
|
31019
|
+
let decoder;
|
|
31020
|
+
try {
|
|
31021
|
+
decoder = new TextDecoder(labelName);
|
|
31022
|
+
} catch {
|
|
31023
|
+
decoder = void 0;
|
|
31024
|
+
}
|
|
31025
|
+
decoderCache.set(labelName, decoder);
|
|
31026
|
+
return decoder;
|
|
31027
|
+
}
|
|
31028
|
+
function decodeLatin1(bytes) {
|
|
31029
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("latin1");
|
|
31030
|
+
}
|
|
31031
|
+
function decodeSegment(bytes, decoder) {
|
|
31032
|
+
if (bytes.length === 0) return "";
|
|
31033
|
+
switch (decoder.kind) {
|
|
31034
|
+
case "latin1":
|
|
31035
|
+
return decodeLatin1(bytes);
|
|
31036
|
+
case "label": {
|
|
31037
|
+
const td = getTextDecoder(decoder.label);
|
|
31038
|
+
if (td === void 0) return decodeLatin1(bytes);
|
|
31039
|
+
return td.decode(bytes);
|
|
31040
|
+
}
|
|
31041
|
+
case "iso2022jp": {
|
|
31042
|
+
const td = getTextDecoder("iso-2022-jp");
|
|
31043
|
+
if (td === void 0) return decodeLatin1(bytes);
|
|
31044
|
+
const wrapped = new Uint8Array(decoder.escape.length + bytes.length);
|
|
31045
|
+
wrapped.set(decoder.escape, 0);
|
|
31046
|
+
wrapped.set(bytes, decoder.escape.length);
|
|
31047
|
+
return td.decode(wrapped);
|
|
31048
|
+
}
|
|
31049
|
+
case "katakana": {
|
|
31050
|
+
const td = getTextDecoder("shift_jis");
|
|
31051
|
+
if (td === void 0) return decodeLatin1(bytes);
|
|
31052
|
+
return td.decode(bytes);
|
|
31053
|
+
}
|
|
31054
|
+
}
|
|
31055
|
+
}
|
|
31056
|
+
function readEscape(bytes, start) {
|
|
31057
|
+
let i = start + 1;
|
|
31058
|
+
while (i < bytes.length && bytes[i] >= 32 && bytes[i] <= 47) {
|
|
31059
|
+
i++;
|
|
31060
|
+
}
|
|
31061
|
+
if (i < bytes.length && bytes[i] >= 48 && bytes[i] <= 126) {
|
|
31062
|
+
i++;
|
|
31063
|
+
}
|
|
31064
|
+
const seq = bytes.subarray(start + 1, i);
|
|
31065
|
+
return { key: decodeLatin1(seq), length: i - start };
|
|
31066
|
+
}
|
|
31067
|
+
function decodeIso2022(bytes, initial) {
|
|
31068
|
+
let out = "";
|
|
31069
|
+
let current = initial;
|
|
31070
|
+
let segStart = 0;
|
|
31071
|
+
let i = 0;
|
|
31072
|
+
while (i < bytes.length) {
|
|
31073
|
+
if (bytes[i] !== 27) {
|
|
31074
|
+
i++;
|
|
31075
|
+
continue;
|
|
31076
|
+
}
|
|
31077
|
+
out += decodeSegment(bytes.subarray(segStart, i), current);
|
|
31078
|
+
const esc = readEscape(bytes, i);
|
|
31079
|
+
current = ESCAPE_DECODERS[esc.key] ?? current;
|
|
31080
|
+
i += esc.length;
|
|
31081
|
+
segStart = i;
|
|
31082
|
+
}
|
|
31083
|
+
out += decodeSegment(bytes.subarray(segStart), current);
|
|
31084
|
+
return out;
|
|
31085
|
+
}
|
|
31086
|
+
var DEFAULT_CONTEXT = { terms: ["ISO_IR 6"], iso2022: false, initial: LATIN1 };
|
|
31087
|
+
function normalizeCharsetName(input) {
|
|
31088
|
+
const trimmed = input.trim();
|
|
31089
|
+
if (CHARSET_DECODERS[trimmed] !== void 0 || ISO2022_INITIAL[trimmed] !== void 0) {
|
|
31090
|
+
return trimmed;
|
|
31091
|
+
}
|
|
31092
|
+
return CHARSET_ALIASES[trimmed.toLowerCase()];
|
|
31093
|
+
}
|
|
31094
|
+
function buildContext(terms) {
|
|
31095
|
+
const iso2022 = terms.length > 1 || terms.some((t) => t.startsWith("ISO 2022"));
|
|
31096
|
+
const first = terms[0] ?? "ISO_IR 6";
|
|
31097
|
+
const initial = iso2022 ? ISO2022_INITIAL[first] : CHARSET_DECODERS[first];
|
|
31098
|
+
if (initial === void 0) return void 0;
|
|
31099
|
+
return { terms, iso2022, initial };
|
|
31100
|
+
}
|
|
31101
|
+
function parseSpecificCharacterSet(raw) {
|
|
31102
|
+
const values = raw.split("\\").map((v) => v.trim());
|
|
31103
|
+
return values.map((v, idx) => v === "" && idx === 0 ? "ISO 2022 IR 6" : v).filter((v) => v !== "");
|
|
31104
|
+
}
|
|
31105
|
+
function contextFromTerms(terms, fallback, source) {
|
|
31106
|
+
const context = buildContext(terms);
|
|
31107
|
+
if (context !== void 0) {
|
|
31108
|
+
return ok(context);
|
|
31109
|
+
}
|
|
31110
|
+
if (fallback !== void 0) {
|
|
31111
|
+
const fallbackTerm = normalizeCharsetName(fallback);
|
|
31112
|
+
const fallbackContext = fallbackTerm !== void 0 ? buildContext([fallbackTerm]) : void 0;
|
|
31113
|
+
if (fallbackContext !== void 0) {
|
|
31114
|
+
return ok(fallbackContext);
|
|
31115
|
+
}
|
|
31116
|
+
return err(new Error(`Unsupported charset fallback '${fallback}' (while handling unsupported ${source})`));
|
|
31117
|
+
}
|
|
31118
|
+
return err(new Error(`Unsupported character set: ${source} \u2014 provide charsetFallback (e.g. 'latin-1') to decode best-effort`));
|
|
31119
|
+
}
|
|
31120
|
+
function resolveCharsetContext(specificCharacterSet, charsetAssume, charsetFallback) {
|
|
31121
|
+
if (specificCharacterSet === void 0 || specificCharacterSet.trim() === "") {
|
|
31122
|
+
if (charsetAssume === void 0) {
|
|
31123
|
+
return ok(DEFAULT_CONTEXT);
|
|
31124
|
+
}
|
|
31125
|
+
const term = normalizeCharsetName(charsetAssume);
|
|
31126
|
+
if (term === void 0) {
|
|
31127
|
+
return contextFromTerms([charsetAssume], charsetFallback, `charsetAssume '${charsetAssume}'`);
|
|
31128
|
+
}
|
|
31129
|
+
return contextFromTerms([term], charsetFallback, `charsetAssume '${charsetAssume}'`);
|
|
31130
|
+
}
|
|
31131
|
+
const terms = parseSpecificCharacterSet(specificCharacterSet);
|
|
31132
|
+
return contextFromTerms(terms, charsetFallback, `'${specificCharacterSet}' (0008,0005)`);
|
|
31133
|
+
}
|
|
31134
|
+
function decodeDicomText(bytes, context) {
|
|
31135
|
+
if (context.iso2022) {
|
|
31136
|
+
return decodeIso2022(bytes, context.initial);
|
|
31137
|
+
}
|
|
31138
|
+
return decodeSegment(bytes, context.initial);
|
|
31139
|
+
}
|
|
31140
|
+
|
|
31141
|
+
// src/tools/_p10ToJson.ts
|
|
31142
|
+
var BINARY_VRS = /* @__PURE__ */ new Set(["OB", "OW", "OD", "OF", "OL", "OV", "UN"]);
|
|
31143
|
+
var CHARSET_VRS = /* @__PURE__ */ new Set(["SH", "LO", "ST", "LT", "UC", "UT", "PN"]);
|
|
31144
|
+
var NO_SPLIT_VRS = /* @__PURE__ */ new Set(["ST", "LT", "UT", "UR"]);
|
|
31145
|
+
var NUMERIC_VR_SIZE = { US: 2, SS: 2, UL: 4, SL: 4, FL: 4, FD: 8, SV: 8, UV: 8 };
|
|
31146
|
+
var MAX_DATASETS = 1e5;
|
|
31147
|
+
function toJsonTag(tag) {
|
|
31148
|
+
return tag.slice(1).toUpperCase();
|
|
31149
|
+
}
|
|
31150
|
+
function isEncodingArtifact(tag) {
|
|
31151
|
+
return tag.endsWith("0000") && tag.length === 9 || tag.startsWith("xfffe");
|
|
31152
|
+
}
|
|
31153
|
+
function resolveVr(element) {
|
|
31154
|
+
const vr = element.vr;
|
|
31155
|
+
if (vr !== void 0 && KNOWN_VR_CODES.has(vr)) {
|
|
31156
|
+
return vr;
|
|
31157
|
+
}
|
|
31158
|
+
return "UN";
|
|
31159
|
+
}
|
|
31160
|
+
function valueBytes(element, byteArray) {
|
|
31161
|
+
return byteArray.subarray(element.dataOffset, element.dataOffset + element.length);
|
|
31162
|
+
}
|
|
31163
|
+
function trimPadding(value) {
|
|
31164
|
+
let end = value.length;
|
|
31165
|
+
while (end > 0) {
|
|
31166
|
+
const ch = value.charCodeAt(end - 1);
|
|
31167
|
+
if (ch !== 32 && ch !== 0) break;
|
|
31168
|
+
end--;
|
|
31169
|
+
}
|
|
31170
|
+
return value.slice(0, end);
|
|
31171
|
+
}
|
|
31172
|
+
function readNumericAt(view, offset, vr, littleEndian) {
|
|
31173
|
+
switch (vr) {
|
|
31174
|
+
case "US":
|
|
31175
|
+
return view.getUint16(offset, littleEndian);
|
|
31176
|
+
case "SS":
|
|
31177
|
+
return view.getInt16(offset, littleEndian);
|
|
31178
|
+
case "UL":
|
|
31179
|
+
return view.getUint32(offset, littleEndian);
|
|
31180
|
+
case "SL":
|
|
31181
|
+
return view.getInt32(offset, littleEndian);
|
|
31182
|
+
case "FL":
|
|
31183
|
+
return view.getFloat32(offset, littleEndian);
|
|
31184
|
+
case "FD":
|
|
31185
|
+
return view.getFloat64(offset, littleEndian);
|
|
31186
|
+
case "SV":
|
|
31187
|
+
return Number(view.getBigInt64(offset, littleEndian));
|
|
31188
|
+
/* v8 ignore next 2 -- 'UV' is the only remaining caller-provided VR */
|
|
31189
|
+
default:
|
|
31190
|
+
return Number(view.getBigUint64(offset, littleEndian));
|
|
31191
|
+
}
|
|
31192
|
+
}
|
|
31193
|
+
function convertNumeric(element, byteArray, vr, littleEndian) {
|
|
31194
|
+
const size = NUMERIC_VR_SIZE[vr];
|
|
31195
|
+
const count = Math.floor(element.length / size);
|
|
31196
|
+
if (count === 0) {
|
|
31197
|
+
return { vr };
|
|
31198
|
+
}
|
|
31199
|
+
const view = new DataView(byteArray.buffer, byteArray.byteOffset + element.dataOffset, element.length);
|
|
31200
|
+
const values = [];
|
|
31201
|
+
for (let i = 0; i < count; i++) {
|
|
31202
|
+
values.push(readNumericAt(view, i * size, vr, littleEndian));
|
|
31203
|
+
}
|
|
31204
|
+
return { vr, Value: values };
|
|
31205
|
+
}
|
|
31206
|
+
function convertAttributeTag(element, byteArray, littleEndian) {
|
|
31207
|
+
const count = Math.floor(element.length / 4);
|
|
31208
|
+
if (count === 0) {
|
|
31209
|
+
return { vr: "AT" };
|
|
31210
|
+
}
|
|
31211
|
+
const view = new DataView(byteArray.buffer, byteArray.byteOffset + element.dataOffset, element.length);
|
|
31212
|
+
const values = [];
|
|
31213
|
+
for (let i = 0; i < count; i++) {
|
|
31214
|
+
const group = view.getUint16(i * 4, littleEndian);
|
|
31215
|
+
const elem = view.getUint16(i * 4 + 2, littleEndian);
|
|
31216
|
+
values.push(group.toString(16).padStart(4, "0").toUpperCase() + elem.toString(16).padStart(4, "0").toUpperCase());
|
|
31217
|
+
}
|
|
31218
|
+
return { vr: "AT", Value: values };
|
|
31219
|
+
}
|
|
31220
|
+
function personNameToJson(value) {
|
|
31221
|
+
const groups = trimPadding(value).split("=");
|
|
31222
|
+
const result = {};
|
|
31223
|
+
const names = ["Alphabetic", "Ideographic", "Phonetic"];
|
|
31224
|
+
for (let i = 0; i < names.length; i++) {
|
|
31225
|
+
const group = (groups[i] ?? "").replace(/\^+$/u, "");
|
|
31226
|
+
const name = names[i];
|
|
31227
|
+
if (group !== "" && name !== void 0) {
|
|
31228
|
+
result[name] = group;
|
|
31229
|
+
}
|
|
31230
|
+
}
|
|
31231
|
+
return result;
|
|
31232
|
+
}
|
|
31233
|
+
function convertPersonName2(element, byteArray, charset) {
|
|
31234
|
+
const decoded = decodeDicomText(valueBytes(element, byteArray), charset);
|
|
31235
|
+
const values = decoded.split("\\").map(personNameToJson);
|
|
31236
|
+
return { vr: "PN", Value: values };
|
|
31237
|
+
}
|
|
31238
|
+
function convertString(element, byteArray, vr, charset) {
|
|
31239
|
+
const bytes = valueBytes(element, byteArray);
|
|
31240
|
+
const decoded = CHARSET_VRS.has(vr) ? decodeDicomText(bytes, charset) : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("latin1");
|
|
31241
|
+
const rawValues = NO_SPLIT_VRS.has(vr) ? [decoded] : decoded.split("\\");
|
|
31242
|
+
const values = rawValues.map((v) => {
|
|
31243
|
+
const trimmed = trimPadding(v);
|
|
31244
|
+
return vr === "DS" || vr === "IS" ? coerceNumeric(trimmed.trim()) : trimmed;
|
|
31245
|
+
});
|
|
31246
|
+
if (values.length === 1 && values[0] === "") {
|
|
31247
|
+
return { vr };
|
|
31248
|
+
}
|
|
31249
|
+
return { vr, Value: values };
|
|
31250
|
+
}
|
|
31251
|
+
function convertLeaf(element, byteArray, vr, settings) {
|
|
31252
|
+
if (element.fragments !== void 0) {
|
|
31253
|
+
return { vr: "OB" };
|
|
31254
|
+
}
|
|
31255
|
+
if (BINARY_VRS.has(vr)) {
|
|
31256
|
+
return { vr };
|
|
31257
|
+
}
|
|
31258
|
+
if (element.length === 0) {
|
|
31259
|
+
return { vr };
|
|
31260
|
+
}
|
|
31261
|
+
if (NUMERIC_VR_SIZE[vr] !== void 0) {
|
|
31262
|
+
return convertNumeric(element, byteArray, vr, settings.littleEndian);
|
|
31263
|
+
}
|
|
31264
|
+
if (vr === "AT") {
|
|
31265
|
+
return convertAttributeTag(element, byteArray, settings.littleEndian);
|
|
31266
|
+
}
|
|
31267
|
+
if (vr === "PN") {
|
|
31268
|
+
return convertPersonName2(element, byteArray, settings.charset);
|
|
31269
|
+
}
|
|
31270
|
+
return convertString(element, byteArray, vr, settings.charset);
|
|
31271
|
+
}
|
|
31272
|
+
function datasetCharset(src, parent, options) {
|
|
31273
|
+
const raw = src.string("x00080005");
|
|
31274
|
+
if (raw === void 0) {
|
|
31275
|
+
return ok(parent);
|
|
31276
|
+
}
|
|
31277
|
+
return resolveCharsetContext(raw, void 0, options?.charsetFallback);
|
|
31278
|
+
}
|
|
31279
|
+
function convertSequence2(element, stack, charset) {
|
|
31280
|
+
const items = element.items ?? [];
|
|
31281
|
+
const values = [];
|
|
31282
|
+
for (const item of items) {
|
|
31283
|
+
const model = {};
|
|
31284
|
+
values.push(model);
|
|
31285
|
+
if (item.dataSet !== void 0) {
|
|
31286
|
+
stack.push({ src: item.dataSet, out: model, parentCharset: charset });
|
|
31287
|
+
}
|
|
31288
|
+
}
|
|
31289
|
+
if (values.length === 0) {
|
|
31290
|
+
return { vr: "SQ" };
|
|
31291
|
+
}
|
|
31292
|
+
return { vr: "SQ", Value: values };
|
|
31293
|
+
}
|
|
31294
|
+
function isSequence(element, vr) {
|
|
31295
|
+
if (element.fragments !== void 0) {
|
|
31296
|
+
return false;
|
|
31297
|
+
}
|
|
31298
|
+
return vr === "SQ" || element.vr === void 0 && element.items !== void 0;
|
|
31299
|
+
}
|
|
31300
|
+
function convertDataset(unit, stack, littleEndian, options) {
|
|
31301
|
+
const charsetResult = datasetCharset(unit.src, unit.parentCharset, options);
|
|
31302
|
+
if (!charsetResult.ok) {
|
|
31303
|
+
return err(charsetResult.error);
|
|
31304
|
+
}
|
|
31305
|
+
const charset = charsetResult.value;
|
|
31306
|
+
const keys = Object.keys(unit.src.elements).sort();
|
|
31307
|
+
for (const key of keys) {
|
|
31308
|
+
const element = unit.src.elements[key];
|
|
31309
|
+
if (element === void 0) continue;
|
|
31310
|
+
if (isEncodingArtifact(element.tag)) continue;
|
|
31311
|
+
const tag = toJsonTag(element.tag);
|
|
31312
|
+
const vr = resolveVr(element);
|
|
31313
|
+
if (isSequence(element, vr)) {
|
|
31314
|
+
unit.out[tag] = convertSequence2(element, stack, charset);
|
|
31315
|
+
} else {
|
|
31316
|
+
unit.out[tag] = convertLeaf(element, unit.src.byteArray, vr, { charset, littleEndian });
|
|
31317
|
+
}
|
|
31318
|
+
}
|
|
31319
|
+
return ok(void 0);
|
|
31320
|
+
}
|
|
31321
|
+
function inflateDeflated(byteArray, position) {
|
|
31322
|
+
const inflated = inflateRawSync(byteArray.subarray(position));
|
|
31323
|
+
const combined = new Uint8Array(position + inflated.byteLength);
|
|
31324
|
+
combined.set(byteArray.subarray(0, position), 0);
|
|
31325
|
+
combined.set(inflated, position);
|
|
31326
|
+
return combined;
|
|
31327
|
+
}
|
|
31328
|
+
function implicitVrLookup(tag) {
|
|
31329
|
+
return lookupTag(tag.slice(1))?.vr;
|
|
31330
|
+
}
|
|
31331
|
+
function parseDicomBuffer(buffer, options) {
|
|
31332
|
+
let dataSet;
|
|
31333
|
+
try {
|
|
31334
|
+
dataSet = dicomParser.parseDicom(buffer, { vrCallback: implicitVrLookup, inflater: inflateDeflated });
|
|
31335
|
+
} catch (error) {
|
|
31336
|
+
return err(new Error(`Failed to parse DICOM data: ${stderr(error).message}`));
|
|
31337
|
+
}
|
|
31338
|
+
const littleEndian = dataSet.string("x00020010") !== "1.2.840.10008.1.2.2";
|
|
31339
|
+
const rootCharset = resolveCharsetContext(void 0, options?.charsetAssume, options?.charsetFallback);
|
|
31340
|
+
if (!rootCharset.ok) {
|
|
31341
|
+
return err(rootCharset.error);
|
|
31342
|
+
}
|
|
31343
|
+
const data = {};
|
|
31344
|
+
const stack = [{ src: dataSet, out: data, parentCharset: rootCharset.value }];
|
|
31345
|
+
let processed = 0;
|
|
31346
|
+
while (stack.length > 0) {
|
|
31347
|
+
processed++;
|
|
31348
|
+
if (processed > MAX_DATASETS) {
|
|
31349
|
+
return err(new Error(`Failed to parse DICOM data: more than ${String(MAX_DATASETS)} nested datasets`));
|
|
31350
|
+
}
|
|
31351
|
+
const unit = stack.pop();
|
|
31352
|
+
if (unit === void 0) break;
|
|
31353
|
+
const converted = convertDataset(unit, stack, littleEndian, options);
|
|
31354
|
+
if (!converted.ok) {
|
|
31355
|
+
return err(converted.error);
|
|
31356
|
+
}
|
|
31357
|
+
}
|
|
31358
|
+
return ok({ data, warnings: dataSet.warnings });
|
|
31359
|
+
}
|
|
31360
|
+
|
|
31361
|
+
// src/tools/dicom2json.ts
|
|
31362
|
+
var Dicom2jsonOptionsSchema = z.object({
|
|
31363
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
31364
|
+
signal: z.instanceof(AbortSignal).optional(),
|
|
31365
|
+
charsetAssume: z.string().min(1).optional(),
|
|
31366
|
+
charsetFallback: z.string().min(1).optional(),
|
|
31367
|
+
dcmtkFallback: z.boolean().optional()
|
|
31368
|
+
}).strict().optional();
|
|
31369
|
+
z.object({
|
|
31370
|
+
charsetAssume: z.string().min(1).optional(),
|
|
31371
|
+
charsetFallback: z.string().min(1).optional()
|
|
31372
|
+
}).strict().optional();
|
|
31373
|
+
async function readFileBounded(inputPath, timeoutMs, signal) {
|
|
31374
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
31375
|
+
const combined = signal !== void 0 ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
31376
|
+
try {
|
|
31377
|
+
return ok(await readFile(inputPath, { signal: combined }));
|
|
31378
|
+
} catch (error) {
|
|
31379
|
+
if (timeoutSignal.aborted) {
|
|
31380
|
+
return err(new Error(`dicom2json: reading '${inputPath}' timed out after ${String(timeoutMs)}ms`));
|
|
31381
|
+
}
|
|
31382
|
+
if (signal?.aborted === true) {
|
|
31383
|
+
return err(new Error(`dicom2json: aborted while reading '${inputPath}'`));
|
|
31384
|
+
}
|
|
31385
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31386
|
+
return err(new Error(`dicom2json: failed to read '${inputPath}': ${message}`));
|
|
31387
|
+
}
|
|
31388
|
+
}
|
|
31389
|
+
async function runDcmtkFallback(inputPath, jsError, remainingMs, options) {
|
|
31390
|
+
if (remainingMs <= 0) {
|
|
31391
|
+
return err(new Error(`dicom2json: JS parse failed and timeout budget exhausted (skipping DCMTK fallback) | js: ${jsError.message}`));
|
|
31392
|
+
}
|
|
31393
|
+
const fallback = await dcm2json(inputPath, {
|
|
31394
|
+
timeoutMs: remainingMs,
|
|
31395
|
+
signal: options?.signal,
|
|
31396
|
+
charsetAssume: options?.charsetAssume,
|
|
31397
|
+
charsetFallback: options?.charsetFallback
|
|
31398
|
+
});
|
|
31399
|
+
if (!fallback.ok) {
|
|
31400
|
+
return err(new Error(`dicom2json: both engines failed | js: ${jsError.message} | dcmtk: ${fallback.error.message}`));
|
|
31401
|
+
}
|
|
31402
|
+
return ok({ data: fallback.value.data, warnings: [], source: fallback.value.source });
|
|
31403
|
+
}
|
|
31404
|
+
async function dicom2json(inputPath, options) {
|
|
31405
|
+
const validation = Dicom2jsonOptionsSchema.safeParse(options);
|
|
31406
|
+
if (!validation.success) {
|
|
31407
|
+
return err(createValidationError("dicom2json", validation.error));
|
|
31408
|
+
}
|
|
31409
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
31410
|
+
const startTime = Date.now();
|
|
31411
|
+
const parsed = await parseFromFile(inputPath, timeoutMs, options);
|
|
31412
|
+
if (parsed.ok) {
|
|
31413
|
+
return parsed;
|
|
31414
|
+
}
|
|
31415
|
+
if (options?.dcmtkFallback === true) {
|
|
31416
|
+
return runDcmtkFallback(inputPath, parsed.error, timeoutMs - (Date.now() - startTime), options);
|
|
31417
|
+
}
|
|
31418
|
+
return err(parsed.error);
|
|
31419
|
+
}
|
|
31420
|
+
async function parseFromFile(inputPath, timeoutMs, options) {
|
|
31421
|
+
const fileResult = await readFileBounded(inputPath, timeoutMs, options?.signal);
|
|
31422
|
+
if (!fileResult.ok) {
|
|
31423
|
+
return err(fileResult.error);
|
|
31424
|
+
}
|
|
31425
|
+
const parsed = parseDicomBuffer(fileResult.value, { charsetAssume: options?.charsetAssume, charsetFallback: options?.charsetFallback });
|
|
31426
|
+
if (!parsed.ok) {
|
|
31427
|
+
return err(parsed.error);
|
|
31428
|
+
}
|
|
31429
|
+
return ok({ data: parsed.value.data, warnings: parsed.value.warnings, source: "js" });
|
|
30902
31430
|
}
|
|
30903
31431
|
var TAG_OR_PATH_PATTERN = /^\([0-9A-Fa-f]{4},[0-9A-Fa-f]{4}\)(\[\d+\]\.\([0-9A-Fa-f]{4},[0-9A-Fa-f]{4}\))*(\[\d+\])?$/;
|
|
30904
31432
|
var TagModificationSchema = z.object({
|
|
@@ -31005,6 +31533,17 @@ async function unlinkFile(path) {
|
|
|
31005
31533
|
}
|
|
31006
31534
|
|
|
31007
31535
|
// src/dicom/DicomInstance.ts
|
|
31536
|
+
async function readDicomJson(path, options) {
|
|
31537
|
+
const shared = {
|
|
31538
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
31539
|
+
signal: options?.signal,
|
|
31540
|
+
charsetAssume: options?.charsetAssume,
|
|
31541
|
+
charsetFallback: options?.charsetFallback
|
|
31542
|
+
};
|
|
31543
|
+
const result = options?.engine === "dcmtk" ? await dcm2json(path, shared) : await dicom2json(path, { ...shared, dcmtkFallback: true });
|
|
31544
|
+
if (!result.ok) return err(result.error);
|
|
31545
|
+
return ok(result.value.data);
|
|
31546
|
+
}
|
|
31008
31547
|
var DicomInstance = class _DicomInstance {
|
|
31009
31548
|
constructor(dataset, changes, filePath, metadata) {
|
|
31010
31549
|
__publicField(this, "dicomDataset");
|
|
@@ -31029,14 +31568,9 @@ var DicomInstance = class _DicomInstance {
|
|
|
31029
31568
|
static async open(path, options) {
|
|
31030
31569
|
const filePathResult = createDicomFilePath(path);
|
|
31031
31570
|
if (!filePathResult.ok) return err(filePathResult.error);
|
|
31032
|
-
const jsonResult = await
|
|
31033
|
-
timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
31034
|
-
signal: options?.signal,
|
|
31035
|
-
charsetAssume: options?.charsetAssume,
|
|
31036
|
-
charsetFallback: options?.charsetFallback
|
|
31037
|
-
});
|
|
31571
|
+
const jsonResult = await readDicomJson(path, options);
|
|
31038
31572
|
if (!jsonResult.ok) return err(jsonResult.error);
|
|
31039
|
-
const datasetResult = DicomDataset.fromJson(jsonResult.value
|
|
31573
|
+
const datasetResult = DicomDataset.fromJson(jsonResult.value);
|
|
31040
31574
|
if (!datasetResult.ok) return err(datasetResult.error);
|
|
31041
31575
|
return ok(new _DicomInstance(datasetResult.value, ChangeSet.empty(), filePathResult.value, /* @__PURE__ */ new Map()));
|
|
31042
31576
|
}
|