@ubercode/dcmtk 0.13.2 → 0.15.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/dist/dicom.cjs +50 -36
- package/dist/dicom.cjs.map +1 -1
- package/dist/dicom.js +50 -36
- package/dist/dicom.js.map +1 -1
- package/dist/{index-Bve4fMIh.d.cts → index-CtqMUrjP.d.cts} +2 -0
- package/dist/{index-CZiFWENk.d.ts → index-D7nnTY1R.d.ts} +2 -0
- package/dist/index.cjs +193 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +193 -66
- package/dist/index.js.map +1 -1
- package/dist/servers.cjs +41 -7
- package/dist/servers.cjs.map +1 -1
- package/dist/servers.d.cts +1 -1
- package/dist/servers.d.ts +1 -1
- package/dist/servers.js +41 -7
- package/dist/servers.js.map +1 -1
- package/dist/tools.cjs +57 -40
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +18 -1
- package/dist/tools.d.ts +18 -1
- package/dist/tools.js +57 -41
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -1777,6 +1777,8 @@ declare class DicomReceiver extends EventEmitter<DicomReceiverEventMap> {
|
|
|
1777
1777
|
private startTcpProxy;
|
|
1778
1778
|
/** Closes the TCP proxy server. */
|
|
1779
1779
|
private closeTcpProxy;
|
|
1780
|
+
/** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
|
|
1781
|
+
private drainAllWorkers;
|
|
1780
1782
|
/** Routes an incoming connection to an idle worker. */
|
|
1781
1783
|
private handleConnection;
|
|
1782
1784
|
/** Finds an idle worker, retrying up to connectionTimeoutMs. */
|
|
@@ -1777,6 +1777,8 @@ declare class DicomReceiver extends EventEmitter<DicomReceiverEventMap> {
|
|
|
1777
1777
|
private startTcpProxy;
|
|
1778
1778
|
/** Closes the TCP proxy server. */
|
|
1779
1779
|
private closeTcpProxy;
|
|
1780
|
+
/** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
|
|
1781
|
+
private drainAllWorkers;
|
|
1780
1782
|
/** Routes an incoming connection to an idle worker. */
|
|
1781
1783
|
private handleConnection;
|
|
1782
1784
|
/** Finds an idle worker, retrying up to connectionTimeoutMs. */
|
package/dist/index.cjs
CHANGED
|
@@ -713,6 +713,51 @@ var LineParser = class extends events.EventEmitter {
|
|
|
713
713
|
}
|
|
714
714
|
};
|
|
715
715
|
|
|
716
|
+
// src/tools/_toolError.ts
|
|
717
|
+
var MAX_ARGS_LENGTH = 200;
|
|
718
|
+
var MAX_STDERR_LENGTH = 500;
|
|
719
|
+
function truncate(value, maxLength) {
|
|
720
|
+
if (value.length <= maxLength) {
|
|
721
|
+
return value;
|
|
722
|
+
}
|
|
723
|
+
return `${value.substring(0, maxLength)}...`;
|
|
724
|
+
}
|
|
725
|
+
var ToolExecutionError = class extends Error {
|
|
726
|
+
constructor(message, details) {
|
|
727
|
+
super(message);
|
|
728
|
+
__publicField(this, "stdout");
|
|
729
|
+
__publicField(this, "stderr");
|
|
730
|
+
__publicField(this, "exitCode");
|
|
731
|
+
this.name = "ToolExecutionError";
|
|
732
|
+
this.stdout = details.stdout;
|
|
733
|
+
this.stderr = details.stderr;
|
|
734
|
+
this.exitCode = details.exitCode;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
function createToolError(toolName, args, exitCode, stderr8, stdout = "") {
|
|
738
|
+
const argsStr = truncate(args.join(" "), MAX_ARGS_LENGTH);
|
|
739
|
+
const stderrStr = truncate(stderr8.trim(), MAX_STDERR_LENGTH);
|
|
740
|
+
const parts = [`${toolName} failed (exit code ${String(exitCode)})`];
|
|
741
|
+
if (argsStr.length > 0) {
|
|
742
|
+
parts.push(`args: ${argsStr}`);
|
|
743
|
+
}
|
|
744
|
+
if (stderrStr.length > 0) {
|
|
745
|
+
parts.push(`stderr: ${stderrStr}`);
|
|
746
|
+
}
|
|
747
|
+
return new ToolExecutionError(parts.join(" | "), { stdout, stderr: stderr8, exitCode });
|
|
748
|
+
}
|
|
749
|
+
function createValidationError(toolName, zodError) {
|
|
750
|
+
const parts = [];
|
|
751
|
+
for (let i = 0; i < zodError.issues.length; i++) {
|
|
752
|
+
const issue = zodError.issues[i];
|
|
753
|
+
if (issue === void 0) continue;
|
|
754
|
+
const path2 = issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)";
|
|
755
|
+
parts.push(`${path2}: ${issue.message}`);
|
|
756
|
+
}
|
|
757
|
+
const detail = parts.length > 0 ? parts.join("; ") : "unknown validation error";
|
|
758
|
+
return new Error(`${toolName}: invalid options \u2014 ${detail}`);
|
|
759
|
+
}
|
|
760
|
+
|
|
716
761
|
// src/dicom/vr.ts
|
|
717
762
|
var VR = {
|
|
718
763
|
/** Application Entity — 16 bytes max, leading/trailing spaces significant */
|
|
@@ -31136,39 +31181,6 @@ function resolveBinary(toolName) {
|
|
|
31136
31181
|
return ok(path.join(pathResult.value, binaryName2));
|
|
31137
31182
|
}
|
|
31138
31183
|
|
|
31139
|
-
// src/tools/_toolError.ts
|
|
31140
|
-
var MAX_ARGS_LENGTH = 200;
|
|
31141
|
-
var MAX_STDERR_LENGTH = 500;
|
|
31142
|
-
function truncate(value, maxLength) {
|
|
31143
|
-
if (value.length <= maxLength) {
|
|
31144
|
-
return value;
|
|
31145
|
-
}
|
|
31146
|
-
return `${value.substring(0, maxLength)}...`;
|
|
31147
|
-
}
|
|
31148
|
-
function createToolError(toolName, args, exitCode, stderr8) {
|
|
31149
|
-
const argsStr = truncate(args.join(" "), MAX_ARGS_LENGTH);
|
|
31150
|
-
const stderrStr = truncate(stderr8.trim(), MAX_STDERR_LENGTH);
|
|
31151
|
-
const parts = [`${toolName} failed (exit code ${String(exitCode)})`];
|
|
31152
|
-
if (argsStr.length > 0) {
|
|
31153
|
-
parts.push(`args: ${argsStr}`);
|
|
31154
|
-
}
|
|
31155
|
-
if (stderrStr.length > 0) {
|
|
31156
|
-
parts.push(`stderr: ${stderrStr}`);
|
|
31157
|
-
}
|
|
31158
|
-
return new Error(parts.join(" | "));
|
|
31159
|
-
}
|
|
31160
|
-
function createValidationError(toolName, zodError) {
|
|
31161
|
-
const parts = [];
|
|
31162
|
-
for (let i = 0; i < zodError.issues.length; i++) {
|
|
31163
|
-
const issue = zodError.issues[i];
|
|
31164
|
-
if (issue === void 0) continue;
|
|
31165
|
-
const path2 = issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)";
|
|
31166
|
-
parts.push(`${path2}: ${issue.message}`);
|
|
31167
|
-
}
|
|
31168
|
-
const detail = parts.length > 0 ? parts.join("; ") : "unknown validation error";
|
|
31169
|
-
return new Error(`${toolName}: invalid options \u2014 ${detail}`);
|
|
31170
|
-
}
|
|
31171
|
-
|
|
31172
31184
|
// src/tools/dcm2xml.ts
|
|
31173
31185
|
var Dcm2xmlCharset = {
|
|
31174
31186
|
/** Use UTF-8 encoding (default). */
|
|
@@ -31552,15 +31564,17 @@ async function dcm2json(inputPath, options) {
|
|
|
31552
31564
|
}
|
|
31553
31565
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
31554
31566
|
const signal = options?.signal;
|
|
31567
|
+
const startTime = Date.now();
|
|
31568
|
+
const remaining = () => Math.max(1e3, timeoutMs - (Date.now() - startTime));
|
|
31555
31569
|
const verbosity = options?.verbosity;
|
|
31556
31570
|
if (options?.directOnly === true) {
|
|
31557
|
-
return tryDirectPath(inputPath,
|
|
31571
|
+
return tryDirectPath(inputPath, remaining(), signal, verbosity);
|
|
31558
31572
|
}
|
|
31559
|
-
const xmlResult = await tryXmlPath(inputPath,
|
|
31573
|
+
const xmlResult = await tryXmlPath(inputPath, remaining(), signal, buildXmlOpts(options));
|
|
31560
31574
|
if (xmlResult.ok) {
|
|
31561
31575
|
return xmlResult;
|
|
31562
31576
|
}
|
|
31563
|
-
return tryDirectPath(inputPath,
|
|
31577
|
+
return tryDirectPath(inputPath, remaining(), signal, verbosity);
|
|
31564
31578
|
}
|
|
31565
31579
|
var DcmdumpFormat = {
|
|
31566
31580
|
/** Print standard DCMTK format. */
|
|
@@ -33288,7 +33302,7 @@ async function dcmsend(options) {
|
|
|
33288
33302
|
return err(result.error);
|
|
33289
33303
|
}
|
|
33290
33304
|
if (result.value.exitCode !== 0) {
|
|
33291
|
-
return err(createToolError("dcmsend", args, result.value.exitCode, result.value.stderr));
|
|
33305
|
+
return err(createToolError("dcmsend", args, result.value.exitCode, result.value.stderr, result.value.stdout));
|
|
33292
33306
|
}
|
|
33293
33307
|
return ok({ success: true, stdout: result.value.stdout, stderr: result.value.stderr });
|
|
33294
33308
|
}
|
|
@@ -33443,10 +33457,10 @@ async function storescu(options) {
|
|
|
33443
33457
|
return err(result.error);
|
|
33444
33458
|
}
|
|
33445
33459
|
if (result.value.exitCode !== 0) {
|
|
33446
|
-
return err(createToolError("storescu", args, result.value.exitCode, result.value.stderr));
|
|
33460
|
+
return err(createToolError("storescu", args, result.value.exitCode, result.value.stderr, result.value.stdout));
|
|
33447
33461
|
}
|
|
33448
33462
|
if (DIMSE_ERROR_PATTERN.test(result.value.stderr)) {
|
|
33449
|
-
return err(createToolError("storescu", args, 0, result.value.stderr));
|
|
33463
|
+
return err(createToolError("storescu", args, 0, result.value.stderr, result.value.stdout));
|
|
33450
33464
|
}
|
|
33451
33465
|
return ok({ success: true, stdout: result.value.stdout, stderr: result.value.stderr });
|
|
33452
33466
|
}
|
|
@@ -36517,7 +36531,7 @@ var Worker = class {
|
|
|
36517
36531
|
this.port = port;
|
|
36518
36532
|
this.tempDir = tempDir;
|
|
36519
36533
|
}
|
|
36520
|
-
/** Current worker state. */
|
|
36534
|
+
/** Current worker state: idle (ready), busy (handling association), finalizing (draining). */
|
|
36521
36535
|
get state() {
|
|
36522
36536
|
return this._state;
|
|
36523
36537
|
}
|
|
@@ -36549,6 +36563,10 @@ var Worker = class {
|
|
|
36549
36563
|
this._instancesReceived = 0;
|
|
36550
36564
|
this._instanceErrors = 0;
|
|
36551
36565
|
}
|
|
36566
|
+
/** Marks the worker as finalizing — still has valid context but cannot accept new connections. */
|
|
36567
|
+
markFinalizing() {
|
|
36568
|
+
this._state = "finalizing";
|
|
36569
|
+
}
|
|
36552
36570
|
/**
|
|
36553
36571
|
* Returns the worker to idle. Context is intentionally preserved so that
|
|
36554
36572
|
* late FILE_RECEIVED events (arriving in a subsequent pipe chunk after
|
|
@@ -36731,6 +36749,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
|
|
|
36731
36749
|
this.options.signal.removeEventListener("abort", this.abortHandler);
|
|
36732
36750
|
}
|
|
36733
36751
|
await this.closeTcpProxy();
|
|
36752
|
+
await this.drainAllWorkers();
|
|
36734
36753
|
const stopPromises = [];
|
|
36735
36754
|
for (const worker of this.workers.values()) {
|
|
36736
36755
|
stopPromises.push(this.stopWorker(worker));
|
|
@@ -36899,6 +36918,18 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
|
|
|
36899
36918
|
this.tcpServer.close(() => resolve());
|
|
36900
36919
|
});
|
|
36901
36920
|
}
|
|
36921
|
+
/** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
|
|
36922
|
+
async drainAllWorkers() {
|
|
36923
|
+
const drainPromises = [];
|
|
36924
|
+
for (const worker of this.workers.values()) {
|
|
36925
|
+
if (worker.state !== "idle") {
|
|
36926
|
+
drainPromises.push(worker.drainPendingFiles());
|
|
36927
|
+
drainPromises.push(worker.drainInstancePending());
|
|
36928
|
+
}
|
|
36929
|
+
}
|
|
36930
|
+
if (drainPromises.length === 0) return;
|
|
36931
|
+
await Promise.race([Promise.all(drainPromises), promises.setTimeout(5e3)]);
|
|
36932
|
+
}
|
|
36902
36933
|
// -----------------------------------------------------------------------
|
|
36903
36934
|
// Connection routing
|
|
36904
36935
|
// -----------------------------------------------------------------------
|
|
@@ -36927,7 +36958,9 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
|
|
|
36927
36958
|
};
|
|
36928
36959
|
worker.beginAssociation(ctx);
|
|
36929
36960
|
this.pipeConnection(worker, remoteSocket);
|
|
36930
|
-
void this.replenishPool()
|
|
36961
|
+
void this.replenishPool().catch((e) => {
|
|
36962
|
+
this.emit("error", { error: e instanceof Error ? e : new Error(String(e)) });
|
|
36963
|
+
});
|
|
36931
36964
|
}
|
|
36932
36965
|
/** Finds an idle worker, retrying up to connectionTimeoutMs. */
|
|
36933
36966
|
async findIdleWorker() {
|
|
@@ -37166,6 +37199,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
|
|
|
37166
37199
|
/** Returns worker to idle pool on association complete, emits summary. */
|
|
37167
37200
|
wireAssociationComplete(worker) {
|
|
37168
37201
|
worker.dcmrecv.onAssociationComplete((data) => {
|
|
37202
|
+
worker.markFinalizing();
|
|
37169
37203
|
setImmediate(() => {
|
|
37170
37204
|
void this.finalizeAssociation(worker, data);
|
|
37171
37205
|
});
|
|
@@ -37319,8 +37353,14 @@ var SenderEngine = class {
|
|
|
37319
37353
|
// Bucket state (bucket mode only)
|
|
37320
37354
|
__publicField(this, "currentBucket", []);
|
|
37321
37355
|
__publicField(this, "bucketTimer");
|
|
37356
|
+
// Abort wiring: stopController fires on stop(); combinedSignal also fires
|
|
37357
|
+
// when the externally provided config.signal aborts. Passed into the
|
|
37358
|
+
// executor and into delayInterruptible so stop() unblocks promptly.
|
|
37359
|
+
__publicField(this, "stopController", new AbortController());
|
|
37360
|
+
__publicField(this, "combinedSignal");
|
|
37322
37361
|
this.config = config;
|
|
37323
37362
|
this.effectiveMaxAssociations = config.configuredMaxAssociations;
|
|
37363
|
+
this.combinedSignal = combineSignals(this.stopController.signal, config.signal);
|
|
37324
37364
|
}
|
|
37325
37365
|
// -----------------------------------------------------------------------
|
|
37326
37366
|
// Public API
|
|
@@ -37346,6 +37386,7 @@ var SenderEngine = class {
|
|
|
37346
37386
|
async stop() {
|
|
37347
37387
|
if (this.isStopped) return;
|
|
37348
37388
|
this.isStopped = true;
|
|
37389
|
+
this.stopController.abort();
|
|
37349
37390
|
this.clearBucketTimer();
|
|
37350
37391
|
this.rejectBucket(`${this.config.senderName}: sender stopped`);
|
|
37351
37392
|
this.rejectQueue(`${this.config.senderName}: sender stopped`);
|
|
@@ -37462,18 +37503,48 @@ var SenderEngine = class {
|
|
|
37462
37503
|
}
|
|
37463
37504
|
};
|
|
37464
37505
|
}
|
|
37465
|
-
/**
|
|
37506
|
+
/**
|
|
37507
|
+
* Flushes the current bucket. Entries with the same bucketParamsKey merge
|
|
37508
|
+
* into one underlying call; differing keys split into separate calls,
|
|
37509
|
+
* preserving first-occurrence order. Each group emits its own
|
|
37510
|
+
* BUCKET_FLUSHED. If the number of groups exceeds available capacity,
|
|
37511
|
+
* the surplus is queued and drains as capacity frees.
|
|
37512
|
+
*/
|
|
37466
37513
|
flushBucketInternal(reason) {
|
|
37467
37514
|
if (this.currentBucket.length === 0) return;
|
|
37468
37515
|
const entries = [...this.currentBucket];
|
|
37469
37516
|
this.currentBucket = [];
|
|
37470
|
-
const
|
|
37471
|
-
|
|
37472
|
-
|
|
37473
|
-
|
|
37474
|
-
|
|
37475
|
-
|
|
37517
|
+
const groups = this.groupBucketByParams(entries);
|
|
37518
|
+
for (const group of groups) {
|
|
37519
|
+
const bucketEntry = this.mergeBucketEntries(group);
|
|
37520
|
+
this.config.emitters.emitBucketFlushed({ fileCount: bucketEntry.files.length, reason });
|
|
37521
|
+
if (this.activeAssociations < this.effectiveMaxAssociations) {
|
|
37522
|
+
void this.executeEntry(bucketEntry);
|
|
37523
|
+
} else {
|
|
37524
|
+
this.queue.push(bucketEntry);
|
|
37525
|
+
}
|
|
37526
|
+
}
|
|
37527
|
+
}
|
|
37528
|
+
/**
|
|
37529
|
+
* Groups bucket entries by bucketParamsKey while preserving first-occurrence
|
|
37530
|
+
* order. Without a key function, returns a single group containing every
|
|
37531
|
+
* entry (current behavior for callers that don't vary binaryParams).
|
|
37532
|
+
*/
|
|
37533
|
+
groupBucketByParams(entries) {
|
|
37534
|
+
const keyFn = this.config.bucketParamsKey;
|
|
37535
|
+
if (keyFn === void 0) return [entries.slice()];
|
|
37536
|
+
const groups = /* @__PURE__ */ new Map();
|
|
37537
|
+
for (let i = 0; i < entries.length; i++) {
|
|
37538
|
+
const entry = entries[i];
|
|
37539
|
+
const key = keyFn(entry.binaryParams);
|
|
37540
|
+
let group = groups.get(key);
|
|
37541
|
+
if (group === void 0) {
|
|
37542
|
+
group = [];
|
|
37543
|
+
groups.set(key, group);
|
|
37544
|
+
}
|
|
37545
|
+
group.push(entry);
|
|
37476
37546
|
}
|
|
37547
|
+
return Array.from(groups.values());
|
|
37477
37548
|
}
|
|
37478
37549
|
/** Resets the bucket flush timer. */
|
|
37479
37550
|
resetBucketTimer() {
|
|
@@ -37515,25 +37586,33 @@ var SenderEngine = class {
|
|
|
37515
37586
|
/** Attempts the binary call up to maxAttempts times. Returns undefined on success. */
|
|
37516
37587
|
async attemptSend(entry, maxAttempts, startMs) {
|
|
37517
37588
|
let lastError;
|
|
37518
|
-
|
|
37589
|
+
let lastOutput = { stdout: "", stderr: "" };
|
|
37519
37590
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
37520
37591
|
if (this.isStopped) {
|
|
37521
|
-
this.
|
|
37522
|
-
entry.resolve(err(new Error(`${this.config.senderName}: sender stopped`)));
|
|
37523
|
-
return void 0;
|
|
37592
|
+
return this.resolveStopped(entry);
|
|
37524
37593
|
}
|
|
37525
|
-
const
|
|
37526
|
-
|
|
37527
|
-
|
|
37594
|
+
const outcome = await this.config.executor(entry.files, entry.timeoutMs, entry.binaryParams, this.combinedSignal);
|
|
37595
|
+
lastOutput = { stdout: outcome.stdout, stderr: outcome.stderr };
|
|
37596
|
+
if (outcome.error === void 0) {
|
|
37597
|
+
this.handleSendSuccess(entry, startMs, lastOutput);
|
|
37528
37598
|
return void 0;
|
|
37529
37599
|
}
|
|
37530
|
-
|
|
37600
|
+
if (this.isStopped) {
|
|
37601
|
+
return this.resolveStopped(entry);
|
|
37602
|
+
}
|
|
37603
|
+
lastError = outcome.error;
|
|
37531
37604
|
if (attempt < maxAttempts - 1) {
|
|
37532
|
-
await
|
|
37605
|
+
await delayInterruptible(this.config.retryDelayMs * (attempt + 1), this.combinedSignal);
|
|
37533
37606
|
}
|
|
37534
37607
|
}
|
|
37535
37608
|
return { error: lastError ?? new Error(`${this.config.senderName}: send failed`), output: lastOutput };
|
|
37536
37609
|
}
|
|
37610
|
+
/** Resolves the entry as stopped and decrements activeAssociations. Used from attemptSend. */
|
|
37611
|
+
resolveStopped(entry) {
|
|
37612
|
+
this.activeAssociations--;
|
|
37613
|
+
entry.resolve(err(new Error(`${this.config.senderName}: sender stopped`)));
|
|
37614
|
+
return void 0;
|
|
37615
|
+
}
|
|
37537
37616
|
/** Handles a successful send: updates state, emits event, resolves promise. */
|
|
37538
37617
|
handleSendSuccess(entry, startMs, output) {
|
|
37539
37618
|
this.activeAssociations--;
|
|
@@ -37639,11 +37718,41 @@ var SenderEngine = class {
|
|
|
37639
37718
|
});
|
|
37640
37719
|
}
|
|
37641
37720
|
};
|
|
37642
|
-
function
|
|
37721
|
+
function delayInterruptible(ms, signal) {
|
|
37722
|
+
if (signal.aborted) return Promise.resolve();
|
|
37643
37723
|
return new Promise((resolve) => {
|
|
37644
|
-
|
|
37724
|
+
const onAbort = () => {
|
|
37725
|
+
clearTimeout(timer);
|
|
37726
|
+
resolve();
|
|
37727
|
+
};
|
|
37728
|
+
const timer = setTimeout(() => {
|
|
37729
|
+
signal.removeEventListener("abort", onAbort);
|
|
37730
|
+
resolve();
|
|
37731
|
+
}, ms);
|
|
37732
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
37645
37733
|
});
|
|
37646
37734
|
}
|
|
37735
|
+
function combineSignals(primary, external) {
|
|
37736
|
+
if (external === void 0) return primary;
|
|
37737
|
+
if (primary.aborted) return primary;
|
|
37738
|
+
if (external.aborted) {
|
|
37739
|
+
const c = new AbortController();
|
|
37740
|
+
c.abort(external.reason);
|
|
37741
|
+
return c.signal;
|
|
37742
|
+
}
|
|
37743
|
+
const merged = new AbortController();
|
|
37744
|
+
const onPrimary = () => {
|
|
37745
|
+
merged.abort(primary.reason);
|
|
37746
|
+
external.removeEventListener("abort", onExternal);
|
|
37747
|
+
};
|
|
37748
|
+
const onExternal = () => {
|
|
37749
|
+
merged.abort(external.reason);
|
|
37750
|
+
primary.removeEventListener("abort", onPrimary);
|
|
37751
|
+
};
|
|
37752
|
+
primary.addEventListener("abort", onPrimary, { once: true });
|
|
37753
|
+
external.addEventListener("abort", onExternal, { once: true });
|
|
37754
|
+
return merged.signal;
|
|
37755
|
+
}
|
|
37647
37756
|
|
|
37648
37757
|
// src/senders/DicomSender.ts
|
|
37649
37758
|
var DEFAULT_MAX_ASSOCIATIONS = 4;
|
|
@@ -37678,6 +37787,9 @@ var DicomSenderOptionsSchema = zod.z.object({
|
|
|
37678
37787
|
required: zod.z.boolean().optional(),
|
|
37679
37788
|
signal: zod.z.instanceof(AbortSignal).optional()
|
|
37680
37789
|
}).strict();
|
|
37790
|
+
function storescuBucketKey(p) {
|
|
37791
|
+
return JSON.stringify(p);
|
|
37792
|
+
}
|
|
37681
37793
|
function createStorescuExecutor(options) {
|
|
37682
37794
|
return async (files, timeoutMs, params, signal) => {
|
|
37683
37795
|
const result = await storescu({
|
|
@@ -37699,8 +37811,13 @@ function createStorescuExecutor(options) {
|
|
|
37699
37811
|
timeoutMs,
|
|
37700
37812
|
signal
|
|
37701
37813
|
});
|
|
37702
|
-
if (!result.ok)
|
|
37703
|
-
|
|
37814
|
+
if (!result.ok) {
|
|
37815
|
+
const e = result.error;
|
|
37816
|
+
const stdout = e instanceof ToolExecutionError ? e.stdout : "";
|
|
37817
|
+
const stderr8 = e instanceof ToolExecutionError ? e.stderr : "";
|
|
37818
|
+
return { stdout, stderr: stderr8, error: e };
|
|
37819
|
+
}
|
|
37820
|
+
return { stdout: result.value.stdout, stderr: result.value.stderr };
|
|
37704
37821
|
};
|
|
37705
37822
|
}
|
|
37706
37823
|
function resolveConfig2(options) {
|
|
@@ -37757,6 +37874,7 @@ var DicomSender = class _DicomSender extends events.EventEmitter {
|
|
|
37757
37874
|
executor: createStorescuExecutor(options),
|
|
37758
37875
|
signal: options.signal,
|
|
37759
37876
|
senderName: "DicomSender",
|
|
37877
|
+
bucketParamsKey: storescuBucketKey,
|
|
37760
37878
|
emitters: {
|
|
37761
37879
|
emitSendComplete: (data) => {
|
|
37762
37880
|
senderRef.current.emit("SEND_COMPLETE", data);
|
|
@@ -37925,6 +38043,9 @@ var DicomSendOptionsSchema = zod.z.object({
|
|
|
37925
38043
|
noUidChecks: zod.z.boolean().optional(),
|
|
37926
38044
|
signal: zod.z.instanceof(AbortSignal).optional()
|
|
37927
38045
|
}).strict();
|
|
38046
|
+
function dcmsendBucketKey(p) {
|
|
38047
|
+
return JSON.stringify(p);
|
|
38048
|
+
}
|
|
37928
38049
|
function createDcmsendExecutor(options) {
|
|
37929
38050
|
return async (files, timeoutMs, params, signal) => {
|
|
37930
38051
|
const result = await dcmsend({
|
|
@@ -37948,8 +38069,13 @@ function createDcmsendExecutor(options) {
|
|
|
37948
38069
|
timeoutMs,
|
|
37949
38070
|
signal
|
|
37950
38071
|
});
|
|
37951
|
-
if (!result.ok)
|
|
37952
|
-
|
|
38072
|
+
if (!result.ok) {
|
|
38073
|
+
const e = result.error;
|
|
38074
|
+
const stdout = e instanceof ToolExecutionError ? e.stdout : "";
|
|
38075
|
+
const stderr8 = e instanceof ToolExecutionError ? e.stderr : "";
|
|
38076
|
+
return { stdout, stderr: stderr8, error: e };
|
|
38077
|
+
}
|
|
38078
|
+
return { stdout: result.value.stdout, stderr: result.value.stderr };
|
|
37953
38079
|
};
|
|
37954
38080
|
}
|
|
37955
38081
|
function resolveConfig3(options) {
|
|
@@ -38006,6 +38132,7 @@ var DicomSend = class _DicomSend extends events.EventEmitter {
|
|
|
38006
38132
|
executor: createDcmsendExecutor(options),
|
|
38007
38133
|
signal: options.signal,
|
|
38008
38134
|
senderName: "DicomSend",
|
|
38135
|
+
bucketParamsKey: dcmsendBucketKey,
|
|
38009
38136
|
emitters: {
|
|
38010
38137
|
emitSendComplete: (data) => {
|
|
38011
38138
|
senderRef.current.emit("SEND_COMPLETE", data);
|
|
@@ -38693,7 +38820,7 @@ function computeThroughput(succeeded, totalBytes, durationMs) {
|
|
|
38693
38820
|
bytesPerSec: durationSec > 0 ? totalBytes / durationSec : 0
|
|
38694
38821
|
};
|
|
38695
38822
|
}
|
|
38696
|
-
function
|
|
38823
|
+
function delay2(ms) {
|
|
38697
38824
|
return new Promise((resolve) => {
|
|
38698
38825
|
setTimeout(resolve, ms);
|
|
38699
38826
|
});
|
|
@@ -38840,7 +38967,7 @@ var DicomHammer = class _DicomHammer extends events.EventEmitter {
|
|
|
38840
38967
|
/** Sends a single file, with optional delay. */
|
|
38841
38968
|
async sendOneFile(file) {
|
|
38842
38969
|
if (this.opts.delayMs > 0) {
|
|
38843
|
-
await
|
|
38970
|
+
await delay2(this.opts.delayMs);
|
|
38844
38971
|
}
|
|
38845
38972
|
const result = await dcmsend({
|
|
38846
38973
|
host: this.opts.host,
|
|
@@ -39007,6 +39134,7 @@ exports.StoreSCP = StoreSCP;
|
|
|
39007
39134
|
exports.StoreSCPPreset = StoreSCPPreset;
|
|
39008
39135
|
exports.StorescpEvent = StorescpEvent;
|
|
39009
39136
|
exports.SubdirectoryMode = SubdirectoryMode;
|
|
39137
|
+
exports.ToolExecutionError = ToolExecutionError;
|
|
39010
39138
|
exports.TransferSyntax = TransferSyntax;
|
|
39011
39139
|
exports.UIDSchema = UIDSchema;
|
|
39012
39140
|
exports.UNIX_SEARCH_PATHS = UNIX_SEARCH_PATHS;
|