@ubercode/dcmtk 0.10.0 → 0.11.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
@@ -2032,13 +2032,14 @@ async function execCommand(binary, args, options) {
2032
2032
  windowsHide: true,
2033
2033
  signal: options?.signal
2034
2034
  });
2035
- wireSpawnListeners(child, timeoutMs, resolve);
2035
+ wireSpawnListeners(binary, child, timeoutMs, resolve);
2036
2036
  });
2037
2037
  }
2038
- function wireSpawnListeners(child, timeoutMs, resolve) {
2038
+ function wireSpawnListeners(binary, child, timeoutMs, resolve) {
2039
2039
  let stdout = "";
2040
2040
  let stderr6 = "";
2041
2041
  let settled = false;
2042
+ const name = binary.split("/").pop() ?? binary;
2042
2043
  const settle = (result) => {
2043
2044
  if (settled) return;
2044
2045
  settled = true;
@@ -2047,25 +2048,25 @@ function wireSpawnListeners(child, timeoutMs, resolve) {
2047
2048
  };
2048
2049
  const timer = setTimeout(() => {
2049
2050
  if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
2050
- settle(err(new Error(`Process timed out after ${timeoutMs}ms`)));
2051
+ settle(err(new Error(`${name} timed out after ${timeoutMs}ms (pid: ${String(child.pid ?? "unknown")})`)));
2051
2052
  }, timeoutMs);
2052
2053
  child.stdout?.on("data", (chunk) => {
2053
2054
  stdout += String(chunk);
2054
2055
  if (stdout.length + stderr6.length > MAX_OUTPUT_BYTES) {
2055
2056
  if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
2056
- settle(err(new Error(`Process output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
2057
+ settle(err(new Error(`${name} output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
2057
2058
  }
2058
2059
  });
2059
2060
  child.stderr?.on("data", (chunk) => {
2060
2061
  stderr6 += String(chunk);
2061
2062
  if (stdout.length + stderr6.length > MAX_OUTPUT_BYTES) {
2062
2063
  if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
2063
- settle(err(new Error(`Process output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
2064
+ settle(err(new Error(`${name} output exceeded ${MAX_OUTPUT_BYTES} bytes`)));
2064
2065
  }
2065
2066
  });
2066
2067
  child.on("error", (error) => {
2067
2068
  if (child.pid !== void 0 && child.pid !== null) killTree(child.pid);
2068
- settle(err(new Error(`Process error: ${error.message}`)));
2069
+ settle(err(new Error(`${name} error: ${error.message}`)));
2069
2070
  });
2070
2071
  child.on("close", (code) => {
2071
2072
  settle(ok({ stdout, stderr: stderr6, exitCode: code ?? 1 }));
@@ -2080,7 +2081,7 @@ async function spawnCommand(binary, args, options) {
2080
2081
  windowsHide: true,
2081
2082
  signal: options?.signal
2082
2083
  });
2083
- wireSpawnListeners(child, timeoutMs, resolve);
2084
+ wireSpawnListeners(binary, child, timeoutMs, resolve);
2084
2085
  });
2085
2086
  }
2086
2087
  var PN_REPS = ["Alphabetic", "Ideographic", "Phonetic"];
@@ -3054,14 +3055,32 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3054
3055
  return this.on(event, listener);
3055
3056
  }
3056
3057
  /**
3057
- * Registers a listener for received files.
3058
+ * Registers a listener for files received by dcmrecv (before move/parse).
3058
3059
  *
3059
- * @param listener - Callback receiving file data
3060
+ * @param listener - Callback receiving raw file data
3060
3061
  * @returns this for chaining
3061
3062
  */
3062
3063
  onFileReceived(listener) {
3063
3064
  return this.on("FILE_RECEIVED", listener);
3064
3065
  }
3066
+ /**
3067
+ * Registers a listener for files stored in the association directory.
3068
+ *
3069
+ * @param listener - Callback receiving stored file data with size
3070
+ * @returns this for chaining
3071
+ */
3072
+ onFileStored(listener) {
3073
+ return this.on("FILE_STORED", listener);
3074
+ }
3075
+ /**
3076
+ * Registers a listener for parsed DicomInstance availability.
3077
+ *
3078
+ * @param listener - Callback receiving instance data
3079
+ * @returns this for chaining
3080
+ */
3081
+ onInstanceReceived(listener) {
3082
+ return this.on("INSTANCE_RECEIVED", listener);
3083
+ }
3065
3084
  /**
3066
3085
  * Registers a listener for completed associations.
3067
3086
  *
@@ -3355,11 +3374,18 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3355
3374
  });
3356
3375
  return;
3357
3376
  }
3377
+ this.emit("FILE_RECEIVED", {
3378
+ filePath: data.filePath,
3379
+ associationId: ctx.associationId,
3380
+ callingAE: data.callingAE,
3381
+ calledAE: data.calledAE,
3382
+ source: data.source
3383
+ });
3358
3384
  const promise = this.handleFileReceived(worker, data, ctx);
3359
3385
  worker.trackFile(promise);
3360
3386
  });
3361
3387
  }
3362
- /** Moves a received file, opens it as DicomInstance, and emits FILE_RECEIVED. */
3388
+ /** Moves file to association dir, emits FILE_STORED, then parses instance. */
3363
3389
  async handleFileReceived(worker, data, ctx) {
3364
3390
  try {
3365
3391
  await this.moveAndEmitFile(worker, data, ctx);
@@ -3376,7 +3402,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3376
3402
  });
3377
3403
  }
3378
3404
  }
3379
- /** Inner handler: move file, parse DICOM, emit FILE_RECEIVED or error. */
3405
+ /** Inner handler: move file, emit FILE_STORED, parse DICOM, emit INSTANCE_RECEIVED. */
3380
3406
  async moveAndEmitFile(worker, data, ctx) {
3381
3407
  const srcPath = data.filePath;
3382
3408
  const destPath = path__namespace.join(ctx.associationDir, path__namespace.basename(srcPath));
@@ -3384,28 +3410,42 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3384
3410
  const finalPath = moveResult.ok ? destPath : srcPath;
3385
3411
  const fileSize = await statFileSafe(finalPath);
3386
3412
  worker.recordFile(finalPath, fileSize);
3387
- const openResult = await DicomInstance.open(finalPath);
3388
- if (!openResult.ok) {
3389
- this.emit("error", {
3390
- error: openResult.error,
3391
- filePath: finalPath,
3392
- associationId: ctx.associationId,
3393
- associationDir: ctx.associationDir,
3394
- callingAE: data.callingAE,
3395
- calledAE: data.calledAE,
3396
- source: data.source
3397
- });
3398
- return;
3399
- }
3400
- this.emit("FILE_RECEIVED", {
3413
+ this.emit("FILE_STORED", {
3401
3414
  filePath: finalPath,
3402
3415
  fileSize,
3403
3416
  associationId: ctx.associationId,
3404
3417
  associationDir: ctx.associationDir,
3405
3418
  callingAE: data.callingAE,
3406
3419
  calledAE: data.calledAE,
3407
- source: data.source,
3408
- instance: openResult.value
3420
+ source: data.source
3421
+ });
3422
+ this.emitInstanceReceived(finalPath, fileSize, data, ctx);
3423
+ }
3424
+ /** Parses a DICOM file and emits INSTANCE_RECEIVED or error. */
3425
+ emitInstanceReceived(filePath, fileSize, data, ctx) {
3426
+ void DicomInstance.open(filePath).then((openResult) => {
3427
+ if (!openResult.ok) {
3428
+ this.emit("error", {
3429
+ error: openResult.error,
3430
+ filePath,
3431
+ associationId: ctx.associationId,
3432
+ associationDir: ctx.associationDir,
3433
+ callingAE: data.callingAE,
3434
+ calledAE: data.calledAE,
3435
+ source: data.source
3436
+ });
3437
+ return;
3438
+ }
3439
+ this.emit("INSTANCE_RECEIVED", {
3440
+ filePath,
3441
+ fileSize,
3442
+ associationId: ctx.associationId,
3443
+ associationDir: ctx.associationDir,
3444
+ callingAE: data.callingAE,
3445
+ calledAE: data.calledAE,
3446
+ source: data.source,
3447
+ instance: openResult.value
3448
+ });
3409
3449
  });
3410
3450
  }
3411
3451
  /** Returns worker to idle pool on association complete, emits summary. */