@ai-matrx/records 0.76.1 → 0.76.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data/react.tsx"],"sourcesContent":["\"use client\";\n\n// src/data/react.tsx — @ai-matrx/records/data/react\n//\n// THE FRAME-SIDE DATA LAYER (Applets CONTRACTS v2 §4): the hooks an Applet's code reads through, so the\n// agent writing the Applet never thinks about state. Arman, 2026-10-06: \"making requests once, we're\n// doing optimistic updates, things are done correctly and easily.\"\n//\n// THE ENGINE IS TANSTACK QUERY, and the QueryClient lives in the frame (the host passes one, or the\n// frame mounts its own provider above this). On top of it this layer owns:\n//\n// · RECORDS-OWNED KEYS — `[\"@ai-matrx/records\", \"data\", portKey, alias, …]`. Two mounted Applets (two\n// ports) never share an entry; one alias's columns, pages and rows share one prefix, so a shape\n// change or a reconnect invalidates exactly that alias.\n// · ONE REQUEST PER KEY — every reader of the same question shares one entry and one in-flight call.\n// · KEYSET PAGES — `useDataRows` is an infinite query over the port's cursor; `loadMore` asks for the\n// page after the last row, so a write landing mid-read never skips or repeats a row.\n// · OPTIMISTIC WRITES WITH ROLLBACK AND RECEIPTS — the change is drawn at once; the store's answer (the\n// re-read row with its new `_version`) replaces it; a refusal puts the row back and answers the\n// store's own sentence. Every update sends the `_version` the person saw as `baseVersion`.\n// · LIVE — one subscription per (port, alias), ref-counted across every hook reading the alias, held a\n// grace after the last one lets go. A bump names ids; only those ids are re-read, and each page\n// holding one is patched in place. An id no loaded page holds (a row someone just made) refreshes\n// the alias's pages; `null` (a reconnect) or a shape change refreshes everything for the alias.\n\nimport { createContext, createElement, useContext, useEffect, useMemo, type ReactNode } from \"react\";\nimport {\n QueryClient as QueryClientClass,\n QueryClientContext,\n QueryClientProvider,\n useInfiniteQuery,\n useQuery,\n type InfiniteData,\n type QueryClient,\n type QueryKey,\n} from \"@tanstack/react-query\";\n\nimport type { RecordsError, RecordsResult } from \"../results\";\nimport type { Column, DataEvents, DataPort, DataSource, Page, Query, Row, RowsEvent, Uuid, Values } from \"./types\";\n\nexport const RECORDS_QUERY_ROOT = \"@ai-matrx/records\";\n/** How long a live subscription outlives the last hook reading its alias (a remount costs nothing). */\nexport const DATA_LIVE_GRACE_MS = 30_000;\n/** Bumps landing this close together are re-read in one batch. */\nconst BUMP_BATCH_MS = 40;\n\n// ── keys ──────────────────────────────────────────────────────────────────────────────────────\n\nexport const dataKeys = {\n all: (portKey: string) => [RECORDS_QUERY_ROOT, \"data\", portKey] as const,\n sources: (portKey: string) => [RECORDS_QUERY_ROOT, \"data\", portKey, \"sources\"] as const,\n alias: (portKey: string, alias: string) => [RECORDS_QUERY_ROOT, \"data\", portKey, \"alias\", alias] as const,\n columns: (portKey: string, alias: string) => [...dataKeys.alias(portKey, alias), \"columns\"] as const,\n rowsPrefix: (portKey: string, alias: string) => [...dataKeys.alias(portKey, alias), \"rows\"] as const,\n rows: (portKey: string, alias: string, query: Query) => [...dataKeys.rowsPrefix(portKey, alias), stableQuery(query)] as const,\n row: (portKey: string, alias: string, id: Uuid) => [...dataKeys.alias(portKey, alias), \"row\", id] as const,\n};\n\n/** A query's identity: the same question asked with its keys in another order is the same entry. */\nfunction stableQuery(query: Query): string {\n const sortKeys = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(sortKeys);\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .filter((k) => (value as Record<string, unknown>)[k] !== undefined)\n .map((k) => [k, sortKeys((value as Record<string, unknown>)[k])]),\n );\n }\n return value;\n };\n return JSON.stringify(sortKeys(query ?? {}));\n}\n\n// ── refusals as thrown values (TanStack's error channel) ───────────────────────────────────────\n\n/** A store refusal carried through the query engine; hooks hand back `.error`, never the wrapper. */\nexport class DataRefusal extends Error {\n readonly error: RecordsError;\n constructor(error: RecordsError) {\n super(error.message);\n this.name = \"DataRefusal\";\n this.error = error;\n }\n}\n\nfunction unwrap<T>(answer: RecordsResult<T>): T {\n if (answer.ok) return answer.data;\n throw new DataRefusal(answer.error);\n}\n\nfunction asRecordsError(thrown: unknown): RecordsError | null {\n if (!thrown) return null;\n if (thrown instanceof DataRefusal) return thrown.error;\n return { code: \"unreachable\", message: `The data could not be reached: ${thrown instanceof Error ? thrown.message : String(thrown)}` };\n}\n\n/** Only a refusal that a second try could change is retried; a store sentence is final. */\nfunction retryable(failureCount: number, thrown: unknown): boolean {\n const code = thrown instanceof DataRefusal ? thrown.error.code : \"unreachable\";\n return (code === \"unreachable\" || code === \"timed_out\") && failureCount < 2;\n}\n\n// ── the port in context ───────────────────────────────────────────────────────────────────────\n\ninterface PortContext {\n port: DataPort;\n portKey: string;\n /** The `rows:<alias>` streams. Absent: not live, and the console says so once. */\n events?: DataEvents;\n}\n\nconst DataPortContext = createContext<PortContext | null>(null);\n\nexport interface DataPortProviderProps {\n port: DataPort;\n /** The frame's event streams (`host.events`); a host-side mount passes `hostEvents(port)`. */\n events?: DataEvents;\n /** Unique per mounted Applet (its instance id): two ports never share a cache entry. */\n portKey: string;\n /** The frame's QueryClient. Left out, the nearest `QueryClientProvider` above is used. */\n queryClient?: QueryClient;\n children?: ReactNode;\n}\n\nexport function DataPortProvider({ port, events, portKey, queryClient, children }: DataPortProviderProps) {\n const value = useMemo(() => ({ port, portKey, ...(events ? { events } : {}) }), [port, events, portKey]);\n const inner = createElement(DataPortContext.Provider, { value }, children);\n return queryClient ? createElement(QueryClientProvider, { client: queryClient }, inner) : inner;\n}\n\n/** A host hook that owns its own port (no provider above it) passes it explicitly. */\nexport interface DataScope {\n port: DataPort;\n portKey: string;\n events?: DataEvents;\n}\n\n/** A host-side mount (no frame between): the host port's own producer, as the frame's event streams. */\nexport function hostEvents(port: { subscribe: DataEvents[\"on\"] }): DataEvents {\n return { on: (event, listener) => port.subscribe(event, listener) };\n}\n\nlet warnedNotLive = false;\n\nexport function useDataPort(scope?: DataScope): PortContext {\n const held = useContext(DataPortContext);\n if (scope) return scope;\n if (!held) {\n throw new Error(\n \"A data hook was called outside <DataPortProvider>. The frame mounts it once with the host's DataPort and this Applet's portKey.\",\n );\n }\n return held;\n}\n\n// ── the cache: reading and patching every entry that holds a row ──────────────────────────────\n\ntype RowsData = InfiniteData<Page, string | null>;\n\nfunction forEachRowsEntry(qc: QueryClient, portKey: string, alias: string, fn: (key: QueryKey, data: RowsData) => RowsData | undefined) {\n for (const [key, data] of qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n if (!data) continue;\n const next = fn(key, data);\n if (next && next !== data) qc.setQueryData(key, next);\n }\n}\n\n/** Replace one row wherever a page holds it (never with an OLDER version than the one drawn). Answers whether any page held it. */\nfunction patchRow(qc: QueryClient, portKey: string, alias: string, row: Row, options: { force?: boolean } = {}): boolean {\n let held = false;\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n let changed = false;\n const pages = data.pages.map((page) => {\n const i = page.rows.findIndex((r) => r._id === row._id);\n if (i < 0) return page;\n held = true;\n if (!options.force && page.rows[i]!._version > row._version) return page;\n changed = true;\n const rows = page.rows.slice();\n rows[i] = row;\n return { ...page, rows };\n });\n return changed ? { ...data, pages } : data;\n });\n const rowKey = dataKeys.row(portKey, alias, row._id);\n const current = qc.getQueryData<Row | null>(rowKey);\n if (options.force || !current || current._version <= row._version) qc.setQueryData(rowKey, row);\n return held;\n}\n\n/** Take a row out of every page (it was archived, or the reader may no longer see it). */\nfunction dropRow(qc: QueryClient, portKey: string, alias: string, id: Uuid): void {\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n let changed = false;\n const pages = data.pages.map((page) => {\n if (!page.rows.some((r) => r._id === id)) return page;\n changed = true;\n return { ...page, rows: page.rows.filter((r) => r._id !== id), total: page.total === null ? null : Math.max(0, page.total - 1) };\n });\n return changed ? { ...data, pages } : data;\n });\n qc.setQueryData(dataKeys.row(portKey, alias, id), null);\n}\n\nfunction findRow(qc: QueryClient, portKey: string, alias: string, id: Uuid): Row | null {\n const direct = qc.getQueryData<Row | null>(dataKeys.row(portKey, alias, id));\n if (direct) return direct;\n for (const [, data] of qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n for (const page of data?.pages ?? []) {\n const hit = page.rows.find((r) => r._id === id);\n if (hit) return hit;\n }\n }\n return null;\n}\n\n// ── live: one subscription per (QueryClient, port, alias), ref-counted ────────────────────────\n\ninterface LiveEntry {\n refs: number;\n stop: () => void;\n timer: ReturnType<typeof setTimeout> | null;\n pending: Set<Uuid>;\n flush: ReturnType<typeof setTimeout> | null;\n}\n\nconst LIVE_KEY = Symbol.for(\"@ai-matrx/records/data-live\");\n\nfunction liveHome(qc: QueryClient): Map<string, LiveEntry> {\n const holder = qc as unknown as Record<symbol, Map<string, LiveEntry> | undefined>;\n let home = holder[LIVE_KEY];\n if (!home) {\n home = new Map();\n holder[LIVE_KEY] = home;\n }\n return home;\n}\n\n/** Re-read exactly these ids and put each where it belongs. */\n/** A list whose order is the door's own (newest first) and which asks nothing of the rows: a new row's place is the top. */\nfunction isPlainNewestFirst(key: QueryKey): boolean {\n try {\n const q = JSON.parse(String(key[key.length - 1])) as Query;\n return !(q.where && q.where.length) && !q.search?.trim() && !(q.sort && q.sort.length);\n } catch {\n return false;\n }\n}\n\n/** A list this many pages deep keeps its pages when rows arrive that it cannot place; it counts them instead. */\nconst REREAD_PAGES_MAX = 2;\n\n/** The rows a list was told about but could not place; `refresh()` reads them in. */\ntype RowsDataWithNewer = RowsData & { newer?: number };\n\n/**\n * Rows no loaded page holds — new to this reader, or newly matching. WHERE they go is the store's to say:\n * · a plain newest-first list puts them at the top (the door's own order);\n * · a list of a page or two is read again (cheap, and exact);\n * · a deeper list is NEVER re-read wholesale (that re-read every loaded page on every bump: page 40 of\n * a 25,000-row table took 6 s, verifier 2026-10-06) — it counts them in `newer` for the screen to offer.\n */\nfunction placeUnseen(qc: QueryClient, portKey: string, alias: string, rows: Row[]): void {\n for (const [key, data] of qc.getQueriesData<RowsDataWithNewer>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n if (!data || data.pages.length === 0) continue;\n const missing = rows.filter((row) => !data.pages.some((page) => page.rows.some((r) => r._id === row._id)));\n if (missing.length === 0) continue;\n if (isPlainNewestFirst(key)) {\n const [first, ...rest] = data.pages;\n qc.setQueryData<RowsDataWithNewer>(key, {\n ...data,\n pages: [{ ...first!, rows: [...missing, ...first!.rows], total: first!.total === null ? null : first!.total + missing.length }, ...rest],\n });\n } else if (data.pages.length <= REREAD_PAGES_MAX) {\n void qc.invalidateQueries({ queryKey: key, exact: true });\n } else {\n qc.setQueryData<RowsDataWithNewer>(key, { ...data, newer: (data.newer ?? 0) + missing.length });\n }\n }\n}\n\nasync function rereadIds(qc: QueryClient, port: DataPort, portKey: string, alias: string, ids: Uuid[]): Promise<void> {\n const answers = await Promise.all(ids.map((id) => port.get(alias, id).then((a) => [id, a] as const)));\n const unseen: Row[] = [];\n for (const [id, answer] of answers) {\n if (!answer.ok) continue; // a re-read that failed leaves the drawn row as it was; the next bump or read corrects it\n if (answer.data === null) {\n dropRow(qc, portKey, alias, id);\n } else if (!patchRow(qc, portKey, alias, answer.data)) {\n unseen.push(answer.data);\n }\n }\n if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen);\n}\n\nfunction onBump(qc: QueryClient, port: DataPort, portKey: string, alias: string, entry: LiveEntry, event: RowsEvent): void {\n if (event.shape) {\n void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });\n return;\n }\n if (event.ids === null) {\n void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n for (const [key] of qc.getQueriesData({ queryKey: [...dataKeys.alias(portKey, alias), \"row\"] })) void qc.invalidateQueries({ queryKey: key });\n return;\n }\n for (const id of event.ids) entry.pending.add(id);\n if (entry.flush) return;\n entry.flush = setTimeout(() => {\n entry.flush = null;\n const ids = [...entry.pending];\n entry.pending.clear();\n if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);\n }, BUMP_BATCH_MS);\n}\n\nfunction acquireLive(qc: QueryClient, port: DataPort, events: DataEvents | undefined, portKey: string, alias: string): () => void {\n const home = liveHome(qc);\n const key = `${portKey}\\u0001${alias}`;\n let entry = home.get(key);\n if (!entry) {\n const made: LiveEntry = { refs: 0, stop: () => {}, timer: null, pending: new Set(), flush: null };\n if (events) {\n made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));\n } else if (!warnedNotLive) {\n // NOT LIVE, SAID OUT LOUD: with no event stream other people's changes appear only on a re-read.\n warnedNotLive = true;\n console.warn(\"[@ai-matrx/records/data/react] No event stream was given to DataPortProvider, so this view is not live.\");\n }\n home.set(key, made);\n entry = made;\n }\n const mine = entry;\n mine.refs += 1;\n if (mine.timer) {\n clearTimeout(mine.timer);\n mine.timer = null;\n }\n return () => {\n mine.refs -= 1;\n if (mine.refs > 0) return;\n mine.timer = setTimeout(() => {\n if (mine.refs > 0) return;\n if (mine.flush) clearTimeout(mine.flush);\n mine.stop();\n if (home.get(key) === mine) home.delete(key);\n }, DATA_LIVE_GRACE_MS);\n };\n}\n\nconst FALLBACK_KEY = Symbol.for(\"@ai-matrx/records/data-query-client\");\n\n/**\n * The frame's QueryClient — the nearest provider's. A host with none (a plain page using a records hook)\n * gets ONE shared client for the page, held on globalThis so a dual-loaded graph still shares it.\n */\nexport function useRecordsQueryClient(): QueryClient {\n const provided = useContext(QueryClientContext);\n if (provided) return provided;\n const g = globalThis as unknown as Record<symbol, QueryClient | undefined>;\n let held = g[FALLBACK_KEY];\n if (!held) {\n held = new QueryClientClass({ defaultOptions: { queries: { staleTime: 30_000, refetchOnWindowFocus: false } } });\n g[FALLBACK_KEY] = held;\n }\n return held;\n}\n\nfunction useLive(alias: string | null, scope?: DataScope): void {\n const qc = useRecordsQueryClient();\n const { port, portKey, events } = useDataPort(scope);\n useEffect(() => {\n if (!alias) return;\n return acquireLive(qc, port, events, portKey, alias);\n }, [qc, port, events, portKey, alias]);\n}\n\n// ── reads ─────────────────────────────────────────────────────────────────────────────────────\n\nexport interface DataRead<T> {\n data: T | undefined;\n loading: boolean;\n /** The store's own sentence when the read was refused. */\n error: RecordsError | null;\n refresh: () => void;\n}\n\nexport function useDataSources(scope?: DataScope): DataRead<DataSource[]> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n const q = useQuery({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()), retry: retryable }, qc);\n return { data: q.data, loading: q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\nexport function useDataColumns(alias: string, scope?: DataScope): DataRead<Column[]> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(alias, scope);\n const q = useQuery({ queryKey: dataKeys.columns(portKey, alias), queryFn: async () => unwrap(await port.columns(alias)), retry: retryable }, qc);\n return { data: q.data, loading: q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\nexport interface DataRows {\n rows: Row[];\n total: number | null;\n loading: boolean;\n /** A next page is being read. */\n loadingMore: boolean;\n hasMore: boolean;\n error: RecordsError | null;\n /** Read the page after the last row (keyset): never skips or repeats a row, whatever is written meanwhile. */\n loadMore: () => void;\n /** Rows written by others that belong somewhere in this (deep, filtered or sorted) list; `refresh()` reads them in. */\n newer: number;\n refresh: () => void;\n}\n\nexport function useDataRows(alias: string, query: Query = {}, scope?: DataScope): DataRows {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(alias, scope);\n const q = useInfiniteQuery(\n {\n queryKey: dataKeys.rows(portKey, alias, query),\n queryFn: async ({ pageParam }) => unwrap(await port.list(alias, query, pageParam ?? undefined)),\n initialPageParam: null as string | null,\n getNextPageParam: (last: Page) => last.cursor ?? undefined,\n retry: retryable,\n },\n qc,\n );\n const rows = useMemo(() => {\n const seen = new Set<Uuid>();\n const out: Row[] = [];\n for (const page of q.data?.pages ?? []) {\n for (const row of page.rows) {\n // A row an optimistic create drew AND the store's page now holds is one row, drawn once.\n if (seen.has(row._id)) continue;\n seen.add(row._id);\n out.push(row);\n }\n }\n return out;\n }, [q.data]);\n return {\n rows,\n total: q.data?.pages[0]?.total ?? null,\n loading: q.isPending,\n loadingMore: q.isFetchingNextPage,\n hasMore: Boolean(q.hasNextPage),\n error: asRecordsError(q.error),\n loadMore: () => {\n if (q.hasNextPage && !q.isFetchingNextPage) void q.fetchNextPage();\n },\n newer: (q.data as RowsDataWithNewer | undefined)?.newer ?? 0,\n refresh: () => void q.refetch(),\n };\n}\n\nexport function useDataRow(alias: string, id: Uuid | null, scope?: DataScope): DataRead<Row | null> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(id ? alias : null, scope);\n const q = useQuery({\n queryKey: dataKeys.row(portKey, alias, id ?? \"-\"),\n queryFn: async () => unwrap(await port.get(alias, id!)),\n enabled: Boolean(id),\n retry: retryable,\n // A row a loaded page already holds is drawn at once; the read confirms it.\n placeholderData: () => (id ? (findRow(qc, portKey, alias, id) ?? undefined) : undefined),\n }, qc);\n return { data: id ? q.data : null, loading: Boolean(id) && q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\n// ── writes: optimistic, rolled back with the store's sentence, answered with a receipt ─────────\n\nexport interface DataMutations {\n /** Drawn at once (marked `_pending`), replaced by the stored row; removed with the store's sentence on refusal. */\n create(values: Values): Promise<RecordsResult<Row>>;\n /** Sends the drawn row's `_version` as baseVersion (or `options.baseVersion`). A stale write is refused and the row re-read. */\n update(id: Uuid, patch: Values, options?: { baseVersion?: number }): Promise<RecordsResult<Row>>;\n archive(id: Uuid): Promise<RecordsResult<void>>;\n restore(id: Uuid): Promise<RecordsResult<void>>;\n}\n\nlet tempCounter = 0;\n\nexport function useDataMutations(alias: string, scope?: DataScope): DataMutations {\n const qc = useRecordsQueryClient();\n const { port, portKey } = useDataPort(scope);\n\n return useMemo<DataMutations>(() => {\n const snapshotRows = () => qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n const putBack = (snap: ReturnType<typeof snapshotRows>) => {\n for (const [key, data] of snap) qc.setQueryData(key, data);\n };\n\n return {\n async create(values) {\n const tempId = `pending-${Date.now().toString(36)}-${(tempCounter += 1)}`;\n const drawn: Row = { ...values, _id: tempId, _version: 0, _organization_id: \"\", _pending: true };\n await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n if (data.pages.length === 0) return data;\n const [first, ...rest] = data.pages;\n return { ...data, pages: [{ ...first!, rows: [drawn, ...first!.rows], total: first!.total === null ? null : first!.total + 1 }, ...rest] };\n });\n const answer = await port.create(alias, values);\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n const pages = data.pages.map((page) => {\n if (!page.rows.some((r) => r._id === tempId)) return page;\n const rows = answer.ok\n ? page.rows.map((r) => (r._id === tempId ? answer.data : r))\n : page.rows.filter((r) => r._id !== tempId);\n return { ...page, rows, total: answer.ok || page.total === null ? page.total : Math.max(0, page.total - 1) };\n });\n return { ...data, pages };\n });\n if (answer.ok) qc.setQueryData(dataKeys.row(portKey, alias, answer.data._id), answer.data);\n return answer;\n },\n\n async update(id, patch, options = {}) {\n const before = findRow(qc, portKey, alias, id);\n const baseVersion = options.baseVersion ?? before?._version;\n if (typeof baseVersion !== \"number\") {\n return { ok: false, error: { code: \"stale_read\", message: \"Nothing was saved: this row's version is not known here.\", hint: \"Read the row first, then change it.\" } };\n }\n await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });\n const rowKey = dataKeys.row(portKey, alias, id);\n const rowBefore = qc.getQueryData<Row | null>(rowKey);\n if (before) patchRow(qc, portKey, alias, { ...before, ...patch, _id: id, _version: before._version, _organization_id: before._organization_id }, { force: true });\n const answer = await port.update(alias, id, patch, baseVersion);\n if (answer.ok) {\n patchRow(qc, portKey, alias, answer.data, { force: true });\n return answer;\n }\n // ROLLBACK: this row goes back to what was drawn before (only the entries that held it), then the\n // store's current row is read so a stale write shows what actually won.\n if (before) patchRow(qc, portKey, alias, before, { force: true });\n qc.setQueryData(rowKey, rowBefore);\n void rereadIds(qc, port, portKey, alias, [id]);\n return answer;\n },\n\n async archive(id) {\n await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n const snap = snapshotRows();\n const rowKey = dataKeys.row(portKey, alias, id);\n const rowBefore = qc.getQueryData<Row | null>(rowKey);\n dropRow(qc, portKey, alias, id);\n const answer = await port.archive(alias, id);\n if (!answer.ok) {\n putBack(snap);\n qc.setQueryData(rowKey, rowBefore);\n // Anything another person changed while the archive was in flight is read again.\n void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n }\n return answer;\n },\n\n async restore(id) {\n const answer = await port.restore(alias, id);\n if (answer.ok) {\n void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n void qc.invalidateQueries({ queryKey: dataKeys.row(portKey, alias, id) });\n }\n return answer;\n },\n };\n }, [qc, port, portKey, alias]);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data/react.tsx"],"sourcesContent":["\"use client\";\n\n// src/data/react.tsx — @ai-matrx/records/data/react\n//\n// THE FRAME-SIDE DATA LAYER (Applets CONTRACTS v2 §4): the hooks an Applet's code reads through, so the\n// agent writing the Applet never thinks about state. Arman, 2026-10-06: \"making requests once, we're\n// doing optimistic updates, things are done correctly and easily.\"\n//\n// THE ENGINE IS TANSTACK QUERY, and the QueryClient lives in the frame (the host passes one, or the\n// frame mounts its own provider above this). On top of it this layer owns:\n//\n// · RECORDS-OWNED KEYS — `[\"@ai-matrx/records\", \"data\", portKey, alias, …]`. Two mounted Applets (two\n// ports) never share an entry; one alias's columns, pages and rows share one prefix, so a shape\n// change or a reconnect invalidates exactly that alias.\n// · ONE REQUEST PER KEY — every reader of the same question shares one entry and one in-flight call.\n// · KEYSET PAGES — `useDataRows` is an infinite query over the port's cursor; `loadMore` asks for the\n// page after the last row, so a write landing mid-read never skips or repeats a row.\n// · OPTIMISTIC WRITES WITH ROLLBACK AND RECEIPTS — the change is drawn at once; the store's answer (the\n// re-read row with its new `_version`) replaces it; a refusal puts the row back and answers the\n// store's own sentence. Every update sends the `_version` the person saw as `baseVersion`.\n// · LIVE — one subscription per (port, alias), ref-counted across every hook reading the alias, held a\n// grace after the last one lets go. A bump names ids; only those ids are re-read, and each page\n// holding one is patched in place. An id no loaded page holds (a row someone just made) refreshes\n// the alias's pages; `null` (a reconnect) or a shape change refreshes everything for the alias.\n\nimport { createContext, createElement, useContext, useEffect, useMemo, type ReactNode } from \"react\";\nimport {\n QueryClient as QueryClientClass,\n QueryClientContext,\n QueryClientProvider,\n useInfiniteQuery,\n useQuery,\n type InfiniteData,\n type QueryClient,\n type QueryKey,\n} from \"@tanstack/react-query\";\n\nimport type { RecordsError, RecordsResult } from \"../results\";\nimport type { Column, DataEvents, DataPort, DataSource, Page, Query, Row, RowsEvent, Uuid, Values } from \"./types\";\n\nexport const RECORDS_QUERY_ROOT = \"@ai-matrx/records\";\n/** How long a live subscription outlives the last hook reading its alias (a remount costs nothing). */\nexport const DATA_LIVE_GRACE_MS = 30_000;\n/** Bumps landing this close together are re-read in one batch. */\nconst BUMP_BATCH_MS = 40;\n\n// ── keys ──────────────────────────────────────────────────────────────────────────────────────\n\nexport const dataKeys = {\n all: (portKey: string) => [RECORDS_QUERY_ROOT, \"data\", portKey] as const,\n sources: (portKey: string) => [RECORDS_QUERY_ROOT, \"data\", portKey, \"sources\"] as const,\n alias: (portKey: string, alias: string) => [RECORDS_QUERY_ROOT, \"data\", portKey, \"alias\", alias] as const,\n columns: (portKey: string, alias: string) => [...dataKeys.alias(portKey, alias), \"columns\"] as const,\n rowsPrefix: (portKey: string, alias: string) => [...dataKeys.alias(portKey, alias), \"rows\"] as const,\n rows: (portKey: string, alias: string, query: Query) => [...dataKeys.rowsPrefix(portKey, alias), stableQuery(query)] as const,\n row: (portKey: string, alias: string, id: Uuid) => [...dataKeys.alias(portKey, alias), \"row\", id] as const,\n};\n\n/** A query's identity: the same question asked with its keys in another order is the same entry. */\nfunction stableQuery(query: Query): string {\n const sortKeys = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(sortKeys);\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .filter((k) => (value as Record<string, unknown>)[k] !== undefined)\n .map((k) => [k, sortKeys((value as Record<string, unknown>)[k])]),\n );\n }\n return value;\n };\n return JSON.stringify(sortKeys(query ?? {}));\n}\n\n// ── refusals as thrown values (TanStack's error channel) ───────────────────────────────────────\n\n/** A store refusal carried through the query engine; hooks hand back `.error`, never the wrapper. */\nexport class DataRefusal extends Error {\n readonly error: RecordsError;\n constructor(error: RecordsError) {\n super(error.message);\n this.name = \"DataRefusal\";\n this.error = error;\n }\n}\n\nfunction unwrap<T>(answer: RecordsResult<T>): T {\n if (answer.ok) return answer.data;\n throw new DataRefusal(answer.error);\n}\n\nfunction asRecordsError(thrown: unknown): RecordsError | null {\n if (!thrown) return null;\n if (thrown instanceof DataRefusal) return thrown.error;\n return { code: \"unreachable\", message: `The data could not be reached: ${thrown instanceof Error ? thrown.message : String(thrown)}` };\n}\n\n/** Only a refusal that a second try could change is retried; a store sentence is final. */\nfunction retryable(failureCount: number, thrown: unknown): boolean {\n const code = thrown instanceof DataRefusal ? thrown.error.code : \"unreachable\";\n return (code === \"unreachable\" || code === \"timed_out\") && failureCount < 2;\n}\n\n// ── the port in context ───────────────────────────────────────────────────────────────────────\n\ninterface PortContext {\n port: DataPort;\n portKey: string;\n /** The `rows:<alias>` streams. Absent: not live, and the console says so once. */\n events?: DataEvents;\n}\n\nconst DataPortContext = createContext<PortContext | null>(null);\n\nexport interface DataPortProviderProps {\n port: DataPort;\n /** The frame's event streams (`host.events`); a host-side mount passes `hostEvents(port)`. */\n events?: DataEvents;\n /** Unique per mounted Applet (its instance id): two ports never share a cache entry. */\n portKey: string;\n /** The frame's QueryClient. Left out, the nearest `QueryClientProvider` above is used. */\n queryClient?: QueryClient;\n children?: ReactNode;\n}\n\nexport function DataPortProvider({ port, events, portKey, queryClient, children }: DataPortProviderProps) {\n const value = useMemo(() => ({ port, portKey, ...(events ? { events } : {}) }), [port, events, portKey]);\n const inner = createElement(DataPortContext.Provider, { value }, children);\n return queryClient ? createElement(QueryClientProvider, { client: queryClient }, inner) : inner;\n}\n\n/** A host hook that owns its own port (no provider above it) passes it explicitly. */\nexport interface DataScope {\n port: DataPort;\n portKey: string;\n events?: DataEvents;\n}\n\n/** A host-side mount (no frame between): the host port's own producer, as the frame's event streams. */\nexport function hostEvents(port: { subscribe: DataEvents[\"on\"] }): DataEvents {\n return { on: (event, listener) => port.subscribe(event, listener) };\n}\n\nlet warnedNotLive = false;\n\nexport function useDataPort(scope?: DataScope): PortContext {\n const held = useContext(DataPortContext);\n if (scope) return scope;\n if (!held) {\n throw new Error(\n \"A data hook was called outside <DataPortProvider>. The frame mounts it once with the host's DataPort and this Applet's portKey.\",\n );\n }\n return held;\n}\n\n// ── the cache: reading and patching every entry that holds a row ──────────────────────────────\n\ntype RowsData = InfiniteData<Page, string | null>;\n\nfunction forEachRowsEntry(qc: QueryClient, portKey: string, alias: string, fn: (key: QueryKey, data: RowsData) => RowsData | undefined) {\n for (const [key, data] of qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n if (!data) continue;\n const next = fn(key, data);\n if (next && next !== data) qc.setQueryData(key, next);\n }\n}\n\n/** Replace one row wherever a page holds it (never with an OLDER version than the one drawn). Answers whether any page held it. */\nfunction patchRow(qc: QueryClient, portKey: string, alias: string, row: Row, options: { force?: boolean } = {}): boolean {\n let held = false;\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n let changed = false;\n const pages = data.pages.map((page) => {\n const i = page.rows.findIndex((r) => r._id === row._id);\n if (i < 0) return page;\n held = true;\n if (!options.force && page.rows[i]!._version > row._version) return page;\n changed = true;\n const rows = page.rows.slice();\n rows[i] = row;\n return { ...page, rows };\n });\n return changed ? { ...data, pages } : data;\n });\n const rowKey = dataKeys.row(portKey, alias, row._id);\n const current = qc.getQueryData<Row | null>(rowKey);\n if (options.force || !current || current._version <= row._version) qc.setQueryData(rowKey, row);\n return held;\n}\n\n/** Take a row out of every page (it was archived, or the reader may no longer see it). */\nfunction dropRow(qc: QueryClient, portKey: string, alias: string, id: Uuid): void {\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n let changed = false;\n const pages = data.pages.map((page) => {\n if (!page.rows.some((r) => r._id === id)) return page;\n changed = true;\n return { ...page, rows: page.rows.filter((r) => r._id !== id), total: page.total === null ? null : Math.max(0, page.total - 1) };\n });\n return changed ? { ...data, pages } : data;\n });\n qc.setQueryData(dataKeys.row(portKey, alias, id), null);\n}\n\nfunction findRow(qc: QueryClient, portKey: string, alias: string, id: Uuid): Row | null {\n const direct = qc.getQueryData<Row | null>(dataKeys.row(portKey, alias, id));\n if (direct) return direct;\n for (const [, data] of qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n for (const page of data?.pages ?? []) {\n const hit = page.rows.find((r) => r._id === id);\n if (hit) return hit;\n }\n }\n return null;\n}\n\n// ── live: one subscription per (QueryClient, port, alias), ref-counted ────────────────────────\n\ninterface LiveEntry {\n refs: number;\n stop: () => void;\n timer: ReturnType<typeof setTimeout> | null;\n pending: Set<Uuid>;\n /** A notice in this batch could not name its rows. */\n unknown: boolean;\n flush: ReturnType<typeof setTimeout> | null;\n}\n\nconst LIVE_KEY = Symbol.for(\"@ai-matrx/records/data-live\");\n\nfunction liveHome(qc: QueryClient): Map<string, LiveEntry> {\n const holder = qc as unknown as Record<symbol, Map<string, LiveEntry> | undefined>;\n let home = holder[LIVE_KEY];\n if (!home) {\n home = new Map();\n holder[LIVE_KEY] = home;\n }\n return home;\n}\n\n/** Re-read exactly these ids and put each where it belongs. */\n/** A list whose order is the door's own (newest first) and which asks nothing of the rows: a new row's place is the top. */\nfunction isPlainNewestFirst(key: QueryKey): boolean {\n try {\n const q = JSON.parse(String(key[key.length - 1])) as Query;\n return !(q.where && q.where.length) && !q.search?.trim() && !(q.sort && q.sort.length);\n } catch {\n return false;\n }\n}\n\n/** A list this many pages deep keeps its pages when rows arrive that it cannot place; it counts them instead. */\nconst REREAD_PAGES_MAX = 2;\n\n/** The rows a list was told about but could not place; `refresh()` reads them in. */\ntype RowsDataWithNewer = RowsData & { newer?: number };\n\n/**\n * Rows no loaded page holds — new to this reader, or newly matching. WHERE they go is the store's to say:\n * · a plain newest-first list puts them at the top (the door's own order);\n * · a list of a page or two is read again (cheap, and exact);\n * · a deeper list is NEVER re-read wholesale (that re-read every loaded page on every bump: page 40 of\n * a 25,000-row table took 6 s, verifier 2026-10-06) — it counts them in `newer` for the screen to offer.\n */\n/**\n * What kind of source an alias is — read once through the same cached `sources()` entry every reader shares.\n * Only a custom TABLE's re-read rows are known to belong to its plain list: its read door answers live rows\n * only, in the door's newest-first order. A platform source's `get` can answer an archived row, or one its\n * list hides by default (a \"discovered\" contact), so its rows are never placed by the engine — the list is\n * asked again (short) or told how many are new (deep). Unknown (the read refused) is treated as \"not a table\".\n */\nasync function sourceKind(qc: QueryClient, port: DataPort, portKey: string, alias: string): Promise<DataSource[\"kind\"] | null> {\n try {\n const sources = await qc.ensureQueryData({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()) });\n return sources.find((s) => s.alias === alias)?.kind ?? null;\n } catch {\n return null;\n }\n}\n\n/** A list that cannot place what moved: one exact re-read while it is short, a `newer` count once it is deep. */\nfunction rereadOrCount(qc: QueryClient, key: QueryKey, data: RowsDataWithNewer, count: number): void {\n if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });\n else qc.setQueryData<RowsDataWithNewer>(key, { ...data, newer: (data.newer ?? 0) + count });\n}\n\nfunction placeUnseen(qc: QueryClient, portKey: string, alias: string, rows: Row[], kind: DataSource[\"kind\"] | null): void {\n for (const [key, data] of qc.getQueriesData<RowsDataWithNewer>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n if (!data || data.pages.length === 0) continue;\n const missing = rows.filter((row) => !data.pages.some((page) => page.rows.some((r) => r._id === row._id)));\n if (missing.length === 0) continue;\n if (kind === \"table\" && isPlainNewestFirst(key)) {\n const [first, ...rest] = data.pages;\n qc.setQueryData<RowsDataWithNewer>(key, {\n ...data,\n pages: [{ ...first!, rows: [...missing, ...first!.rows], total: first!.total === null ? null : first!.total + missing.length }, ...rest],\n });\n } else {\n rereadOrCount(qc, key, data, missing.length);\n }\n }\n}\n\nasync function rereadIds(qc: QueryClient, port: DataPort, portKey: string, alias: string, ids: Uuid[]): Promise<void> {\n const answers = await Promise.all(ids.map((id) => port.get(alias, id).then((a) => [id, a] as const)));\n const unseen: Row[] = [];\n for (const [id, answer] of answers) {\n if (!answer.ok) continue; // a re-read that failed leaves the drawn row as it was; the next bump or read corrects it\n if (answer.data === null) {\n dropRow(qc, portKey, alias, id);\n } else if (!patchRow(qc, portKey, alias, answer.data)) {\n unseen.push(answer.data);\n }\n }\n if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen, await sourceKind(qc, port, portKey, alias));\n}\n\n/**\n * \"Something moved and I cannot say which\" (a reconnect, a wake, more ids than a notice lists). Never a re-read of\n * every loaded page: each list gets one exact re-read while short, or a `newer` mark once deep; each open row is\n * read again (one cheap read per row a view is showing).\n */\nfunction rereadUnknown(qc: QueryClient, portKey: string, alias: string): void {\n for (const [key, data] of qc.getQueriesData<RowsDataWithNewer>({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {\n if (!data || data.pages.length === 0) {\n void qc.invalidateQueries({ queryKey: key, exact: true });\n continue;\n }\n if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });\n else qc.setQueryData<RowsDataWithNewer>(key, { ...data, newer: Math.max(data.newer ?? 0, 1) });\n }\n void qc.invalidateQueries({ queryKey: [...dataKeys.alias(portKey, alias), \"row\"] });\n}\n\nfunction onBump(qc: QueryClient, port: DataPort, portKey: string, alias: string, entry: LiveEntry, event: RowsEvent): void {\n if (event.shape) {\n void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });\n return;\n }\n // Batched like any other notice: a burst of \"cannot say which\" is one bounded re-read, not one per notice.\n if (event.ids === null) entry.unknown = true;\n else for (const id of event.ids) entry.pending.add(id);\n if (entry.flush) return;\n entry.flush = setTimeout(() => {\n entry.flush = null;\n const ids = [...entry.pending];\n const unknown = entry.unknown;\n entry.pending.clear();\n entry.unknown = false;\n if (unknown) rereadUnknown(qc, portKey, alias);\n else if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);\n }, BUMP_BATCH_MS);\n}\n\nfunction acquireLive(qc: QueryClient, port: DataPort, events: DataEvents | undefined, portKey: string, alias: string): () => void {\n const home = liveHome(qc);\n const key = `${portKey}\\u0001${alias}`;\n let entry = home.get(key);\n if (!entry) {\n const made: LiveEntry = { refs: 0, stop: () => {}, timer: null, pending: new Set(), unknown: false, flush: null };\n if (events) {\n made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));\n } else if (!warnedNotLive) {\n // NOT LIVE, SAID OUT LOUD: with no event stream other people's changes appear only on a re-read.\n warnedNotLive = true;\n console.warn(\"[@ai-matrx/records/data/react] No event stream was given to DataPortProvider, so this view is not live.\");\n }\n home.set(key, made);\n entry = made;\n }\n const mine = entry;\n mine.refs += 1;\n if (mine.timer) {\n clearTimeout(mine.timer);\n mine.timer = null;\n }\n return () => {\n mine.refs -= 1;\n if (mine.refs > 0) return;\n mine.timer = setTimeout(() => {\n if (mine.refs > 0) return;\n if (mine.flush) clearTimeout(mine.flush);\n mine.stop();\n if (home.get(key) === mine) home.delete(key);\n }, DATA_LIVE_GRACE_MS);\n };\n}\n\nconst FALLBACK_KEY = Symbol.for(\"@ai-matrx/records/data-query-client\");\n\n/**\n * The frame's QueryClient — the nearest provider's. A host with none (a plain page using a records hook)\n * gets ONE shared client for the page, held on globalThis so a dual-loaded graph still shares it.\n */\nexport function useRecordsQueryClient(): QueryClient {\n const provided = useContext(QueryClientContext);\n if (provided) return provided;\n const g = globalThis as unknown as Record<symbol, QueryClient | undefined>;\n let held = g[FALLBACK_KEY];\n if (!held) {\n held = new QueryClientClass({ defaultOptions: { queries: { staleTime: 30_000, refetchOnWindowFocus: false } } });\n g[FALLBACK_KEY] = held;\n }\n return held;\n}\n\nfunction useLive(alias: string | null, scope?: DataScope): void {\n const qc = useRecordsQueryClient();\n const { port, portKey, events } = useDataPort(scope);\n useEffect(() => {\n if (!alias) return;\n return acquireLive(qc, port, events, portKey, alias);\n }, [qc, port, events, portKey, alias]);\n}\n\n// ── reads ─────────────────────────────────────────────────────────────────────────────────────\n\nexport interface DataRead<T> {\n data: T | undefined;\n loading: boolean;\n /** The store's own sentence when the read was refused. */\n error: RecordsError | null;\n refresh: () => void;\n}\n\nexport function useDataSources(scope?: DataScope): DataRead<DataSource[]> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n const q = useQuery({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()), retry: retryable }, qc);\n return { data: q.data, loading: q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\nexport function useDataColumns(alias: string, scope?: DataScope): DataRead<Column[]> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(alias, scope);\n const q = useQuery({ queryKey: dataKeys.columns(portKey, alias), queryFn: async () => unwrap(await port.columns(alias)), retry: retryable }, qc);\n return { data: q.data, loading: q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\nexport interface DataRows {\n rows: Row[];\n total: number | null;\n loading: boolean;\n /** A next page is being read. */\n loadingMore: boolean;\n hasMore: boolean;\n error: RecordsError | null;\n /** Read the page after the last row (keyset): never skips or repeats a row, whatever is written meanwhile. */\n loadMore: () => void;\n /** Rows written by others that belong somewhere in this (deep, filtered or sorted) list; `refresh()` reads them in. */\n newer: number;\n refresh: () => void;\n}\n\nexport function useDataRows(alias: string, query: Query = {}, scope?: DataScope): DataRows {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(alias, scope);\n const q = useInfiniteQuery(\n {\n queryKey: dataKeys.rows(portKey, alias, query),\n queryFn: async ({ pageParam }) => unwrap(await port.list(alias, query, pageParam ?? undefined)),\n initialPageParam: null as string | null,\n getNextPageParam: (last: Page) => last.cursor ?? undefined,\n retry: retryable,\n },\n qc,\n );\n const rows = useMemo(() => {\n const seen = new Set<Uuid>();\n const out: Row[] = [];\n for (const page of q.data?.pages ?? []) {\n for (const row of page.rows) {\n // A row an optimistic create drew AND the store's page now holds is one row, drawn once.\n if (seen.has(row._id)) continue;\n seen.add(row._id);\n out.push(row);\n }\n }\n return out;\n }, [q.data]);\n return {\n rows,\n total: q.data?.pages[0]?.total ?? null,\n loading: q.isPending,\n loadingMore: q.isFetchingNextPage,\n hasMore: Boolean(q.hasNextPage),\n error: asRecordsError(q.error),\n loadMore: () => {\n if (q.hasNextPage && !q.isFetchingNextPage) void q.fetchNextPage();\n },\n newer: (q.data as RowsDataWithNewer | undefined)?.newer ?? 0,\n refresh: () => void q.refetch(),\n };\n}\n\nexport function useDataRow(alias: string, id: Uuid | null, scope?: DataScope): DataRead<Row | null> {\n const { port, portKey } = useDataPort(scope);\n const qc = useRecordsQueryClient();\n useLive(id ? alias : null, scope);\n const q = useQuery({\n queryKey: dataKeys.row(portKey, alias, id ?? \"-\"),\n queryFn: async () => unwrap(await port.get(alias, id!)),\n enabled: Boolean(id),\n retry: retryable,\n // A row a loaded page already holds is drawn at once; the read confirms it.\n placeholderData: () => (id ? (findRow(qc, portKey, alias, id) ?? undefined) : undefined),\n }, qc);\n return { data: id ? q.data : null, loading: Boolean(id) && q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };\n}\n\n// ── writes: optimistic, rolled back with the store's sentence, answered with a receipt ─────────\n\nexport interface DataMutations {\n /** Drawn at once (marked `_pending`), replaced by the stored row; removed with the store's sentence on refusal. */\n create(values: Values): Promise<RecordsResult<Row>>;\n /** Sends the drawn row's `_version` as baseVersion (or `options.baseVersion`). A stale write is refused and the row re-read. */\n update(id: Uuid, patch: Values, options?: { baseVersion?: number }): Promise<RecordsResult<Row>>;\n archive(id: Uuid): Promise<RecordsResult<void>>;\n restore(id: Uuid): Promise<RecordsResult<void>>;\n}\n\nlet tempCounter = 0;\n\n/**\n * The row as an optimistic update draws it: the patch over the row, with `_custom` MERGED (a patch naming one\n * custom field never blanks the others), and the runtime's own keys kept as they were.\n */\nexport function drawPatch(before: Row, patch: Values): Row {\n const drawn: Row = { ...before, ...patch, _id: before._id, _version: before._version, _organization_id: before._organization_id };\n if (patch._custom && typeof patch._custom === \"object\") {\n drawn._custom = { ...(before._custom ?? {}), ...patch._custom };\n } else if (before._custom !== undefined) {\n drawn._custom = before._custom;\n }\n return drawn;\n}\n\nexport function useDataMutations(alias: string, scope?: DataScope): DataMutations {\n const qc = useRecordsQueryClient();\n const { port, portKey } = useDataPort(scope);\n\n return useMemo<DataMutations>(() => {\n const snapshotRows = () => qc.getQueriesData<RowsData>({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n const putBack = (snap: ReturnType<typeof snapshotRows>) => {\n for (const [key, data] of snap) qc.setQueryData(key, data);\n };\n\n return {\n async create(values) {\n const tempId = `pending-${Date.now().toString(36)}-${(tempCounter += 1)}`;\n const drawn: Row = { ...values, _id: tempId, _version: 0, _organization_id: \"\", _pending: true };\n await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n if (data.pages.length === 0) return data;\n const [first, ...rest] = data.pages;\n return { ...data, pages: [{ ...first!, rows: [drawn, ...first!.rows], total: first!.total === null ? null : first!.total + 1 }, ...rest] };\n });\n const answer = await port.create(alias, values);\n forEachRowsEntry(qc, portKey, alias, (_key, data) => {\n const pages = data.pages.map((page) => {\n if (!page.rows.some((r) => r._id === tempId)) return page;\n const rows = answer.ok\n ? page.rows.map((r) => (r._id === tempId ? answer.data : r))\n : page.rows.filter((r) => r._id !== tempId);\n return { ...page, rows, total: answer.ok || page.total === null ? page.total : Math.max(0, page.total - 1) };\n });\n return { ...data, pages };\n });\n if (answer.ok) qc.setQueryData(dataKeys.row(portKey, alias, answer.data._id), answer.data);\n return answer;\n },\n\n async update(id, patch, options = {}) {\n const before = findRow(qc, portKey, alias, id);\n const baseVersion = options.baseVersion ?? before?._version;\n if (typeof baseVersion !== \"number\") {\n return { ok: false, error: { code: \"stale_read\", message: \"Nothing was saved: this row's version is not known here.\", hint: \"Read the row first, then change it.\" } };\n }\n await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });\n const rowKey = dataKeys.row(portKey, alias, id);\n const rowBefore = qc.getQueryData<Row | null>(rowKey);\n if (before) patchRow(qc, portKey, alias, drawPatch(before, patch), { force: true });\n const answer = await port.update(alias, id, patch, baseVersion);\n if (answer.ok) {\n patchRow(qc, portKey, alias, answer.data, { force: true });\n return answer;\n }\n // ROLLBACK: this row goes back to what was drawn before (only the entries that held it), then the\n // store's current row is read so a stale write shows what actually won.\n if (before) patchRow(qc, portKey, alias, before, { force: true });\n qc.setQueryData(rowKey, rowBefore);\n void rereadIds(qc, port, portKey, alias, [id]);\n return answer;\n },\n\n async archive(id) {\n await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n const snap = snapshotRows();\n const rowKey = dataKeys.row(portKey, alias, id);\n const rowBefore = qc.getQueryData<Row | null>(rowKey);\n dropRow(qc, portKey, alias, id);\n const answer = await port.archive(alias, id);\n if (!answer.ok) {\n putBack(snap);\n qc.setQueryData(rowKey, rowBefore);\n // Anything another person changed while the archive was in flight is read again.\n void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n }\n return answer;\n },\n\n async restore(id) {\n const answer = await port.restore(alias, id);\n if (answer.ok) {\n void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });\n void qc.invalidateQueries({ queryKey: dataKeys.row(portKey, alias, id) });\n }\n return answer;\n },\n };\n }, [qc, port, portKey, alias]);\n}\n"],"mappings":"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","names":[]}
@@ -43478,21 +43478,31 @@ function isPlainNewestFirst(key) {
43478
43478
  }
