(locksDir: string)
| 392 | * - Legacy proper-lockfile locks (directories created by mtime-based locking) |
| 393 | */ |
| 394 | export function cleanupStaleLocks(locksDir: string): number { |
| 395 | const fs = getFsImplementation() |
| 396 | let cleanedCount = 0 |
| 397 | |
| 398 | try { |
| 399 | const lockEntries = fs |
| 400 | .readdirStringSync(locksDir) |
| 401 | .filter((f: string) => f.endsWith('.lock')) |
| 402 | |
| 403 | for (const lockEntry of lockEntries) { |
| 404 | const lockFilePath = join(locksDir, lockEntry) |
| 405 | |
| 406 | try { |
| 407 | const stats = fs.lstatSync(lockFilePath) |
| 408 | |
| 409 | if (stats.isDirectory()) { |
| 410 | // Legacy proper-lockfile directory lock - always remove when PID-based |
| 411 | // locking is enabled since these are from a different locking mechanism |
| 412 | fs.rmSync(lockFilePath, { recursive: true, force: true }) |
| 413 | cleanedCount++ |
| 414 | logForDebugging(`Cleaned up legacy directory lock: ${lockEntry}`) |
| 415 | } else if (!isLockActive(lockFilePath)) { |
| 416 | // PID-based file lock with no running process |
| 417 | fs.unlinkSync(lockFilePath) |
| 418 | cleanedCount++ |
| 419 | logForDebugging(`Cleaned up stale lock: ${lockEntry}`) |
| 420 | } |
| 421 | } catch { |
| 422 | // Ignore individual cleanup errors |
| 423 | } |
| 424 | } |
| 425 | } catch (error) { |
| 426 | if (isENOENT(error)) { |
| 427 | return 0 |
| 428 | } |
| 429 | logError(toError(error)) |
| 430 | } |
| 431 | |
| 432 | return cleanedCount |
| 433 | } |
| 434 |
no test coverage detected