@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.
@@ -1777,6 +1777,8 @@ declare class DicomReceiver extends EventEmitter<DicomReceiverEventMap> {
1777
1777
  private startTcpProxy;
1778
1778
  /** Closes the TCP proxy server. */
1779
1779
  private closeTcpProxy;
1780
+ /** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
1781
+ private drainAllWorkers;
1780
1782
  /** Routes an incoming connection to an idle worker. */
1781
1783
  private handleConnection;
1782
1784
  /** Finds an idle worker, retrying up to connectionTimeoutMs. */
@@ -1777,6 +1777,8 @@ declare class DicomReceiver extends EventEmitter<DicomReceiverEventMap> {
1777
1777
  private startTcpProxy;
1778
1778
  /** Closes the TCP proxy server. */
1779
1779
  private closeTcpProxy;
1780
+ /** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
1781
+ private drainAllWorkers;
1780
1782
  /** Routes an incoming connection to an idle worker. */
1781
1783
  private handleConnection;
1782
1784
  /** Finds an idle worker, retrying up to connectionTimeoutMs. */
package/dist/index.cjs CHANGED
@@ -31552,15 +31552,17 @@ async function dcm2json(inputPath, options) {
31552
31552
  }
31553
31553
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
31554
31554
  const signal = options?.signal;
31555
+ const startTime = Date.now();
31556
+ const remaining = () => Math.max(1e3, timeoutMs - (Date.now() - startTime));
31555
31557
  const verbosity = options?.verbosity;
31556
31558
  if (options?.directOnly === true) {
31557
- return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
31559
+ return tryDirectPath(inputPath, remaining(), signal, verbosity);
31558
31560
  }
31559
- const xmlResult = await tryXmlPath(inputPath, timeoutMs, signal, buildXmlOpts(options));
31561
+ const xmlResult = await tryXmlPath(inputPath, remaining(), signal, buildXmlOpts(options));
31560
31562
  if (xmlResult.ok) {
31561
31563
  return xmlResult;
31562
31564
  }
31563
- return tryDirectPath(inputPath, timeoutMs, signal, verbosity);
31565
+ return tryDirectPath(inputPath, remaining(), signal, verbosity);
31564
31566
  }
31565
31567
  var DcmdumpFormat = {
31566
31568
  /** Print standard DCMTK format. */
@@ -36517,7 +36519,7 @@ var Worker = class {
36517
36519
  this.port = port;
36518
36520
  this.tempDir = tempDir;
36519
36521
  }
36520
- /** Current worker state. */
36522
+ /** Current worker state: idle (ready), busy (handling association), finalizing (draining). */
36521
36523
  get state() {
36522
36524
  return this._state;
36523
36525
  }
@@ -36549,6 +36551,10 @@ var Worker = class {
36549
36551
  this._instancesReceived = 0;
36550
36552
  this._instanceErrors = 0;
36551
36553
  }
36554
+ /** Marks the worker as finalizing — still has valid context but cannot accept new connections. */
36555
+ markFinalizing() {
36556
+ this._state = "finalizing";
36557
+ }
36552
36558
  /**
36553
36559
  * Returns the worker to idle. Context is intentionally preserved so that
36554
36560
  * late FILE_RECEIVED events (arriving in a subsequent pipe chunk after
@@ -36731,6 +36737,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36731
36737
  this.options.signal.removeEventListener("abort", this.abortHandler);
36732
36738
  }
36733
36739
  await this.closeTcpProxy();
36740
+ await this.drainAllWorkers();
36734
36741
  const stopPromises = [];
36735
36742
  for (const worker of this.workers.values()) {
36736
36743
  stopPromises.push(this.stopWorker(worker));
@@ -36899,6 +36906,18 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36899
36906
  this.tcpServer.close(() => resolve());
36900
36907
  });
36901
36908
  }
36909
+ /** Drains in-flight file and instance work on all non-idle workers, with a 5s safety timeout. */
36910
+ async drainAllWorkers() {
36911
+ const drainPromises = [];
36912
+ for (const worker of this.workers.values()) {
36913
+ if (worker.state !== "idle") {
36914
+ drainPromises.push(worker.drainPendingFiles());
36915
+ drainPromises.push(worker.drainInstancePending());
36916
+ }
36917
+ }
36918
+ if (drainPromises.length === 0) return;
36919
+ await Promise.race([Promise.all(drainPromises), promises.setTimeout(5e3)]);
36920
+ }
36902
36921
  // -----------------------------------------------------------------------
36903
36922
  // Connection routing
36904
36923
  // -----------------------------------------------------------------------
@@ -36927,7 +36946,9 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36927
36946
  };
36928
36947
  worker.beginAssociation(ctx);
36929
36948
  this.pipeConnection(worker, remoteSocket);
36930
- void this.replenishPool();
36949
+ void this.replenishPool().catch((e) => {
36950
+ this.emit("error", { error: e instanceof Error ? e : new Error(String(e)) });
36951
+ });
36931
36952
  }
36932
36953
  /** Finds an idle worker, retrying up to connectionTimeoutMs. */
36933
36954
  async findIdleWorker() {
@@ -37166,6 +37187,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
37166
37187
  /** Returns worker to idle pool on association complete, emits summary. */
37167
37188
  wireAssociationComplete(worker) {
37168
37189
  worker.dcmrecv.onAssociationComplete((data) => {
37190
+ worker.markFinalizing();
37169
37191
  setImmediate(() => {
37170
37192
  void this.finalizeAssociation(worker, data);
37171
37193
  });