@ai-matrx/records 0.87.32 → 0.92.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +115 -0
  3. package/dist/{client-E0VQ0NPO.d.ts → client-Cn3odIIB.d.cts} +10 -5
  4. package/dist/{client-C3ZZNfGH.d.cts → client-Dzd8gtAo.d.ts} +10 -5
  5. package/dist/core/index.cjs +28 -10
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +23 -12
  8. package/dist/core/index.d.ts +23 -12
  9. package/dist/core/index.js +28 -10
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/data/index.cjs +22 -10
  12. package/dist/data/index.cjs.map +1 -1
  13. package/dist/data/index.d.cts +12 -142
  14. package/dist/data/index.d.ts +12 -142
  15. package/dist/data/index.js +22 -10
  16. package/dist/data/index.js.map +1 -1
  17. package/dist/{declareTable-BK3zufr7.d.cts → declareTable-CrL6AZKB.d.cts} +4 -4
  18. package/dist/{declareTable-B12XybRp.d.ts → declareTable-ZRW5EsTQ.d.ts} +4 -4
  19. package/dist/entity/index.cjs +984 -0
  20. package/dist/entity/index.cjs.map +1 -0
  21. package/dist/entity/index.d.cts +36 -0
  22. package/dist/entity/index.d.ts +36 -0
  23. package/dist/entity/index.js +964 -0
  24. package/dist/entity/index.js.map +1 -0
  25. package/dist/entity/react.cjs +1024 -0
  26. package/dist/entity/react.cjs.map +1 -0
  27. package/dist/entity/react.d.cts +511 -0
  28. package/dist/entity/react.d.ts +511 -0
  29. package/dist/entity/react.js +1015 -0
  30. package/dist/entity/react.js.map +1 -0
  31. package/dist/{field-BanTVGK2.d.cts → field-CCY1rG9l.d.cts} +1 -1
  32. package/dist/{field-BPABOJ2Y.d.ts → field-enufS5ry.d.ts} +1 -1
  33. package/dist/forms/index.cjs +984 -0
  34. package/dist/forms/index.cjs.map +1 -0
  35. package/dist/forms/index.d.cts +333 -0
  36. package/dist/forms/index.d.ts +333 -0
  37. package/dist/forms/index.js +961 -0
  38. package/dist/forms/index.js.map +1 -0
  39. package/dist/index.cjs +10 -1
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +11 -8
  42. package/dist/index.d.ts +11 -8
  43. package/dist/index.js +10 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/memory/index.d.cts +7 -6
  46. package/dist/memory/index.d.ts +7 -6
  47. package/dist/port-C54ykAOv.d.ts +301 -0
  48. package/dist/port-Drioh9al.d.cts +301 -0
  49. package/dist/portal-DRdmZ-xv.d.cts +331 -0
  50. package/dist/portal-or_f7BkT.d.ts +331 -0
  51. package/dist/{ports-TMWX-t9l.d.ts → ports-BeOgjwYl.d.ts} +2 -2
  52. package/dist/{ports-D8-iuY1P.d.cts → ports-s0D2jvqn.d.cts} +2 -2
  53. package/dist/react/index.cjs +58 -526
  54. package/dist/react/index.cjs.map +1 -1
  55. package/dist/react/index.d.cts +16 -1
  56. package/dist/react/index.d.ts +16 -1
  57. package/dist/react/index.js +55 -530
  58. package/dist/react/index.js.map +1 -1
  59. package/dist/realtime/index.d.cts +2 -2
  60. package/dist/realtime/index.d.ts +2 -2
  61. package/dist/{record-DpB2QgAZ.d.cts → record-BwCf9wKW.d.cts} +7 -1
  62. package/dist/{record-DpB2QgAZ.d.ts → record-BwCf9wKW.d.ts} +7 -1
  63. package/dist/relation-8vAWCa67.d.cts +230 -0
  64. package/dist/relation-eLMgv_dV.d.ts +230 -0
  65. package/dist/rule-D9yNiorf.d.ts +250 -0
  66. package/dist/rule-wHI5B0Ka.d.cts +250 -0
  67. package/dist/search/index.cjs +283 -0
  68. package/dist/search/index.cjs.map +1 -0
  69. package/dist/search/index.d.cts +44 -0
  70. package/dist/search/index.d.ts +44 -0
  71. package/dist/search/index.js +260 -0
  72. package/dist/search/index.js.map +1 -0
  73. package/dist/setup/index.cjs +6854 -0
  74. package/dist/setup/index.cjs.map +1 -0
  75. package/dist/setup/index.d.cts +87 -0
  76. package/dist/setup/index.d.ts +87 -0
  77. package/dist/setup/index.js +6829 -0
  78. package/dist/setup/index.js.map +1 -0
  79. package/dist/{subscription-D7KQBHXN.d.ts → subscription-BynsG1K6.d.ts} +3 -249
  80. package/dist/{subscription-BD9DnJxX.d.cts → subscription-CdoI-UaB.d.cts} +3 -249
  81. package/dist/{table-kind-BZu8mtND.d.cts → table-kind-DzctQ7dO.d.cts} +3 -558
  82. package/dist/{table-kind-xKxTHtrV.d.ts → table-kind-i3lZJa3f.d.ts} +3 -558
  83. package/dist/templates/index.d.cts +10 -9
  84. package/dist/templates/index.d.ts +10 -9
  85. package/dist/typed-table/index.cjs +22 -10
  86. package/dist/typed-table/index.cjs.map +1 -1
  87. package/dist/typed-table/index.d.cts +10 -7
  88. package/dist/typed-table/index.d.ts +10 -7
  89. package/dist/typed-table/index.js +22 -10
  90. package/dist/typed-table/index.js.map +1 -1
  91. package/dist/types-BKYEeFt4.d.cts +139 -0
  92. package/dist/{types-Dv61jStB.d.ts → types-BvL_oKFl.d.ts} +2 -2
  93. package/dist/{types-CLP5H4Wk.d.cts → types-D8qdG3Lg.d.cts} +2 -2
  94. package/dist/{types-C2uspRtf.d.cts → types-DAFP6XFe.d.cts} +2 -2
  95. package/dist/{types-DmPkWQic.d.ts → types-DYzXhD7j.d.ts} +2 -2
  96. package/dist/types-cpKY7Pjb.d.ts +139 -0
  97. package/dist/use-cases/index.d.cts +5 -5
  98. package/dist/use-cases/index.d.ts +5 -5
  99. package/dist/{validate-CWVToe1T.d.ts → validate-DbvAParj.d.ts} +1 -1
  100. package/dist/{validate-DEB_mn4Y.d.cts → validate-DfNMxjmU.d.cts} +1 -1
  101. package/dist/versions/index.cjs +375 -0
  102. package/dist/versions/index.cjs.map +1 -0
  103. package/dist/versions/index.d.cts +160 -0
  104. package/dist/versions/index.d.ts +160 -0
  105. package/dist/versions/index.js +353 -0
  106. package/dist/versions/index.js.map +1 -0
  107. package/package.json +62 -2
