(opts: {
port: number;
path?: string;
})
| 31 | * fails with ESRCH if not. We DO NOT trust a stale pidfile. |
| 32 | */ |
| 33 | export async function tryClaim(opts: { |
| 34 | port: number; |
| 35 | path?: string; |
| 36 | }): Promise< |
| 37 | | { claimed: true; release: () => Promise<void> } |
| 38 | | { claimed: false; existing: PidfileContents } |
| 39 | > { |
| 40 | const path = opts.path ?? defaultPidfilePath(); |
| 41 | await mkdir(dirname(path), { recursive: true, mode: 0o700 }); |
| 42 | |
| 43 | // Check for an existing pidfile. |
| 44 | if (existsSync(path)) { |
| 45 | try { |
| 46 | const raw = await readFile(path, 'utf-8'); |
| 47 | const existing = JSON.parse(raw) as PidfileContents; |
| 48 | if (isAlive(existing.pid)) { |
| 49 | return { claimed: false, existing }; |
| 50 | } |
| 51 | // Stale — drop it and continue to claim. |
| 52 | await unlink(path).catch(() => {}); |
| 53 | } catch { |
| 54 | // Unparseable pidfile — treat as stale. |
| 55 | await unlink(path).catch(() => {}); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | // Use SYNCHRONOUS open with O_EXCL for atomic exclusion. Bun's async |
| 60 | // fs.open(wx) doesn't reliably preserve O_EXCL semantics across concurrent |
| 61 | // calls in the same process. Sync openSync goes straight to syscall and is |
| 62 | // genuinely atomic. |
| 63 | // |
| 64 | // Constant 0x800 = O_EXCL on macOS/Linux; combined with O_CREAT (0x200) and |
| 65 | // O_WRONLY (0x1) it's the equivalent of 'wx'. The sync API accepts the |
| 66 | // string flag form too, but explicit numeric flags are the most defensive. |
| 67 | const contents: PidfileContents = { |
| 68 | pid: process.pid, |
| 69 | port: opts.port, |
| 70 | startedAt: Date.now(), |
| 71 | }; |
| 72 | let fd: number; |
| 73 | try { |
| 74 | fd = openSync(path, 'wx', 0o600); |
| 75 | } catch (err: unknown) { |
| 76 | const e = err as { code?: string }; |
| 77 | if (e.code === 'EEXIST') { |
| 78 | // Race: another caller won. |
| 79 | const raw = await readFile(path, 'utf-8').catch(() => '{}'); |
| 80 | const existing = JSON.parse(raw || '{}') as PidfileContents; |
| 81 | return { claimed: false, existing }; |
| 82 | } |
| 83 | throw err; |
| 84 | } |
| 85 | try { |
| 86 | writeSync(fd, JSON.stringify(contents, null, 2)); |
| 87 | } finally { |
| 88 | closeSync(fd); |
| 89 | } |
| 90 |
no test coverage detected