/** * detach/handle.ts — DetachHandle implementation. * * Pattern: Object-as-state-machine. Mutable status field; transitions * are one-way and irreversible (queued → running → done/failed). * Role: Backs the consumer-facing `DetachHandle` interface. The * public surface is the interface (defined in `types.ts`); * this class is the runtime impl. * * Internal vs public split: * - PUBLIC (in `types.ts`) — read-only properties + `wait()` * - INTERNAL (this file) — `_markRunning` / `_markDone` / * `_markFailed` mutators called by drivers * * The class implements `DetachHandle` (which has only readonly fields * exposed). Drivers cast to `HandleImpl` via the `asImpl()` helper to * call the mutators — a controlled escape from readonly. Consumers * cannot do this (they only see the interface). * * Promise caching contract: * - First `wait()` call: * - if status terminal → returns IMMEDIATELY-resolved Promise * - if not terminal → returns NEW Promise; resolvers stored * for use by `_markDone` / `_markFailed` * - Subsequent `wait()` calls → returns the SAME cached Promise * - The resolved/rejected value is the SAME on every call (no * re-running, no duplicated work) * * Concurrency notes: * - All transitions are sync. JavaScript is single-threaded so no * atomics or locks needed. * - State transitions out of terminal states are forbidden — calling * `_markDone` after `_markFailed` (or vice-versa) is a no-op * (defensive: prevents driver bugs from corrupting state). */ import type { DetachHandle, DetachWaitResult } from './types.js'; /** * Internal handle implementation. Drivers call the `_mark*` methods * to drive state transitions; consumers see only the readonly * `DetachHandle` interface. */ export declare class HandleImpl implements DetachHandle { readonly id: string; status: DetachHandle['status']; result: unknown; error: Error | undefined; private waitPromise; private resolveWait; private rejectWait; constructor(id: string); /** * Public — opt-in async join. Returns a cached Promise. * See `DetachHandle.wait()` docstring for contract. */ wait(): Promise; /** Transition queued → running. No-op if already past 'queued'. */ _markRunning(): void; /** * Transition to terminal 'done' with the given result. No-op if * already terminal (defensive: prevents driver bugs from corrupting * state). */ _markDone(result: unknown): void; /** * Transition to terminal 'failed' with the given error. No-op if * already terminal. */ _markFailed(error: Error): void; } /** * Type-narrowing helper — cast a public `DetachHandle` to its * implementation. Drivers (only) use this to call internal mutators. * * Throws if the handle isn't actually a `HandleImpl` — defends * against consumers passing a hand-rolled object that satisfies the * interface shape but lacks the mutators. */ export declare function asImpl(handle: DetachHandle): HandleImpl; /** * Driver-facing factory. Drivers MUST use this to create handles * (NOT construct `HandleImpl` directly — keeps the impl type private). */ export declare function createHandle(id: string): DetachHandle;