@ai-matrx/records 0.86.2 → 0.87.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.
@@ -22,6 +22,19 @@ interface Column {
22
22
  choices?: string[];
23
23
  /** For a link column: what it may point at (a custom table's id, or entity tokens). */
24
24
  linksTo?: string[];
25
+ /**
26
+ * For a link column: the alias, in the SAME port, that serves the table it points at — so a form can list
27
+ * the linked rows to pick from. Absent when no source of this Applet serves that table.
28
+ */
29
+ linksToSource?: string;
30
+ /**
31
+ * The store's own answer to "does this column hold more than one value" (`custom.field.multi`). A choice
32
+ * column's type is `list` whether it takes one word or many — only this tells them apart (Status, one
33
+ * value, drew as four switches and the store refused the list it sent: social planner, 2026-10-08).
34
+ */
35
+ multi?: boolean;
36
+ /** The column a row is named by (the table's `title_field`) — what a link to the row reads as. */
37
+ title?: boolean;
25
38
  readOnly: boolean;
26
39
  }
27
40
  /** §0. One row, keyed by `Column.key`. Underscore keys are the runtime's own. */
@@ -22,6 +22,19 @@ interface Column {
22
22
  choices?: string[];
23
23
  /** For a link column: what it may point at (a custom table's id, or entity tokens). */
24
24
  linksTo?: string[];
25
+ /**
26
+ * For a link column: the alias, in the SAME port, that serves the table it points at — so a form can list
27
+ * the linked rows to pick from. Absent when no source of this Applet serves that table.
28
+ */
29
+ linksToSource?: string;
30
+ /**
31
+ * The store's own answer to "does this column hold more than one value" (`custom.field.multi`). A choice
32
+ * column's type is `list` whether it takes one word or many — only this tells them apart (Status, one
33
+ * value, drew as four switches and the store refused the list it sent: social planner, 2026-10-08).
34
+ */
35
+ multi?: boolean;
36
+ /** The column a row is named by (the table's `title_field`) — what a link to the row reads as. */
37
+ title?: boolean;
25
38
  readOnly: boolean;
26
39
  }
27
40
  /** §0. One row, keyed by `Column.key`. Underscore keys are the runtime's own. */
@@ -50,8 +50,9 @@ function optionsTableOf(field) {
50
50
  return typeof id === "string" && id ? id : null;
51
51
  }
52
52
  async function readTableShape(client, tableId) {
53
- const fields = await client.fields({ table_id: tableId });
53
+ const [fields, table] = await Promise.all([client.fields({ table_id: tableId }), client.tableRead({ table_id: tableId, organization_id: client.config.organizationId })]);
54
54
  if (!fields.ok) return fields;
55
+ const titleField = table.ok ? table.data?.title_field ?? null : null;
55
56
  const choiceFields = fields.data.filter((f) => optionsTableOf(f) !== null);
56
57
  const options = await Promise.all(choiceFields.map((f) => client.fieldOptions({ field_id: f.id })));
57
58
  const choicesById = /* @__PURE__ */ new Map();
@@ -75,8 +76,10 @@ async function readTableShape(client, tableId) {
75
76
  type,
76
77
  origin: "custom",
77
78
  required: Boolean(field.required),
79
+ multi: field.multi === true,
78
80
  readOnly: COMPUTED_TYPES.has(field.type) || field.source === "formula" || field.compute_on !== null && field.compute_on !== void 0
79
81
  };
82
+ if (titleField !== null && (titleField === field.key || titleField === field.id)) column.title = true;
80
83
  const choices = choicesById.get(field.id);
81
84
  if (choices) column.choices = choices;
82
85
  if (field.type === "relation" && field.relation_target) column.linksTo = [field.relation_target];
@@ -229,7 +232,8 @@ function isWritingDoor(door) {
229
232
  }
230
233
 
231
234
  // src/core/recordChanges.ts
232
- var listeners = /* @__PURE__ */ new Set();
235
+ import { globalSlot } from "@ai-matrx/kit/global-slot";
236
+ var listeners = globalSlot("ai-matrx.records.record-change-listeners", () => /* @__PURE__ */ new Set());
233
237
  function announceRecordChanged(recordId) {
234
238
  for (const listener of [...listeners]) {
235
239
  try {
@@ -239,7 +243,7 @@ function announceRecordChanged(recordId) {
239
243
  }
240
244
  }
241
245
  }
242
- var removed = /* @__PURE__ */ new Set();
246
+ var removed = globalSlot("ai-matrx.records.removed-records", () => /* @__PURE__ */ new Set());
243
247
  function markRecordRemoved(recordId) {
244
248
  removed.add(recordId);
245
249
  }
@@ -250,7 +254,7 @@ function onRecordChanged(listener) {
250
254
  listeners.add(listener);
251
255
  return () => listeners.delete(listener);
252
256
  }
253
- var tableListeners = /* @__PURE__ */ new Set();
257
+ var tableListeners = globalSlot("ai-matrx.records.table-structure-listeners", () => /* @__PURE__ */ new Set());
254
258
  function announceTableStructureChanged(tableId) {
255
259
  for (const listener of [...tableListeners]) {
256
260
  try {
@@ -566,9 +570,10 @@ function sourceArchivedOf(value) {
566
570
  }
567
571
 
568
572
  // src/ops.ts
573
+ import { globalSlot as globalSlot2 } from "@ai-matrx/kit/global-slot";
569
574
  var REMEMBER_MS = 3e4;
570
575
  var REMEMBER_MAX = 500;
571
- var ours = /* @__PURE__ */ new Map();
576
+ var ours = globalSlot2("ai-matrx.records.op-ledger", () => /* @__PURE__ */ new Map());
572
577
  function trim(now) {
573
578
  for (const [id, at] of ours) {
574
579
  if (now - at <= REMEMBER_MS) break;
@@ -6379,7 +6384,15 @@ function createTableDataPort({ client, aliases }) {
6379
6384
  const binding = bindingOf(alias);
6380
6385
  if (!binding) return noSuchAlias(alias);
6381
6386
  const shape = await shapeOf(alias, binding);
6382
- return shape.ok ? { ok: true, data: shape.data.columns } : shape;
6387
+ if (!shape.ok) return shape;
6388
+ return {
6389
+ ok: true,
6390
+ data: shape.data.columns.map((c) => {
6391
+ const target = c.type === "relation" && c.linksTo?.length === 1 ? c.linksTo[0] : null;
6392
+ const served = target ? Object.entries(aliases).find(([, b]) => b.tableId === target)?.[0] : void 0;
6393
+ return served ? { ...c, linksToSource: served } : c;
6394
+ })
6395
+ };
6383
6396
  },
6384
6397
  async list(alias, q = {}, cursor) {
6385
6398
  const binding = bindingOf(alias);