diff --git a/packages/core/src/file-history.ts b/packages/core/src/file-history.ts index 1abdda2..2a5717d 100644 --- a/packages/core/src/file-history.ts +++ b/packages/core/src/file-history.ts @@ -193,43 +193,51 @@ export async function fileHistoryMakeSnapshot( const mostRecentSnapshot = captured.snapshots.at(-1); if (mostRecentSnapshot) { - await Promise.all( - Array.from(captured.trackedFiles, async (trackingPath) => { - const latestBackup = mostRecentSnapshot.trackedFileBackups[trackingPath]; - const nextVersion = latestBackup ? latestBackup.version + 1 : 1; - - let fileStats: Stats | undefined; - try { - fileStats = await stat(trackingPath); - } catch { - fileStats = undefined; - } - - if (!fileStats) { - trackedFileBackups[trackingPath] = { - backupFileName: null, - version: nextVersion, - backupTime: new Date().toISOString(), - }; - return; - } - - if ( - latestBackup && - latestBackup.backupFileName !== null && - !(await checkOriginFileChanged( - trackingPath, - latestBackup.backupFileName, - fileStats, - )) - ) { - trackedFileBackups[trackingPath] = latestBackup; - return; - } - - trackedFileBackups[trackingPath] = await createBackup(trackingPath, nextVersion); - }), - ); + const trackedFilesArray = Array.from(captured.trackedFiles); + // ⚡ Bolt: Chunked concurrency to prevent EMFILE (too many open files) OS errors when evaluating thousands of tracked files. + const CONCURRENCY_LIMIT = 20; + + for (let i = 0; i < trackedFilesArray.length; i += CONCURRENCY_LIMIT) { + const chunk = trackedFilesArray.slice(i, i + CONCURRENCY_LIMIT); + + await Promise.all( + chunk.map(async (trackingPath) => { + const latestBackup = mostRecentSnapshot.trackedFileBackups[trackingPath]; + const nextVersion = latestBackup ? latestBackup.version + 1 : 1; + + let fileStats: Stats | undefined; + try { + fileStats = await stat(trackingPath); + } catch { + fileStats = undefined; + } + + if (!fileStats) { + trackedFileBackups[trackingPath] = { + backupFileName: null, + version: nextVersion, + backupTime: new Date().toISOString(), + }; + return; + } + + if ( + latestBackup && + latestBackup.backupFileName !== null && + !(await checkOriginFileChanged( + trackingPath, + latestBackup.backupFileName, + fileStats, + )) + ) { + trackedFileBackups[trackingPath] = latestBackup; + return; + } + + trackedFileBackups[trackingPath] = await createBackup(trackingPath, nextVersion); + }) + ); + } } let createdSnapshot: FileHistorySnapshot | undefined;