43479
43479
  }
43480
43480
  var REREAD_PAGES_MAX = 2;
43481
- function placeUnseen(qc, portKey, alias, rows) {
43481
+ async function sourceKind(qc, port, portKey, alias) {
43482
+ try {
43483
+ const sources = await qc.ensureQueryData({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()) });
43484
+ return sources.find((s) => s.alias === alias)?.kind ?? null;
43485
+ } catch {
43486
+ return null;
43487
+ }
43488
+ }
43489
+ function rereadOrCount(qc, key, data, count) {
43490
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
43491
+ else qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + count });
43492
+ }
43493
+ function placeUnseen(qc, portKey, alias, rows, kind) {
43482
43494
  for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
43483
43495
  if (!data || data.pages.length === 0) continue;
43484
43496
  const missing = rows.filter((row) => !data.pages.some((page2) => page2.rows.some((r) => r._id === row._id)));
43485
43497
  if (missing.length === 0) continue;
43486
- if (isPlainNewestFirst(key)) {
43498
+ if (kind === "table" && isPlainNewestFirst(key)) {
43487
43499
  const [first, ...rest] = data.pages;
43488
43500
  qc.setQueryData(key, {
43489
43501
  ...data,
43490
43502
  pages: [{ ...first, rows: [...missing, ...first.rows], total: first.total === null ? null : first.total + missing.length }, ...rest]
43491
43503
  });
43492
- } else if (data.pages.length <= REREAD_PAGES_MAX) {
43493
- void qc.invalidateQueries({ queryKey: key, exact: true });
43494
43504
  } else {
43495
- qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + missing.length });
43505
+ rereadOrCount(qc, key, data, missing.length);
43496
43506
  }
