@ubercode/dcmtk 0.2.0 → 0.4.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.
- package/README.md +17 -12
- package/dist/{DicomInstance-CQEIuF_x.d.ts → DicomInstance-CGBr3a-C.d.ts} +2 -2
- package/dist/{DicomInstance-By9zd7GM.d.cts → DicomInstance-DWOjhccQ.d.cts} +2 -2
- package/dist/{dcmodify-Gds9u5Vj.d.cts → dcmodify-B9js5K1f.d.cts} +2 -0
- package/dist/{dcmodify-B-_uUIKB.d.ts → dcmodify-BvaIeyJg.d.ts} +2 -0
- package/dist/dicom.cjs +27 -11
- package/dist/dicom.cjs.map +1 -1
- package/dist/dicom.d.cts +3 -3
- package/dist/dicom.d.ts +3 -3
- package/dist/dicom.js +27 -11
- package/dist/dicom.js.map +1 -1
- package/dist/index.cjs +1299 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +320 -7
- package/dist/index.d.ts +320 -7
- package/dist/index.js +1297 -173
- package/dist/index.js.map +1 -1
- package/dist/servers.cjs +28 -12
- 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 +28 -12
- package/dist/servers.js.map +1 -1
- package/dist/tools.cjs +755 -157
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +226 -10
- package/dist/tools.d.ts +226 -10
- package/dist/tools.js +755 -157
- package/dist/tools.js.map +1 -1
- package/package.json +17 -1
package/README.md
CHANGED
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
[](https://nodejs.org/)
|
|
10
10
|
[](https://opensource.org/licenses/MIT)
|
|
11
11
|
|
|
12
|
-
Type-safe Node.js bindings for the [DCMTK](https://dicom.offis.de/dcmtk.php.en) (DICOM Toolkit) command-line utilities. Wraps 51 DCMTK binaries, 6 long-lived server processes,
|
|
12
|
+
Type-safe Node.js bindings for the [DCMTK](https://dicom.offis.de/dcmtk.php.en) (DICOM Toolkit) command-line utilities. Wraps 51 DCMTK binaries, 6 long-lived server processes, a pooled DicomReceiver with auto-scaling workers, and a high-throughput DicomSender with queuing and backpressure — all with a modern async/await API, branded types, and the Result pattern for safe error handling.
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
|
-
- **51 tool wrappers** — async functions for every DCMTK command-line binary
|
|
17
|
-
- **
|
|
16
|
+
- **51 tool wrappers** — async functions for every DCMTK command-line binary with `verbosity` control and full CLI flag coverage
|
|
17
|
+
- **Network resilience** — all 7 network tools support PDU sizing, ACSE/DIMSE/association timeouts, and hostname lookup control
|
|
18
|
+
- **6 server classes + DicomReceiver + DicomSender** — long-lived DICOM listeners with typed EventEmitter APIs, a pooled receiver with auto-scaling workers, and a high-throughput sender with queuing, bucketing, and backpressure
|
|
18
19
|
- **PacsClient** — high-level PACS client with Echo, Query, Retrieve, and Store operations
|
|
19
20
|
- **DICOM data layer** — immutable `DicomDataset`, explicit `ChangeSet` builder, and `DicomInstance` unified file I/O
|
|
20
21
|
- **Result pattern** — all fallible operations return `Result<T>` instead of throwing
|
|
@@ -63,6 +64,8 @@ const result = await echoscu({
|
|
|
63
64
|
host: '127.0.0.1',
|
|
64
65
|
port: 4242,
|
|
65
66
|
calledAETitle: 'PACS',
|
|
67
|
+
verbosity: 'verbose',
|
|
68
|
+
associationTimeout: 10,
|
|
66
69
|
});
|
|
67
70
|
|
|
68
71
|
if (result.ok) {
|
|
@@ -103,6 +106,7 @@ if (result.ok) {
|
|
|
103
106
|
| [PACS Client](docs/pacs-client.md) | High-level Echo, Query, Retrieve, Store API |
|
|
104
107
|
| [DICOM Data Layer](docs/dicom-data-layer.md) | DicomDataset, ChangeSet, DicomInstance |
|
|
105
108
|
| [Servers](docs/servers.md) | 6 server classes + DicomReceiver pooled receiver |
|
|
109
|
+
| [Senders](docs/senders.md) | DicomSender high-throughput sender with backpressure |
|
|
106
110
|
| [Utilities](docs/utilities.md) | batch processing, retry with backoff |
|
|
107
111
|
|
|
108
112
|
## Tool Reference
|
|
@@ -121,15 +125,16 @@ if (result.ok) {
|
|
|
121
125
|
|
|
122
126
|
## Server Reference
|
|
123
127
|
|
|
124
|
-
| Class | Binary
|
|
125
|
-
| --------------- |
|
|
126
|
-
| `Dcmrecv` | dcmrecv
|
|
127
|
-
| `StoreSCP` | storescp
|
|
128
|
-
| `DcmQRSCP` | dcmqrscp
|
|
129
|
-
| `Wlmscpfs` | wlmscpfs
|
|
130
|
-
| `DcmprsCP` | dcmprscp
|
|
131
|
-
| `Dcmpsrcv` | dcmpsrcv
|
|
132
|
-
| `DicomReceiver` | dcmrecv (pool)
|
|
128
|
+
| Class | Binary | Description | Docs |
|
|
129
|
+
| --------------- | --------------- | ------------------------------------------ | ------------------------------------------- |
|
|
130
|
+
| `Dcmrecv` | dcmrecv | DICOM receiver (C-STORE SCP) | [servers.md](docs/servers.md#dcmrecv) |
|
|
131
|
+
| `StoreSCP` | storescp | Storage SCP with advanced options | [servers.md](docs/servers.md#storescp) |
|
|
132
|
+
| `DcmQRSCP` | dcmqrscp | Query/Retrieve SCP (C-FIND, C-MOVE, C-GET) | [servers.md](docs/servers.md#dcmqrscp) |
|
|
133
|
+
| `Wlmscpfs` | wlmscpfs | Worklist Management SCP | [servers.md](docs/servers.md#wlmscpfs) |
|
|
134
|
+
| `DcmprsCP` | dcmprscp | Print Management SCP | [servers.md](docs/servers.md#dcmprscp) |
|
|
135
|
+
| `Dcmpsrcv` | dcmpsrcv | Viewer network receiver | [servers.md](docs/servers.md#dcmpsrcv) |
|
|
136
|
+
| `DicomReceiver` | dcmrecv (pool) | Pooled receiver with auto-scaling workers | [servers.md](docs/servers.md#dicomreceiver) |
|
|
137
|
+
| `DicomSender` | storescu (pool) | High-throughput sender with backpressure | [senders.md](docs/senders.md) |
|
|
133
138
|
|
|
134
139
|
## License
|
|
135
140
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { R as Result } from './types-Cgumy1N4.js';
|
|
2
|
-
import { D as DicomJsonElement, T as TagModification } from './dcmodify-
|
|
2
|
+
import { D as DicomJsonElement, T as TagModification } from './dcmodify-BvaIeyJg.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Branded types for domain primitives.
|
|
@@ -90,7 +90,7 @@ declare function createDicomTag(input: string): Result<DicomTag>;
|
|
|
90
90
|
/**
|
|
91
91
|
* Creates a validated AETitle from a raw string.
|
|
92
92
|
*
|
|
93
|
-
* @param input - A string expected to be 1-16
|
|
93
|
+
* @param input - A string expected to be 1-16 printable ASCII chars (no backslash)
|
|
94
94
|
* @returns A Result containing the branded AETitle or an error
|
|
95
95
|
*/
|
|
96
96
|
declare function createAETitle(input: string): Result<AETitle>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { R as Result } from './types-Cgumy1N4.cjs';
|
|
2
|
-
import { D as DicomJsonElement, T as TagModification } from './dcmodify-
|
|
2
|
+
import { D as DicomJsonElement, T as TagModification } from './dcmodify-B9js5K1f.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Branded types for domain primitives.
|
|
@@ -90,7 +90,7 @@ declare function createDicomTag(input: string): Result<DicomTag>;
|
|
|
90
90
|
/**
|
|
91
91
|
* Creates a validated AETitle from a raw string.
|
|
92
92
|
*
|
|
93
|
-
* @param input - A string expected to be 1-16
|
|
93
|
+
* @param input - A string expected to be 1-16 printable ASCII chars (no backslash)
|
|
94
94
|
* @returns A Result containing the branded AETitle or an error
|
|
95
95
|
*/
|
|
96
96
|
declare function createAETitle(input: string): Result<AETitle>;
|
|
@@ -94,6 +94,8 @@ interface DcmodifyOptions extends ToolBaseOptions {
|
|
|
94
94
|
readonly insertIfMissing?: boolean | undefined;
|
|
95
95
|
/** Treat 'tag not found' as success when erasing (uses -imt flag). Defaults to false. */
|
|
96
96
|
readonly ignoreMissingTags?: boolean | undefined;
|
|
97
|
+
/** Verbosity level for diagnostic output. `'verbose'` maps to `-v`, `'debug'` maps to `-d`. */
|
|
98
|
+
readonly verbosity?: 'verbose' | 'debug' | undefined;
|
|
97
99
|
}
|
|
98
100
|
/** Result of a successful dcmodify operation. */
|
|
99
101
|
interface DcmodifyResult {
|
|
@@ -94,6 +94,8 @@ interface DcmodifyOptions extends ToolBaseOptions {
|
|
|
94
94
|
readonly insertIfMissing?: boolean | undefined;
|
|
95
95
|
/** Treat 'tag not found' as success when erasing (uses -imt flag). Defaults to false. */
|
|
96
96
|
readonly ignoreMissingTags?: boolean | undefined;
|
|
97
|
+
/** Verbosity level for diagnostic output. `'verbose'` maps to `-v`, `'debug'` maps to `-d`. */
|
|
98
|
+
readonly verbosity?: 'verbose' | 'debug' | undefined;
|
|
97
99
|
}
|
|
98
100
|
/** Result of a successful dcmodify operation. */
|
|
99
101
|
interface DcmodifyResult {
|
package/dist/dicom.cjs
CHANGED
|
@@ -30718,19 +30718,28 @@ function repairJson(raw) {
|
|
|
30718
30718
|
var Dcm2jsonOptionsSchema = zod.z.object({
|
|
30719
30719
|
timeoutMs: zod.z.number().int().positive().optional(),
|
|
30720
30720
|
signal: zod.z.instanceof(AbortSignal).optional(),
|
|
30721
|
-
directOnly: zod.z.boolean().optional()
|
|
30721
|
+
directOnly: zod.z.boolean().optional(),
|
|
30722
|
+
verbosity: zod.z.enum(["verbose", "debug"]).optional()
|
|
30722
30723
|
}).strict().optional();
|
|
30723
|
-
|
|
30724
|
+
var VERBOSITY_FLAGS = { verbose: "-v", debug: "-d" };
|
|
30725
|
+
function buildVerbosityArgs(verbosity) {
|
|
30726
|
+
if (verbosity !== void 0) {
|
|
30727
|
+
return [VERBOSITY_FLAGS[verbosity]];
|
|
30728
|
+
}
|
|
30729
|
+
return [];
|
|
30730
|
+
}
|
|
30731
|
+
async function tryXmlPath(inputPath, timeoutMs, signal, verbosity) {
|
|
30724
30732
|
const xmlBinary = resolveBinary("dcm2xml");
|
|
30725
30733
|
if (!xmlBinary.ok) {
|
|
30726
30734
|
return err(xmlBinary.error);
|
|
30727
30735
|
}
|
|
30728
|
-
const
|
|
30736
|
+
const xmlArgs = [...buildVerbosityArgs(verbosity), "-nat", inputPath];
|
|
30737
|
+
const xmlResult = await execCommand(xmlBinary.value, xmlArgs, { timeoutMs, signal });
|
|
30729
30738
|
if (!xmlResult.ok) {
|
|
30730
30739
|
return err(xmlResult.error);
|
|
30731
30740
|
}
|
|
30732
30741
|
if (xmlResult.value.exitCode !== 0) {
|
|
30733
|
-
return err(createToolError("dcm2xml",
|
|
30742
|
+
return err(createToolError("dcm2xml", xmlArgs, xmlResult.value.exitCode, xmlResult.value.stderr));
|
|
30734
30743
|
}
|
|
30735
30744
|
const jsonResult = xmlToJson(xmlResult.value.stdout);
|
|
30736
30745
|
if (!jsonResult.ok) {
|
|
@@ -30738,17 +30747,18 @@ async function tryXmlPath(inputPath, timeoutMs, signal) {
|
|
|
30738
30747
|
}
|
|
30739
30748
|
return ok({ data: jsonResult.value, source: "xml" });
|
|
30740
30749
|
}
|
|
30741
|
-
async function tryDirectPath(inputPath, timeoutMs, signal) {
|
|
30750
|
+
async function tryDirectPath(inputPath, timeoutMs, signal, verbosity) {
|
|
30742
30751
|
const jsonBinary = resolveBinary("dcm2json");
|
|
30743
30752
|
if (!jsonBinary.ok) {
|
|
30744
30753
|
return err(jsonBinary.error);
|
|
30745
30754
|
}
|
|
30746
|
-
const
|
|
30755
|
+
const directArgs = [...buildVerbosityArgs(verbosity), inputPath];
|
|
30756
|
+
const result = await execCommand(jsonBinary.value, directArgs, { timeoutMs, signal });
|
|
30747
30757
|
if (!result.ok) {
|
|
30748
30758
|
return err(result.error);
|
|
30749
30759
|
}
|
|
30750
30760
|
if (result.value.exitCode !== 0) {
|
|
30751
|
-
return err(createToolError("dcm2json",
|
|
30761
|
+
return err(createToolError("dcm2json", directArgs, result.value.exitCode, result.value.stderr));
|
|
30752
30762
|
}
|
|
30753
30763
|
try {
|
|
30754
30764
|
const repaired = repairJson(result.value.stdout);
|
|
@@ -30765,20 +30775,22 @@ async function dcm2json(inputPath, options) {
|
|
|
30765
30775
|
}
|
|
30766
30776
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
30767
30777
|
const signal = options?.signal;
|
|
30778
|
+
const verbosity = options?.verbosity;
|
|
30768
30779
|
if (options?.directOnly === true) {
|
|
30769
|
-
return tryDirectPath(inputPath, timeoutMs, signal);
|
|
30780
|
+
return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
|
|
30770
30781
|
}
|
|
30771
|
-
const xmlResult = await tryXmlPath(inputPath, timeoutMs, signal);
|
|
30782
|
+
const xmlResult = await tryXmlPath(inputPath, timeoutMs, signal, verbosity);
|
|
30772
30783
|
if (xmlResult.ok) {
|
|
30773
30784
|
return xmlResult;
|
|
30774
30785
|
}
|
|
30775
|
-
return tryDirectPath(inputPath, timeoutMs, signal);
|
|
30786
|
+
return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
|
|
30776
30787
|
}
|
|
30777
30788
|
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+\])?)*)?$/;
|
|
30778
30789
|
var TagModificationSchema = zod.z.object({
|
|
30779
30790
|
tag: zod.z.string().regex(TAG_OR_PATH_PATTERN),
|
|
30780
30791
|
value: zod.z.string()
|
|
30781
30792
|
});
|
|
30793
|
+
var VERBOSITY_FLAGS2 = { verbose: "-v", debug: "-d" };
|
|
30782
30794
|
var DcmodifyOptionsSchema = zod.z.object({
|
|
30783
30795
|
timeoutMs: zod.z.number().int().positive().optional(),
|
|
30784
30796
|
signal: zod.z.instanceof(AbortSignal).optional(),
|
|
@@ -30787,12 +30799,16 @@ var DcmodifyOptionsSchema = zod.z.object({
|
|
|
30787
30799
|
erasePrivateTags: zod.z.boolean().optional(),
|
|
30788
30800
|
noBackup: zod.z.boolean().optional(),
|
|
30789
30801
|
insertIfMissing: zod.z.boolean().optional(),
|
|
30790
|
-
ignoreMissingTags: zod.z.boolean().optional()
|
|
30802
|
+
ignoreMissingTags: zod.z.boolean().optional(),
|
|
30803
|
+
verbosity: zod.z.enum(["verbose", "debug"]).optional()
|
|
30791
30804
|
}).strict().refine((data) => data.modifications.length > 0 || data.erasures !== void 0 && data.erasures.length > 0 || data.erasePrivateTags === true, {
|
|
30792
30805
|
message: "At least one of modifications, erasures, or erasePrivateTags is required"
|
|
30793
30806
|
});
|
|
30794
30807
|
function buildArgs(inputPath, options) {
|
|
30795
30808
|
const args = [];
|
|
30809
|
+
if (options.verbosity !== void 0) {
|
|
30810
|
+
args.push(VERBOSITY_FLAGS2[options.verbosity]);
|
|
30811
|
+
}
|
|
30796
30812
|
if (options.noBackup !== false) {
|
|
30797
30813
|
args.push("-nb");
|
|
30798
30814
|
}
|