fix(fs): Improve waitForFileToBeReady function for file stability detection

This commit is contained in:
Philipp Kunz 2025-01-07 04:30:39 +01:00
parent d557e4b4fe
commit 4c4e41b158
5 changed files with 1315 additions and 272 deletions

View File

@ -1,5 +1,12 @@
# Changelog # Changelog
## 2025-01-07 - 11.1.1 - fix(fs)
Improve waitForFileToBeReady function for file stability detection
- Enhanced error handling and file stability checks in waitForFileToBeReady function
- Added timeout feature for file readiness check
- Improved directory access check before file availability check
## 2025-01-07 - 11.1.0 - feat(SmartFile) ## 2025-01-07 - 11.1.0 - feat(SmartFile)
Add rename functionality to SmartFile class Add rename functionality to SmartFile class

View File

@ -49,7 +49,7 @@
"@push.rocks/smartjson": "^5.0.20", "@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartmime": "^2.0.4", "@push.rocks/smartmime": "^2.0.4",
"@push.rocks/smartpath": "^5.0.18", "@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.4", "@push.rocks/smartpromise": "^4.1.0",
"@push.rocks/smartrequest": "^2.0.23", "@push.rocks/smartrequest": "^2.0.23",
"@push.rocks/smartstream": "^3.2.5", "@push.rocks/smartstream": "^3.2.5",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
@ -63,8 +63,8 @@
"@git.zone/tsbuild": "^2.2.0", "@git.zone/tsbuild": "^2.2.0",
"@git.zone/tsrun": "^1.3.3", "@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.90",
"@push.rocks/tapbundle": "^5.5.3", "@push.rocks/tapbundle": "^5.5.4",
"@types/node": "^22.10.2" "@types/node": "^22.10.5"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",

1481
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartfile', name: '@push.rocks/smartfile',
version: '11.1.0', version: '11.1.1',
description: 'Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.' description: 'Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.'
} }

View File

@ -397,37 +397,57 @@ export const listFileTree = async (
/** /**
* Watches for file stability before resolving the promise. * Watches for file stability before resolving the promise.
* @param filePathArg The path of the file to monitor.
* @param timeoutMs The maximum time to wait for the file to stabilize (in milliseconds). Default is 60 seconds.
* @returns A promise that resolves when the file is stable or rejects on timeout or error.
*/ */
export const waitForFileToBeReady = (filePathArg: string): Promise<void> => { export const waitForFileToBeReady = (
filePathArg: string,
timeoutMs: number = 60000
): Promise<void> => {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let lastFileSize = -1; let lastFileSize = -1;
let fileIsStable = false; let fileIsStable = false;
let isFileAvailable = false;
const startTime = Date.now();
const fileDir = plugins.path.dirname(filePathArg);
const checkFileStability = async () => { const checkFileStability = async () => {
let currentFileSize: number; try {
const deferred = plugins.smartpromise.defer(); const stats: any = await plugins.smartpromise.fromCallback((cb) =>
plugins.fs.stat(filePathArg, (err, stats) => { plugins.fs.stat(filePathArg, cb)
if (err) { );
isFileAvailable = true;
if (stats.size === lastFileSize) {
fileIsStable = true; fileIsStable = true;
watcher.close(); } else {
reject(err); lastFileSize = stats.size;
return; fileIsStable = false;
}
} catch (err) {
if (err.code === 'ENOENT') {
isFileAvailable = false; // File not available yet
} else {
throw err; // Propagate other errors
} }
currentFileSize = stats.size;
deferred.resolve();
});
await deferred.promise;
if (currentFileSize === lastFileSize) {
fileIsStable = true;
await plugins.smartdelay.delayFor(100);
resolve();
} }
lastFileSize = currentFileSize;
}; };
const watcher = plugins.fs.watch(filePathArg, (eventType, filename) => { const checkDirectory = async () => {
if (eventType === 'change') { try {
checkFileStability(); await plugins.smartpromise.fromCallback((cb) =>
plugins.fs.access(fileDir, plugins.fs.constants.R_OK, cb)
);
return true;
} catch {
return false;
}
};
const watcher = plugins.fs.watch(filePathArg, { persistent: true }, async () => {
if (isFileAvailable && !fileIsStable) {
await checkFileStability();
} }
}); });
@ -436,13 +456,34 @@ export const waitForFileToBeReady = (filePathArg: string): Promise<void> => {
reject(error); reject(error);
}); });
try {
while (!fileIsStable) { while (!fileIsStable) {
// Check for timeout
if (Date.now() - startTime > timeoutMs) {
watcher.close();
reject(new Error(`Timeout waiting for file to be ready: ${filePathArg}`));
return;
}
// Ensure directory exists
if (!await checkDirectory()) {
await plugins.smartdelay.delayFor(500); // Wait and retry
continue;
}
// Check file stability
await checkFileStability(); await checkFileStability();
if (!fileIsStable) { if (!fileIsStable) {
await plugins.smartdelay.delayFor(5000); await plugins.smartdelay.delayFor(1000); // Polling interval
} }
} }
watcher.close(); watcher.close();
resolve();
} catch (err) {
watcher.close();
reject(err);
}
}); });
}; };