{"version":3,"sources":["../src/native.ts","../src/react/provider.tsx","../src/core/backoff.ts","../src/core/client-id.ts","../src/core/topics.ts","../src/core/diagnostics.ts","../src/core/dedupe.ts","../src/core/environment.ts","../src/core/origin.ts","../src/core/presence.ts","../src/core/queue.ts","../src/core/rooms.ts","../src/core/manager.ts","../src/core/ambient.ts","../src/native/environment.ts","../src/react/use-channel.ts","../src/react/use-presence.ts","../src/react/use-typing.ts","../src/core/typing.ts","../src/react/use-diagnostics.ts"],"sourcesContent":["\"use client\";\n\n/**\n * `@ai-matrx/realtime/native` — the React Native / Expo binding.\n *\n * It is the `/react` binding with the platform seam already filled: the\n * AppState-driven environment bridge is wired in, so an Expo app mounts\n * `<NativeRealtimeProvider client={supabase}>` and gets background/foreground\n * survival, reconnect, and reconcile-on-wake with no mobile-specific code of\n * its own (C23/C25 — the platform layer is built INSIDE the package).\n *\n * Everything else — hooks, presence, typing, diagnostics — is identical to web,\n * because the core is platform-agnostic by construction (D4).\n */\n\nimport { createElement, useMemo, type ReactNode } from \"react\";\nimport {\n  RealtimeProvider,\n  type RealtimeProviderProps,\n} from \"./react/provider\";\nimport {\n  createReactNativeEnvironment,\n  type ReactNativeEnvironmentOptions,\n} from \"./native/environment\";\n\nexport {\n  createReactNativeEnvironment,\n} from \"./native/environment\";\nexport type { ReactNativeEnvironmentOptions } from \"./native/environment\";\n\nexport type NativeRealtimeProviderProps = Omit<RealtimeProviderProps, \"environment\"> & {\n  reachability?: ReactNativeEnvironmentOptions[\"reachability\"];\n};\n\nexport function NativeRealtimeProvider(props: NativeRealtimeProviderProps): ReactNode {\n  const { reachability, ...rest } = props;\n  const environment = useMemo(\n    () =>\n      createReactNativeEnvironment(\n        reachability !== undefined ? { reachability } : {},\n      ),\n    [reachability],\n  );\n  return createElement(RealtimeProvider, { ...rest, environment });\n}\n\n// The full React binding (and, through it, the core) so a native app needs ONE\n// import specifier.\nexport * from \"./react\";\n","\"use client\";\n\n/**\n * `<RealtimeProvider>` — the one place an app wires realtime.\n *\n * The host injects IDENTITY and nothing else: a Supabase client and the signed-in\n * user id. Every hard part (topics, echo suppression, dedup, the handler queue,\n * reconnect, backfill, tab-sleep, presence expiry, diagnostics) is already\n * inside the manager (C22).\n *\n * The manager is torn down on unmount, which tears down every channel it owns.\n * That is the enforced lifecycle from R1: a leaked channel is not something a\n * consumer can cause from here.\n */\n\nimport {\n  createContext,\n  useContext,\n  type Context,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\";\nimport { createRealtimeManager, type RealtimeManager } from \"../core/manager\";\nimport { setAmbientRealtimeManager } from \"../core/ambient\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { createConsoleDiagnostics } from \"../core/diagnostics\";\nimport { defaultEnvironment, type EnvironmentBridge } from \"../core/environment\";\nimport type { BackoffOptions } from \"../core/backoff\";\nimport type { DedupeOptions } from \"../core/dedupe\";\nimport type { WriteLedger } from \"../core/origin\";\nimport type { ConnectionHealth, DiagnosticsSink, RealtimeClientLike } from \"../core/types\";\n\n/**\n * 🚨 THE CONTEXTS LIVE IN A GLOBAL SLOT, NOT AT MODULE SCOPE.\n *\n * This package ships dual ESM + CJS. A host bundling the ESM build while a\n * SIBLING PACKAGE (`@ai-matrx/messaging`) resolves the CJS one evaluates this\n * module twice, and two `createContext` calls are two different contexts: the\n * sibling's `useRealtimeManager()` would read `null` while the host's provider\n * sits right above it, and the sibling would silently build a SECOND manager.\n * That is the module-level-mutable-state ban applied to React context.\n */\ninterface Contexts {\n  manager: Context<RealtimeManager | null>;\n  /**\n   * Whether a `<RealtimeProvider>` is mounted above — TRUE even while its\n   * `client` is still null and the manager is therefore null. A sibling package\n   * deciding whether to mount its own provider must be able to tell \"no\n   * provider\" from \"provider still resolving its client\"; the manager context\n   * alone cannot, and guessing means a provider that appears, then un-mounts\n   * when the host's client arrives, rebuilding every channel underneath it.\n   */\n  mounted: Context<boolean>;\n}\n\nfunction contexts(): Contexts {\n  return globalSlot<Contexts>(\"ai-matrx.realtime.react-contexts\", () => ({\n    manager: createContext<RealtimeManager | null>(null),\n    mounted: createContext<boolean>(false),\n  }));\n}\n\nexport interface RealtimeProviderProps {\n  /** Any Supabase client. `null` while the host is still resolving one. */\n  client: RealtimeClientLike | null | undefined;\n  /** The signed-in user id, when known. Enables the `updated_by` echo test. */\n  actorId?: string | undefined;\n  /** Defaults to the browser bridge (inert under SSR). `/native` supplies the RN one. */\n  environment?: EnvironmentBridge;\n  /** Defaults to a console sink that always states the remedy. */\n  diagnostics?: DiagnosticsSink;\n  backoff?: BackoffOptions;\n  dedupe?: DedupeOptions;\n  queue?: { maxDepth?: number; sliceMs?: number };\n  /** Share the host's write ledger so optimistic writes suppress their own echoes. */\n  ledger?: WriteLedger;\n  children: ReactNode;\n}\n\nexport function RealtimeProvider(props: RealtimeProviderProps): ReactNode {\n  const { client, actorId, children } = props;\n\n  // 🚨 THE MANAGER'S LIFETIME IS `client` + `actorId`, AND NOTHING ELSE.\n  //\n  // Hosts pass `environment={createBrowserEnvironment()}` or an inline\n  // `diagnostics={(e) => log(e)}` — fresh objects on every render. Keying the\n  // manager on those identities tears down and rebuilds EVERY CHANNEL on every\n  // parent re-render: a permanent resubscribe loop that looks like \"realtime\n  // works but drops events\", and one that no host could see or fix from\n  // outside. (Caught by `hooks.test.tsx` — a host would have shipped it.)\n  //\n  // So the mutable-shaped props are read through a ref via stable delegates\n  // below, and the pure-configuration ones (backoff/dedupe/queue/ledger) are\n  // read ONCE when the manager is built. Changing those later is a\n  // configuration change, not a render prop; it takes effect when the client or\n  // the signed-in user changes.\n  const optionsRef = useRef(props);\n  optionsRef.current = props;\n\n  const [fallbackEnvironment] = useState(() => defaultEnvironment());\n  const [fallbackDiagnostics] = useState(() => createConsoleDiagnostics());\n\n  const environment = useMemo<EnvironmentBridge>(\n    () => ({\n      name: \"provider\",\n      isVisible: () => (optionsRef.current.environment ?? fallbackEnvironment).isVisible(),\n      isOnline: () => (optionsRef.current.environment ?? fallbackEnvironment).isOnline(),\n      subscribe: (listener) =>\n        (optionsRef.current.environment ?? fallbackEnvironment).subscribe(listener),\n    }),\n    [fallbackEnvironment],\n  );\n\n  const diagnostics = useMemo<DiagnosticsSink>(\n    () => (event) => (optionsRef.current.diagnostics ?? fallbackDiagnostics)(event),\n    [fallbackDiagnostics],\n  );\n\n  const manager = useMemo<RealtimeManager | null>(() => {\n    if (!client) return null;\n    const { backoff, dedupe, queue, ledger } = optionsRef.current;\n    return createRealtimeManager({\n      client,\n      actorId,\n      environment,\n      diagnostics,\n      ...(backoff !== undefined ? { backoff } : {}),\n      ...(dedupe !== undefined ? { dedupe } : {}),\n      ...(queue !== undefined ? { queue } : {}),\n      ...(ledger !== undefined ? { ledger } : {}),\n    });\n  }, [client, actorId, environment, diagnostics]);\n\n  // PUBLISH THE ONE MANAGER for code that cannot call a hook — Redux\n  // middleware, thunks, module-level subscription owners. Without this door\n  // their only options are a SECOND manager (a second write ledger, so every\n  // optimistic write of theirs costs a refetch) or a hand-rolled channel. See\n  // `core/ambient.ts`. Publishing happens in a layout effect ordering-wise\n  // alongside disposal so the ambient value never outlives the manager.\n  useEffect(() => {\n    setAmbientRealtimeManager(manager);\n    if (!manager) return undefined;\n    return () => {\n      setAmbientRealtimeManager(null);\n      manager.dispose();\n    };\n  }, [manager]);\n\n  const { manager: ManagerContext, mounted: MountedContext } = contexts();\n  return (\n    <MountedContext.Provider value={true}>\n      <ManagerContext.Provider value={manager}>{children}</ManagerContext.Provider>\n    </MountedContext.Provider>\n  );\n}\n\n/**\n * The manager, or `null` while the host has no client yet. Every hook in this\n * package tolerates `null` by staying disconnected — a surface rendered before\n * auth resolves must not throw.\n */\nexport function useRealtimeManager(): RealtimeManager | null {\n  return useContext(contexts().manager);\n}\n\n/**\n * Is a `<RealtimeProvider>` mounted above this component?\n *\n * For the ONE decision that needs it: a package that owns realtime work of its\n * own (messaging, meet) must ride the host's single manager when the host has\n * one — a second manager means a second write ledger, so one manager's\n * optimistic echo looks remote to the other — and mount its own only in an app\n * that wires none. `useRealtimeManager() !== null` cannot answer that question:\n * it is also null while a mounted provider waits for its Supabase client, and a\n * package that guessed \"no provider\" there would mount one, then tear it (and\n * every channel under it) down the moment the host's client arrived.\n */\nexport function useIsRealtimeProviderMounted(): boolean {\n  return useContext(contexts().mounted);\n}\n\n/**\n * The manager, or a thrown error naming the fix. Use it in code that genuinely\n * cannot proceed without realtime — never to \"handle\" a missing provider.\n */\nexport function useRequiredRealtimeManager(): RealtimeManager {\n  const manager = useContext(contexts().manager);\n  if (!manager) {\n    throw new Error(\n      \"No realtime manager. Wrap this tree in <RealtimeProvider client={supabase}> \" +\n        \"(from @ai-matrx/realtime/react, or NativeRealtimeProvider from @ai-matrx/realtime/native). \" +\n        \"If the provider IS mounted, its `client` prop is still null — render this surface \" +\n        \"after the Supabase client exists, or use useRealtimeManager() and tolerate null.\",\n    );\n  }\n  return manager;\n}\n\n/** Aggregate connection health, polled. For a live/degraded badge. */\nexport function useRealtimeHealth(pollMs = 2_000): ConnectionHealth {\n  const manager = useRealtimeManager();\n  const [health, setHealth] = useState<ConnectionHealth>(\"live\");\n  useEffect(() => {\n    if (!manager) {\n      setHealth(\"down\");\n      return undefined;\n    }\n    const tick = (): void => setHealth(manager.health());\n    tick();\n    const timer = setInterval(tick, pollMs);\n    return () => clearInterval(timer);\n  }, [manager, pollMs]);\n  return health;\n}\n","/**\n * Reconnect backoff with a STABILITY RESET and jitter.\n *\n * Two mistakes this replaces, both measured in matrx-frontend:\n *\n * - **Resetting the attempt counter on SUBSCRIBED.** A flapping channel then\n *   cycles at the 1s floor forever, and each cycle fires a full catch-up\n *   fetch — a self-inflicted request storm on the exact network that is\n *   already struggling. The counter resets only after the channel has STAYED\n *   healthy for `stabilityMs`.\n * - **No jitter.** Every tab of every user reconnects on the same doubling\n *   schedule after a Realtime deploy, so the service gets a synchronized\n *   thundering herd at 1s, 2s, 4s… Jitter spreads them.\n */\n\nexport interface BackoffOptions {\n  /** First delay in ms; doubles up to `maxDelayMs`. Default 1000. */\n  initialDelayMs?: number;\n  /** Ceiling for the delay. Default 30000. */\n  maxDelayMs?: number;\n  /**\n   * How long a connection must HOLD before its next failure starts from the\n   * floor again. Default 30000.\n   */\n  stabilityMs?: number;\n  /** Fraction of the delay applied as random jitter, 0..1. Default 0.25. */\n  jitter?: number;\n  /** Injectable for tests. Default `Math.random`. */\n  random?: () => number;\n  /** Injectable for tests. Default `Date.now`. */\n  now?: () => number;\n}\n\nexport interface Backoff {\n  /** Delay for the next attempt, advancing the attempt counter. */\n  nextDelayMs(): number;\n  /** Record that the connection came up (starts the stability window). */\n  markConnected(): void;\n  /** Consecutive failed attempts since the last stability reset. */\n  attempts(): number;\n  /** True once `attempts` passes the alarm threshold — for loud diagnostics. */\n  isAlarming(): boolean;\n  reset(): void;\n}\n\n/**\n * Past this many consecutive failures the channel is not \"retrying\", it is\n * BROKEN, and the diagnostics sink escalates from warn to error. Silence here\n * is how a permanently dead subscription hides behind a spinner.\n */\n// Three retries cover only about seven seconds (1s + 2s + 4s) of ordinary\n// socket churn.  A transient provider deploy, laptop wake, or brief network\n// handoff must stay observable at warning level without creating one red\n// capture per logical channel.  Escalate only after the sixth consecutive\n// failure, after roughly a minute of the default capped ladder.\nexport const RECONNECT_ALARM_ATTEMPTS = 6;\n\n/**\n * The bounded policy: after this many consecutive failed attempts WITHOUT a\n * stable connection in between, the manager stops retrying a channel, says so\n * once (`channel.reconnect.abandoned`), and leaves it `disconnected` until the\n * tab wakes or the network returns. A channel for a conversation that was never\n * created (or whose first send was refused) otherwise reconnects forever.\n */\nexport const RECONNECT_GIVE_UP_ATTEMPTS = 10;\n\nexport function createBackoff(options: BackoffOptions = {}): Backoff {\n  const {\n    initialDelayMs = 1_000,\n    maxDelayMs = 30_000,\n    stabilityMs = 30_000,\n    jitter = 0.25,\n    random = Math.random,\n    now = Date.now,\n  } = options;\n\n  let attempt = 0;\n  let connectedAt: number | null = null;\n\n  return {\n    nextDelayMs(): number {\n      // A connection that HELD long enough earned a fresh floor. A connection\n      // that dropped inside the window is flapping and keeps climbing.\n      if (connectedAt !== null && now() - connectedAt >= stabilityMs) {\n        attempt = 0;\n      }\n      connectedAt = null;\n      const base = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);\n      attempt += 1;\n      const spread = base * jitter;\n      return Math.round(base - spread / 2 + random() * spread);\n    },\n    markConnected(): void {\n      connectedAt = now();\n    },\n    attempts(): number {\n      return attempt;\n    },\n    isAlarming(): boolean {\n      return attempt >= RECONNECT_ALARM_ATTEMPTS;\n    },\n    reset(): void {\n      attempt = 0;\n      connectedAt = null;\n    },\n  };\n}\n","/**\n * The client session id — the single fact echo suppression is built on.\n *\n * Every broadcast this package sends carries it (`MatrxBroadcastEnvelope.cid`),\n * and every write a consumer registers on the write ledger is attributed to it.\n * It is STABLE for the lifetime of the JS realm and UNIQUE per tab/app instance:\n * two tabs of the same user are two clients, because a user watching her own\n * edit appear in the other tab is a real remote event, not an echo.\n *\n * It lives in a globalThis slot (see `slot.ts`) so the ESM and CJS graphs of\n * this package agree on it. A per-module `let` here would hand the sender and\n * the receiver two different ids and disable echo suppression entirely.\n */\n\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\n/** RFC-4122-shaped id, with a documented fallback for runtimes without WebCrypto. */\nexport function randomId(): string {\n  const cryptoRef = (globalThis as { crypto?: Crypto }).crypto;\n  if (cryptoRef && typeof cryptoRef.randomUUID === \"function\") {\n    return cryptoRef.randomUUID();\n  }\n  if (cryptoRef && typeof cryptoRef.getRandomValues === \"function\") {\n    const bytes = cryptoRef.getRandomValues(new Uint8Array(16));\n    const hex: string[] = [];\n    for (const byte of bytes) hex.push(byte.toString(16).padStart(2, \"0\"));\n    return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n      .slice(6, 8)\n      .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10, 16).join(\"\")}`;\n  }\n  // Last resort (old JSC on some RN builds, hardened sandboxes). Uniqueness is\n  // still adequate for a per-event key; it is never used as a security value.\n  return `mxrt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n}\n\n/** The stable id of THIS client session. */\nexport function clientSessionId(): string {\n  return globalSlot(\"ai-matrx.realtime.client-session-id\", () => randomId());\n}\n","/**\n * THE CHANNEL-NAMING REGISTRY — one place where channels are named, so two\n * features can never collide (R1).\n *\n * Two failures live here, and they look nothing alike:\n *\n * 1. **Two features choosing the same topic string.** Supabase's\n *    `RealtimeClient.channel(topic)` DEDUPES BY TOPIC: a second call with a\n *    topic that is still registered returns the SAME channel object. Two\n *    features that both picked `\"messages\"` therefore silently share one\n *    channel, and the second one's `.on(...)` calls land after the first\n *    one's `subscribe()` — which throws, or (worse) attaches nothing. The cure\n *    is a declared namespace per feature, refused at registration time if it\n *    is already taken by a different declaration.\n *\n * 2. **One feature colliding with ITSELF across a remount.** React 19\n *    double-invokes effects and Fast Refresh re-runs them, while\n *    `removeChannel` is async and NOT awaited by React. The remount's\n *    `.channel(topic)` therefore hands back the previous mount's still-joined\n *    channel, and `.on(\"postgres_changes\", ...)` on it throws \"cannot add\n *    postgres_changes callbacks after subscribe()\". The cure is\n *    `uniqueChannelTopic()`.\n *\n * 🚨 **`uniqueChannelTopic()` IS FOR POSTGRES CHANGES ONLY.** For that transport\n * the topic really is nothing but a client-side connection key — the server\n * pushes on the declared filter — so making every INSTANCE unique costs nothing.\n * For **broadcast and presence the topic IS the room**: it is the routing key\n * the server fans messages out on, and a unique instance topic silently puts\n * every participant in a room of one. That was the 0.1.x defect. Those channels\n * go on the wire with their DECLARED topic and are shared+ref-counted inside the\n * client by `rooms.ts`, which is where failure 2 is handled for them.\n *\n * Both are applied automatically by the manager — a consumer names a channel\n * through a namespace and never thinks about any of it again.\n */\n\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\n/** Every Matrx channel topic starts here, so a topic is recognizable in logs. */\nexport const TOPIC_PREFIX = \"mx\";\n\nexport interface ChannelNamespaceSpec {\n  /** Feature-level name, unique platform-wide. Lowercase, `[a-z0-9-]`. */\n  namespace: string;\n  /**\n   * Ordered part names this namespace requires, e.g. `[\"userId\"]` or\n   * `[\"conversationId\"]`. A namespace with zero parts is a singleton channel.\n   */\n  parts: readonly string[];\n  /** What this channel carries. Shown by the diagnostics surface. */\n  description: string;\n  /**\n   * A FOREIGN topic root, used verbatim in place of `mx:<namespace>`.\n   *\n   * Only for a channel whose topic string is a contract owned by a separately\n   * released peer — the deployed Chrome extension's `matrx-extension-bridge`\n   * is the case this exists for. Renaming such a topic to `mx:…` does not\n   * migrate anything; it puts this client in a room of its own while the peer\n   * keeps listening where it always did, silently.\n   *\n   * The namespace is still declared and still collision-checked, so nothing\n   * else can claim it and the diagnostics surface still lists it. Pair it with\n   * `wire: {mode:\"raw\"}` on the channel spec — a foreign topic almost always\n   * comes with a foreign payload shape.\n   */\n  foreignTopic?: string;\n}\n\nexport interface ChannelNamespace {\n  readonly spec: ChannelNamespaceSpec;\n  /** Build the stable topic for one instance of this channel. */\n  topic(parts?: Readonly<Record<string, string>>): string;\n}\n\ninterface Registry {\n  namespaces: Map<string, ChannelNamespaceSpec>;\n  sequence: number;\n}\n\nfunction registry(): Registry {\n  return globalSlot<Registry>(\"ai-matrx.realtime.topic-registry\", () => ({\n    namespaces: new Map(),\n    sequence: 0,\n  }));\n}\n\nconst NAMESPACE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nexport class ChannelNamespaceCollisionError extends Error {\n  readonly namespace: string;\n  readonly existing: ChannelNamespaceSpec;\n  readonly attempted: ChannelNamespaceSpec;\n\n  constructor(\n    namespace: string,\n    existing: ChannelNamespaceSpec,\n    attempted: ChannelNamespaceSpec,\n  ) {\n    super(\n      `Realtime channel namespace \"${namespace}\" is already declared as ` +\n        `{parts: [${existing.parts.join(\", \")}]} \"${existing.description}\" and cannot be ` +\n        `re-declared as {parts: [${attempted.parts.join(\", \")}]} \"${attempted.description}\". ` +\n        `Two features sharing a topic share ONE Supabase channel and silently lose ` +\n        `each other's bindings. Pick a distinct namespace, or import the existing one.`,\n    );\n    this.namespace = namespace;\n    this.existing = existing;\n    this.attempted = attempted;\n    this.name = \"ChannelNamespaceCollisionError\";\n  }\n}\n\nfunction sameSpec(a: ChannelNamespaceSpec, b: ChannelNamespaceSpec): boolean {\n  return (\n    a.description === b.description &&\n    a.foreignTopic === b.foreignTopic &&\n    a.parts.length === b.parts.length &&\n    a.parts.every((part, index) => part === b.parts[index])\n  );\n}\n\n/** Same topic shape (parts + foreign topic): the same channel, whatever its prose says. */\nfunction sameShape(a: ChannelNamespaceSpec, b: ChannelNamespaceSpec): boolean {\n  return (\n    a.foreignTopic === b.foreignTopic &&\n    a.parts.length === b.parts.length &&\n    a.parts.every((part, index) => part === b.parts[index])\n  );\n}\n\nfunction isProduction(): boolean {\n  return (\n    typeof process !== \"undefined\" &&\n    typeof process.env === \"object\" &&\n    process.env?.NODE_ENV === \"production\"\n  );\n}\n\n/**\n * Declare a channel namespace. Idempotent for an identical declaration (so\n * Fast Refresh and dual module graphs are fine); throws on a genuine collision.\n *\n * Outside production, the SAME channel (same parts and foreign topic) re-declared\n * with a new description is a newer copy of its own module — Fast Refresh, or a\n * package reinstalled under a long-running dev server whose globalThis registry\n * still holds the old declaration. It replaces the old declaration and warns;\n * it never throws (live 2026-10-07: one in-place description change 500'd every\n * page on the shared dev server). A different shape under the same name is two\n * channels and is always refused; in production a description mismatch is a\n * second declarer and is refused too.\n */\nexport function defineChannelNamespace(\n  spec: ChannelNamespaceSpec,\n): ChannelNamespace {\n  if (!NAMESPACE_PATTERN.test(spec.namespace)) {\n    throw new Error(\n      `Invalid realtime channel namespace \"${spec.namespace}\": use lowercase ` +\n        `words joined by hyphens (e.g. \"note-editors\"). The topic string ends up ` +\n        `in logs and the diagnostics surface, so it must stay readable.`,\n    );\n  }\n  if (spec.description.trim().length === 0) {\n    throw new Error(\n      `Realtime channel namespace \"${spec.namespace}\" needs a description — the ` +\n        `diagnostics surface lists live channels by it, and an unnamed channel is ` +\n        `an unfixable bug report.`,\n    );\n  }\n\n  const store = registry().namespaces;\n  const existing = store.get(spec.namespace);\n  if (existing && !sameSpec(existing, spec)) {\n    if (!sameShape(existing, spec) || isProduction()) {\n      throw new ChannelNamespaceCollisionError(spec.namespace, existing, spec);\n    }\n    console.warn(\n      `[realtime] channel namespace \"${spec.namespace}\" re-declared with a new description ` +\n        `(\"${existing.description}\" -> \"${spec.description}\"); keeping the new one. ` +\n        `Expected after Fast Refresh or a package reinstall in development. If two features ` +\n        `declare this name, give one of them a distinct namespace.`,\n    );\n    store.set(spec.namespace, spec);\n  }\n  if (!existing) store.set(spec.namespace, spec);\n\n  return {\n    spec,\n    topic(parts) {\n      const values: string[] = [];\n      for (const name of spec.parts) {\n        const value = parts?.[name];\n        if (value === undefined || value === \"\") {\n          throw new Error(\n            `Realtime channel \"${spec.namespace}\" requires part \"${name}\". ` +\n              `A missing part would produce a topic shared by every caller that ` +\n              `also omitted it — everyone would receive everyone's events.`,\n          );\n        }\n        values.push(encodeTopicPart(value));\n      }\n      const root =\n        spec.foreignTopic !== undefined ? [spec.foreignTopic] : [TOPIC_PREFIX, spec.namespace];\n      return [...root, ...values].join(\":\");\n    },\n  };\n}\n\n/** Every namespace declared in this realm — the diagnostics surface reads it. */\nexport function listChannelNamespaces(): readonly ChannelNamespaceSpec[] {\n  return [...registry().namespaces.values()].sort((a, b) =>\n    a.namespace.localeCompare(b.namespace),\n  );\n}\n\n/**\n * Make a topic unique to THIS channel instance. **Postgres Changes only** — see\n * the file header. Calling it for a broadcast or presence channel splits the\n * room.\n *\n * Ported from `@ai-matrx/data`'s `uniqueChannelTopic` (40 call sites in\n * matrx-frontend). Channel NAMING belongs to this package, so this is its home;\n * `@ai-matrx/data` keeps its copy only until its consumers move here (see the\n * README § Migrating off hand-rolled channels).\n *\n * The counter lives in a globalThis slot, not a module `let`: with dual\n * ESM/CJS output an unsplit module can be instantiated twice, and two counters\n * both starting at 1 hand out the same \"unique\" topic twice.\n */\nexport function uniqueChannelTopic(base: string): string {\n  const store = registry();\n  store.sequence += 1;\n  return `${base}#${store.sequence}`;\n}\n\nfunction encodeTopicPart(value: string): string {\n  // `:` is the topic separator and `#` is the instance separator; anything\n  // carrying them would make a topic ambiguous to read and to parse.\n  return value.replace(/[:#\\s]/g, \"_\");\n}\n\n/** Test-only: forget every declaration so a suite can prove collision behavior. */\nexport function resetChannelRegistryForTests(): void {\n  const store = registry();\n  store.namespaces.clear();\n  store.sequence = 0;\n}\n","/**\n * THE DIAGNOSTICS SURFACE (R7) — realtime bugs must be observable, not guessed.\n *\n * Every realtime bug we have ever chased was diagnosed by adding console.logs\n * to production code and shipping them. This is that, permanently and for free:\n * a live inventory of every channel this realm has open, its subscription\n * state, what it has received, and — the numbers nobody ever had — how many\n * events were suppressed as echoes, dropped as duplicates, refused as stale,\n * and shed under queue pressure.\n *\n * It is DATA, not UI (D3: this package is headless). `useRealtimeDiagnostics`\n * in `/react` polls it; an admin surface renders it.\n */\n\nimport { clientSessionId } from \"./client-id\";\nimport { listChannelNamespaces, type ChannelNamespaceSpec } from \"./topics\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport type {\n  ConnectionHealth,\n  DiagnosticEvent,\n  DiagnosticsSink,\n  RealtimeStatus,\n} from \"./types\";\n\nexport interface ChannelDiagnostics {\n  topic: string;\n  namespace: string | undefined;\n  status: RealtimeStatus;\n  /** ms since epoch of the most recent successful SUBSCRIBED. */\n  subscribedAt: number | null;\n  /** How many times this channel has come back after dropping. */\n  reconnects: number;\n  /** Consecutive failed connect attempts right now. */\n  failedAttempts: number;\n  /** Raw events handed over by the socket. */\n  received: number;\n  /** Events that actually reached a consumer handler. */\n  delivered: number;\n  /** Dropped as this client's own echo. */\n  suppressedEcho: number;\n  /** Dropped as a duplicate of an event already delivered. */\n  deduped: number;\n  /** Dropped as strictly older than state we already hold. */\n  stale: number;\n  /** Currently queued, waiting for the handler slice. */\n  queueDepth: number;\n  /** Shed because the queue hit its bound (each shed triggers a backfill). */\n  queueDropped: number;\n  /** Consumer handlers that threw or rejected. */\n  handlerErrors: number;\n  /** Backfill/reconcile passes this channel has run. */\n  backfills: number;\n  lastBackfillAt: number | null;\n  lastEventAt: number | null;\n  /** Events received in the last 60 seconds. */\n  eventsPerMinute: number;\n}\n\nexport interface DiagnosticsSnapshot {\n  clientId: string;\n  health: ConnectionHealth;\n  channels: readonly ChannelDiagnostics[];\n  /** Every channel namespace declared in this realm, live or not. */\n  namespaces: readonly ChannelNamespaceSpec[];\n  capturedAt: number;\n}\n\nexport interface ChannelMetrics {\n  readonly topic: string;\n  status: RealtimeStatus;\n  namespace: string | undefined;\n  subscribedAt: number | null;\n  reconnects: number;\n  failedAttempts: number;\n  received: number;\n  delivered: number;\n  suppressedEcho: number;\n  deduped: number;\n  stale: number;\n  queueDepth: () => number;\n  queueDropped: () => number;\n  handlerErrors: number;\n  backfills: number;\n  lastBackfillAt: number | null;\n  lastEventAt: number | null;\n  recordEvent(): void;\n  snapshot(): ChannelDiagnostics;\n}\n\ninterface MetricsRegistry {\n  channels: Set<ChannelMetrics>;\n}\n\nfunction metricsRegistry(): MetricsRegistry {\n  return globalSlot<MetricsRegistry>(\"ai-matrx.realtime.metrics-registry\", () => ({\n    channels: new Set(),\n  }));\n}\n\nexport function createChannelMetrics(args: {\n  topic: string;\n  namespace: string | undefined;\n  queueDepth: () => number;\n  queueDropped: () => number;\n  now?: () => number;\n}): ChannelMetrics {\n  const now = args.now ?? Date.now;\n  const recent: number[] = [];\n\n  const metrics: ChannelMetrics = {\n    topic: args.topic,\n    namespace: args.namespace,\n    status: \"connecting\",\n    subscribedAt: null,\n    reconnects: 0,\n    failedAttempts: 0,\n    received: 0,\n    delivered: 0,\n    suppressedEcho: 0,\n    deduped: 0,\n    stale: 0,\n    queueDepth: args.queueDepth,\n    queueDropped: args.queueDropped,\n    handlerErrors: 0,\n    backfills: 0,\n    lastBackfillAt: null,\n    lastEventAt: null,\n    recordEvent(): void {\n      const at = now();\n      metrics.received += 1;\n      metrics.lastEventAt = at;\n      recent.push(at);\n      const cutoff = at - 60_000;\n      while (recent.length > 0 && (recent[0] ?? 0) < cutoff) recent.shift();\n    },\n    snapshot(): ChannelDiagnostics {\n      const cutoff = now() - 60_000;\n      while (recent.length > 0 && (recent[0] ?? 0) < cutoff) recent.shift();\n      return {\n        topic: metrics.topic,\n        namespace: metrics.namespace,\n        status: metrics.status,\n        subscribedAt: metrics.subscribedAt,\n        reconnects: metrics.reconnects,\n        failedAttempts: metrics.failedAttempts,\n        received: metrics.received,\n        delivered: metrics.delivered,\n        suppressedEcho: metrics.suppressedEcho,\n        deduped: metrics.deduped,\n        stale: metrics.stale,\n        queueDepth: metrics.queueDepth(),\n        queueDropped: metrics.queueDropped(),\n        handlerErrors: metrics.handlerErrors,\n        backfills: metrics.backfills,\n        lastBackfillAt: metrics.lastBackfillAt,\n        lastEventAt: metrics.lastEventAt,\n        eventsPerMinute: recent.length,\n      };\n    },\n  };\n\n  return metrics;\n}\n\n/**\n * THE CONSOLE HANDLE — the name the docstring below promises.\n *\n * `realtimeDiagnostics()` has always been documented as \"safe to call from\n * anywhere, including a devtools console\", and in every BUNDLED host that\n * sentence was false: the function existed only as a module binding inside a\n * Turbopack/Vite chunk, so on a live page nobody — operator, support engineer,\n * or verifying agent — could reach the counters at all. Measured 2026-09-11 on\n * matrx-frontend `/tasks`: no realtime global existed on `window`, and the\n * repo contained ZERO callers of `realtimeDiagnostics` or\n * `useRealtimeDiagnostics`, so the entire R7 surface was unobservable in the\n * product it was built for.\n *\n * So the package publishes the handle itself rather than asking every host to\n * remember to. It is installed from `registerChannelMetrics` — NOT at module\n * scope — because this package declares `sideEffects: false`, which lets a\n * bundler drop a top-level call; a channel registering its metrics is the\n * moment there is something to report and is impossible to tree-shake away.\n */\nexport const DIAGNOSTICS_GLOBAL = \"__matrxRealtimeDiagnostics\";\n\n/** Idempotent: re-registering channels must not redefine the handle. */\nexport function installDiagnosticsConsoleHandle(): void {\n  const host = globalThis as unknown as Record<string, unknown>;\n  if (typeof host[DIAGNOSTICS_GLOBAL] === \"function\") return;\n  Object.defineProperty(host, DIAGNOSTICS_GLOBAL, {\n    value: realtimeDiagnostics,\n    configurable: true,\n    writable: true,\n    enumerable: false,\n  });\n}\n\nexport function registerChannelMetrics(metrics: ChannelMetrics): () => void {\n  const registry = metricsRegistry();\n  registry.channels.add(metrics);\n  installDiagnosticsConsoleHandle();\n  return () => {\n    registry.channels.delete(metrics);\n  };\n}\n\n/** The live snapshot. Safe to call from anywhere, including a devtools console. */\nexport function realtimeDiagnostics(): DiagnosticsSnapshot {\n  const channels = [...metricsRegistry().channels]\n    .map((entry) => entry.snapshot())\n    .sort((a, b) => a.topic.localeCompare(b.topic));\n  return {\n    clientId: clientSessionId(),\n    health: healthOf(channels),\n    channels,\n    namespaces: listChannelNamespaces(),\n    capturedAt: Date.now(),\n  };\n}\n\nfunction healthOf(channels: readonly ChannelDiagnostics[]): ConnectionHealth {\n  if (channels.length === 0) return \"live\";\n  const connected = channels.filter((c) => c.status === \"connected\").length;\n  if (connected === channels.length) return \"live\";\n  if (connected === 0) return \"down\";\n  return \"degraded\";\n}\n\n/**\n * The default diagnostic sink. A host that injects nothing STILL sees every\n * background failure, with the remedy attached — silence is never the default.\n */\nexport function createConsoleDiagnostics(prefix = \"[realtime]\"): DiagnosticsSink {\n  return (event: DiagnosticEvent) => {\n    const line = `${prefix} ${event.code}${event.topic ? ` (${event.topic})` : \"\"}: ${event.message}${\n      event.remedy ? ` — ${event.remedy}` : \"\"\n    }`;\n    if (event.level === \"error\") console.error(line, event.detail ?? \"\");\n    else if (event.level === \"warn\") console.warn(line, event.detail ?? \"\");\n    else console.info(line, event.detail ?? \"\");\n  };\n}\n","/**\n * Idempotency: at-least-once in, exactly-once out (R3).\n *\n * Realtime delivery is at-least-once by nature — a resubscribe after a flap,\n * a channel that rejoined while the old one was still draining, or a consumer\n * that also broadcasts a row it INSERTed will all deliver the same logical\n * event twice. Every duplicate that reaches a reducer is a doubled message in\n * a thread, a doubled row in a list, or a doubled network call.\n *\n * The consumer supplies an event key (or lets the package use the envelope's\n * `eid`); this seen-set drops the repeat. It is time-bounded, not unbounded:\n * a session that runs for a day must not grow a set of every event it ever saw.\n */\n\nexport interface DedupeOptions {\n  /** How long a key stays \"seen\". Default 300000 (5 minutes). */\n  ttlMs?: number;\n  /**\n   * Hard cap on retained keys. Default 5000. On overflow the OLDEST keys are\n   * evicted — the newest events are the ones a duplicate is likely to shadow.\n   */\n  maxKeys?: number;\n  /** Injectable for tests. Default `Date.now`. */\n  now?: () => number;\n}\n\nexport interface Dedupe {\n  /** `true` if this key has NOT been seen (i.e. deliver it); records the key. */\n  admit(key: string): boolean;\n  /** Number of keys currently retained. */\n  size(): number;\n  /** Total keys rejected as duplicates since creation. */\n  rejected(): number;\n  clear(): void;\n}\n\nexport function createDedupe(options: DedupeOptions = {}): Dedupe {\n  const { ttlMs = 300_000, maxKeys = 5_000, now = Date.now } = options;\n  // Insertion-ordered by construction, which is what makes oldest-first\n  // eviction a plain `keys().next()` instead of a sort.\n  const seen = new Map<string, number>();\n  let rejected = 0;\n\n  const evictExpired = (at: number): void => {\n    for (const [key, expiresAt] of seen) {\n      if (expiresAt > at) break; // Map iterates in insertion order; all later keys are newer.\n      seen.delete(key);\n    }\n  };\n\n  return {\n    admit(key: string): boolean {\n      const at = now();\n      evictExpired(at);\n      const existing = seen.get(key);\n      if (existing !== undefined && existing > at) {\n        rejected += 1;\n        return false;\n      }\n      seen.delete(key);\n      seen.set(key, at + ttlMs);\n      while (seen.size > maxKeys) {\n        const oldest = seen.keys().next();\n        if (oldest.done === true) break;\n        seen.delete(oldest.value);\n      }\n      return true;\n    },\n    size(): number {\n      return seen.size;\n    },\n    rejected(): number {\n      return rejected;\n    },\n    clear(): void {\n      seen.clear();\n    },\n  };\n}\n","/**\n * THE ENVIRONMENT BRIDGE (R4) — tab sleep, app background, and network flaps.\n *\n * A realtime channel does not usually die loudly. It dies because the laptop\n * closed, because a background tab was throttled until the socket idled out,\n * or because Wi-Fi handed off to LTE. In every one of those the page comes back\n * looking fine and silently showing stale data — the worst possible failure,\n * because the user trusts it.\n *\n * So the manager needs to know two things about its host: is the surface\n * VISIBLE, and is the device ONLINE. This port supplies them. It ships a\n * working default for every platform we run on — the browser one here, the\n * React Native one in `../native/environment`, and an inert one for SSR and\n * node tests. A host never has to fill this in to get correct behavior.\n */\n\nexport type EnvironmentSignal = \"visible\" | \"hidden\" | \"online\" | \"offline\";\n\nexport interface EnvironmentBridge {\n  /** Name of the bridge, for the diagnostics surface. */\n  readonly name: string;\n  isVisible(): boolean;\n  isOnline(): boolean;\n  subscribe(listener: (signal: EnvironmentSignal) => void): () => void;\n}\n\n/**\n * The SSR / node / worker bridge: always visible, always online, no events.\n * It is inert by design and says so — importing this package during SSR must\n * never touch `document` or `window`.\n */\nexport function createInertEnvironment(): EnvironmentBridge {\n  return {\n    name: \"inert\",\n    isVisible: () => true,\n    isOnline: () => true,\n    subscribe: () => () => {},\n  };\n}\n\ninterface DocumentLike {\n  visibilityState?: string;\n  addEventListener(type: string, listener: () => void): void;\n  removeEventListener(type: string, listener: () => void): void;\n}\n\ninterface WindowLike {\n  addEventListener(type: string, listener: () => void): void;\n  removeEventListener(type: string, listener: () => void): void;\n}\n\n/** The browser bridge: `visibilitychange` + `online`/`offline`. */\nexport function createBrowserEnvironment(): EnvironmentBridge {\n  const doc = (globalThis as { document?: DocumentLike }).document;\n  const win = globalThis as unknown as WindowLike & { navigator?: { onLine?: boolean } };\n\n  return {\n    name: \"browser\",\n    isVisible: () => doc?.visibilityState !== \"hidden\",\n    isOnline: () => win.navigator?.onLine !== false,\n    subscribe(listener) {\n      const onVisibility = (): void => {\n        listener(doc?.visibilityState === \"hidden\" ? \"hidden\" : \"visible\");\n      };\n      const onOnline = (): void => listener(\"online\");\n      const onOffline = (): void => listener(\"offline\");\n      doc?.addEventListener(\"visibilitychange\", onVisibility);\n      win.addEventListener(\"online\", onOnline);\n      win.addEventListener(\"offline\", onOffline);\n      return () => {\n        doc?.removeEventListener(\"visibilitychange\", onVisibility);\n        win.removeEventListener(\"online\", onOnline);\n        win.removeEventListener(\"offline\", onOffline);\n      };\n    },\n  };\n}\n\n/**\n * The default: the browser bridge where a DOM exists, the inert one otherwise.\n * This is what makes `import`ing the package safe during SSR (contract point 4\n * of the independence contract).\n */\nexport function defaultEnvironment(): EnvironmentBridge {\n  const hasDom =\n    typeof (globalThis as { document?: unknown }).document === \"object\" &&\n    (globalThis as { document?: unknown }).document !== null &&\n    typeof (globalThis as { addEventListener?: unknown }).addEventListener === \"function\";\n  return hasDom ? createBrowserEnvironment() : createInertEnvironment();\n}\n","/**\n * THE WRITE LEDGER — echo suppression for Postgres Changes, and the named\n * solution to autosave↔realtime coexistence (R2 + R6).\n *\n * The problem, stated exactly (it is not the one people expect):\n *\n *   Supabase sends you your OWN writes. Postgres Changes has no \"not to self\"\n *   option — the echo of your UPDATE arrives 50–500ms AFTER your REST response\n *   already returned the fresh row. By the time the echo lands, any \"save in\n *   flight\" flag has been cleared. **Flag-only suppression therefore always\n *   misses the echo.**\n *\n * What an unsuppressed echo does (the notes freeze, 2026-07): the echo of save\n * N arrives while the user has typed toward N+1 → dirty-local ≠ server → a\n * false \"conflict\" → the conflict UI does O(n²) diff work every render → the\n * next save clears it → its echo re-arms it. Self-sustaining main-thread\n * saturation, from one missing comparison.\n *\n * And what OVER-suppression does (the notes phantom conflict, 2026-09-13):\n * every platform table carries an integer `version` that `_touch_row` bumps on\n * EVERY update — a desktop sync writing `file_path`, an ingest job stamping\n * metadata, a folder move. Such a write changes nothing the editor compares\n * (same content, same label), and it lands under the same actor. A ledger that\n * decides \"same content + same actor + our write pending ⇒ own echo\" swallows\n * it — and the client keeps the OLD version. Its next save's compare-and-swap\n * misses on a row whose text is identical to the client's own base, and the\n * user is shown a conflict dialog for an edit nobody made. The client had\n * suppressed the one event that carried the number it needed.\n *\n * So the ledger is REVISION-MONOTONIC FIRST, then timestamp, then flags:\n *\n *  R1. A revision strictly older than what we hold → `stale`.\n *  R2. A revision equal to the one an unsettled write of ours will produce,\n *      with matching content (or, with no content to compare, the same actor)\n *      → `own-echo` (this is the echo that beats the REST response). A newer\n *      revision carrying exactly the content of an unsettled write of ours is\n *      also our echo (our write landed above the number we expected, after a\n *      compare-and-swap rebase).\n *  R3. A revision equal to what we already settled, with matching content →\n *      `own-echo`; with different content → `remote` (a real divergence).\n *  R4. A revision NEWER than anything we hold or expect → `remote`, ALWAYS —\n *      same actor, same content, pending write or not. We did not produce that\n *      revision, or we have not learned it yet; either way the consumer must\n *      see it, because a version the client never adopts is the next false\n *      conflict. Delivering it is harmless: a consumer that compares content\n *      finds nothing to merge and simply advances its base.\n *\n * Only when neither side carries a revision does the older doctrine apply:\n *\n *  1. Strictly older `updatedAt` than what we already hold → `stale`. An event\n *     that is not newer carries zero information, and merging it REGRESSES the\n *     row's `updated_at`, which then fails the next save's optimistic lock and\n *     produces a sticky false conflict.\n *  2. Content we are known to have written and not yet settled → `own-echo`.\n *  3. Equal `updatedAt` → an echo only if the content fingerprint also matches.\n *     A same-millisecond collaborator write must still land.\n *  4. While one of OUR writes is unsettled, an event attributed to this client\n *     (by `requestId`, by client id, or by actor id) is an echo — but a\n *     DIVERGENT payload still comes through as `remote`, because that is a real\n *     conflict and hiding it is worse than showing it.\n *  5. If either timestamp is unparseable, fall through to the fingerprint and\n *     otherwise DELIVER. Degrade to delivering, never to silently dropping.\n *\n * `localWritePending` on the verdict is R6's answer: the consumer can tell an\n * optimistic local write still in flight from its echo from a genuine remote\n * write, using package APIs alone.\n */\n\nexport type EventOrigin = \"own-echo\" | \"remote\" | \"stale\";\n\nexport type OriginReason =\n  | \"request-id-match\"\n  | \"older-revision\"\n  | \"pending-write-same-revision\"\n  | \"equal-revision-same-content\"\n  | \"newer-revision\"\n  | \"older-timestamp\"\n  | \"equal-timestamp-same-content\"\n  | \"pending-write-same-actor\"\n  | \"pending-write-same-content\"\n  | \"no-local-record\"\n  | \"newer-timestamp\"\n  | \"divergent-content\";\n\nexport interface OriginVerdict {\n  origin: EventOrigin;\n  /** Stable machine reason — the diagnostics surface counts these. */\n  reason: OriginReason;\n  /** True while THIS client has an unsettled write for the row (autosave in flight). */\n  localWritePending: boolean;\n}\n\nexport interface LocalWrite {\n  /** Fully-qualified table, e.g. `workbench.notes`. */\n  table: string;\n  /** Row primary key as a string. */\n  id: string;\n  /** Content fingerprint of what we wrote — any stable string the consumer builds. */\n  fingerprint?: string | undefined;\n  /** The `updated_at` we expect the server to stamp, once known. */\n  updatedAt?: string | undefined;\n  /**\n   * The integer revision this write will PRODUCE (the row's `version` after\n   * the server's `_touch_row` bump — on a platform table that is the\n   * compare-and-swap `expectedVersion + 1`). Supplying it is what lets the\n   * ledger recognize the echo that beats the REST response by number instead\n   * of by content, and — the important half — refuse to call a HIGHER number\n   * an echo.\n   */\n  revision?: number | undefined;\n  /**\n   * The id the SERVER will stamp on the row's metadata for this write, when\n   * the write goes through a backend that stamps one (the `metadata.request_id`\n   * convention). An exact-match echo test, and the only fully reliable one.\n   */\n  requestId?: string | undefined;\n}\n\nexport interface WriteTicket {\n  readonly key: string;\n  readonly requestId: string | undefined;\n}\n\nexport interface ClassifyInput {\n  table: string;\n  id: string;\n  updatedAt?: string | null | undefined;\n  fingerprint?: string | undefined;\n  /** `updated_by` from the row, if the table carries the actor stamp. */\n  updatedBy?: string | null | undefined;\n  /** `metadata.request_id` from the row, if the backend stamps one. */\n  requestId?: string | null | undefined;\n  /** The row's integer `version`, if the table carries one. */\n  revision?: number | null | undefined;\n}\n\nexport interface WriteLedgerOptions {\n  /**\n   * The user id this client acts as, when known. Enables the `updated_by`\n   * echo test — the cheapest and most reliable one for tables carrying the\n   * platform `_stamp_actor` trigger.\n   */\n  actorId?: string | undefined;\n  /**\n   * How long an unsettled write stays on the ledger before it is assumed lost.\n   * Default 30000. Without a TTL a failed request holds suppression open\n   * forever and the row stops updating from anyone.\n   */\n  ttlMs?: number;\n  /** Injectable for tests. Default `Date.now`. */\n  now?: () => number;\n}\n\nexport interface SettleResult {\n  updatedAt?: string | undefined;\n  fingerprint?: string | undefined;\n  /** The revision the server actually stamped (the REST response's `version`). */\n  revision?: number | undefined;\n}\n\nexport interface ObservedRecord {\n  table: string;\n  id: string;\n  updatedAt?: string | null | undefined;\n  fingerprint?: string | undefined;\n  /** The row's `version` as read. */\n  revision?: number | null | undefined;\n}\n\nexport interface WriteLedger {\n  /** Register a write BEFORE the optimistic reducer and the REST call. */\n  begin(write: LocalWrite): WriteTicket;\n  /** Record what the server actually returned. Keeps the monotonic floor honest. */\n  settle(ticket: WriteTicket, result?: SettleResult): void;\n  /** The request failed; stop suppressing for it. */\n  abandon(ticket: WriteTicket): void;\n  /** Record server state we learned some other way (a list fetch, a backfill). */\n  observe(record: ObservedRecord): void;\n  /** Classify one incoming payload. */\n  classify(input: ClassifyInput): OriginVerdict;\n  /** Is one of OUR writes still unsettled for this row? */\n  isPending(table: string, id: string): boolean;\n  /** The highest revision the ledger holds for a row, if it holds one. Diagnostics and tests. */\n  heldRevision(table: string, id: string): number | undefined;\n  /** Rows currently tracked. Diagnostics. */\n  size(): number;\n  clear(): void;\n}\n\ninterface Entry {\n  updatedAt: string | undefined;\n  fingerprint: string | undefined;\n  /** Highest revision settled or observed for the row. */\n  revision: number | undefined;\n  /** Unsettled request ids for this row, with their expiry. */\n  pending: Map<string, number>;\n  /** Fingerprints of unsettled writes, for the equal/unknown-timestamp path. */\n  pendingFingerprints: Set<string>;\n  /** Revision each unsettled write will produce, by request id, when the consumer knows it.\n   * `settled` flips once the server told us the real number: a settled write can\n   * never claim a HIGHER revision as its echo (that higher number is someone else's). */\n  pendingRevisions: Map<string, { revision: number; fingerprint: string | undefined; settled: boolean }>;\n  /** Fingerprints of writes still awaiting their REST response — the content\n   * `begin` was told, keyed by request id so settle/abandon/expiry release\n   * exactly what begin added (the settled content can differ: a keystroke\n   * mid-save changes what the host fingerprints at settle time). */\n  unsettledByRequest: Map<string, string>;\n  touchedAt: number;\n}\n\nfunction rowKey(table: string, id: string): string {\n  return `${table}\\0${id}`;\n}\n\nfunction parseTime(value: string | null | undefined): number | null {\n  if (value === null || value === undefined || value === \"\") return null;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? null : parsed;\n}\n\n/** A revision is meaningful only as a finite integer; anything else is \"absent\". */\nexport function canonicalRevision(value: number | null | undefined): number | undefined {\n  return typeof value === \"number\" && Number.isSafeInteger(value) ? value : undefined;\n}\n\nlet ticketSequence = 0;\n\n/** A settled row entry idle this long is forgotten (it is only a floor). */\nconst IDLE_ENTRY_MS = 60 * 60 * 1000;\n\nexport function createWriteLedger(options: WriteLedgerOptions = {}): WriteLedger {\n  const { actorId, ttlMs = 30_000, now = Date.now } = options;\n  const rows = new Map<string, Entry>();\n\n  const entryFor = (table: string, id: string): Entry => {\n    const key = rowKey(table, id);\n    const existing = rows.get(key);\n    if (existing) return existing;\n    const created: Entry = {\n      updatedAt: undefined,\n      fingerprint: undefined,\n      revision: undefined,\n      pending: new Map(),\n      pendingFingerprints: new Set(),\n      pendingRevisions: new Map(),\n      unsettledByRequest: new Map(),\n      touchedAt: now(),\n    };\n    rows.set(key, created);\n    return created;\n  };\n\n  const sweep = (): void => {\n    const at = now();\n    for (const [key, entry] of rows) {\n      for (const [requestId, expiresAt] of entry.pending) {\n        if (expiresAt <= at) {\n          entry.pending.delete(requestId);\n          entry.pendingRevisions.delete(requestId);\n          entry.unsettledByRequest.delete(requestId);\n        }\n      }\n      // An entry with nothing pending is only a monotonic floor. Forgetting a\n      // floor after a long idle costs at most one delivered event (the safe\n      // direction); keeping every row ever saved for the life of the tab costs\n      // memory without bound.\n      if (\n        entry.pending.size === 0 &&\n        at - entry.touchedAt > (entry.revision === undefined && entry.updatedAt === undefined ? ttlMs : IDLE_ENTRY_MS)\n      ) {\n        rows.delete(key);\n      }\n    }\n  };\n\n  const advance = (\n    entry: Entry,\n    updatedAt: string | undefined,\n    fingerprint: string | undefined,\n    revision: number | undefined,\n  ): void => {\n    // The revision is authoritative when both sides carry one: a higher number\n    // IS newer, whatever the clocks say.\n    if (revision !== undefined && (entry.revision === undefined || revision >= entry.revision)) {\n      entry.revision = revision;\n      if (fingerprint !== undefined) entry.fingerprint = fingerprint;\n      const incomingTime = parseTime(updatedAt);\n      const heldTime = parseTime(entry.updatedAt);\n      if (incomingTime !== null && (heldTime === null || incomingTime >= heldTime)) {\n        entry.updatedAt = updatedAt;\n      }\n      entry.touchedAt = now();\n      return;\n    }\n    if (revision !== undefined) {\n      // Older revision than held: nothing to learn.\n      entry.touchedAt = now();\n      return;\n    }\n    const incoming = parseTime(updatedAt);\n    const held = parseTime(entry.updatedAt);\n    if (incoming !== null && (held === null || incoming >= held)) {\n      entry.updatedAt = updatedAt;\n      entry.fingerprint = fingerprint ?? entry.fingerprint;\n    } else if (incoming === null && fingerprint !== undefined) {\n      entry.fingerprint = fingerprint;\n    }\n    entry.touchedAt = now();\n  };\n\n  return {\n    begin(write: LocalWrite): WriteTicket {\n      sweep();\n      const entry = entryFor(write.table, write.id);\n      ticketSequence += 1;\n      const requestId = write.requestId ?? `mxrt-w${ticketSequence}`;\n      entry.pending.set(requestId, now() + ttlMs);\n      if (write.fingerprint !== undefined) {\n        entry.pendingFingerprints.add(write.fingerprint);\n        entry.unsettledByRequest.set(requestId, write.fingerprint);\n      }\n      const revision = canonicalRevision(write.revision);\n      if (revision !== undefined) {\n        entry.pendingRevisions.set(requestId, { revision, fingerprint: write.fingerprint, settled: false });\n      }\n      if (write.updatedAt !== undefined) advance(entry, write.updatedAt, write.fingerprint, undefined);\n      entry.touchedAt = now();\n      return { key: rowKey(write.table, write.id), requestId: write.requestId ?? requestId };\n    },\n\n    settle(ticket, result): void {\n      const entry = rows.get(ticket.key);\n      if (!entry) return;\n      if (ticket.requestId !== undefined) {\n        // The request id STAYS suppressive for the rest of its TTL on purpose:\n        // the echo arrives AFTER the REST response, so clearing it here is the\n        // flag-only mistake this ledger exists to replace.\n        entry.pending.set(ticket.requestId, now() + ttlMs);\n        const prior = entry.pendingRevisions.get(ticket.requestId);\n        const revision = canonicalRevision(result?.revision);\n        if (revision !== undefined) {\n          // The server told us which revision this write produced. That number,\n          // with this content, is the echo — and nothing above it is.\n          entry.pendingRevisions.set(ticket.requestId, {\n            revision,\n            fingerprint: result?.fingerprint ?? prior?.fingerprint,\n            settled: true,\n          });\n        } else if (prior) {\n          entry.pendingRevisions.set(ticket.requestId, { ...prior, settled: true });\n        }\n        // Release what THIS write's begin added — never the settled content,\n        // which a mid-save keystroke can make a different string.\n        entry.unsettledByRequest.delete(ticket.requestId);\n      }\n      advance(entry, result?.updatedAt, result?.fingerprint, canonicalRevision(result?.revision));\n    },\n\n    abandon(ticket): void {\n      const entry = rows.get(ticket.key);\n      if (!entry) return;\n      if (ticket.requestId !== undefined) {\n        entry.pending.delete(ticket.requestId);\n        entry.pendingRevisions.delete(ticket.requestId);\n        entry.unsettledByRequest.delete(ticket.requestId);\n      }\n    },\n\n    observe(record): void {\n      sweep();\n      const entry = entryFor(record.table, record.id);\n      advance(entry, record.updatedAt ?? undefined, record.fingerprint, canonicalRevision(record.revision));\n    },\n\n    classify(input: ClassifyInput): OriginVerdict {\n      sweep();\n      const entry = rows.get(rowKey(input.table, input.id));\n      const pending = entry !== undefined && entry.pending.size > 0;\n\n      // 0. Exact match: the backend stamped OUR request id on the row.\n      if (\n        input.requestId !== null &&\n        input.requestId !== undefined &&\n        entry?.pending.has(input.requestId) === true\n      ) {\n        return { origin: \"own-echo\", reason: \"request-id-match\", localWritePending: pending };\n      }\n\n      if (!entry) {\n        return { origin: \"remote\", reason: \"no-local-record\", localWritePending: false };\n      }\n\n      // ── The revision lane: authoritative whenever the event carries one ──\n      const incomingRevision = canonicalRevision(input.revision);\n      if (incomingRevision !== undefined) {\n        // R1. Older than what we hold: zero information.\n        if (entry.revision !== undefined && incomingRevision < entry.revision) {\n          return { origin: \"stale\", reason: \"older-revision\", localWritePending: pending };\n        }\n\n        // R2. Exactly the revision one of OUR unsettled writes produces, with\n        //     the content we wrote (or nothing to compare): the echo that beat\n        //     the REST response.\n        //     The number alone is NOT enough: a collaborator can win the race\n        //     for exactly the revision we expected. Content must agree, or —\n        //     when the consumer gave us no content to compare — the actor must.\n        const actorAgrees =\n          actorId !== undefined &&\n          input.updatedBy !== null &&\n          input.updatedBy !== undefined &&\n          input.updatedBy === actorId;\n        let expectedNumberSeen = false;\n        for (const expected of entry.pendingRevisions.values()) {\n          if (expected.revision !== incomingRevision) continue;\n          expectedNumberSeen = true;\n          const contentComparable =\n            input.fingerprint !== undefined && expected.fingerprint !== undefined;\n          if (\n            (contentComparable && expected.fingerprint === input.fingerprint) ||\n            (!contentComparable && actorAgrees)\n          ) {\n            advance(entry, input.updatedAt ?? undefined, input.fingerprint, incomingRevision);\n            return {\n              origin: \"own-echo\",\n              reason: \"pending-write-same-revision\",\n              localWritePending: true,\n            };\n          }\n        }\n\n        // R3. The revision we already settled or observed.\n        if (entry.revision !== undefined && incomingRevision === entry.revision) {\n          if (\n            input.fingerprint === undefined ||\n            entry.fingerprint === undefined ||\n            input.fingerprint === entry.fingerprint\n          ) {\n            return {\n              origin: \"own-echo\",\n              reason: \"equal-revision-same-content\",\n              localWritePending: pending,\n            };\n          }\n          return { origin: \"remote\", reason: \"divergent-content\", localWritePending: pending };\n        }\n\n        // R2b. Newer than we hold, but carrying EXACTLY the content of a write\n        //      of ours whose REST response has NOT come back yet: our write\n        //      landed at a number above the one `begin` expected (a\n        //      compare-and-swap rebase produced +2, not +1). That is our echo;\n        //      `settle` will record the real number. A write the server has\n        //      already answered can never claim a higher number — that higher\n        //      number is someone else's write, however familiar its content\n        //      (the matrx-local bump was exactly that shape).\n        let unsettledMatch = false;\n        if (input.fingerprint !== undefined) {\n          for (const fp of entry.unsettledByRequest.values()) {\n            if (fp === input.fingerprint) { unsettledMatch = true; break; }\n          }\n        }\n        if (\n          unsettledMatch &&\n          (entry.revision === undefined || incomingRevision > entry.revision)\n        ) {\n          advance(entry, input.updatedAt ?? undefined, input.fingerprint, incomingRevision);\n          return {\n            origin: \"own-echo\",\n            reason: \"pending-write-same-content\",\n            localWritePending: true,\n          };\n        }\n\n        // R4. NEWER than anything we hold or expect. Never an echo — we did not\n        //     make this revision, or we have not learned it. Same actor, same\n        //     content (that we did NOT write), pending write or not: DELIVER.\n        //     The consumer that swallows this number is the consumer that\n        //     shows the next phantom conflict.\n        //     The one nuance: the number one of our writes EXPECTED, carrying\n        //     content we did not write, is a collaborator who won the race for\n        //     that revision — our CAS will miss and say so. Still remote.\n        advance(entry, input.updatedAt ?? undefined, input.fingerprint, incomingRevision);\n        return {\n          origin: \"remote\",\n          reason: expectedNumberSeen ? \"divergent-content\" : \"newer-revision\",\n          localWritePending: pending,\n        };\n      }\n\n      const incoming = parseTime(input.updatedAt);\n      const held = parseTime(entry.updatedAt);\n\n      // 1. Strictly older than what we hold: zero information, and merging it\n      //    regresses updated_at into a sticky false conflict.\n      if (incoming !== null && held !== null && incoming < held) {\n        return { origin: \"stale\", reason: \"older-timestamp\", localWritePending: pending };\n      }\n\n      // 2. Content we are KNOWN to have written and not yet settled. This\n      //    outranks the timestamp tests: an exact match against a write still\n      //    in flight is the strongest own-echo signal there is, and it holds\n      //    even when the server stamped a timestamp we never saw.\n      if (\n        pending &&\n        input.fingerprint !== undefined &&\n        entry.pendingFingerprints.has(input.fingerprint)\n      ) {\n        return {\n          origin: \"own-echo\",\n          reason: \"pending-write-same-content\",\n          localWritePending: true,\n        };\n      }\n\n      // 3. Equal timestamp is an echo ONLY if the content also matches.\n      if (\n        incoming !== null &&\n        held !== null &&\n        incoming === held &&\n        input.fingerprint !== undefined &&\n        entry.fingerprint !== undefined\n      ) {\n        if (input.fingerprint === entry.fingerprint) {\n          return {\n            origin: \"own-echo\",\n            reason: \"equal-timestamp-same-content\",\n            localWritePending: pending,\n          };\n        }\n        return { origin: \"remote\", reason: \"divergent-content\", localWritePending: pending };\n      }\n\n      // 4. While OUR write is unsettled: same actor.\n      if (pending) {\n        if (\n          actorId !== undefined &&\n          input.updatedBy !== null &&\n          input.updatedBy !== undefined &&\n          input.updatedBy === actorId &&\n          // A divergent payload from the same actor is another tab of the same\n          // user — a real remote write. Only suppress when content agrees, or\n          // when the consumer gave us nothing to compare.\n          (input.fingerprint === undefined ||\n            entry.pendingFingerprints.size === 0 ||\n            entry.pendingFingerprints.has(input.fingerprint))\n        ) {\n          return {\n            origin: \"own-echo\",\n            reason: \"pending-write-same-actor\",\n            localWritePending: true,\n          };\n        }\n      }\n\n      // 5. Newer, or unparseable, or divergent — DELIVER.\n      advance(entry, input.updatedAt ?? undefined, input.fingerprint, incomingRevision);\n      return {\n        origin: \"remote\",\n        reason: incoming !== null && held !== null ? \"newer-timestamp\" : \"divergent-content\",\n        localWritePending: pending,\n      };\n    },\n\n    isPending(table: string, id: string): boolean {\n      sweep();\n      const entry = rows.get(rowKey(table, id));\n      return entry !== undefined && entry.pending.size > 0;\n    },\n\n    heldRevision(table: string, id: string): number | undefined {\n      return rows.get(rowKey(table, id))?.revision;\n    },\n\n    size(): number {\n      return rows.size;\n    },\n\n    clear(): void {\n      rows.clear();\n    },\n  };\n}\n","/**\n * PRESENCE (R5) — who is here, without ghosts.\n *\n * Supabase presence removes a member when its socket closes cleanly. The\n * interesting cases are the ones where it does NOT close cleanly: a closed\n * laptop, a killed tab, a phone that lost signal in a tunnel. The entry then\n * lingers until the server's own timeout, and a room shows a participant who\n * left twenty minutes ago. Worse, a rejoin during that window shows the same\n * person twice.\n *\n * Two mechanisms fix that here, and neither depends on clocks agreeing between\n * devices (they never do):\n *\n *  - **Heartbeat.** This client re-`track()`s itself every `heartbeatMs`. That\n *    is a positive liveness signal, not an absence of one.\n *  - **Local-clock expiry.** Every member's `lastSeenAt` is stamped with OUR\n *    clock when WE received their state. A member we have not heard from in\n *    `expireAfterMs` is pruned from the roster we publish, whatever the server\n *    still holds. A ghost therefore cannot outlive one expiry window.\n *\n * Join/leave bursts are debounced: a rejoining channel emits a full `sync` plus\n * a `join` per member, and a roster that re-renders once per member is the\n * O(N²) selector storm this package exists to prevent.\n */\n\nimport { clientSessionId } from \"./client-id\";\nimport type { PresenceStateLike, RealtimeChannelLike } from \"./types\";\n\n/** Reserved keys the package stamps into every tracked state object. */\nconst CLIENT_FIELD = \"__mx_cid\";\nconst STAMP_FIELD = \"__mx_ts\";\n\nexport interface PresenceMember<TState> {\n  /** The presence key this member tracked under. */\n  key: string;\n  /** The member's client session id (distinct per tab, even for one user). */\n  clientId: string;\n  state: TState;\n  /** OUR clock, first time we saw this member. */\n  joinedAt: number;\n  /** OUR clock, most recent time we saw this member's state. */\n  lastSeenAt: number;\n  isSelf: boolean;\n}\n\nexport interface PresenceOptions<TState extends Record<string, unknown>> {\n  /** The presence key for THIS client. Defaults to the client session id. */\n  key?: string;\n  /** What this client publishes about itself. */\n  state: TState;\n  /** How often this client re-announces itself. Default 15000. */\n  heartbeatMs?: number;\n  /** A member unheard-from this long is pruned locally. Default 45000. */\n  expireAfterMs?: number;\n  /** Coalesce sync/join/leave bursts into one emission. Default 150. */\n  debounceMs?: number;\n  /**\n   * Whether THIS holder announces itself. Default true.\n   *\n   * One channel carries ONE presence entry per key, so a topic must have\n   * exactly one publisher: a second holder that also tracks overwrites the\n   * first's state on every heartbeat, and the platform's own typing indicator\n   * is what that breaks. A surface that only wants to READ the roster (a\n   * typists label, an online-dot list) sets `publish: false` — it still sees\n   * everyone, it just stops fighting over the entry.\n   */\n  publish?: boolean;\n  onChange?: (members: readonly PresenceMember<TState>[]) => void;\n}\n\nexport interface PresenceRoom<TState extends Record<string, unknown>> {\n  readonly key: string;\n  members(): readonly PresenceMember<TState>[];\n  /** Publish new state for this client. Merged, not replaced. */\n  update(state: Partial<TState>): void;\n  subscribe(listener: (members: readonly PresenceMember<TState>[]) => void): () => void;\n  dispose(): void;\n}\n\nexport interface PresenceRuntime<TState extends Record<string, unknown>>\n  extends PresenceRoom<TState> {\n  /** Called by the manager once the channel is SUBSCRIBED. */\n  onSubscribed(channel: RealtimeChannelLike): void;\n  /** Called by the manager when the channel drops (roster is no longer trustworthy). */\n  onDisconnected(): void;\n  /** Called by the manager whenever the channel reports a presence event. */\n  onPresenceEvent(channel: RealtimeChannelLike): void;\n}\n\nexport interface PresenceTimers {\n  setInterval: (run: () => void, ms: number) => unknown;\n  clearInterval: (handle: unknown) => void;\n  setTimeout: (run: () => void, ms: number) => unknown;\n  clearTimeout: (handle: unknown) => void;\n  now: () => number;\n}\n\nconst defaultTimers: PresenceTimers = {\n  setInterval: (run, ms) => setInterval(run, ms),\n  clearInterval: (handle) => clearInterval(handle as ReturnType<typeof setInterval>),\n  setTimeout: (run, ms) => setTimeout(run, ms),\n  clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n  now: () => Date.now(),\n};\n\nexport function createPresenceRuntime<TState extends Record<string, unknown>>(\n  options: PresenceOptions<TState>,\n  timers: PresenceTimers = defaultTimers,\n): PresenceRuntime<TState> {\n  const {\n    key = clientSessionId(),\n    heartbeatMs = 15_000,\n    expireAfterMs = 45_000,\n    debounceMs = 150,\n    publish = true,\n    onChange,\n  } = options;\n\n  const selfClientId = clientSessionId();\n  let state = { ...options.state };\n  let channel: RealtimeChannelLike | null = null;\n  let heartbeat: unknown = null;\n  let sweeper: unknown = null;\n  let emitTimer: unknown = null;\n  let disposed = false;\n\n  const seen = new Map<string, { joinedAt: number; lastSeenAt: number }>();\n  let roster: PresenceMember<TState>[] = [];\n  const listeners = new Set<(members: readonly PresenceMember<TState>[]) => void>();\n  if (onChange) listeners.add(onChange);\n\n  const track = (): void => {\n    if (!channel || disposed || !publish) return;\n    channel.track({\n      ...state,\n      [CLIENT_FIELD]: selfClientId,\n      [STAMP_FIELD]: timers.now(),\n    });\n  };\n\n  const emit = (): void => {\n    const snapshot = roster;\n    for (const listener of listeners) listener(snapshot);\n  };\n\n  const scheduleEmit = (): void => {\n    if (disposed) return;\n    if (emitTimer !== null) return;\n    emitTimer = timers.setTimeout(() => {\n      emitTimer = null;\n      emit();\n    }, debounceMs);\n  };\n\n  const rebuild = (presence: PresenceStateLike): void => {\n    const at = timers.now();\n    const next: PresenceMember<TState>[] = [];\n    const present = new Set<string>();\n\n    for (const [memberKey, entries] of Object.entries(presence)) {\n      const raw = entries[entries.length - 1];\n      if (raw === null || typeof raw !== \"object\") continue;\n      const record = raw as Record<string, unknown>;\n      const clientId = typeof record[CLIENT_FIELD] === \"string\" ? (record[CLIENT_FIELD] as string) : memberKey;\n      present.add(memberKey);\n      const previous = seen.get(memberKey);\n      const joinedAt = previous?.joinedAt ?? at;\n      seen.set(memberKey, { joinedAt, lastSeenAt: at });\n      const publicState: Record<string, unknown> = { ...record };\n      delete publicState[CLIENT_FIELD];\n      delete publicState[STAMP_FIELD];\n      delete publicState[\"presence_ref\"];\n      next.push({\n        key: memberKey,\n        clientId,\n        state: publicState as TState,\n        joinedAt,\n        lastSeenAt: at,\n        isSelf: clientId === selfClientId,\n      });\n    }\n\n    for (const memberKey of [...seen.keys()]) {\n      if (!present.has(memberKey)) seen.delete(memberKey);\n    }\n\n    roster = next.sort((a, b) => a.joinedAt - b.joinedAt || a.key.localeCompare(b.key));\n    scheduleEmit();\n  };\n\n  const sweepGhosts = (): void => {\n    if (disposed) return;\n    const cutoff = timers.now() - expireAfterMs;\n    const kept = roster.filter((member) => member.isSelf || member.lastSeenAt >= cutoff);\n    if (kept.length !== roster.length) {\n      for (const member of roster) {\n        if (!kept.includes(member)) seen.delete(member.key);\n      }\n      roster = kept;\n      scheduleEmit();\n    }\n  };\n\n  return {\n    key,\n    members: () => roster,\n    update(partial: Partial<TState>): void {\n      state = { ...state, ...partial };\n      track();\n    },\n    subscribe(listener): () => void {\n      listeners.add(listener);\n      listener(roster);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n    onSubscribed(next: RealtimeChannelLike): void {\n      channel = next;\n      track();\n      if (heartbeat === null && publish) {\n        heartbeat = timers.setInterval(track, heartbeatMs);\n      }\n      if (sweeper === null) {\n        // Sweep at half the expiry window so a ghost is never visible for more\n        // than 1.5 windows.\n        sweeper = timers.setInterval(sweepGhosts, Math.max(1_000, Math.floor(expireAfterMs / 2)));\n      }\n      rebuild(next.presenceState());\n    },\n    onPresenceEvent(next: RealtimeChannelLike): void {\n      channel = next;\n      rebuild(next.presenceState());\n    },\n    onDisconnected(): void {\n      // A dropped channel means the roster is unknowable, not empty-with-us.\n      // Keep only ourselves so the UI degrades honestly instead of claiming a\n      // room emptied out.\n      channel = null;\n      roster = roster.filter((member) => member.isSelf);\n      scheduleEmit();\n    },\n    dispose(): void {\n      disposed = true;\n      if (heartbeat !== null) timers.clearInterval(heartbeat);\n      if (sweeper !== null) timers.clearInterval(sweeper);\n      if (emitTimer !== null) timers.clearTimeout(emitTimer);\n      heartbeat = null;\n      sweeper = null;\n      emitTimer = null;\n      try {\n        if (publish) channel?.untrack();\n      } catch {\n        // A channel already torn down rejects untrack; the server drops the\n        // presence entry on socket close either way.\n      }\n      channel = null;\n      listeners.clear();\n      seen.clear();\n      roster = [];\n    },\n  };\n}\n","/**\n * THE DECOUPLED ORDERED HANDLER QUEUE (R3) — the freeze-class killer.\n *\n * A Supabase channel invokes your handler ON THE SOCKET'S CALLBACK PATH. If\n * that handler does real work — a Redux dispatch that re-runs every sorted\n * selector, an O(content) diff, an RPC — then a burst of events executes that\n * work back-to-back with no yield, the main thread never returns to the\n * browser, and the tab freezes. Every historical Matrx browser lockup is a\n * variation of this (a background tab waking with 500 queued events is the\n * classic trigger).\n *\n * This queue puts a scheduler between the socket and the consumer:\n *\n *  - Handlers run OFF the socket callback (the socket loop returns immediately).\n *  - Order is preserved PER CHANNEL — one queue per channel, strictly FIFO,\n *    and an async handler is awaited before the next event starts, so a\n *    consumer never sees event N+1 applied before event N finished.\n *  - The drain YIELDS to the event loop every `sliceMs`, so even a 10,000-event\n *    burst keeps the tab interactive instead of trading correctness for a\n *    frozen window.\n *  - Depth is BOUNDED. An unbounded queue does not prevent the freeze, it just\n *    moves it and adds an out-of-memory ending. On overflow the queue drops\n *    the oldest work, SAYS SO through `onOverflow`, and the manager turns that\n *    into a backfill — reconciling from the database is exactly the right\n *    recovery for \"we could not keep up with the stream\".\n */\n\nexport type QueueTask = () => void | Promise<void>;\n\nexport interface EventQueueOptions {\n  /** Max pending tasks before the oldest are dropped. Default 500. */\n  maxDepth?: number;\n  /** Max ms of uninterrupted draining before yielding to the event loop. Default 8. */\n  sliceMs?: number;\n  /** Called with the number of tasks dropped. The manager triggers a backfill. */\n  onOverflow?: (dropped: number) => void;\n  /** Called when a consumer handler throws or rejects. Never swallowed. */\n  onError?: (error: unknown) => void;\n  /** Injectable for tests. Default `Date.now`. */\n  now?: () => number;\n  /** Injectable for tests. Default `setTimeout(fn, 0)`. */\n  schedule?: (run: () => void) => void;\n}\n\nexport interface EventQueue {\n  push(task: QueueTask): void;\n  depth(): number;\n  /** Total tasks dropped by overflow since creation. */\n  dropped(): number;\n  /** Total tasks completed since creation. */\n  processed(): number;\n  /** Resolves when the queue is empty. Tests and teardown use it. */\n  idle(): Promise<void>;\n  /** Stop draining and discard pending work (channel closed). */\n  dispose(): void;\n}\n\nexport function createEventQueue(options: EventQueueOptions = {}): EventQueue {\n  const {\n    maxDepth = 500,\n    sliceMs = 8,\n    onOverflow,\n    onError,\n    now = Date.now,\n    schedule = (run: () => void): void => {\n      setTimeout(run, 0);\n    },\n  } = options;\n\n  const pending: QueueTask[] = [];\n  const idleWaiters: Array<() => void> = [];\n  let draining = false;\n  let disposed = false;\n  let dropped = 0;\n  let processed = 0;\n\n  const releaseIdle = (): void => {\n    while (idleWaiters.length > 0) idleWaiters.pop()?.();\n  };\n\n  const drain = async (): Promise<void> => {\n    const sliceStart = now();\n    while (pending.length > 0) {\n      if (disposed) break;\n      const task = pending.shift();\n      if (task === undefined) break;\n      try {\n        await task();\n      } catch (error) {\n        // A consumer handler that throws must never kill the subscription and\n        // must never vanish. The queue keeps draining; the sink hears about it.\n        onError?.(error);\n      }\n      processed += 1;\n      if (now() - sliceStart >= sliceMs && pending.length > 0) {\n        // Hand the thread back. This is the difference between \"busy\" and\n        // \"frozen\" — without it a burst monopolizes the main thread.\n        schedule(() => {\n          void drain();\n        });\n        return;\n      }\n    }\n    draining = false;\n    if (pending.length === 0) releaseIdle();\n  };\n\n  return {\n    push(task: QueueTask): void {\n      if (disposed) return;\n      pending.push(task);\n      if (pending.length > maxDepth) {\n        const excess = pending.length - maxDepth;\n        pending.splice(0, excess);\n        dropped += excess;\n        onOverflow?.(excess);\n      }\n      if (!draining) {\n        draining = true;\n        schedule(() => {\n          void drain();\n        });\n      }\n    },\n    depth(): number {\n      return pending.length;\n    },\n    dropped(): number {\n      return dropped;\n    },\n    processed(): number {\n      return processed;\n    },\n    idle(): Promise<void> {\n      if (pending.length === 0 && !draining) return Promise.resolve();\n      return new Promise<void>((resolve) => {\n        idleWaiters.push(resolve);\n      });\n    },\n    dispose(): void {\n      disposed = true;\n      pending.length = 0;\n      releaseIdle();\n    },\n  };\n}\n","/**\n * SHARED ROOMS — because for Broadcast and Presence, THE TOPIC *IS* THE ROOM.\n *\n * `uniqueChannelTopic()` (see `topics.ts`) makes every connection attempt's\n * topic unique, and for **Postgres Changes that is free**: the server pushes on\n * the declared filter, so the topic is genuinely nothing but a client-side\n * connection key. For **Broadcast and Presence it is the opposite of free** —\n * the topic is the routing key the server fans messages out on. Two clients\n * that both declared `mx:room:1` land in `mx:room:1#1` and `mx:room:1#2` and\n * never see each other, and so do two `open()` calls inside ONE client. That\n * defect shipped in 0.1.x and was found by `@ai-matrx/messaging`, the first real\n * consumer of that surface.\n *\n * The cure has two halves, and BOTH are required:\n *\n *  1. **The declared topic goes on the wire verbatim** for any channel carrying\n *     broadcast or presence, so two browsers land in the same room.\n *  2. **One underlying channel per declared topic, ref-counted**, so a second\n *     `open()` in the same client attaches its bindings to that same channel\n *     rather than asking the Supabase client for a topic it has already\n *     registered (which hands back the live channel and then throws, or binds\n *     nothing). The channel is removed only when the LAST holder lets go.\n *\n * Half 2 is what makes half 1 safe. It also replaces what `uniqueChannelTopic`\n * was doing for these channels: the React-19 double-invoked effect (mount →\n * cleanup → mount, all in one tick) is handled by DEFERRING the real teardown\n * by one turn, so the re-run re-attaches to the still-live channel instead of\n * racing an async `removeChannel`. Pure Postgres-Changes channels keep the\n * unique instance topic — nothing here changes for them.\n */\n\nimport type {\n  DiagnosticsSink,\n  PostgresChangeFilter,\n  PostgresChangePayload,\n  PresenceStateLike,\n  RealtimeChannelLike,\n  RealtimeClientLike,\n} from \"./types\";\n\ntype BindingType = \"postgres_changes\" | \"broadcast\" | \"presence\";\n\ninterface Binding {\n  type: BindingType;\n  filter: unknown;\n  callback: (payload: never) => void;\n  /** True once this binding sits on the room's CURRENT real channel. */\n  attached: boolean;\n}\n\ninterface Room {\n  topic: string;\n  params: unknown;\n  /** Non-null only between `join()` and teardown. `subscribe()` has been called on it. */\n  real: RealtimeChannelLike | null;\n  /** True once the real channel reported SUBSCRIBED. */\n  joined: boolean;\n  /**\n   * A member arrived with `postgres_changes` bindings after the room joined.\n   * The real client refuses those after `subscribe()` (they ride the JOIN\n   * payload), so the channel has to be rebuilt rather than silently bind nothing.\n   */\n  needsRebuild: boolean;\n  members: Set<RoomProxy>;\n  teardownPending: boolean;\n  /** The multiple-presence-publishers warning is said once, not per heartbeat. */\n  warnedMultiplePublishers: boolean;\n}\n\n/** A per-`open()` stand-in for one real channel. One room may hold several. */\nclass RoomProxy implements RealtimeChannelLike {\n  readonly bindings: Binding[] = [];\n  readonly room: Room;\n  private readonly registry: RoomInternals;\n  statusCallback: ((status: string, error?: Error) => void) | undefined;\n  /** This member has called `track()` and not yet `untrack()`. */\n  tracking = false;\n  released = false;\n\n  constructor(room: Room, registry: RoomInternals) {\n    this.room = room;\n    this.registry = registry;\n  }\n\n  on(\n    type: \"postgres_changes\",\n    filter: PostgresChangeFilter,\n    callback: (payload: PostgresChangePayload) => void,\n  ): RealtimeChannelLike;\n  on(\n    type: \"broadcast\",\n    filter: { event: string },\n    callback: (payload: { event: string; payload?: unknown }) => void,\n  ): RealtimeChannelLike;\n  on(\n    type: \"presence\",\n    filter: { event: \"sync\" | \"join\" | \"leave\" },\n    callback: (payload: unknown) => void,\n  ): RealtimeChannelLike;\n  on(type: string, filter: unknown, callback: (payload: never) => void): RealtimeChannelLike {\n    const binding: Binding = {\n      type: type as BindingType,\n      filter,\n      callback,\n      attached: false,\n    };\n    this.bindings.push(binding);\n    if (this.room.real !== null) {\n      // 🚨 SUPABASE-JS REFUSES A PRESENCE BINDING ON A CHANNEL THAT HAS ALREADY\n      // JOINED. It throws `cannot add 'presence' callbacks ... after\n      // 'subscribed'` — synchronously, out of `.on()`, straight through the\n      // holder and into whatever React tree called it. That is how a shared\n      // room kills a route: the first holder (Postgres Changes + broadcast)\n      // subscribes, then the surface's typing indicator and online roster join\n      // the SAME topic a moment later and the second one detonates.\n      //\n      // A presence binding is therefore join-time, exactly like Postgres\n      // Changes: the room rebuilds, every holder rejoins, and each one's\n      // backfill door covers the gap.\n      const joinTimeOnly =\n        binding.type === \"postgres_changes\" ||\n        (binding.type === \"presence\" &&\n          this.room.joined &&\n          ![...this.room.members].some((member) =>\n            member.bindings.some(\n              (candidate) =>\n                candidate !== binding &&\n                candidate.attached &&\n                candidate.type === \"presence\" &&\n                (candidate.filter as { event: \"sync\" | \"join\" | \"leave\" }).event ===\n                  (binding.filter as { event: \"sync\" | \"join\" | \"leave\" }).event,\n            ),\n          ));\n      if (joinTimeOnly) {\n        this.room.needsRebuild = true;\n      } else {\n        this.registry.attach(this.room.real, this, binding);\n      }\n    }\n    return this;\n  }\n\n  subscribe(callback?: (status: string, error?: Error) => void): unknown {\n    this.statusCallback = callback;\n    this.registry.ensureJoined(this.room, this);\n    return this;\n  }\n\n  send(args: { type: \"broadcast\"; event: string; payload?: unknown }): unknown {\n    // No outbox by design (README § broadcast is ephemeral). The manager\n    // already refuses to pretend a send succeeded while disconnected.\n    return this.room.real?.send(args);\n  }\n\n  track(state: Record<string, unknown>): unknown {\n    if (!this.tracking) this.registry.noteTracking(this.room, this);\n    this.tracking = true;\n    return this.room.real?.track(state);\n  }\n\n  untrack(): unknown {\n    this.tracking = false;\n    // One presence entry per channel: untracking because THIS holder went away\n    // would delete a sibling holder's entry too. Only the last one untracks.\n    if (this.registry.anyTracking(this.room)) return undefined;\n    return this.room.real?.untrack();\n  }\n\n  presenceState(): PresenceStateLike {\n    return this.room.real?.presenceState() ?? {};\n  }\n}\n\ninterface RoomInternals {\n  attach(real: RealtimeChannelLike, proxy: RoomProxy, binding: Binding): void;\n  ensureJoined(room: Room, proxy: RoomProxy): void;\n  anyTracking(room: Room): boolean;\n  noteTracking(room: Room, proxy: RoomProxy): void;\n}\n\nexport interface RoomRegistryOptions {\n  client: RealtimeClientLike;\n  diagnostics: DiagnosticsSink;\n  /** Run something on the next turn — the React-19 remount grace window. */\n  schedule: (run: () => void) => void;\n}\n\n/** A `RealtimeClientLike` façade for ONE channel handle, plus its release door. */\nexport interface RoomMember extends RealtimeClientLike {\n  /** Drop this handle's claim on its room. Idempotent. */\n  release(): void;\n}\n\nexport interface RoomRegistry {\n  /** A client façade one `open()` can use exactly as it would use the real client. */\n  member(): RoomMember;\n  /** Rooms currently holding a live underlying channel. */\n  liveRoomCount(): number;\n  disposeAll(): void;\n}\n\ninterface ChannelParams {\n  config?: {\n    broadcast?: { self?: boolean; ack?: boolean };\n    presence?: { key?: string };\n  };\n}\n\nfunction asChannelParams(params: unknown): ChannelParams {\n  return params !== null && typeof params === \"object\" ? (params as ChannelParams) : {};\n}\n\nfunction presenceConfigOf(params: unknown): { key?: string } | undefined {\n  return asChannelParams(params).config?.presence;\n}\n\nfunction broadcastConfigOf(params: unknown): { self?: boolean; ack?: boolean } | undefined {\n  return asChannelParams(params).config?.broadcast;\n}\n\nfunction stableParams(params: unknown): string {\n  try {\n    return JSON.stringify(params ?? null);\n  } catch {\n    return \"<unserializable>\";\n  }\n}\n\nexport function createRoomRegistry(options: RoomRegistryOptions): RoomRegistry {\n  const { client, diagnostics, schedule } = options;\n  const rooms = new Map<string, Room>();\n\n  const attach = (\n    real: RealtimeChannelLike,\n    proxy: RoomProxy,\n    binding: Binding,\n  ): void => {\n    if (binding.attached) return;\n    const room = roomOf(real);\n\n    // Presence is configured once per Supabase channel and its three event\n    // handlers are join-time-only. A shared room therefore installs one real\n    // handler per event and fans that event out to every holder locally. This\n    // lets a typing label or online roster mount after the engine has joined\n    // without asking supabase-js to mutate a subscribed channel.\n    if (binding.type === \"presence\" && room !== null) {\n      const event = (binding.filter as { event: \"sync\" | \"join\" | \"leave\" }).event;\n      const existing = [...room.members].some((member) =>\n        member.bindings.some(\n          (candidate) =>\n            candidate !== binding &&\n            candidate.attached &&\n            candidate.type === \"presence\" &&\n            (candidate.filter as { event: \"sync\" | \"join\" | \"leave\" }).event === event,\n        ),\n      );\n      if (existing) {\n        binding.attached = true;\n        return;\n      }\n    }\n    binding.attached = true;\n    // Supabase has no \"off\" for a single binding, and the channel outlives this\n    // holder. The guard is what keeps a CLOSED handle from receiving one more\n    // event — silence after close has to be a fact, not a hope.\n    const guarded = (payload: unknown): void => {\n      if (binding.type !== \"presence\" || room === null) {\n        if (proxy.released) return;\n        (binding.callback as unknown as (value: unknown) => void)(payload);\n        return;\n      }\n      const event = (binding.filter as { event: \"sync\" | \"join\" | \"leave\" }).event;\n      for (const member of room.members) {\n        if (member.released) continue;\n        for (const candidate of member.bindings) {\n          if (\n            candidate.type === \"presence\" &&\n            (candidate.filter as { event: \"sync\" | \"join\" | \"leave\" }).event === event\n          ) {\n            (candidate.callback as unknown as (value: unknown) => void)(payload);\n          }\n        }\n      }\n    };\n    // Belt and braces. The rule above is what PREVENTS the throw; this is what\n    // happens if a client version refuses a binding we thought was safe. A\n    // realtime layer must never take a surface down — the room is marked for a\n    // rebuild and the reason is stated, rather than the exception escaping into\n    // a React render.\n    try {\n      if (binding.type === \"postgres_changes\") {\n        real.on(\"postgres_changes\", binding.filter as PostgresChangeFilter, guarded);\n        return;\n      }\n      if (binding.type === \"broadcast\") {\n        real.on(\"broadcast\", binding.filter as { event: string }, guarded);\n        return;\n      }\n      real.on(\"presence\", binding.filter as { event: \"sync\" | \"join\" | \"leave\" }, guarded);\n    } catch (error) {\n      binding.attached = false;\n      if (room !== null) room.needsRebuild = true;\n      diagnostics({\n        level: \"warn\",\n        code: \"channel.room.binding.refused\",\n        topic: room?.topic ?? \"unknown\",\n        message:\n          `The client refused a \"${binding.type}\" binding on a live channel: ` +\n          `${String((error as Error)?.message ?? error)}`,\n        remedy:\n          \"The room is marked for a rebuild, so the binding takes effect on the next join. \" +\n          \"Open all of one topic's holders together at mount to avoid the rejoin.\",\n      });\n    }\n  };\n\n  /** Which room owns this live channel — for the refused-binding recovery. */\n  const roomOf = (real: RealtimeChannelLike): Room | null => {\n    for (const room of rooms.values()) {\n      if (room.real === real) return room;\n    }\n    return null;\n  };\n\n  const detachBindings = (room: Room): void => {\n    for (const proxy of room.members) {\n      for (const binding of proxy.bindings) binding.attached = false;\n    }\n  };\n\n  const teardownReal = (room: Room): void => {\n    const dead = room.real;\n    room.real = null;\n    room.joined = false;\n    room.needsRebuild = false;\n    detachBindings(room);\n    if (dead === null) return;\n    try {\n      void client.removeChannel(dead);\n    } catch {\n      // A channel torn down mid-join can reject; the socket drops it anyway.\n    }\n  };\n\n  const join = (room: Room): void => {\n    const real = client.channel(room.topic, room.params);\n    room.real = real;\n    room.joined = false;\n    room.needsRebuild = false;\n    detachBindings(room);\n    for (const proxy of room.members) {\n      for (const binding of proxy.bindings) attach(real, proxy, binding);\n    }\n    real.subscribe((status: string, error?: Error) => {\n      // A channel we already replaced can still report; its status is not ours.\n      if (room.real !== real) return;\n      if (status === \"SUBSCRIBED\") {\n        room.joined = true;\n      } else if (status === \"CHANNEL_ERROR\" || status === \"TIMED_OUT\" || status === \"CLOSED\") {\n        // The room is gone. Drop it here so the next holder's `subscribe()`\n        // re-joins a FRESH channel instead of re-using one the server hung up\n        // on — every holder then rides its own jittered backoff into one room.\n        teardownReal(room);\n      }\n      // Snapshot: a holder may close and re-open from inside its own callback.\n      for (const proxy of [...room.members]) proxy.statusCallback?.(status, error);\n    });\n  };\n\n  const ensureJoined = (room: Room, proxy: RoomProxy): void => {\n    if (room.real !== null && room.needsRebuild) {\n      // A holder arrived needing something that can only be set at JOIN time:\n      // Postgres Changes bindings, or a presence key the channel was not opened\n      // with. Rebuilding costs every holder a rejoin (and therefore a backfill,\n      // which is honest — there IS a gap) and is the only way those take effect.\n      diagnostics({\n        level: \"info\",\n        code: \"channel.room.rebuild\",\n        topic: room.topic,\n        message:\n          \"Rebuilding the shared channel: a new holder needs something that only takes effect at \" +\n          \"join time (Postgres Changes bindings, or a presence key).\",\n        remedy:\n          \"No action needed — every holder reconciles on the rejoin. To avoid the rebuild entirely, \" +\n          \"open all of one topic's holders together at mount.\",\n      });\n      teardownReal(room);\n    }\n    if (room.real === null) {\n      join(room);\n      return;\n    }\n    for (const binding of proxy.bindings) attach(room.real, proxy, binding);\n    // The room is already live: this holder must hear about it, or it would sit\n    // in \"connecting\" forever waiting for a join that already happened.\n    if (room.joined) proxy.statusCallback?.(\"SUBSCRIBED\");\n  };\n\n  const anyTracking = (room: Room): boolean => {\n    for (const proxy of room.members) {\n      if (proxy.tracking && !proxy.released) return true;\n    }\n    return false;\n  };\n\n  const noteTracking = (room: Room, proxy: RoomProxy): void => {\n    if (!anyTracking(room)) return;\n    if (room.warnedMultiplePublishers) return;\n    room.warnedMultiplePublishers = true;\n    // One channel carries ONE presence entry per key. A second publisher does\n    // not add to it — it OVERWRITES it on every heartbeat, which is how a\n    // typing indicator that looked fine in one tab dies in two.\n    diagnostics({\n      level: \"warn\",\n      code: \"channel.room.presence.multiple-publishers\",\n      topic: room.topic,\n      message:\n        `Two holders of the declared topic \"${room.topic}\" are both publishing presence under the ` +\n        \"same key; each heartbeat overwrites the other's state.\",\n      remedy:\n        \"One publisher per topic. The surface that only READS the roster (a typists label, an \" +\n        \"online list) passes `presence: { publish: false }` — it still sees everyone.\",\n    });\n    void proxy;\n  };\n\n  const internals: RoomInternals = { attach, ensureJoined, anyTracking, noteTracking };\n\n  const reconcileParams = (room: Room, params: unknown): void => {\n    const incomingPresence = presenceConfigOf(params);\n    const roomPresence = presenceConfigOf(room.params);\n    if (\n      incomingPresence !== undefined &&\n      stableParams(incomingPresence) !== stableParams(roomPresence)\n    ) {\n      if (roomPresence === undefined) {\n        // A presence holder joined a room opened without presence config. Its\n        // key has to be ON the channel, so the room's config is upgraded rather\n        // than the holder quietly getting a keyless presence entry.\n        const current = asChannelParams(room.params);\n        room.params = {\n          ...current,\n          config: { ...current.config, presence: incomingPresence },\n        };\n        if (room.real !== null) room.needsRebuild = true;\n      } else {\n        diagnostics({\n          level: \"warn\",\n          code: \"channel.room.config-mismatch\",\n          topic: room.topic,\n          message:\n            `Two holders of the declared topic \"${room.topic}\" asked for different presence keys; ` +\n            \"the FIRST one's key is in force for both.\",\n          remedy:\n            \"Holders of one topic share ONE underlying channel and therefore ONE presence entry. \" +\n            \"Give them the same presence key, or declare distinct topics.\",\n          detail: { inForce: roomPresence, ignored: incomingPresence },\n        });\n      }\n    }\n    if (\n      stableParams(broadcastConfigOf(room.params)) !== stableParams(broadcastConfigOf(params))\n    ) {\n      diagnostics({\n        level: \"warn\",\n        code: \"channel.room.config-mismatch\",\n        topic: room.topic,\n        message:\n          `Two holders of the declared topic \"${room.topic}\" asked for different broadcast config ` +\n          \"(usually a disagreement about echoSuppression); the FIRST one's is in force for both.\",\n        remedy:\n          \"Holders of one topic share ONE underlying channel. Give them the same echoSuppression \" +\n          \"setting, or declare distinct topics. Delivery is still filtered per holder by client id, \" +\n          \"so this degrades what reaches the socket, not correctness.\",\n        detail: {\n          inForce: broadcastConfigOf(room.params),\n          ignored: broadcastConfigOf(params),\n        },\n      });\n    }\n  };\n\n  const roomFor = (topic: string, params: unknown): Room => {\n    const existing = rooms.get(topic);\n    if (existing) {\n      reconcileParams(existing, params);\n      return existing;\n    }\n    const created: Room = {\n      topic,\n      params,\n      real: null,\n      joined: false,\n      needsRebuild: false,\n      members: new Set(),\n      teardownPending: false,\n      warnedMultiplePublishers: false,\n    };\n    rooms.set(topic, created);\n    return created;\n  };\n\n  const detach = (room: Room, proxy: RoomProxy): void => {\n    if (proxy.released) return;\n    proxy.released = true;\n    const wasTracking = proxy.tracking;\n    proxy.tracking = false;\n    room.members.delete(proxy);\n    if (wasTracking && room.real !== null && !anyTracking(room)) {\n      try {\n        void room.real.untrack();\n      } catch {\n        // Already-dead channels reject untrack; the server drops the entry on close.\n      }\n    }\n    if (room.members.size > 0) return;\n    if (room.teardownPending) return;\n    room.teardownPending = true;\n    // DEFERRED BY ONE TURN, ON PURPOSE. React 19 double-invokes effects, so the\n    // cleanup and the re-run land in the same tick; `removeChannel` is async, so\n    // removing here would hand the re-run a channel mid-teardown. The unique\n    // instance topic cannot rescue a shared room — this grace window is what\n    // replaces it. `disposeAll()` still tears down immediately, so nothing leaks.\n    schedule(() => {\n      room.teardownPending = false;\n      if (room.members.size > 0) return;\n      teardownReal(room);\n      if (rooms.get(room.topic) === room) rooms.delete(room.topic);\n    });\n  };\n\n  return {\n    member(): RoomMember {\n      let current: RoomProxy | null = null;\n      return {\n        channel(topic: string, params?: unknown): RealtimeChannelLike {\n          const room = roomFor(topic, params);\n          const proxy = new RoomProxy(room, internals);\n          room.members.add(proxy);\n          current = proxy;\n          return proxy;\n        },\n        removeChannel(channel: RealtimeChannelLike): unknown {\n          const proxy = channel as RoomProxy;\n          detach(proxy.room, proxy);\n          if (current === proxy) current = null;\n          return Promise.resolve(\"ok\");\n        },\n        release(): void {\n          if (current === null) return;\n          const proxy = current;\n          current = null;\n          detach(proxy.room, proxy);\n        },\n      };\n    },\n    liveRoomCount(): number {\n      let live = 0;\n      for (const room of rooms.values()) {\n        if (room.real !== null) live += 1;\n      }\n      return live;\n    },\n    disposeAll(): void {\n      for (const room of [...rooms.values()]) {\n        for (const proxy of room.members) proxy.released = true;\n        room.members.clear();\n        teardownReal(room);\n      }\n      rooms.clear();\n    },\n  };\n}\n","/**\n * THE REALTIME MANAGER — the one object that owns channels.\n *\n * Everything a consumer used to hand-roll lives here, and none of it is\n * optional: unique topics, enforced teardown, echo suppression, dedup, the\n * decoupled ordered handler queue, jittered reconnect with a stability reset,\n * a BACKFILL door on every recovery path, tab-sleep and network awareness, and\n * live diagnostics. Opening a channel is one call; there is no correct-usage\n * checklist to forget.\n *\n * THE RULE THAT MADE THIS PACKAGE: realtime has NO REPLAY. Every event that\n * happened while the socket was down is gone forever. A reconnect without a\n * re-read leaves a permanently wrong screen that looks perfectly healthy — so\n * reconnecting IS re-reading here, not something each caller remembers.\n */\n\nimport { createBackoff, RECONNECT_GIVE_UP_ATTEMPTS, type Backoff, type BackoffOptions } from \"./backoff\";\nimport { clientSessionId, randomId } from \"./client-id\";\nimport {\n  createChannelMetrics,\n  createConsoleDiagnostics,\n  registerChannelMetrics,\n  type ChannelMetrics,\n} from \"./diagnostics\";\nimport { createDedupe, type Dedupe, type DedupeOptions } from \"./dedupe\";\nimport {\n  defaultEnvironment,\n  type EnvironmentBridge,\n  type EnvironmentSignal,\n} from \"./environment\";\nimport { canonicalRevision, createWriteLedger, type OriginVerdict, type WriteLedger } from \"./origin\";\nimport {\n  createPresenceRuntime,\n  type PresenceOptions,\n  type PresenceRoom,\n  type PresenceRuntime,\n  type PresenceTimers,\n} from \"./presence\";\nimport { createEventQueue, type EventQueue } from \"./queue\";\nimport { createRoomRegistry, type RoomMember } from \"./rooms\";\nimport { uniqueChannelTopic } from \"./topics\";\nimport type {\n  ConnectionHealth,\n  DiagnosticsSink,\n  MatrxBroadcastEnvelope,\n  PostgresChangeEvent,\n  PostgresChangePayload,\n  RealtimeChannelLike,\n  RealtimeClientLike,\n  RealtimeStatus,\n} from \"./types\";\n\n/** Why the package is asking the consumer to re-read from the database. */\nexport type BackfillReason =\n  | \"reconnect\"\n  | \"wake\"\n  | \"network-restored\"\n  | \"queue-overflow\"\n  | \"manual\";\n\nexport interface BackfillContext {\n  reason: BackfillReason;\n  topic: string;\n  /** ms the channel was not receiving events, when known. */\n  gapMs: number | null;\n}\n\nexport interface PostgresChangeDelivery<TRow = Record<string, unknown>> {\n  payload: PostgresChangePayload;\n  /** The row the event carries (`new` for INSERT/UPDATE, `old` for DELETE). */\n  row: TRow | null;\n  /** Where this event came from, per the write ledger. Always `remote` here. */\n  origin: OriginVerdict;\n}\n\nexport interface PostgresChangeBinding<TRow = Record<string, unknown>> {\n  event: PostgresChangeEvent;\n  schema: string;\n  table: string;\n  /** PostgREST filter, e.g. `file_id=eq.<uuid>`. */\n  filter?: string;\n  /**\n   * The row's primary key, as a string. REQUIRED for echo suppression and for\n   * the default dedup key — without it the package cannot tell your write from\n   * anyone else's, and it says so once through the diagnostics sink rather than\n   * silently degrading.\n   */\n  rowId?: (row: Record<string, unknown>) => string | undefined;\n  /** A stable string summarizing the row's content. Powers content-aware echo tests. */\n  fingerprint?: (row: Record<string, unknown>) => string | undefined;\n  /** Column carrying the row's `updated_at`. Default `updated_at`. */\n  updatedAtField?: string;\n  /** Column carrying the actor stamp. Default `updated_by`. */\n  updatedByField?: string;\n  /**\n   * Column carrying the row's integer revision. Default `version` — every\n   * platform base-contract table has one, bumped by `_touch_row` on EVERY\n   * update. A revision the ledger has never seen is never suppressed as an\n   * echo, whatever the content or actor says (see `origin.ts` R4). Set to\n   * `null` for a table whose `version` column means something else.\n   */\n  revisionField?: string | null;\n  onChange: (delivery: PostgresChangeDelivery<TRow>) => void | Promise<void>;\n}\n\nexport interface BroadcastMessage<TData> {\n  event: string;\n  data: TData;\n  /** The sending client session id. */\n  from: string;\n  eventId: string;\n  sentAt: number;\n  /** False when the message did not carry a Matrx envelope (a foreign sender). */\n  enveloped: boolean;\n}\n\nexport interface BroadcastBinding<TData = unknown> {\n  event: string;\n  onMessage: (message: BroadcastMessage<TData>) => void | Promise<void>;\n}\n\n/**\n * RAW WIRE — for a channel whose payload shape is owned by a FOREIGN PEER.\n *\n * Normally this package wraps every broadcast in the Matrx envelope\n * (`{v, cid, eid, ts, data}`), and that envelope is what makes echo suppression\n * and dedup possible at all. But some channels do not get to choose their wire:\n * the other end is a separately released program that reads a payload shape it\n * defined — the deployed Chrome extension's `matrx-extension-bridge:<userId>`\n * channel is the case that produced this mode. Wrapping there would put a shape\n * on the wire the peer does not recognize, and the peer would simply stop\n * hearing us with nothing failing loudly on either side.\n *\n * So raw mode means EXACTLY this, in both directions:\n *\n *  - **Send:** the consumer's payload goes on the wire verbatim. Byte-identical\n *    to what a hand-rolled `channel.send({type:\"broadcast\", event, payload})`\n *    would have put there.\n *  - **Receive:** no envelope is read, ever — not even from a payload that\n *    happens to look enveloped. `BroadcastMessage.enveloped` is always `false`\n *    and `data` is the payload exactly as it arrived.\n *\n * WHAT THAT COSTS, AND WHY IT IS NEVER SILENT. Two protections ride the\n * envelope, and raw mode has to get them back explicitly or announce their\n * absence:\n *\n *  1. **Client-side echo suppression** used `cid`. There is no `cid` on a raw\n *     wire, so either supply {@link RawWireOptions.isOwnMessage} (the peer's\n *     shape carries something identifying the sender) or set\n *     {@link RawWireOptions.acceptEchoFromSelf} to say in code that you accept\n *     the server-side `broadcast.self:false` as the only protection. Supplying\n *     neither is legal and produces a `channel.raw.echo-unguarded` warning — the\n *     package will not quietly downgrade a guarantee.\n *  2. **Dedup** keyed off `eid`. Without one, every arrival is a distinct key\n *     and dedup does nothing. Supply {@link ChannelSpec.eventKey} — it receives\n *     the raw payload — or accept a `channel.raw.dedup-unguarded` warning.\n *\n * Raw mode is TRANSITIONAL by intent: it exists so both ends of a foreign wire\n * can ride this package today, and so the day the peer ships an envelope-aware\n * release, the only change is deleting `wire` from the spec.\n */\nexport interface RawWireOptions {\n  /**\n   * Does this inbound payload come from THIS client? Return `true` and the\n   * message is suppressed exactly as an envelope echo would have been. Supply it\n   * whenever the foreign shape carries anything identifying the sender.\n   */\n  isOwnMessage?: (payload: unknown) => boolean;\n  /**\n   * Set `true` to state, in code, that this channel runs without client-side\n   * echo suppression because the foreign shape carries no sender identity. The\n   * package still announces the fact (at `info`), because a reader of the\n   * diagnostics stream must be able to see which channels are unguarded.\n   */\n  acceptEchoFromSelf?: boolean;\n}\n\n/** How this channel's broadcasts ride the wire. Default: the Matrx envelope. */\nexport type BroadcastWire =\n  | { mode: \"matrx\" }\n  | ({ mode: \"raw\" } & RawWireOptions);\n\nexport interface ChannelSpec<\n  TPresence extends Record<string, unknown> = Record<string, unknown>,\n> {\n  /** Build it with a channel namespace — never a hand-written string. */\n  topic: string;\n  postgresChanges?: readonly PostgresChangeBinding[];\n  broadcast?: readonly BroadcastBinding[];\n  presence?: PresenceOptions<TPresence>;\n  /**\n   * THE BACKFILL DOOR. Re-read whatever the missed events would have changed.\n   * Called on every recovery path: reconnect, tab wake, network restore, and\n   * queue overflow. Omitting it is allowed and LOUD — the package warns once,\n   * because a channel with no reconciliation is a screen that will eventually\n   * lie.\n   */\n  onBackfill?: (context: BackfillContext) => void | Promise<void>;\n  onStatusChange?: (status: RealtimeStatus) => void;\n  /**\n   * Echo suppression is ON by default and opting out is the discouraged path\n   * (R2). Set `false` only when the consumer genuinely needs to see its own\n   * writes come back over the wire.\n   */\n  echoSuppression?: boolean;\n  /**\n   * A PRIVATE channel — Supabase authorizes the topic against `realtime.messages`\n   * RLS before allowing the join, which is how Database Broadcast\n   * (`realtime.broadcast_changes()` from a Postgres trigger) delivers rows only\n   * to the users entitled to them.\n   *\n   * Two things follow, and both are handled here rather than by every consumer:\n   * the channel is created with `config.private: true`, and the socket must be\n   * carrying the current access token, so `client.realtime.setAuth()` is awaited\n   * BEFORE subscribing. Skipping that second step is the silent failure this\n   * exists to prevent — the join is refused and the channel receives nothing\n   * while looking perfectly healthy.\n   *\n   * A client with no `realtime.setAuth` cannot open a private channel; the\n   * manager emits `channel.private.unsupported` and connects anyway as a public\n   * channel would, so the failure is visible instead of mysterious.\n   */\n  private?: boolean;\n  /**\n   * THE WIRE MODE for this channel's broadcasts. Defaults to `{mode:\"matrx\"}`.\n   * Use `{mode:\"raw\", …}` ONLY when a foreign peer owns the payload shape —\n   * see {@link RawWireOptions} for what raw mode costs and how to buy it back.\n   * A raw channel's `topic` is likewise the peer's contract, so declare its\n   * namespace with `foreignTopic` rather than inventing an `mx:` name.\n   */\n  wire?: BroadcastWire;\n  /**\n   * Idempotency key for an event. Defaults to the broadcast envelope's `eid`,\n   * and for Postgres Changes to `table:rowId:eventType:commitTimestamp`.\n   * On a raw-wire channel there is no `eid`, so supplying this is the ONLY way\n   * to keep dedup — `payload` is the raw payload exactly as it arrived.\n   */\n  eventKey?: (\n    source: \"broadcast\" | \"postgres_changes\",\n    payload: unknown,\n  ) => string | undefined;\n  /**\n   * Called when this holder gains or loses TAB LEADERSHIP of its topic. Several\n   * holders of one topic in one tab (two Board tiles of one document) share one\n   * client id and one channel; the work that must happen once per tab — the\n   * autosave, the host election — belongs to the leader. See\n   * {@link ChannelHandle.isLocalLeader}.\n   */\n  onLeaderChange?: (isLeader: boolean) => void;\n}\n\nexport interface ChannelHandle<\n  TPresence extends Record<string, unknown> = Record<string, unknown>,\n> {\n  readonly topic: string;\n  status(): RealtimeStatus;\n  /**\n   * Send a broadcast, wrapped in the Matrx envelope so receivers can suppress\n   * it — unless the spec declared `wire: {mode:\"raw\"}`, in which case `data`\n   * goes on the wire verbatim.\n   */\n  send<TData>(event: string, data: TData): void;\n  /** The presence room, when the spec declared one. */\n  presence: PresenceRoom<TPresence> | null;\n  /** Ask for a reconciliation pass right now. */\n  requestBackfill(reason?: BackfillReason): void;\n  /**\n   * True for exactly ONE open holder of this topic per manager (per tab): the\n   * longest-open one. When it closes, the next-oldest becomes leader and hears\n   * `onLeaderChange(true)`. Use it to run once-per-tab work (autosave, host\n   * election) — same-tab holders share a client id, so nothing else can tell\n   * them apart. A holder with no peers is the leader.\n   */\n  isLocalLeader(): boolean;\n  /** Enforced teardown. Idempotent. */\n  close(): void;\n}\n\nexport interface ManagerTimers extends PresenceTimers {\n  schedule: (run: () => void) => void;\n}\n\nconst defaultTimers: ManagerTimers = {\n  setInterval: (run, ms) => setInterval(run, ms),\n  clearInterval: (handle) => clearInterval(handle as ReturnType<typeof setInterval>),\n  setTimeout: (run, ms) => setTimeout(run, ms),\n  clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n  now: () => Date.now(),\n  schedule: (run) => {\n    setTimeout(run, 0);\n  },\n};\n\nexport interface RealtimeManagerOptions {\n  /** Any Supabase client. The package never constructs one — hosts own identity. */\n  client: RealtimeClientLike;\n  /** The signed-in user id, when known. Enables the `updated_by` echo test. */\n  actorId?: string | undefined;\n  /** Visibility/online bridge. Defaults to the browser one, or inert under SSR. */\n  environment?: EnvironmentBridge;\n  /** Background-failure sink. Defaults to a console sink that states the remedy. */\n  diagnostics?: DiagnosticsSink;\n  backoff?: BackoffOptions;\n  /**\n   * Consecutive failed reconnects (no stable connection between) after which a\n   * channel is abandoned: one `channel.reconnect.abandoned` diagnostic, status\n   * `disconnected`, no more timers. A tab wake or network restore starts a\n   * fresh bounded round. Default 10; 0 or Infinity disables the bound.\n   */\n  giveUpAfterAttempts?: number;\n  dedupe?: DedupeOptions;\n  queue?: { maxDepth?: number; sliceMs?: number };\n  /** Share one write ledger with the host's write path. One is created if omitted. */\n  ledger?: WriteLedger;\n  /**\n   * How long a surface may be hidden before waking triggers a reconcile.\n   * Default 10000 — shorter than any realistic socket idle-out.\n   */\n  wakeBackfillAfterMs?: number;\n  timers?: ManagerTimers;\n}\n\nexport interface RealtimeManager {\n  readonly clientId: string;\n  /** The shared write ledger — register optimistic writes on it (R6). */\n  readonly ledger: WriteLedger;\n  open<TPresence extends Record<string, unknown> = Record<string, unknown>>(\n    spec: ChannelSpec<TPresence>,\n  ): ChannelHandle<TPresence>;\n  health(): ConnectionHealth;\n  /** Reconcile every open channel (a manual \"I don't trust the screen\" button). */\n  backfillAll(reason?: BackfillReason): void;\n  openChannelCount(): number;\n  dispose(): void;\n}\n\nexport function createRealtimeManager(\n  options: RealtimeManagerOptions,\n): RealtimeManager {\n  const {\n    client,\n    actorId,\n    environment = defaultEnvironment(),\n    diagnostics = createConsoleDiagnostics(),\n    wakeBackfillAfterMs = 10_000,\n    timers = defaultTimers,\n  } = options;\n\n  const clientId = clientSessionId();\n  const ledger = options.ledger ?? createWriteLedger({ actorId, now: timers.now });\n  const channels = new Set<InternalChannel>();\n  // Broadcast and presence route on the topic, so their holders must SHARE one\n  // underlying channel named by the declared topic. See `rooms.ts`.\n  const rooms = createRoomRegistry({ client, diagnostics, schedule: timers.schedule });\n  // SAME-TAB HOLDERS. Every holder in this manager shares ONE client id and ONE\n  // underlying channel (`broadcast.self:false`), so a frame one holder sends is\n  // never heard by a sibling holder of the same topic — the wire does not echo\n  // it back and the receive side would drop it as \"ours\" anyway. Siblings are\n  // therefore wired together locally, in open order (the first is the tab leader).\n  interface TabPeer {\n    deliver(event: string, envelope: MatrxBroadcastEnvelope<unknown>): void;\n    leaderChanged(isLeader: boolean): void;\n  }\n  const tabPeers = new Map<string, TabPeer[]>();\n  const fanOutLocal = (\n    topic: string,\n    from: TabPeer,\n    event: string,\n    envelope: MatrxBroadcastEnvelope<unknown>,\n  ): void => {\n    for (const peer of [...(tabPeers.get(topic) ?? [])]) {\n      if (peer !== from) peer.deliver(event, envelope);\n    }\n  };\n  let hiddenSince: number | null = environment.isVisible() ? null : timers.now();\n  let disposed = false;\n\n  const unsubscribeEnvironment = environment.subscribe((signal: EnvironmentSignal) => {\n    if (disposed) return;\n    if (signal === \"hidden\") {\n      hiddenSince = timers.now();\n      return;\n    }\n    if (signal === \"visible\") {\n      const hiddenFor = hiddenSince === null ? 0 : timers.now() - hiddenSince;\n      hiddenSince = null;\n      if (hiddenFor >= wakeBackfillAfterMs) {\n        for (const channel of channels) channel.wake(hiddenFor);\n      }\n      return;\n    }\n    if (signal === \"online\") {\n      // The device came back. Do not wait out a 30s backoff that was computed\n      // while there was no network at all — that is the \"it reconnects, just\n      // not for half a minute\" complaint.\n      for (const channel of channels) channel.networkRestored();\n      return;\n    }\n    // offline: mark every channel degraded so surfaces can say so, honestly.\n    for (const channel of channels) channel.markOffline();\n  });\n\n  interface InternalChannel {\n    handle: ChannelHandle<Record<string, unknown>>;\n    wake(hiddenForMs: number): void;\n    networkRestored(): void;\n    markOffline(): void;\n    status(): RealtimeStatus;\n    close(): void;\n  }\n\n  function open<TPresence extends Record<string, unknown>>(\n    spec: ChannelSpec<TPresence>,\n  ): ChannelHandle<TPresence> {\n    if (disposed) {\n      throw new Error(\n        \"This realtime manager is disposed. Opening a channel on it would leak a \" +\n          \"subscription no one can close — create a new manager instead.\",\n      );\n    }\n\n    const echoSuppression = spec.echoSuppression !== false;\n    const rawWire: RawWireOptions | null =\n      spec.wire !== undefined && spec.wire.mode === \"raw\" ? spec.wire : null;\n    const namespace = spec.topic.split(\":\")[1];\n\n    if (rawWire !== null) {\n      // A raw channel is a DELIBERATE, NAMED downgrade of this package's two\n      // envelope-borne guarantees. It is announced at open time — once, on the\n      // channel that took it — so nobody has to read the spec to learn that a\n      // given topic is running unguarded.\n      if (rawWire.isOwnMessage === undefined) {\n        diagnostics(\n          rawWire.acceptEchoFromSelf === true\n            ? {\n                level: \"info\",\n                code: \"channel.raw.echo-accepted\",\n                topic: spec.topic,\n                message:\n                  \"Raw wire: client-side echo suppression is OFF by declaration — the foreign \" +\n                  \"payload shape carries no sender identity, so only the server's \" +\n                  \"`broadcast.self:false` keeps this client from hearing itself.\",\n                remedy:\n                  \"No action needed today. When the peer ships a shape carrying a sender id, \" +\n                  \"supply `wire.isOwnMessage` and drop `acceptEchoFromSelf`.\",\n              }\n            : {\n                level: \"warn\",\n                code: \"channel.raw.echo-unguarded\",\n                topic: spec.topic,\n                message:\n                  \"Raw wire: this channel has NO client-side echo suppression. There is no `cid` \" +\n                  \"on a raw payload, and the spec supplied neither `wire.isOwnMessage` nor \" +\n                  \"`wire.acceptEchoFromSelf`.\",\n                remedy:\n                  \"Supply `wire.isOwnMessage` if the foreign shape identifies its sender; \" +\n                  \"otherwise set `wire.acceptEchoFromSelf: true` to state the trade-off in code.\",\n              },\n        );\n      }\n      if ((spec.broadcast?.length ?? 0) > 0 && spec.eventKey === undefined) {\n        diagnostics({\n          level: \"warn\",\n          code: \"channel.raw.dedup-unguarded\",\n          topic: spec.topic,\n          message:\n            \"Raw wire: dedup is inert on this channel. The default idempotency key is the \" +\n            \"envelope's `eid`, and a raw payload has none, so a redelivered message is \" +\n            \"delivered again.\",\n          remedy:\n            \"Supply `eventKey(source, payload)` returning a stable id from the foreign shape \" +\n            \"(a requestId, a sequence number, or `${direction}:${requestId}`).\",\n        });\n      }\n    }\n\n    // THE TOPIC-SEMANTICS SPLIT. For Postgres Changes the topic is only a\n    // client-side connection key, so every instance gets a unique one and the\n    // React-19 double-effect collision is impossible. For broadcast and presence\n    // the topic IS the room: it must go on the wire exactly as declared, and the\n    // holders inside this client share ONE channel, ref-counted.\n    const sharedRoom =\n      (spec.broadcast?.length ?? 0) > 0 || spec.presence !== undefined;\n    const roomMember: RoomMember | null = sharedRoom ? rooms.member() : null;\n    const channelClient: RealtimeClientLike = roomMember ?? client;\n\n    let status: RealtimeStatus = \"connecting\";\n    let channel: RealtimeChannelLike | null = null;\n    let everConnected = false;\n    let retryTimer: unknown = null;\n    let abandoned = false;\n    const giveUpAfter = options.giveUpAfterAttempts ?? RECONNECT_GIVE_UP_ATTEMPTS;\n    let closed = false;\n    let backfillRunning = false;\n    let backfillQueuedReason: BackfillReason | null = null;\n    let lastHealthyAt: number | null = null;\n\n    const backoff: Backoff = createBackoff({ ...options.backoff, now: timers.now });\n    const dedupe: Dedupe = createDedupe({ ...options.dedupe, now: timers.now });\n\n    const queue: EventQueue = createEventQueue({\n      ...options.queue,\n      now: timers.now,\n      schedule: timers.schedule,\n      onOverflow: (dropped) => {\n        diagnostics({\n          level: \"error\",\n          code: \"channel.queue.overflow\",\n          topic: spec.topic,\n          message: `Shed ${dropped} realtime event(s): handlers cannot keep up with the stream.`,\n          remedy:\n            \"A reconcile has been requested so no data is lost. If this repeats, make the \" +\n            \"channel's handler cheaper (batch dispatches, no per-event RPC) or narrow the subscription filter.\",\n          detail: { dropped },\n        });\n        requestBackfill(\"queue-overflow\");\n      },\n      onError: (error) => {\n        metrics.handlerErrors += 1;\n        diagnostics({\n          level: \"error\",\n          code: \"channel.handler.threw\",\n          topic: spec.topic,\n          message: \"A realtime handler threw. The subscription is still alive.\",\n          remedy:\n            \"Fix the handler: an exception here means the event was received but not applied, \" +\n            \"so the screen is now out of sync until the next reconcile.\",\n          detail: { error: String(error) },\n        });\n      },\n    });\n\n    const metrics: ChannelMetrics = createChannelMetrics({\n      topic: spec.topic,\n      namespace,\n      queueDepth: () => queue.depth(),\n      queueDropped: () => queue.dropped(),\n      now: timers.now,\n    });\n    const unregisterMetrics = registerChannelMetrics(metrics);\n\n    const presence: PresenceRuntime<TPresence> | null = spec.presence\n      ? createPresenceRuntime<TPresence>(spec.presence, timers)\n      : null;\n\n    if (!spec.onBackfill) {\n      diagnostics({\n        level: \"warn\",\n        code: \"channel.backfill.missing\",\n        topic: spec.topic,\n        message:\n          \"This channel has no onBackfill handler, so events missed during a disconnect are lost forever.\",\n        remedy:\n          \"Give the channel an onBackfill that re-reads the rows it watches. Realtime has no replay; \" +\n          \"without it a dropped socket leaves a screen that looks healthy and is wrong.\",\n      });\n    }\n\n    const setStatus = (next: RealtimeStatus): void => {\n      if (status === next) return;\n      status = next;\n      metrics.status = next;\n      spec.onStatusChange?.(next);\n    };\n\n    const runBackfill = (reason: BackfillReason, gapMs: number | null): void => {\n      const handler = spec.onBackfill;\n      if (!handler) return;\n      if (backfillRunning) {\n        // Never run two reconciles at once — that is how a flapping channel\n        // turns into a request storm. Remember that one more is owed.\n        backfillQueuedReason = reason;\n        return;\n      }\n      backfillRunning = true;\n      metrics.backfills += 1;\n      metrics.lastBackfillAt = timers.now();\n      void (async () => {\n        try {\n          await handler({ reason, topic: spec.topic, gapMs });\n        } catch (error) {\n          diagnostics({\n            level: \"error\",\n            code: \"channel.backfill.failed\",\n            topic: spec.topic,\n            message: `Reconcile after \"${reason}\" failed; this surface may be showing stale data.`,\n            remedy:\n              \"Retry the read or surface a 'reconnecting' state to the user. Do not treat a failed \" +\n              \"backfill as a no-op — the missed events are not coming back on their own.\",\n            detail: { error: String(error) },\n          });\n        } finally {\n          backfillRunning = false;\n          const owed = backfillQueuedReason;\n          backfillQueuedReason = null;\n          if (owed !== null) runBackfill(owed, null);\n        }\n      })();\n    };\n\n    const requestBackfill = (reason: BackfillReason = \"manual\"): void => {\n      runBackfill(reason, lastHealthyAt === null ? null : timers.now() - lastHealthyAt);\n    };\n\n    const teardownChannel = (): void => {\n      if (!channel) return;\n      const dead = channel;\n      channel = null;\n      try {\n        void channelClient.removeChannel(dead);\n      } catch {\n        // A channel torn down mid-join can reject. The socket drops it anyway\n        // and there is nothing a caller could do about it.\n      }\n    };\n\n    const scheduleReconnect = (cause: string, error?: Error): void => {\n      if (closed) return;\n      // Supabase can report several failures for one drop (CHANNEL_ERROR then\n      // CLOSED, say). A pending retry owns that physical failure. Counting a\n      // second terminal callback as another attempt falsely escalates one drop\n      // into an outage and pushes recovery farther into the backoff ladder.\n      if (retryTimer !== null || abandoned) {\n        return;\n      }\n      if (giveUpAfter > 0 && Number.isFinite(giveUpAfter) && backoff.attempts() >= giveUpAfter) {\n        abandoned = true;\n        teardownChannel();\n        setStatus(\"disconnected\");\n        diagnostics({\n          level: \"error\",\n          code: \"channel.reconnect.abandoned\",\n          topic: spec.topic,\n          message: `Channel dropped (${cause}); gave up after ${backoff.attempts()} failed attempts. It stays disconnected until the tab wakes or the network returns.`,\n          remedy: \"The subject of this channel may not exist (a conversation whose first send was refused, a deleted row) or the session cannot join it. Check the topic, the session, and the channel's authorization.\",\n          detail: {\n            attempt: backoff.attempts(),\n            ...(error ? { providerError: { name: error.name.slice(0, 128), message: error.message.slice(0, 1024) } } : {}),\n          },\n        });\n        return;\n      }\n      const delay = backoff.nextDelayMs();\n      metrics.failedAttempts = backoff.attempts();\n      setStatus(everConnected ? \"reconnecting\" : \"connecting\");\n      diagnostics({\n        level: backoff.isAlarming() ? \"error\" : \"warn\",\n        code: \"channel.reconnect.scheduled\",\n        topic: spec.topic,\n        message: `Channel dropped (${cause}); retrying in ${delay}ms (attempt ${backoff.attempts()}).`,\n        remedy: backoff.isAlarming()\n          ? (spec.postgresChanges?.length\n            ? \"Check the connection and session, then the table's supabase_realtime publication, SELECT policy, and schema cache.\"\n            : \"Check the connection, session, and Realtime broadcast/presence authorization for this channel.\")\n          : \"The channel will retry automatically; configured backfill runs after reconnection.\",\n        detail: {\n          attempt: backoff.attempts(), delay,\n          ...(error ? { providerError: { name: error.name.slice(0, 128), message: error.message.slice(0, 1024) } } : {}),\n        },\n      });\n      retryTimer = timers.setTimeout(() => {\n        retryTimer = null;\n        teardownChannel();\n        connect();\n      }, delay);\n    };\n\n    const keyFor = (\n      source: \"broadcast\" | \"postgres_changes\",\n      payload: unknown,\n      fallback: string,\n    ): string => spec.eventKey?.(source, payload) ?? fallback;\n\n    const attachPostgresChanges = (target: RealtimeChannelLike): RealtimeChannelLike => {\n      let current = target;\n      for (const binding of spec.postgresChanges ?? []) {\n        const updatedAtField = binding.updatedAtField ?? \"updated_at\";\n        const updatedByField = binding.updatedByField ?? \"updated_by\";\n        const revisionField =\n          binding.revisionField === undefined ? \"version\" : binding.revisionField;\n        current = current.on(\n          \"postgres_changes\",\n          {\n            event: binding.event,\n            schema: binding.schema,\n            table: binding.table,\n            ...(binding.filter !== undefined ? { filter: binding.filter } : {}),\n          },\n          (payload: PostgresChangePayload) => {\n            metrics.recordEvent();\n            const rowSource =\n              payload.eventType === \"DELETE\" ? payload.old : (payload.new ?? payload.old);\n            const row =\n              rowSource !== null && typeof rowSource === \"object\"\n                ? (rowSource as Record<string, unknown>)\n                : null;\n            const rowId = row ? binding.rowId?.(row) : undefined;\n\n            let origin: OriginVerdict = {\n              origin: \"remote\",\n              reason: \"no-local-record\",\n              localWritePending: false,\n            };\n\n            // A DELETE carries the row's LAST state (REPLICA IDENTITY FULL):\n            // its revision and content equal what we hold, so the ledger would\n            // read it as an echo of our own write and the row would live on in\n            // every list until a backfill. Removing a row is idempotent —\n            // deliver every DELETE, ours included.\n            if (echoSuppression && row && rowId !== undefined && payload.eventType !== \"DELETE\") {\n              const metadata = row[\"metadata\"];\n              const requestId =\n                metadata !== null && typeof metadata === \"object\"\n                  ? ((metadata as Record<string, unknown>)[\"request_id\"] as string | undefined)\n                  : undefined;\n              origin = ledger.classify({\n                table: `${binding.schema}.${binding.table}`,\n                id: rowId,\n                updatedAt: row[updatedAtField] as string | null | undefined,\n                ...(binding.fingerprint !== undefined\n                  ? { fingerprint: binding.fingerprint(row) }\n                  : {}),\n                updatedBy: row[updatedByField] as string | null | undefined,\n                requestId: requestId ?? null,\n                revision:\n                  revisionField === null\n                    ? null\n                    : canonicalRevision(row[revisionField] as number | null | undefined) ?? null,\n              });\n              if (origin.origin === \"own-echo\") {\n                metrics.suppressedEcho += 1;\n                return;\n              }\n              if (origin.origin === \"stale\") {\n                metrics.stale += 1;\n                return;\n              }\n            }\n\n            const fallbackKey = `pg:${binding.schema}.${binding.table}:${rowId ?? \"?\"}:${\n              payload.eventType ?? \"*\"\n            }:${payload.commit_timestamp ?? String(metrics.received)}`;\n            if (!dedupe.admit(keyFor(\"postgres_changes\", payload, fallbackKey))) {\n              metrics.deduped += 1;\n              return;\n            }\n\n            queue.push(async () => {\n              metrics.delivered += 1;\n              await binding.onChange({ payload, row, origin });\n            });\n          },\n        );\n      }\n      return current;\n    };\n\n    const attachBroadcast = (target: RealtimeChannelLike): RealtimeChannelLike => {\n      let current = target;\n      for (const binding of spec.broadcast ?? []) {\n        current = current.on(\n          \"broadcast\",\n          { event: binding.event },\n          (raw: { event: string; payload?: unknown }) => {\n            metrics.recordEvent();\n            if (rawWire !== null) {\n              // No envelope is read on a raw wire — not even from a payload\n              // that happens to look enveloped. The peer owns this shape.\n              if (echoSuppression && rawWire.isOwnMessage?.(raw.payload) === true) {\n                metrics.suppressedEcho += 1;\n                return;\n              }\n              const rawMessage: BroadcastMessage<unknown> = {\n                event: raw.event,\n                data: raw.payload,\n                from: \"unknown\",\n                eventId: `${raw.event}:${metrics.received}`,\n                sentAt: timers.now(),\n                enveloped: false,\n              };\n              if (!dedupe.admit(keyFor(\"broadcast\", raw.payload, rawMessage.eventId))) {\n                metrics.deduped += 1;\n                return;\n              }\n              queue.push(async () => {\n                metrics.delivered += 1;\n                await binding.onMessage(rawMessage);\n              });\n              return;\n            }\n            const envelope = readEnvelope(raw.payload);\n            if (echoSuppression && envelope !== null && envelope.cid === clientId) {\n              // Belt AND braces: the channel is opened with `broadcast.self`\n              // off, but a consumer's own second subscription, a replayed\n              // message, or a foreign sender relaying our envelope all get\n              // here anyway. The id check is the one that always holds.\n              metrics.suppressedEcho += 1;\n              return;\n            }\n            const message: BroadcastMessage<unknown> = envelope\n              ? {\n                  event: raw.event,\n                  data: envelope.data,\n                  from: envelope.cid,\n                  eventId: envelope.eid,\n                  sentAt: envelope.ts,\n                  enveloped: true,\n                }\n              : {\n                  event: raw.event,\n                  data: raw.payload,\n                  from: \"unknown\",\n                  eventId: `${raw.event}:${metrics.received}`,\n                  sentAt: timers.now(),\n                  enveloped: false,\n                };\n            if (!dedupe.admit(keyFor(\"broadcast\", raw.payload, message.eventId))) {\n              metrics.deduped += 1;\n              return;\n            }\n            queue.push(async () => {\n              metrics.delivered += 1;\n              await binding.onMessage(message);\n            });\n          },\n        );\n      }\n      return current;\n    };\n\n    const attachPresence = (target: RealtimeChannelLike): RealtimeChannelLike => {\n      if (!presence) return target;\n      let current = target;\n      for (const event of [\"sync\", \"join\", \"leave\"] as const) {\n        current = current.on(\"presence\", { event }, () => {\n          metrics.recordEvent();\n          if (channel) presence.onPresenceEvent(channel);\n        });\n      }\n      return current;\n    };\n\n    const connect = (): void => {\n      if (closed) return;\n      // A UNIQUE topic per connection attempt — for POSTGRES CHANGES ONLY.\n      // Supabase dedupes channels by topic and `removeChannel` is async, so a\n      // remount or a retry that reuses the base topic gets handed the previous,\n      // already-subscribed channel — and `.on()` on it throws or silently binds\n      // nothing. A broadcast/presence channel cannot buy that safety with a\n      // unique topic (the topic is its routing key), so it goes through the room\n      // registry, which buys the same safety by sharing and ref-counting.\n      const instanceTopic = sharedRoom ? spec.topic : uniqueChannelTopic(spec.topic);\n      let next = channelClient.channel(instanceTopic, {\n        config: {\n          broadcast: { self: !echoSuppression, ack: false },\n          ...(presence ? { presence: { key: presence.key } } : {}),\n          ...(spec.private === true ? { private: true } : {}),\n        },\n      });\n      // A PRIVATE channel is authorized against `realtime.messages` RLS at JOIN\n      // time, so the socket has to be carrying the current access token first.\n      // Subscribing without it is refused and the channel then receives nothing\n      // while reporting itself healthy — the exact silent failure this package\n      // exists to delete.\n      if (spec.private === true) {\n        const socket = client.realtime;\n        if (socket?.setAuth === undefined) {\n          diagnostics({\n            level: \"warn\",\n            code: \"channel.private.unsupported\",\n            topic: spec.topic,\n            message:\n              \"This channel declared `private: true`, but the Supabase client \" +\n              \"exposes no `realtime.setAuth`. The join will be attempted \" +\n              \"unauthorized and the server will deliver nothing.\",\n            remedy:\n              \"Pass a real @supabase/supabase-js client to <RealtimeProvider>, \" +\n              \"or drop `private` if the topic is not RLS-authorized.\",\n          });\n        } else {\n          try {\n            const result = socket.setAuth();\n            if (result instanceof Promise) {\n              void result.catch((error: unknown) => {\n                diagnostics({\n                  level: \"error\",\n                  code: \"channel.private.auth-failed\",\n                  topic: spec.topic,\n                  message:\n                    \"Could not attach the access token to the realtime socket, \" +\n                    \"so this private channel's join will be refused.\",\n                  remedy:\n                    \"Check the Supabase session is valid; the channel will retry \" +\n                    \"on the next reconnect.\",\n                  detail: { error: String(error) },\n                });\n              });\n            }\n          } catch (error) {\n            diagnostics({\n              level: \"error\",\n              code: \"channel.private.auth-failed\",\n              topic: spec.topic,\n              message:\n                \"Could not attach the access token to the realtime socket, so \" +\n                \"this private channel's join will be refused.\",\n              remedy:\n                \"Check the Supabase session is valid; the channel will retry on \" +\n                \"the next reconnect.\",\n              detail: { error: String(error) },\n            });\n          }\n        }\n      }\n      next = attachPostgresChanges(next);\n      next = attachBroadcast(next);\n      next = attachPresence(next);\n      channel = next;\n\n      next.subscribe((subscribeStatus: string, error?: Error) => {\n        if (closed) return;\n        // `removeChannel` is asynchronous. Supabase may deliver the removed\n        // channel's terminal CLOSED callback after its replacement is already\n        // live. That callback belongs to the old connection generation; if it\n        // is allowed through, it arms another retry and tears down the healthy\n        // replacement, creating a self-sustaining reconnect loop.\n        if (channel !== next) return;\n        if (subscribeStatus === \"SUBSCRIBED\") {\n          const rejoined = everConnected;\n          const gapMs = lastHealthyAt === null ? null : timers.now() - lastHealthyAt;\n          everConnected = true;\n          lastHealthyAt = timers.now();\n          backoff.markConnected();\n          metrics.subscribedAt = timers.now();\n          metrics.failedAttempts = 0;\n          setStatus(\"connected\");\n          if (channel) presence?.onSubscribed(channel);\n          if (rejoined) {\n            metrics.reconnects += 1;\n            // THE WHOLE POINT: the events during the gap are gone. Re-read.\n            runBackfill(\"reconnect\", gapMs);\n          }\n          return;\n        }\n        if (\n          subscribeStatus === \"CHANNEL_ERROR\" ||\n          subscribeStatus === \"TIMED_OUT\" ||\n          subscribeStatus === \"CLOSED\"\n        ) {\n          presence?.onDisconnected();\n          scheduleReconnect(subscribeStatus, error);\n        }\n      });\n    };\n\n    connect();\n\n    // Same-tab wiring (see `tabPeers`). Raw-wire channels belong to a foreign\n    // peer's shape and never fan out; every sharedRoom holder still takes part\n    // in leadership, so \"one autosaver per tab\" holds for them too.\n    const peer: TabPeer = {\n      deliver(event: string, envelope: MatrxBroadcastEnvelope<unknown>): void {\n        if (closed || rawWire !== null || status !== \"connected\") return;\n        const message: BroadcastMessage<unknown> = {\n          event,\n          data: envelope.data,\n          from: envelope.cid,\n          eventId: envelope.eid,\n          sentAt: envelope.ts,\n          enveloped: true,\n        };\n        for (const binding of spec.broadcast ?? []) {\n          if (binding.event !== event) continue;\n          metrics.recordEvent();\n          if (!dedupe.admit(keyFor(\"broadcast\", envelope, message.eventId))) {\n            metrics.deduped += 1;\n            continue;\n          }\n          queue.push(async () => {\n            metrics.delivered += 1;\n            await binding.onMessage(message);\n          });\n        }\n      },\n      leaderChanged(isLeader: boolean): void {\n        if (closed) return;\n        try {\n          spec.onLeaderChange?.(isLeader);\n        } catch (error) {\n          diagnostics({\n            level: \"error\",\n            code: \"channel.leader.handler-threw\",\n            topic: spec.topic,\n            message: `onLeaderChange threw: ${String((error as Error)?.message ?? error)}`,\n            remedy: \"A leadership handler must not throw; the channel is unaffected.\",\n          });\n        }\n      },\n    };\n    if (sharedRoom) {\n      const list = tabPeers.get(spec.topic) ?? [];\n      list.push(peer);\n      tabPeers.set(spec.topic, list);\n    }\n    const leaveTab = (): void => {\n      const list = tabPeers.get(spec.topic);\n      if (!list) return;\n      const at = list.indexOf(peer);\n      if (at === -1) return;\n      list.splice(at, 1);\n      if (list.length === 0) {\n        tabPeers.delete(spec.topic);\n        return;\n      }\n      if (at === 0) list[0]?.leaderChanged(true);\n    };\n\n    const close = (): void => {\n      if (closed) return;\n      closed = true;\n      leaveTab();\n      if (retryTimer !== null) timers.clearTimeout(retryTimer);\n      retryTimer = null;\n      presence?.dispose();\n      queue.dispose();\n      teardownChannel();\n      // Let go of the shared room. It survives one turn for a React-19 remount,\n      // then the last holder's release removes the underlying channel.\n      roomMember?.release();\n      unregisterMetrics();\n      setStatus(\"disconnected\");\n      channels.delete(internal);\n    };\n\n    const handle: ChannelHandle<TPresence> = {\n      topic: spec.topic,\n      status: () => status,\n      send<TData>(event: string, data: TData): void {\n        if (!channel || status !== \"connected\") {\n          diagnostics({\n            level: \"warn\",\n            code: \"channel.send.not-connected\",\n            topic: spec.topic,\n            message: `Broadcast \"${event}\" was not sent: the channel is ${status}.`,\n            remedy:\n              \"Broadcast is ephemeral and has no outbox by design. Anything that must survive a \" +\n              \"disconnect belongs in a database write, which the receiver picks up via Postgres \" +\n              \"Changes or the next reconcile.\",\n          });\n          return;\n        }\n        if (rawWire !== null) {\n          // VERBATIM. Byte-identical to what the foreign peer's own sender\n          // would have put on the wire — that is the entire contract of raw\n          // mode, and `raw-wire.test.ts` proves it against a real fixture.\n          channel.send({ type: \"broadcast\", event, payload: data });\n          return;\n        }\n        const envelope: MatrxBroadcastEnvelope<TData> = {\n          v: 1,\n          cid: clientId,\n          eid: randomId(),\n          ts: timers.now(),\n          data,\n        };\n        channel.send({ type: \"broadcast\", event, payload: envelope });\n        // The wire never echoes this to siblings in THIS tab (self:false, one\n        // shared client id) — hand it to them directly.\n        if (sharedRoom) fanOutLocal(spec.topic, peer, event, envelope as MatrxBroadcastEnvelope<unknown>);\n      },\n      isLocalLeader: () => !closed && (tabPeers.get(spec.topic)?.[0] === peer || !sharedRoom),\n      presence: presence as PresenceRoom<TPresence> | null,\n      requestBackfill,\n      close,\n    };\n\n    const internal: InternalChannel = {\n      handle: handle as unknown as ChannelHandle<Record<string, unknown>>,\n      wake(hiddenForMs: number): void {\n        if (closed) return;\n        // A tab that slept may hold a socket the server already gave up on, and\n        // it will not learn that until it tries. Reconcile now, and force a\n        // fresh join if the channel is not demonstrably healthy.\n        runBackfill(\"wake\", hiddenForMs);\n        if (abandoned) {\n          abandoned = false;\n          backoff.reset();\n          metrics.failedAttempts = 0;\n        }\n        if (status !== \"connected\") {\n          if (retryTimer !== null) timers.clearTimeout(retryTimer);\n          retryTimer = null;\n          teardownChannel();\n          connect();\n        }\n      },\n      networkRestored(): void {\n        if (closed || status === \"connected\") return;\n        if (retryTimer !== null) timers.clearTimeout(retryTimer);\n        retryTimer = null;\n        abandoned = false;\n        backoff.reset();\n        metrics.failedAttempts = 0;\n        teardownChannel();\n        connect();\n        runBackfill(\"network-restored\", lastHealthyAt === null ? null : timers.now() - lastHealthyAt);\n      },\n      markOffline(): void {\n        if (closed) return;\n        presence?.onDisconnected();\n        setStatus(everConnected ? \"reconnecting\" : \"connecting\");\n      },\n      status: () => status,\n      close,\n    };\n    channels.add(internal);\n\n    return handle;\n  }\n\n  return {\n    clientId,\n    ledger,\n    open,\n    health(): ConnectionHealth {\n      if (channels.size === 0) return \"live\";\n      let connected = 0;\n      for (const channel of channels) {\n        if (channel.status() === \"connected\") connected += 1;\n      }\n      if (connected === channels.size) return \"live\";\n      if (connected === 0) return \"down\";\n      return \"degraded\";\n    },\n    backfillAll(reason: BackfillReason = \"manual\"): void {\n      for (const channel of channels) channel.handle.requestBackfill(reason);\n    },\n    openChannelCount: () => channels.size,\n    dispose(): void {\n      if (disposed) return;\n      disposed = true;\n      unsubscribeEnvironment();\n      for (const channel of [...channels]) channel.close();\n      channels.clear();\n      // No grace window on dispose: a manager that is gone must leave nothing\n      // joined, even if a room's deferred teardown has not fired yet.\n      rooms.disposeAll();\n    },\n  };\n}\n\nfunction readEnvelope(payload: unknown): MatrxBroadcastEnvelope | null {\n  if (payload === null || typeof payload !== \"object\") return null;\n  const record = payload as Record<string, unknown>;\n  if (record[\"v\"] !== 1) return null;\n  if (typeof record[\"cid\"] !== \"string\") return null;\n  if (typeof record[\"eid\"] !== \"string\") return null;\n  return {\n    v: 1,\n    cid: record[\"cid\"],\n    eid: record[\"eid\"],\n    ts: typeof record[\"ts\"] === \"number\" ? record[\"ts\"] : 0,\n    data: record[\"data\"],\n  };\n}\n","/**\n * THE AMBIENT MANAGER — the door for code that is not a React component.\n *\n * `<RealtimeProvider>` builds exactly ONE manager per app, and `useRealtimeManager()`\n * hands it to components. But a large share of realtime ownership in a real app\n * is deliberately NOT in a component:\n *\n *  - Redux middleware, which is where this doctrine says a feature's\n *    subscription SHOULD live (a component effect re-subscribes on identity\n *    churn and multiplies channels across mounts).\n *  - Plain service modules and thunks that own a module-level subscription.\n *\n * None of those can call a hook. Before this module they had exactly two\n * options, and both are defects:\n *\n *  1. `createRealtimeManager(...)` of their own — a SECOND manager, and\n *     therefore a second write ledger, so the app's own optimistic writes look\n *     REMOTE to that manager and every one of them costs a refetch. This is the\n *     failure the \"one provider per app\" rule exists to prevent, and a\n *     middleware author reaching for the constructor cannot see it happening.\n *  2. Hand-roll `client.channel(...)` — which is the thing this package exists\n *     to delete.\n *\n * So the provider PUBLISHES its manager here, and non-React owners read it.\n * There is no second manager and no second ledger: this is a pointer to the one\n * the provider already built.\n *\n * WHY IT IS SUBSCRIBABLE. The manager's lifetime is `client` + `actorId`: it is\n * `null` before auth resolves and it is REBUILT when the signed-in user changes\n * (and disposed, taking every channel with it). A middleware that read\n * `currentRealtimeManager()` once at module load would hold null forever on a\n * cold start, and one that ignored the change would hold a disposed manager\n * after a user switch — silently, with a screen that looks healthy. So the\n * contract is `onRealtimeManagerChange(listener)`: open on arrival, drop your\n * handle on departure. `subscribeToRealtimeManager` below is the whole recipe\n * in one call, so no consumer has to remember the shape.\n *\n * No module-level mutable state (the package ban): the registry lives in a\n * `globalThis` slot, shared across the ESM and CJS module graphs.\n */\n\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport type { ChannelHandle, ChannelSpec, RealtimeManager } from \"./manager\";\n\ninterface AmbientRegistry {\n  manager: RealtimeManager | null;\n  listeners: Set<(manager: RealtimeManager | null) => void>;\n}\n\nfunction registry(): AmbientRegistry {\n  return globalSlot<AmbientRegistry>(\"ai-matrx.realtime.ambient-manager\", () => ({\n    manager: null,\n    listeners: new Set(),\n  }));\n}\n\n/**\n * Publish the app's manager. Called by `<RealtimeProvider>` ONLY — a consumer\n * that calls this is claiming to be the app's provider, which is the thing the\n * one-provider rule forbids.\n */\nexport function setAmbientRealtimeManager(manager: RealtimeManager | null): void {\n  const state = registry();\n  if (state.manager === manager) return;\n  state.manager = manager;\n  for (const listener of Array.from(state.listeners)) listener(manager);\n}\n\n/**\n * The app's manager right now, or `null` when no provider is mounted or its\n * Supabase client has not resolved yet.\n *\n * Reading this ONCE is almost always wrong — see the header. Prefer\n * {@link subscribeToRealtimeManager}.\n */\nexport function currentRealtimeManager(): RealtimeManager | null {\n  return registry().manager;\n}\n\n/**\n * Observe the app's manager. The listener fires immediately with the current\n * value (which may be `null`), and again on every change. Returns an\n * unsubscribe.\n */\nexport function onRealtimeManagerChange(\n  listener: (manager: RealtimeManager | null) => void,\n): () => void {\n  const state = registry();\n  state.listeners.add(listener);\n  listener(state.manager);\n  return () => {\n    state.listeners.delete(listener);\n  };\n}\n\n/**\n * THE RECIPE, IN ONE CALL — for a Redux middleware or module-level owner that\n * wants one channel open for as long as the app has a manager.\n *\n * Opens `spec` when a manager exists, closes the handle when it goes away, and\n * re-opens on the new manager when the provider rebuilds one. Returns a stop\n * function that closes the channel and unsubscribes.\n *\n * `spec` is a FACTORY so the caller can capture whatever it needs at open time\n * (the current user id, a fresh `dispatch`) rather than at module load.\n */\nexport function subscribeToRealtimeManager(\n  spec: (manager: RealtimeManager) => ChannelSpec,\n): () => void {\n  let handle: ChannelHandle | null = null;\n  const stop = onRealtimeManagerChange((manager) => {\n    if (handle) {\n      handle.close();\n      handle = null;\n    }\n    if (manager) handle = manager.open(spec(manager));\n  });\n  return () => {\n    stop();\n    if (handle) {\n      handle.close();\n      handle = null;\n    }\n  };\n}\n","\"use client\";\n\n/**\n * The React Native environment bridge.\n *\n * A phone is a far harsher realtime host than a browser tab: the OS suspends\n * the app on background, kills the socket without telling JS, and hands the app\n * back \"active\" with a WebSocket object that still LOOKS open and will never\n * deliver another event. That is the mobile shape of the same silent-stale\n * failure the browser bridge handles — so `background → active` maps onto the\n * same wake path, which reconciles from the database and re-joins any channel\n * that is not demonstrably healthy.\n *\n * Reachability: RN has no `navigator.onLine` worth trusting and\n * `@react-native-community/netinfo` is a native module we refuse to force on\n * every consumer (it would make this package un-installable in Expo Go without\n * a rebuild). So online-ness is reported optimistically and the ONE consequence\n * — skipping a backoff after a network restore — is simply not available on\n * native; reconnect still runs on its normal schedule, and the wake path covers\n * the case that actually matters. A host that already has NetInfo can inject a\n * `reachability` probe and get the fast path back.\n */\n\nimport { AppState, type AppStateStatus } from \"react-native\";\nimport type { EnvironmentBridge, EnvironmentSignal } from \"../core/environment\";\n\nexport interface ReactNativeEnvironmentOptions {\n  /**\n   * Optional reachability source, e.g. a NetInfo subscription the host already\n   * owns. When supplied, network-restore reconnects skip the backoff delay.\n   */\n  reachability?: {\n    isOnline: () => boolean;\n    subscribe: (listener: (online: boolean) => void) => () => void;\n  };\n}\n\nexport function createReactNativeEnvironment(\n  options: ReactNativeEnvironmentOptions = {},\n): EnvironmentBridge {\n  const { reachability } = options;\n  return {\n    name: \"react-native\",\n    isVisible: () => AppState.currentState === \"active\",\n    isOnline: () => reachability?.isOnline() ?? true,\n    subscribe(listener: (signal: EnvironmentSignal) => void) {\n      const subscription = AppState.addEventListener(\"change\", (state: AppStateStatus) => {\n        listener(state === \"active\" ? \"visible\" : \"hidden\");\n      });\n      const unsubscribeReachability = reachability?.subscribe((online) => {\n        listener(online ? \"online\" : \"offline\");\n      });\n      return () => {\n        subscription.remove();\n        unsubscribeReachability?.();\n      };\n    },\n  };\n}\n","\"use client\";\n\n/**\n * `useChannel` — one hook, one channel, every hard part already handled.\n *\n * The subscription is keyed on the channel's SHAPE (topic + binding\n * declarations), never on handler identity. That is deliberate: a new inline\n * `onChange` closure on every render must not tear down and rebuild the\n * subscription — the \"why does my realtime reconnect forever\" bug. Handlers are\n * read through a ref at event time, which is also why they may safely close over\n * fresh props.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useRealtimeManager } from \"./provider\";\nimport type {\n  BackfillReason,\n  ChannelHandle,\n  ChannelSpec,\n} from \"../core/manager\";\nimport type { RealtimeStatus } from \"../core/types\";\n\nexport interface UseChannelOptions {\n  /** Turn the subscription off without unmounting. Default `true`. */\n  enabled?: boolean;\n}\n\nexport interface UseChannelResult<\n  TPresence extends Record<string, unknown> = Record<string, unknown>,\n> {\n  status: RealtimeStatus;\n  /** Send a broadcast on this channel, enveloped so receivers suppress the echo. */\n  send: <TData>(event: string, data: TData) => void;\n  requestBackfill: (reason?: BackfillReason) => void;\n  /** The live handle, or `null` while disconnected/disabled. */\n  channel: ChannelHandle<TPresence> | null;\n}\n\nfunction shapeOf(spec: ChannelSpec<Record<string, unknown>>): string {\n  const pg = (spec.postgresChanges ?? [])\n    .map((b) => `${b.event}:${b.schema}.${b.table}:${b.filter ?? \"\"}`)\n    .join(\"|\");\n  const broadcast = (spec.broadcast ?? []).map((b) => b.event).join(\"|\");\n  const presence = spec.presence ? `presence:${spec.presence.key ?? \"self\"}` : \"\";\n  const wire = spec.wire?.mode ?? \"matrx\";\n  return `${spec.topic}::${pg}::${broadcast}::${presence}::echo=${\n    spec.echoSuppression !== false\n  }::wire=${wire}`;\n}\n\nexport function useChannel<\n  TPresence extends Record<string, unknown> = Record<string, unknown>,\n>(\n  spec: ChannelSpec<TPresence> | null,\n  options: UseChannelOptions = {},\n): UseChannelResult<TPresence> {\n  const { enabled = true } = options;\n  const manager = useRealtimeManager();\n  const [status, setStatus] = useState<RealtimeStatus>(\"disconnected\");\n  // The handle lives in BOTH a ref and state: the ref keeps `send` stable and\n  // correct inside callbacks, and the state makes dependent hooks (typing,\n  // presence) re-run the moment the channel exists.\n  const [handleState, setHandleState] = useState<ChannelHandle<TPresence> | null>(null);\n  const handleRef = useRef<ChannelHandle<TPresence> | null>(null);\n  const specRef = useRef(spec);\n  specRef.current = spec;\n\n  const shape = spec ? shapeOf(spec as ChannelSpec<Record<string, unknown>>) : \"\";\n  const active = manager !== null && spec !== null && enabled;\n\n  useEffect(() => {\n    if (!manager || !active) {\n      setStatus(\"disconnected\");\n      setHandleState(null);\n      return undefined;\n    }\n    const declared = specRef.current;\n    if (!declared) return undefined;\n\n    // Rebuild the spec so every callback dereferences the LATEST render's\n    // handlers through specRef, while the subscription itself stays keyed on\n    // the shape string above.\n    const live: ChannelSpec<TPresence> = {\n      topic: declared.topic,\n      ...(declared.echoSuppression !== undefined\n        ? { echoSuppression: declared.echoSuppression }\n        : {}),\n      ...(declared.presence !== undefined ? { presence: declared.presence } : {}),\n      ...(declared.postgresChanges !== undefined\n        ? {\n            postgresChanges: declared.postgresChanges.map((binding, index) => ({\n              ...binding,\n              onChange: (delivery) =>\n                specRef.current?.postgresChanges?.[index]?.onChange(delivery),\n            })),\n          }\n        : {}),\n      ...(declared.broadcast !== undefined\n        ? {\n            broadcast: declared.broadcast.map((binding, index) => ({\n              ...binding,\n              onMessage: (message) =>\n                specRef.current?.broadcast?.[index]?.onMessage(message),\n            })),\n          }\n        : {}),\n      // Only forward a backfill door when the caller declared one — the manager\n      // WARNS about a channel that has none, and a wrapper that always supplies\n      // an empty one would silence exactly the warning that matters.\n      ...(declared.onBackfill !== undefined\n        ? { onBackfill: (context) => specRef.current?.onBackfill?.(context) }\n        : {}),\n      onStatusChange: (next) => {\n        setStatus(next);\n        specRef.current?.onStatusChange?.(next);\n      },\n      ...(declared.eventKey !== undefined\n        ? { eventKey: (source, payload) => specRef.current?.eventKey?.(source, payload) }\n        : {}),\n      // The wire mode is part of the shape key, so it is stable for the life of\n      // this subscription — but `isOwnMessage` is a caller closure and must be\n      // dereferenced late, exactly like the handlers above. Forwarding\n      // `declared.wire` by identity would pin the FIRST render's closure.\n      ...(declared.wire !== undefined\n        ? {\n            wire:\n              declared.wire.mode === \"raw\"\n                ? {\n                    mode: \"raw\" as const,\n                    ...(declared.wire.acceptEchoFromSelf !== undefined\n                      ? { acceptEchoFromSelf: declared.wire.acceptEchoFromSelf }\n                      : {}),\n                    ...(declared.wire.isOwnMessage !== undefined\n                      ? {\n                          isOwnMessage: (payload: unknown) => {\n                            const current = specRef.current?.wire;\n                            return current !== undefined && current.mode === \"raw\"\n                              ? (current.isOwnMessage?.(payload) ?? false)\n                              : false;\n                          },\n                        }\n                      : {}),\n                  }\n                : { mode: \"matrx\" as const },\n          }\n        : {}),\n    };\n\n    const handle = manager.open<TPresence>(live);\n    handleRef.current = handle;\n    setHandleState(handle);\n    setStatus(handle.status());\n\n    return () => {\n      handleRef.current = null;\n      setHandleState(null);\n      handle.close();\n    };\n  }, [manager, active, shape]);\n\n  return {\n    status,\n    send: <TData,>(event: string, data: TData): void => {\n      handleRef.current?.send(event, data);\n    },\n    requestBackfill: (reason?: BackfillReason): void => {\n      handleRef.current?.requestBackfill(reason);\n    },\n    channel: handleState,\n  };\n}\n","\"use client\";\n\n/**\n * `usePresence` — who is here, with ghosts already handled.\n *\n * The roster comes back debounced (a rejoin emits one `sync` plus one `join`\n * per member; re-rendering once per member is the O(N²) storm this package\n * prevents) and swept (a crashed client cannot outlive one expiry window).\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useChannel, type UseChannelOptions } from \"./use-channel\";\nimport type { PresenceMember, PresenceOptions } from \"../core/presence\";\nimport type { ChannelSpec } from \"../core/manager\";\nimport type { RealtimeStatus } from \"../core/types\";\n\nexport interface UsePresenceArgs<TState extends Record<string, unknown>> {\n  /** Build it with a channel namespace — never a hand-written string. */\n  topic: string;\n  presence: PresenceOptions<TState>;\n  /** Extra bindings to share this channel with (R1: one channel per feature). */\n  spec?: Omit<ChannelSpec<TState>, \"topic\" | \"presence\">;\n}\n\nexport interface UsePresenceResult<TState extends Record<string, unknown>> {\n  members: readonly PresenceMember<TState>[];\n  /** Everyone but this client. The usual thing a UI wants. */\n  others: readonly PresenceMember<TState>[];\n  status: RealtimeStatus;\n  /** Publish new state for this client (merged, not replaced). */\n  update: (state: Partial<TState>) => void;\n}\n\nexport function usePresence<TState extends Record<string, unknown>>(\n  args: UsePresenceArgs<TState>,\n  options: UseChannelOptions = {},\n): UsePresenceResult<TState> {\n  const [members, setMembers] = useState<readonly PresenceMember<TState>[]>([]);\n\n  const spec: ChannelSpec<TState> = {\n    ...(args.spec ?? {}),\n    topic: args.topic,\n    presence: { ...args.presence, onChange: setMembers },\n    // Presence is ephemeral. Clearing before the channel's fresh sync is its\n    // honest reconnect behavior and prevents a departed peer from surviving a\n    // gap on screen. Preserve any extra holder backfill supplied by the host.\n    onBackfill: async (context) => {\n      setMembers([]);\n      await args.spec?.onBackfill?.(context);\n    },\n  };\n\n  const { channel, status } = useChannel<TState>(spec, options);\n\n  // Republish when the caller's state object changes value (not identity).\n  const stateKey = JSON.stringify(args.presence.state);\n  const stateRef = useRef(args.presence.state);\n  stateRef.current = args.presence.state;\n  useEffect(() => {\n    channel?.presence?.update(stateRef.current);\n  }, [channel, stateKey]);\n\n  return {\n    members,\n    others: members.filter((member) => !member.isSelf),\n    status,\n    update: (state: Partial<TState>): void => {\n      channel?.presence?.update(state);\n    },\n  };\n}\n","\"use client\";\n\n/**\n * `useTyping` — the whole typing-indicator lifecycle in one hook.\n *\n * The consumer calls `onKeystroke()` on every input event and renders\n * `typists`. Nothing else. Start, heartbeat, stop, expiry, and the\n * one-presence-update-per-character trap are all inside (see `core/typing.ts`\n * for why a stop message can never be the mechanism).\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useChannel, type UseChannelOptions } from \"./use-channel\";\nimport { createTypingTracker, type TypingMember, type TypingOptions, type TypingState } from \"../core/typing\";\nimport type { PresenceRoom } from \"../core/presence\";\nimport type { RealtimeStatus } from \"../core/types\";\n\nexport interface UseTypingArgs {\n  /** Build it with a channel namespace — never a hand-written string. */\n  topic: string;\n  /**\n   * What other clients should know about this typist (userId, display name,\n   * avatar). It rides the presence state and comes back on every `TypingMember`.\n   */\n  identity: Record<string, unknown>;\n  /** Presence key for this client. Defaults to the client session id. */\n  presenceKey?: string;\n  /**\n   * Whether this holder ANNOUNCES its own typing. Default true.\n   *\n   * A composer publishes; a typists label only reads. One channel carries one\n   * presence entry per key, so mounting both as publishers on one topic makes\n   * them overwrite each other — the read-only one passes `publish: false`.\n   */\n  publish?: boolean;\n  typing?: TypingOptions;\n}\n\nexport interface UseTypingResult {\n  typists: readonly TypingMember[];\n  /** Call on EVERY keystroke. Self-throttling. */\n  onKeystroke: () => void;\n  /** Explicit stop — on send, blur, or navigation. */\n  stopTyping: () => void;\n  status: RealtimeStatus;\n}\n\nexport function useTyping(\n  args: UseTypingArgs,\n  options: UseChannelOptions = {},\n): UseTypingResult {\n  const [typists, setTypists] = useState<readonly TypingMember[]>([]);\n  const trackerRef = useRef<ReturnType<typeof createTypingTracker> | null>(null);\n\n  const identityKey = JSON.stringify(args.identity);\n  const { channel, status } = useChannel<TypingState>(\n    {\n      topic: args.topic,\n      presence: {\n        ...(args.presenceKey !== undefined ? { key: args.presenceKey } : {}),\n        ...(args.publish !== undefined ? { publish: args.publish } : {}),\n        state: { typing: false, ...args.identity } as TypingState,\n      },\n      // Typing is an ephemeral lease, not durable data to fetch. On reconnect\n      // clear the stale local roster; the channel's presence sync immediately\n      // repopulates the peers that are actually still here.\n      onBackfill: () => setTypists([]),\n    },\n    options,\n  );\n\n  const typingOptions = args.typing;\n  const typingKey = JSON.stringify(typingOptions ?? {});\n\n  useEffect(() => {\n    const room = channel?.presence as PresenceRoom<TypingState> | null | undefined;\n    if (!room) {\n      setTypists([]);\n      return undefined;\n    }\n    const tracker = createTypingTracker(room, {\n      ...(typingOptions ?? {}),\n      onChange: setTypists,\n    });\n    trackerRef.current = tracker;\n    return () => {\n      trackerRef.current = null;\n      // Announce the stop on unmount as a courtesy; the lease expiry is what\n      // actually guarantees the indicator clears.\n      tracker.stop();\n      tracker.dispose();\n    };\n    // typingOptions is compared by VALUE (typingKey): a fresh options object on\n    // every render must not rebuild the tracker and lose the lease table.\n  }, [channel, typingKey, identityKey]);\n\n  return {\n    typists,\n    onKeystroke: (): void => trackerRef.current?.keystroke(),\n    stopTyping: (): void => trackerRef.current?.stop(),\n    status,\n  };\n}\n","/**\n * THE TYPING LIFECYCLE (R5) — start, heartbeat, stop, timeout.\n *\n * \"X is typing…\" that never goes away is the most-reported realtime bug in\n * every chat product ever built, and it has exactly one cause: the STOP is an\n * event, and events get lost. The tab closed, the network dropped, the user\n * navigated away mid-word — and the last thing anyone heard was \"started\".\n *\n * The cure is to stop treating \"typing\" as a latch and treat it as a LEASE:\n *\n *  - Typing is re-announced every `heartbeatMs` while the user is typing.\n *  - A remote typist is shown only while their lease is fresh, measured on OUR\n *    clock at OUR receipt time — so no clock skew, and no dependence on a stop\n *    message ever arriving.\n *  - This client stops advertising `idleMs` after the last keystroke, and sends\n *    an explicit stop as a courtesy, never as the mechanism.\n *\n * A crashed sender's indicator therefore clears in at most one expiry window,\n * automatically, with no message from anyone.\n */\n\nimport type { PresenceMember, PresenceRoom, PresenceTimers } from \"./presence\";\n\nconst defaultTimers: PresenceTimers = {\n  setInterval: (run, ms) => setInterval(run, ms),\n  clearInterval: (handle) => clearInterval(handle as ReturnType<typeof setInterval>),\n  setTimeout: (run, ms) => setTimeout(run, ms),\n  clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n  now: () => Date.now(),\n};\n\nexport interface TypingState extends Record<string, unknown> {\n  typing: boolean;\n}\n\nexport interface TypingMember {\n  key: string;\n  clientId: string;\n  /** Whatever the consumer put on the presence state (name, avatar, userId…). */\n  state: Record<string, unknown>;\n}\n\nexport interface TypingOptions {\n  /** Stop advertising this long after the last keystroke. Default 3000. */\n  idleMs?: number;\n  /** Re-announce interval while actively typing. Default 2000. */\n  heartbeatMs?: number;\n  /**\n   * Drop a remote typist we have not heard from in this long. Must exceed\n   * `heartbeatMs` with margin for a slow network. Default 6000.\n   */\n  expireAfterMs?: number;\n  onChange?: (typists: readonly TypingMember[]) => void;\n}\n\nexport interface TypingTracker {\n  /** Call on EVERY keystroke. Cheap, idempotent, self-throttling. */\n  keystroke(): void;\n  /** Explicit stop (send pressed, blur, unmount). A courtesy, not the mechanism. */\n  stop(): void;\n  typists(): readonly TypingMember[];\n  subscribe(listener: (typists: readonly TypingMember[]) => void): () => void;\n  dispose(): void;\n}\n\nexport function createTypingTracker(\n  room: PresenceRoom<TypingState>,\n  options: TypingOptions = {},\n  timers: PresenceTimers = defaultTimers,\n): TypingTracker {\n  const {\n    idleMs = 3_000,\n    heartbeatMs = 2_000,\n    expireAfterMs = 6_000,\n    onChange,\n  } = options;\n\n  let advertising = false;\n  let lastKeystrokeAt = 0;\n  let lastAnnouncedAt = 0;\n  let idleTimer: unknown = null;\n  let sweeper: unknown = null;\n  let disposed = false;\n\n  /** Remote typist → OUR clock at last `typing: true` receipt. */\n  const leases = new Map<string, { at: number; member: TypingMember }>();\n  let typists: TypingMember[] = [];\n  const listeners = new Set<(typists: readonly TypingMember[]) => void>();\n  if (onChange) listeners.add(onChange);\n\n  const emit = (): void => {\n    const snapshot = typists;\n    for (const listener of listeners) listener(snapshot);\n  };\n\n  const recompute = (): void => {\n    const cutoff = timers.now() - expireAfterMs;\n    let changed = false;\n    for (const [key, lease] of leases) {\n      if (lease.at < cutoff) {\n        leases.delete(key);\n        changed = true;\n      }\n    }\n    const next = [...leases.values()].map((lease) => lease.member);\n    if (changed || next.length !== typists.length) {\n      typists = next;\n      emit();\n    }\n  };\n\n  const ingest = (members: readonly PresenceMember<TypingState>[]): void => {\n    const at = timers.now();\n    let changed = false;\n    const live = new Set<string>();\n    for (const member of members) {\n      if (member.isSelf) continue;\n      live.add(member.key);\n      if (member.state.typing === true) {\n        const existing = leases.get(member.key);\n        leases.set(member.key, {\n          at,\n          member: { key: member.key, clientId: member.clientId, state: member.state },\n        });\n        if (existing === undefined) changed = true;\n      } else if (leases.delete(member.key)) {\n        changed = true;\n      }\n    }\n    // Someone who vanished from the roster entirely is no longer typing.\n    for (const key of [...leases.keys()]) {\n      if (!live.has(key)) {\n        leases.delete(key);\n        changed = true;\n      }\n    }\n    if (changed) {\n      typists = [...leases.values()].map((lease) => lease.member);\n      emit();\n    }\n  };\n\n  const unsubscribeRoom = room.subscribe(ingest);\n\n  const announce = (typing: boolean): void => {\n    advertising = typing;\n    lastAnnouncedAt = timers.now();\n    room.update({ typing } as Partial<TypingState>);\n  };\n\n  const armIdle = (): void => {\n    if (idleTimer !== null) timers.clearTimeout(idleTimer);\n    idleTimer = timers.setTimeout(() => {\n      idleTimer = null;\n      if (advertising) announce(false);\n    }, idleMs);\n  };\n\n  sweeper = timers.setInterval(recompute, Math.max(500, Math.floor(expireAfterMs / 3)));\n\n  return {\n    keystroke(): void {\n      if (disposed) return;\n      lastKeystrokeAt = timers.now();\n      // Re-announce only on the heartbeat, so a fast typist does not send one\n      // presence update per character (the \"typing indicator melts the socket\"\n      // failure).\n      if (!advertising || lastKeystrokeAt - lastAnnouncedAt >= heartbeatMs) {\n        announce(true);\n      }\n      armIdle();\n    },\n    stop(): void {\n      if (disposed) return;\n      if (idleTimer !== null) {\n        timers.clearTimeout(idleTimer);\n        idleTimer = null;\n      }\n      if (advertising) announce(false);\n    },\n    typists: () => typists,\n    subscribe(listener): () => void {\n      listeners.add(listener);\n      listener(typists);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n    dispose(): void {\n      disposed = true;\n      if (idleTimer !== null) timers.clearTimeout(idleTimer);\n      if (sweeper !== null) timers.clearInterval(sweeper);\n      idleTimer = null;\n      sweeper = null;\n      unsubscribeRoom();\n      listeners.clear();\n      leases.clear();\n      typists = [];\n    },\n  };\n}\n","\"use client\";\n\n/**\n * `useRealtimeDiagnostics` — the inspector's data source (R7).\n *\n * Realtime bugs must be observable, not guessed at. This is a poll, not a\n * subscription, on purpose: the counters are hot-path fields, and notifying\n * React on every event would reintroduce the render storm the queue exists to\n * prevent.\n */\n\nimport { useEffect, useState } from \"react\";\nimport { realtimeDiagnostics, type DiagnosticsSnapshot } from \"../core/diagnostics\";\n\nexport function useRealtimeDiagnostics(pollMs = 1_000): DiagnosticsSnapshot {\n  const [snapshot, setSnapshot] = useState<DiagnosticsSnapshot>(() => realtimeDiagnostics());\n  useEffect(() => {\n    const tick = (): void => setSnapshot(realtimeDiagnostics());\n    tick();\n    const timer = setInterval(tick, pollMs);\n    return () => clearInterval(timer);\n  }, [pollMs]);\n  return snapshot;\n}\n"],"mappings":"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","names":["import_react","import_global_slot","import_global_slot","registry","registry","defaultTimers","import_global_slot","registry","import_global_slot","import_react","import_react","import_react","defaultTimers","import_react"]}