@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.76.2
4
+ ### Unreleased
5
+
6
+ **The data runtime takes platform sources (Applets AP-3 phase B, unit UE).** In `@ai-matrx/records/data/react`:
7
+ - A row nobody's loaded page holds is put at the top of a plain list only when the source is a custom `table`
8
+ (the engine asks `sources()` once, through the shared cache); a platform source's re-read can answer a row its
9
+ list would not show, so its short lists are asked again (one exact re-read) and its deep lists count it in `newer`.
10
+ - A notice that cannot name its rows (`ids: null`) is batched with the others and bounded the same way: one re-read
11
+ per short list, a `newer` mark on a deep one, never a re-read of every loaded page.
12
+ - An optimistic update merges `_custom` (new export `drawPatch`): editing one custom field never blanks the others.
13
+
3
14
  ## 0.76.1
4
15
 
5
16
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -27,6 +27,7 @@ __export(react_exports, {
27
27
  DataRefusal: () => DataRefusal,
28
28
  RECORDS_QUERY_ROOT: () => RECORDS_QUERY_ROOT,
29
29
  dataKeys: () => dataKeys,
30
+ drawPatch: () => drawPatch,
30
31
  hostEvents: () => hostEvents,
31
32
  useDataColumns: () => useDataColumns,
32
33
  useDataMutations: () => useDataMutations,
@@ -174,21 +175,31 @@ function isPlainNewestFirst(key) {
174
175
  }
175
176
  }
176
177
  var REREAD_PAGES_MAX = 2;
177
- function placeUnseen(qc, portKey, alias, rows) {
178
+ async function sourceKind(qc, port, portKey, alias) {
179
+ try {
180
+ const sources = await qc.ensureQueryData({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()) });
181
+ return sources.find((s) => s.alias === alias)?.kind ?? null;
182
+ } catch {
183
+ return null;
184
+ }
185
+ }
186
+ function rereadOrCount(qc, key, data, count) {
187
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
188
+ else qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + count });
189
+ }
190
+ function placeUnseen(qc, portKey, alias, rows, kind) {
178
191
  for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
179
192
  if (!data || data.pages.length === 0) continue;
180
193
  const missing = rows.filter((row) => !data.pages.some((page) => page.rows.some((r) => r._id === row._id)));
181
194
  if (missing.length === 0) continue;
182
- if (isPlainNewestFirst(key)) {
195
+ if (kind === "table" && isPlainNewestFirst(key)) {
183
196
  const [first, ...rest] = data.pages;
184
197
  qc.setQueryData(key, {
185
198
  ...data,
186
199
  pages: [{ ...first, rows: [...missing, ...first.rows], total: first.total === null ? null : first.total + missing.length }, ...rest]
187
200
  });
188
- } else if (data.pages.length <= REREAD_PAGES_MAX) {
189
- void qc.invalidateQueries({ queryKey: key, exact: true });
190
201
  } else {
191
- qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + missing.length });
202
+ rereadOrCount(qc, key, data, missing.length);
192
203
  }
193
204
  }
194
205
  }
@@ -203,25 +214,35 @@ async function rereadIds(qc, port, portKey, alias, ids) {
203
214
  unseen.push(answer.data);
204
215
  }
205
216
  }
206
- if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen);
217
+ if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen, await sourceKind(qc, port, portKey, alias));
218
+ }
219
+ function rereadUnknown(qc, portKey, alias) {
220
+ for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
221
+ if (!data || data.pages.length === 0) {
222
+ void qc.invalidateQueries({ queryKey: key, exact: true });
223
+ continue;
224
+ }
225
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
226
+ else qc.setQueryData(key, { ...data, newer: Math.max(data.newer ?? 0, 1) });
227
+ }
228
+ void qc.invalidateQueries({ queryKey: [...dataKeys.alias(portKey, alias), "row"] });
207
229
  }
208
230
  function onBump(qc, port, portKey, alias, entry, event) {
209
231
  if (event.shape) {
210
232
  void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });
211
233
  return;
212
234
  }
