@push.rocks/smartchok
Version:
A cross-runtime file watcher with glob pattern support for Node.js, Deno, and Bun.
84 lines • 6.24 kB
JavaScript
import * as fs from 'fs';
/**
* Implements awaitWriteFinish functionality by polling file size until stable.
* This replaces chokidar's built-in write stabilization.
*/
export class WriteStabilizer {
constructor(stabilityThreshold = 300, pollInterval = 100) {
this.stabilityThreshold = stabilityThreshold;
this.pollInterval = pollInterval;
this.pendingWrites = new Map();
}
/**
* Wait for a file write to complete by polling until size is stable
*/
async waitForWriteFinish(filePath) {
// Cancel any existing pending check for this file
this.cancel(filePath);
return new Promise((resolve, reject) => {
const poll = async () => {
try {
const stats = await fs.promises.stat(filePath);
const pending = this.pendingWrites.get(filePath);
if (!pending) {
// Was cancelled
return;
}
const now = Date.now();
if (stats.size !== pending.lastSize) {
// Size changed - file is still being written, reset timer
pending.lastSize = stats.size;
pending.lastChange = now;
pending.timeoutId = setTimeout(poll, this.pollInterval);
}
else if (now - pending.lastChange >= this.stabilityThreshold) {
// Size has been stable for the threshold duration
this.pendingWrites.delete(filePath);
resolve(stats);
}
else {
// Size is the same but not yet past threshold
pending.timeoutId = setTimeout(poll, this.pollInterval);
}
}
catch (error) {
this.pendingWrites.delete(filePath);
if (error.code === 'ENOENT') {
// File was deleted during polling
reject(new Error(`File was deleted: ${filePath}`));
}
else {
reject(error);
}
}
};
this.pendingWrites.set(filePath, {
lastSize: -1,
lastChange: Date.now(),
timeoutId: setTimeout(poll, this.pollInterval),
resolve,
reject
});
});
}
/**
* Cancel any pending write stabilization for a file
*/
cancel(filePath) {
const pending = this.pendingWrites.get(filePath);
if (pending) {
clearTimeout(pending.timeoutId);
this.pendingWrites.delete(filePath);
}
}
/**
* Cancel all pending write stabilizations
*/
cancelAll() {
for (const [filePath, pending] of this.pendingWrites) {
clearTimeout(pending.timeoutId);
}
this.pendingWrites.clear();
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGUtc3RhYmlsaXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3V0aWxzL3dyaXRlLXN0YWJpbGl6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFVekI7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLGVBQWU7SUFHMUIsWUFDVSxxQkFBNkIsR0FBRyxFQUNoQyxlQUF1QixHQUFHO1FBRDFCLHVCQUFrQixHQUFsQixrQkFBa0IsQ0FBYztRQUNoQyxpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUo1QixrQkFBYSxHQUFHLElBQUksR0FBRyxFQUF5QixDQUFDO0lBS3RELENBQUM7SUFFSjs7T0FFRztJQUNILEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxRQUFnQjtRQUN2QyxrREFBa0Q7UUFDbEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUV0QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3JDLE1BQU0sSUFBSSxHQUFHLEtBQUssSUFBSSxFQUFFO2dCQUN0QixJQUFJLENBQUM7b0JBQ0gsTUFBTSxLQUFLLEdBQUcsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztvQkFDL0MsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7b0JBRWpELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzt3QkFDYixnQkFBZ0I7d0JBQ2hCLE9BQU87b0JBQ1QsQ0FBQztvQkFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7b0JBRXZCLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7d0JBQ3BDLDBEQUEwRDt3QkFDMUQsT0FBTyxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDO3dCQUM5QixPQUFPLENBQUMsVUFBVSxHQUFHLEdBQUcsQ0FBQzt3QkFDekIsT0FBTyxDQUFDLFNBQVMsR0FBRyxVQUFVLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztvQkFDMUQsQ0FBQzt5QkFBTSxJQUFJLEdBQUcsR0FBRyxPQUFPLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO3dCQUMvRCxrREFBa0Q7d0JBQ2xELElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO3dCQUNwQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7b0JBQ2pCLENBQUM7eUJBQU0sQ0FBQzt3QkFDTiw4Q0FBOEM7d0JBQzlDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsVUFBVSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7b0JBQzFELENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO29CQUNwQixJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztvQkFDcEMsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO3dCQUM1QixrQ0FBa0M7d0JBQ2xDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxxQkFBcUIsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO29CQUNyRCxDQUFDO3lCQUFNLENBQUM7d0JBQ04sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO29CQUNoQixDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDLENBQUM7WUFFRixJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUU7Z0JBQy9CLFFBQVEsRUFBRSxDQUFDLENBQUM7Z0JBQ1osVUFBVSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7Z0JBQ3RCLFNBQVMsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUM7Z0JBQzlDLE9BQU87Z0JBQ1AsTUFBTTthQUNQLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFFBQWdCO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2pELElBQUksT0FBTyxFQUFFLENBQUM7WUFDWixZQUFZLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ2hDLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxTQUFTO1FBQ1AsS0FBSyxNQUFNLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNyRCxZQUFZLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ2xDLENBQUM7UUFDRCxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzdCLENBQUM7Q0FDRiJ9