@ubercode/dcmtk 0.13.2 → 0.14.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/servers.cjs CHANGED
@@ -2396,15 +2396,17 @@ async function dcm2json(inputPath, options) {
2396
2396
  }
2397
2397
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2398
2398
  const signal = options?.signal;
2399
+ const startTime = Date.now();
2400
+ const remaining = () => Math.max(1e3, timeoutMs - (Date.now() - startTime));
2399
2401
  const verbosity = options?.verbosity;
2400
2402
  if (options?.directOnly === true) {
2401
- return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
2403
+ return tryDirectPath(inputPath, remaining(), signal, verbosity);
2402
2404
  }
2403
- const xmlResult = await tryXmlPath(inputPath, timeoutMs, signal, buildXmlOpts(options));
2405
+ const xmlResult = await tryXmlPath(inputPath, remaining(), signal, buildXmlOpts(options));
2404
2406
  if (xmlResult.ok) {
2405
2407
  return xmlResult;
2406
2408
  }
2407
- return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
2409
+ return tryDirectPath(inputPath, remaining(), signal, verbosity);
2408
2410
  }
2409
2411
  var TAG_OR_PATH_PATTERN = /^\([0-9A-Fa-f]{4},[0-9A-Fa-f]{4}\)(\[\d+\]\.\([0-9A-Fa-f]{4},[0-9A-Fa-f]{4}\))*(\[\d+\])?$/;
2410
2412
  var TagModificationSchema = zod.z.object({
@@ -2888,7 +2890,7 @@ var Worker = class {
2888
2890
  this.port = port;
2889
2891
  this.tempDir = tempDir;
2890
2892
  }
2891
- /** Current worker state. */
2893
+ /** Current worker state: idle (ready), busy (handling association), finalizing (draining). */
2892
2894
  get state() {
2893
2895
  return this._state;
2894
2896
  }
@@ -2920,6 +2922,10 @@ var Worker = class {
2920
2922
  this._instancesReceived = 0;
2921
2923
  this._instanceErrors = 0;
2922
2924
  }
2925
+ /** Marks the worker as finalizing — still has valid context but cannot accept new connections. */
2926
+ markFinalizing() {
2927
+ this._state = "finalizing";
2928
+ }
2923
2929
  /**
2924
2930
  * Returns the worker to idle. Context is intentionally preserved so that
2925
2931
  * late FILE_RECEIVED events (arriving in a subsequent pipe chunk after
@@ -3102,6 +3108,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3102
3108
  this.options.signal.removeEventListener("abort", this.abortHandler);
3103
3109
  }
3104
3110
  await this.closeTcpProxy();
3111
+ await this.drainAllWorkers();
3105
3112
  const stopPromises = [];
3106
3113
  for (const worker of this.workers.values()) {
3107
3114
  stopPromises.push(this.stopWorker(worker));
@@ -3270,6 +3277,18 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3270
3277
  this.tcpServer.close(() => resolve());
3271
3278
  });
3272
3279
  }
3280
+ /** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
3281
+ async drainAllWorkers() {
3282
+ const drainPromises = [];
3283
+ for (const worker of this.workers.values()) {
3284
+ if (worker.state !== "idle") {
3285
+ drainPromises.push(worker.drainPendingFiles());
3286
+ drainPromises.push(worker.drainInstancePending());
3287
+ }
3288
+ }
3289
+ if (drainPromises.length === 0) return;
3290
+ await Promise.race([Promise.all(drainPromises), promises.setTimeout(5e3)]);
3291
+ }
3273
3292
  // -----------------------------------------------------------------------
3274
3293
  // Connection routing
3275
3294
  // -----------------------------------------------------------------------
@@ -3298,7 +3317,9 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3298
3317
  };
3299
3318
  worker.beginAssociation(ctx);
3300
3319
  this.pipeConnection(worker, remoteSocket);
3301
- void this.replenishPool();
3320
+ void this.replenishPool().catch((e) => {
3321
+ this.emit("error", { error: e instanceof Error ? e : new Error(String(e)) });
3322
+ });
3302
3323
  }
3303
3324
  /** Finds an idle worker, retrying up to connectionTimeoutMs. */
3304
3325
  async findIdleWorker() {
@@ -3537,6 +3558,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3537
3558
  /** Returns worker to idle pool on association complete, emits summary. */
3538
3559
  wireAssociationComplete(worker) {
3539
3560
  worker.dcmrecv.onAssociationComplete((data) => {
3561
+ worker.markFinalizing();
3540
3562
  setImmediate(() => {
3541
3563
  void this.finalizeAssociation(worker, data);
3542
3564
  });