43497
43507
  }
43498
43508
  }
@@ -43507,25 +43517,35 @@ async function rereadIds(qc, port, portKey, alias, ids) {
43507
43517
  unseen.push(answer.data);
43508
43518
  }
43509
43519
  }
43510
- if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen);
43520
+ if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen, await sourceKind(qc, port, portKey, alias));
43521
+ }
43522
+ function rereadUnknown(qc, portKey, alias) {
43523
+ for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
43524
+ if (!data || data.pages.length === 0) {
43525
+ void qc.invalidateQueries({ queryKey: key, exact: true });
43526
+ continue;
43527
+ }
43528
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
43529
+ else qc.setQueryData(key, { ...data, newer: Math.max(data.newer ?? 0, 1) });
43530
+ }
43531
+ void qc.invalidateQueries({ queryKey: [...dataKeys.alias(portKey, alias), "row"] });
43511
43532
  }
43512
43533
  function onBump(qc, port, portKey, alias, entry, event) {
43513
43534
  if (event.shape) {
43514
43535
  void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });
43515
43536
  return;
43516
43537
  }
43517
- if (event.ids === null) {
43518
- void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
43519
- for (const [key] of qc.getQueriesData({ queryKey: [...dataKeys.alias(portKey, alias), "row"] })) void qc.invalidateQueries({ queryKey: key });
43520
- return;
43521
- }
43522
- for (const id of event.ids) entry.pending.add(id);
43538
+ if (event.ids === null) entry.unknown = true;
43539
+ else for (const id of event.ids) entry.pending.add(id);
43523
43540
  if (entry.flush) return;