@@ -171,9 +171,18 @@ function stableFilterKey(filter) {
171
171
  return `{${parts.join(",")}}`;
172
172
  }
173
173
  function liftWithheld(rows) {
174
+ let shared;
175
+ for (const row of rows) {
176
+ const m = (row.measures ?? {})["_withheld"];
177
+ if (m !== void 0 && m !== null && typeof m === "object") {
178
+ shared = m;
179
+ break;
180
+ }
181
+ }
174
182
  return rows.map((row) => {
175
183
  const measures = row.measures ?? {};
176
- const marker = measures["_withheld"];
184
+ const own = measures["_withheld"];
185
+ const marker = own !== void 0 && own !== null && typeof own === "object" ? own : shared;
177
186
  const rowCount = row.row_count ?? 0;
178
187
  if (marker === void 0 || marker === null || typeof marker !== "object") {
179
188
  return rowCount === row.row_count ? row : { ...row, row_count: rowCount };
@@ -4773,24 +4782,27 @@ function createRecordsClient(given) {
4773
4782
  if (!pass.ok) return pass;
4774
4783
  return carryOnCopy(pass.data, onProgress);
4775
4784
  },
4776
- async recordRead({ record_id, withComputed = false, organization_id }) {
4785
+ async recordRead({ record_id, withComputed = false, withVersion = false, organization_id }) {
4777
4786
  if (organization_id && organization_id !== org) {
4778
- return clientFor(organization_id).recordRead({ record_id, withComputed, organization_id });
4787
+ return clientFor(organization_id).recordRead({ record_id, withComputed, withVersion, organization_id });
4779
4788
  }
4780
4789
  if (readOrgs(organization_id).length > 1) {
4781
4790
  return openInOwnOrg(
4782
4791
  record_id,
4783
4792
  organization_id,
4784
- (sub) => sub.recordRead({ record_id, withComputed, organization_id: sub.config.organizationId }),
4793
+ (sub) => sub.recordRead({ record_id, withComputed, withVersion, organization_id: sub.config.organizationId }),
4785
4794
  () => true
4786
4795
  );
4787
4796
  }
4788
- const door = await callDoor(
4789
- DOORS.readRecord,
4790
- { p_organization_id: org, p_record_id: record_id },
4791
- "recordRead"
4792
- );
4797
+ const [door, header] = await Promise.all([
4798
+ callDoor(DOORS.readRecord, { p_organization_id: org, p_record_id: record_id }, "recordRead"),
4799
+ // A version that cannot be read never takes the record away: it comes back as null.
4800
+ withVersion ? callDoor(DOORS.recordHeaders, { p_organization_id: org, p_ids: [record_id] }, "recordRead.version").catch(
4801
+ () => null
4802
+ ) : Promise.resolve(null)
4803
+ ]);
4793
4804
  if (!door.ok) return err(door.error);
4805
+ const version = header && header.ok ? header.data?.find((h) => h.id === record_id)?.version ?? null : null;
4794
4806
  const { document, hidden } = unfold(door.data);
4795
4807
  let computed = [];
4796
4808
  let computedUnavailable = null;
@@ -4803,7 +4815,7 @@ function createRecordsClient(given) {
4803
4815
  if (provenance.ok) computed = provenance.data ?? [];
4804
4816
  else computedUnavailable = provenance.error;
4805
4817
  }
4806
- return ok({ record_id, document, hidden, computed, computedUnavailable });
4818
+ return ok({ record_id, document, hidden, computed, computedUnavailable, ...withVersion ? { version } : {} });
4807
4819
  },
4808
4820
  async listByIds({ table_id, ids }) {
4809
4821
  if (ids.length === 0) return ok([]);
@@ -8440,11 +8452,14 @@ function liveRowsFor(tableId, order = { sortFields: [], narrowed: false }) {
8440
8452
  );
8441
8453
  };
8442
8454
  let timer = null;
8443
- const stopLocal = onRecordChanged(() => {
8455
+ const written = /* @__PURE__ */ new Set();
8456
+ const stopLocal = onRecordChanged((changed) => {
8457
+ written.add(changed);
8444
8458
  if (timer !== null) clearTimeout(timer);
8445
8459
  timer = setTimeout(() => {
8446
8460
  timer = null;
8447
- const ids = (entry.snapshot.data?.rows ?? []).map((row) => row.id);
8461
+ const ids = [.../* @__PURE__ */ new Set([...(entry.snapshot.data?.rows ?? []).map((row) => row.id), ...written])];
8462
+ written.clear();
8448
8463
  if (ids.length > 0) patchIds(ids).catch(() => entry.refetch());
8449
8464
  }, 250);
8450
8465
  });
@@ -8572,6 +8587,24 @@ function useRecordPage(tableId, options = {}) {
8572
8587
  const { live, liveReason } = liveStatus(useRecordsClient(), tableId);
8573
8588
  return { ...useSharedRows(useKeptPage(tableId, base)), live, liveReason };
8574
8589
  }
8590
+ function useRecordWrites() {
8591
+ const [writes, setWrites] = useState(0);
8592
+ useEffect3(() => {
8593
+ let timer = null;
8594
+ const stop = onRecordChanged(() => {
8595
+ if (timer !== null) clearTimeout(timer);
8596
+ timer = setTimeout(() => {
8597
+ timer = null;
8598
+ setWrites((n) => n + 1);
8599
+ }, 250);
8600
+ });
8601
+ return () => {
8602
+ if (timer !== null) clearTimeout(timer);
8603
+ stop();
8604
+ };
8605
+ }, []);
8606
+ return writes;
8607
+ }
8575
8608
  function useRecordChangeRevision(recordId) {
8576
8609
  const [revision, setRevision] = useState(0);
8577
8610
  useEffect3(() => {
@@ -9596,7 +9629,7 @@ function archivedTableOf(error) {
9596
9629
  }
9597
9630
 
9598
9631
  // src/react/useStoreTable.ts
9599
- import { useEffect as useEffect7, useMemo as useMemo4 } from "react";
9632
+ import { useEffect as useEffect6, useMemo as useMemo3 } from "react";
9600
9633
 
9601
9634
  // src/typed-table/store-table.ts
9602
9635
  function storeTable(def) {
@@ -10689,7 +10722,7 @@ function blankToNull(type, value, field2) {
10689
10722
  }
10690
10723
  function createTableDataPort({ client, aliases }) {
10691
10724
  const shapes = /* @__PURE__ */ new Map();
10692
- let warnedNotLive2 = false;
10725
+ let warnedNotLive = false;
10693
10726
  function bindingOf(alias) {
10694
10727
  return Object.prototype.hasOwnProperty.call(aliases, alias) ? aliases[alias] : null;
10695
10728
  }
@@ -10889,8 +10922,8 @@ function createTableDataPort({ client, aliases }) {
10889
10922
  }
10890
10923
  const realtime = client.config.realtime;
10891
10924
  if (!realtime) {
10892
- if (!warnedNotLive2) {
10893
- warnedNotLive2 = true;
10925
+ if (!warnedNotLive) {
10926
+ warnedNotLive = true;
10894
10927
  console.warn("[@ai-matrx/records/data] This host bound no realtime port, so other people's changes will not appear until a re-read.");
10895
10928
  }
10896
10929
  return () => {
@@ -10910,517 +10943,8 @@ function createTableDataPort({ client, aliases }) {
10910
10943
  };
10911
10944
  }
10912
10945
 
10913
- // src/data/react.tsx
10914
- import { createContext as createContext4, createElement as createElement4, useContext as useContext4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef4 } from "react";
10915
- import {
10916
- QueryClient as QueryClientClass,
10917
- QueryClientContext,
10918
- QueryClientProvider,
10919
- hashKey,
10920
- useInfiniteQuery,
10921
- useQuery
10922
- } from "@tanstack/react-query";
10923
- var RECORDS_QUERY_ROOT = "@ai-matrx/records";
10924
- var DATA_LIVE_GRACE_MS = 3e4;
10925
- var BUMP_BATCH_MS = 40;
10926
- var dataKeys = {
10927
- all: (portKey) => [RECORDS_QUERY_ROOT, "data", portKey],
10928
- sources: (portKey) => [RECORDS_QUERY_ROOT, "data", portKey, "sources"],
10929
- alias: (portKey, alias) => [RECORDS_QUERY_ROOT, "data", portKey, "alias", alias],
10930
- columns: (portKey, alias) => [...dataKeys.alias(portKey, alias), "columns"],
10931
- rowsPrefix: (portKey, alias) => [...dataKeys.alias(portKey, alias), "rows"],
10932
- rows: (portKey, alias, query) => [...dataKeys.rowsPrefix(portKey, alias), stableQuery(query)],
10933
- row: (portKey, alias, id) => [...dataKeys.alias(portKey, alias), "row", id]
10934
- };
10935
- function stableQuery(query) {
10936
- const sortKeys = (value) => {
10937
- if (Array.isArray(value)) return value.map(sortKeys);
10938
- if (value && typeof value === "object") {
10939
- return Object.fromEntries(
10940
- Object.keys(value).sort().filter((k) => value[k] !== void 0).map((k) => [k, sortKeys(value[k])])
10941
- );
10942
- }
10943
- return value;
10944
- };
10945
- return JSON.stringify(sortKeys(query ?? {}));
10946
- }
10947
- var DataRefusal = class extends Error {
10948
- error;
10949
- constructor(error) {
10950
- super(error.message);
10951
- this.name = "DataRefusal";
10952
- this.error = error;
10953
- }
10954
- };
10955
- function unwrap(answer) {
10956
- if (answer.ok) return answer.data;
10957
- throw new DataRefusal(answer.error);
10958
- }
10959
- function asRecordsError(thrown) {
10960
- if (!thrown) return null;
10961
- if (thrown instanceof DataRefusal) return thrown.error;
10962
- return { code: "unreachable", message: `The data could not be reached: ${thrown instanceof Error ? thrown.message : String(thrown)}` };
10963
- }
10964
- function retryable(failureCount, thrown) {
10965
- const code = thrown instanceof DataRefusal ? thrown.error.code : "unreachable";
10966
- return (code === "unreachable" || code === "timed_out") && failureCount < 2;
10967
- }
10968
- var DataPortContext = createContext4(null);
10969
- function hostEvents(port) {
10970
- return { on: (event, listener) => port.subscribe(event, listener) };
10971
- }
10972
- var warnedNotLive = false;
10973
- function useDataPort(scope) {
10974
- const held = useContext4(DataPortContext);
10975
- if (scope) return scope;
10976
- if (!held) {
10977
- throw new Error(
10978
- "A data hook was called outside <DataPortProvider>. The frame mounts it once with the host's DataPort and this Applet's portKey."
10979
- );
10980
- }
10981
- return held;
10982
- }
10983
- function forEachRowsEntry(qc, portKey, alias, fn) {
10984
- for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
10985
- if (!data) continue;
10986
- const next = fn(key, data);
10987
- if (next && next !== data) qc.setQueryData(key, next);
10988
- }
10989
- }
10990
- var LANDED_MS = 3e4;
10991
- var LANDED_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-landed");
10992
- function landedHome(qc) {
10993
- const holder = qc;
10994
- let home = holder[LANDED_KEY];
10995
- if (!home) {
10996
- const made = { byAlias: /* @__PURE__ */ new Map(), unsubscribe: () => {
10997
- } };
10998
- made.unsubscribe = qc.getQueryCache().subscribe((event) => {
10999
- if (event.type !== "updated" || event.action.type !== "success") return;
11000
- const key = event.query.queryKey;
11001
- if (key[0] !== RECORDS_QUERY_ROOT || key[1] !== "data" || key[3] !== "alias" || key[5] !== "rows") return;
11002
- const portKey = String(key[2]);
11003
- const alias = String(key[4]);
11004
- const landed = made.byAlias.get(`${portKey}${alias}`);
11005
- if (!landed || landed.size === 0) return;
11006
- reapplyLanded(qc, event.query.queryKey, landed);
11007
- });
11008
- holder[LANDED_KEY] = made;
11009
- home = made;
11010
- }
11011
- return home;
11012
- }
11013
- function remember(qc, portKey, alias, id, row) {
11014
- const home = landedHome(qc);
11015
- const key = `${portKey}${alias}`;
11016
- let landed = home.byAlias.get(key);
11017
- if (!landed) home.byAlias.set(key, landed = /* @__PURE__ */ new Map());
11018
- const now = Date.now();
11019
- for (const [oldId, entry] of landed) if (now - entry.at > LANDED_MS) landed.delete(oldId);
11020
- landed.set(id, { row, at: now });
11021
- }
11022
- function reapplyLanded(qc, key, landed) {
11023
- const data = qc.getQueryData(key);
11024
- if (!data) return;
11025
- const now = Date.now();
11026
- let changed = false;
11027
- const pages = data.pages.map((page2) => {
11028
- let rows = null;
11029
- page2.rows.forEach((r, i) => {
11030
- const entry = landed.get(r._id);
11031
- if (!entry || now - entry.at > LANDED_MS) return;
11032
- if (entry.row === null) {
11033
- rows ??= page2.rows.slice();
11034
- rows[i] = null;
11035
- } else if (entry.row._version > r._version) {
11036
- rows ??= page2.rows.slice();
11037
- rows[i] = entry.row;
11038
- }
11039
- });
11040
- if (!rows) return page2;
11041
- changed = true;
11042
- return { ...page2, rows: rows.filter((r) => r !== null) };
11043
- });
11044
- if (changed) qc.setQueryData(key, { ...data, pages });
11045
- }
11046
- function patchRow(qc, portKey, alias, row, options = {}) {
11047
- if (options.fromStore) remember(qc, portKey, alias, row._id, row);
11048
- let held = false;
11049
- forEachRowsEntry(qc, portKey, alias, (_key, data) => {
11050
- let changed = false;
11051
- const pages = data.pages.map((page2) => {
11052
- const i = page2.rows.findIndex((r) => r._id === row._id);
11053
- if (i < 0) return page2;
11054
- held = true;
11055
- if (!options.force && page2.rows[i]._version > row._version) return page2;
11056
- changed = true;
11057
- const rows = page2.rows.slice();
11058
- rows[i] = row;
11059
- return { ...page2, rows };
11060
- });
11061
- return changed ? { ...data, pages } : data;
11062
- });
11063
- const rowKey = dataKeys.row(portKey, alias, row._id);
11064
- const current = qc.getQueryData(rowKey);
11065
- if (options.force || !current || current._version <= row._version) qc.setQueryData(rowKey, row);
11066
- return held;
11067
- }
11068
- function dropRow(qc, portKey, alias, id, options = {}) {
11069
- if (options.fromStore) remember(qc, portKey, alias, id, null);
11070
- forEachRowsEntry(qc, portKey, alias, (_key, data) => {
11071
- let changed = false;
11072
- const pages = data.pages.map((page2) => {
11073
- if (!page2.rows.some((r) => r._id === id)) return page2;
11074
- changed = true;
11075
- return { ...page2, rows: page2.rows.filter((r) => r._id !== id), total: page2.total === null ? null : Math.max(0, page2.total - 1) };
11076
- });
11077
- return changed ? { ...data, pages } : data;
11078
- });
11079
- qc.setQueryData(dataKeys.row(portKey, alias, id), null);
11080
- }
11081
- function findRow(qc, portKey, alias, id) {
11082
- const direct = qc.getQueryData(dataKeys.row(portKey, alias, id));
11083
- if (direct) return direct;
11084
- for (const [, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
11085
- for (const page2 of data?.pages ?? []) {
11086
- const hit = page2.rows.find((r) => r._id === id);
11087
- if (hit) return hit;
11088
- }
11089
- }
11090
- return null;
11091
- }
11092
- var LIVE_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-live");
11093
- function liveHome(qc) {
11094
- const holder = qc;
11095
- let home = holder[LIVE_KEY];
11096
- if (!home) {
11097
- home = /* @__PURE__ */ new Map();
11098
- holder[LIVE_KEY] = home;
11099
- }
11100
- return home;
11101
- }
11102
- function isPlainNewestFirst(key) {
11103
- try {
11104
- const q = JSON.parse(String(key[key.length - 1]));
11105
- return !(q.where && q.where.length) && !q.search?.trim() && !(q.sort && q.sort.length);
11106
- } catch {
11107
- return false;
11108
- }
11109
- }
11110
- var REREAD_PAGES_MAX = 2;
11111
- async function sourceKind(qc, port, portKey, alias) {
11112
- try {
11113
- const sources = await qc.ensureQueryData({ queryKey: dataKeys.sources(portKey), queryFn: async () => unwrap(await port.sources()) });
11114
- return sources.find((s) => s.alias === alias)?.kind ?? null;
11115
- } catch {
11116
- return null;
11117
- }
11118
- }
11119
- var DEFERRED_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-deferred-rereads");
11120
- var OWED_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-owed-pages");
11121
- function owedPages(qc) {
11122
- const holder = qc;
11123
- return holder[OWED_KEY] ??= /* @__PURE__ */ new Map();
11124
- }
11125
- function owedPage(qc, hash) {
11126
- const owed = owedPages(qc).get(hash);
11127
- if (!owed) return null;
11128
- const query = qc.getQueryCache().get(hash);
11129
- const data = query?.state.data;
11130
- const last = data?.pages[data.pages.length - 1];
11131
- if (!query || query.state.status === "error" || !data || data.pages.length >= owed.pages || !last?.cursor) {
11132
- owedPages(qc).delete(hash);
11133
- return null;
11134
- }
11135
- return owed;
11136
- }
11137
- function rereadList(qc, key) {
11138
- const query = qc.getQueryCache().find({ queryKey: key, exact: true });
11139
- if (!query || query.state.fetchStatus !== "fetching" && !owedPage(qc, query.queryHash)) {
11140
- void qc.invalidateQueries({ queryKey: key, exact: true });
11141
- return;
11142
- }
11143
- const holder = qc;
11144
- const waiting = holder[DEFERRED_KEY] ??= /* @__PURE__ */ new Set();
11145
- if (waiting.has(query.queryHash)) return;
11146
- waiting.add(query.queryHash);
11147
- const settle = () => {
11148
- if (query.state.fetchStatus === "fetching") return;
11149
- const owed = owedPage(qc, query.queryHash);
11150
- if (owed) {
11151
- owed.fetchNext();
11152
- return;
11153
- }
11154
- stop();
11155
- waiting.delete(query.queryHash);
11156
- void qc.invalidateQueries({ queryKey: key, exact: true });
11157
- };
11158
- const stop = qc.getQueryCache().subscribe((event) => {
11159
- if (event.query === query) settle();
11160
- });
11161
- settle();
11162
- }
11163
- function rereadOrCount(qc, key, data, count) {
11164
- if (data.pages.length <= REREAD_PAGES_MAX) rereadList(qc, key);
11165
- else qc.setQueryData(key, { ...data, newer: (data.newer ?? 0) + count });
11166
- }
11167
- function placeUnseen(qc, portKey, alias, rows, kind) {
11168
- for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
11169
- if (!data || data.pages.length === 0) continue;
11170
- const missing = rows.filter((row) => !data.pages.some((page2) => page2.rows.some((r) => r._id === row._id)));
11171
- if (missing.length === 0) continue;
11172
- if (kind === "table" && isPlainNewestFirst(key)) {
11173
- const [first, ...rest] = data.pages;
11174
- qc.setQueryData(key, {
11175
- ...data,
11176
- pages: [{ ...first, rows: [...missing, ...first.rows], total: first.total === null ? null : first.total + missing.length }, ...rest]
11177
- });
11178
- } else {
11179
- rereadOrCount(qc, key, data, missing.length);
11180
- }
11181
- }
11182
- }
11183
- async function rereadIds(qc, port, portKey, alias, ids) {
11184
- const answers = await Promise.all(ids.map((id) => port.get(alias, id).then((a) => [id, a])));
11185
- const unseen = [];
11186
- for (const [id, answer] of answers) {
11187
- if (!answer.ok) continue;
11188
- if (answer.data === null) {
11189
- dropRow(qc, portKey, alias, id, { fromStore: true });
11190
- } else if (!patchRow(qc, portKey, alias, answer.data, { fromStore: true })) {
11191
- unseen.push(answer.data);
11192
- }
11193
- }
11194
- if (unseen.length > 0) placeUnseen(qc, portKey, alias, unseen, await sourceKind(qc, port, portKey, alias));
11195
- }
11196
- function rereadUnknown(qc, portKey, alias) {
11197
- for (const [key, data] of qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) })) {
11198
- if (!data || data.pages.length === 0) {
11199
- rereadList(qc, key);
11200
- continue;
11201
- }
11202
- if (data.pages.length <= REREAD_PAGES_MAX) rereadList(qc, key);
11203
- else qc.setQueryData(key, { ...data, newer: Math.max(data.newer ?? 0, 1) });
11204
- }
11205
- void qc.invalidateQueries({ queryKey: [...dataKeys.alias(portKey, alias), "row"] });
11206
- }
11207
- function onBump(qc, port, portKey, alias, entry, event) {
11208
- if (event.shape) {
11209
- void qc.invalidateQueries({ queryKey: dataKeys.alias(portKey, alias) });
11210
- return;
11211
- }
11212
- if (event.ids === null) entry.unknown = true;
11213
- else for (const id of event.ids) entry.pending.add(id);
11214
- if (entry.flush) return;
11215
- entry.flush = setTimeout(() => {
11216
- entry.flush = null;
11217
- const ids = [...entry.pending];
11218
- const unknown = entry.unknown;
11219
- entry.pending.clear();
11220
- entry.unknown = false;
11221
- if (unknown) rereadUnknown(qc, portKey, alias);
11222
- else if (ids.length > 0) void rereadIds(qc, port, portKey, alias, ids);
11223
- }, BUMP_BATCH_MS);
11224
- }
11225
- function acquireLive(qc, port, events, portKey, alias) {
11226
- const home = liveHome(qc);
11227
- const key = `${portKey}${alias}`;
11228
- let entry = home.get(key);
11229
- if (!entry) {
11230
- const made = { refs: 0, stop: () => {
11231
- }, timer: null, pending: /* @__PURE__ */ new Set(), unknown: false, flush: null };
11232
- if (events) {
11233
- made.stop = events.on(`rows:${alias}`, (event) => onBump(qc, port, portKey, alias, made, event));
11234
- } else if (!warnedNotLive) {
11235
- warnedNotLive = true;
11236
- console.warn("[@ai-matrx/records/data/react] No event stream was given to DataPortProvider, so this view is not live.");
11237
- }
11238
- home.set(key, made);
11239
- entry = made;
11240
- }
11241
- const mine = entry;
11242
- mine.refs += 1;
11243
- if (mine.timer) {
11244
- clearTimeout(mine.timer);
11245
- mine.timer = null;
11246
- }
11247
- return () => {
11248
- mine.refs -= 1;
11249
- if (mine.refs > 0) return;
11250
- mine.timer = setTimeout(() => {
11251
- if (mine.refs > 0) return;
11252
- if (mine.flush) clearTimeout(mine.flush);
11253
- mine.stop();
11254
- if (home.get(key) === mine) home.delete(key);
11255
- }, DATA_LIVE_GRACE_MS);
11256
- };
11257
- }
11258
- var FALLBACK_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/data-query-client");
11259
- function useRecordsQueryClient() {
11260
- const provided = useContext4(QueryClientContext);
11261
- if (provided) return provided;
11262
- const g = globalThis;
11263
- let held = g[FALLBACK_KEY];
11264
- if (!held) {
11265
- held = new QueryClientClass({ defaultOptions: { queries: { staleTime: 3e4, refetchOnWindowFocus: false } } });
11266
- g[FALLBACK_KEY] = held;
11267
- }
11268
- return held;
11269
- }
11270
- function useLive(alias, scope) {
11271
- const qc = useRecordsQueryClient();
11272
- const { port, portKey, events } = useDataPort(scope);
11273
- useEffect6(() => {
11274
- if (!alias) return;
11275
- return acquireLive(qc, port, events, portKey, alias);
11276
- }, [qc, port, events, portKey, alias]);
11277
- }
11278
- function useDataRows(alias, query = {}, scope, seed) {
11279
- const { port, portKey } = useDataPort(scope);
11280
- const qc = useRecordsQueryClient();
11281
- useLive(alias, scope);
11282
- const rowsKey = dataKeys.rows(portKey, alias, query);
11283
- const q = useInfiniteQuery(
11284
- {
11285
- queryKey: rowsKey,
11286
- queryFn: async ({ pageParam }) => unwrap(await port.list(alias, query, pageParam ?? void 0)),
11287
- initialPageParam: null,
11288
- getNextPageParam: (last) => last.cursor ?? void 0,
11289
- retry: retryable,
11290
- ...seed ? { initialData: { pages: [seed.data], pageParams: [null] }, initialDataUpdatedAt: seed.at } : {}
11291
- },
11292
- qc
11293
- );
11294
- const rows = useMemo3(() => {
11295
- const seen = /* @__PURE__ */ new Set();
11296
- const out = [];
11297
- for (const page2 of q.data?.pages ?? []) {
11298
- for (const row of page2.rows) {
11299
- if (seen.has(row._id)) continue;
11300
- seen.add(row._id);
11301
- out.push(row);
11302
- }
11303
- }
11304
- return out;
11305
- }, [q.data]);
11306
- const want = useRef4({ key: "", pages: 0 });
11307
- const keyHash = hashKey(rowsKey);
11308
- const loaded = q.data?.pages.length ?? 0;
11309
- const { isFetching, hasNextPage, isFetchNextPageError, fetchNextPage } = q;
11310
- useEffect6(() => {
11311
- const w = want.current;
11312
- if (w.key !== keyHash || isFetching || loaded >= w.pages) return;
11313
- if (!hasNextPage || isFetchNextPageError) {
11314
- want.current = { key: keyHash, pages: 0 };
11315
- owedPages(qc).delete(keyHash);
11316
- return;
11317
- }
11318
- void fetchNextPage({ cancelRefetch: false });
11319
- }, [qc, keyHash, isFetching, loaded, hasNextPage, isFetchNextPageError, fetchNextPage]);
11320
- return {
11321
- rows,
11322
- total: q.data?.pages[0]?.total ?? null,
11323
- loading: q.isPending,
11324
- loadingMore: q.isFetchingNextPage,
11325
- hasMore: Boolean(q.hasNextPage),
11326
- error: asRecordsError(q.error),
11327
- loadMore: () => {
11328
- if (!q.hasNextPage || q.isFetchingNextPage) return;
11329
- want.current = { key: keyHash, pages: loaded + 1 };
11330
- owedPages(qc).set(keyHash, { pages: loaded + 1, fetchNext: () => void q.fetchNextPage({ cancelRefetch: false }) });
11331
- if (!q.isFetching) void q.fetchNextPage({ cancelRefetch: false });
11332
- },
11333
- newer: q.data?.newer ?? 0,
11334
- refresh: () => void q.refetch()
11335
- };
11336
- }
11337
- var tempCounter = 0;
11338
- function drawPatch(before, patch) {
11339
- const drawn = { ...before, ...patch, _id: before._id, _version: before._version, _organization_id: before._organization_id };
11340
- if (patch._custom && typeof patch._custom === "object") {
11341
- drawn._custom = { ...before._custom ?? {}, ...patch._custom };
11342
- } else if (before._custom !== void 0) {
11343
- drawn._custom = before._custom;
11344
- }
11345
- return drawn;
11346
- }
11347
- function useDataMutations(alias, scope) {
11348
- const qc = useRecordsQueryClient();
11349
- const { port, portKey } = useDataPort(scope);
11350
- return useMemo3(() => {
11351
- const snapshotRows = () => qc.getQueriesData({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
11352
- const putBack = (snap) => {
11353
- for (const [key, data] of snap) qc.setQueryData(key, data);
11354
- };
11355
- return {
11356
- async create(values) {
11357
- const tempId = `pending-${Date.now().toString(36)}-${tempCounter += 1}`;
11358
- const drawn = { ...values, _id: tempId, _version: 0, _organization_id: "", _pending: true };
11359
- await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
11360
- forEachRowsEntry(qc, portKey, alias, (_key, data) => {
11361
- if (data.pages.length === 0) return data;
11362
- const [first, ...rest] = data.pages;
11363
- return { ...data, pages: [{ ...first, rows: [drawn, ...first.rows], total: first.total === null ? null : first.total + 1 }, ...rest] };
11364
- });
11365
- const answer = await port.create(alias, values);
11366
- forEachRowsEntry(qc, portKey, alias, (_key, data) => {
11367
- const pages = data.pages.map((page2) => {
11368
- if (!page2.rows.some((r) => r._id === tempId)) return page2;
11369
- const rows = answer.ok ? page2.rows.map((r) => r._id === tempId ? answer.data : r) : page2.rows.filter((r) => r._id !== tempId);
11370
- return { ...page2, rows, total: answer.ok || page2.total === null ? page2.total : Math.max(0, page2.total - 1) };
11371
- });
11372
- return { ...data, pages };
11373
- });
11374
- if (answer.ok) qc.setQueryData(dataKeys.row(portKey, alias, answer.data._id), answer.data);
11375
- return answer;
11376
- },
11377
- async update(id, patch, options = {}) {
11378
- const before = findRow(qc, portKey, alias, id);
11379
- const baseVersion = options.baseVersion ?? before?._version;
11380
- if (typeof baseVersion !== "number") {
11381
- 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." } };
11382
- }
11383
- await qc.cancelQueries({ queryKey: dataKeys.alias(portKey, alias) });
11384
- const rowKey = dataKeys.row(portKey, alias, id);
11385
- const rowBefore = qc.getQueryData(rowKey);
11386
- if (before) patchRow(qc, portKey, alias, drawPatch(before, patch), { force: true });
11387
- const answer = await port.update(alias, id, patch, baseVersion);
11388
- if (answer.ok) {
11389
- patchRow(qc, portKey, alias, answer.data, { force: true, fromStore: true });
11390
- return answer;
11391
- }
11392
- if (before) patchRow(qc, portKey, alias, before, { force: true });
11393
- qc.setQueryData(rowKey, rowBefore);
11394
- void rereadIds(qc, port, portKey, alias, [id]);
11395
- return answer;
11396
- },
11397
- async archive(id) {
11398
- await qc.cancelQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
11399
- const snap = snapshotRows();
11400
- const rowKey = dataKeys.row(portKey, alias, id);
11401
- const rowBefore = qc.getQueryData(rowKey);
11402
- dropRow(qc, portKey, alias, id);
11403
- const answer = await port.archive(alias, id);
11404
- if (!answer.ok) {
11405
- putBack(snap);
11406
- qc.setQueryData(rowKey, rowBefore);
11407
- void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
11408
- }
11409
- return answer;
11410
- },
11411
- async restore(id) {
11412
- const answer = await port.restore(alias, id);
11413
- if (answer.ok) {
11414
- void qc.invalidateQueries({ queryKey: dataKeys.rowsPrefix(portKey, alias) });
11415
- void qc.invalidateQueries({ queryKey: dataKeys.row(portKey, alias, id) });
11416
- }
11417
- return answer;
11418
- }
11419
- };
11420
- }, [qc, port, portKey, alias]);
11421
- }
11422
-
11423
10946
  // src/react/useStoreTable.ts
10947
+ import { hostEvents, useDataMutations, useDataRows } from "@ai-matrx/records/data/react";
11424
10948
  var PAGE3 = 500;
11425
10949
  function columnRefusal2(table, column) {
11426
10950
  return {
@@ -11449,7 +10973,7 @@ function toNamed(table, row) {
11449
10973
  function useStoreTable(table, options = {}) {
11450
10974
  const client = useRecordsClient();
11451
10975
  const alias = table.id;
11452
- const scope = useMemo4(
10976
+ const scope = useMemo3(
11453
10977
  () => {
11454
10978
  const port = createTableDataPort({ client, aliases: { [alias]: { kind: "table", tableId: table.id, organizationId: table.organizationId, label: table.name } } });
11455
10979
  return { port, events: hostEvents(port), portKey: `store-table|${client.config.organizationId ?? "-"}|${client.config.actor.user_id ?? "-"}` };
@@ -11457,7 +10981,7 @@ function useStoreTable(table, options = {}) {
11457
10981
  [client, alias, table.id, table.organizationId, table.name]
11458
10982
  );
11459
10983
  const asked = JSON.stringify(options);
11460
- const built = useMemo4(() => {
10984
+ const built = useMemo3(() => {
11461
10985
  const where = [];
11462
10986
  for (const [name, value] of Object.entries(options.where ?? {})) {
11463
10987
  const column = table.columns[name];
@@ -11484,10 +11008,10 @@ function useStoreTable(table, options = {}) {
11484
11008
  const limit = Math.min(options.limit ?? STORE_ROWS_CEILING, STORE_ROWS_CEILING);
11485
11009
  const read = useDataRows(alias, built.ok ? built.data : { pageSize: 1 }, scope);
11486
11010
  const write = useDataMutations(alias, scope);
11487
- useEffect7(() => {
11011
+ useEffect6(() => {
11488
11012
  if (built.ok && read.hasMore && !read.loadingMore && !read.loading && !read.error && read.rows.length < limit) read.loadMore();
11489
11013
  }, [built.ok, read.hasMore, read.loadingMore, read.loading, read.error, read.rows.length, limit, read]);
11490
- const rows = useMemo4(() => read.rows.slice(0, limit).filter((r) => r["_pending"] !== true).map((r) => toNamed(table, r)), [read.rows, limit, table]);
11014
+ const rows = useMemo3(() => read.rows.slice(0, limit).filter((r) => r["_pending"] !== true).map((r) => toNamed(table, r)), [read.rows, limit, table]);
11491
11015
  const total = built.ok ? read.total : null;
11492
11016
  return {
11493
11017
  rows: built.ok ? rows : [],
@@ -13234,6 +12758,7 @@ export {
13234
12758
  useRecordChangeRevision,
13235
12759
  useRecordMutation,
13236
12760
  useRecordPage,
12761
+ useRecordWrites,
13237
12762
  useRecords,
13238
12763
  useRecordsActor,
13239
12764
  useRecordsClient,