/** A canonical project root. The registry never derives identity by parsing it. */ declare const canonicalRootPathBrand: unique symbol; export type CanonicalRootPath = string & { readonly [canonicalRootPathBrand]: "CanonicalRootPath"; }; /** A pool-lifetime root generation. Generations are strictly increasing per root. */ declare const rootGenerationBrand: unique symbol; export type RootGeneration = number & { readonly [rootGenerationBrand]: "RootGeneration"; }; /** A realm-local concrete pool identity. IDs are never reused by a registry. */ declare const concretePoolIdBrand: unique symbol; export type ConcretePoolId = string & { readonly [concretePoolIdBrand]: "ConcretePoolId"; }; /** * Immutable provenance for one registration handle. The object identity is the * deregistration authority; the serial fields make the provenance inspectable * without treating a pool object as a registration token. */ export interface RegistrationIdentity { readonly concretePoolId: ConcretePoolId; readonly registrationSequence: number; } export declare function asCanonicalRootPath(root: string): CanonicalRootPath; export declare function asRootGeneration(generation: number): RootGeneration; export declare function asConcretePoolId(id: string): ConcretePoolId; export type LifecycleCloseCause = "sweep" | "explicit"; export type LifecycleRootState = "live" | "reaping" | "tombstoned"; /** Counts returned by a concrete pool after it has detached its indexed records. */ export interface LifecycleTeardownResult { readonly tornDownSessionCount?: number; readonly tornDownFacadeCount?: number; } /** The concrete-pool portion of the lifecycle contract. */ export interface LifecyclePool { closeProjectRoot(root: CanonicalRootPath, generation: RootGeneration): unknown; } export interface LifecyclePoolRegistrationOptions { readonly reapingEnabled: boolean; readonly evictOuterFacade: (root: CanonicalRootPath, generation: RootGeneration) => void; } export interface LifecyclePoolRegistration { readonly concretePoolId: ConcretePoolId; readonly reapingEnabled: boolean; readonly registrationIdentity: RegistrationIdentity; /** Short alias for callers that refer to the provenance token as identity. */ readonly identity: RegistrationIdentity; deregister(): void; } export type LifecycleStatResult = boolean | { readonly exists?: boolean; } | unknown; export type LifecycleStat = (root: CanonicalRootPath, concretePoolId: ConcretePoolId, generation: RootGeneration) => LifecycleStatResult | Promise; export type LifecycleDemandCheck = (root: CanonicalRootPath, concretePoolId: ConcretePoolId) => boolean | { readonly exists?: boolean; } | Promise; export interface LifecycleTimerSeam { setInterval(callback: () => void, delayMs: number): unknown; clearInterval(handle: unknown): void; } export interface RootReapedLifecycleEvent { readonly type: "subc_root_reaped"; readonly realm: string; readonly concretePoolId: ConcretePoolId; readonly canonicalRoot: CanonicalRootPath; readonly generation: RootGeneration; readonly consecutiveAbsenceCount: number; readonly tornDownSessionCount: number; readonly tornDownFacadeCount: number; readonly cause: LifecycleCloseCause; } export interface RootGenerationRejectedLifecycleEvent { readonly type: "subc_root_generation_rejected"; readonly realm: string; readonly concretePoolId: ConcretePoolId; readonly canonicalRoot: CanonicalRootPath; readonly expectedGeneration: RootGeneration; readonly currentGeneration?: RootGeneration; readonly boundary: string; } export type LifecycleEvent = RootReapedLifecycleEvent | RootGenerationRejectedLifecycleEvent; export interface LifecycleRegistryOptions { /** Label attached to realm-local metrics and lifecycle events. */ readonly realm?: string; /** Production cadence. Tests normally replace the timer and use a short value. */ readonly intervalMs?: number; readonly timer?: LifecycleTimerSeam; readonly setInterval?: LifecycleTimerSeam["setInterval"]; readonly clearInterval?: LifecycleTimerSeam["clearInterval"]; /** A stat that resolves for an existing root and rejects with ENOENT when absent. */ readonly stat?: LifecycleStat; /** Positive demand is the only signal that permits an initial or successor root. */ readonly demandCheck?: LifecycleDemandCheck; readonly onEvent?: (event: LifecycleEvent) => void; } export interface LifecycleRootSnapshot { readonly concretePoolId: ConcretePoolId; readonly registrationIdentity: RegistrationIdentity; readonly canonicalRoot: CanonicalRootPath; readonly generation: RootGeneration; readonly state: LifecycleRootState; readonly consecutiveAbsences: number; } export interface LifecycleRegistrationSnapshot { readonly concretePoolId: ConcretePoolId; readonly reapingEnabled: boolean; readonly roots: readonly LifecycleRootSnapshot[]; } export interface LifecycleRegistrySnapshot { readonly realm: string; readonly intervalMs: number; readonly timerActive: boolean; readonly registrations: readonly LifecycleRegistrationSnapshot[]; } export declare const DEFAULT_LIFECYCLE_INTERVAL_MS: number; /** * One lifecycle registry per JavaScript module realm. * * The registry deliberately knows only the concrete pool's root-close seam. It * owns root generations, absence observations, and transition ownership, while * the concrete pool remains responsible for indexed session and route cleanup. */ export declare class LifecycleRegistry { readonly realm: string; readonly intervalMs: number; private readonly registrations; private readonly registrationByPool; private readonly nextGenerationByRoot; private nextConcretePoolNumber; private nextRegistrationSequence; private readonly timer; private readonly statRoot; private readonly demand; private readonly onEvent?; private intervalHandle; private sweepInFlight; constructor(options?: LifecycleRegistryOptions); /** Register an unregistered concrete pool and permanently bind its reapingEnabled setting. */ registerLifecyclePool(pool: LifecyclePool, options: LifecyclePoolRegistrationOptions): LifecyclePoolRegistration; /** Functional form used by construction sites that keep a registry instance. */ registerPool(pool: LifecyclePool, options: LifecyclePoolRegistrationOptions): LifecyclePoolRegistration; /** Register a root before its facade is exposed. Repeated live registration is idempotent. */ registerRoot(poolId: ConcretePoolId, root: CanonicalRootPath): RootGeneration; /** * Get an existing live generation without creating one. This is the only * non-creating lookup used by status and background paths. */ currentGeneration(poolId: ConcretePoolId, root: CanonicalRootPath): RootGeneration | undefined; isTombstoned(poolId: ConcretePoolId, root: CanonicalRootPath, generation: RootGeneration): boolean; isCurrentRegistration(identity: RegistrationIdentity): boolean; isCurrentLiveGeneration(poolId: ConcretePoolId, root: CanonicalRootPath, generation: RootGeneration): boolean; getRootSnapshot(poolId: ConcretePoolId, root: CanonicalRootPath): LifecycleRootSnapshot | undefined; /** Return only roots visible to lifecycle operations; tombstones are retained until close settles. */ rootSnapshots(poolId?: ConcretePoolId): LifecycleRootSnapshot[]; snapshot(): LifecycleRegistrySnapshot; /** Alias for callers that use status terminology. */ status(): LifecycleRegistrySnapshot; /** * Demand gate for initial and successor creation. A live root is returned * without repeating the filesystem check; absent roots require a fresh positive * result and then receive exactly one greater generation. */ ensureRootForDemand(poolId: ConcretePoolId, root: CanonicalRootPath): Promise; demandRoot(poolId: ConcretePoolId, root: CanonicalRootPath): Promise; getBridgeDemand(poolId: ConcretePoolId, root: CanonicalRootPath): Promise; /** * Coordinate one generation's close. The live-to-reaping transition, tombstone, * outer eviction, and concrete close invocation all happen before the returned * promise can yield. Concurrent requests coalesce on the same close promise. */ requestProjectRootClose(poolId: ConcretePoolId, root: CanonicalRootPath, expectedGeneration: RootGeneration, cause?: LifecycleCloseCause): Promise; /** Coalesce concurrent ticks and observe only enabled, live registrations. */ sweep(): Promise; /** Alias used by timer seams and tests. */ sweepNow(): Promise; /** Emit a generation-rejection event when a lifecycle boundary refuses the expected generation. */ recordGenerationRejection(poolId: ConcretePoolId, root: CanonicalRootPath, expectedGeneration: RootGeneration, boundary: string): void; rejectGeneration(poolId: ConcretePoolId, root: CanonicalRootPath, expectedGeneration: RootGeneration, boundary: string): void; private runSweep; private currentRegistration; private currentRoot; private snapshotRoot; private finishRootClose; private deregisterInternal; private refreshTimer; private emit; } /** Create a lifecycle registry for callers that do not inject one; use getLifecycleRegistry() for the shared default. */ export declare function createLifecycleRegistry(options?: LifecycleRegistryOptions): LifecycleRegistry; export declare function getLifecycleRegistry(): LifecycleRegistry; export { LifecycleRegistry as SubcLifecycleRegistry }; export declare function registerLifecyclePool(pool: LifecyclePool, options: LifecyclePoolRegistrationOptions): LifecyclePoolRegistration; //# sourceMappingURL=lifecycle-registry.d.ts.map