@ubercode/dcmtk 0.7.2 → 0.8.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/index.cjs +636 -297
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -57
- package/dist/index.d.ts +206 -57
- package/dist/index.js +636 -298
- package/dist/index.js.map +1 -1
- package/dist/tools.cjs +78 -16
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +29 -2
- package/dist/tools.d.ts +29 -2
- package/dist/tools.js +78 -16
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33106,6 +33106,30 @@ async function echoscu(options) {
|
|
|
33106
33106
|
return ok({ success: true, stderr: result.value.stderr });
|
|
33107
33107
|
}
|
|
33108
33108
|
var VERBOSITY_FLAGS19 = { verbose: "-v", debug: "-d" };
|
|
33109
|
+
var DECOMPRESS_FLAGS = {
|
|
33110
|
+
never: "--decompress-never",
|
|
33111
|
+
lossless: "--decompress-lossless",
|
|
33112
|
+
lossy: "--decompress-lossy"
|
|
33113
|
+
};
|
|
33114
|
+
function pushSendModeArgs(args, options) {
|
|
33115
|
+
if (options.noHalt === true) {
|
|
33116
|
+
args.push("--no-halt");
|
|
33117
|
+
}
|
|
33118
|
+
if (options.noIllegalProposal === true) {
|
|
33119
|
+
args.push("--no-illegal-proposal");
|
|
33120
|
+
}
|
|
33121
|
+
if (options.decompress !== void 0) {
|
|
33122
|
+
args.push(DECOMPRESS_FLAGS[options.decompress]);
|
|
33123
|
+
}
|
|
33124
|
+
if (options.multiAssociations === true) {
|
|
33125
|
+
args.push("+ma");
|
|
33126
|
+
} else if (options.multiAssociations === false) {
|
|
33127
|
+
args.push("-ma");
|
|
33128
|
+
}
|
|
33129
|
+
if (options.createReportFile !== void 0) {
|
|
33130
|
+
args.push("--create-report-file", options.createReportFile);
|
|
33131
|
+
}
|
|
33132
|
+
}
|
|
33109
33133
|
var DcmsendOptionsSchema = z.object({
|
|
33110
33134
|
timeoutMs: z.number().int().positive().optional(),
|
|
33111
33135
|
signal: z.instanceof(AbortSignal).optional(),
|
|
@@ -33115,8 +33139,15 @@ var DcmsendOptionsSchema = z.object({
|
|
|
33115
33139
|
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
33116
33140
|
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
33117
33141
|
scanDirectory: z.boolean().optional(),
|
|
33142
|
+
recurse: z.boolean().optional(),
|
|
33143
|
+
scanPattern: z.string().min(1).optional(),
|
|
33118
33144
|
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
33119
33145
|
noUidChecks: z.boolean().optional(),
|
|
33146
|
+
noHalt: z.boolean().optional(),
|
|
33147
|
+
noIllegalProposal: z.boolean().optional(),
|
|
33148
|
+
decompress: z.enum(["never", "lossless", "lossy"]).optional(),
|
|
33149
|
+
multiAssociations: z.boolean().optional(),
|
|
33150
|
+
createReportFile: z.string().min(1).optional(),
|
|
33120
33151
|
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
33121
33152
|
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
33122
33153
|
noHostnameLookup: z.boolean().optional(),
|
|
@@ -33162,6 +33193,13 @@ function buildArgs33(options) {
|
|
|
33162
33193
|
if (options.scanDirectory === true) {
|
|
33163
33194
|
args.push("--scan-directories");
|
|
33164
33195
|
}
|
|
33196
|
+
if (options.recurse === true) {
|
|
33197
|
+
args.push("+r");
|
|
33198
|
+
}
|
|
33199
|
+
if (options.scanPattern !== void 0) {
|
|
33200
|
+
args.push("--scan-pattern", options.scanPattern);
|
|
33201
|
+
}
|
|
33202
|
+
pushSendModeArgs(args, options);
|
|
33165
33203
|
args.push(options.host, String(options.port));
|
|
33166
33204
|
args.push(...options.files);
|
|
33167
33205
|
return args;
|
|
@@ -33200,7 +33238,18 @@ var ProposedTransferSyntax = {
|
|
|
33200
33238
|
J2K_LOSSLESS: "j2kLossless",
|
|
33201
33239
|
J2K_LOSSY: "j2kLossy",
|
|
33202
33240
|
JLS_LOSSLESS: "jlsLossless",
|
|
33203
|
-
JLS_LOSSY: "jlsLossy"
|
|
33241
|
+
JLS_LOSSY: "jlsLossy",
|
|
33242
|
+
MPEG2: "mpeg2",
|
|
33243
|
+
MPEG2_HIGH: "mpeg2High",
|
|
33244
|
+
MPEG4: "mpeg4",
|
|
33245
|
+
MPEG4_BD: "mpeg4Bd",
|
|
33246
|
+
MPEG4_2_2D: "mpeg4_2_2d",
|
|
33247
|
+
MPEG4_2_3D: "mpeg4_2_3d",
|
|
33248
|
+
MPEG4_2_ST: "mpeg4_2_st",
|
|
33249
|
+
HEVC: "hevc",
|
|
33250
|
+
HEVC10: "hevc10",
|
|
33251
|
+
RLE: "rle",
|
|
33252
|
+
DEFLATED: "deflated"
|
|
33204
33253
|
};
|
|
33205
33254
|
var PROPOSED_TS_FLAG_MAP = {
|
|
33206
33255
|
[ProposedTransferSyntax.UNCOMPRESSED]: "-x=",
|
|
@@ -33213,8 +33262,20 @@ var PROPOSED_TS_FLAG_MAP = {
|
|
|
33213
33262
|
[ProposedTransferSyntax.J2K_LOSSLESS]: "-xv",
|
|
33214
33263
|
[ProposedTransferSyntax.J2K_LOSSY]: "-xw",
|
|
33215
33264
|
[ProposedTransferSyntax.JLS_LOSSLESS]: "-xt",
|
|
33216
|
-
[ProposedTransferSyntax.JLS_LOSSY]: "-xu"
|
|
33265
|
+
[ProposedTransferSyntax.JLS_LOSSY]: "-xu",
|
|
33266
|
+
[ProposedTransferSyntax.MPEG2]: "-xm",
|
|
33267
|
+
[ProposedTransferSyntax.MPEG2_HIGH]: "-xh",
|
|
33268
|
+
[ProposedTransferSyntax.MPEG4]: "-xn",
|
|
33269
|
+
[ProposedTransferSyntax.MPEG4_BD]: "-xl",
|
|
33270
|
+
[ProposedTransferSyntax.MPEG4_2_2D]: "-x2",
|
|
33271
|
+
[ProposedTransferSyntax.MPEG4_2_3D]: "-x3",
|
|
33272
|
+
[ProposedTransferSyntax.MPEG4_2_ST]: "-xo",
|
|
33273
|
+
[ProposedTransferSyntax.HEVC]: "-x4",
|
|
33274
|
+
[ProposedTransferSyntax.HEVC10]: "-x5",
|
|
33275
|
+
[ProposedTransferSyntax.RLE]: "-xr",
|
|
33276
|
+
[ProposedTransferSyntax.DEFLATED]: "-xd"
|
|
33217
33277
|
};
|
|
33278
|
+
var PROPOSED_TS_VALUES = Object.values(ProposedTransferSyntax);
|
|
33218
33279
|
var StorescuOptionsSchema = z.object({
|
|
33219
33280
|
timeoutMs: z.number().int().positive().optional(),
|
|
33220
33281
|
signal: z.instanceof(AbortSignal).optional(),
|
|
@@ -33233,20 +33294,18 @@ var StorescuOptionsSchema = z.object({
|
|
|
33233
33294
|
dimseTimeout: z.number().int().positive().optional(),
|
|
33234
33295
|
noHostnameLookup: z.boolean().optional(),
|
|
33235
33296
|
required: z.boolean().optional(),
|
|
33236
|
-
proposedTransferSyntax: z.enum(
|
|
33237
|
-
|
|
33238
|
-
"littleEndian",
|
|
33239
|
-
"bigEndian",
|
|
33240
|
-
"implicitVR",
|
|
33241
|
-
"jpegLossless",
|
|
33242
|
-
"jpeg8Bit",
|
|
33243
|
-
"jpeg12Bit",
|
|
33244
|
-
"j2kLossless",
|
|
33245
|
-
"j2kLossy",
|
|
33246
|
-
"jlsLossless",
|
|
33247
|
-
"jlsLossy"
|
|
33248
|
-
]).optional()
|
|
33297
|
+
proposedTransferSyntax: z.union([z.enum(PROPOSED_TS_VALUES), z.array(z.enum(PROPOSED_TS_VALUES)).min(1)]).optional(),
|
|
33298
|
+
combineProposedTransferSyntaxes: z.boolean().optional()
|
|
33249
33299
|
}).strict();
|
|
33300
|
+
function pushProposedTsArgs(args, ts) {
|
|
33301
|
+
if (typeof ts === "string") {
|
|
33302
|
+
args.push(PROPOSED_TS_FLAG_MAP[ts]);
|
|
33303
|
+
} else {
|
|
33304
|
+
for (let i = 0; i < ts.length; i++) {
|
|
33305
|
+
args.push(PROPOSED_TS_FLAG_MAP[ts[i]]);
|
|
33306
|
+
}
|
|
33307
|
+
}
|
|
33308
|
+
}
|
|
33250
33309
|
var VERBOSITY_FLAGS20 = { verbose: "-v", debug: "-d" };
|
|
33251
33310
|
var DIMSE_ERROR_PATTERN = /^E:.*(?:DIMSE Failed|Store Failed)/m;
|
|
33252
33311
|
function pushNetworkArgs3(args, options) {
|
|
@@ -33288,7 +33347,10 @@ function buildArgs34(options) {
|
|
|
33288
33347
|
args.push("+r");
|
|
33289
33348
|
}
|
|
33290
33349
|
if (options.proposedTransferSyntax !== void 0) {
|
|
33291
|
-
args
|
|
33350
|
+
pushProposedTsArgs(args, options.proposedTransferSyntax);
|
|
33351
|
+
}
|
|
33352
|
+
if (options.combineProposedTransferSyntaxes === true) {
|
|
33353
|
+
args.push("+C");
|
|
33292
33354
|
}
|
|
33293
33355
|
if (options.required === true) {
|
|
33294
33356
|
args.push("-R");
|
|
@@ -36828,79 +36890,13 @@ var SenderHealth = {
|
|
|
36828
36890
|
DOWN: "down"
|
|
36829
36891
|
};
|
|
36830
36892
|
|
|
36831
|
-
// src/senders/
|
|
36832
|
-
var DEFAULT_MAX_ASSOCIATIONS = 4;
|
|
36833
|
-
var DEFAULT_MAX_QUEUE_LENGTH = 1e3;
|
|
36834
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
36835
|
-
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
36836
|
-
var DEFAULT_BUCKET_FLUSH_MS = 5e3;
|
|
36837
|
-
var DEFAULT_MAX_BUCKET_SIZE = 50;
|
|
36838
|
-
var MAX_ASSOCIATIONS_LIMIT = 64;
|
|
36893
|
+
// src/senders/SenderEngine.ts
|
|
36839
36894
|
var DEGRADE_THRESHOLD = 3;
|
|
36840
36895
|
var DOWN_THRESHOLD = 10;
|
|
36841
36896
|
var RECOVERY_THRESHOLD = 3;
|
|
36842
|
-
var
|
|
36843
|
-
|
|
36844
|
-
|
|
36845
|
-
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
36846
|
-
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
36847
|
-
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
36848
|
-
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT).optional(),
|
|
36849
|
-
proposedTransferSyntax: z.enum([
|
|
36850
|
-
"uncompressed",
|
|
36851
|
-
"littleEndian",
|
|
36852
|
-
"bigEndian",
|
|
36853
|
-
"implicitVR",
|
|
36854
|
-
"jpegLossless",
|
|
36855
|
-
"jpeg8Bit",
|
|
36856
|
-
"jpeg12Bit",
|
|
36857
|
-
"j2kLossless",
|
|
36858
|
-
"j2kLossy",
|
|
36859
|
-
"jlsLossless",
|
|
36860
|
-
"jlsLossy"
|
|
36861
|
-
]).optional(),
|
|
36862
|
-
maxQueueLength: z.number().int().min(1).optional(),
|
|
36863
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
36864
|
-
maxRetries: z.number().int().min(0).optional(),
|
|
36865
|
-
retryDelayMs: z.number().int().min(0).optional(),
|
|
36866
|
-
bucketFlushMs: z.number().int().positive().optional(),
|
|
36867
|
-
maxBucketSize: z.number().int().min(1).optional(),
|
|
36868
|
-
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
36869
|
-
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
36870
|
-
associationTimeout: z.number().int().positive().optional(),
|
|
36871
|
-
acseTimeout: z.number().int().positive().optional(),
|
|
36872
|
-
dimseTimeout: z.number().int().positive().optional(),
|
|
36873
|
-
noHostnameLookup: z.boolean().optional(),
|
|
36874
|
-
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
36875
|
-
required: z.boolean().optional(),
|
|
36876
|
-
signal: z.instanceof(AbortSignal).optional()
|
|
36877
|
-
}).strict();
|
|
36878
|
-
function resolveConfig(options) {
|
|
36879
|
-
const mode = options.mode ?? "multiple";
|
|
36880
|
-
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS;
|
|
36881
|
-
return {
|
|
36882
|
-
mode,
|
|
36883
|
-
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
36884
|
-
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH,
|
|
36885
|
-
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
36886
|
-
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
36887
|
-
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS,
|
|
36888
|
-
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS,
|
|
36889
|
-
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE
|
|
36890
|
-
};
|
|
36891
|
-
}
|
|
36892
|
-
var DicomSender = class _DicomSender extends EventEmitter {
|
|
36893
|
-
constructor(options) {
|
|
36894
|
-
super();
|
|
36895
|
-
__publicField(this, "options");
|
|
36896
|
-
__publicField(this, "mode");
|
|
36897
|
-
__publicField(this, "configuredMaxAssociations");
|
|
36898
|
-
__publicField(this, "maxQueueLength");
|
|
36899
|
-
__publicField(this, "defaultTimeoutMs");
|
|
36900
|
-
__publicField(this, "defaultMaxRetries");
|
|
36901
|
-
__publicField(this, "retryDelayMs");
|
|
36902
|
-
__publicField(this, "bucketFlushMs");
|
|
36903
|
-
__publicField(this, "maxBucketSize");
|
|
36897
|
+
var SenderEngine = class {
|
|
36898
|
+
constructor(config) {
|
|
36899
|
+
__publicField(this, "config");
|
|
36904
36900
|
// Queue and concurrency state
|
|
36905
36901
|
__publicField(this, "queue", []);
|
|
36906
36902
|
__publicField(this, "activeAssociations", 0);
|
|
@@ -36913,108 +36909,36 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
36913
36909
|
// Bucket state (bucket mode only)
|
|
36914
36910
|
__publicField(this, "currentBucket", []);
|
|
36915
36911
|
__publicField(this, "bucketTimer");
|
|
36916
|
-
|
|
36917
|
-
|
|
36918
|
-
this.setMaxListeners(20);
|
|
36919
|
-
this.on("error", () => {
|
|
36920
|
-
});
|
|
36921
|
-
this.options = options;
|
|
36922
|
-
const cfg = resolveConfig(options);
|
|
36923
|
-
this.mode = cfg.mode;
|
|
36924
|
-
this.configuredMaxAssociations = cfg.configuredMaxAssociations;
|
|
36925
|
-
this.effectiveMaxAssociations = cfg.configuredMaxAssociations;
|
|
36926
|
-
this.maxQueueLength = cfg.maxQueueLength;
|
|
36927
|
-
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
36928
|
-
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
36929
|
-
this.retryDelayMs = cfg.retryDelayMs;
|
|
36930
|
-
this.bucketFlushMs = cfg.bucketFlushMs;
|
|
36931
|
-
this.maxBucketSize = cfg.maxBucketSize;
|
|
36932
|
-
if (options.signal !== void 0) {
|
|
36933
|
-
this.wireAbortSignal(options.signal);
|
|
36934
|
-
}
|
|
36912
|
+
this.config = config;
|
|
36913
|
+
this.effectiveMaxAssociations = config.configuredMaxAssociations;
|
|
36935
36914
|
}
|
|
36936
36915
|
// -----------------------------------------------------------------------
|
|
36937
36916
|
// Public API
|
|
36938
36917
|
// -----------------------------------------------------------------------
|
|
36939
|
-
/**
|
|
36940
|
-
|
|
36941
|
-
*
|
|
36942
|
-
* @param options - Configuration options
|
|
36943
|
-
* @returns A Result containing the instance or a validation error
|
|
36944
|
-
*/
|
|
36945
|
-
static create(options) {
|
|
36946
|
-
const validation = DicomSenderOptionsSchema.safeParse(options);
|
|
36947
|
-
if (!validation.success) {
|
|
36948
|
-
return err(createValidationError("DicomSender", validation.error));
|
|
36949
|
-
}
|
|
36950
|
-
return ok(new _DicomSender(options));
|
|
36951
|
-
}
|
|
36952
|
-
/**
|
|
36953
|
-
* Sends one or more DICOM files to the remote endpoint.
|
|
36954
|
-
*
|
|
36955
|
-
* In single/multiple mode, files are sent as one storescu call.
|
|
36956
|
-
* In bucket mode, files are accumulated into a bucket and flushed
|
|
36957
|
-
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
36958
|
-
*
|
|
36959
|
-
* The returned promise resolves when the files are actually sent
|
|
36960
|
-
* (not just queued). Callers can await for confirmation or
|
|
36961
|
-
* fire-and-forget with `void sender.send(files)`.
|
|
36962
|
-
*
|
|
36963
|
-
* @param files - One or more DICOM file paths
|
|
36964
|
-
* @param options - Per-send overrides
|
|
36965
|
-
* @returns A Result containing the send result or an error
|
|
36966
|
-
*/
|
|
36967
|
-
send(files, options) {
|
|
36918
|
+
/** Sends files through the engine's queue/bucket system. */
|
|
36919
|
+
send(files, timeoutMs, maxRetries, binaryParams) {
|
|
36968
36920
|
if (this.isStopped) {
|
|
36969
|
-
return Promise.resolve(err(new Error(
|
|
36921
|
+
return Promise.resolve(err(new Error(`${this.config.senderName}: sender is stopped`)));
|
|
36970
36922
|
}
|
|
36971
36923
|
if (files.length === 0) {
|
|
36972
|
-
return Promise.resolve(err(new Error(
|
|
36973
|
-
}
|
|
36974
|
-
const params = {
|
|
36975
|
-
timeoutMs: options?.timeoutMs ?? this.defaultTimeoutMs,
|
|
36976
|
-
maxRetries: options?.maxRetries ?? this.defaultMaxRetries,
|
|
36977
|
-
calledAETitle: options?.calledAETitle,
|
|
36978
|
-
callingAETitle: options?.callingAETitle,
|
|
36979
|
-
required: options?.required
|
|
36980
|
-
};
|
|
36981
|
-
return this.dispatchSend(files, params);
|
|
36982
|
-
}
|
|
36983
|
-
/** Dispatches a send to the appropriate mode handler. */
|
|
36984
|
-
dispatchSend(files, params) {
|
|
36985
|
-
switch (this.mode) {
|
|
36986
|
-
case "single":
|
|
36987
|
-
case "multiple":
|
|
36988
|
-
return this.enqueueSend(files, params);
|
|
36989
|
-
case "bucket":
|
|
36990
|
-
return this.enqueueBucket(files, params);
|
|
36991
|
-
default:
|
|
36992
|
-
assertUnreachable(this.mode);
|
|
36924
|
+
return Promise.resolve(err(new Error(`${this.config.senderName}: no files provided`)));
|
|
36993
36925
|
}
|
|
36926
|
+
return this.dispatchSend(files, timeoutMs, maxRetries, binaryParams);
|
|
36994
36927
|
}
|
|
36995
|
-
/**
|
|
36996
|
-
* Flushes the current bucket immediately (bucket mode only).
|
|
36997
|
-
* In single/multiple mode this is a no-op.
|
|
36998
|
-
*/
|
|
36928
|
+
/** Flushes the current bucket immediately (bucket mode only). */
|
|
36999
36929
|
flush() {
|
|
37000
|
-
if (this.mode !== "bucket") return;
|
|
36930
|
+
if (this.config.mode !== "bucket") return;
|
|
37001
36931
|
if (this.currentBucket.length === 0) return;
|
|
37002
36932
|
this.clearBucketTimer();
|
|
37003
36933
|
this.flushBucketInternal("timer");
|
|
37004
36934
|
}
|
|
37005
|
-
/**
|
|
37006
|
-
* Gracefully stops the sender. Rejects all queued items and
|
|
37007
|
-
* waits for active associations to complete.
|
|
37008
|
-
*/
|
|
36935
|
+
/** Gracefully stops the engine. Rejects all queued items and waits for active associations. */
|
|
37009
36936
|
async stop() {
|
|
37010
36937
|
if (this.isStopped) return;
|
|
37011
36938
|
this.isStopped = true;
|
|
37012
|
-
if (this.options.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37013
|
-
this.options.signal.removeEventListener("abort", this.abortHandler);
|
|
37014
|
-
}
|
|
37015
36939
|
this.clearBucketTimer();
|
|
37016
|
-
this.rejectBucket(
|
|
37017
|
-
this.rejectQueue(
|
|
36940
|
+
this.rejectBucket(`${this.config.senderName}: sender stopped`);
|
|
36941
|
+
this.rejectQueue(`${this.config.senderName}: sender stopped`);
|
|
37018
36942
|
await this.waitForActive();
|
|
37019
36943
|
}
|
|
37020
36944
|
/** Current sender status. */
|
|
@@ -37029,75 +36953,37 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37029
36953
|
stopped: this.isStopped
|
|
37030
36954
|
};
|
|
37031
36955
|
}
|
|
36956
|
+
/** Whether the engine has been stopped. */
|
|
36957
|
+
get stopped() {
|
|
36958
|
+
return this.isStopped;
|
|
36959
|
+
}
|
|
37032
36960
|
// -----------------------------------------------------------------------
|
|
37033
|
-
//
|
|
36961
|
+
// Dispatch
|
|
37034
36962
|
// -----------------------------------------------------------------------
|
|
37035
|
-
/**
|
|
37036
|
-
|
|
37037
|
-
|
|
37038
|
-
|
|
37039
|
-
|
|
37040
|
-
|
|
37041
|
-
|
|
37042
|
-
|
|
37043
|
-
|
|
37044
|
-
|
|
37045
|
-
|
|
37046
|
-
* Registers a listener for successful sends.
|
|
37047
|
-
*
|
|
37048
|
-
* @param listener - Callback receiving send complete data
|
|
37049
|
-
* @returns this for chaining
|
|
37050
|
-
*/
|
|
37051
|
-
onSendComplete(listener) {
|
|
37052
|
-
return this.on("SEND_COMPLETE", listener);
|
|
37053
|
-
}
|
|
37054
|
-
/**
|
|
37055
|
-
* Registers a listener for failed sends.
|
|
37056
|
-
*
|
|
37057
|
-
* @param listener - Callback receiving send failed data
|
|
37058
|
-
* @returns this for chaining
|
|
37059
|
-
*/
|
|
37060
|
-
onSendFailed(listener) {
|
|
37061
|
-
return this.on("SEND_FAILED", listener);
|
|
37062
|
-
}
|
|
37063
|
-
/**
|
|
37064
|
-
* Registers a listener for health state changes.
|
|
37065
|
-
*
|
|
37066
|
-
* @param listener - Callback receiving health change data
|
|
37067
|
-
* @returns this for chaining
|
|
37068
|
-
*/
|
|
37069
|
-
onHealthChanged(listener) {
|
|
37070
|
-
return this.on("HEALTH_CHANGED", listener);
|
|
37071
|
-
}
|
|
37072
|
-
/**
|
|
37073
|
-
* Registers a listener for bucket flushes (bucket mode only).
|
|
37074
|
-
*
|
|
37075
|
-
* @param listener - Callback receiving bucket flush data
|
|
37076
|
-
* @returns this for chaining
|
|
37077
|
-
*/
|
|
37078
|
-
onBucketFlushed(listener) {
|
|
37079
|
-
return this.on("BUCKET_FLUSHED", listener);
|
|
36963
|
+
/** Dispatches a send to the appropriate mode handler. */
|
|
36964
|
+
dispatchSend(files, timeoutMs, maxRetries, binaryParams) {
|
|
36965
|
+
switch (this.config.mode) {
|
|
36966
|
+
case "single":
|
|
36967
|
+
case "multiple":
|
|
36968
|
+
return this.enqueueSend(files, timeoutMs, maxRetries, binaryParams);
|
|
36969
|
+
case "bucket":
|
|
36970
|
+
return this.enqueueBucket(files, timeoutMs, maxRetries, binaryParams);
|
|
36971
|
+
default:
|
|
36972
|
+
assertUnreachable(this.config.mode);
|
|
36973
|
+
}
|
|
37080
36974
|
}
|
|
37081
36975
|
// -----------------------------------------------------------------------
|
|
37082
36976
|
// Single/Multiple mode: queue-based dispatch
|
|
37083
36977
|
// -----------------------------------------------------------------------
|
|
37084
36978
|
/** Enqueues a send and dispatches immediately if capacity allows. */
|
|
37085
|
-
enqueueSend(files,
|
|
36979
|
+
enqueueSend(files, timeoutMs, maxRetries, binaryParams) {
|
|
37086
36980
|
return new Promise((resolve) => {
|
|
37087
36981
|
const totalQueued = this.queue.length + this.currentBucket.length;
|
|
37088
|
-
if (totalQueued >= this.maxQueueLength) {
|
|
37089
|
-
resolve(err(new Error(
|
|
36982
|
+
if (totalQueued >= this.config.maxQueueLength) {
|
|
36983
|
+
resolve(err(new Error(`${this.config.senderName}: queue full`)));
|
|
37090
36984
|
return;
|
|
37091
36985
|
}
|
|
37092
|
-
const entry = {
|
|
37093
|
-
files,
|
|
37094
|
-
timeoutMs: params.timeoutMs,
|
|
37095
|
-
maxRetries: params.maxRetries,
|
|
37096
|
-
calledAETitle: params.calledAETitle,
|
|
37097
|
-
callingAETitle: params.callingAETitle,
|
|
37098
|
-
required: params.required,
|
|
37099
|
-
resolve
|
|
37100
|
-
};
|
|
36986
|
+
const entry = { files, timeoutMs, maxRetries, binaryParams, resolve };
|
|
37101
36987
|
if (this.activeAssociations < this.effectiveMaxAssociations) {
|
|
37102
36988
|
void this.executeEntry(entry);
|
|
37103
36989
|
} else {
|
|
@@ -37117,17 +37003,16 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37117
37003
|
// Bucket mode: accumulate-then-flush
|
|
37118
37004
|
// -----------------------------------------------------------------------
|
|
37119
37005
|
/** Adds files to the current bucket and triggers flush if full. */
|
|
37120
|
-
enqueueBucket(files,
|
|
37006
|
+
enqueueBucket(files, timeoutMs, maxRetries, binaryParams) {
|
|
37121
37007
|
return new Promise((resolve) => {
|
|
37122
37008
|
const totalQueued = this.queue.length + this.currentBucket.length;
|
|
37123
|
-
if (totalQueued >= this.maxQueueLength) {
|
|
37124
|
-
resolve(err(new Error(
|
|
37009
|
+
if (totalQueued >= this.config.maxQueueLength) {
|
|
37010
|
+
resolve(err(new Error(`${this.config.senderName}: queue full`)));
|
|
37125
37011
|
return;
|
|
37126
37012
|
}
|
|
37127
|
-
|
|
37128
|
-
this.currentBucket.push({ files, resolve, timeoutMs, maxRetries, calledAETitle, callingAETitle, required });
|
|
37013
|
+
this.currentBucket.push({ files, resolve, timeoutMs, maxRetries, binaryParams });
|
|
37129
37014
|
const totalFiles = this.countBucketFiles();
|
|
37130
|
-
if (totalFiles >= this.maxBucketSize) {
|
|
37015
|
+
if (totalFiles >= this.config.maxBucketSize) {
|
|
37131
37016
|
this.clearBucketTimer();
|
|
37132
37017
|
void this.flushBucketInternal("maxSize");
|
|
37133
37018
|
} else {
|
|
@@ -37159,9 +37044,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37159
37044
|
files: allFiles,
|
|
37160
37045
|
timeoutMs,
|
|
37161
37046
|
maxRetries,
|
|
37162
|
-
|
|
37163
|
-
callingAETitle: entries[0]?.callingAETitle,
|
|
37164
|
-
required: entries[0]?.required,
|
|
37047
|
+
binaryParams: entries[0].binaryParams,
|
|
37165
37048
|
resolve: (result) => {
|
|
37166
37049
|
for (let i = 0; i < entries.length; i++) {
|
|
37167
37050
|
entries[i].resolve(result);
|
|
@@ -37175,7 +37058,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37175
37058
|
const entries = [...this.currentBucket];
|
|
37176
37059
|
this.currentBucket = [];
|
|
37177
37060
|
const bucketEntry = this.mergeBucketEntries(entries);
|
|
37178
|
-
this.
|
|
37061
|
+
this.config.emitters.emitBucketFlushed({ fileCount: bucketEntry.files.length, reason });
|
|
37179
37062
|
if (this.activeAssociations < this.effectiveMaxAssociations) {
|
|
37180
37063
|
void this.executeEntry(bucketEntry);
|
|
37181
37064
|
} else {
|
|
@@ -37188,7 +37071,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37188
37071
|
this.bucketTimer = setTimeout(() => {
|
|
37189
37072
|
this.bucketTimer = void 0;
|
|
37190
37073
|
void this.flushBucketInternal("timer");
|
|
37191
|
-
}, this.bucketFlushMs);
|
|
37074
|
+
}, this.config.bucketFlushMs);
|
|
37192
37075
|
}
|
|
37193
37076
|
/** Clears the bucket flush timer. */
|
|
37194
37077
|
clearBucketTimer() {
|
|
@@ -37200,7 +37083,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37200
37083
|
// -----------------------------------------------------------------------
|
|
37201
37084
|
// Core send execution with retry
|
|
37202
37085
|
// -----------------------------------------------------------------------
|
|
37203
|
-
/** Executes a single queue entry: calls
|
|
37086
|
+
/** Executes a single queue entry: calls the binary with retry. */
|
|
37204
37087
|
async executeEntry(entry) {
|
|
37205
37088
|
this.activeAssociations++;
|
|
37206
37089
|
const startMs = Date.now();
|
|
@@ -37209,7 +37092,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37209
37092
|
if (failure === void 0) return;
|
|
37210
37093
|
this.activeAssociations--;
|
|
37211
37094
|
this.recordFailure();
|
|
37212
|
-
this.
|
|
37095
|
+
this.config.emitters.emitSendFailed({
|
|
37213
37096
|
files: entry.files,
|
|
37214
37097
|
error: failure.error,
|
|
37215
37098
|
attempts: maxAttempts,
|
|
@@ -37219,48 +37102,27 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37219
37102
|
entry.resolve(err(failure.error));
|
|
37220
37103
|
this.drainQueue();
|
|
37221
37104
|
}
|
|
37222
|
-
/** Attempts
|
|
37105
|
+
/** Attempts the binary call up to maxAttempts times. Returns undefined on success. */
|
|
37223
37106
|
async attemptSend(entry, maxAttempts, startMs) {
|
|
37224
37107
|
let lastError;
|
|
37225
37108
|
const lastOutput = { stdout: "", stderr: "" };
|
|
37226
37109
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
37227
37110
|
if (this.isStopped) {
|
|
37228
37111
|
this.activeAssociations--;
|
|
37229
|
-
entry.resolve(err(new Error(
|
|
37112
|
+
entry.resolve(err(new Error(`${this.config.senderName}: sender stopped`)));
|
|
37230
37113
|
return void 0;
|
|
37231
37114
|
}
|
|
37232
|
-
const result = await this.
|
|
37115
|
+
const result = await this.config.executor(entry.files, entry.timeoutMs, entry.binaryParams, this.config.signal);
|
|
37233
37116
|
if (result.ok) {
|
|
37234
37117
|
this.handleSendSuccess(entry, startMs, result.value);
|
|
37235
37118
|
return void 0;
|
|
37236
37119
|
}
|
|
37237
37120
|
lastError = result.error;
|
|
37238
37121
|
if (attempt < maxAttempts - 1) {
|
|
37239
|
-
await delay2(this.retryDelayMs * (attempt + 1));
|
|
37122
|
+
await delay2(this.config.retryDelayMs * (attempt + 1));
|
|
37240
37123
|
}
|
|
37241
37124
|
}
|
|
37242
|
-
return { error: lastError ?? new Error(
|
|
37243
|
-
}
|
|
37244
|
-
/** Calls storescu with the configured options. */
|
|
37245
|
-
callStorescu(entry) {
|
|
37246
|
-
return storescu({
|
|
37247
|
-
host: this.options.host,
|
|
37248
|
-
port: this.options.port,
|
|
37249
|
-
files: [...entry.files],
|
|
37250
|
-
calledAETitle: entry.calledAETitle ?? this.options.calledAETitle,
|
|
37251
|
-
callingAETitle: entry.callingAETitle ?? this.options.callingAETitle,
|
|
37252
|
-
proposedTransferSyntax: this.options.proposedTransferSyntax,
|
|
37253
|
-
maxPduReceive: this.options.maxPduReceive,
|
|
37254
|
-
maxPduSend: this.options.maxPduSend,
|
|
37255
|
-
associationTimeout: this.options.associationTimeout,
|
|
37256
|
-
acseTimeout: this.options.acseTimeout,
|
|
37257
|
-
dimseTimeout: this.options.dimseTimeout,
|
|
37258
|
-
noHostnameLookup: this.options.noHostnameLookup,
|
|
37259
|
-
verbosity: this.options.verbosity,
|
|
37260
|
-
required: entry.required ?? this.options.required,
|
|
37261
|
-
timeoutMs: entry.timeoutMs,
|
|
37262
|
-
signal: this.options.signal
|
|
37263
|
-
});
|
|
37125
|
+
return { error: lastError ?? new Error(`${this.config.senderName}: send failed`), output: lastOutput };
|
|
37264
37126
|
}
|
|
37265
37127
|
/** Handles a successful send: updates state, emits event, resolves promise. */
|
|
37266
37128
|
handleSendSuccess(entry, startMs, output) {
|
|
@@ -37268,7 +37130,7 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37268
37130
|
const durationMs = Date.now() - startMs;
|
|
37269
37131
|
this.recordSuccess();
|
|
37270
37132
|
const data = { files: entry.files, fileCount: entry.files.length, durationMs, stdout: output.stdout, stderr: output.stderr };
|
|
37271
|
-
this.
|
|
37133
|
+
this.config.emitters.emitSendComplete(data);
|
|
37272
37134
|
entry.resolve(ok(data));
|
|
37273
37135
|
this.drainQueue();
|
|
37274
37136
|
}
|
|
@@ -37286,8 +37148,8 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37286
37148
|
if (this.health === SenderHealth.DOWN) {
|
|
37287
37149
|
this.health = SenderHealth.DEGRADED;
|
|
37288
37150
|
} else {
|
|
37289
|
-
this.effectiveMaxAssociations = Math.min(this.effectiveMaxAssociations * 2, this.configuredMaxAssociations);
|
|
37290
|
-
if (this.effectiveMaxAssociations >= this.configuredMaxAssociations) {
|
|
37151
|
+
this.effectiveMaxAssociations = Math.min(this.effectiveMaxAssociations * 2, this.config.configuredMaxAssociations);
|
|
37152
|
+
if (this.effectiveMaxAssociations >= this.config.configuredMaxAssociations) {
|
|
37291
37153
|
this.health = SenderHealth.HEALTHY;
|
|
37292
37154
|
}
|
|
37293
37155
|
}
|
|
@@ -37307,22 +37169,26 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37307
37169
|
this.emitHealthChanged(previousHealth);
|
|
37308
37170
|
}
|
|
37309
37171
|
} else if (this.consecutiveFailures >= DEGRADE_THRESHOLD && this.consecutiveFailures % DEGRADE_THRESHOLD === 0) {
|
|
37310
|
-
|
|
37311
|
-
|
|
37312
|
-
|
|
37172
|
+
this.degradeHealth(previousHealth);
|
|
37173
|
+
}
|
|
37174
|
+
}
|
|
37175
|
+
/** Handles HEALTHY→DEGRADED or DEGRADED→DEGRADED transitions. */
|
|
37176
|
+
degradeHealth(previousHealth) {
|
|
37177
|
+
if (this.health === SenderHealth.HEALTHY) {
|
|
37178
|
+
this.health = SenderHealth.DEGRADED;
|
|
37179
|
+
this.effectiveMaxAssociations = Math.max(1, Math.floor(this.config.configuredMaxAssociations / 2));
|
|
37180
|
+
this.emitHealthChanged(previousHealth);
|
|
37181
|
+
} else if (this.health === SenderHealth.DEGRADED) {
|
|
37182
|
+
const newMax = Math.max(1, Math.floor(this.effectiveMaxAssociations / 2));
|
|
37183
|
+
if (newMax !== this.effectiveMaxAssociations) {
|
|
37184
|
+
this.effectiveMaxAssociations = newMax;
|
|
37313
37185
|
this.emitHealthChanged(previousHealth);
|
|
37314
|
-
} else if (this.health === SenderHealth.DEGRADED) {
|
|
37315
|
-
const newMax = Math.max(1, Math.floor(this.effectiveMaxAssociations / 2));
|
|
37316
|
-
if (newMax !== this.effectiveMaxAssociations) {
|
|
37317
|
-
this.effectiveMaxAssociations = newMax;
|
|
37318
|
-
this.emitHealthChanged(previousHealth);
|
|
37319
|
-
}
|
|
37320
37186
|
}
|
|
37321
37187
|
}
|
|
37322
37188
|
}
|
|
37323
|
-
/** Emits a HEALTH_CHANGED event. */
|
|
37189
|
+
/** Emits a HEALTH_CHANGED event via the wrapper's emitter. */
|
|
37324
37190
|
emitHealthChanged(previousHealth) {
|
|
37325
|
-
this.
|
|
37191
|
+
this.config.emitters.emitHealthChanged({
|
|
37326
37192
|
previousHealth,
|
|
37327
37193
|
newHealth: this.health,
|
|
37328
37194
|
effectiveMaxAssociations: this.effectiveMaxAssociations,
|
|
@@ -37362,20 +37228,6 @@ var DicomSender = class _DicomSender extends EventEmitter {
|
|
|
37362
37228
|
check();
|
|
37363
37229
|
});
|
|
37364
37230
|
}
|
|
37365
|
-
// -----------------------------------------------------------------------
|
|
37366
|
-
// Abort signal
|
|
37367
|
-
// -----------------------------------------------------------------------
|
|
37368
|
-
/** Wires an AbortSignal to stop the sender. */
|
|
37369
|
-
wireAbortSignal(signal) {
|
|
37370
|
-
if (signal.aborted) {
|
|
37371
|
-
void this.stop();
|
|
37372
|
-
return;
|
|
37373
|
-
}
|
|
37374
|
-
this.abortHandler = () => {
|
|
37375
|
-
void this.stop();
|
|
37376
|
-
};
|
|
37377
|
-
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37378
|
-
}
|
|
37379
37231
|
};
|
|
37380
37232
|
function delay2(ms) {
|
|
37381
37233
|
return new Promise((resolve) => {
|
|
@@ -37383,6 +37235,492 @@ function delay2(ms) {
|
|
|
37383
37235
|
});
|
|
37384
37236
|
}
|
|
37385
37237
|
|
|
37238
|
+
// src/senders/DicomSender.ts
|
|
37239
|
+
var DEFAULT_MAX_ASSOCIATIONS = 4;
|
|
37240
|
+
var DEFAULT_MAX_QUEUE_LENGTH = 1e3;
|
|
37241
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
37242
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
37243
|
+
var DEFAULT_BUCKET_FLUSH_MS = 5e3;
|
|
37244
|
+
var DEFAULT_MAX_BUCKET_SIZE = 50;
|
|
37245
|
+
var MAX_ASSOCIATIONS_LIMIT = 64;
|
|
37246
|
+
var DicomSenderOptionsSchema = z.object({
|
|
37247
|
+
host: z.string().min(1),
|
|
37248
|
+
port: z.number().int().min(1).max(65535),
|
|
37249
|
+
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37250
|
+
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37251
|
+
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
37252
|
+
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT).optional(),
|
|
37253
|
+
proposedTransferSyntax: z.union([z.enum(PROPOSED_TS_VALUES), z.array(z.enum(PROPOSED_TS_VALUES)).min(1)]).optional(),
|
|
37254
|
+
combineProposedTransferSyntaxes: z.boolean().optional(),
|
|
37255
|
+
maxQueueLength: z.number().int().min(1).optional(),
|
|
37256
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
37257
|
+
maxRetries: z.number().int().min(0).optional(),
|
|
37258
|
+
retryDelayMs: z.number().int().min(0).optional(),
|
|
37259
|
+
bucketFlushMs: z.number().int().positive().optional(),
|
|
37260
|
+
maxBucketSize: z.number().int().min(1).optional(),
|
|
37261
|
+
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
37262
|
+
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
37263
|
+
associationTimeout: z.number().int().positive().optional(),
|
|
37264
|
+
acseTimeout: z.number().int().positive().optional(),
|
|
37265
|
+
dimseTimeout: z.number().int().positive().optional(),
|
|
37266
|
+
noHostnameLookup: z.boolean().optional(),
|
|
37267
|
+
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
37268
|
+
required: z.boolean().optional(),
|
|
37269
|
+
signal: z.instanceof(AbortSignal).optional()
|
|
37270
|
+
}).strict();
|
|
37271
|
+
function createStorescuExecutor(options) {
|
|
37272
|
+
return async (files, timeoutMs, params, signal) => {
|
|
37273
|
+
const result = await storescu({
|
|
37274
|
+
host: options.host,
|
|
37275
|
+
port: options.port,
|
|
37276
|
+
files: [...files],
|
|
37277
|
+
calledAETitle: params.calledAETitle ?? options.calledAETitle,
|
|
37278
|
+
callingAETitle: params.callingAETitle ?? options.callingAETitle,
|
|
37279
|
+
proposedTransferSyntax: params.proposedTransferSyntax ?? options.proposedTransferSyntax,
|
|
37280
|
+
combineProposedTransferSyntaxes: params.combineProposedTransferSyntaxes ?? options.combineProposedTransferSyntaxes,
|
|
37281
|
+
maxPduReceive: options.maxPduReceive,
|
|
37282
|
+
maxPduSend: options.maxPduSend,
|
|
37283
|
+
associationTimeout: options.associationTimeout,
|
|
37284
|
+
acseTimeout: options.acseTimeout,
|
|
37285
|
+
dimseTimeout: options.dimseTimeout,
|
|
37286
|
+
noHostnameLookup: options.noHostnameLookup,
|
|
37287
|
+
verbosity: options.verbosity,
|
|
37288
|
+
required: params.required ?? options.required,
|
|
37289
|
+
timeoutMs,
|
|
37290
|
+
signal
|
|
37291
|
+
});
|
|
37292
|
+
if (!result.ok) return err(result.error);
|
|
37293
|
+
return ok({ stdout: result.value.stdout, stderr: result.value.stderr });
|
|
37294
|
+
};
|
|
37295
|
+
}
|
|
37296
|
+
function resolveConfig(options) {
|
|
37297
|
+
const mode = options.mode ?? "multiple";
|
|
37298
|
+
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS;
|
|
37299
|
+
return {
|
|
37300
|
+
mode,
|
|
37301
|
+
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
37302
|
+
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH,
|
|
37303
|
+
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37304
|
+
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
37305
|
+
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS,
|
|
37306
|
+
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS,
|
|
37307
|
+
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE
|
|
37308
|
+
};
|
|
37309
|
+
}
|
|
37310
|
+
var DicomSender = class _DicomSender extends EventEmitter {
|
|
37311
|
+
constructor(engine, cfg, signal) {
|
|
37312
|
+
super();
|
|
37313
|
+
__publicField(this, "engine");
|
|
37314
|
+
__publicField(this, "defaultTimeoutMs");
|
|
37315
|
+
__publicField(this, "defaultMaxRetries");
|
|
37316
|
+
__publicField(this, "signal");
|
|
37317
|
+
__publicField(this, "abortHandler");
|
|
37318
|
+
this.setMaxListeners(20);
|
|
37319
|
+
this.on("error", () => {
|
|
37320
|
+
});
|
|
37321
|
+
this.engine = engine;
|
|
37322
|
+
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
37323
|
+
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
37324
|
+
this.signal = signal;
|
|
37325
|
+
if (signal !== void 0) {
|
|
37326
|
+
this.wireAbortSignal(signal);
|
|
37327
|
+
}
|
|
37328
|
+
}
|
|
37329
|
+
// -----------------------------------------------------------------------
|
|
37330
|
+
// Public API
|
|
37331
|
+
// -----------------------------------------------------------------------
|
|
37332
|
+
/**
|
|
37333
|
+
* Creates a new DicomSender instance.
|
|
37334
|
+
*
|
|
37335
|
+
* @param options - Configuration options
|
|
37336
|
+
* @returns A Result containing the instance or a validation error
|
|
37337
|
+
*/
|
|
37338
|
+
static create(options) {
|
|
37339
|
+
const validation = DicomSenderOptionsSchema.safeParse(options);
|
|
37340
|
+
if (!validation.success) {
|
|
37341
|
+
return err(createValidationError("DicomSender", validation.error));
|
|
37342
|
+
}
|
|
37343
|
+
const cfg = resolveConfig(options);
|
|
37344
|
+
const senderRef = { current: void 0 };
|
|
37345
|
+
const engine = new SenderEngine({
|
|
37346
|
+
...cfg,
|
|
37347
|
+
executor: createStorescuExecutor(options),
|
|
37348
|
+
signal: options.signal,
|
|
37349
|
+
senderName: "DicomSender",
|
|
37350
|
+
emitters: {
|
|
37351
|
+
emitSendComplete: (data) => {
|
|
37352
|
+
senderRef.current.emit("SEND_COMPLETE", data);
|
|
37353
|
+
},
|
|
37354
|
+
emitSendFailed: (data) => {
|
|
37355
|
+
senderRef.current.emit("SEND_FAILED", data);
|
|
37356
|
+
},
|
|
37357
|
+
emitHealthChanged: (data) => {
|
|
37358
|
+
senderRef.current.emit("HEALTH_CHANGED", data);
|
|
37359
|
+
},
|
|
37360
|
+
emitBucketFlushed: (data) => {
|
|
37361
|
+
senderRef.current.emit("BUCKET_FLUSHED", data);
|
|
37362
|
+
}
|
|
37363
|
+
}
|
|
37364
|
+
});
|
|
37365
|
+
const sender = new _DicomSender(engine, cfg, options.signal);
|
|
37366
|
+
senderRef.current = sender;
|
|
37367
|
+
return ok(sender);
|
|
37368
|
+
}
|
|
37369
|
+
/**
|
|
37370
|
+
* Sends one or more DICOM files to the remote endpoint.
|
|
37371
|
+
*
|
|
37372
|
+
* In single/multiple mode, files are sent as one storescu call.
|
|
37373
|
+
* In bucket mode, files are accumulated into a bucket and flushed
|
|
37374
|
+
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
37375
|
+
*
|
|
37376
|
+
* The returned promise resolves when the files are actually sent
|
|
37377
|
+
* (not just queued). Callers can await for confirmation or
|
|
37378
|
+
* fire-and-forget with `void sender.send(files)`.
|
|
37379
|
+
*
|
|
37380
|
+
* @param files - One or more DICOM file paths
|
|
37381
|
+
* @param options - Per-send overrides
|
|
37382
|
+
* @returns A Result containing the send result or an error
|
|
37383
|
+
*/
|
|
37384
|
+
send(files, options) {
|
|
37385
|
+
const params = buildStorescuParams(options);
|
|
37386
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
37387
|
+
const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;
|
|
37388
|
+
return this.engine.send(files, timeoutMs, maxRetries, params);
|
|
37389
|
+
}
|
|
37390
|
+
/**
|
|
37391
|
+
* Flushes the current bucket immediately (bucket mode only).
|
|
37392
|
+
* In single/multiple mode this is a no-op.
|
|
37393
|
+
*/
|
|
37394
|
+
flush() {
|
|
37395
|
+
this.engine.flush();
|
|
37396
|
+
}
|
|
37397
|
+
/**
|
|
37398
|
+
* Gracefully stops the sender. Rejects all queued items and
|
|
37399
|
+
* waits for active associations to complete.
|
|
37400
|
+
*/
|
|
37401
|
+
async stop() {
|
|
37402
|
+
if (this.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37403
|
+
this.signal.removeEventListener("abort", this.abortHandler);
|
|
37404
|
+
}
|
|
37405
|
+
await this.engine.stop();
|
|
37406
|
+
}
|
|
37407
|
+
/** Current sender status. */
|
|
37408
|
+
get status() {
|
|
37409
|
+
return this.engine.status;
|
|
37410
|
+
}
|
|
37411
|
+
// -----------------------------------------------------------------------
|
|
37412
|
+
// Typed event listener convenience methods
|
|
37413
|
+
// -----------------------------------------------------------------------
|
|
37414
|
+
/**
|
|
37415
|
+
* Registers a typed listener for a DicomSender-specific event.
|
|
37416
|
+
*
|
|
37417
|
+
* @param event - The event name from DicomSenderEventMap
|
|
37418
|
+
* @param listener - Callback receiving typed event data
|
|
37419
|
+
* @returns this for chaining
|
|
37420
|
+
*/
|
|
37421
|
+
onEvent(event, listener) {
|
|
37422
|
+
return this.on(event, listener);
|
|
37423
|
+
}
|
|
37424
|
+
/**
|
|
37425
|
+
* Registers a listener for successful sends.
|
|
37426
|
+
*
|
|
37427
|
+
* @param listener - Callback receiving send complete data
|
|
37428
|
+
* @returns this for chaining
|
|
37429
|
+
*/
|
|
37430
|
+
onSendComplete(listener) {
|
|
37431
|
+
return this.on("SEND_COMPLETE", listener);
|
|
37432
|
+
}
|
|
37433
|
+
/**
|
|
37434
|
+
* Registers a listener for failed sends.
|
|
37435
|
+
*
|
|
37436
|
+
* @param listener - Callback receiving send failed data
|
|
37437
|
+
* @returns this for chaining
|
|
37438
|
+
*/
|
|
37439
|
+
onSendFailed(listener) {
|
|
37440
|
+
return this.on("SEND_FAILED", listener);
|
|
37441
|
+
}
|
|
37442
|
+
/**
|
|
37443
|
+
* Registers a listener for health state changes.
|
|
37444
|
+
*
|
|
37445
|
+
* @param listener - Callback receiving health change data
|
|
37446
|
+
* @returns this for chaining
|
|
37447
|
+
*/
|
|
37448
|
+
onHealthChanged(listener) {
|
|
37449
|
+
return this.on("HEALTH_CHANGED", listener);
|
|
37450
|
+
}
|
|
37451
|
+
/**
|
|
37452
|
+
* Registers a listener for bucket flushes (bucket mode only).
|
|
37453
|
+
*
|
|
37454
|
+
* @param listener - Callback receiving bucket flush data
|
|
37455
|
+
* @returns this for chaining
|
|
37456
|
+
*/
|
|
37457
|
+
onBucketFlushed(listener) {
|
|
37458
|
+
return this.on("BUCKET_FLUSHED", listener);
|
|
37459
|
+
}
|
|
37460
|
+
// -----------------------------------------------------------------------
|
|
37461
|
+
// Abort signal
|
|
37462
|
+
// -----------------------------------------------------------------------
|
|
37463
|
+
/** Wires an AbortSignal to stop the sender. */
|
|
37464
|
+
wireAbortSignal(signal) {
|
|
37465
|
+
if (signal.aborted) {
|
|
37466
|
+
void this.stop();
|
|
37467
|
+
return;
|
|
37468
|
+
}
|
|
37469
|
+
this.abortHandler = () => {
|
|
37470
|
+
void this.stop();
|
|
37471
|
+
};
|
|
37472
|
+
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37473
|
+
}
|
|
37474
|
+
};
|
|
37475
|
+
function buildStorescuParams(options) {
|
|
37476
|
+
return {
|
|
37477
|
+
calledAETitle: options?.calledAETitle,
|
|
37478
|
+
callingAETitle: options?.callingAETitle,
|
|
37479
|
+
required: options?.required,
|
|
37480
|
+
proposedTransferSyntax: options?.proposedTransferSyntax,
|
|
37481
|
+
combineProposedTransferSyntaxes: options?.combineProposedTransferSyntaxes
|
|
37482
|
+
};
|
|
37483
|
+
}
|
|
37484
|
+
var DEFAULT_MAX_ASSOCIATIONS2 = 4;
|
|
37485
|
+
var DEFAULT_MAX_QUEUE_LENGTH2 = 1e3;
|
|
37486
|
+
var DEFAULT_MAX_RETRIES2 = 3;
|
|
37487
|
+
var DEFAULT_RETRY_DELAY_MS2 = 1e3;
|
|
37488
|
+
var DEFAULT_BUCKET_FLUSH_MS2 = 5e3;
|
|
37489
|
+
var DEFAULT_MAX_BUCKET_SIZE2 = 50;
|
|
37490
|
+
var MAX_ASSOCIATIONS_LIMIT2 = 64;
|
|
37491
|
+
var DicomSendOptionsSchema = z.object({
|
|
37492
|
+
host: z.string().min(1),
|
|
37493
|
+
port: z.number().int().min(1).max(65535),
|
|
37494
|
+
calledAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37495
|
+
callingAETitle: z.string().min(1).max(16).refine(isValidAETitle, { message: "AE Title contains invalid characters" }).optional(),
|
|
37496
|
+
mode: z.enum(["single", "multiple", "bucket"]).optional(),
|
|
37497
|
+
maxAssociations: z.number().int().min(1).max(MAX_ASSOCIATIONS_LIMIT2).optional(),
|
|
37498
|
+
maxQueueLength: z.number().int().min(1).optional(),
|
|
37499
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
37500
|
+
maxRetries: z.number().int().min(0).optional(),
|
|
37501
|
+
retryDelayMs: z.number().int().min(0).optional(),
|
|
37502
|
+
bucketFlushMs: z.number().int().positive().optional(),
|
|
37503
|
+
maxBucketSize: z.number().int().min(1).optional(),
|
|
37504
|
+
maxPduReceive: z.number().int().min(4096).max(131072).optional(),
|
|
37505
|
+
maxPduSend: z.number().int().min(4096).max(131072).optional(),
|
|
37506
|
+
associationTimeout: z.number().int().positive().optional(),
|
|
37507
|
+
acseTimeout: z.number().int().positive().optional(),
|
|
37508
|
+
dimseTimeout: z.number().int().positive().optional(),
|
|
37509
|
+
noHostnameLookup: z.boolean().optional(),
|
|
37510
|
+
verbosity: z.enum(["verbose", "debug"]).optional(),
|
|
37511
|
+
noHalt: z.boolean().optional(),
|
|
37512
|
+
noIllegalProposal: z.boolean().optional(),
|
|
37513
|
+
decompress: z.enum(["never", "lossless", "lossy"]).optional(),
|
|
37514
|
+
multiAssociations: z.boolean().optional(),
|
|
37515
|
+
noUidChecks: z.boolean().optional(),
|
|
37516
|
+
signal: z.instanceof(AbortSignal).optional()
|
|
37517
|
+
}).strict();
|
|
37518
|
+
function createDcmsendExecutor(options) {
|
|
37519
|
+
return async (files, timeoutMs, params, signal) => {
|
|
37520
|
+
const result = await dcmsend({
|
|
37521
|
+
host: options.host,
|
|
37522
|
+
port: options.port,
|
|
37523
|
+
files: [...files],
|
|
37524
|
+
calledAETitle: params.calledAETitle ?? options.calledAETitle,
|
|
37525
|
+
callingAETitle: params.callingAETitle ?? options.callingAETitle,
|
|
37526
|
+
maxPduReceive: options.maxPduReceive,
|
|
37527
|
+
maxPduSend: options.maxPduSend,
|
|
37528
|
+
associationTimeout: options.associationTimeout,
|
|
37529
|
+
acseTimeout: options.acseTimeout,
|
|
37530
|
+
dimseTimeout: options.dimseTimeout,
|
|
37531
|
+
noHostnameLookup: options.noHostnameLookup,
|
|
37532
|
+
verbosity: options.verbosity,
|
|
37533
|
+
noHalt: options.noHalt,
|
|
37534
|
+
noIllegalProposal: options.noIllegalProposal,
|
|
37535
|
+
decompress: options.decompress,
|
|
37536
|
+
multiAssociations: options.multiAssociations,
|
|
37537
|
+
noUidChecks: options.noUidChecks,
|
|
37538
|
+
timeoutMs,
|
|
37539
|
+
signal
|
|
37540
|
+
});
|
|
37541
|
+
if (!result.ok) return err(result.error);
|
|
37542
|
+
return ok({ stdout: result.value.stdout, stderr: result.value.stderr });
|
|
37543
|
+
};
|
|
37544
|
+
}
|
|
37545
|
+
function resolveConfig2(options) {
|
|
37546
|
+
const mode = options.mode ?? "multiple";
|
|
37547
|
+
const rawMax = options.maxAssociations ?? DEFAULT_MAX_ASSOCIATIONS2;
|
|
37548
|
+
return {
|
|
37549
|
+
mode,
|
|
37550
|
+
configuredMaxAssociations: mode === "single" ? 1 : rawMax,
|
|
37551
|
+
maxQueueLength: options.maxQueueLength ?? DEFAULT_MAX_QUEUE_LENGTH2,
|
|
37552
|
+
defaultTimeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
37553
|
+
defaultMaxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES2,
|
|
37554
|
+
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS2,
|
|
37555
|
+
bucketFlushMs: options.bucketFlushMs ?? DEFAULT_BUCKET_FLUSH_MS2,
|
|
37556
|
+
maxBucketSize: options.maxBucketSize ?? DEFAULT_MAX_BUCKET_SIZE2
|
|
37557
|
+
};
|
|
37558
|
+
}
|
|
37559
|
+
var DicomSend = class _DicomSend extends EventEmitter {
|
|
37560
|
+
constructor(engine, cfg, signal) {
|
|
37561
|
+
super();
|
|
37562
|
+
__publicField(this, "engine");
|
|
37563
|
+
__publicField(this, "defaultTimeoutMs");
|
|
37564
|
+
__publicField(this, "defaultMaxRetries");
|
|
37565
|
+
__publicField(this, "signal");
|
|
37566
|
+
__publicField(this, "abortHandler");
|
|
37567
|
+
this.setMaxListeners(20);
|
|
37568
|
+
this.on("error", () => {
|
|
37569
|
+
});
|
|
37570
|
+
this.engine = engine;
|
|
37571
|
+
this.defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
37572
|
+
this.defaultMaxRetries = cfg.defaultMaxRetries;
|
|
37573
|
+
this.signal = signal;
|
|
37574
|
+
if (signal !== void 0) {
|
|
37575
|
+
this.wireAbortSignal(signal);
|
|
37576
|
+
}
|
|
37577
|
+
}
|
|
37578
|
+
// -----------------------------------------------------------------------
|
|
37579
|
+
// Public API
|
|
37580
|
+
// -----------------------------------------------------------------------
|
|
37581
|
+
/**
|
|
37582
|
+
* Creates a new DicomSend instance.
|
|
37583
|
+
*
|
|
37584
|
+
* @param options - Configuration options
|
|
37585
|
+
* @returns A Result containing the instance or a validation error
|
|
37586
|
+
*/
|
|
37587
|
+
static create(options) {
|
|
37588
|
+
const validation = DicomSendOptionsSchema.safeParse(options);
|
|
37589
|
+
if (!validation.success) {
|
|
37590
|
+
return err(createValidationError("DicomSend", validation.error));
|
|
37591
|
+
}
|
|
37592
|
+
const cfg = resolveConfig2(options);
|
|
37593
|
+
const senderRef = { current: void 0 };
|
|
37594
|
+
const engine = new SenderEngine({
|
|
37595
|
+
...cfg,
|
|
37596
|
+
executor: createDcmsendExecutor(options),
|
|
37597
|
+
signal: options.signal,
|
|
37598
|
+
senderName: "DicomSend",
|
|
37599
|
+
emitters: {
|
|
37600
|
+
emitSendComplete: (data) => {
|
|
37601
|
+
senderRef.current.emit("SEND_COMPLETE", data);
|
|
37602
|
+
},
|
|
37603
|
+
emitSendFailed: (data) => {
|
|
37604
|
+
senderRef.current.emit("SEND_FAILED", data);
|
|
37605
|
+
},
|
|
37606
|
+
emitHealthChanged: (data) => {
|
|
37607
|
+
senderRef.current.emit("HEALTH_CHANGED", data);
|
|
37608
|
+
},
|
|
37609
|
+
emitBucketFlushed: (data) => {
|
|
37610
|
+
senderRef.current.emit("BUCKET_FLUSHED", data);
|
|
37611
|
+
}
|
|
37612
|
+
}
|
|
37613
|
+
});
|
|
37614
|
+
const sender = new _DicomSend(engine, cfg, options.signal);
|
|
37615
|
+
senderRef.current = sender;
|
|
37616
|
+
return ok(sender);
|
|
37617
|
+
}
|
|
37618
|
+
/**
|
|
37619
|
+
* Sends one or more DICOM files to the remote endpoint.
|
|
37620
|
+
*
|
|
37621
|
+
* In single/multiple mode, files are sent as one dcmsend call.
|
|
37622
|
+
* In bucket mode, files are accumulated into a bucket and flushed
|
|
37623
|
+
* when the bucket reaches maxBucketSize or the flush timer fires.
|
|
37624
|
+
*
|
|
37625
|
+
* @param files - One or more DICOM file paths
|
|
37626
|
+
* @param options - Per-send overrides
|
|
37627
|
+
* @returns A Result containing the send result or an error
|
|
37628
|
+
*/
|
|
37629
|
+
send(files, options) {
|
|
37630
|
+
const params = {
|
|
37631
|
+
calledAETitle: options?.calledAETitle,
|
|
37632
|
+
callingAETitle: options?.callingAETitle
|
|
37633
|
+
};
|
|
37634
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
37635
|
+
const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;
|
|
37636
|
+
return this.engine.send(files, timeoutMs, maxRetries, params);
|
|
37637
|
+
}
|
|
37638
|
+
/**
|
|
37639
|
+
* Flushes the current bucket immediately (bucket mode only).
|
|
37640
|
+
* In single/multiple mode this is a no-op.
|
|
37641
|
+
*/
|
|
37642
|
+
flush() {
|
|
37643
|
+
this.engine.flush();
|
|
37644
|
+
}
|
|
37645
|
+
/**
|
|
37646
|
+
* Gracefully stops the sender. Rejects all queued items and
|
|
37647
|
+
* waits for active associations to complete.
|
|
37648
|
+
*/
|
|
37649
|
+
async stop() {
|
|
37650
|
+
if (this.signal !== void 0 && this.abortHandler !== void 0) {
|
|
37651
|
+
this.signal.removeEventListener("abort", this.abortHandler);
|
|
37652
|
+
}
|
|
37653
|
+
await this.engine.stop();
|
|
37654
|
+
}
|
|
37655
|
+
/** Current sender status. */
|
|
37656
|
+
get status() {
|
|
37657
|
+
return this.engine.status;
|
|
37658
|
+
}
|
|
37659
|
+
// -----------------------------------------------------------------------
|
|
37660
|
+
// Typed event listener convenience methods
|
|
37661
|
+
// -----------------------------------------------------------------------
|
|
37662
|
+
/**
|
|
37663
|
+
* Registers a typed listener for a DicomSend-specific event.
|
|
37664
|
+
*
|
|
37665
|
+
* @param event - The event name from DicomSendEventMap
|
|
37666
|
+
* @param listener - Callback receiving typed event data
|
|
37667
|
+
* @returns this for chaining
|
|
37668
|
+
*/
|
|
37669
|
+
onEvent(event, listener) {
|
|
37670
|
+
return this.on(event, listener);
|
|
37671
|
+
}
|
|
37672
|
+
/**
|
|
37673
|
+
* Registers a listener for successful sends.
|
|
37674
|
+
*
|
|
37675
|
+
* @param listener - Callback receiving send complete data
|
|
37676
|
+
* @returns this for chaining
|
|
37677
|
+
*/
|
|
37678
|
+
onSendComplete(listener) {
|
|
37679
|
+
return this.on("SEND_COMPLETE", listener);
|
|
37680
|
+
}
|
|
37681
|
+
/**
|
|
37682
|
+
* Registers a listener for failed sends.
|
|
37683
|
+
*
|
|
37684
|
+
* @param listener - Callback receiving send failed data
|
|
37685
|
+
* @returns this for chaining
|
|
37686
|
+
*/
|
|
37687
|
+
onSendFailed(listener) {
|
|
37688
|
+
return this.on("SEND_FAILED", listener);
|
|
37689
|
+
}
|
|
37690
|
+
/**
|
|
37691
|
+
* Registers a listener for health state changes.
|
|
37692
|
+
*
|
|
37693
|
+
* @param listener - Callback receiving health change data
|
|
37694
|
+
* @returns this for chaining
|
|
37695
|
+
*/
|
|
37696
|
+
onHealthChanged(listener) {
|
|
37697
|
+
return this.on("HEALTH_CHANGED", listener);
|
|
37698
|
+
}
|
|
37699
|
+
/**
|
|
37700
|
+
* Registers a listener for bucket flushes (bucket mode only).
|
|
37701
|
+
*
|
|
37702
|
+
* @param listener - Callback receiving bucket flush data
|
|
37703
|
+
* @returns this for chaining
|
|
37704
|
+
*/
|
|
37705
|
+
onBucketFlushed(listener) {
|
|
37706
|
+
return this.on("BUCKET_FLUSHED", listener);
|
|
37707
|
+
}
|
|
37708
|
+
// -----------------------------------------------------------------------
|
|
37709
|
+
// Abort signal
|
|
37710
|
+
// -----------------------------------------------------------------------
|
|
37711
|
+
/** Wires an AbortSignal to stop the sender. */
|
|
37712
|
+
wireAbortSignal(signal) {
|
|
37713
|
+
if (signal.aborted) {
|
|
37714
|
+
void this.stop();
|
|
37715
|
+
return;
|
|
37716
|
+
}
|
|
37717
|
+
this.abortHandler = () => {
|
|
37718
|
+
void this.stop();
|
|
37719
|
+
};
|
|
37720
|
+
signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
37721
|
+
}
|
|
37722
|
+
};
|
|
37723
|
+
|
|
37386
37724
|
// src/pacs/types.ts
|
|
37387
37725
|
var QueryLevel = {
|
|
37388
37726
|
STUDY: "STUDY",
|
|
@@ -37932,7 +38270,7 @@ var DEFAULT_CONFIG = {
|
|
|
37932
38270
|
signal: void 0,
|
|
37933
38271
|
onRetry: void 0
|
|
37934
38272
|
};
|
|
37935
|
-
function
|
|
38273
|
+
function resolveConfig3(opts) {
|
|
37936
38274
|
if (!opts) return DEFAULT_CONFIG;
|
|
37937
38275
|
return {
|
|
37938
38276
|
...DEFAULT_CONFIG,
|
|
@@ -37977,7 +38315,7 @@ function shouldBreakAfterFailure(attempt, lastError, config) {
|
|
|
37977
38315
|
return false;
|
|
37978
38316
|
}
|
|
37979
38317
|
async function retry(operation, options) {
|
|
37980
|
-
const config =
|
|
38318
|
+
const config = resolveConfig3(options);
|
|
37981
38319
|
let lastResult = err(new Error("No attempts executed"));
|
|
37982
38320
|
for (let attempt = 0; attempt < config.maxAttempts; attempt += 1) {
|
|
37983
38321
|
lastResult = await operation();
|
|
@@ -37991,6 +38329,6 @@ async function retry(operation, options) {
|
|
|
37991
38329
|
return lastResult;
|
|
37992
38330
|
}
|
|
37993
38331
|
|
|
37994
|
-
export { AETitleSchema, AssociationTracker, ChangeSet, ColorConversion, DCMPRSCP_FATAL_EVENTS, DCMPRSCP_PATTERNS, DCMPSRCV_FATAL_EVENTS, DCMPSRCV_PATTERNS, DCMQRSCP_FATAL_EVENTS, DCMQRSCP_PATTERNS, DCMRECV_FATAL_EVENTS, DCMRECV_PATTERNS, DEFAULT_BLOCK_TIMEOUT_MS, DEFAULT_DICOM_PORT, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_PARSE_CONCURRENCY, DEFAULT_START_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, Dcm2pnmOutputFormat, Dcm2xmlCharset, DcmQRSCP, DcmdumpFormat, Dcmj2pnmOutputFormat, DcmprsCP, DcmprscpEvent, Dcmpsrcv, DcmpsrcvEvent, DcmqrscpEvent, Dcmrecv, DcmrecvEvent, DcmtkProcess, DicomDataset, DicomInstance, DicomReceiver, DicomSender, DicomTagPathSchema, DicomTagSchema, FilenameMode, GetQueryModel, Img2dcmInputFormat, JplsColorConversion, LineParser, LutType, MAX_BLOCK_LINES, MAX_CHANGESET_OPERATIONS, MAX_EVENT_PATTERNS, MAX_TRAVERSAL_DEPTH, MoveQueryModel, PDU_SIZE, PacsClient, PortSchema, PreferredTransferSyntax, ProcessState, ProposedTransferSyntax, QueryLevel, QueryModel, REQUIRED_BINARIES, RetrieveMode, SOP_CLASSES, STORESCP_FATAL_EVENTS, STORESCP_PATTERNS, SenderHealth, SenderMode, StorageMode, StoreSCP, StoreSCPPreset, StorescpEvent, SubdirectoryMode, TransferSyntax, UIDSchema, UNIX_SEARCH_PATHS, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, WINDOWS_SEARCH_PATHS, WLMSCPFS_FATAL_EVENTS, WLMSCPFS_PATTERNS, Wlmscpfs, WlmscpfsEvent, assertUnreachable, batch, cda2dcm, clearDcmtkPathCache, createAETitle, createDicomFilePath, createDicomTag, createDicomTagPath, createPort, createSOPClassUID, createTransferSyntaxUID, dcm2cda, dcm2json, dcm2pdf, dcm2pnm, dcm2xml, dcmcjpeg, dcmcjpls, dcmconv, dcmcrle, dcmdecap, dcmdjpeg, dcmdjpls, dcmdrle, dcmdspfn, dcmdump, dcmencap, dcmftest, dcmgpdir, dcmj2pnm, dcmmkcrv, dcmmkdir, dcmmklut, dcmodify, dcmp2pgm, dcmprscu, dcmpschk, dcmpsmk, dcmpsprt, dcmqridx, dcmquant, dcmscale, dcmsend, dcod2lum, dconvlum, drtdump, dsr2xml, dsrdump, dump2dcm, echoscu, err, execCommand, findDcmtkPath, findscu, getVRCategory, getscu, img2dcm, isBinaryVR, isNumericVR, isStringVR, json2dcm, lookupTag, lookupTagByKeyword, lookupTagByName, mapResult, movescu, ok, parseAETitle, parseDicomTag, parseDicomTagPath, parsePort, parseSOPClassUID, parseTransferSyntaxUID, pdf2dcm, retry, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, spawnCommand, stl2dcm, storescu, tag, tagPathToSegments, termscu, walkTags, xml2dcm, xml2dsr, xmlToJson };
|
|
38332
|
+
export { AETitleSchema, AssociationTracker, ChangeSet, ColorConversion, DCMPRSCP_FATAL_EVENTS, DCMPRSCP_PATTERNS, DCMPSRCV_FATAL_EVENTS, DCMPSRCV_PATTERNS, DCMQRSCP_FATAL_EVENTS, DCMQRSCP_PATTERNS, DCMRECV_FATAL_EVENTS, DCMRECV_PATTERNS, DEFAULT_BLOCK_TIMEOUT_MS, DEFAULT_DICOM_PORT, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_PARSE_CONCURRENCY, DEFAULT_START_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, Dcm2pnmOutputFormat, Dcm2xmlCharset, DcmQRSCP, DcmdumpFormat, Dcmj2pnmOutputFormat, DcmprsCP, DcmprscpEvent, Dcmpsrcv, DcmpsrcvEvent, DcmqrscpEvent, Dcmrecv, DcmrecvEvent, DcmtkProcess, DicomDataset, DicomInstance, DicomReceiver, DicomSend, DicomSender, DicomTagPathSchema, DicomTagSchema, FilenameMode, GetQueryModel, Img2dcmInputFormat, JplsColorConversion, LineParser, LutType, MAX_BLOCK_LINES, MAX_CHANGESET_OPERATIONS, MAX_EVENT_PATTERNS, MAX_TRAVERSAL_DEPTH, MoveQueryModel, PDU_SIZE, PacsClient, PortSchema, PreferredTransferSyntax, ProcessState, ProposedTransferSyntax, QueryLevel, QueryModel, REQUIRED_BINARIES, RetrieveMode, SOP_CLASSES, STORESCP_FATAL_EVENTS, STORESCP_PATTERNS, SenderHealth, SenderMode, StorageMode, StoreSCP, StoreSCPPreset, StorescpEvent, SubdirectoryMode, TransferSyntax, UIDSchema, UNIX_SEARCH_PATHS, VR, VR_CATEGORY, VR_CATEGORY_NAME, VR_META, WINDOWS_SEARCH_PATHS, WLMSCPFS_FATAL_EVENTS, WLMSCPFS_PATTERNS, Wlmscpfs, WlmscpfsEvent, assertUnreachable, batch, cda2dcm, clearDcmtkPathCache, createAETitle, createDicomFilePath, createDicomTag, createDicomTagPath, createPort, createSOPClassUID, createTransferSyntaxUID, dcm2cda, dcm2json, dcm2pdf, dcm2pnm, dcm2xml, dcmcjpeg, dcmcjpls, dcmconv, dcmcrle, dcmdecap, dcmdjpeg, dcmdjpls, dcmdrle, dcmdspfn, dcmdump, dcmencap, dcmftest, dcmgpdir, dcmj2pnm, dcmmkcrv, dcmmkdir, dcmmklut, dcmodify, dcmp2pgm, dcmprscu, dcmpschk, dcmpsmk, dcmpsprt, dcmqridx, dcmquant, dcmscale, dcmsend, dcod2lum, dconvlum, drtdump, dsr2xml, dsrdump, dump2dcm, echoscu, err, execCommand, findDcmtkPath, findscu, getVRCategory, getscu, img2dcm, isBinaryVR, isNumericVR, isStringVR, json2dcm, lookupTag, lookupTagByKeyword, lookupTagByName, mapResult, movescu, ok, parseAETitle, parseDicomTag, parseDicomTagPath, parsePort, parseSOPClassUID, parseTransferSyntaxUID, pdf2dcm, retry, segmentsToModifyPath, segmentsToString, sopClassNameFromUID, spawnCommand, stl2dcm, storescu, tag, tagPathToSegments, termscu, walkTags, xml2dcm, xml2dsr, xmlToJson };
|
|
37995
38333
|
//# sourceMappingURL=index.js.map
|
|
37996
38334
|
//# sourceMappingURL=index.js.map
|