@ubercode/dcmtk 0.13.1 → 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,10 +36551,18 @@ var Worker = class {
36549
36551
  this._instancesReceived = 0;
36550
36552
  this._instanceErrors = 0;
36551
36553
  }
36552
- /** Returns the worker to idle and clears all association state. */
36554
+ /** Marks the worker as finalizing still has valid context but cannot accept new connections. */
36555
+ markFinalizing() {
36556
+ this._state = "finalizing";
36557
+ }
36558
+ /**
36559
+ * Returns the worker to idle. Context is intentionally preserved so that
36560
+ * late FILE_RECEIVED events (arriving in a subsequent pipe chunk after
36561
+ * ASSOCIATION_RELEASE) still have valid association info. Context is
36562
+ * cleared on the next {@link beginAssociation} call.
36563
+ */
36553
36564
  endAssociation() {
36554
36565
  this._state = "idle";
36555
- this._context = void 0;
36556
36566
  this._files.length = 0;
36557
36567
  this._fileSizes.length = 0;
36558
36568
  this._outputLines.length = 0;
@@ -36727,6 +36737,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36727
36737
  this.options.signal.removeEventListener("abort", this.abortHandler);
36728
36738
  }
36729
36739
  await this.closeTcpProxy();
36740
+ await this.drainAllWorkers();
36730
36741
  const stopPromises = [];
36731
36742
  for (const worker of this.workers.values()) {
36732
36743
  stopPromises.push(this.stopWorker(worker));
@@ -36895,6 +36906,18 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36895
36906
  this.tcpServer.close(() => resolve());
36896
36907
  });
36897
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
+ }
36898
36921
  // -----------------------------------------------------------------------
36899
36922
  // Connection routing
36900
36923
  // -----------------------------------------------------------------------
@@ -36923,7 +36946,9 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
36923
36946
  };
36924
36947
  worker.beginAssociation(ctx);
36925
36948
  this.pipeConnection(worker, remoteSocket);
36926
- void this.replenishPool();
36949
+ void this.replenishPool().catch((e) => {
36950
+ this.emit("error", { error: e instanceof Error ? e : new Error(String(e)) });
36951
+ });
36927
36952
  }
36928
36953
  /** Finds an idle worker, retrying up to connectionTimeoutMs. */
36929
36954
  async findIdleWorker() {
@@ -37162,7 +37187,10 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
37162
37187
  /** Returns worker to idle pool on association complete, emits summary. */
37163
37188
  wireAssociationComplete(worker) {
37164
37189
  worker.dcmrecv.onAssociationComplete((data) => {
37165
- void this.finalizeAssociation(worker, data);
37190
+ worker.markFinalizing();
37191
+ setImmediate(() => {
37192
+ void this.finalizeAssociation(worker, data);
37193
+ });
37166
37194
  });
37167
37195
  }
37168
37196
  /** Awaits file ops, emits ASSOCIATION_COMPLETE, awaits parsing, emits ASSOCIATION_FINALIZED. */