dicom-curate 0.30.0 → 0.32.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.
@@ -80107,8 +80107,8 @@ async function curateOne({
80107
80107
  console.error(
80108
80108
  `Upload failed for ${uploadUrl}: ${resp.status} ${resp.statusText}`
80109
80109
  );
80110
- clonedMapResults.errors = clonedMapResults.errors ?? [];
80111
- clonedMapResults.errors.push(
80110
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
80111
+ clonedMapResults.uploadErrors.push(
80112
80112
  `Upload failed: ${resp.status} ${resp.statusText}`
80113
80113
  );
80114
80114
  } else {
@@ -80121,8 +80121,8 @@ async function curateOne({
80121
80121
  }
80122
80122
  } catch (e4) {
80123
80123
  console.error("Upload error", e4);
80124
- clonedMapResults.errors = clonedMapResults.errors ?? [];
80125
- clonedMapResults.errors.push(
80124
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
80125
+ clonedMapResults.uploadErrors.push(
80126
80126
  `Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
80127
80127
  );
80128
80128
  }
@@ -80161,8 +80161,8 @@ async function curateOne({
80161
80161
  };
80162
80162
  } catch (e4) {
80163
80163
  console.error("S3 Upload error", e4);
80164
- clonedMapResults.errors = clonedMapResults.errors ?? [];
80165
- clonedMapResults.errors.push(
80164
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
80165
+ clonedMapResults.uploadErrors.push(
80166
80166
  `S3 Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
80167
80167
  );
80168
80168
  }
@@ -73816,8 +73816,8 @@ async function curateOne({
73816
73816
  console.error(
73817
73817
  `Upload failed for ${uploadUrl}: ${resp.status} ${resp.statusText}`
73818
73818
  );
73819
- clonedMapResults.errors = clonedMapResults.errors ?? [];
73820
- clonedMapResults.errors.push(
73819
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
73820
+ clonedMapResults.uploadErrors.push(
73821
73821
  `Upload failed: ${resp.status} ${resp.statusText}`
73822
73822
  );
73823
73823
  } else {
@@ -73830,8 +73830,8 @@ async function curateOne({
73830
73830
  }
73831
73831
  } catch (e4) {
73832
73832
  console.error("Upload error", e4);
73833
- clonedMapResults.errors = clonedMapResults.errors ?? [];
73834
- clonedMapResults.errors.push(
73833
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
73834
+ clonedMapResults.uploadErrors.push(
73835
73835
  `Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
73836
73836
  );
73837
73837
  }
@@ -73870,8 +73870,8 @@ async function curateOne({
73870
73870
  };
73871
73871
  } catch (e4) {
73872
73872
  console.error("S3 Upload error", e4);
73873
- clonedMapResults.errors = clonedMapResults.errors ?? [];
73874
- clonedMapResults.errors.push(
73873
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
73874
+ clonedMapResults.uploadErrors.push(
73875
73875
  `S3 Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
73876
73876
  );
73877
73877
  }
package/dist/esm/index.js CHANGED
@@ -81667,8 +81667,8 @@ async function curateOne({
81667
81667
  console.error(
81668
81668
  `Upload failed for ${uploadUrl}: ${resp.status} ${resp.statusText}`
81669
81669
  );
81670
- clonedMapResults.errors = clonedMapResults.errors ?? [];
81671
- clonedMapResults.errors.push(
81670
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
81671
+ clonedMapResults.uploadErrors.push(
81672
81672
  `Upload failed: ${resp.status} ${resp.statusText}`
81673
81673
  );
81674
81674
  } else {
@@ -81681,8 +81681,8 @@ async function curateOne({
81681
81681
  }
81682
81682
  } catch (e4) {
81683
81683
  console.error("Upload error", e4);
81684
- clonedMapResults.errors = clonedMapResults.errors ?? [];
81685
- clonedMapResults.errors.push(
81684
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
81685
+ clonedMapResults.uploadErrors.push(
81686
81686
  `Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
81687
81687
  );
81688
81688
  }
@@ -81721,8 +81721,8 @@ async function curateOne({
81721
81721
  };
81722
81722
  } catch (e4) {
81723
81723
  console.error("S3 Upload error", e4);
81724
- clonedMapResults.errors = clonedMapResults.errors ?? [];
81725
- clonedMapResults.errors.push(
81724
+ clonedMapResults.uploadErrors = clonedMapResults.uploadErrors ?? [];
81725
+ clonedMapResults.uploadErrors.push(
81726
81726
  `S3 Upload error: ${e4 instanceof Error ? e4.message : String(e4)}`
81727
81727
  );
81728
81728
  }
@@ -87495,6 +87495,7 @@ var progressCallback = () => {
87495
87495
  };
87496
87496
  var scanResumeCallback = null;
87497
87497
  var scanPaused = false;
87498
+ var totalDiscoveredFiles = void 0;
87498
87499
  var LOW_WATER_MARK = 50;
87499
87500
  function setMappingWorkerOptions(opts) {
87500
87501
  mappingWorkerOptions = opts;
@@ -87503,6 +87504,9 @@ function setScanResumeCallback(cb) {
87503
87504
  scanResumeCallback = cb;
87504
87505
  scanPaused = false;
87505
87506
  }
87507
+ function setTotalDiscoveredFiles(n4) {
87508
+ totalDiscoveredFiles = n4;
87509
+ }
87506
87510
  function markScanPaused() {
87507
87511
  scanPaused = true;
87508
87512
  }
@@ -87538,6 +87542,7 @@ async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, p
87538
87542
  filesToProcess = [];
87539
87543
  directoryScanFinished = false;
87540
87544
  scanAnomalies = [];
87545
+ totalDiscoveredFiles = void 0;
87541
87546
  if (progressCb)
87542
87547
  progressCallback = progressCb;
87543
87548
  const effectiveWorkerCount = workerCount ?? Math.min(await getHardwareConcurrency(), 8);
@@ -87642,7 +87647,7 @@ function recoverCrashedWorker(mappingWorker, errorMessage) {
87642
87647
  response: "progress",
87643
87648
  mapResults: errorMapResults,
87644
87649
  processedFiles: filesMapped,
87645
- totalFiles: filesToProcess.length + filesMapped + workersActive
87650
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
87646
87651
  });
87647
87652
  dispatchMappingJobs();
87648
87653
  pendingReplacements += 1;
@@ -87704,7 +87709,7 @@ async function createMappingWorker() {
87704
87709
  response: "progress",
87705
87710
  mapResults: event.data.mapResults,
87706
87711
  processedFiles: filesMapped,
87707
- totalFiles: filesToProcess.length + filesMapped + workersActive
87712
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
87708
87713
  });
87709
87714
  dispatchMappingJobs();
87710
87715
  if (filesMapped % 100 === 0) {
@@ -87730,7 +87735,7 @@ async function createMappingWorker() {
87730
87735
  response: "progress",
87731
87736
  mapResults: errorMapResults,
87732
87737
  processedFiles: filesMapped,
87733
- totalFiles: filesToProcess.length + filesMapped + workersActive
87738
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
87734
87739
  });
87735
87740
  dispatchMappingJobs();
87736
87741
  break;
@@ -87795,6 +87800,10 @@ async function initializeFileListWorker(rejectCallback) {
87795
87800
  scanAnomalies.push({ fileInfo: anomalyFileInfo, anomalies });
87796
87801
  break;
87797
87802
  }
87803
+ case "count": {
87804
+ setTotalDiscoveredFiles(event.data.totalDiscovered);
87805
+ break;
87806
+ }
87798
87807
  case "done": {
87799
87808
  console.log("directoryScanFinished");
87800
87809
  setDirectoryScanFinished(true);
@@ -87810,7 +87819,11 @@ async function initializeFileListWorker(rejectCallback) {
87810
87819
  break;
87811
87820
  }
87812
87821
  default: {
87813
- console.error(`Unknown response from worker ${event.data.response}`);
87822
+ console.error(
87823
+ // @ts-expect-error: event.data is never here (all cases handled),
87824
+ // but we log anyway as a defensive guard against future message types.
87825
+ `Unknown response from worker ${event.data.response}`
87826
+ );
87814
87827
  }
87815
87828
  }
87816
87829
  dispatchMappingJobs();
@@ -12067,6 +12067,7 @@ var progressCallback = () => {
12067
12067
  };
12068
12068
  var scanResumeCallback = null;
12069
12069
  var scanPaused = false;
12070
+ var totalDiscoveredFiles = void 0;
12070
12071
  var LOW_WATER_MARK = 50;
12071
12072
  function setMappingWorkerOptions(opts) {
12072
12073
  mappingWorkerOptions = opts;
@@ -12075,6 +12076,9 @@ function setScanResumeCallback(cb) {
12075
12076
  scanResumeCallback = cb;
12076
12077
  scanPaused = false;
12077
12078
  }
12079
+ function setTotalDiscoveredFiles(n) {
12080
+ totalDiscoveredFiles = n;
12081
+ }
12078
12082
  function markScanPaused() {
12079
12083
  scanPaused = true;
12080
12084
  }
@@ -12113,6 +12117,7 @@ async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, p
12113
12117
  filesToProcess = [];
12114
12118
  directoryScanFinished = false;
12115
12119
  scanAnomalies = [];
12120
+ totalDiscoveredFiles = void 0;
12116
12121
  if (progressCb)
12117
12122
  progressCallback = progressCb;
12118
12123
  const effectiveWorkerCount = workerCount ?? Math.min(await getHardwareConcurrency(), 8);
@@ -12217,7 +12222,7 @@ function recoverCrashedWorker(mappingWorker, errorMessage) {
12217
12222
  response: "progress",
12218
12223
  mapResults: errorMapResults,
12219
12224
  processedFiles: filesMapped,
12220
- totalFiles: filesToProcess.length + filesMapped + workersActive
12225
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
12221
12226
  });
12222
12227
  dispatchMappingJobs();
12223
12228
  pendingReplacements += 1;
@@ -12279,7 +12284,7 @@ async function createMappingWorker() {
12279
12284
  response: "progress",
12280
12285
  mapResults: event.data.mapResults,
12281
12286
  processedFiles: filesMapped,
12282
- totalFiles: filesToProcess.length + filesMapped + workersActive
12287
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
12283
12288
  });
12284
12289
  dispatchMappingJobs();
12285
12290
  if (filesMapped % 100 === 0) {
@@ -12305,7 +12310,7 @@ async function createMappingWorker() {
12305
12310
  response: "progress",
12306
12311
  mapResults: errorMapResults,
12307
12312
  processedFiles: filesMapped,
12308
- totalFiles: filesToProcess.length + filesMapped + workersActive
12313
+ totalFiles: totalDiscoveredFiles ?? filesToProcess.length + filesMapped + workersActive
12309
12314
  });
12310
12315
  dispatchMappingJobs();
12311
12316
  break;
@@ -12339,5 +12344,6 @@ export {
12339
12344
  setDirectoryScanFinished,
12340
12345
  setMappingWorkerOptions,
12341
12346
  setScanResumeCallback,
12347
+ setTotalDiscoveredFiles,
12342
12348
  terminateAllWorkers
12343
12349
  };
@@ -37139,8 +37139,12 @@ var DEFAULT_EXCLUDED_FILETYPES = [
37139
37139
  var keepScanning = true;
37140
37140
  var pauseResolve = null;
37141
37141
  var pausePromise = null;
37142
+ var totalDiscovered = 0;
37143
+ var countingMode = false;
37144
+ var fileBuffer = [];
37142
37145
  function pauseScanning() {
37143
37146
  if (!pausePromise) {
37147
+ countingMode = true;
37144
37148
  pausePromise = new Promise((resolve) => {
37145
37149
  pauseResolve = resolve;
37146
37150
  });
@@ -37148,6 +37152,7 @@ function pauseScanning() {
37148
37152
  }
37149
37153
  function resumeScanning() {
37150
37154
  if (pauseResolve) {
37155
+ countingMode = false;
37151
37156
  pauseResolve();
37152
37157
  pauseResolve = null;
37153
37158
  pausePromise = null;
@@ -37281,6 +37286,113 @@ async function shouldProcessFileNode(filePath, fileName, fileSize, fileAnomalies
37281
37286
  return true;
37282
37287
  }
37283
37288
  }
37289
+ function cheapFilter(fileName, fileSize, filePath) {
37290
+ const allExcludedFiletypes = [
37291
+ ...noDefaultExclusions ? [] : DEFAULT_EXCLUDED_FILETYPES,
37292
+ ...excludedFiletypes
37293
+ ];
37294
+ if (excludedPathRegexes.some((regex) => regex.test(filePath))) {
37295
+ return false;
37296
+ }
37297
+ if (allExcludedFiletypes.some(
37298
+ (excluded) => fileName.toLowerCase() === excluded.toLowerCase()
37299
+ )) {
37300
+ return false;
37301
+ }
37302
+ if (noDicomSignatureCheck) {
37303
+ return true;
37304
+ }
37305
+ if (fileSize < 132) {
37306
+ return false;
37307
+ }
37308
+ return true;
37309
+ }
37310
+ async function drainBuffer() {
37311
+ while (fileBuffer.length > 0 && keepScanning) {
37312
+ const item = fileBuffer.shift();
37313
+ const fileAnomalies = [];
37314
+ if (item.kind === "handle") {
37315
+ const filePath = `${item.prefix}/${item.name}`;
37316
+ if (await shouldProcessFile(item.file, fileAnomalies, filePath)) {
37317
+ globalThis.postMessage({
37318
+ response: "file",
37319
+ fileInfo: {
37320
+ path: item.prefix,
37321
+ name: item.name,
37322
+ size: item.size,
37323
+ kind: "handle",
37324
+ fileHandle: item.entry
37325
+ },
37326
+ previousFileInfo: item.prev
37327
+ });
37328
+ } else {
37329
+ totalDiscovered--;
37330
+ globalThis.postMessage({
37331
+ response: "count",
37332
+ totalDiscovered
37333
+ });
37334
+ if (fileAnomalies.length > 0) {
37335
+ globalThis.postMessage({
37336
+ response: "scanAnomalies",
37337
+ fileInfo: {
37338
+ path: item.prefix,
37339
+ name: item.name,
37340
+ size: item.size,
37341
+ kind: "handle",
37342
+ fileHandle: item.entry
37343
+ },
37344
+ anomalies: fileAnomalies,
37345
+ previousFileInfo: item.prev
37346
+ });
37347
+ }
37348
+ }
37349
+ } else {
37350
+ if (await shouldProcessFileNode(
37351
+ item.filePath,
37352
+ item.name,
37353
+ item.size,
37354
+ fileAnomalies,
37355
+ `${item.prefix}/${item.name}`
37356
+ )) {
37357
+ globalThis.postMessage({
37358
+ response: "file",
37359
+ fileInfo: {
37360
+ path: item.prefix,
37361
+ name: item.name,
37362
+ size: item.size,
37363
+ kind: "path",
37364
+ fullPath: item.filePath
37365
+ },
37366
+ previousFileInfo: item.prev
37367
+ });
37368
+ } else {
37369
+ totalDiscovered--;
37370
+ globalThis.postMessage({
37371
+ response: "count",
37372
+ totalDiscovered
37373
+ });
37374
+ if (fileAnomalies.length > 0) {
37375
+ globalThis.postMessage({
37376
+ response: "scanAnomalies",
37377
+ fileInfo: {
37378
+ path: item.prefix,
37379
+ name: item.name,
37380
+ size: item.size,
37381
+ kind: "path",
37382
+ fullPath: item.filePath
37383
+ },
37384
+ anomalies: fileAnomalies,
37385
+ previousFileInfo: item.prev
37386
+ });
37387
+ }
37388
+ }
37389
+ }
37390
+ if (!await waitIfPaused())
37391
+ return;
37392
+ if (countingMode)
37393
+ return;
37394
+ }
37395
+ }
37284
37396
  fixupNodeWorkerEnvironment().then(() => {
37285
37397
  globalThis.addEventListener("message", (event) => {
37286
37398
  switch (event.data.request) {
@@ -37304,6 +37416,9 @@ fixupNodeWorkerEnvironment().then(() => {
37304
37416
  noDicomSignatureCheck = event.data.noDicomSignatureCheck ?? false;
37305
37417
  noDefaultExclusions = event.data.noDefaultExclusions ?? false;
37306
37418
  keepScanning = true;
37419
+ totalDiscovered = 0;
37420
+ countingMode = false;
37421
+ fileBuffer.length = 0;
37307
37422
  if ("path" in eventData) {
37308
37423
  scanDirectoryNode(eventData.path);
37309
37424
  } else if ("directoryHandle" in eventData) {
@@ -37349,6 +37464,7 @@ async function scanS3Bucket(bucketOptions) {
37349
37464
  for (const item of data2.Contents) {
37350
37465
  const fileAnomalies = [];
37351
37466
  if (item.Key && item.Size !== void 0 && await shouldProcessFileItem(item, fileAnomalies)) {
37467
+ totalDiscovered++;
37352
37468
  const prev = previousIndex ? previousIndex[item.Key] : void 0;
37353
37469
  globalThis.postMessage({
37354
37470
  response: "file",
@@ -37397,18 +37513,54 @@ async function scanDirectory(dir) {
37397
37513
  for await (const entry of dir2.values()) {
37398
37514
  if (!keepScanning)
37399
37515
  return;
37400
- if (!await waitIfPaused())
37401
- return;
37402
37516
  if (entry.kind === "file") {
37403
37517
  const file = await entry.getFile();
37518
+ const key = `${prefix}/${entry.name}`;
37519
+ const prev = previousIndex ? previousIndex[key] : void 0;
37520
+ if (countingMode) {
37521
+ if (cheapFilter(entry.name, file.size, key)) {
37522
+ totalDiscovered++;
37523
+ fileBuffer.push({
37524
+ kind: "handle",
37525
+ entry,
37526
+ file,
37527
+ prefix,
37528
+ name: entry.name,
37529
+ size: file.size,
37530
+ prev
37531
+ });
37532
+ globalThis.postMessage({
37533
+ response: "count",
37534
+ totalDiscovered
37535
+ });
37536
+ }
37537
+ continue;
37538
+ }
37539
+ if (fileBuffer.length > 0) {
37540
+ await drainBuffer();
37541
+ if (countingMode) {
37542
+ if (cheapFilter(entry.name, file.size, key)) {
37543
+ totalDiscovered++;
37544
+ fileBuffer.push({
37545
+ kind: "handle",
37546
+ entry,
37547
+ file,
37548
+ prefix,
37549
+ name: entry.name,
37550
+ size: file.size,
37551
+ prev
37552
+ });
37553
+ globalThis.postMessage({
37554
+ response: "count",
37555
+ totalDiscovered
37556
+ });
37557
+ }
37558
+ continue;
37559
+ }
37560
+ }
37404
37561
  const fileAnomalies = [];
37405
- if (await shouldProcessFile(
37406
- file,
37407
- fileAnomalies,
37408
- `${prefix}/${entry.name}`
37409
- )) {
37410
- const key = `${prefix}/${entry.name}`;
37411
- const prev = previousIndex ? previousIndex[key] : void 0;
37562
+ if (await shouldProcessFile(file, fileAnomalies, key)) {
37563
+ totalDiscovered++;
37412
37564
  globalThis.postMessage({
37413
37565
  response: "file",
37414
37566
  fileInfo: {
@@ -37421,8 +37573,6 @@ async function scanDirectory(dir) {
37421
37573
  previousFileInfo: prev
37422
37574
  });
37423
37575
  } else if (fileAnomalies.length > 0) {
37424
- const key = `${prefix}/${entry.name}`;
37425
- const prev = previousIndex ? previousIndex[key] : void 0;
37426
37576
  globalThis.postMessage({
37427
37577
  response: "scanAnomalies",
37428
37578
  fileInfo: {
@@ -37436,6 +37586,8 @@ async function scanDirectory(dir) {
37436
37586
  previousFileInfo: prev
37437
37587
  });
37438
37588
  }
37589
+ if (!await waitIfPaused())
37590
+ return;
37439
37591
  } else if (entry.kind === "directory") {
37440
37592
  await traverse(
37441
37593
  entry,
@@ -37446,6 +37598,10 @@ async function scanDirectory(dir) {
37446
37598
  }
37447
37599
  try {
37448
37600
  await traverse(dir, dir.name);
37601
+ if (fileBuffer.length > 0) {
37602
+ countingMode = false;
37603
+ await drainBuffer();
37604
+ }
37449
37605
  globalThis.postMessage({ response: "done" });
37450
37606
  } catch (error2) {
37451
37607
  globalThis.postMessage({
@@ -37466,13 +37622,51 @@ async function scanDirectoryNode(dirPath) {
37466
37622
  for (const entry of entries) {
37467
37623
  if (!keepScanning)
37468
37624
  return;
37469
- if (!await waitIfPaused())
37470
- return;
37471
37625
  if (entry.isFile()) {
37472
37626
  const filePath = path.join(currentPath, entry.name);
37473
37627
  const stats = await fs.stat(filePath);
37474
- const fileAnomalies = [];
37475
37628
  const key = `${prefix}/${entry.name}`;
37629
+ const prev = previousIndex ? previousIndex[key] : void 0;
37630
+ if (countingMode) {
37631
+ if (cheapFilter(entry.name, stats.size, key)) {
37632
+ totalDiscovered++;
37633
+ fileBuffer.push({
37634
+ kind: "path",
37635
+ filePath,
37636
+ name: entry.name,
37637
+ size: stats.size,
37638
+ prefix,
37639
+ prev
37640
+ });
37641
+ globalThis.postMessage({
37642
+ response: "count",
37643
+ totalDiscovered
37644
+ });
37645
+ }
37646
+ continue;
37647
+ }
37648
+ if (fileBuffer.length > 0) {
37649
+ await drainBuffer();
37650
+ if (countingMode) {
37651
+ if (cheapFilter(entry.name, stats.size, key)) {
37652
+ totalDiscovered++;
37653
+ fileBuffer.push({
37654
+ kind: "path",
37655
+ filePath,
37656
+ name: entry.name,
37657
+ size: stats.size,
37658
+ prefix,
37659
+ prev
37660
+ });
37661
+ globalThis.postMessage({
37662
+ response: "count",
37663
+ totalDiscovered
37664
+ });
37665
+ }
37666
+ continue;
37667
+ }
37668
+ }
37669
+ const fileAnomalies = [];
37476
37670
  if (await shouldProcessFileNode(
37477
37671
  filePath,
37478
37672
  entry.name,
@@ -37480,7 +37674,7 @@ async function scanDirectoryNode(dirPath) {
37480
37674
  fileAnomalies,
37481
37675
  key
37482
37676
  )) {
37483
- const prev = previousIndex ? previousIndex[key] : void 0;
37677
+ totalDiscovered++;
37484
37678
  globalThis.postMessage({
37485
37679
  response: "file",
37486
37680
  fileInfo: {
@@ -37493,7 +37687,6 @@ async function scanDirectoryNode(dirPath) {
37493
37687
  previousFileInfo: prev
37494
37688
  });
37495
37689
  } else if (fileAnomalies.length > 0) {
37496
- const prev = previousIndex ? previousIndex[key] : void 0;
37497
37690
  globalThis.postMessage({
37498
37691
  response: "scanAnomalies",
37499
37692
  fileInfo: {
@@ -37507,6 +37700,8 @@ async function scanDirectoryNode(dirPath) {
37507
37700
  previousFileInfo: prev
37508
37701
  });
37509
37702
  }
37703
+ if (!await waitIfPaused())
37704
+ return;
37510
37705
  } else if (entry.isDirectory()) {
37511
37706
  await traverse(
37512
37707
  path.join(currentPath, entry.name),
@@ -37517,6 +37712,10 @@ async function scanDirectoryNode(dirPath) {
37517
37712
  }
37518
37713
  const dirName = await import("path").then((p4) => p4.basename(dirPath));
37519
37714
  await traverse(dirPath, dirName);
37715
+ if (fileBuffer.length > 0) {
37716
+ countingMode = false;
37717
+ await drainBuffer();
37718
+ }
37520
37719
  globalThis.postMessage({ response: "done" });
37521
37720
  } catch (error2) {
37522
37721
  globalThis.postMessage({
@@ -1,4 +1,4 @@
1
- import type { TFileInfo, THashMethod, TOutputTarget, TMappingOptions, TMapResults } from './types';
1
+ import type { TFileInfo, THashMethod, TMappingOptions, TMapResults, TOutputTarget } from './types';
2
2
  export type TCurateOneArgs = {
3
3
  fileInfo: TFileInfo;
4
4
  outputTarget: TOutputTarget;
@@ -34,6 +34,11 @@ export declare function setMappingWorkerOptions(opts: TMappingWorkerOptions): vo
34
34
  * after the scan worker is created.
35
35
  */
36
36
  export declare function setScanResumeCallback(cb: (() => void) | null): void;
37
+ /**
38
+ * Update the total discovered file count from the scan worker's 'count'
39
+ * messages. Pass undefined to reset (e.g. at the start of a new run).
40
+ */
41
+ export declare function setTotalDiscoveredFiles(n: number | undefined): void;
37
42
  /**
38
43
  * Mark the scan as paused. Called from the scan worker message handler in
39
44
  * index.ts when the queue exceeds the high-water mark.
@@ -20,6 +20,9 @@ export type FileScanMsg = {
20
20
  } | {
21
21
  response: 'error';
22
22
  error: string;
23
+ } | {
24
+ response: 'count';
25
+ totalDiscovered: number;
23
26
  } | {
24
27
  response: 'done';
25
28
  };
@@ -1,6 +1,6 @@
1
- import { TColumnMappings, TMappedValues, Row } from './csvMapping';
2
1
  import type { TNaturalData } from 'dcmjs';
3
2
  import type { SpecPart } from './composeSpecs';
3
+ import type { Row, TColumnMappings, TMappedValues } from './csvMapping';
4
4
  export type Iso8601Duration = string;
5
5
  export type TPs315Options = {
6
6
  cleanDescriptorsOption: boolean;
@@ -131,6 +131,9 @@ export type TMapResults = {
131
131
  };
132
132
  anomalies: string[];
133
133
  errors: string[];
134
+ /** Upload/write failures only (retryable). Separate from `errors` which
135
+ * contains DICOM validation issues that cannot be resolved by retrying. */
136
+ uploadErrors?: string[];
134
137
  quarantine: {
135
138
  [objectPath: string]: string;
136
139
  };