213
- if (event.ids === null) {
214
- void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
215
- for (const [key] of qc.getQueriesData({ queryKey: [...dataKeys.alias(portKey, alias), "row"] })) void qc.invalidateQueries({ queryKey: key });
216
- return;
217
- }
218
- for (const id of event.ids) entry.pending.add(id);
235
+ if (event.ids === null) entry.unknown = true;
236
+ else for (const id of event.ids) entry.pending.add(id);
219
237
  if (entry.flush) return;
220
238
  entry.flush = setTimeout(() => {
221
239
  entry.flush = null;
222
240
  const ids = [...entry.pending];
241
+ const unknown = entry.unknown;
223
242
  entry.pending.clear();
224
- if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
243
+ entry.unknown = false;
244
+ if (unknown) rereadUnknown(qc, portKey, alias);
245
+ else if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
225
246
  }, BUMP_BATCH_MS);
226
247
  }
227
248
  function acquireLive(qc, port, events, portKey, alias) {
@@ -230,7 +251,7 @@ function acquireLive(qc, port, events, portKey, alias) {
230
251
  let entry = home.get(key);
231
252
  if (!entry) {
232
253
  const made = { refs: 0, stop: () => {
233
- }, timer: null, pending: /* @__PURE__ */ new Set(), flush: null };
254
+ }, timer: null, pending: /* @__PURE__ */ new Set(), unknown: false, flush: null };
234
255
  if (events) {
235
256
  made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));
