@ai-matrx/records 0.92.1 → 0.93.0

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 (103) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +4 -4
  3. package/dist/{client-Dzd8gtAo.d.ts → client-DsPuWFp0.d.ts} +8 -8
  4. package/dist/{client-Cn3odIIB.d.cts → client-EyD3rS0T.d.cts} +8 -8
  5. package/dist/core/index.cjs +101 -32
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +16 -15
  8. package/dist/core/index.d.ts +16 -15
  9. package/dist/core/index.js +101 -32
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/data/index.cjs +46 -24
  12. package/dist/data/index.cjs.map +1 -1
  13. package/dist/data/index.d.cts +11 -11
  14. package/dist/data/index.d.ts +11 -11
  15. package/dist/data/index.js +46 -24
  16. package/dist/data/index.js.map +1 -1
  17. package/dist/{declareTable-CrL6AZKB.d.cts → declareTable-Bkk_Ewib.d.cts} +4 -4
  18. package/dist/{declareTable-ZRW5EsTQ.d.ts → declareTable-kL5qZoTe.d.ts} +4 -4
  19. package/dist/entity/index.cjs +43 -29
  20. package/dist/entity/index.cjs.map +1 -1
  21. package/dist/entity/index.d.cts +7 -6
  22. package/dist/entity/index.d.ts +7 -6
  23. package/dist/entity/index.js +43 -29
  24. package/dist/entity/index.js.map +1 -1
  25. package/dist/entity/react.cjs +43 -29
  26. package/dist/entity/react.cjs.map +1 -1
  27. package/dist/entity/react.js +43 -29
  28. package/dist/entity/react.js.map +1 -1
  29. package/dist/{field-CCY1rG9l.d.cts → field-6rwt4ejZ.d.cts} +1 -1
  30. package/dist/{field-enufS5ry.d.ts → field-B-WaVZnS.d.ts} +1 -1
  31. package/dist/forms/index.d.cts +3 -3
  32. package/dist/forms/index.d.ts +3 -3
  33. package/dist/index.cjs +92 -25
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +13 -33
  36. package/dist/index.d.ts +13 -33
  37. package/dist/index.js +92 -25
  38. package/dist/index.js.map +1 -1
  39. package/dist/memory/index.d.cts +7 -7
  40. package/dist/memory/index.d.ts +7 -7
  41. package/dist/objectOrganization-BII_K3Gm.d.ts +45 -0
  42. package/dist/objectOrganization-DfjDQtDb.d.cts +45 -0
  43. package/dist/{port-C54ykAOv.d.ts → port--acgmxh5.d.ts} +5 -2
  44. package/dist/{port-Drioh9al.d.cts → port-Cx76dq-X.d.cts} +5 -2
  45. package/dist/{portal-DRdmZ-xv.d.cts → portal-9sRr6cpG.d.cts} +1 -1
  46. package/dist/{portal-or_f7BkT.d.ts → portal-BDAqsGkg.d.ts} +1 -1
  47. package/dist/{ports-s0D2jvqn.d.cts → ports-9BQ3zo2y.d.cts} +108 -2
  48. package/dist/{ports-BeOgjwYl.d.ts → ports-DYWE1ENo.d.ts} +108 -2
  49. package/dist/react/index.cjs +101 -32
  50. package/dist/react/index.cjs.map +1 -1
  51. package/dist/react/index.d.cts +1463 -1363
  52. package/dist/react/index.d.ts +1463 -1363
  53. package/dist/react/index.js +101 -32
  54. package/dist/react/index.js.map +1 -1
  55. package/dist/realtime/index.cjs +43 -13
  56. package/dist/realtime/index.cjs.map +1 -1
  57. package/dist/realtime/index.d.cts +9 -3
  58. package/dist/realtime/index.d.ts +9 -3
  59. package/dist/realtime/index.js +43 -13
  60. package/dist/realtime/index.js.map +1 -1
  61. package/dist/{record-BwCf9wKW.d.cts → record-DmmeuUrC.d.cts} +1 -1
  62. package/dist/{record-BwCf9wKW.d.ts → record-DmmeuUrC.d.ts} +1 -1
  63. package/dist/{relation-eLMgv_dV.d.ts → relation-Op1M67le.d.ts} +2 -2
  64. package/dist/{relation-8vAWCa67.d.cts → relation-qrb2NbHf.d.cts} +2 -2
  65. package/dist/{rule-wHI5B0Ka.d.cts → rule-CeHg1sy1.d.cts} +1 -1
  66. package/dist/{rule-D9yNiorf.d.ts → rule-tN2lxvJ1.d.ts} +1 -1
  67. package/dist/search/index.d.cts +1 -1
  68. package/dist/search/index.d.ts +1 -1
  69. package/dist/setup/index.cjs +117 -42
  70. package/dist/setup/index.cjs.map +1 -1
  71. package/dist/setup/index.d.cts +25 -11
  72. package/dist/setup/index.d.ts +25 -11
  73. package/dist/setup/index.js +117 -42
  74. package/dist/setup/index.js.map +1 -1
  75. package/dist/{subscription-CdoI-UaB.d.cts → subscription-Drbxn-9Z.d.cts} +3 -3
  76. package/dist/{subscription-BynsG1K6.d.ts → subscription-hdTaRrfK.d.ts} +3 -3
  77. package/dist/{table-kind-i3lZJa3f.d.ts → table-kind-BZkvzBgc.d.ts} +4 -29
  78. package/dist/{table-kind-DzctQ7dO.d.cts → table-kind-C-hZxDN7.d.cts} +4 -29
  79. package/dist/templates/index.d.cts +10 -10
  80. package/dist/templates/index.d.ts +10 -10
  81. package/dist/typed-table/index.cjs +46 -24
  82. package/dist/typed-table/index.cjs.map +1 -1
  83. package/dist/typed-table/index.d.cts +10 -10
  84. package/dist/typed-table/index.d.ts +10 -10
  85. package/dist/typed-table/index.js +46 -24
  86. package/dist/typed-table/index.js.map +1 -1
  87. package/dist/{types-BvL_oKFl.d.ts → types-BauAeFW8.d.ts} +2 -2
  88. package/dist/{types-DAFP6XFe.d.cts → types-CBmUdBT0.d.cts} +2 -2
  89. package/dist/{types-BKYEeFt4.d.cts → types-Dek9owBe.d.cts} +2 -2
  90. package/dist/{types-D8qdG3Lg.d.cts → types-DsgXrESm.d.cts} +2 -2
  91. package/dist/{types-cpKY7Pjb.d.ts → types-HVgWcZDY.d.ts} +2 -2
  92. package/dist/{types-DYzXhD7j.d.ts → types-hrw-Eff1.d.ts} +2 -2
  93. package/dist/use-cases/index.d.cts +5 -5
  94. package/dist/use-cases/index.d.ts +5 -5
  95. package/dist/{validate-DfNMxjmU.d.cts → validate-B_iUeyh2.d.cts} +1 -1
  96. package/dist/{validate-DbvAParj.d.ts → validate-w-HGGTf-.d.ts} +1 -1
  97. package/dist/views/index.cjs +438 -0
  98. package/dist/views/index.cjs.map +1 -0
  99. package/dist/views/index.d.cts +401 -0
  100. package/dist/views/index.d.ts +401 -0
  101. package/dist/views/index.js +415 -0
  102. package/dist/views/index.js.map +1 -0
  103. package/package.json +12 -2
