/** * How a `.lock` owner record can be opened safely on this platform. * * `posix-nofollow` proves the object's TYPE from the descriptor the bytes come off, so the * pathname can change freely without ever being followed. Windows has neither `O_NOFOLLOW` * nor `O_NONBLOCK`, so no such descriptor exists there and `windows-validated` brackets the * open with `lstat`/`fstat` identity proofs instead. `unsupported` is neither, and the * owner protocol fails closed rather than reading a pathname it cannot vouch for. */ export type SessionStateLockOwnerAccessStrategy = "posix-nofollow" | "windows-validated" | "unsupported"; /** * Select the safe owner-record access protocol for one platform/runtime pair. * * Platform identity comes first. A win32 runtime may expose numeric compatibility constants * without providing POSIX no-follow semantics, so their mere presence must never select the * POSIX path there. * * @internal Pure seam for cross-platform detection tests. */ export declare function detectedSessionStateLockOwnerAccessStrategy(platform: NodeJS.Platform, posixNoFollowAvailable: boolean): SessionStateLockOwnerAccessStrategy; /** * Test seams for the windows a reclaim passes through: the moment the path's TYPE has just * been decided and its owner has not been read yet, the stale verdict, and the FINAL * identity validation immediately before the unlink. Production code never sets them. */ export declare const SessionStateLockTestHooks: { afterLockTypeDecision?: (lockFile: string) => void | Promise; afterStaleInspection?: (lockFile: string) => void | Promise; beforeStaleRemoval?: (lockFile: string) => void | Promise; afterTransitionStaleInspection?: (transitionFile: string) => void | Promise; beforeTransitionStaleRemoval?: (transitionFile: string) => void | Promise; afterLegacyDirectoryStaleVerdict?: (lockDir: string) => void | Promise; /** * @internal Which owner-access strategy to exercise, so the Windows path can be proved * on the test filesystem this suite actually runs on. Production code never sets it, * and it is never serialized or exposed as runtime configuration. */ ownerAccessStrategy?: SessionStateLockOwnerAccessStrategy; /** * @internal How an owner pid is probed for liveness. Whether a signal is permitted is a * property of the OS and this process's privileges, so it cannot be arranged on disk. * Production code never sets it, and it is never serialized or exposed as runtime * configuration. */ probeProcessSignal?: (pid: number) => void; /** * @internal Runs inside a NEW owner record's write, after the exclusive create has * taken the pathname and before the record's bytes land. Throwing from it fails that * write exactly as an I/O fault does, which is the only deterministic way to reach the * write-failure cleanup and prove what that cleanup is authorized to delete. * Production code never sets it, and it is never serialized or exposed as runtime * configuration. */ ownerRecordWriteFault?: (file: string) => void | Promise; /** @internal Stable host identity seam for shared-volume ownership tests. */ ownerHostId?: () => string | Promise; /** @internal Installation identity loader seam for cache retry tests. */ loadInstallationHostId?: () => Promise; /** @internal Previous identity seam for upgrade recovery tests. */ legacyOwnerHostId?: () => string | Promise; /** @internal Lets legacy same-host fixtures exercise their pre-qualification paths. */ unqualifiedOwnerIsLocal?: boolean; /** @internal Runs after final live-owner validation and before descriptor rewrite. */ afterCurrentOwnerValidation?: (file: string) => void | Promise; /** * @internal Runs after a live owner has been proven and immediately before its * final pathname capture for release. Tests use it to replace the pathname and * prove that live-owner cleanup refuses a successor. */ beforeCurrentOwnerRelease?: (file: string) => void | Promise; beforeLegacyDirectoryRemoval?: (lockDir: string) => void | Promise; }; /** Raised when the lock could not be acquired; callers map it to their own refusal. */ export declare class SessionStateLockUnavailableError extends Error { constructor(cause?: unknown); } /** * The identity-bound deletion primitives this lock is built on. * * There is no portable atomic compare-and-delete in `fs`: validating a record and then * unlinking its PATHNAME is always two syscalls, and a successor that claims the path in * between loses its brand-new lock to the first reclaimer. These natives close that hole * for real — every removal is descriptor-relative, no-follow, and refuses unless the * object still carries the exact `dev`/`ino`/`nlink`/`size`/`mtimeNs`/SHA-256 identity the * caller proved. A replacement is therefore never deleted, by construction rather than by * a narrower window. */ export type SessionStateLockNativeBindings = Pick; /** How the deletion primitives are obtained. Throwing means they are unavailable. */ type SessionStateLockNativeLoader = () => SessionStateLockNativeBindings; /** * @internal The seam focused TS tests bind a faithful in-process implementation to. * * It exists because the deletion contract — "refuse unless the object on disk still has * exactly this identity" — is the thing under test, and a test double that merely reports * success would assert nothing. * * A LOADER rather than a value, so the unavailable case needs no special production * branch: a loader that throws is indistinguishable from an addon that will not load, * which is the only way to observe that path where the addon is present. Passing * `undefined` restores the real addon. */ export declare function setSessionStateLockNativeBindings(load: SessionStateLockNativeLoader | undefined): void; /** * Decide what actually occupies the lock path, without following it. * * @internal exported as the seam these decisions are tested through: a live legacy * directory owner must be provably left alone without waiting out a real stale window. */ export declare function reclaimStaleSessionStateLock(lockFile: string): Promise; /** * Serialize one read-modify-write of `stateFile` against every other holder of * `.lock`. * * Taking the path, cleaning up a failed write, and releasing are all pathname transitions, * so each runs under the transition claim — never the caller's operation, which holds the * lock file itself and may run for as long as it needs. */ export declare function withSessionStateFileLock(stateFile: string, operation: () => Promise): Promise; export {};