dicom-curate 0.29.0 → 0.30.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/esm/index.js +72 -7
- package/dist/esm/mappingWorkerPool.js +37 -1
- package/dist/types/mappingWorkerPool.d.ts +12 -0
- package/dist/types/types.d.ts +1 -0
- package/dist/umd/dicom-curate.umd.js +90 -3
- package/dist/umd/dicom-curate.umd.js.map +1 -1
- package/dist/umd/dicom-curate.umd.min.js +1 -1
- package/dist/umd/dicom-curate.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -48613,14 +48613,14 @@ var require_dist_cjs70 = __commonJS({
|
|
|
48613
48613
|
const exitConditions = [runPolling(params, input, acceptorChecks)];
|
|
48614
48614
|
const finalize = [];
|
|
48615
48615
|
if (options.abortSignal) {
|
|
48616
|
-
const { aborted, clearListener } = abortTimeout(options.abortSignal);
|
|
48616
|
+
const { aborted: aborted2, clearListener } = abortTimeout(options.abortSignal);
|
|
48617
48617
|
finalize.push(clearListener);
|
|
48618
|
-
exitConditions.push(
|
|
48618
|
+
exitConditions.push(aborted2);
|
|
48619
48619
|
}
|
|
48620
48620
|
if (options.abortController?.signal) {
|
|
48621
|
-
const { aborted, clearListener } = abortTimeout(options.abortController.signal);
|
|
48621
|
+
const { aborted: aborted2, clearListener } = abortTimeout(options.abortController.signal);
|
|
48622
48622
|
finalize.push(clearListener);
|
|
48623
|
-
exitConditions.push(
|
|
48623
|
+
exitConditions.push(aborted2);
|
|
48624
48624
|
}
|
|
48625
48625
|
return Promise.race(exitConditions).then((result) => {
|
|
48626
48626
|
for (const fn of finalize) {
|
|
@@ -87483,6 +87483,7 @@ var filesMapped = 0;
|
|
|
87483
87483
|
var workerCurrentFile = /* @__PURE__ */ new Map();
|
|
87484
87484
|
var lastWorkerProgressTime = 0;
|
|
87485
87485
|
var pendingReplacements = 0;
|
|
87486
|
+
var aborted = false;
|
|
87486
87487
|
var currentFileInfoIndex;
|
|
87487
87488
|
var filesToProcess = [];
|
|
87488
87489
|
var directoryScanFinished = false;
|
|
@@ -87505,12 +87506,32 @@ function setScanResumeCallback(cb) {
|
|
|
87505
87506
|
function markScanPaused() {
|
|
87506
87507
|
scanPaused = true;
|
|
87507
87508
|
}
|
|
87509
|
+
function terminateAllWorkers() {
|
|
87510
|
+
aborted = true;
|
|
87511
|
+
while (availableMappingWorkers.length) {
|
|
87512
|
+
availableMappingWorkers.pop().terminate();
|
|
87513
|
+
}
|
|
87514
|
+
for (const [worker] of workerCurrentFile) {
|
|
87515
|
+
try {
|
|
87516
|
+
worker.terminate();
|
|
87517
|
+
} catch {
|
|
87518
|
+
}
|
|
87519
|
+
}
|
|
87520
|
+
workerCurrentFile.clear();
|
|
87521
|
+
filesToProcess.length = 0;
|
|
87522
|
+
workersActive = 0;
|
|
87523
|
+
pendingReplacements = 0;
|
|
87524
|
+
directoryScanFinished = false;
|
|
87525
|
+
scanPaused = false;
|
|
87526
|
+
scanResumeCallback = null;
|
|
87527
|
+
}
|
|
87508
87528
|
async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, progressCb, workerCount) {
|
|
87509
87529
|
mappingWorkerOptions = {};
|
|
87510
87530
|
workersActive = 0;
|
|
87511
87531
|
mapResultsList = skipCollectingMappings ? void 0 : [];
|
|
87512
87532
|
filesMapped = 0;
|
|
87513
87533
|
pendingReplacements = 0;
|
|
87534
|
+
aborted = false;
|
|
87514
87535
|
workerCurrentFile.clear();
|
|
87515
87536
|
lastWorkerProgressTime = Date.now();
|
|
87516
87537
|
currentFileInfoIndex = fileInfoIndex;
|
|
@@ -87526,6 +87547,8 @@ async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, p
|
|
|
87526
87547
|
availableMappingWorkers.push(...workers);
|
|
87527
87548
|
}
|
|
87528
87549
|
async function dispatchMappingJobs() {
|
|
87550
|
+
if (aborted)
|
|
87551
|
+
return;
|
|
87529
87552
|
while (filesToProcess.length > 0 && availableMappingWorkers.length > 0) {
|
|
87530
87553
|
const { fileInfo, previousFileInfo } = filesToProcess.pop();
|
|
87531
87554
|
const mappingWorker = availableMappingWorkers.pop();
|
|
@@ -87588,6 +87611,8 @@ async function getHardwareConcurrency() {
|
|
|
87588
87611
|
return cpus().length;
|
|
87589
87612
|
}
|
|
87590
87613
|
function recoverCrashedWorker(mappingWorker, errorMessage) {
|
|
87614
|
+
if (aborted)
|
|
87615
|
+
return;
|
|
87591
87616
|
if (!workerCurrentFile.has(mappingWorker)) {
|
|
87592
87617
|
return;
|
|
87593
87618
|
}
|
|
@@ -87623,6 +87648,10 @@ function recoverCrashedWorker(mappingWorker, errorMessage) {
|
|
|
87623
87648
|
pendingReplacements += 1;
|
|
87624
87649
|
void createMappingWorker().then((worker) => {
|
|
87625
87650
|
pendingReplacements -= 1;
|
|
87651
|
+
if (aborted) {
|
|
87652
|
+
worker.terminate();
|
|
87653
|
+
return;
|
|
87654
|
+
}
|
|
87626
87655
|
availableMappingWorkers.push(worker);
|
|
87627
87656
|
dispatchMappingJobs();
|
|
87628
87657
|
}).catch((error2) => {
|
|
@@ -87652,6 +87681,8 @@ async function createMappingWorker() {
|
|
|
87652
87681
|
});
|
|
87653
87682
|
}
|
|
87654
87683
|
mappingWorker.addEventListener("message", (event) => {
|
|
87684
|
+
if (aborted)
|
|
87685
|
+
return;
|
|
87655
87686
|
if (event.data.response === "lookup") {
|
|
87656
87687
|
const outputPath = event.data.outputPath;
|
|
87657
87688
|
const entry = currentFileInfoIndex?.[OUTPUT_FILE_PREFIX + outputPath];
|
|
@@ -87870,7 +87901,14 @@ function queueUrlsForMapping(organizeOptions) {
|
|
|
87870
87901
|
setDirectoryScanFinished(true);
|
|
87871
87902
|
}
|
|
87872
87903
|
async function curateMany(organizeOptions, onProgress) {
|
|
87904
|
+
if (organizeOptions.signal?.aborted) {
|
|
87905
|
+
return Promise.reject(
|
|
87906
|
+
new DOMException("The operation was aborted.", "AbortError")
|
|
87907
|
+
);
|
|
87908
|
+
}
|
|
87873
87909
|
return new Promise(async (resolve, reject) => {
|
|
87910
|
+
let settled = false;
|
|
87911
|
+
const signal = organizeOptions.signal;
|
|
87874
87912
|
const STALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
87875
87913
|
const stallWatchdog = setInterval(() => {
|
|
87876
87914
|
if (getWorkersActive() > 0 && Date.now() - getLastWorkerProgressTime() > STALL_TIMEOUT_MS) {
|
|
@@ -87889,15 +87927,42 @@ async function curateMany(organizeOptions, onProgress) {
|
|
|
87889
87927
|
}, 6e4);
|
|
87890
87928
|
const progressCallback2 = (msg) => {
|
|
87891
87929
|
onProgress?.(msg);
|
|
87892
|
-
if (msg.response === "done") {
|
|
87930
|
+
if (msg.response === "done" && !settled) {
|
|
87931
|
+
settled = true;
|
|
87893
87932
|
clearInterval(stallWatchdog);
|
|
87933
|
+
signal?.removeEventListener("abort", onAbort);
|
|
87894
87934
|
resolve(msg);
|
|
87895
87935
|
}
|
|
87896
87936
|
};
|
|
87897
87937
|
const rejectCallback = (reason) => {
|
|
87938
|
+
if (settled)
|
|
87939
|
+
return;
|
|
87940
|
+
settled = true;
|
|
87898
87941
|
clearInterval(stallWatchdog);
|
|
87942
|
+
signal?.removeEventListener("abort", onAbort);
|
|
87899
87943
|
reject(reason);
|
|
87900
87944
|
};
|
|
87945
|
+
let fileListWorker;
|
|
87946
|
+
const onAbort = () => {
|
|
87947
|
+
try {
|
|
87948
|
+
fileListWorker?.terminate();
|
|
87949
|
+
} catch {
|
|
87950
|
+
}
|
|
87951
|
+
terminateAllWorkers();
|
|
87952
|
+
rejectCallback(
|
|
87953
|
+
new DOMException("The operation was aborted.", "AbortError")
|
|
87954
|
+
);
|
|
87955
|
+
};
|
|
87956
|
+
if (signal) {
|
|
87957
|
+
if (signal.aborted) {
|
|
87958
|
+
clearInterval(stallWatchdog);
|
|
87959
|
+
rejectCallback(
|
|
87960
|
+
new DOMException("The operation was aborted.", "AbortError")
|
|
87961
|
+
);
|
|
87962
|
+
return;
|
|
87963
|
+
}
|
|
87964
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
87965
|
+
}
|
|
87901
87966
|
try {
|
|
87902
87967
|
await initializeMappingWorkers(
|
|
87903
87968
|
organizeOptions.skipCollectingMappings,
|
|
@@ -87909,7 +87974,7 @@ async function curateMany(organizeOptions, onProgress) {
|
|
|
87909
87974
|
await collectMappingOptions(organizeOptions)
|
|
87910
87975
|
);
|
|
87911
87976
|
if (organizeOptions.inputType === "directory" || organizeOptions.inputType === "path" || organizeOptions.inputType === "s3") {
|
|
87912
|
-
|
|
87977
|
+
fileListWorker = await initializeFileListWorker(rejectCallback);
|
|
87913
87978
|
setScanResumeCallback(() => {
|
|
87914
87979
|
fileListWorker.postMessage({ request: "resume" });
|
|
87915
87980
|
});
|
|
@@ -87966,7 +88031,7 @@ async function curateMany(organizeOptions, onProgress) {
|
|
|
87966
88031
|
}
|
|
87967
88032
|
dispatchMappingJobs();
|
|
87968
88033
|
} catch (error2) {
|
|
87969
|
-
|
|
88034
|
+
rejectCallback(error2);
|
|
87970
88035
|
}
|
|
87971
88036
|
});
|
|
87972
88037
|
}
|
|
@@ -12055,6 +12055,7 @@ var filesMapped = 0;
|
|
|
12055
12055
|
var workerCurrentFile = /* @__PURE__ */ new Map();
|
|
12056
12056
|
var lastWorkerProgressTime = 0;
|
|
12057
12057
|
var pendingReplacements = 0;
|
|
12058
|
+
var aborted = false;
|
|
12058
12059
|
var currentFileInfoIndex;
|
|
12059
12060
|
var filesToProcess = [];
|
|
12060
12061
|
var directoryScanFinished = false;
|
|
@@ -12077,12 +12078,35 @@ function setScanResumeCallback(cb) {
|
|
|
12077
12078
|
function markScanPaused() {
|
|
12078
12079
|
scanPaused = true;
|
|
12079
12080
|
}
|
|
12081
|
+
function terminateAllWorkers() {
|
|
12082
|
+
aborted = true;
|
|
12083
|
+
while (availableMappingWorkers.length) {
|
|
12084
|
+
availableMappingWorkers.pop().terminate();
|
|
12085
|
+
}
|
|
12086
|
+
for (const [worker] of workerCurrentFile) {
|
|
12087
|
+
try {
|
|
12088
|
+
worker.terminate();
|
|
12089
|
+
} catch {
|
|
12090
|
+
}
|
|
12091
|
+
}
|
|
12092
|
+
workerCurrentFile.clear();
|
|
12093
|
+
filesToProcess.length = 0;
|
|
12094
|
+
workersActive = 0;
|
|
12095
|
+
pendingReplacements = 0;
|
|
12096
|
+
directoryScanFinished = false;
|
|
12097
|
+
scanPaused = false;
|
|
12098
|
+
scanResumeCallback = null;
|
|
12099
|
+
}
|
|
12100
|
+
function isAborted() {
|
|
12101
|
+
return aborted;
|
|
12102
|
+
}
|
|
12080
12103
|
async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, progressCb, workerCount) {
|
|
12081
12104
|
mappingWorkerOptions = {};
|
|
12082
12105
|
workersActive = 0;
|
|
12083
12106
|
mapResultsList = skipCollectingMappings ? void 0 : [];
|
|
12084
12107
|
filesMapped = 0;
|
|
12085
12108
|
pendingReplacements = 0;
|
|
12109
|
+
aborted = false;
|
|
12086
12110
|
workerCurrentFile.clear();
|
|
12087
12111
|
lastWorkerProgressTime = Date.now();
|
|
12088
12112
|
currentFileInfoIndex = fileInfoIndex;
|
|
@@ -12098,6 +12122,8 @@ async function initializeMappingWorkers(skipCollectingMappings, fileInfoIndex, p
|
|
|
12098
12122
|
availableMappingWorkers.push(...workers);
|
|
12099
12123
|
}
|
|
12100
12124
|
async function dispatchMappingJobs() {
|
|
12125
|
+
if (aborted)
|
|
12126
|
+
return;
|
|
12101
12127
|
while (filesToProcess.length > 0 && availableMappingWorkers.length > 0) {
|
|
12102
12128
|
const { fileInfo, previousFileInfo } = filesToProcess.pop();
|
|
12103
12129
|
const mappingWorker = availableMappingWorkers.pop();
|
|
@@ -12160,6 +12186,8 @@ async function getHardwareConcurrency() {
|
|
|
12160
12186
|
return cpus().length;
|
|
12161
12187
|
}
|
|
12162
12188
|
function recoverCrashedWorker(mappingWorker, errorMessage) {
|
|
12189
|
+
if (aborted)
|
|
12190
|
+
return;
|
|
12163
12191
|
if (!workerCurrentFile.has(mappingWorker)) {
|
|
12164
12192
|
return;
|
|
12165
12193
|
}
|
|
@@ -12195,6 +12223,10 @@ function recoverCrashedWorker(mappingWorker, errorMessage) {
|
|
|
12195
12223
|
pendingReplacements += 1;
|
|
12196
12224
|
void createMappingWorker().then((worker) => {
|
|
12197
12225
|
pendingReplacements -= 1;
|
|
12226
|
+
if (aborted) {
|
|
12227
|
+
worker.terminate();
|
|
12228
|
+
return;
|
|
12229
|
+
}
|
|
12198
12230
|
availableMappingWorkers.push(worker);
|
|
12199
12231
|
dispatchMappingJobs();
|
|
12200
12232
|
}).catch((error) => {
|
|
@@ -12224,6 +12256,8 @@ async function createMappingWorker() {
|
|
|
12224
12256
|
});
|
|
12225
12257
|
}
|
|
12226
12258
|
mappingWorker.addEventListener("message", (event) => {
|
|
12259
|
+
if (aborted)
|
|
12260
|
+
return;
|
|
12227
12261
|
if (event.data.response === "lookup") {
|
|
12228
12262
|
const outputPath = event.data.outputPath;
|
|
12229
12263
|
const entry = currentFileInfoIndex?.[OUTPUT_FILE_PREFIX + outputPath];
|
|
@@ -12299,9 +12333,11 @@ export {
|
|
|
12299
12333
|
getWorkerCurrentFile,
|
|
12300
12334
|
getWorkersActive,
|
|
12301
12335
|
initializeMappingWorkers,
|
|
12336
|
+
isAborted,
|
|
12302
12337
|
markScanPaused,
|
|
12303
12338
|
scanAnomalies,
|
|
12304
12339
|
setDirectoryScanFinished,
|
|
12305
12340
|
setMappingWorkerOptions,
|
|
12306
|
-
setScanResumeCallback
|
|
12341
|
+
setScanResumeCallback,
|
|
12342
|
+
terminateAllWorkers
|
|
12307
12343
|
};
|
|
@@ -39,6 +39,18 @@ export declare function setScanResumeCallback(cb: (() => void) | null): void;
|
|
|
39
39
|
* index.ts when the queue exceeds the high-water mark.
|
|
40
40
|
*/
|
|
41
41
|
export declare function markScanPaused(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Hard-terminate all workers (idle and active) and reset pool state.
|
|
44
|
+
* Called when curateMany is aborted via AbortSignal. Equivalent to a
|
|
45
|
+
* tab reload — partially written files are handled by hash checks on
|
|
46
|
+
* the next run.
|
|
47
|
+
*/
|
|
48
|
+
export declare function terminateAllWorkers(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the current run has been aborted. Used by worker message handlers
|
|
51
|
+
* to bail out on messages arriving after teardown.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isAborted(): boolean;
|
|
42
54
|
/**
|
|
43
55
|
* Initialize the mapping worker pool. Call once per curateMany invocation.
|
|
44
56
|
*/
|
package/dist/types/types.d.ts
CHANGED
|
@@ -126940,6 +126940,10 @@
|
|
|
126940
126940
|
// The termination condition in dispatchMappingJobs() waits for this to reach 0
|
|
126941
126941
|
// before finishing, to avoid orphaning in-flight replacements.
|
|
126942
126942
|
let pendingReplacements = 0;
|
|
126943
|
+
// Set to true when curateMany is aborted via AbortSignal. Guards dispatch,
|
|
126944
|
+
// crash recovery, and worker message handlers against acting on stale state
|
|
126945
|
+
// after teardown.
|
|
126946
|
+
let aborted = false;
|
|
126943
126947
|
// Stored fileInfoIndex from initializeMappingWorkers, used for lookup
|
|
126944
126948
|
// responses when workers query for previousMappedFileInfo.
|
|
126945
126949
|
let currentFileInfoIndex;
|
|
@@ -126984,6 +126988,36 @@
|
|
|
126984
126988
|
function markScanPaused() {
|
|
126985
126989
|
scanPaused = true;
|
|
126986
126990
|
}
|
|
126991
|
+
/**
|
|
126992
|
+
* Hard-terminate all workers (idle and active) and reset pool state.
|
|
126993
|
+
* Called when curateMany is aborted via AbortSignal. Equivalent to a
|
|
126994
|
+
* tab reload — partially written files are handled by hash checks on
|
|
126995
|
+
* the next run.
|
|
126996
|
+
*/
|
|
126997
|
+
function terminateAllWorkers() {
|
|
126998
|
+
aborted = true;
|
|
126999
|
+
// Terminate idle workers
|
|
127000
|
+
while (availableMappingWorkers.length) {
|
|
127001
|
+
availableMappingWorkers.pop().terminate();
|
|
127002
|
+
}
|
|
127003
|
+
// Terminate active workers (those with an in-flight file)
|
|
127004
|
+
for (const [worker] of workerCurrentFile) {
|
|
127005
|
+
try {
|
|
127006
|
+
worker.terminate();
|
|
127007
|
+
}
|
|
127008
|
+
catch {
|
|
127009
|
+
/* already terminated */
|
|
127010
|
+
}
|
|
127011
|
+
}
|
|
127012
|
+
workerCurrentFile.clear();
|
|
127013
|
+
// Clear the queue and reset counters
|
|
127014
|
+
filesToProcess.length = 0;
|
|
127015
|
+
workersActive = 0;
|
|
127016
|
+
pendingReplacements = 0;
|
|
127017
|
+
directoryScanFinished = false;
|
|
127018
|
+
scanPaused = false;
|
|
127019
|
+
scanResumeCallback = null;
|
|
127020
|
+
}
|
|
126987
127021
|
/**
|
|
126988
127022
|
* Initialize the mapping worker pool. Call once per curateMany invocation.
|
|
126989
127023
|
*/
|
|
@@ -126993,6 +127027,7 @@
|
|
|
126993
127027
|
mapResultsList = skipCollectingMappings ? undefined : [];
|
|
126994
127028
|
filesMapped = 0;
|
|
126995
127029
|
pendingReplacements = 0;
|
|
127030
|
+
aborted = false;
|
|
126996
127031
|
workerCurrentFile.clear();
|
|
126997
127032
|
lastWorkerProgressTime = Date.now();
|
|
126998
127033
|
currentFileInfoIndex = fileInfoIndex;
|
|
@@ -127011,6 +127046,8 @@
|
|
|
127011
127046
|
* replacements, scan finished) and emits the 'done' progress message.
|
|
127012
127047
|
*/
|
|
127013
127048
|
async function dispatchMappingJobs() {
|
|
127049
|
+
if (aborted)
|
|
127050
|
+
return;
|
|
127014
127051
|
while (filesToProcess.length > 0 && availableMappingWorkers.length > 0) {
|
|
127015
127052
|
const { fileInfo, previousFileInfo } = filesToProcess.pop();
|
|
127016
127053
|
const mappingWorker = availableMappingWorkers.pop();
|
|
@@ -127097,6 +127134,9 @@
|
|
|
127097
127134
|
* on('exit'), and the stall watchdog.
|
|
127098
127135
|
*/
|
|
127099
127136
|
function recoverCrashedWorker(mappingWorker, errorMessage) {
|
|
127137
|
+
// Bail out if processing has been aborted — no recovery needed.
|
|
127138
|
+
if (aborted)
|
|
127139
|
+
return;
|
|
127100
127140
|
// Guard against double-recovery (e.g., both onerror and on('exit') firing
|
|
127101
127141
|
// for the same crash). Without this, workersActive could go negative.
|
|
127102
127142
|
if (!workerCurrentFile.has(mappingWorker)) {
|
|
@@ -127137,6 +127177,12 @@
|
|
|
127137
127177
|
void createMappingWorker()
|
|
127138
127178
|
.then((worker) => {
|
|
127139
127179
|
pendingReplacements -= 1;
|
|
127180
|
+
// If processing was aborted while the replacement was being created,
|
|
127181
|
+
// terminate it immediately instead of adding it to the pool.
|
|
127182
|
+
if (aborted) {
|
|
127183
|
+
worker.terminate();
|
|
127184
|
+
return;
|
|
127185
|
+
}
|
|
127140
127186
|
availableMappingWorkers.push(worker);
|
|
127141
127187
|
dispatchMappingJobs();
|
|
127142
127188
|
})
|
|
@@ -127174,6 +127220,9 @@
|
|
|
127174
127220
|
});
|
|
127175
127221
|
}
|
|
127176
127222
|
mappingWorker.addEventListener('message', (event) => {
|
|
127223
|
+
// Ignore messages from workers after abort — the pool is torn down.
|
|
127224
|
+
if (aborted)
|
|
127225
|
+
return;
|
|
127177
127226
|
// Handle lookup requests from the worker. The worker sends these when
|
|
127178
127227
|
// curateOne needs to check if a mapped file was already uploaded
|
|
127179
127228
|
// (previousMappedFileInfo). The index is kept on the main thread to
|
|
@@ -127423,7 +127472,14 @@
|
|
|
127423
127472
|
setDirectoryScanFinished(true);
|
|
127424
127473
|
}
|
|
127425
127474
|
async function curateMany(organizeOptions, onProgress) {
|
|
127475
|
+
// Early rejection for pre-aborted signal — don't create any workers.
|
|
127476
|
+
if (organizeOptions.signal?.aborted) {
|
|
127477
|
+
return Promise.reject(new DOMException('The operation was aborted.', 'AbortError'));
|
|
127478
|
+
}
|
|
127426
127479
|
return new Promise(async (resolve, reject) => {
|
|
127480
|
+
// Prevents double-settle when abort races with natural completion.
|
|
127481
|
+
let settled = false;
|
|
127482
|
+
const signal = organizeOptions.signal;
|
|
127427
127483
|
// Stall watchdog: if no mapping worker at all has reported back for 10
|
|
127428
127484
|
// minutes (i.e., all active workers are stuck), terminate them and count
|
|
127429
127485
|
// their in-flight files as mapping errors. This guards against undetectable
|
|
@@ -127453,15 +127509,46 @@
|
|
|
127453
127509
|
// Progress callback wraps the user's callback and handles lifecycle
|
|
127454
127510
|
const progressCallback = (msg) => {
|
|
127455
127511
|
onProgress?.(msg);
|
|
127456
|
-
if (msg.response === 'done') {
|
|
127512
|
+
if (msg.response === 'done' && !settled) {
|
|
127513
|
+
settled = true;
|
|
127457
127514
|
clearInterval(stallWatchdog);
|
|
127515
|
+
signal?.removeEventListener('abort', onAbort);
|
|
127458
127516
|
resolve(msg);
|
|
127459
127517
|
}
|
|
127460
127518
|
};
|
|
127461
127519
|
const rejectCallback = (reason) => {
|
|
127520
|
+
if (settled)
|
|
127521
|
+
return;
|
|
127522
|
+
settled = true;
|
|
127462
127523
|
clearInterval(stallWatchdog);
|
|
127524
|
+
signal?.removeEventListener('abort', onAbort);
|
|
127463
127525
|
reject(reason);
|
|
127464
127526
|
};
|
|
127527
|
+
// Reference to the scan worker, hoisted so the abort handler can
|
|
127528
|
+
// terminate it. Assigned later when a directory/path/s3 input is used.
|
|
127529
|
+
let fileListWorker;
|
|
127530
|
+
const onAbort = () => {
|
|
127531
|
+
// Terminate the scan worker if it exists
|
|
127532
|
+
try {
|
|
127533
|
+
fileListWorker?.terminate();
|
|
127534
|
+
}
|
|
127535
|
+
catch {
|
|
127536
|
+
/* already terminated */
|
|
127537
|
+
}
|
|
127538
|
+
// Hard-terminate all mapping workers and reset pool state
|
|
127539
|
+
terminateAllWorkers();
|
|
127540
|
+
// Reject with standard AbortError
|
|
127541
|
+
rejectCallback(new DOMException('The operation was aborted.', 'AbortError'));
|
|
127542
|
+
};
|
|
127543
|
+
if (signal) {
|
|
127544
|
+
// Re-check in case abort happened between the early check and here
|
|
127545
|
+
if (signal.aborted) {
|
|
127546
|
+
clearInterval(stallWatchdog);
|
|
127547
|
+
rejectCallback(new DOMException('The operation was aborted.', 'AbortError'));
|
|
127548
|
+
return;
|
|
127549
|
+
}
|
|
127550
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
127551
|
+
}
|
|
127465
127552
|
try {
|
|
127466
127553
|
// create the mapping workers
|
|
127467
127554
|
await initializeMappingWorkers(organizeOptions.skipCollectingMappings, organizeOptions.fileInfoIndex, progressCallback, organizeOptions.workerCount);
|
|
@@ -127476,7 +127563,7 @@
|
|
|
127476
127563
|
if (organizeOptions.inputType === 'directory' ||
|
|
127477
127564
|
organizeOptions.inputType === 'path' ||
|
|
127478
127565
|
organizeOptions.inputType === 's3') {
|
|
127479
|
-
|
|
127566
|
+
fileListWorker = await initializeFileListWorker(rejectCallback);
|
|
127480
127567
|
// Wire up backpressure resume: when the dispatch loop drains the
|
|
127481
127568
|
// queue below the low-water mark, it calls this to resume scanning.
|
|
127482
127569
|
setScanResumeCallback(() => {
|
|
@@ -127543,7 +127630,7 @@
|
|
|
127543
127630
|
dispatchMappingJobs();
|
|
127544
127631
|
}
|
|
127545
127632
|
catch (error) {
|
|
127546
|
-
|
|
127633
|
+
rejectCallback(error);
|
|
127547
127634
|
}
|
|
127548
127635
|
});
|
|
127549
127636
|
}
|