@@ -1,16 +1,17 @@
1
1
  import { RealtimeManager } from '@ai-matrx/realtime';
2
- import { R as RecordsClient } from '../client-Cn3odIIB.cjs';
3
- import { C as CreateEntityDataPortOptions, E as EntityDataPort } from '../port-Drioh9al.cjs';
4
- import { a as RecordsConfig, b as RecordsErrorSink, c as RecordsDataSource, d as RecordsChangeSink } from '../ports-s0D2jvqn.cjs';
5
- import { U as Uuid, R as RecordsResult } from '../record-BwCf9wKW.cjs';
2
+ import { R as RecordsClient } from '../client-EyD3rS0T.cjs';
3
+ import { C as CreateEntityDataPortOptions, E as EntityDataPort } from '../port-Cx76dq-X.cjs';
4
+ import { R as RecordOrganizationPort, O as ObjectOrganizationAnswer } from '../objectOrganization-DfjDQtDb.cjs';
5
+ import { a as RecordsConfig, b as RowAgentPort, c as RecordsErrorSink, d as RecordsDataSource, e as RecordsChangeSink, f as RowAgentTarget } from '../ports-9BQ3zo2y.cjs';
6
+ import { U as Uuid, R as RecordsResult } from '../record-DmmeuUrC.cjs';
6
7
  import { RecordSearchRequest, RecordSearchHit, listStoreRecordCandidates } from '../search/index.cjs';
