/** * Claim parsing and lock-file mechanics (exclusive creation, reclaim, * release) for the owned-lock primitive. * @module @skillsmith/core/config/owned-lock.claim * @see owned-lock.ts for the full soundness argument and the PUBLIC API. * @see owned-lock.acquire.ts for the core acquire loop + `StuckLockError`, * split into a sibling file purely to keep both under the repo's * 500-line-per-file gate. * * INTERNAL module -- not part of the public surface (no `package.json` * subpath export). Co-located tests and the cross-process race-test child * harness import directly from here, by relative path, specifically to * reach `createLockExclusive`'s `linkSyncOverride` test seam -- see * `owned-lock.acquire.ts` for the analogous rationale on the acquire loop's * own destructive test-only options. */ import type { Claim, ReclaimOutcome, RefusalCategory } from './owned-lock.types.js'; export declare function randomHex(bytes: number): string; /** * Synchronously block the calling thread for `ms` milliseconds without * spinning the CPU. `Atomics.wait` on a throwaway `SharedArrayBuffer` is the * standard synchronous-sleep primitive in Node (unlike browsers, Node does * not forbid calling it on the main thread). */ export declare function sleepSync(ms: number): void; /** * Parse raw lock-file text into a {@link Claim}. A v1 claim is one line of * canonical JSON; a legacy claim is a bare decimal integer (today's * `acquireConfigLock` format, `String(process.pid)`); anything else -- * including valid JSON at a DIFFERENT `v` -- is `unparseable` and therefore * NEVER auto-reclaimed. */ export declare function parseClaim(text: string): Claim; /** * Bounded, TOCTOU-free claim read: open once, `fstat` THAT fd (not the * path), refuse above {@link MAX_LOCK_BYTES}, read from the same fd. An open * failure is classified by {@link claimForOpenFailure}. */ export declare function readClaim(path: string): Claim; export declare function isAutoReclaimDisabled(): boolean; /** * Conservative in every ambiguous direction: PID reuse and a not-our-signal * `EPERM` both read as ALIVE (declining to reclaim costs a timeout; an * incorrect reclaim costs a lost caller). `pid <= 0` is rejected WITHOUT * probing -- `kill(0, 0)` signals the process GROUP, which would make the * liveness probe meaningless. */ export declare function isOwnerDefinitelyDead(claim: Claim, killProbe?: typeof process.kill): boolean; export declare function classifyRefusal(claim: Claim): RefusalCategory; /** * Create `path` exclusively via a temp file + `linkSync` (atomic; `EEXIST` * if `path` exists) so a lock file is NEVER observable without a complete * claim -- closing R2 (a writer could otherwise crash between create and * write, leaving a permanently unreclaimable main lock). On filesystems * without hardlink support this throws rather than falling back to a * non-atomic `openSync('wx')` + separate write -- that two-step sequence * would itself reopen R2 (an observer between the two steps, or a crash in * between, sees an empty/truncated lock that is then PERMANENTLY * unreclaimable, since an `unparseable` claim is never auto-reclaimed by * design). Failing closed on an unsupported filesystem is the sound * resolution the reviewer required; hardlink support is effectively * universal on the filesystems Node actually runs on. * * @param linkSyncOverride - @internal test seam (owned-lock.test.ts item 14) * to exercise the hardlink-unavailable fail-closed path deterministically. * Never set outside that test. */ export declare function createLockExclusive(path: string, recordJson: string, linkSyncOverride?: (existingPath: string, newPath: string) => void): boolean; export interface ReclaimInternalOptions { /** @internal NEGATIVE CONTROL ONLY (owned-lock-reclaim-race.test.ts) -- removes the authoritative re-read that makes this mechanism sound. Never set outside that spec. */ unsafeSkipRevalidation?: boolean; linkSyncOverride?: (existingPath: string, newPath: string) => void; /** Max wait for the reclaim lock (ms); defaults to RECLAIM_LOCK_TIMEOUT_MS. 0 means a single try, with no synchronous sleep. */ reclaimLockTimeoutMs?: number; } /** * Validate and destroy a stale main lock inside a region from which every * other reclaimer is excluded (the reclaim lock). This is the ONLY code path * that may ever unlink `.lock` on the strength of a liveness * inference: a caller's own pre-filter is advisory and discarded; only the * re-read performed HERE, under the reclaim lock, authorizes the unlink. */ export declare function tryReclaimUnderLock(lockPath: string, reclaimPath: string, opts: ReclaimInternalOptions): ReclaimOutcome; export declare function releaseOwned(path: string, token: string): void; export declare function makeRelease(lockPath: string, token: string): () => void; //# sourceMappingURL=owned-lock.claim.d.ts.map