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 {};