Compare commits

..

8 Commits

6 changed files with 5026 additions and 599 deletions

View File

@ -1,5 +1,30 @@
# 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)
Add rename functionality to SmartFile class
- Implemented a new method to rename a file within the SmartFile class.
- The rename method updates the file path and optionally writes the renamed file to the disk.
## 2024-12-15 - 11.0.23 - fix(fs)
Handle errors in toObjectSync method
- Added error handling in toObjectSync function to capture and provide more informative error messages.
## 2024-06-23 - 11.0.22 - fix(core)
Update dependencies and changelog
- Updated @push.rocks/smartstream to ^3.0.44
- Updated glob to ^10.4.2
- Updated @types/node to ^20.14.8
## 2024-06-23 - 11.0.21 - fix(dependencies)
Update dependencies to latest versions
@ -8,7 +33,6 @@ Update dependencies to latest versions
- Updated glob to ^10.4.2
- Updated @types/node to ^20.14.8
## 2024-06-07 - 11.0.20 - Changelog
11.0.20

View File

@ -1,7 +1,7 @@
{
"name": "@push.rocks/smartfile",
"private": false,
"version": "11.0.21",
"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.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
@ -42,29 +42,29 @@
},
"homepage": "https://code.foss.global/push.rocks/smartfile",
"dependencies": {
"@push.rocks/lik": "^6.0.15",
"@push.rocks/lik": "^6.1.0",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile-interfaces": "^1.0.7",
"@push.rocks/smarthash": "^3.0.4",
"@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartmime": "^2.0.2",
"@push.rocks/smartmime": "^2.0.4",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartstream": "^3.0.44",
"@push.rocks/smartpromise": "^4.1.0",
"@push.rocks/smartrequest": "^2.0.23",
"@push.rocks/smartstream": "^3.2.5",
"@types/fs-extra": "^11.0.4",
"@types/glob": "^8.1.0",
"@types/js-yaml": "^4.0.9",
"fs-extra": "^11.2.0",
"glob": "^10.4.2",
"glob": "^11.0.0",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.80",
"@git.zone/tsrun": "^1.2.46",
"@git.zone/tsbuild": "^2.2.0",
"@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^1.0.90",
"@push.rocks/tapbundle": "^5.0.23",
"@types/node": "^20.14.8"
"@push.rocks/tapbundle": "^5.5.4",
"@types/node": "^22.10.5"
},
"files": [
"ts/**/*",

5438
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartfile',
version: '11.0.21',
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.'
}

View File

@ -91,7 +91,7 @@ export class SmartFile extends plugins.smartjson.Smartjson {
});
}
public static async fromUrl (urlArg: string) {
public static async fromUrl(urlArg: string) {
const response = await plugins.smartrequest.getBinary(urlArg);
const smartfile = await SmartFile.fromBuffer(urlArg, response.body);
return smartfile;
@ -212,6 +212,43 @@ export class SmartFile extends plugins.smartjson.Smartjson {
await fs.remove(plugins.path.join(this.base, this.path));
}
/**
* Renames the file to the specified new name.
* - Updates the `path` property with the new name.
* - Writes the file to the new location if it exists on disk.
* @param newName The new name of the file (including extension if applicable).
* @param writeToDisk (optional) If true, also renames the file on the disk.
* @returns The updated file path after renaming.
*/
public async rename(newName: string, writeToDisk: boolean = false): Promise<string> {
// Validate the new name
if (!newName || typeof newName !== 'string') {
throw new Error('Invalid new name provided.');
}
// Extract the directory path
const oldFilePath = this.path;
const dirPath = plugins.path.dirname(this.path);
// Create the new file path
const newFilePath = plugins.path.join(dirPath, newName);
// Update the `path` property
this.path = newFilePath;
// Optionally write the renamed file to disk
if (writeToDisk) {
const oldAbsolutePath = plugins.smartpath.transform.makeAbsolute(oldFilePath, this.base);
const newAbsolutePath = plugins.smartpath.transform.makeAbsolute(newFilePath, this.base);
// Rename the file on disk
await plugins.fsExtra.rename(oldAbsolutePath, newAbsolutePath);
}
// Return the new path
return this.path;
}
// -----------------------------------------------
// vinyl compatibility
// -----------------------------------------------

View File

@ -200,7 +200,12 @@ export const toObjectSync = (filePathArg, fileTypeArg?) => {
const fileString = plugins.fsExtra.readFileSync(filePathArg, 'utf8');
let fileType;
fileTypeArg ? (fileType = fileTypeArg) : (fileType = interpreter.filetype(filePathArg));
return interpreter.objectFile(fileString, fileType);
try {
return interpreter.objectFile(fileString, fileType);
} catch (err) {
err.message = `Failed to read file at ${filePathArg}` + err.message;
throw err;
};
};
/**
@ -392,37 +397,57 @@ export const listFileTree = async (
/**
* 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) => {
let lastFileSize = -1;
let fileIsStable = false;
let isFileAvailable = false;
const startTime = Date.now();
const fileDir = plugins.path.dirname(filePathArg);
const checkFileStability = async () => {
let currentFileSize: number;
const deferred = plugins.smartpromise.defer();
plugins.fs.stat(filePathArg, (err, stats) => {
if (err) {
try {
const stats: any = await plugins.smartpromise.fromCallback((cb) =>
plugins.fs.stat(filePathArg, cb)
);
isFileAvailable = true;
if (stats.size === lastFileSize) {
fileIsStable = true;
watcher.close();
reject(err);
return;
} else {
lastFileSize = stats.size;
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) => {
if (eventType === 'change') {
checkFileStability();
const checkDirectory = async () => {
try {
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();
}
});
@ -431,13 +456,34 @@ export const waitForFileToBeReady = (filePathArg: string): Promise<void> => {
reject(error);
});
while (!fileIsStable) {
await checkFileStability();
if (!fileIsStable) {
await plugins.smartdelay.delayFor(5000);
try {
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();
if (!fileIsStable) {
await plugins.smartdelay.delayFor(1000); // Polling interval
}
}
watcher.close();
resolve();
} catch (err) {
watcher.close();
reject(err);
}
watcher.close();
});
};