236
257
  } else if (!warnedNotLive) {
@@ -345,6 +366,15 @@ function useDataRow(alias, id, scope) {
345
366
  return { data: id ? q.data : null, loading: Boolean(id) && q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };
346
367
  }
347
368
  var tempCounter = 0;
369
+ function drawPatch(before, patch) {
370
+ const drawn = { ...before, ...patch, _id: before._id, _version: before._version, _organization_id: before._organization_id };
371
+ if (patch._custom && typeof patch._custom === "object") {
372
+ drawn._custom = { ...before._custom ?? {}, ...patch._custom };
373
+ } else if (before._custom !== void 0) {
374
+ drawn._custom = before._custom;
375
+ }
376
+ return drawn;
377
+ }
348
378
  function useDataMutations(alias, scope) {
349
379
  const qc = useRecordsQueryClient();
350
380
  const { port, portKey } = useDataPort(scope);
@@ -384,7 +414,7 @@ function useDataMutations(alias, scope) {
384
414
  await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });
385
415
  const rowKey = dataKeys.row(portKey, alias, id);
386
416
  const rowBefore = qc.getQueryData(rowKey);
387
- if (before) patchRow(qc, portKey, alias, { ...before, ...patch, _id: id, _version: before._version, _organization_id: before._organization_id }, { force: true });
417
+ if (before) patchRow(qc, portKey, alias, drawPatch(before, patch), { force: true });
388
418
  const answer = await port.update(alias, id, patch, baseVersion);
389
419
  if (answer.ok) {
390
420
  patchRow(qc, portKey, alias, answer.data, { force: true });
@@ -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":["QueryClientClass"]}
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":["QueryClientClass"]}
@@ -305,6 +305,11 @@ interface DataMutations {
305
305
  archive(id: Uuid): Promise<RecordsResult<void>>;
306
306
  restore(id: Uuid): Promise<RecordsResult<void>>;
307
307
  }
308
+ /**
309
+ * The row as an optimistic update draws it: the patch over the row, with `_custom` MERGED (a patch naming one
310
+ * custom field never blanks the others), and the runtime's own keys kept as they were.
311
+ */
312
+ declare function drawPatch(before: Row, patch: Values): Row;
308
313
  declare function useDataMutations(alias: string, scope?: DataScope): DataMutations;
309
314
 
310
- export { DATA_LIVE_GRACE_MS, type DataMutations, DataPortProvider, type DataPortProviderProps, type DataRead, DataRefusal, type DataRows, type DataScope, RECORDS_QUERY_ROOT, dataKeys, hostEvents, useDataColumns, useDataMutations, useDataPort, useDataRow, useDataRows, useDataSources, useRecordsQueryClient };
315
+ export { DATA_LIVE_GRACE_MS, type DataMutations, DataPortProvider, type DataPortProviderProps, type DataRead, DataRefusal, type DataRows, type DataScope, RECORDS_QUERY_ROOT, dataKeys, drawPatch, hostEvents, useDataColumns, useDataMutations, useDataPort, useDataRow, useDataRows, useDataSources, useRecordsQueryClient };
@@ -305,6 +305,11 @@ interface DataMutations {
305
305
  archive(id: Uuid): Promise<RecordsResult<void>>;
306
306
  restore(id: Uuid): Promise<RecordsResult<void>>;
307
307
  }
308
+ /**
309
+ * The row as an optimistic update draws it: the patch over the row, with `_custom` MERGED (a patch naming one
310
+ * custom field never blanks the others), and the runtime's own keys kept as they were.
311
+ */
312
+ declare function drawPatch(before: Row, patch: Values): Row;
308
313
  declare function useDataMutations(alias: string, scope?: DataScope): DataMutations;
309
314
 
310
- export { DATA_LIVE_GRACE_MS, type DataMutations, DataPortProvider, type DataPortProviderProps, type DataRead, DataRefusal, type DataRows, type DataScope, RECORDS_QUERY_ROOT, dataKeys, hostEvents, useDataColumns, useDataMutations, useDataPort, useDataRow, useDataRows, useDataSources, useRecordsQueryClient };
315
+ export { DATA_LIVE_GRACE_MS, type DataMutations, DataPortProvider, type DataPortProviderProps, type DataRead, DataRefusal, type DataRows, type DataScope, RECORDS_QUERY_ROOT, dataKeys, drawPatch, hostEvents, useDataColumns, useDataMutations, useDataPort, useDataRow, useDataRows, useDataSources, useRecordsQueryClient };
@@ -145,21 +145,31 @@ function isPlainNewestFirst(key) {
145
145
  }
146
146
  }
147
147
  var REREAD_PAGES_MAX = 2;
148
- function placeUnseen(qc, portKey, alias, rows) {
148
+ async function sourceKind(qc, port, portKey, alias) {
149
+ try {
150
+ const sources = await qc.ensureQueryData({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()) });
151
+ return sources.find((s) => s.alias === alias)?.kind ?? null;
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+ function rereadOrCount(qc, key, data, count) {
157
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
158
+ else qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + count });
159
+ }
160
+ function placeUnseen(qc, portKey, alias, rows, kind) {
149
161
  for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
150
162
  if (!data || data.pages.length === 0) continue;
151
163
  const missing = rows.filter((row) => !data.pages.some((page) => page.rows.some((r) => r._id === row._id)));
152
164
  if (missing.length === 0) continue;
153
- if (isPlainNewestFirst(key)) {
165
+ if (kind === "table" && isPlainNewestFirst(key)) {
154
166
  const [first, ...rest] = data.pages;
155
167
  qc.setQueryData(key, {
156
168
  ...data,
157
169
  pages: [{ ...first, rows: [...missing, ...first.rows], total: first.total === null ? null : first.total + missing.length }, ...rest]
158
170
  });
159
- } else if (data.pages.length <= REREAD_PAGES_MAX) {
160
- void qc.invalidateQueries({ queryKey: key, exact: true });
161
171
  } else {
162
- qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + missing.length });
172
+ rereadOrCount(qc, key, data, missing.length);
163
173
  }
164
174
  }
165
175
  }
@@ -174,25 +184,35 @@ async function rereadIds(qc, port, portKey, alias, ids) {
174
184
  unseen.push(answer.data);
175
185
  }
176
186
  }
