/** * Multi-tab coordination: primary/secondary election (Web Locks) * + presence heartbeat (BroadcastChannel). Browser-only; opt-in; no-op * when the APIs are absent. The lock/channel interfaces are hub-local * (structurally compatible with @noy-db/by-peer + @noy-db/by-tabs, but * not imported — those packages depend on hub). */ export type TabRole = 'primary' | 'secondary' | 'unknown'; export interface TabPresence { readonly tabId: string; readonly lastSeen: number; readonly role: TabRole; } export type Unsubscribe = () => void; /** Structural subset of the Web Locks API / by-peer's MinimalLockManager. */ export interface TabLockManager { request(name: string, options: { mode?: 'exclusive' | 'shared'; signal?: AbortSignal; }, callback: (lock: unknown) => Promise): Promise; } /** Structural subset of by-peer's PeerChannel / a BroadcastChannel wrapper. */ export interface TabChannel { send(payload: string): void; on(event: 'message', listener: (payload: string) => void): Unsubscribe; on(event: 'close', listener: () => void): Unsubscribe; close(): void; readonly isOpen: boolean; } export interface TabCoordinationOptions { readonly lockManager?: TabLockManager; readonly channel?: TabChannel; readonly tabId?: string; readonly lockName?: string; readonly heartbeatMs?: number; readonly staleMs?: number; readonly now?: () => number; /** * Close the channel on `dispose()`. Set this only for a channel the * coordinator owns (e.g. the one `defaultChannel()` created); leave it * false for a caller-injected channel so the coordinator never closes a * channel it didn't create. Default: false. */ readonly closeChannelOnDispose?: boolean; /** * Also propagate committed writes to other tabs. Default true: * when tab coordination is enabled and a channel is available, a write in * one tab refreshes that document in every other tab. Set false to opt out. */ readonly propagateWrites?: boolean; /** * Channel for write propagation — distinct from the presence * channel. Default: an inline BroadcastChannel on `noydb:tab-writes`. */ readonly writeChannel?: TabChannel; } export declare class TabCoordinator { #private; readonly tabId: string; role: TabRole; constructor(opts?: TabCoordinationOptions); start(): void; activeTabs(): TabPresence[]; onTabRoleChange(fn: (r: TabRole) => void): Unsubscribe; onActiveTabsChange(fn: (t: TabPresence[]) => void): Unsubscribe; dispose(): void; /** @internal test seam — broadcast one heartbeat now. */ _beat(): void; } /** Browser default lock manager (navigator.locks) or undefined. */ export declare function defaultLockManager(): TabLockManager | undefined; /** Browser default channel: an inline BroadcastChannel wrapper, or undefined. */ export declare function defaultChannel(name?: string): TabChannel | undefined;