import { ThreadStream } from "../client/stream/index.js"; import { AcquiredProjection, ProjectionSpec, RootEventBus } from "./types.js"; //#region src/stream/channel-registry.d.ts /** * Ref-counted, thread-aware projection registry. * * Owns the `spec.key → (store, runtime)` mapping for one * {@link StreamController}. Lifecycle: * * - `acquire(spec)` → +1 ref, returns `{ store, release }`. The * first acquire opens the projection's runtime; subsequent * acquires for the same key share both the store and the * runtime. * - `release()` → -1 ref. When the last consumer releases, * the entry is removed and its runtime disposed. * - `bind(thread)` → swap or detach the underlying thread; every * live entry's runtime is recreated against the new thread, * keeping the same store identity. * - `dispose()` → tear everything down (idempotent). Safe to * call multiple times. * * The registry is intentionally not generic over a state shape — * different consumers can hold projections producing different * snapshot types, so the registry keys everything as `unknown` and * lets {@link acquire} reapply the caller's `T` at the boundary. */ declare class ChannelRegistry { #private; /** * Construct a registry bound to the controller's root event bus. * * The bus is forwarded to every projection's `open()` so root-scoped * projections can avoid opening a second server subscription when * their channel set is already covered by the root pump. * * @param rootBus - Read-only fan-out of the root subscription. */ constructor(rootBus: RootEventBus); /** * Rebind every live entry to a new {@link ThreadStream} (or detach * when `thread == null`). * * Each live entry has its current runtime disposed (best-effort) * and its store reset to `entry.initial` so consumers see a clean * slate during the swap. When `thread != null`, a fresh runtime is * opened against the new thread. * * Critically the {@link StreamStore} *instance* is preserved across * the rebind: framework subscribers (e.g. React's * `useSyncExternalStore`) keep observing the same store reference, * so their subscriptions survive the swap. * * No-op when called with the currently bound thread. * * @param thread - The thread stream to bind, or `undefined` to detach. */ bind(thread: ThreadStream | undefined): void; /** Currently bound thread (may be `undefined` pre-mount). */ get thread(): ThreadStream | undefined; /** * Acquire a ref-counted projection. * * If no entry exists for `spec.key`, one is created (allocating a * {@link StreamStore} seeded with `spec.initial`) and — when a * thread is currently bound — its runtime is opened immediately. * If an entry already exists, its ref count is incremented and the * existing store is returned. * * The returned `release()` is idempotent: calling it more than once * is a no-op. When the ref count drops to zero, the entry is removed * and its runtime disposed (best-effort, never throws into callers). * * Safe to call from any framework lifecycle hook. Subsequent calls * for the same `spec.key` always return the same `store` reference * for the lifetime of the controller, so consumers can rely on store * identity. * * @typeParam T - Snapshot type produced by this projection. * @param spec - Projection contract; the registry keys off `spec.key`. * @returns A `{ store, release }` handle. */ acquire(spec: ProjectionSpec): AcquiredProjection; /** * Tear everything down. * * Detaches the bound thread (so no further `bind()` calls reopen * runtimes) and disposes every live runtime in parallel. Safe to * call multiple times — subsequent calls find an empty registry * and resolve immediately. */ dispose(): Promise; /** * Number of live entries. Diagnostic-only — callers should not * branch on this value at runtime; it exists for tests asserting * that consumers properly release their projections. */ get size(): number; } //#endregion export { ChannelRegistry }; //# sourceMappingURL=channel-registry.d.ts.map