177
- if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen);
187
+ if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen, await sourceKind(qc, port, portKey, alias));
188
+ }
189
+ function rereadUnknown(qc, portKey, alias) {
190
+ for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
191
+ if (!data || data.pages.length === 0) {
192
+ void qc.invalidateQueries({ queryKey: key, exact: true });
193
+ continue;
194
+ }
195
+ if (data.pages.length <= REREAD_PAGES_MAX) void qc.invalidateQueries({ queryKey: key, exact: true });
196
+ else qc.setQueryData(key, { ...data, newer: Math.max(data.newer ?? 0, 1) });
197
+ }
198
+ void qc.invalidateQueries({ queryKey: [...dataKeys.alias(portKey, alias), "row"] });
178
199
  }
179
200
  function onBump(qc, port, portKey, alias, entry, event) {
180
201
  if (event.shape) {
181
202
  void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });
182
203
  return;
183
204
  }
184
- if (event.ids === null) {
185
- void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
186
- for (const [key] of qc.getQueriesData({ queryKey: [...dataKeys.alias(portKey, alias), "row"] })) void qc.invalidateQueries({ queryKey: key });
187
- return;
188
- }
189
- for (const id of event.ids) entry.pending.add(id);
205
+ if (event.ids === null) entry.unknown = true;
206
+ else for (const id of event.ids) entry.pending.add(id);
190
207
  if (entry.flush) return;
191
208
  entry.flush = setTimeout(() => {
192
209
  entry.flush = null;
193
210
  const ids = [...entry.pending];
211
+ const unknown = entry.unknown;
194
212
  entry.pending.clear();
195
- if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
213
+ entry.unknown = false;
214
+ if (unknown) rereadUnknown(qc, portKey, alias);
215
+ else if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
196
216
  }, BUMP_BATCH_MS);
197
217
  }
198
218
  function acquireLive(qc, port, events, portKey, alias) {
@@ -201,7 +221,7 @@ function acquireLive(qc, port, events, portKey, alias) {
201
221
  let entry = home.get(key);
202
222
  if (!entry) {
203
223
  const made = { refs: 0, stop: () => {
204
- }, timer: null, pending: /* @__PURE__ */ new Set(), flush: null };
224
+ }, timer: null, pending: /* @__PURE__ */ new Set(), unknown: false, flush: null };
205
225
  if (events) {
206
226
  made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));
207
227
  } else if (!warnedNotLive) {
@@ -316,6 +336,15 @@ function useDataRow(alias, id, scope) {
316
336
  return { data: id ? q.data : null, loading: Boolean(id) && q.isPending, error: asRecordsError(q.error), refresh: () => void q.refetch() };
317
337
  }
318
338
  var tempCounter = 0;
339
+ function drawPatch(before, patch) {
340
+ const drawn = { ...before, ...patch, _id: before._id, _version: before._version, _organization_id: before._organization_id };
341
+ if (patch._custom && typeof patch._custom === "object") {
342
+ drawn._custom = { ...before._custom ?? {}, ...patch._custom };
343
+ } else if (before._custom !== void 0) {
344
+ drawn._custom = before._custom;
345
+ }
346
+ return drawn;
347
+ }
319
348
  function useDataMutations(alias, scope) {
320
349
  const qc = useRecordsQueryClient();
321
350
  const { port, portKey } = useDataPort(scope);
@@ -355,7 +384,7 @@ function useDataMutations(alias, scope) {
355
384
  await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });
356
385
  const rowKey = dataKeys.row(portKey, alias, id);
357
386
  const rowBefore = qc.getQueryData(rowKey);
358
- if (before) patchRow(qc, portKey, alias, { ...before, ...patch, _id: id, _version: before._version, _organization_id: before._organization_id }, { force: true });
387
+ if (before) patchRow(qc, portKey, alias, drawPatch(before, patch), { force: true });
359
388
  const answer = await port.update(alias, id, patch, baseVersion);
360
389
  if (answer.ok) {
361
390
  patchRow(qc, portKey, alias, answer.data, { force: true });
@@ -397,6 +426,7 @@ export {
397
426
  DataRefusal,
398
427
  RECORDS_QUERY_ROOT,
399
428
  dataKeys,
429
+ drawPatch,
400
430
  hostEvents,
401
431
  useDataColumns,
402
432
  useDataMutations,