()
| 296 | * @returns Array of full lockfile paths sorted by modification time (newest first) |
| 297 | */ |
| 298 | export async function getSortedIdeLockfiles(): Promise<string[]> { |
| 299 | try { |
| 300 | const ideLockFilePaths = await getIdeLockfilesPaths() |
| 301 | |
| 302 | // Collect all lockfiles from all directories |
| 303 | const allLockfiles: Array<{ path: string; mtime: Date }>[] = |
| 304 | await Promise.all( |
| 305 | ideLockFilePaths.map(async ideLockFilePath => { |
| 306 | try { |
| 307 | const entries = await getFsImplementation().readdir(ideLockFilePath) |
| 308 | const lockEntries = entries.filter(file => |
| 309 | file.name.endsWith('.lock'), |
| 310 | ) |
| 311 | // Stat all lockfiles in parallel; skip ones that fail |
| 312 | const stats = await Promise.all( |
| 313 | lockEntries.map(async file => { |
| 314 | const fullPath = join(ideLockFilePath, file.name) |
| 315 | try { |
| 316 | const fileStat = await getFsImplementation().stat(fullPath) |
| 317 | return { path: fullPath, mtime: fileStat.mtime } |
| 318 | } catch { |
| 319 | return null |
| 320 | } |
| 321 | }), |
| 322 | ) |
| 323 | return stats.filter(s => s !== null) |
| 324 | } catch (error) { |
| 325 | // Candidate paths are pushed without pre-checking existence, so |
| 326 | // missing/inaccessible dirs are expected here — skip silently. |
| 327 | if (!isFsInaccessible(error)) { |
| 328 | logError(error) |
| 329 | } |
| 330 | return [] |
| 331 | } |
| 332 | }), |
| 333 | ) |
| 334 | |
| 335 | // Flatten and sort all lockfiles by last modified date (newest first) |
| 336 | return allLockfiles |
| 337 | .flat() |
| 338 | .sort((a, b) => b.mtime.getTime() - a.mtime.getTime()) |
| 339 | .map(file => file.path) |
| 340 | } catch (error) { |
| 341 | logError(error as Error) |
| 342 | return [] |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | async function readIdeLockfile(path: string): Promise<IdeLockfileInfo | null> { |
| 347 | try { |
no test coverage detected