@ubercode/dcmtk 0.15.0 → 0.16.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 +10 -4
- package/dist/dicom.cjs.map +1 -1
- package/dist/dicom.js +10 -4
- package/dist/dicom.js.map +1 -1
- package/dist/{index-D7nnTY1R.d.ts → index-C6dWOhHS.d.ts} +32 -1
- package/dist/{index-CtqMUrjP.d.cts → index-D1msvaAt.d.cts} +32 -1
- package/dist/index.cjs +148 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +148 -11
- package/dist/index.js.map +1 -1
- package/dist/servers.cjs +142 -10
- 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 +142 -10
- package/dist/servers.js.map +1 -1
- package/dist/tools.cjs +10 -4
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.js +10 -4
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ export { L as LineSource, P as ProcessLine, a as ResultValue, b as assertUnreach
|
|
|
3
3
|
import { A as AETitle, D as DicomTag, a as DicomTagPath, P as Port, S as SOPClassUID, T as TransferSyntaxUID, b as DicomDataset } from './DicomInstance-BGXtON9T.cjs';
|
|
4
4
|
export { B as Brand, C as ChangeSet, c as DicomFilePath, d as DicomInstance, e as DicomOpenOptions, W as WalkTagEntry, f as WalkTagsOptions, g as createAETitle, h as createDicomFilePath, i as createDicomTag, j as createDicomTagPath, k as createPort, l as createSOPClassUID, m as createTransferSyntaxUID, t as tag, w as walkTags } from './DicomInstance-BGXtON9T.cjs';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
export { A as AssociationAcknowledgedData, a as AssociationCompleteData, b as AssociationContext, c as AssociationReceivedData, d as AssociationSummary, e as AssociationTracker, C as CStoreRequestData, f as CannotStartListenerData, g as ConfigErrorData, D as DCMPRSCP_FATAL_EVENTS, h as DCMPRSCP_PATTERNS, i as DCMPSRCV_FATAL_EVENTS, j as DCMPSRCV_PATTERNS, k as DCMQRSCP_FATAL_EVENTS, l as DCMQRSCP_PATTERNS, m as DCMRECV_FATAL_EVENTS, n as DCMRECV_PATTERNS, o as DatabaseReadyData, p as DcmQRSCP, q as DcmQRSCPEventMap, r as DcmQRSCPOptions, s as DcmprsCP, t as DcmprsCPEventMap, u as DcmprsCPOptions, v as DcmprscpEvent, w as DcmprscpEventValue, x as Dcmpsrcv, y as DcmpsrcvEvent, z as DcmpsrcvEventMap, B as DcmpsrcvEventValue, E as DcmpsrcvOptions, F as DcmqrscpEvent, G as DcmqrscpEventValue, H as Dcmrecv, I as DcmrecvEvent, J as DcmrecvEventMap, K as DcmrecvEventValue, L as DcmrecvOptions, M as DcmtkProcess, N as DcmtkProcessConfig, O as DcmtkProcessEventMap, P as DicomReceiver, Q as DicomReceiverEventMap, R as DicomReceiverOptions, S as EventPattern, T as FileDeletedData, U as FileReceivedData, V as FilenameMode, W as FilenameModeValue, X as LineParser, Y as LineParserEventMap, Z as MultiLineConfig, _ as PoolAssociationReceivedData, $ as PoolCStoreRequestData, a0 as PoolEchoRequestData, a1 as PoolRefusingAssociationData, a2 as PoolStatus, a3 as PreferredTransferSyntax, a4 as PreferredTransferSyntaxValue, a5 as PrintAssociationAcknowledgedData, a6 as PrintAssociationReceivedData, a7 as PrintCannotStartListenerData, a8 as ProcessState, a9 as ProcessStateValue, aa as QRAssociationAcknowledgedData, ab as QRAssociationReceivedData, ac as QRCFindRequestData, ad as QRCGetRequestData, ae as QRCMoveRequestData, af as QRCStoreRequestData, ag as QRCannotStartListenerData, ah as QRListeningData, ai as ReceiverAssociationAcknowledgedData, aj as ReceiverAssociationData, ak as ReceiverAssociationFinalizedData, al as ReceiverAssociationReceivedData, am as ReceiverCStoreRequestData, an as ReceiverCannotStartListenerData, ao as ReceiverConfigErrorData, ap as ReceiverDatabaseReadyData, aq as ReceiverEchoRequestData, ar as ReceiverErrorData, as as ReceiverFileData, at as ReceiverFileReceivedData, au as ReceiverFileStoredData, av as ReceiverInstanceData, aw as ReceiverInstanceErrorData, ax as ReceiverListeningData, ay as RefusingAssociationData, az as STORESCP_FATAL_EVENTS, aA as STORESCP_PATTERNS, aB as StorageMode, aC as StorageModeValue, aD as StoreSCP, aE as StoreSCPEventMap, aF as StoreSCPOptions, aG as StoreSCPPreset, aH as StoreSCPPresetName, aI as StoredFileData, aJ as StorescpEvent, aK as StorescpEventValue, aL as StoringFileData, aM as SubdirectoryCreatedData, aN as SubdirectoryMode, aO as SubdirectoryModeValue, aP as TrackedFile, aQ as WLMSCPFS_FATAL_EVENTS, aR as WLMSCPFS_PATTERNS, aS as WlmAssociationAcknowledgedData, aT as WlmAssociationReceivedData, aU as WlmCFindRequestData, aV as WlmCannotStartListenerData, aW as WlmListeningData, aX as Wlmscpfs, aY as WlmscpfsEvent, aZ as WlmscpfsEventMap, a_ as WlmscpfsEventValue, a$ as WlmscpfsOptions } from './index-
|
|
6
|
+
export { A as AssociationAcknowledgedData, a as AssociationCompleteData, b as AssociationContext, c as AssociationReceivedData, d as AssociationSummary, e as AssociationTracker, C as CStoreRequestData, f as CannotStartListenerData, g as ConfigErrorData, D as DCMPRSCP_FATAL_EVENTS, h as DCMPRSCP_PATTERNS, i as DCMPSRCV_FATAL_EVENTS, j as DCMPSRCV_PATTERNS, k as DCMQRSCP_FATAL_EVENTS, l as DCMQRSCP_PATTERNS, m as DCMRECV_FATAL_EVENTS, n as DCMRECV_PATTERNS, o as DatabaseReadyData, p as DcmQRSCP, q as DcmQRSCPEventMap, r as DcmQRSCPOptions, s as DcmprsCP, t as DcmprsCPEventMap, u as DcmprsCPOptions, v as DcmprscpEvent, w as DcmprscpEventValue, x as Dcmpsrcv, y as DcmpsrcvEvent, z as DcmpsrcvEventMap, B as DcmpsrcvEventValue, E as DcmpsrcvOptions, F as DcmqrscpEvent, G as DcmqrscpEventValue, H as Dcmrecv, I as DcmrecvEvent, J as DcmrecvEventMap, K as DcmrecvEventValue, L as DcmrecvOptions, M as DcmtkProcess, N as DcmtkProcessConfig, O as DcmtkProcessEventMap, P as DicomReceiver, Q as DicomReceiverEventMap, R as DicomReceiverOptions, S as EventPattern, T as FileDeletedData, U as FileReceivedData, V as FilenameMode, W as FilenameModeValue, X as LineParser, Y as LineParserEventMap, Z as MultiLineConfig, _ as PoolAssociationReceivedData, $ as PoolCStoreRequestData, a0 as PoolEchoRequestData, a1 as PoolRefusingAssociationData, a2 as PoolStatus, a3 as PreferredTransferSyntax, a4 as PreferredTransferSyntaxValue, a5 as PrintAssociationAcknowledgedData, a6 as PrintAssociationReceivedData, a7 as PrintCannotStartListenerData, a8 as ProcessState, a9 as ProcessStateValue, aa as QRAssociationAcknowledgedData, ab as QRAssociationReceivedData, ac as QRCFindRequestData, ad as QRCGetRequestData, ae as QRCMoveRequestData, af as QRCStoreRequestData, ag as QRCannotStartListenerData, ah as QRListeningData, ai as ReceiverAssociationAcknowledgedData, aj as ReceiverAssociationData, ak as ReceiverAssociationFinalizedData, al as ReceiverAssociationReceivedData, am as ReceiverCStoreRequestData, an as ReceiverCannotStartListenerData, ao as ReceiverConfigErrorData, ap as ReceiverDatabaseReadyData, aq as ReceiverEchoRequestData, ar as ReceiverErrorData, as as ReceiverFileData, at as ReceiverFileReceivedData, au as ReceiverFileStoredData, av as ReceiverInstanceData, aw as ReceiverInstanceErrorData, ax as ReceiverListeningData, ay as RefusingAssociationData, az as STORESCP_FATAL_EVENTS, aA as STORESCP_PATTERNS, aB as StorageMode, aC as StorageModeValue, aD as StoreSCP, aE as StoreSCPEventMap, aF as StoreSCPOptions, aG as StoreSCPPreset, aH as StoreSCPPresetName, aI as StoredFileData, aJ as StorescpEvent, aK as StorescpEventValue, aL as StoringFileData, aM as SubdirectoryCreatedData, aN as SubdirectoryMode, aO as SubdirectoryModeValue, aP as TrackedFile, aQ as WLMSCPFS_FATAL_EVENTS, aR as WLMSCPFS_PATTERNS, aS as WlmAssociationAcknowledgedData, aT as WlmAssociationReceivedData, aU as WlmCFindRequestData, aV as WlmCannotStartListenerData, aW as WlmListeningData, aX as Wlmscpfs, aY as WlmscpfsEvent, aZ as WlmscpfsEventMap, a_ as WlmscpfsEventValue, a$ as WlmscpfsOptions } from './index-D1msvaAt.cjs';
|
|
7
7
|
import { T as TagModification } from './dcmodify-Cf-RPHF3.cjs';
|
|
8
8
|
export { b as DcmodifyOptions, c as DcmodifyResult, D as DicomJsonElement, a as DicomJsonModel, d as ToolBaseOptions, e as dcmodify, x as xmlToJson } from './dcmodify-Cf-RPHF3.cjs';
|
|
9
9
|
import { ProposedTransferSyntaxValue } from './tools.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { L as LineSource, P as ProcessLine, a as ResultValue, b as assertUnreach
|
|
|
3
3
|
import { A as AETitle, D as DicomTag, a as DicomTagPath, P as Port, S as SOPClassUID, T as TransferSyntaxUID, b as DicomDataset } from './DicomInstance-DGWB2mfD.js';
|
|
4
4
|
export { B as Brand, C as ChangeSet, c as DicomFilePath, d as DicomInstance, e as DicomOpenOptions, W as WalkTagEntry, f as WalkTagsOptions, g as createAETitle, h as createDicomFilePath, i as createDicomTag, j as createDicomTagPath, k as createPort, l as createSOPClassUID, m as createTransferSyntaxUID, t as tag, w as walkTags } from './DicomInstance-DGWB2mfD.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
export { A as AssociationAcknowledgedData, a as AssociationCompleteData, b as AssociationContext, c as AssociationReceivedData, d as AssociationSummary, e as AssociationTracker, C as CStoreRequestData, f as CannotStartListenerData, g as ConfigErrorData, D as DCMPRSCP_FATAL_EVENTS, h as DCMPRSCP_PATTERNS, i as DCMPSRCV_FATAL_EVENTS, j as DCMPSRCV_PATTERNS, k as DCMQRSCP_FATAL_EVENTS, l as DCMQRSCP_PATTERNS, m as DCMRECV_FATAL_EVENTS, n as DCMRECV_PATTERNS, o as DatabaseReadyData, p as DcmQRSCP, q as DcmQRSCPEventMap, r as DcmQRSCPOptions, s as DcmprsCP, t as DcmprsCPEventMap, u as DcmprsCPOptions, v as DcmprscpEvent, w as DcmprscpEventValue, x as Dcmpsrcv, y as DcmpsrcvEvent, z as DcmpsrcvEventMap, B as DcmpsrcvEventValue, E as DcmpsrcvOptions, F as DcmqrscpEvent, G as DcmqrscpEventValue, H as Dcmrecv, I as DcmrecvEvent, J as DcmrecvEventMap, K as DcmrecvEventValue, L as DcmrecvOptions, M as DcmtkProcess, N as DcmtkProcessConfig, O as DcmtkProcessEventMap, P as DicomReceiver, Q as DicomReceiverEventMap, R as DicomReceiverOptions, S as EventPattern, T as FileDeletedData, U as FileReceivedData, V as FilenameMode, W as FilenameModeValue, X as LineParser, Y as LineParserEventMap, Z as MultiLineConfig, _ as PoolAssociationReceivedData, $ as PoolCStoreRequestData, a0 as PoolEchoRequestData, a1 as PoolRefusingAssociationData, a2 as PoolStatus, a3 as PreferredTransferSyntax, a4 as PreferredTransferSyntaxValue, a5 as PrintAssociationAcknowledgedData, a6 as PrintAssociationReceivedData, a7 as PrintCannotStartListenerData, a8 as ProcessState, a9 as ProcessStateValue, aa as QRAssociationAcknowledgedData, ab as QRAssociationReceivedData, ac as QRCFindRequestData, ad as QRCGetRequestData, ae as QRCMoveRequestData, af as QRCStoreRequestData, ag as QRCannotStartListenerData, ah as QRListeningData, ai as ReceiverAssociationAcknowledgedData, aj as ReceiverAssociationData, ak as ReceiverAssociationFinalizedData, al as ReceiverAssociationReceivedData, am as ReceiverCStoreRequestData, an as ReceiverCannotStartListenerData, ao as ReceiverConfigErrorData, ap as ReceiverDatabaseReadyData, aq as ReceiverEchoRequestData, ar as ReceiverErrorData, as as ReceiverFileData, at as ReceiverFileReceivedData, au as ReceiverFileStoredData, av as ReceiverInstanceData, aw as ReceiverInstanceErrorData, ax as ReceiverListeningData, ay as RefusingAssociationData, az as STORESCP_FATAL_EVENTS, aA as STORESCP_PATTERNS, aB as StorageMode, aC as StorageModeValue, aD as StoreSCP, aE as StoreSCPEventMap, aF as StoreSCPOptions, aG as StoreSCPPreset, aH as StoreSCPPresetName, aI as StoredFileData, aJ as StorescpEvent, aK as StorescpEventValue, aL as StoringFileData, aM as SubdirectoryCreatedData, aN as SubdirectoryMode, aO as SubdirectoryModeValue, aP as TrackedFile, aQ as WLMSCPFS_FATAL_EVENTS, aR as WLMSCPFS_PATTERNS, aS as WlmAssociationAcknowledgedData, aT as WlmAssociationReceivedData, aU as WlmCFindRequestData, aV as WlmCannotStartListenerData, aW as WlmListeningData, aX as Wlmscpfs, aY as WlmscpfsEvent, aZ as WlmscpfsEventMap, a_ as WlmscpfsEventValue, a$ as WlmscpfsOptions } from './index-
|
|
6
|
+
export { A as AssociationAcknowledgedData, a as AssociationCompleteData, b as AssociationContext, c as AssociationReceivedData, d as AssociationSummary, e as AssociationTracker, C as CStoreRequestData, f as CannotStartListenerData, g as ConfigErrorData, D as DCMPRSCP_FATAL_EVENTS, h as DCMPRSCP_PATTERNS, i as DCMPSRCV_FATAL_EVENTS, j as DCMPSRCV_PATTERNS, k as DCMQRSCP_FATAL_EVENTS, l as DCMQRSCP_PATTERNS, m as DCMRECV_FATAL_EVENTS, n as DCMRECV_PATTERNS, o as DatabaseReadyData, p as DcmQRSCP, q as DcmQRSCPEventMap, r as DcmQRSCPOptions, s as DcmprsCP, t as DcmprsCPEventMap, u as DcmprsCPOptions, v as DcmprscpEvent, w as DcmprscpEventValue, x as Dcmpsrcv, y as DcmpsrcvEvent, z as DcmpsrcvEventMap, B as DcmpsrcvEventValue, E as DcmpsrcvOptions, F as DcmqrscpEvent, G as DcmqrscpEventValue, H as Dcmrecv, I as DcmrecvEvent, J as DcmrecvEventMap, K as DcmrecvEventValue, L as DcmrecvOptions, M as DcmtkProcess, N as DcmtkProcessConfig, O as DcmtkProcessEventMap, P as DicomReceiver, Q as DicomReceiverEventMap, R as DicomReceiverOptions, S as EventPattern, T as FileDeletedData, U as FileReceivedData, V as FilenameMode, W as FilenameModeValue, X as LineParser, Y as LineParserEventMap, Z as MultiLineConfig, _ as PoolAssociationReceivedData, $ as PoolCStoreRequestData, a0 as PoolEchoRequestData, a1 as PoolRefusingAssociationData, a2 as PoolStatus, a3 as PreferredTransferSyntax, a4 as PreferredTransferSyntaxValue, a5 as PrintAssociationAcknowledgedData, a6 as PrintAssociationReceivedData, a7 as PrintCannotStartListenerData, a8 as ProcessState, a9 as ProcessStateValue, aa as QRAssociationAcknowledgedData, ab as QRAssociationReceivedData, ac as QRCFindRequestData, ad as QRCGetRequestData, ae as QRCMoveRequestData, af as QRCStoreRequestData, ag as QRCannotStartListenerData, ah as QRListeningData, ai as ReceiverAssociationAcknowledgedData, aj as ReceiverAssociationData, ak as ReceiverAssociationFinalizedData, al as ReceiverAssociationReceivedData, am as ReceiverCStoreRequestData, an as ReceiverCannotStartListenerData, ao as ReceiverConfigErrorData, ap as ReceiverDatabaseReadyData, aq as ReceiverEchoRequestData, ar as ReceiverErrorData, as as ReceiverFileData, at as ReceiverFileReceivedData, au as ReceiverFileStoredData, av as ReceiverInstanceData, aw as ReceiverInstanceErrorData, ax as ReceiverListeningData, ay as RefusingAssociationData, az as STORESCP_FATAL_EVENTS, aA as STORESCP_PATTERNS, aB as StorageMode, aC as StorageModeValue, aD as StoreSCP, aE as StoreSCPEventMap, aF as StoreSCPOptions, aG as StoreSCPPreset, aH as StoreSCPPresetName, aI as StoredFileData, aJ as StorescpEvent, aK as StorescpEventValue, aL as StoringFileData, aM as SubdirectoryCreatedData, aN as SubdirectoryMode, aO as SubdirectoryModeValue, aP as TrackedFile, aQ as WLMSCPFS_FATAL_EVENTS, aR as WLMSCPFS_PATTERNS, aS as WlmAssociationAcknowledgedData, aT as WlmAssociationReceivedData, aU as WlmCFindRequestData, aV as WlmCannotStartListenerData, aW as WlmListeningData, aX as Wlmscpfs, aY as WlmscpfsEvent, aZ as WlmscpfsEventMap, a_ as WlmscpfsEventValue, a$ as WlmscpfsOptions } from './index-C6dWOhHS.js';
|
|
7
7
|
import { T as TagModification } from './dcmodify-CHvwChFu.js';
|
|
8
8
|
export { b as DcmodifyOptions, c as DcmodifyResult, D as DicomJsonElement, a as DicomJsonModel, d as ToolBaseOptions, e as dcmodify, x as xmlToJson } from './dcmodify-CHvwChFu.js';
|
|
9
9
|
import { ProposedTransferSyntaxValue } from './tools.js';
|
package/dist/index.js
CHANGED
|
@@ -31265,8 +31265,14 @@ var KNOWN_VR_CODES = /* @__PURE__ */ new Set([
|
|
|
31265
31265
|
"UT",
|
|
31266
31266
|
"UV"
|
|
31267
31267
|
]);
|
|
31268
|
+
function decodeXmlEntities(value) {
|
|
31269
|
+
return value.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
31270
|
+
}
|
|
31271
|
+
function decodeIfString(value) {
|
|
31272
|
+
return typeof value === "string" ? decodeXmlEntities(value) : value;
|
|
31273
|
+
}
|
|
31268
31274
|
function buildPnString(comp) {
|
|
31269
|
-
const parts = [comp.FamilyName ?? "", comp.GivenName ?? "", comp.MiddleName ?? "", comp.NamePrefix ?? "", comp.NameSuffix ?? ""];
|
|
31275
|
+
const parts = [comp.FamilyName ?? "", comp.GivenName ?? "", comp.MiddleName ?? "", comp.NamePrefix ?? "", comp.NameSuffix ?? ""].map(decodeXmlEntities);
|
|
31270
31276
|
let last = parts.length - 1;
|
|
31271
31277
|
for (; last >= 0; last--) {
|
|
31272
31278
|
if (parts[last] !== "") break;
|
|
@@ -31305,9 +31311,9 @@ function convertBulkDataURI(attr, element) {
|
|
|
31305
31311
|
const bulkArray = toArray(attr.BulkDataURI);
|
|
31306
31312
|
const firstBulk = bulkArray[0];
|
|
31307
31313
|
if (typeof firstBulk === "object" && firstBulk !== null && "@_uri" in firstBulk) {
|
|
31308
|
-
element.BulkDataURI = safeString(firstBulk["@_uri"]);
|
|
31314
|
+
element.BulkDataURI = decodeXmlEntities(safeString(firstBulk["@_uri"]));
|
|
31309
31315
|
} else {
|
|
31310
|
-
element.BulkDataURI = safeString(firstBulk);
|
|
31316
|
+
element.BulkDataURI = decodeXmlEntities(safeString(firstBulk));
|
|
31311
31317
|
}
|
|
31312
31318
|
}
|
|
31313
31319
|
function convertPNValue(attr, element) {
|
|
@@ -31351,7 +31357,7 @@ function convertRegularValue(attr, element, vr) {
|
|
|
31351
31357
|
const values = [];
|
|
31352
31358
|
const isNumeric = NUMERIC_JSON_VRS.has(vr);
|
|
31353
31359
|
for (const v of valArray) {
|
|
31354
|
-
const unwrapped = unwrapValue(v);
|
|
31360
|
+
const unwrapped = decodeIfString(unwrapValue(v));
|
|
31355
31361
|
values.push(isNumeric ? coerceNumeric(unwrapped) : unwrapped);
|
|
31356
31362
|
}
|
|
31357
31363
|
if (values.length > 0) element.Value = values;
|
|
@@ -36460,6 +36466,7 @@ var DEFAULT_CONNECTION_TIMEOUT_MS = 1e4;
|
|
|
36460
36466
|
var CONNECTION_RETRY_INTERVAL_MS = 500;
|
|
36461
36467
|
var MAX_CONNECTION_RETRIES = 200;
|
|
36462
36468
|
var MAX_OUTPUT_LINES_PER_ASSOCIATION = 500;
|
|
36469
|
+
var ABORT_REAP_GRACE_MS = 5e3;
|
|
36463
36470
|
var DicomReceiverOptionsSchema = z.object({
|
|
36464
36471
|
port: z.number().int().min(0).max(65535),
|
|
36465
36472
|
storageDir: z.string().min(1).refine(isSafePath, { message: "path traversal detected in storageDir" }),
|
|
@@ -36497,6 +36504,9 @@ var Worker = class {
|
|
|
36497
36504
|
__publicField(this, "_instancePending", /* @__PURE__ */ new Set());
|
|
36498
36505
|
__publicField(this, "_instancesReceived", 0);
|
|
36499
36506
|
__publicField(this, "_instanceErrors", 0);
|
|
36507
|
+
__publicField(this, "_finalized", false);
|
|
36508
|
+
__publicField(this, "_associationReceivedEmitted", false);
|
|
36509
|
+
__publicField(this, "_reapScheduled", false);
|
|
36500
36510
|
__publicField(this, "_files", []);
|
|
36501
36511
|
__publicField(this, "_fileSizes", []);
|
|
36502
36512
|
__publicField(this, "_outputLines", []);
|
|
@@ -36537,6 +36547,62 @@ var Worker = class {
|
|
|
36537
36547
|
this._instancePending.clear();
|
|
36538
36548
|
this._instancesReceived = 0;
|
|
36539
36549
|
this._instanceErrors = 0;
|
|
36550
|
+
this._finalized = false;
|
|
36551
|
+
this._associationReceivedEmitted = false;
|
|
36552
|
+
this._reapScheduled = false;
|
|
36553
|
+
}
|
|
36554
|
+
/**
|
|
36555
|
+
* Claims the single abort-reap timer slot for this association. The socket
|
|
36556
|
+
* `cleanup` handler runs on up to four socket events (remote/worker ×
|
|
36557
|
+
* error/close, plus an explicit setup-race call), so without this guard each
|
|
36558
|
+
* teardown would arm its own redundant grace-period timer. Returns true only
|
|
36559
|
+
* for the first caller; subsequent calls return false and arm nothing.
|
|
36560
|
+
*/
|
|
36561
|
+
claimReapSchedule() {
|
|
36562
|
+
if (this._reapScheduled) return false;
|
|
36563
|
+
this._reapScheduled = true;
|
|
36564
|
+
return true;
|
|
36565
|
+
}
|
|
36566
|
+
/** True once per association: the first caller wins the finalize, others get false. */
|
|
36567
|
+
beginFinalizeOnce() {
|
|
36568
|
+
if (this._finalized) return false;
|
|
36569
|
+
this._finalized = true;
|
|
36570
|
+
return true;
|
|
36571
|
+
}
|
|
36572
|
+
/** Whether finalize handling has already been claimed for the current association. */
|
|
36573
|
+
get finalized() {
|
|
36574
|
+
return this._finalized;
|
|
36575
|
+
}
|
|
36576
|
+
/** Records that ASSOCIATION_RECEIVED was emitted to consumers for this association. */
|
|
36577
|
+
markAssociationReceived() {
|
|
36578
|
+
this._associationReceivedEmitted = true;
|
|
36579
|
+
}
|
|
36580
|
+
/**
|
|
36581
|
+
* Whether ASSOCIATION_RECEIVED was emitted for the current association —
|
|
36582
|
+
* i.e. the association was handed off to consumers. When false (a connection
|
|
36583
|
+
* that aborted during/before DICOM negotiation), no consumer ever engaged
|
|
36584
|
+
* with the association directory, so the pool owns its cleanup.
|
|
36585
|
+
*/
|
|
36586
|
+
get associationReceived() {
|
|
36587
|
+
return this._associationReceivedEmitted;
|
|
36588
|
+
}
|
|
36589
|
+
/**
|
|
36590
|
+
* Atomically reserves a just-selected idle worker so concurrent connection
|
|
36591
|
+
* routing cannot hand the same worker to two associations. Must be called
|
|
36592
|
+
* synchronously the instant an idle worker is picked — before any `await` —
|
|
36593
|
+
* because `handleConnection` yields (e.g. on `ensureDirectory`) between
|
|
36594
|
+
* selecting a worker and calling {@link beginAssociation}. Without this,
|
|
36595
|
+
* two concurrent connections both see the worker as idle, both proceed, and
|
|
36596
|
+
* the second `beginAssociation` overwrites the first's context — orphaning
|
|
36597
|
+
* the first association (no terminal event ever fires for it). Released via
|
|
36598
|
+
* {@link release} if association setup fails before `beginAssociation`.
|
|
36599
|
+
*/
|
|
36600
|
+
reserve() {
|
|
36601
|
+
this._state = "busy";
|
|
36602
|
+
}
|
|
36603
|
+
/** Returns a reserved-but-not-yet-begun worker to the idle pool (setup failed). */
|
|
36604
|
+
release() {
|
|
36605
|
+
this._state = "idle";
|
|
36540
36606
|
}
|
|
36541
36607
|
/** Marks the worker as finalizing — still has valid context but cannot accept new connections. */
|
|
36542
36608
|
markFinalizing() {
|
|
@@ -36922,6 +36988,7 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
36922
36988
|
const associationDir = path.join(this.options.storageDir, associationId);
|
|
36923
36989
|
const mkdirResult = await ensureDirectory(associationDir);
|
|
36924
36990
|
if (!mkdirResult.ok) {
|
|
36991
|
+
worker.release();
|
|
36925
36992
|
remoteSocket.destroy();
|
|
36926
36993
|
this.emit("error", { error: mkdirResult.error });
|
|
36927
36994
|
return;
|
|
@@ -36940,12 +37007,18 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
36940
37007
|
/** Finds an idle worker, retrying up to connectionTimeoutMs. */
|
|
36941
37008
|
async findIdleWorker() {
|
|
36942
37009
|
const idle = this.getIdleWorker();
|
|
36943
|
-
if (idle !== void 0)
|
|
37010
|
+
if (idle !== void 0) {
|
|
37011
|
+
idle.reserve();
|
|
37012
|
+
return idle;
|
|
37013
|
+
}
|
|
36944
37014
|
const maxRetries = Math.min(Math.ceil(this.connectionTimeoutMs / CONNECTION_RETRY_INTERVAL_MS), MAX_CONNECTION_RETRIES);
|
|
36945
37015
|
for (let i = 0; i < maxRetries; i++) {
|
|
36946
37016
|
await setTimeout$1(CONNECTION_RETRY_INTERVAL_MS);
|
|
36947
37017
|
const found = this.getIdleWorker();
|
|
36948
|
-
if (found !== void 0)
|
|
37018
|
+
if (found !== void 0) {
|
|
37019
|
+
found.reserve();
|
|
37020
|
+
return found;
|
|
37021
|
+
}
|
|
36949
37022
|
if (this.stopping) return void 0;
|
|
36950
37023
|
}
|
|
36951
37024
|
return void 0;
|
|
@@ -36961,11 +37034,13 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
36961
37034
|
pipeConnection(worker, remoteSocket) {
|
|
36962
37035
|
const workerSocket = net.createConnection({ port: worker.port, host: "127.0.0.1" });
|
|
36963
37036
|
worker.setSockets(remoteSocket, workerSocket);
|
|
37037
|
+
const ctx = worker.context;
|
|
36964
37038
|
const cleanup = () => {
|
|
36965
37039
|
remoteSocket.unpipe(workerSocket);
|
|
36966
37040
|
workerSocket.unpipe(remoteSocket);
|
|
36967
37041
|
if (!remoteSocket.destroyed) remoteSocket.destroy();
|
|
36968
37042
|
if (!workerSocket.destroyed) workerSocket.destroy();
|
|
37043
|
+
this.scheduleAbortReap(worker, ctx);
|
|
36969
37044
|
};
|
|
36970
37045
|
workerSocket.on("connect", () => {
|
|
36971
37046
|
remoteSocket.pipe(workerSocket);
|
|
@@ -36976,6 +37051,42 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
36976
37051
|
workerSocket.on("error", cleanup);
|
|
36977
37052
|
remoteSocket.on("close", cleanup);
|
|
36978
37053
|
workerSocket.on("close", cleanup);
|
|
37054
|
+
if (remoteSocket.destroyed) {
|
|
37055
|
+
cleanup();
|
|
37056
|
+
}
|
|
37057
|
+
}
|
|
37058
|
+
/**
|
|
37059
|
+
* After a connection's sockets tear down, reaps the association as an
|
|
37060
|
+
* aborted one *only if* dcmrecv never reported completion within
|
|
37061
|
+
* {@link ABORT_REAP_GRACE_MS}. Without this, a peer abort destroys the
|
|
37062
|
+
* sockets but dcmrecv may never emit ASSOCIATION_COMPLETE, so the worker
|
|
37063
|
+
* stays busy forever, ASSOCIATION_FINALIZED never fires (consumers can't
|
|
37064
|
+
* release per-association state), and the association directory is never
|
|
37065
|
+
* removed. The grace delay ensures a normal A-RELEASE — whose socket also
|
|
37066
|
+
* closes — is finalized by its own completion report and never reaped here.
|
|
37067
|
+
*/
|
|
37068
|
+
scheduleAbortReap(worker, ctx) {
|
|
37069
|
+
if (ctx === void 0 || worker.context !== ctx || worker.state !== "busy") return;
|
|
37070
|
+
if (!worker.claimReapSchedule()) return;
|
|
37071
|
+
const timer = setTimeout(() => {
|
|
37072
|
+
if (worker.context !== ctx || worker.state !== "busy" || worker.finalized) return;
|
|
37073
|
+
worker.markFinalizing();
|
|
37074
|
+
const removeDir = !worker.associationReceived;
|
|
37075
|
+
void this.finalizeAssociation(
|
|
37076
|
+
worker,
|
|
37077
|
+
{
|
|
37078
|
+
associationId: ctx.associationId,
|
|
37079
|
+
callingAE: "",
|
|
37080
|
+
calledAE: "",
|
|
37081
|
+
source: "",
|
|
37082
|
+
files: [],
|
|
37083
|
+
durationMs: Date.now() - ctx.startAt,
|
|
37084
|
+
endReason: "abort"
|
|
37085
|
+
},
|
|
37086
|
+
removeDir
|
|
37087
|
+
);
|
|
37088
|
+
}, ABORT_REAP_GRACE_MS);
|
|
37089
|
+
timer.unref();
|
|
36979
37090
|
}
|
|
36980
37091
|
// -----------------------------------------------------------------------
|
|
36981
37092
|
// Worker pool management
|
|
@@ -37174,19 +37285,38 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
37174
37285
|
/** Returns worker to idle pool on association complete, emits summary. */
|
|
37175
37286
|
wireAssociationComplete(worker) {
|
|
37176
37287
|
worker.dcmrecv.onAssociationComplete((data) => {
|
|
37288
|
+
if (worker.finalized) return;
|
|
37177
37289
|
worker.markFinalizing();
|
|
37290
|
+
const removeDir = !worker.associationReceived;
|
|
37178
37291
|
setImmediate(() => {
|
|
37179
|
-
void this.finalizeAssociation(worker, data);
|
|
37292
|
+
void this.finalizeAssociation(worker, data, removeDir);
|
|
37180
37293
|
});
|
|
37181
37294
|
});
|
|
37182
37295
|
}
|
|
37183
|
-
/**
|
|
37184
|
-
|
|
37296
|
+
/**
|
|
37297
|
+
* Awaits file ops, emits ASSOCIATION_COMPLETE, awaits parsing, emits
|
|
37298
|
+
* ASSOCIATION_FINALIZED, returns the worker to idle. Runs at most once per
|
|
37299
|
+
* association via {@link Worker.beginFinalizeOnce}.
|
|
37300
|
+
*
|
|
37301
|
+
* @param removeDir - When true, removes the association directory after
|
|
37302
|
+
* finalizing. Set when no consumer ever engaged with the association
|
|
37303
|
+
* (no ASSOCIATION_RECEIVED was emitted) — e.g. a connection that aborted
|
|
37304
|
+
* during negotiation — so the pool reclaims the directory it created.
|
|
37305
|
+
* When a consumer did engage this stays false: the consumer owns
|
|
37306
|
+
* directory cleanup once it has processed the received files (removing it
|
|
37307
|
+
* here could race in-flight consumer reads of those files).
|
|
37308
|
+
*/
|
|
37309
|
+
async finalizeAssociation(worker, data, removeDir = false) {
|
|
37310
|
+
if (!worker.beginFinalizeOnce()) return;
|
|
37185
37311
|
await worker.drainPendingFiles();
|
|
37186
37312
|
this.emitAssociationComplete(worker, data);
|
|
37187
37313
|
await worker.drainInstancePending();
|
|
37188
37314
|
this.emitAssociationFinalized(worker, data);
|
|
37315
|
+
const associationDir = worker.context?.associationDir;
|
|
37189
37316
|
worker.endAssociation();
|
|
37317
|
+
if (removeDir && associationDir !== void 0) {
|
|
37318
|
+
await removeDirSafe(associationDir);
|
|
37319
|
+
}
|
|
37190
37320
|
void this.scaleDown();
|
|
37191
37321
|
}
|
|
37192
37322
|
/** Emits the ASSOCIATION_COMPLETE event with transfer stats. */
|
|
@@ -37228,12 +37358,14 @@ var DicomReceiver = class _DicomReceiver extends EventEmitter {
|
|
|
37228
37358
|
source: data.source,
|
|
37229
37359
|
files: [...worker.files],
|
|
37230
37360
|
instancesReceived: worker.instancesReceived,
|
|
37231
|
-
instanceErrors: worker.instanceErrors
|
|
37361
|
+
instanceErrors: worker.instanceErrors,
|
|
37362
|
+
endReason: data.endReason
|
|
37232
37363
|
});
|
|
37233
37364
|
}
|
|
37234
37365
|
/** Bubbles ASSOCIATION_RECEIVED from dcmrecv worker. */
|
|
37235
37366
|
wireAssociationReceived(worker) {
|
|
37236
37367
|
worker.dcmrecv.onEvent("ASSOCIATION_RECEIVED", (data) => {
|
|
37368
|
+
worker.markAssociationReceived();
|
|
37237
37369
|
this.emit("ASSOCIATION_RECEIVED", {
|
|
37238
37370
|
associationId: worker.context?.associationId ?? "",
|
|
37239
37371
|
callingAE: data.callingAE,
|
|
@@ -37544,7 +37676,12 @@ var SenderEngine = class {
|
|
|
37544
37676
|
this.activeAssociations++;
|
|
37545
37677
|
const startMs = Date.now();
|
|
37546
37678
|
const maxAttempts = entry.maxRetries + 1;
|
|
37547
|
-
|
|
37679
|
+
let failure;
|
|
37680
|
+
try {
|
|
37681
|
+
failure = await this.attemptSend(entry, maxAttempts, startMs);
|
|
37682
|
+
} catch (e) {
|
|
37683
|
+
failure = { error: e instanceof Error ? e : new Error(String(e)), output: { stdout: "", stderr: "" } };
|
|
37684
|
+
}
|
|
37548
37685
|
if (failure === void 0) return;
|
|
37549
37686
|
this.activeAssociations--;
|
|
37550
37687
|
this.recordFailure();
|