import { Effect, Option, type Scope } from "effect"; /** What a lock file records about its holder. */ export interface LockMeta { /** The operation holding the lock, for display: "update", "merge", ... */ readonly op: string; /** Optional finer-grained step within `op`; "" when the holder set none. */ readonly phase: string; /** Process id of the holder, used for liveness detection. */ readonly pid: number; /** Hostname of the holder; pid liveness is only meaningful on the same host. */ readonly host: string; /** ISO timestamp of the acquisition. */ readonly startedAt: string; /** ISO timestamp of the current phase. */ readonly phaseStarted: string; } /** Which step of the lock lifecycle failed. */ export type LockOperation = "acquire" | "wait" | "release"; declare const LockError_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "LockError"; } & Readonly; /** * A lock could not be taken or written. `operation` is `"wait"` when the * acquire timed out against a live holder, and `"acquire"` when the lock file * itself could not be created or rewritten. `"release"` completes the lifecycle * for consumers that match exhaustively: releasing swallows its filesystem * errors, since staleness detection recovers a lock file left behind. */ export declare class LockError extends LockError_base<{ readonly path: string; readonly operation: LockOperation; readonly cause: unknown; }> { get message(): string; } /** An acquired lock. The scope that acquired it releases it. */ export interface LockHandle { /** The lock file this handle owns. */ readonly path: string; /** * Records a new phase in the lock file, which also refreshes its mtime so a * long operation is never mistaken for a stale one. A no-op after `release`. * Fails with `operation: "acquire"` when the rewrite fails. */ phase(description: string): Effect.Effect; /** * Gives the lock up. Idempotent, never fails, and never deletes a lock file * that another holder has since reclaimed. */ readonly release: Effect.Effect; } /** Options for a single acquire attempt. */ export interface AcquireOptions { /** The operation to record in the lock file. */ readonly op: string; /** Initial phase within `op`. Defaults to "". */ readonly phase?: string; /** Age past which a lock file is reclaimed. Defaults to 15 minutes. */ readonly staleMs?: number; } /** Options for an acquire that waits out a live holder. */ export interface WaitOptions extends AcquireOptions { /** Interval between attempts, jittered. Defaults to 150 ms. */ readonly pollMs?: number; /** How long to keep trying before failing with `operation: "wait"`. Defaults to 2 minutes. */ readonly timeoutMs?: number; } /** * Metadata of the lock file at `path`. Never fails: an absent, empty, or * unparseable file reads as `{}`, which every caller here treats as "a lock * whose holder cannot be identified". */ export declare const readLockMeta: (path: string) => Effect.Effect, never, never>; /** * Takes the lock at `path` if it is free or stale, without waiting. Scoped: * closing the scope releases the lock. Resolves `None` while a live holder * has it. */ export declare const tryAcquireLock: (path: string, options: AcquireOptions) => Effect.Effect, LockError, Scope.Scope>; /** * Waits for the lock at `path` and holds it for the rest of the scope. Fails * with `operation: "wait"` once `timeoutMs` elapses against a live holder. */ export declare const acquireLock: (path: string, options: WaitOptions) => Effect.Effect; /** * Runs `effect` while holding the lock at `path`, waiting up to `timeoutMs` * for a live holder to finish. The lock is released on success, failure and * interruption alike. */ export declare const withLock: (path: string, effect: Effect.Effect, options: WaitOptions) => Effect.Effect; /** * Metadata of the live holder of `path`, or `None` when the lock is free or * stale enough that the next acquire would reclaim it. */ export declare const lockStatus: (path: string, options?: { readonly staleMs?: number; } | undefined) => Effect.Effect>, never, never>; /** Coarse duration for display: "45s", "3m", "2h", "5d". */ export declare function humanAge(seconds: number): string; /** * How long the holder has been in its current phase, as of `nowMs`. Null when * the metadata carries no parseable timestamp. Pure, so pass * `yield* Clock.currentTimeMillis`. */ export declare function lockAge(meta: Partial, nowMs: number): string | null; /** One-line description of a holder: "op: phase", the op, or "busy". */ export declare function lockLabel(meta: Partial): string; export {};