@ai-matrx/records 0.80.8 → 0.81.1

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.
@@ -86,6 +86,8 @@ __export(react_exports, {
86
86
  RULE_USES: () => RULE_USES,
87
87
  RecordsHoldScope: () => RecordsHoldScope,
88
88
  RecordsProvider: () => RecordsProvider,
89
+ RecordsSeedProvider: () => RecordsSeedProvider,
90
+ SEED_FRESH_MS: () => SEED_FRESH_MS,
89
91
  SHOWN_TO_LABEL: () => SHOWN_TO_LABEL,
90
92
  SHOWN_TO_VALUES: () => SHOWN_TO_VALUES,
91
93
  SHOWN_TO_WHO_MAY_CHANGE: () => SHOWN_TO_WHO_MAY_CHANGE,
@@ -277,6 +279,7 @@ __export(react_exports, {
277
279
  useReverseColumns: () => useReverseColumns,
278
280
  useReverseLinks: () => useReverseLinks,
279
281
  useRowActions: () => useRowActions,
282
+ useSeededAnswer: () => useSeededAnswer,
280
283
  useStoreTable: () => useStoreTable,
281
284
  useTable: () => useTable,
282
285
  useTableArchive: () => useTableArchive,
@@ -300,6 +303,10 @@ function registry() {
300
303
  return g[REGISTRY] ??= /* @__PURE__ */ new Map();
301
304
  }
302
305
  var primedKey = (organizationId, tableId, viewId) => `${organizationId}|${tableId}|${viewId ?? ""}`;
306
+ function primeTablePageBundle(input) {
307
+ const answer = Promise.resolve(input.answer).catch((thrown) => ({ data: null, error: thrown }));
308
+ registry().set(primedKey(input.organizationId, input.tableId, input.viewId), { answer, at: Date.now() });
309
+ }
303
310
  function takePrimedTablePageBundle(organizationId, tableId, viewId) {
304
311
  if (!organizationId) return null;
305
312
  const key = primedKey(organizationId, tableId, viewId);
@@ -309,6 +316,9 @@ function takePrimedTablePageBundle(organizationId, tableId, viewId) {
309
316
  primed.delete(key);
310
317
  return Date.now() - held.at < PRIMED_TTL_MS ? held.answer : null;
311
318
  }
319
+ function primeRecordPageBundle(input) {
320
+ primeTablePageBundle({ organizationId: input.organizationId, tableId: `record:${input.recordId}`, answer: input.answer });
321
+ }
312
322
 
313
323
  // src/core/recordChanges.ts
314
324
  var listeners = /* @__PURE__ */ new Set();
@@ -426,8 +436,8 @@ function liftWithheld(rows) {
426
436
  }
427
437
 
428
438
  // src/subscription.ts
429
- function hasQuietHours(quiet) {
430
- return Boolean(quiet?.start && quiet?.end);
439
+ function hasQuietHours(quiet2) {
440
+ return Boolean(quiet2?.start && quiet2?.end);
431
441
  }
432
442
  function callersTimeZone() {
433
443
  try {
@@ -7851,7 +7861,7 @@ function useRecordsActor() {
7851
7861
  }
7852
7862
 
7853
7863
  // src/react/hooks.ts
7854
- var import_react3 = require("react");
7864
+ var import_react4 = require("react");
7855
7865
 
7856
7866
  // src/react/dataCache.ts
7857
7867
  var RECORDS_CACHE_GRACE_MS = 3e4;
@@ -8004,6 +8014,12 @@ function releaseEntry(handle) {
8004
8014
  entry.refs = Math.max(0, entry.refs - 1);
8005
8015
  if (entry.refs === 0) scheduleClose(entry);
8006
8016
  }
8017
+ function seedEntry(handle, data, stale) {
8018
+ const entry = handle;
8019
+ if (entry.started || entry.snapshot.data !== null) return;
8020
+ entry.snapshot = { data, loading: false, error: null };
8021
+ entry.started = !stale;
8022
+ }
8007
8023
  function openHold(holdKey) {
8008
8024
  const holds = root().holds;
8009
8025
  let hold = holds.get(holdKey);
@@ -8034,65 +8050,215 @@ function recordsCacheCounters() {
8034
8050
  return { ...root().counters };
8035
8051
  }
8036
8052
 
8037
- // src/react/hold.tsx
8053
+ // src/react/seed.tsx
8038
8054
  var import_react2 = require("react");
8039
- var HoldContext = (0, import_react2.createContext)(null);
8055
+
8056
+ // src/core/seed.ts
8057
+ var BUNDLE_DOORS = /* @__PURE__ */ new Set(["table_page_bundle", "record_page_bundle"]);
8058
+ function seedKey(door, args) {
8059
+ return `${door}|${canonicalArgs(args)}`;
8060
+ }
8061
+ function answersOf(door, args, data) {
8062
+ const out = [{ door, args, data }];
8063
+ if (!BUNDLE_DOORS.has(door) || !data || typeof data !== "object") return out;
8064
+ const parts = data.parts;
8065
+ if (!Array.isArray(parts)) return out;
8066
+ for (const part of parts) {
8067
+ if (part.error !== void 0 || part.data === void 0) continue;
8068
+ if (typeof part.door !== "string" || !part.args || typeof part.args !== "object") continue;
8069
+ out.push({ door: part.door, args: part.args, data: part.data });
8070
+ }
8071
+ return out;
8072
+ }
8073
+ function seedIndex(seed) {
8074
+ const index = /* @__PURE__ */ new Map();
8075
+ for (const a of seed.answers) {
8076
+ for (const spread of answersOf(a.door, a.args, a.data)) index.set(seedKey(spread.door, spread.args), spread.data);
8077
+ }
8078
+ return index;
8079
+ }
8080
+ var MISS = { code: "SEED_MISS", message: "Not in the page's seed.", details: null, hint: null };
8081
+ function missingQuery(onMiss) {
8082
+ const answer = () => {
8083
+ onMiss();
8084
+ return Promise.resolve({ data: null, error: MISS, count: null });
8085
+ };
8086
+ const chain = new Proxy(function() {
8087
+ }, {
8088
+ get(_target, prop) {
8089
+ if (prop === "then") return (ok2, bad) => answer().then(ok2, bad);
8090
+ return () => chain;
8091
+ },
8092
+ apply: () => chain
8093
+ });
8094
+ return { select: () => chain };
8095
+ }
8096
+ function seededDataSource(index) {
8097
+ let missed = false;
8098
+ const source = {
8099
+ rpc(fn, args) {
8100
+ const key = seedKey(fn, args);
8101
+ if (index.has(key)) return Promise.resolve({ data: index.get(key), error: null });
8102
+ missed = true;
8103
+ return Promise.resolve({ data: null, error: MISS });
8104
+ },
8105
+ schema: () => ({ from: () => missingQuery(() => missed = true) })
8106
+ };
8107
+ return { source, missed: () => missed };
8108
+ }
8109
+
8110
+ // src/react/seed.tsx
8111
+ var SEED_FRESH_MS = 3e4;
8112
+ var stores = /* @__PURE__ */ new WeakMap();
8113
+ var quiet = () => {
8114
+ };
8115
+ function storeFor(seed) {
8116
+ const held = stores.get(seed);
8117
+ if (held) return held;
8118
+ const index = seedIndex(seed);
8119
+ const runs = /* @__PURE__ */ new Map();
8120
+ const store = {
8121
+ live: true,
8122
+ run(slot, run, config) {
8123
+ const asked = runs.get(slot);
8124
+ if (asked) return asked;
8125
+ const { source, missed } = seededDataSource(index);
8126
+ const { realtime: _live, ...quietConfig } = config;
8127
+ const client = createRecordsClient({ ...quietConfig, dataSource: source, onError: quiet });
8128
+ const stale = typeof seed.at !== "number" || Date.now() - seed.at > SEED_FRESH_MS;
8129
+ const outcome = Promise.resolve().then(() => run(client)).then(
8130
+ (answer) => answer.ok && !missed() ? { seeded: true, data: answer.data, stale } : { seeded: false },
8131
+ () => ({ seeded: false })
8132
+ );
8133
+ runs.set(slot, outcome);
8134
+ return outcome;
8135
+ }
8136
+ };
8137
+ stores.set(seed, store);
8138
+ if (typeof window !== "undefined") {
8139
+ for (const a of seed.answers) {
8140
+ const org = a.args["p_organization_id"];
8141
+ if (typeof org !== "string") continue;
8142
+ if (a.door === "table_page_bundle" && typeof a.args["p_table_id"] === "string") {
8143
+ const view = a.args["p_view_id"];
8144
+ primeTablePageBundle({ organizationId: org, tableId: a.args["p_table_id"], viewId: typeof view === "string" ? view : null, answer: { data: a.data, error: null } });
8145
+ } else if (a.door === "record_page_bundle" && typeof a.args["p_record_id"] === "string") {
8146
+ primeRecordPageBundle({ organizationId: org, recordId: a.args["p_record_id"], answer: { data: a.data, error: null } });
8147
+ }
8148
+ }
8149
+ }
8150
+ return store;
8151
+ }
8152
+ var SeedContext = (0, import_react2.createContext)(null);
8153
+ function RecordsSeedProvider({ seed, children }) {
8154
+ const store = seed && Array.isArray(seed.answers) ? storeFor(seed) : null;
8155
+ (0, import_react2.useEffect)(() => {
8156
+ if (store) store.live = false;
8157
+ }, [store]);
8158
+ return (0, import_react2.createElement)(SeedContext.Provider, { value: store }, children);
8159
+ }
8160
+ function useSeedStore() {
8161
+ const store = (0, import_react2.useContext)(SeedContext);
8162
+ return store && store.live ? store : null;
8163
+ }
8164
+
8165
+ // src/react/hold.tsx
8166
+ var import_react3 = require("react");
8167
+ var HoldContext = (0, import_react3.createContext)(null);
8040
8168
  function RecordsHoldScope({ holdKey, children }) {
8041
- return (0, import_react2.createElement)(HoldContext.Provider, { value: holdKey }, children);
8169
+ return (0, import_react3.createElement)(HoldContext.Provider, { value: holdKey }, children);
8042
8170
  }
8043
8171
  function useRecordsHold(holdKey) {
8044
- (0, import_react2.useEffect)(() => holdKey ? openHold(holdKey) : void 0, [holdKey]);
8172
+ (0, import_react3.useEffect)(() => holdKey ? openHold(holdKey) : void 0, [holdKey]);
8045
8173
  }
8046
8174
  function useHoldKey() {
8047
- return (0, import_react2.useContext)(HoldContext);
8175
+ return (0, import_react3.useContext)(HoldContext);
8176
+ }
8177
+
8178
+ // src/react/pageOrder.ts
8179
+ function pageOrderOf(sort, narrowed) {
8180
+ return { sortFields: (sort ?? []).map((key) => key.field), narrowed };
8181
+ }
8182
+ var same = (a, b) => {
8183
+ if (a === b) return true;
8184
+ if ((a === null || a === void 0 || a === "") && (b === null || b === void 0 || b === "")) return true;
8185
+ return JSON.stringify(a) === JSON.stringify(b);
8186
+ };
8187
+ function patchMovesRows(held, fresh, order) {
8188
+ if (order.sortFields.length === 0 && !order.narrowed) return false;
8189
+ for (const row of held) {
8190
+ const next = fresh.get(row.id);
8191
+ if (!next) continue;
8192
+ const was = row.document ?? {};
8193
+ const now = next.document ?? {};
8194
+ if (order.narrowed) {
8195
+ if (!same(was, now)) return true;
8196
+ continue;
8197
+ }
8198
+ for (const field2 of order.sortFields) if (!same(was[field2], now[field2])) return true;
8199
+ }
8200
+ return false;
8048
8201
  }
8049
8202
 
8050
8203
  // src/react/hooks.ts
8051
8204
  function useAsync(run, key, live) {
8052
8205
  const client = useRecordsClient();
8053
8206
  const holdKey = useHoldKey();
8054
- const slot = (0, import_react3.useId)();
8055
- const entry = (0, import_react3.useMemo)(
8207
+ const slot = (0, import_react4.useId)();
8208
+ const entry = (0, import_react4.useMemo)(
8056
8209
  () => typeof window === "undefined" ? serverEntry(run, client) : entryFor(client, key, run, live ?? null),
8057
8210
  // eslint-disable-next-line react-hooks/exhaustive-deps
8058
8211
  [client, key]
8059
8212
  );
8060
8213
  entry.run = run;
8061
8214
  entry.client = client;
8062
- const snapshot = (0, import_react3.useSyncExternalStore)(entry.subscribe, entry.getSnapshot, entry.getSnapshot);
8063
- (0, import_react3.useEffect)(() => {
8215
+ const seed = useSeedStore();
8216
+ if (seed && entry.snapshot.data === null && !entry.snapshot.error && !entry.started) {
8217
+ const outcome = (0, import_react4.use)(seed.run(`${identityOf(client)}|${key}`, run, client.config));
8218
+ if (outcome.seeded) seedEntry(entry, outcome.data, outcome.stale);
8219
+ }
8220
+ const snapshot = (0, import_react4.useSyncExternalStore)(entry.subscribe, entry.getSnapshot, entry.getSnapshot);
8221
+ (0, import_react4.useEffect)(() => {
8064
8222
  acquireEntry(entry);
8065
8223
  return () => releaseEntry(entry);
8066
8224
  }, [entry]);
8067
- (0, import_react3.useEffect)(() => {
8225
+ (0, import_react4.useEffect)(() => {
8068
8226
  if (holdKey) pinToHold(holdKey, slot, entry);
8069
8227
  }, [holdKey, slot, entry]);
8070
- const lastData = (0, import_react3.useRef)(null);
8228
+ const lastData = (0, import_react4.useRef)(null);
8071
8229
  if (snapshot.data !== null) lastData.current = snapshot.data;
8072
8230
  const shown = snapshot.data === null && snapshot.loading && !snapshot.error && lastData.current !== null ? { ...snapshot, data: lastData.current } : snapshot;
8073
- const reload = (0, import_react3.useCallback)(() => entry.refetch(), [entry]);
8074
- const setData = (0, import_react3.useCallback)((update) => entry.setData(update), [entry]);
8231
+ const reload = (0, import_react4.useCallback)(() => entry.refetch(), [entry]);
8232
+ const setData = (0, import_react4.useCallback)((update) => entry.setData(update), [entry]);
8075
8233
  return { ...shown, reload, setData };
8076
8234
  }
8235
+ function useSeededAnswer(key, run) {
8236
+ const client = useRecordsClient();
8237
+ const seed = useSeedStore();
8238
+ if (!seed || key === null) return void 0;
8239
+ const outcome = (0, import_react4.use)(seed.run(`${identityOf(client)}|own:${key}`, run, client.config));
8240
+ return outcome.seeded ? outcome.data : void 0;
8241
+ }
8077
8242
  function serverEntry(run, client) {
8078
- const snapshot = { data: null, loading: true, error: null };
8079
- return {
8243
+ const entry = {
8080
8244
  key: "server",
8081
- snapshot,
8245
+ snapshot: { data: null, loading: true, error: null },
8082
8246
  run,
8083
8247
  client,
8084
8248
  subscribe: () => () => {
8085
8249
  },
8086
- getSnapshot: () => snapshot,
8250
+ // Read through the entry: a page seed may answer it during this render (`seedEntry`).
8251
+ getSnapshot: () => entry.snapshot,
8087
8252
  refetch: () => {
8088
8253
  },
8089
8254
  setData: () => {
8090
8255
  }
8091
8256
  };
8257
+ return entry;
8092
8258
  }
8093
8259
  function useTable(tableId) {
8094
8260
  const revision = useShapeRevision(tableId);
8095
- (0, import_react3.useEffect)(() => {
8261
+ (0, import_react4.useEffect)(() => {
8096
8262
  if (tableId) registerShapeInterest(tableId);
8097
8263
  }, [tableId]);
8098
8264
  return useAsync(
@@ -8149,12 +8315,12 @@ function subscribeToShape(listener) {
8149
8315
  };
8150
8316
  }
8151
8317
  function useShapeRevision(tableId) {
8152
- const read = (0, import_react3.useCallback)(() => tableId ? shapeRevisions.get(tableId) ?? 0 : 0, [tableId]);
8153
- return (0, import_react3.useSyncExternalStore)(subscribeToShape, read, () => 0);
8318
+ const read = (0, import_react4.useCallback)(() => tableId ? shapeRevisions.get(tableId) ?? 0 : 0, [tableId]);
8319
+ return (0, import_react4.useSyncExternalStore)(subscribeToShape, read, () => 0);
8154
8320
  }
8155
8321
  function useFields(tableId, recordType) {
8156
8322
  const revision = useShapeRevision(tableId);
8157
- (0, import_react3.useEffect)(() => {
8323
+ (0, import_react4.useEffect)(() => {
8158
8324
  if (tableId) registerShapeInterest(tableId);
8159
8325
  }, [tableId]);
8160
8326
  return useAsync(
@@ -8179,7 +8345,7 @@ async function answerOrRefusal(ask, where) {
8179
8345
  }
8180
8346
  }
8181
8347
  function useReverseColumns(tableId) {
8182
- const revision = (0, import_react3.useSyncExternalStore)(subscribeToShape, () => anyShapeRevision, () => 0);
8348
+ const revision = (0, import_react4.useSyncExternalStore)(subscribeToShape, () => anyShapeRevision, () => 0);
8183
8349
  return useAsync(
8184
8350
  async (client) => {
8185
8351
  if (!tableId || !doorReady(client.config, DOORS.reverseColumns)) return { ok: true, data: [] };
@@ -8189,8 +8355,8 @@ function useReverseColumns(tableId) {
8189
8355
  );
8190
8356
  }
8191
8357
  function useReverseLinks(tableId, columns, recordIds, limit = 3, offset = 0) {
8192
- const [writes, setWrites] = (0, import_react3.useState)(0);
8193
- (0, import_react3.useEffect)(() => onRecordChanged(() => setWrites((n) => n + 1)), []);
8358
+ const [writes, setWrites] = (0, import_react4.useState)(0);
8359
+ (0, import_react4.useEffect)(() => onRecordChanged(() => setWrites((n) => n + 1)), []);
8194
8360
  const idsKey = recordIds.join(",");
8195
8361
  const columnsKey = columns.map((c) => c.source_field_id).join(",");
8196
8362
  return useAsync(
@@ -8217,7 +8383,7 @@ function useReverseLinks(tableId, columns, recordIds, limit = 3, offset = 0) {
8217
8383
  tableId ? `reverse-links:${tableId}:${columnsKey}:${limit}:${offset}:${idsKey}:${writes}` : "reverse-links:none"
8218
8384
  );
8219
8385
  }
8220
- function liveRowsFor(tableId) {
8386
+ function liveRowsFor(tableId, order = { sortFields: [], narrowed: false }) {
8221
8387
  return (entry) => {
8222
8388
  const client = entry.client;
8223
8389
  const realtime = client.config.realtime;
@@ -8236,6 +8402,10 @@ function liveRowsFor(tableId) {
8236
8402
  entry.refetch();
8237
8403
  return;
8238
8404
  }
8405
+ if (patchMovesRows(held, fresh, order)) {
8406
+ entry.refetch();
8407
+ return;
8408
+ }
8239
8409
  entry.setData(
8240
8410
  (current) => current === null ? current : {
8241
8411
  ...current,
@@ -8285,7 +8455,7 @@ function liveStatus(client, tableId) {
8285
8455
  return { live, liveReason };
8286
8456
  }
8287
8457
  function useKeptPage(tableId, base) {
8288
- const lastGood = (0, import_react3.useRef)(null);
8458
+ const lastGood = (0, import_react4.useRef)(null);
8289
8459
  if (base.data !== null && !base.error) lastGood.current = { tableId, data: base.data };
8290
8460
  if (base.error && base.data === null && lastGood.current && lastGood.current.tableId === tableId) {
8291
8461
  return { ...base, data: lastGood.current.data };
@@ -8302,7 +8472,7 @@ function sameRowAnswer(a, b) {
8302
8472
  }
8303
8473
  }
8304
8474
  function useSharedRows(state) {
8305
- const held = (0, import_react3.useRef)({
8475
+ const held = (0, import_react4.useRef)({
8306
8476
  source: null,
8307
8477
  shared: null,
8308
8478
  byId: /* @__PURE__ */ new Map()
@@ -8343,7 +8513,7 @@ function useRecords(tableId, options = {}) {
8343
8513
  // on every render must not re-read the page on every render, and a parent
8344
8514
  // that changes the question must re-read on the first one.
8345
8515
  `records:${tableId ?? "none"}:${pageSize}:${page2}:${filterKey}:s${shape}`,
8346
- liveRowsFor(tableId)
8516
+ liveRowsFor(tableId, pageOrderOf(void 0, filterKey !== "{}"))
8347
8517
  );
8348
8518
  const { live, liveReason } = liveStatus(useRecordsClient(), tableId);
8349
8519
  return { ...useSharedRows(useKeptPage(tableId, base)), live, liveReason };
@@ -8371,14 +8541,14 @@ function useRecordPage(tableId, options = {}) {
8371
8541
  return answered.ok ? { ok: true, data: { rows: answered.data.rows, total: answered.data.total, sortIgnored: answered.data.sort_ignored ?? [] } } : answered;
8372
8542
  },
8373
8543
  `page:${tableId ?? "none"}:${pageSize}:${page2}:${filterKey}:${search ?? ""}:${sortKey}:${viewId ?? ""}:s${shape}`,
8374
- liveRowsFor(tableId)
8544
+ liveRowsFor(tableId, pageOrderOf(options.sort, filterKey !== "{}" || search !== null))
8375
8545
  );
8376
8546
  const { live, liveReason } = liveStatus(useRecordsClient(), tableId);
8377
8547
  return { ...useSharedRows(useKeptPage(tableId, base)), live, liveReason };
8378
8548
  }
8379
8549
  function useRecordChangeRevision(recordId) {
8380
- const [revision, setRevision] = (0, import_react3.useState)(0);
8381
- (0, import_react3.useEffect)(() => {
8550
+ const [revision, setRevision] = (0, import_react4.useState)(0);
8551
+ (0, import_react4.useEffect)(() => {
8382
8552
  if (!recordId) return;
8383
8553
  return onRecordChanged((changed) => {
8384
8554
  if (changed === recordId) setRevision((n) => n + 1);
@@ -8429,10 +8599,10 @@ function liveRecordFor(recordId, tableId) {
8429
8599
  }
8430
8600
  function useRecordMutation() {
8431
8601
  const client = useRecordsClient();
8432
- const [saving, setSaving] = (0, import_react3.useState)(false);
8433
- const [error, setError] = (0, import_react3.useState)(null);
8434
- const [conflict, setConflict] = (0, import_react3.useState)(null);
8435
- const run = (0, import_react3.useCallback)(
8602
+ const [saving, setSaving] = (0, import_react4.useState)(false);
8603
+ const [error, setError] = (0, import_react4.useState)(null);
8604
+ const [conflict, setConflict] = (0, import_react4.useState)(null);
8605
+ const run = (0, import_react4.useCallback)(
8436
8606
  async (recordId, call) => {
8437
8607
  setSaving(true);
8438
8608
  setError(null);
@@ -8451,7 +8621,7 @@ function useRecordMutation() {
8451
8621
  },
8452
8622
  []
8453
8623
  );
8454
- return (0, import_react3.useMemo)(
8624
+ return (0, import_react4.useMemo)(
8455
8625
  () => ({
8456
8626
  write: (args) => run(null, () => client.recordWrite({ table_id: args.table_id, data: args.data })),
8457
8627
  update: (args) => run(
@@ -8480,15 +8650,15 @@ function useTableArchive(tableId, options) {
8480
8650
  const client = useRecordsClient();
8481
8651
  const chunk = options?.chunk ?? TABLE_ARCHIVE_PASS;
8482
8652
  const includeTable = options?.includeTable ?? true;
8483
- const [progress, setProgress] = (0, import_react3.useState)(null);
8484
- const [running, setRunning] = (0, import_react3.useState)(false);
8485
- const [passes, setPasses] = (0, import_react3.useState)(0);
8486
- const [error, setError] = (0, import_react3.useState)(null);
8487
- const [interrupted, setInterrupted] = (0, import_react3.useState)(false);
8488
- const [chunkInUse, setChunkInUse] = (0, import_react3.useState)(chunk);
8489
- const [slowedDown, setSlowedDown] = (0, import_react3.useState)(false);
8490
- const cut = (0, import_react3.useRef)(false);
8491
- const preview = (0, import_react3.useCallback)(async () => {
8653
+ const [progress, setProgress] = (0, import_react4.useState)(null);
8654
+ const [running, setRunning] = (0, import_react4.useState)(false);
8655
+ const [passes, setPasses] = (0, import_react4.useState)(0);
8656
+ const [error, setError] = (0, import_react4.useState)(null);
8657
+ const [interrupted, setInterrupted] = (0, import_react4.useState)(false);
8658
+ const [chunkInUse, setChunkInUse] = (0, import_react4.useState)(chunk);
8659
+ const [slowedDown, setSlowedDown] = (0, import_react4.useState)(false);
8660
+ const cut = (0, import_react4.useRef)(false);
8661
+ const preview = (0, import_react4.useCallback)(async () => {
8492
8662
  setError(null);
8493
8663
  const answered = await client.tableArchive({ table_id: tableId, chunk: 0, includeTable });
8494
8664
  if (!answered.ok) {
@@ -8499,7 +8669,7 @@ function useTableArchive(tableId, options) {
8499
8669
  if (answered.data.in_progress) setInterrupted(true);
8500
8670
  return answered.data;
8501
8671
  }, [client, tableId, includeTable]);
8502
- const loop = (0, import_react3.useCallback)(async () => {
8672
+ const loop = (0, import_react4.useCallback)(async () => {
8503
8673
  cut.current = false;
8504
8674
  setRunning(true);
8505
8675
  setError(null);
@@ -8536,7 +8706,7 @@ function useTableArchive(tableId, options) {
8536
8706
  setRunning(false);
8537
8707
  }
8538
8708
  }, [client, tableId, chunk, includeTable]);
8539
- return (0, import_react3.useMemo)(
8709
+ return (0, import_react4.useMemo)(
8540
8710
  () => ({
8541
8711
  progress,
8542
8712
  running,
@@ -8581,11 +8751,11 @@ function useEntityBackLinks(token, recordId, options) {
8581
8751
  }) : Promise.resolve({ ok: true, data: null }),
8582
8752
  `entity-back-links:${scope}`
8583
8753
  );
8584
- const [more, setMore] = (0, import_react3.useState)({ scope, items: [], cursor: void 0, busy: false, error: null });
8754
+ const [more, setMore] = (0, import_react4.useState)({ scope, items: [], cursor: void 0, busy: false, error: null });
8585
8755
  const current = more.scope === scope ? more : { scope, items: [], cursor: void 0, busy: false, error: null };
8586
8756
  const firstAnswer = first.data;
8587
8757
  const cursor = current.cursor === void 0 ? firstAnswer?.next_cursor ?? null : current.cursor;
8588
- const loadMore = (0, import_react3.useCallback)(async () => {
8758
+ const loadMore = (0, import_react4.useCallback)(async () => {
8589
8759
  if (!asking || !cursor) return;
8590
8760
  setMore({ ...current, busy: true, error: null });
8591
8761
  const answered = await client.entityBackLinks({
@@ -8607,7 +8777,7 @@ function useEntityBackLinks(token, recordId, options) {
8607
8777
  error: null
8608
8778
  });
8609
8779
  }, [client, asking?.token, asking?.recordId, asking?.organizationId, limit, cursor, current, scope]);
8610
- const reload = (0, import_react3.useCallback)(() => {
8780
+ const reload = (0, import_react4.useCallback)(() => {
8611
8781
  setMore({ scope, items: [], cursor: void 0, busy: false, error: null });
8612
8782
  first.reload();
8613
8783
  }, [first, scope]);
@@ -8643,9 +8813,9 @@ function useEntityFieldRights(token) {
8643
8813
  }
8644
8814
  function useEntityFieldMutation() {
8645
8815
  const client = useRecordsClient();
8646
- const [saving, setSaving] = (0, import_react3.useState)(false);
8647
- const [error, setError] = (0, import_react3.useState)(null);
8648
- const run = (0, import_react3.useCallback)(
8816
+ const [saving, setSaving] = (0, import_react4.useState)(false);
8817
+ const [error, setError] = (0, import_react4.useState)(null);
8818
+ const run = (0, import_react4.useCallback)(
8649
8819
  async (call, whenRefused) => {
8650
8820
  setSaving(true);
8651
8821
  setError(null);
@@ -8660,7 +8830,7 @@ function useEntityFieldMutation() {
8660
8830
  },
8661
8831
  []
8662
8832
  );
8663
- return (0, import_react3.useMemo)(
8833
+ return (0, import_react4.useMemo)(
8664
8834
  () => ({
8665
8835
  declare: (args) => run(() => client.entityFieldDeclare({ token: args.token, spec: args.spec }), null),
8666
8836
  update: (args) => run(
@@ -8686,9 +8856,9 @@ function useEntityFieldMutation() {
8686
8856
  }
8687
8857
  function useFieldMutation() {
8688
8858
  const client = useRecordsClient();
8689
- const [saving, setSaving] = (0, import_react3.useState)(false);
8690
- const [error, setError] = (0, import_react3.useState)(null);
8691
- const run = (0, import_react3.useCallback)(
8859
+ const [saving, setSaving] = (0, import_react4.useState)(false);
8860
+ const [error, setError] = (0, import_react4.useState)(null);
8861
+ const run = (0, import_react4.useCallback)(
8692
8862
  async (call, whenRefused) => {
8693
8863
  setSaving(true);
8694
8864
  setError(null);
@@ -8703,14 +8873,14 @@ function useFieldMutation() {
8703
8873
  },
8704
8874
  []
8705
8875
  );
8706
- const announce = (0, import_react3.useCallback)((tableId) => {
8876
+ const announce = (0, import_react4.useCallback)((tableId) => {
8707
8877
  if (tableId) {
8708
8878
  tableShapeChanged(tableId);
8709
8879
  return;
8710
8880
  }
8711
8881
  for (const id of [...shapeRevisions.keys()]) tableShapeChanged(id);
8712
8882
  }, []);
8713
- return (0, import_react3.useMemo)(
8883
+ return (0, import_react4.useMemo)(
8714
8884
  () => ({
8715
8885
  declare: async (args) => {
8716
8886
  const made = await run(
@@ -8747,9 +8917,9 @@ function useFieldMutation() {
8747
8917
  }
8748
8918
  function useMergeField(key, state) {
8749
8919
  const client = useRecordsClient();
8750
- const [resolution, setResolution] = (0, import_react3.useState)(null);
8920
+ const [resolution, setResolution] = (0, import_react4.useState)(null);
8751
8921
  const resolver = client.config.resolver;
8752
- (0, import_react3.useEffect)(() => {
8922
+ (0, import_react4.useEffect)(() => {
8753
8923
  if (!key) {
8754
8924
  setResolution(null);
8755
8925
  return;
@@ -8784,11 +8954,11 @@ function useMergeField(key, state) {
8784
8954
  }
8785
8955
 
8786
8956
  // src/react/entity-reference.ts
8787
- var import_react4 = require("react");
8957
+ var import_react5 = require("react");
8788
8958
  function useEntityReferenceKinds() {
8789
8959
  const client = useRecordsClient();
8790
- const [state, setState] = (0, import_react4.useState)({ data: null, loading: true, error: null });
8791
- (0, import_react4.useEffect)(() => {
8960
+ const [state, setState] = (0, import_react5.useState)({ data: null, loading: true, error: null });
8961
+ (0, import_react5.useEffect)(() => {
8792
8962
  let live = true;
8793
8963
  void entityReferenceKinds(client.config.dataSource).then((res) => {
8794
8964
  if (!live) return;
@@ -8803,8 +8973,8 @@ function useEntityReferenceKinds() {
8803
8973
  function useEntityReferenceWords(refs) {
8804
8974
  const client = useRecordsClient();
8805
8975
  const key = refs.map((r) => `${r.token}:${r.id}`).join(",");
8806
- const [state, setState] = (0, import_react4.useState)({ data: null, loading: refs.length > 0, error: null });
8807
- (0, import_react4.useEffect)(() => {
8976
+ const [state, setState] = (0, import_react5.useState)({ data: null, loading: refs.length > 0, error: null });
8977
+ (0, import_react5.useEffect)(() => {
8808
8978
  let live = true;
8809
8979
  void entityReferenceWords(client.config.dataSource, client.config.organizationId, refs).then((res) => {
8810
8980
  if (!live) return;
@@ -8819,8 +8989,8 @@ function useEntityReferenceWords(refs) {
8819
8989
  function useEntityReferenceSearch(tokens, search, enabled = true) {
8820
8990
  const client = useRecordsClient();
8821
8991
  const key = `${tokens.join(",")}|${search.trim()}|${enabled ? 1 : 0}`;
8822
- const [state, setState] = (0, import_react4.useState)({ data: null, loading: enabled && tokens.length > 0, error: null });
8823
- (0, import_react4.useEffect)(() => {
8992
+ const [state, setState] = (0, import_react5.useState)({ data: null, loading: enabled && tokens.length > 0, error: null });
8993
+ (0, import_react5.useEffect)(() => {
8824
8994
  if (!enabled || tokens.length === 0) {
8825
8995
  setState({ data: [], loading: false, error: null });
8826
8996
  return;
@@ -8842,7 +9012,7 @@ function useEntityReferenceSearch(tokens, search, enabled = true) {
8842
9012
  }
8843
9013
 
8844
9014
  // src/react/useAppTable.ts
8845
- var import_react5 = require("react");
9015
+ var import_react6 = require("react");
8846
9016
 
8847
9017
  // src/core/declareTable.ts
8848
9018
  function tokenFor(name) {
@@ -9137,16 +9307,16 @@ function useAppTable(def, options = {}) {
9137
9307
  const client = useRecordsClient();
9138
9308
  const narrowed = options.organizationId ?? null;
9139
9309
  const writeOrg = narrowed ?? client.config.organizationId;
9140
- const [rows, setRows] = (0, import_react5.useState)([]);
9141
- const [total, setTotal] = (0, import_react5.useState)(null);
9142
- const [truncated, setTruncated] = (0, import_react5.useState)(false);
9143
- const [loading, setLoading] = (0, import_react5.useState)(true);
9144
- const [error, setError] = (0, import_react5.useState)(null);
9145
- const [status, setStatus] = (0, import_react5.useState)(null);
9146
- const [revision, setRevision] = (0, import_react5.useState)(0);
9147
- const rowsRef = (0, import_react5.useRef)([]);
9310
+ const [rows, setRows] = (0, import_react6.useState)([]);
9311
+ const [total, setTotal] = (0, import_react6.useState)(null);
9312
+ const [truncated, setTruncated] = (0, import_react6.useState)(false);
9313
+ const [loading, setLoading] = (0, import_react6.useState)(true);
9314
+ const [error, setError] = (0, import_react6.useState)(null);
9315
+ const [status, setStatus] = (0, import_react6.useState)(null);
9316
+ const [revision, setRevision] = (0, import_react6.useState)(0);
9317
+ const rowsRef = (0, import_react6.useRef)([]);
9148
9318
  rowsRef.current = rows;
9149
- (0, import_react5.useEffect)(() => {
9319
+ (0, import_react6.useEffect)(() => {
9150
9320
  let live = true;
9151
9321
  setLoading(true);
9152
9322
  (async () => {
@@ -9178,8 +9348,8 @@ function useAppTable(def, options = {}) {
9178
9348
  live = false;
9179
9349
  };
9180
9350
  }, [client, def.slug, def.kept_for, def.specHash, writeOrg, narrowed, revision]);
9181
- const reload = (0, import_react5.useCallback)(() => setRevision((n) => n + 1), []);
9182
- const upsert = (0, import_react5.useCallback)(
9351
+ const reload = (0, import_react6.useCallback)(() => setRevision((n) => n + 1), []);
9352
+ const upsert = (0, import_react6.useCallback)(
9183
9353
  async (data, upsertOptions = {}) => {
9184
9354
  if (!writeOrg) {
9185
9355
  return { ok: false, error: { code: "invalid_argument", message: `${def.name}: no organization to write to.` } };
@@ -9190,7 +9360,7 @@ function useAppTable(def, options = {}) {
9190
9360
  },
9191
9361
  [client, def, writeOrg, reload]
9192
9362
  );
9193
- const remove = (0, import_react5.useCallback)(
9363
+ const remove = (0, import_react6.useCallback)(
9194
9364
  async (rowId) => {
9195
9365
  const org = rowsRef.current.find((r) => r._id === rowId)?._organizationId ?? writeOrg;
9196
9366
  const answer = await removeAppRow(client, def, rowId, { organizationId: org });
@@ -9207,7 +9377,7 @@ function archivedTableOf(error) {
9207
9377
  }
9208
9378
 
9209
9379
  // src/react/useStoreTable.ts
9210
- var import_react7 = require("react");
9380
+ var import_react8 = require("react");
9211
9381
 
9212
9382
  // src/app-table/store-table.ts
9213
9383
  function storeTable(def) {
@@ -10442,7 +10612,7 @@ function createTableDataPort({ client, aliases }) {
10442
10612
  }
10443
10613
 
10444
10614
  // src/data/react.tsx
10445
- var import_react6 = require("react");
10615
+ var import_react7 = require("react");
10446
10616
  var import_react_query = require("@tanstack/react-query");
10447
10617
  var RECORDS_QUERY_ROOT = "@ai-matrx/records";
10448
10618
  var DATA_LIVE_GRACE_MS = 3e4;
@@ -10489,13 +10659,13 @@ function retryable(failureCount, thrown) {
10489
10659
  const code = thrown instanceof DataRefusal ? thrown.error.code : "unreachable";
10490
10660
  return (code === "unreachable" || code === "timed_out") && failureCount < 2;
10491
10661
  }
10492
- var DataPortContext = (0, import_react6.createContext)(null);
10662
+ var DataPortContext = (0, import_react7.createContext)(null);
10493
10663
  function hostEvents(port) {
10494
10664
  return { on: (event, listener) => port.subscribe(event, listener) };
10495
10665
  }
10496
10666
  var warnedNotLive = false;
10497
10667
  function useDataPort(scope) {
10498
- const held = (0, import_react6.useContext)(DataPortContext);
10668
+ const held = (0, import_react7.useContext)(DataPortContext);
10499
10669
  if (scope) return scope;
10500
10670
  if (!held) {
10501
10671
  throw new Error(
@@ -10781,7 +10951,7 @@ function acquireLive(qc, port, events, portKey, alias) {
10781
10951
  }
10782
10952
  var FALLBACK_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-query-client");
10783
10953
  function useRecordsQueryClient() {
10784
- const provided = (0, import_react6.useContext)(import_react_query.QueryClientContext);
10954
+ const provided = (0, import_react7.useContext)(import_react_query.QueryClientContext);
10785
10955
  if (provided) return provided;
10786
10956
  const g = globalThis;
10787
10957
  let held = g[FALLBACK_KEY];
@@ -10794,7 +10964,7 @@ function useRecordsQueryClient() {
10794
10964
  function useLive(alias, scope) {
10795
10965
  const qc = useRecordsQueryClient();
10796
10966
  const { port, portKey, events } = useDataPort(scope);
10797
- (0, import_react6.useEffect)(() => {
10967
+ (0, import_react7.useEffect)(() => {
10798
10968
  if (!alias) return;
10799
10969
  return acquireLive(qc, port, events, portKey, alias);
10800
10970
  }, [qc, port, events, portKey, alias]);
@@ -10814,7 +10984,7 @@ function useDataRows(alias, query = {}, scope) {
10814
10984
  },
10815
10985
  qc
10816
10986
  );
10817
- const rows = (0, import_react6.useMemo)(() => {
10987
+ const rows = (0, import_react7.useMemo)(() => {
10818
10988
  const seen = /* @__PURE__ */ new Set();
10819
10989
  const out = [];
10820
10990
  for (const page2 of q.data?.pages ?? []) {
@@ -10826,11 +10996,11 @@ function useDataRows(alias, query = {}, scope) {
10826
10996
  }
10827
10997
  return out;
10828
10998
  }, [q.data]);
10829
- const want = (0, import_react6.useRef)({ key: "", pages: 0 });
10999
+ const want = (0, import_react7.useRef)({ key: "", pages: 0 });
10830
11000
  const keyHash = (0, import_react_query.hashKey)(rowsKey);
10831
11001
  const loaded = q.data?.pages.length ?? 0;
10832
11002
  const { isFetching, hasNextPage, isFetchNextPageError, fetchNextPage } = q;
10833
- (0, import_react6.useEffect)(() => {
11003
+ (0, import_react7.useEffect)(() => {
10834
11004
  const w = want.current;
10835
11005
  if (w.key !== keyHash || isFetching || loaded >= w.pages) return;
10836
11006
  if (!hasNextPage || isFetchNextPageError) {
@@ -10870,7 +11040,7 @@ function drawPatch(before, patch) {
10870
11040
  function useDataMutations(alias, scope) {
10871
11041
  const qc = useRecordsQueryClient();
10872
11042
  const { port, portKey } = useDataPort(scope);
10873
- return (0, import_react6.useMemo)(() => {
11043
+ return (0, import_react7.useMemo)(() => {
10874
11044
  const snapshotRows = () => qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
10875
11045
  const putBack = (snap) => {
10876
11046
  for (const [key, data] of snap) qc.setQueryData(key, data);
@@ -10972,7 +11142,7 @@ function toNamed(table, row) {
10972
11142
  function useStoreTable(table, options = {}) {
10973
11143
  const client = useRecordsClient();
10974
11144
  const alias = table.id;
10975
- const scope = (0, import_react7.useMemo)(
11145
+ const scope = (0, import_react8.useMemo)(
10976
11146
  () => {
10977
11147
  const port = createTableDataPort({ client, aliases: { [alias]: { kind: "table", tableId: table.id, organizationId: table.organizationId, label: table.name } } });
10978
11148
  return { port, events: hostEvents(port), portKey: `store-table|${client.config.organizationId ?? "-"}|${client.config.actor.user_id ?? "-"}` };
@@ -10980,7 +11150,7 @@ function useStoreTable(table, options = {}) {
10980
11150
  [client, alias, table.id, table.organizationId, table.name]
10981
11151
  );
10982
11152
  const asked = JSON.stringify(options);
10983
- const built = (0, import_react7.useMemo)(() => {
11153
+ const built = (0, import_react8.useMemo)(() => {
10984
11154
  const where = [];
10985
11155
  for (const [name, value] of Object.entries(options.where ?? {})) {
10986
11156
  const column = table.columns[name];
@@ -11007,10 +11177,10 @@ function useStoreTable(table, options = {}) {
11007
11177
  const limit = Math.min(options.limit ?? STORE_ROWS_CEILING, STORE_ROWS_CEILING);
11008
11178
  const read = useDataRows(alias, built.ok ? built.data : { pageSize: 1 }, scope);
11009
11179
  const write = useDataMutations(alias, scope);
11010
- (0, import_react7.useEffect)(() => {
11180
+ (0, import_react8.useEffect)(() => {
11011
11181
  if (built.ok && read.hasMore && !read.loadingMore && !read.loading && !read.error && read.rows.length < limit) read.loadMore();
11012
11182
  }, [built.ok, read.hasMore, read.loadingMore, read.loading, read.error, read.rows.length, limit, read]);
11013
- const rows = (0, import_react7.useMemo)(() => read.rows.slice(0, limit).filter((r) => r["_pending"] !== true).map((r) => toNamed(table, r)), [read.rows, limit, table]);
11183
+ const rows = (0, import_react8.useMemo)(() => read.rows.slice(0, limit).filter((r) => r["_pending"] !== true).map((r) => toNamed(table, r)), [read.rows, limit, table]);
11014
11184
  const total = built.ok ? read.total : null;
11015
11185
  return {
11016
11186
  rows: built.ok ? rows : [],