@@ -9,14 +9,6 @@ import {
99import { dirname } from "node:path" ;
1010import { setTimeout as delay } from "node:timers/promises" ;
1111
12- // File-lock primitives shared by every credential lock in this repo. The
13- // invariants that make them safe: per-waiter unique claims name the holder,
14- // kill(pid, 0) liveness takes over a crashed holder's lock, an untagged lock
15- // older than the stale horizon is presumed orphaned, a steal re-reads before
16- // unlinking so a concurrent winner's fresh lock is never removed, and both a
17- // post-create content check and a fenced release keep a loser from deleting
18- // or releasing under a live winner.
19-
2012export function isErrnoCode ( err : unknown , code : string ) : boolean {
2113 return (
2214 typeof err === "object" &&
@@ -26,27 +18,27 @@ export function isErrnoCode(err: unknown, code: string): boolean {
2618 ) ;
2719}
2820
29- export function isEnoent ( err : unknown ) : boolean {
21+ function isEnoent ( err : unknown ) : boolean {
3022 return isErrnoCode ( err , "ENOENT" ) ;
3123}
3224
3325// Per-waiter unique claim (pid + counter): pid alone is not unique per call,
3426// so the counter leg tells our claim from a concurrent winner's.
3527let lockClaimCounter = 0 ;
3628
37- export function newLockClaim ( ) : string {
29+ function newLockClaim ( ) : string {
3830 return `${ String ( process . pid ) } :${ String ( ( lockClaimCounter += 1 ) ) } ` ;
3931}
4032
41- export function holderPid ( content : string ) : number | null {
33+ function holderPid ( content : string ) : number | null {
4234 const pid = Number ( content . split ( ":" ) [ 0 ] ?. trim ( ) ) ;
4335 return Number . isInteger ( pid ) && pid > 0 ? pid : null ;
4436}
4537
4638// kill(pid, 0) liveness: success or EPERM means alive; ESRCH/EINVAL mean
4739// dead. Any other failure reads as alive — never steal a live holder's lock
4840// on a confused signal check; the waiter times out with a recovery hint.
49- export function isPidAlive ( pid : number ) : boolean {
41+ function isPidAlive ( pid : number ) : boolean {
5042 try {
5143 process . kill ( pid , 0 ) ;
5244 return true ;
@@ -57,9 +49,7 @@ export function isPidAlive(pid: number): boolean {
5749
5850// Null when the lock vanished under the waiter (a release raced the read);
5951// anything but ENOENT propagates — permission and disk errors must surface.
60- export async function readLockContent (
61- lockPath : string ,
62- ) : Promise < string | null > {
52+ async function readLockContent ( lockPath : string ) : Promise < string | null > {
6353 try {
6454 return await readFile ( lockPath , "utf8" ) ;
6555 } catch ( err ) {
@@ -71,7 +61,7 @@ export async function readLockContent(
7161// True when the lock is safe to take over: a tagged holder with a dead PID
7262// (crashed), or a legacy untagged lock older than the stale horizon. A
7363// vanished lock reads as not stale; the acquire loop retries the create.
74- export async function isLockStale (
64+ async function isLockStale (
7565 lockPath : string ,
7666 staleMs : number ,
7767 content : string ,
@@ -114,9 +104,6 @@ export interface FileLockOptions {
114104 timeoutError : FileLockTimeoutError ;
115105}
116106
117- // Credential locks in this repo serialize on exactly this acquire/release
118- // pairing: exclusive-create with a holder tag, stale takeover, and a fenced
119- // claim-matched release. Do not fork a variant that drops any of the three.
120107export async function withFileLock < TResult > (
121108 lockPath : string ,
122109 work : ( ) => Promise < TResult > ,
0 commit comments