43524
43541
  entry.flush = setTimeout(() => {
43525
43542
  entry.flush = null;
43526
43543
  const ids = [...entry.pending];
43544
+ const unknown = entry.unknown;
43527
43545
  entry.pending.clear();
43528
- if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
43546
+ entry.unknown = false;
43547
+ if (unknown) rereadUnknown(qc, portKey, alias);
43548
+ else if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
43529
43549
  }, BUMP_BATCH_MS);
43530
43550
  }
43531
43551
  function acquireLive(qc, port, events, portKey, alias) {
@@ -43534,7 +43554,7 @@ function acquireLive(qc, port, events, portKey, alias) {
43534
43554
  let entry = home.get(key);
43535
43555
  if (!entry) {
43536
43556
  const made = { refs: 0, stop: () => {
43537
- }, timer: null, pending: /* @__PURE__ */ new Set(), flush: null };
43557
+ }, timer: null, pending: /* @__PURE__ */ new Set(), unknown: false, flush: null };
43538
43558
  if (events) {
43539
43559
  made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));
43540
43560
  } else if (!warnedNotLive) {
@@ -43622,6 +43642,15 @@ function useDataRows(alias, query = {}, scope) {
43622
43642
  };
43623
43643
  }
43624
43644
  var tempCounter = 0;