7
- import '../table-kind-DzctQ7dO.cjs';
8
- import '../field-CCY1rG9l.cjs';
9
- import '../subscription-CdoI-UaB.cjs';
10
- import '../rule-wHI5B0Ka.cjs';
11
- import '../relation-8vAWCa67.cjs';
12
- import '../portal-DRdmZ-xv.cjs';
13
- import '../types-BKYEeFt4.cjs';
8
+ import '../table-kind-C-hZxDN7.cjs';
9
+ import '../field-6rwt4ejZ.cjs';
10
+ import '../subscription-Drbxn-9Z.cjs';
11
+ import '../rule-CeHg1sy1.cjs';
12
+ import '../relation-qrb2NbHf.cjs';
13
+ import '../portal-9sRr6cpG.cjs';
14
+ import '../types-Dek9owBe.cjs';
14
15
 
15
16
  /** A signed-in database identity: the client every door and live change rides, and who it is. */
16
17
  interface RecordsSession {
@@ -46,6 +47,13 @@ interface CreateRecordsOptions<TAssociations = never> {
46
47
  entityAliases?: CreateEntityDataPortOptions["aliases"];
47
48
  /** The associations store (`@ai-matrx/associations/core` `createAssociationsStore`) bound by the host; handed back as-is. */
48
49
  associations?: TAssociations;
50
+ /**
51
+ * WHICH ORGANIZATION OWNS A RECORD — a port. Default: `custom.where_id_opens`, one call per ask. A host
52
+ * that keeps answers across remounts or seeds them from a server render binds its own.
53
+ */
54
+ organizationOf?: RecordOrganizationPort;
55
+ /** RUN AN AGENT ON A ROW — a port the host binds (the run belongs to `@ai-matrx/agents`). Unbound, there is none. */
56
+ rowAgent?: RowAgentPort;
49
57
  onError?: RecordsErrorSink;
50
58
  }
51
59
  interface RecordsSetup<TAssociations = never> {
@@ -79,6 +87,12 @@ interface RecordsSetup<TAssociations = never> {
79
87
  watch(tableId: Uuid, sink: Partial<RecordsChangeSink>, organizationId?: Uuid): () => void;
80
88
  /** The associations store the host bound, if any. */
81
89
  readonly associations: TAssociations | undefined;
90
+ /** Which organization owns an object (table, record, form…), read from the object — never the person's pick. */
91
+ organizationOf(id: Uuid): Promise<ObjectOrganizationAnswer>;
92
+ /** The host's agent runner, or null when none is bound (agent buttons are then not drawn). */
93
+ readonly rowAgent: RowAgentPort | null;
94
+ /** Run an agent on a row through the bound runner; refuses by name when none is bound. */
95
+ runAgentOnRow(target: RowAgentTarget): Promise<RecordsResult<void>>;
82
96
  /** Close live channels (a Node script calls this before it exits). */
83
97
  close(): void;
84
98
  }
@@ -1,16 +1,17 @@
1
1
  import { RealtimeManager } from '@ai-matrx/realtime';
2
- import { R as RecordsClient } from '../client-Dzd8gtAo.js';
3
- import { C as CreateEntityDataPortOptions, E as EntityDataPort } from '../port-C54ykAOv.js';
4
- import { a as RecordsConfig, b as RecordsErrorSink, c as RecordsDataSource, d as RecordsChangeSink } from '../ports-BeOgjwYl.js';
5
- import { U as Uuid, R as RecordsResult } from '../record-BwCf9wKW.js';
2
+ import { R as RecordsClient } from '../client-DsPuWFp0.js';
3
+ import { C as CreateEntityDataPortOptions, E as EntityDataPort } from '../port--acgmxh5.js';
4
+ import { R as RecordOrganizationPort, O as ObjectOrganizationAnswer } from '../objectOrganization-BII_K3Gm.js';
5
+ import { a as RecordsConfig, b as RowAgentPort, c as RecordsErrorSink, d as RecordsDataSource, e as RecordsChangeSink, f as RowAgentTarget } from '../ports-DYWE1ENo.js';
6
+ import { U as Uuid, R as RecordsResult } from '../record-DmmeuUrC.js';
6
7
  import { RecordSearchRequest, RecordSearchHit, listStoreRecordCandidates } from '../search/index.js';
7
- import '../table-kind-i3lZJa3f.js';
8
- import '../field-enufS5ry.js';
9
- import '../subscription-BynsG1K6.js';
10
- import '../rule-D9yNiorf.js';
11
- import '../relation-eLMgv_dV.js';
12
- import '../portal-or_f7BkT.js';
13
- import '../types-cpKY7Pjb.js';
8
+ import '../table-kind-BZkvzBgc.js';
9
+ import '../field-B-WaVZnS.js';
10
+ import '../subscription-hdTaRrfK.js';
11
+ import '../rule-tN2lxvJ1.js';
12
+ import '../relation-Op1M67le.js';
13
+ import '../portal-BDAqsGkg.js';
14
+ import '../types-HVgWcZDY.js';
14
15
 
15
16
  /** A signed-in database identity: the client every door and live change rides, and who it is. */
16
17
  interface RecordsSession {
@@ -46,6 +47,13 @@ interface CreateRecordsOptions<TAssociations = never> {
46
47
  entityAliases?: CreateEntityDataPortOptions["aliases"];
47
48
  /** The associations store (`@ai-matrx/associations/core` `createAssociationsStore`) bound by the host; handed back as-is. */
48
49
  associations?: TAssociations;
50
+ /**
51
+ * WHICH ORGANIZATION OWNS A RECORD — a port. Default: `custom.where_id_opens`, one call per ask. A host
52
+ * that keeps answers across remounts or seeds them from a server render binds its own.
53
+ */
54
+ organizationOf?: RecordOrganizationPort;
55
+ /** RUN AN AGENT ON A ROW — a port the host binds (the run belongs to `@ai-matrx/agents`). Unbound, there is none. */
56
+ rowAgent?: RowAgentPort;
49
57
  onError?: RecordsErrorSink;
50
58
  }
51
59
  interface RecordsSetup<TAssociations = never> {
@@ -79,6 +87,12 @@ interface RecordsSetup<TAssociations = never> {
79
87
  watch(tableId: Uuid, sink: Partial<RecordsChangeSink>, organizationId?: Uuid): () => void;
80
88
  /** The associations store the host bound, if any. */
81
89
  readonly associations: TAssociations | undefined;
90
+ /** Which organization owns an object (table, record, form…), read from the object — never the person's pick. */
91
+ organizationOf(id: Uuid): Promise<ObjectOrganizationAnswer>;
92
+ /** The host's agent runner, or null when none is bound (agent buttons are then not drawn). */
93
+ readonly rowAgent: RowAgentPort | null;
94
+ /** Run an agent on a row through the bound runner; refuses by name when none is bound. */
95
+ runAgentOnRow(target: RowAgentTarget): Promise<RecordsResult<void>>;
82
96
  /** Close live channels (a Node script calls this before it exits). */
83
97
  close(): void;
84
98
  }
@@ -375,35 +375,48 @@ function sourceArchivedOf(value) {
375
375
  import { globalSlot as globalSlot2 } from "@ai-matrx/kit/global-slot";
376
376
  var REMEMBER_MS = 3e4;
377
377
  var REMEMBER_MAX = 500;
378
- var ours = globalSlot2("ai-matrx.records.op-ledger", () => /* @__PURE__ */ new Map());
379
- function trim(now) {
380
- for (const [id, at] of ours) {
381
- if (now - at <= REMEMBER_MS) break;
382
- ours.delete(id);
383
- }
384
- while (ours.size > REMEMBER_MAX) {
385
- const oldest = ours.keys().next();
386
- if (oldest.done === true) break;
387
- ours.delete(oldest.value);
378
+ function ledgerOver(ours) {
379
+ function trim(now) {
380
+ for (const [id, at] of ours) {
381
+ if (now - at <= REMEMBER_MS) break;
382
+ ours.delete(id);
383
+ }
384
+ while (ours.size > REMEMBER_MAX) {
385
+ const oldest = ours.keys().next();
386
+ if (oldest.done === true) break;
387
+ ours.delete(oldest.value);
388
+ }
388
389
  }
390
+ return {
391
+ mint() {
392
+ const id = crypto.randomUUID();
393
+ const now = Date.now();
394
+ ours.set(id, now);
395
+ trim(now);
396
+ return id;
397
+ },
398
+ isOwn(opId) {
399
+ if (!opId) return false;
400
+ const at = ours.get(opId);
401
+ if (at === void 0) return false;
402
+ if (Date.now() - at > REMEMBER_MS) {
403
+ ours.delete(opId);
404
+ return false;
405
+ }
406
+ return true;
407
+ },
408
+ clear() {
409
+ ours.clear();
410
+ },
411
+ get size() {
412
+ return ours.size;
413
+ }
414
+ };
389
415
  }
390
- function mintOpId() {
391
- const id = crypto.randomUUID();
392
- const now = Date.now();
393
- ours.set(id, now);
394
- trim(now);
395
- return id;
396
- }
397
- function isOwnOp(opId) {
398
- if (!opId) return false;
399
- const at = ours.get(opId);
400
- if (at === void 0) return false;
401
- if (Date.now() - at > REMEMBER_MS) {
402
- ours.delete(opId);
403
- return false;
404
- }
405
- return true;
416
+ function createOpLedger() {
417
+ return ledgerOver(/* @__PURE__ */ new Map());
406
418
  }
419
+ var defaultOpLedger = ledgerOver(globalSlot2("ai-matrx.records.op-ledger", () => /* @__PURE__ */ new Map()));
407
420
 
408
421
  // src/choice.ts
409
422
  import { isUuidShape as isUuidShape2 } from "@ai-matrx/kit/uuid";
@@ -2462,6 +2475,8 @@ function createRecordsClient(given) {
2462
2475
  const config = { ...given, dataSource: asRecordsDataSource(given.dataSource) };
2463
2476
  const schema = config.schema ?? DEFAULT_SCHEMA;
2464
2477
  const org = config.organizationId;
2478
+ const ledger = config.opLedger ?? defaultOpLedger;
2479
+ const mintOp = () => ledger.mint();
2465
2480
  const scream = sink(config);
2466
2481
  const BUNDLE_TTL_MS = 1e4;
2467
2482
  const BUNDLE_OPENERS = /* @__PURE__ */ new Set(["applicable_fields", "views", "view_look_read", "table_decorations", "row_actions", "reverse_columns", "record_change_actions", "grid_layout", "io_imports"]);
@@ -2848,14 +2863,14 @@ function createRecordsClient(given) {
2848
2863
  async recordWrite({ table_id, data }) {
2849
2864
  const result = await callDoor(
2850
2865
  DOORS.recordWrite,
2851
- { p_organization_id: org, p_table_id: table_id, p_data: { ...data, _op_id: mintOpId() } },
2866
+ { p_organization_id: org, p_table_id: table_id, p_data: { ...data, _op_id: mintOp() } },
2852
2867
  "recordWrite"
2853
2868
  );
2854
2869
  if (result.ok && typeof result.data === "string") announceRecordChanged(result.data);
2855
2870
  return result;
2856
2871
  },
2857
2872
  async recordWriteMany({ table_id, rows, ids }) {
2858
- const opId = mintOpId();
2873
+ const opId = mintOp();
2859
2874
  return callDoor(
2860
2875
  DOORS.recordWriteMany,
2861
2876
  {
@@ -2868,7 +2883,7 @@ function createRecordsClient(given) {
2868
2883
  );
2869
2884
  },
2870
2885
  async recordChangeMany({ table_id, changes }) {
2871
- const opId = mintOpId();
2886
+ const opId = mintOp();
2872
2887
  const result = await callDoor(
2873
2888
  DOORS.recordChangeMany,
2874
2889
  {
@@ -2891,7 +2906,7 @@ function createRecordsClient(given) {
2891
2906
  return result;
2892
2907
  },
2893
2908
  async recordWriteGraph({ table_id, parent, children, edges }) {
2894
- const opId = mintOpId();
2909
+ const opId = mintOp();
2895
2910
  return callDoor(
2896
2911
  DOORS.recordWriteGraph,
2897
2912
  {
@@ -2915,7 +2930,7 @@ function createRecordsClient(given) {
2915
2930
  {
2916
2931
  p_organization_id: org,
2917
2932
  p_record_id: record_id,
2918
- p_patch: { ...patch, _op_id: mintOpId() },
2933
+ p_patch: { ...patch, _op_id: mintOp() },
2919
2934
  ...expectedVersion === void 0 ? {} : { p_expected_version: expectedVersion }
2920
2935
  },
2921
2936
  "recordUpdate"
@@ -2929,7 +2944,7 @@ function createRecordsClient(given) {
2929
2944
  {
2930
2945
  p_organization_id: org,
2931
2946
  p_record_id: record_id,
2932
- p_patch: { ...patch, _op_id: mintOpId() },
2947
+ p_patch: { ...patch, _op_id: mintOp() },
2933
2948
  p_add: add,
2934
2949
  ...expectedVersion === void 0 ? {} : { p_expected_version: expectedVersion }
2935
2950
  },
@@ -3954,7 +3969,7 @@ function createRecordsClient(given) {
3954
3969
  p_table_id: table_id,
3955
3970
  p_key: keys,
3956
3971
  // The op id rides the envelope, as `recordWrite` / `recordUpdate` send it; each door takes it off.
3957
- p_data: { ...data, _op_id: mintOpId() },
3972
+ p_data: { ...data, _op_id: mintOp() },
3958
3973
  ...expectedVersion === void 0 ? {} : { p_expected_version: expectedVersion }
3959
3974
  },
3960
3975
  "recordUpsert"
@@ -6342,6 +6357,7 @@ function toWireValues(values) {
6342
6357
  }
6343
6358
  function createEntityDataPort(options) {
6344
6359
  const client = options.client ?? createEntityClient({ dataSource: options.dataSource });
6360
+ const ledger = options.opLedger ?? defaultOpLedger;
6345
6361
  const activeOrganization = () => (options.activeOrganizationId ? options.activeOrganizationId() : options.organization) ?? null;
6346
6362
  const gets = /* @__PURE__ */ new Map();
6347
6363
  const aliases = options.aliases;
@@ -6532,27 +6548,27 @@ function createEntityDataPort(options) {
6532
6548
  "Pick an organization, then add the row again. Nothing was saved."
6533
6549
  );
6534
6550
  }
6535
- const answer = await client.insert({ token: binding.token, organizationId, rows: [toWireValues(values)], opId: mintOpId() });
6551
+ const answer = await client.insert({ token: binding.token, organizationId, rows: [toWireValues(values)], opId: ledger.mint() });
6536
6552
  return readBack(alias, answer, "The new row");
6537
6553
  },
6538
6554
  async update(alias, id, patch, baseVersion) {
6539
6555
  const binding = bindingOf(alias);
6540
6556
  if (!binding) return noSuchAlias(alias);
6541
6557
  if (!isUuidShape3(id)) return refusal("invalid_argument", `"${String(id)}" is not a row id.`);
6542
- const answer = await client.update({ token: binding.token, id, baseVersion, patch: toWireValues(patch), opId: mintOpId() });
6558
+ const answer = await client.update({ token: binding.token, id, baseVersion, patch: toWireValues(patch), opId: ledger.mint() });
6543
6559
  return readBack(alias, answer, "The change");
6544
6560
  },
6545
6561
  async archive(alias, id) {
6546
6562
  const binding = bindingOf(alias);
6547
6563
  if (!binding) return noSuchAlias(alias);
6548
6564
  if (!isUuidShape3(id)) return refusal("invalid_argument", `"${String(id)}" is not a row id.`);
6549
- return done(await client.archive({ token: binding.token, ids: [id], opId: mintOpId() }));
6565
+ return done(await client.archive({ token: binding.token, ids: [id], opId: ledger.mint() }));
6550
6566
  },
6551
6567
  async restore(alias, id) {
6552
6568
  const binding = bindingOf(alias);
6553
6569
  if (!binding) return noSuchAlias(alias);
6554
6570
  if (!isUuidShape3(id)) return refusal("invalid_argument", `"${String(id)}" is not a row id.`);
6555
- return done(await client.unarchive({ token: binding.token, ids: [id], opId: mintOpId() }));
6571
+ return done(await client.unarchive({ token: binding.token, ids: [id], opId: ledger.mint() }));
6556
6572
  },
6557
6573
  subscribe(event, listener) {
6558
6574
  const alias = event.startsWith("rows:") ? event.slice(5) : null;
@@ -6602,6 +6618,48 @@ function createEntityDataPort(options) {
6602
6618
  };
6603
6619
  }
6604
6620
 
6621
+ // src/objectOrganization.ts
6622
+ var WHERE_ID_OPENS_DOOR = "where_id_opens";
6623
+ function doorIsAbsent(error) {
6624
+ if (error.code === "PGRST202" || error.code === "42883") return true;
6625
+ return /could not find the function/i.test(error.message ?? "");
6626
+ }
6627
+ var DOOR_ABSENT_WHY = "custom.where_id_opens is missing from this database, so this page cannot tell which organization the record lives in and holds instead of guessing. Remedy: the chair applies migrations/campaign/openbyid_one_address_opens_any_id.sql and openbyid_the_resolver_can_be_reached.sql.";
6628
+ async function resolveObjectOrganization(dataSource, id) {
6629
+ let answered;
6630
+ try {
6631
+ answered = await dataSource.rpc(WHERE_ID_OPENS_DOOR, { p_id: id }, { schema: "custom" });
6632
+ } catch (thrown) {
6633
+ return { state: "unavailable", why: thrown instanceof Error ? thrown.message : "The record store did not answer." };
6634
+ }
6635
+ return readObjectOrganizationAnswer(answered, id);
6636
+ }
6637
+ function readObjectOrganizationAnswer(answered, id) {
6638
+ if (answered.error) {
6639
+ if (doorIsAbsent(answered.error)) {
6640
+ console.warn(`[@ai-matrx/records] ${DOOR_ABSENT_WHY}`);
6641
+ return { state: "unavailable", why: DOOR_ABSENT_WHY };
6642
+ }
6643
+ return { state: "unavailable", why: answered.error.message ?? "The record store refused to say where this is." };
6644
+ }
6645
+ const row = answered.data;
6646
+ if (row === null || row === void 0) return { state: "not-given" };
6647
+ if (typeof row !== "object" || typeof row.organization_id !== "string" || typeof row.kind !== "string") {
6648
+ return { state: "unavailable", why: "The record store answered something this page cannot read." };
6649
+ }
6650
+ return {
6651
+ state: "found",
6652
+ organizationId: row.organization_id,
6653
+ kind: row.kind,
6654
+ path: typeof row.path === "string" ? row.path : null,
6655
+ live: row.live !== false,
6656
+ resolvedId: typeof row.resolved_id === "string" ? row.resolved_id : id
6657
+ };
6658
+ }
6659
+ function whereIdOpensPort(dataSource) {
6660
+ return { organizationOf: (id) => resolveObjectOrganization(dataSource, id) };
6661
+ }
6662
+
6605
6663
  // src/realtime/index.ts
6606
6664
  import {
6607
6665
  defineChannelNamespace as defineChannelNamespace2,
@@ -6620,7 +6678,7 @@ function noticeKey(notice) {
6620
6678
  const ids = notice.record_ids == null ? "*" : [...notice.record_ids].sort().join(",");
6621
6679
  return `${notice.table_id ?? ""}:${notice.kind ?? ""}:${notice.op ?? ""}:${notice.op_id ?? ""}:${notice.at}:${ids}`;
6622
6680
  }
6623
- function storeTableChannelSpec(tableId, onNotice, onBackfill) {
6681
+ function storeTableChannelSpec(tableId, onNotice, onBackfill, ledger = defaultOpLedger) {
6624
6682
  return {
6625
6683
  topic: storeTableChannel.topic({ tableId }),
6626
6684
  // Database Broadcast is authorized by RLS on realtime.messages — that is what `private` means here;
@@ -6628,14 +6686,14 @@ function storeTableChannelSpec(tableId, onNotice, onBackfill) {
6628
6686
  // delivers nothing forever).
6629
6687
  private: true,
6630
6688
  // The sender is Postgres: no Matrx envelope. OUR OWN WRITE is known by the op id the write door carried.
6631
- wire: { mode: "raw", isOwnMessage: (payload) => isOwnOp(payload?.op_id) },
6689
+ wire: { mode: "raw", isOwnMessage: (payload) => ledger.isOwn(payload?.op_id) },
6632
6690
  eventKey: (_source, payload) => noticeKey(payload ?? {}),
6633
6691
  broadcast: [
6634
6692
  {
6635
6693
  event: STORE_NOTICE_EVENT,
6636
6694
  onMessage: (message) => {
6637
6695
  const notice = message.data ?? {};
6638
- if (isOwnOp(notice.op_id)) return;
6696
+ if (ledger.isOwn(notice.op_id)) return;
6639
6697
  onNotice(notice);
6640
6698
  }
6641
6699
  }
@@ -6655,7 +6713,8 @@ function createRecordsRealtimePort(options = {}) {
6655
6713
  },
6656
6714
  () => {
6657
6715
  for (const listener of channel.listeners) listener.backfill();
6658
- }
6716
+ },
6717
+ options.ledger
6659
6718
  );
6660
6719
  if (options.manager) {
6661
6720
  const handle = options.manager.open(spec);
@@ -6773,22 +6832,26 @@ async function createRecords(options) {
6773
6832
  } else if (options.live) {
6774
6833
  realtime = options.live;
6775
6834
  }
6776
- const realtimePort = realtime ? createRecordsRealtimePort({ manager: realtime }) : void 0;
6835
+ const opLedger = createOpLedger();
6836
+ const realtimePort = realtime ? createRecordsRealtimePort({ manager: realtime, ledger: opLedger }) : void 0;
6777
6837
  const actor = options.actor ?? { actor: "user", ...userId ? { user_id: userId } : {} };
6778
6838
  const records = createRecordsClient({
6779
6839
  dataSource,
6780
6840
  actor,
6781
6841
  organizationId,
6782
6842
  organizationIds,
6843
+ opLedger,
6783
6844
  ...realtimePort ? { realtime: realtimePort } : {},
6784
6845
  ...options.onError ? { onError: options.onError } : {}
6785
6846
  });
6786
6847
  const entities = createEntityDataPort({
6787
6848
  dataSource,
6788
6849
  aliases: options.entityAliases ?? {},
6850
+ opLedger,
6789
6851
  ...organizationId ? { organization: organizationId } : {}
6790
6852
  });
6791
6853
  const organizationIdChosen = organizationId;
6854
+ const organizationPort = options.organizationOf ?? whereIdOpensPort(dataSource);
6792
6855
  let warnedNotLive = false;
6793
6856
  return {
6794
6857
  authKind: options.auth.kind,
@@ -6817,6 +6880,18 @@ async function createRecords(options) {
6817
6880
  }) });
6818
6881
  },
6819
6882
  associations: options.associations,
6883
+ organizationOf: (id) => organizationPort.organizationOf(id),
6884
+ rowAgent: options.rowAgent ?? null,
6885
+ async runAgentOnRow(target) {
6886
+ if (!options.rowAgent) {
6887
+ return {
6888
+ ok: false,
6889
+ error: { code: "not_supported", message: "No agent runner is bound: pass createRecords({ rowAgent }) to run an agent on a row." }
6890
+ };
6891
+ }
6892
+ await options.rowAgent.run(target);
6893
+ return { ok: true, data: void 0 };
6894
+ },
6820
6895
  close() {
6821
6896
  if (ownsRealtime) realtime?.dispose();
6822
6897
  }