/** * A small client-scoped stale-while-revalidate store for read models that many * surfaces show (subscription accounts, their usage limits). Internal to * `@opengeni/react`: hosts reach it only through the hooks built on it and the * `OpenGeniProvider` `retainReadsAcrossMounts` opt-in. * * Scoping: * * - Every entry lives under the exact client object that read it (a WeakMap), * like `createDiscoveryCache`; the key names the exact read * (`["codex-accounts", workspaceId, "session", sessionId]`). * - By default nothing outlives its readers: mounted readers of one key share * one request and one snapshot, and the entry is dropped as soon as the last * reader unmounts. A reader renders an entry's rows only once it has * registered on it (layout phase, then a re-render before paint), and only * the reader instance that let go may take an entry back (StrictMode, effect * re-runs); a different reader mounting in the same commit (a host * re-keying its tree for another person) never renders the previous rows, * starts from nothing, and the earlier read and writes are discarded. Only a scope a host retains * (`retainSharedQueries`, used by the provider opt-in) keeps unmounted * entries for `retainMs`, so a host that reuses one client for different * people never serves one person's rows to the next mount. Ending the last * retention clears the scope. * - `clearSharedQueries(scope)` drops every row at once. Mounted readers stay * subscribed and read again; writes from operations that started before the * clear are discarded. * * Semantics: * * - A mount with retained data renders it immediately and revalidates in the * background once it is older than `staleTimeMs`. An enabled reader reports * `loading` until its key's first read completes. * - Page visibility pauses only polling and background revalidation of rows * already on screen; invalidation and `refresh()` read on a hidden page too. * - A loader is bound to its key when the render that supplied it commits, so a * render that never commits (a transition) or a key change cannot run one * key's loader for another key's entry. * - Failures are never cached. An access refusal (401/403/404) also drops the * retained data; another failure keeps it beside the error. * - `invalidateSharedQueries` supersedes any read in flight (its result is * discarded), re-reads mounted entries and evicts unmounted ones, so a later * mount never shows rows a mutation has made wrong. */ export type SharedQueryKey = readonly (string | number | boolean | null)[]; export type SharedQuerySnapshot = { data: T | null; /** True only while there is nothing to show yet (first read for this key). */ loading: boolean; /** True while any read for this key is in flight, including background ones. */ fetching: boolean; error: Error | null; /** Epoch milliseconds of the last successful read or write, or null. */ updatedAt: number | null; }; export type SharedQueryOptions = { /** False: never read, and show no data (not even retained rows). */ enabled?: boolean | undefined; /** Poll while mounted and the page is visible. Shortest interval wins. */ pollIntervalMs?: number | undefined; /** Retained data younger than this is not re-read on mount. Default 10s. */ staleTimeMs?: number | undefined; /** How long an unmounted entry is retained in a retained scope. Default 10 minutes. */ retainMs?: number | undefined; }; export type SharedQueryResult = SharedQuerySnapshot & { /** Force a read now (joining one queued after any in-flight read). */ refresh: () => Promise; }; /** Records data read some other way; false when the write was refused. */ export type SharedQueryWriter = (parts: SharedQueryKey, data: T) => boolean; type Loader = (signal: AbortSignal) => Promise; /** A refusal that repeating the read cannot fix; retained rows must go. */ export declare function isSharedQueryAccessRefusal(cause: unknown): boolean; /** The retained snapshot for a key, without subscribing or reading. */ export declare function peekSharedQuery(scope: object, parts: SharedQueryKey): SharedQuerySnapshot | null; /** * A writer for data read some other way (for example a live provider refresh). * Create it BEFORE starting that read: a clear in between refuses every write, * and an entry that started over for a new owner meanwhile refuses writes to * it, so a retired identity's late result is never recorded. Mounted readers * render a write at once; with no reader, only a retained scope keeps it. */ export declare function sharedQueryWriter(scope: object): SharedQueryWriter; /** * Mark every entry whose key starts with `prefix` as wrong. Reads in flight are * superseded. Mounted entries re-read now (keeping their rows on screen * meanwhile); paused ones re-read when they resume; unmounted entries are * evicted so the next mount never renders them. */ export declare function invalidateSharedQueries(scope: object, prefix: SharedQueryKey): Promise; /** * Drop everything retained for a client. Unmounted entries go; mounted ones * are reset in place (their readers stay subscribed) and active ones read * again. Writes from operations that began before the clear are refused. */ export declare function clearSharedQueries(scope: object): void; /** * Keep unmounted entries for this client until the returned release is * called. Releasing the last retention clears the client's entries (after one * task, so StrictMode's immediate remount keeps them). */ export declare function retainSharedQueries(scope: object): () => void; /** * Keep reads made through `client` after the components that made them * unmount, for as long as the calling component stays mounted: returning to a * screen (for example a conversation opened a moment ago) then draws its last * rows at once and re-reads in the background. Use it only when every * signed-in person gets their own client object; changing `client` or * unmounting drops everything kept. `OpenGeniProvider retainReadsAcrossMounts` * does the same for the provider's client. */ export declare function useRetainedReads(client: object | null | undefined, enabled?: boolean): void; /** * Subscribe to one shared read. `scope` is the client object that performs * the read; `parts` must name every input the loader depends on. */ export declare function useSharedQuery(scope: object, parts: SharedQueryKey, load: Loader, options?: SharedQueryOptions): SharedQueryResult; /** * Subscribe to an entry that is only ever written (`sharedQueryWriter`), never * read: it renders whatever is recorded and keeps the entry while mounted. */ export declare function useSharedQueryValue(scope: object, parts: SharedQueryKey): SharedQuerySnapshot; /** Display-only freshness of mounted reads. Never exposes another entry's data. */ export declare function useSharedQueryActivity(scope: object, prefixes: readonly string[]): { updatedAt: number | null; fetching: boolean; failed: boolean; }; export {};