POSIX rename(2) atomically replaces dest even when readers hold open handles. Windows MoveFileEx (which fs.renameSync uses with MOVEFILE_REPLACE_EXISTING) cannot — it throws EPERM/EBUSY when another process has the destination open. Concurrent install.js readers and antivirus scanners are realistic triggers; both release within ms. renameAtomicWithRetry() preserves the bare renameSync call on POSIX (no overhead) and on Windows retries up to 4 times with 10/30/90/270ms backoff, then falls back to copyFileSync + unlinkSync. If even copy fails because dest is hard-locked, log a non-fatal warning and leave the prior dest in place — a subsequent build retries from a fresh state. The build no longer crashes on Windows transient locking. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.1 KiB
7.1 KiB