43645
+ function drawPatch(before, patch) {
43646
+ const drawn = { ...before, ...patch, _id: before._id, _version: before._version, _organization_id: before._organization_id };
43647
+ if (patch._custom && typeof patch._custom === "object") {
43648
+ drawn._custom = { ...before._custom ?? {}, ...patch._custom };
43649
+ } else if (before._custom !== void 0) {
43650
+ drawn._custom = before._custom;
43651
+ }
43652
+ return drawn;
43653
+ }
43625
43654
  function useDataMutations(alias, scope) {
43626
43655
  const qc = useRecordsQueryClient();
43627
43656
  const { port, portKey } = useDataPort(scope);
@@ -43661,7 +43690,7 @@ function useDataMutations(alias, scope) {
43661
43690
  await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });
43662
43691
  const rowKey = dataKeys.row(portKey, alias, id);
43663
43692
  const rowBefore = qc.getQueryData(rowKey);
43664
- if (before) patchRow(qc, portKey, alias, { ...before, ...patch, _id: id, _version: before._version, _organization_id: before._organization_id }, { force: true });
43693
+ if (before) patchRow(qc, portKey, alias, drawPatch(before, patch), { force: true });
43665
43694
  const answer = await port.update(alias, id, patch, baseVersion);
43666
43695
  if (answer.ok) {
43667
43696
  patchRow(qc, portKey, alias, answer.data, { force: true });