@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.
- package/CHANGELOG.md +30 -0
- package/dist/core/index.cjs +6 -4
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +6 -4
- package/dist/core/index.js.map +1 -1
- package/dist/data/index.cjs +19 -6
- package/dist/data/index.cjs.map +1 -1
- package/dist/data/index.d.cts +13 -0
- package/dist/data/index.d.ts +13 -0
- package/dist/data/index.js +19 -6
- package/dist/data/index.js.map +1 -1
- package/dist/data/react.d.cts +13 -0
- package/dist/data/react.d.ts +13 -0
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -34
- package/dist/index.d.ts +0 -34
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/list/index.cjs.map +1 -1
- package/dist/list/index.d.cts +5 -9
- package/dist/list/index.d.ts +5 -9
- package/dist/list/index.js.map +1 -1
- package/dist/react/index.cjs +20 -7
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -36
- package/dist/react/index.d.ts +2 -36
- package/dist/react/index.js +20 -7
- package/dist/react/index.js.map +1 -1
- package/dist/realtime/index.cjs +2 -1
- package/dist/realtime/index.cjs.map +1 -1
- package/dist/realtime/index.js +2 -1
- package/dist/realtime/index.js.map +1 -1
- package/dist/typed-table/index.cjs +7 -5
- package/dist/typed-table/index.cjs.map +1 -1
- package/dist/typed-table/index.d.cts +2 -2
- package/dist/typed-table/index.d.ts +2 -2
- package/dist/typed-table/index.js +7 -5
- package/dist/typed-table/index.js.map +1 -1
- package/package.json +3 -3
package/dist/react/index.d.cts
CHANGED
|
@@ -15285,40 +15285,6 @@ interface RecordsSeed {
|
|
|
15285
15285
|
answers: RecordsSeedAnswer[];
|
|
15286
15286
|
}
|
|
15287
15287
|
|
|
15288
|
-
/**
|
|
15289
|
-
* THE CLIENT OPERATION LEDGER — how a browser recognises the echo of its own write.
|
|
15290
|
-
*
|
|
15291
|
-
* Every private-channel notice the record store broadcasts reaches EVERY browser watching that
|
|
15292
|
-
* Table, including the one that caused it. Until now the writer had no way to tell its own
|
|
15293
|
-
* change from a colleague's: the notice carried ids and nothing about who moved them, so
|
|
15294
|
-
* `recordsRealtimePort.ts` declared `acceptEchoFromSelf: true` and the writer re-read a page it
|
|
15295
|
-
* had already updated. It was named honestly rather than faked, and this is the other half.
|
|
15296
|
-
*
|
|
15297
|
-
* THE SHAPE. Every write through `RecordsClient` mints a uuid, sends it to the store as the
|
|
15298
|
-
* platform envelope key `_op_id`, and remembers it here. The store lifts that key out before
|
|
15299
|
-
* storage — it is never written onto the record — carries it on the change-feed row, and puts
|
|
15300
|
-
* it in the notice as `op_id`. A notice whose `op_id` is in this ledger is this browser's own
|
|
15301
|
-
* echo and is dropped.
|
|
15302
|
-
*
|
|
15303
|
-
* WHY A TIME WINDOW RATHER THAN A DELETE-ON-MATCH. One write can produce MORE THAN ONE notice:
|
|
15304
|
-
* a record write that also moves a column produces a `record` notice and a `field` notice, and
|
|
15305
|
-
* a statement touching two Tables produces one per Table. Forgetting an op the first time it
|
|
15306
|
-
* came back would make the second notice look like somebody else's and cost the re-read this
|
|
15307
|
-
* exists to avoid. So an op is remembered for a window and expires on its own.
|
|
15308
|
-
*
|
|
15309
|
-
* 🚨 THE WINDOW IS A CORRECTNESS BOUNDARY, NOT A TUNING KNOB, AND IT FAILS IN THE SAFE
|
|
15310
|
-
* DIRECTION BY CONSTRUCTION. Too SHORT and a late echo is treated as a stranger's change: one
|
|
15311
|
-
* extra read of a page that is already correct — invisible. Too LONG and a stranger's change
|
|
15312
|
-
* that happened to reuse our uuid would be dropped — which cannot happen, because it is a v4
|
|
15313
|
-
* uuid nobody else has ever seen. The only real cost of a long window is memory, so it is
|
|
15314
|
-
* bounded by COUNT as well as by time.
|
|
15315
|
-
*
|
|
15316
|
-
* WHAT THIS IS NOT. It is not a replacement for the store's optimistic concurrency, and it
|
|
15317
|
-
* never decides what is true — a dropped echo means "I already know about this", and every
|
|
15318
|
-
* path that could be wrong about that (a reconnect, a tab wake, a notice with no `op_id` at
|
|
15319
|
-
* all) re-reads through the door as it always did.
|
|
15320
|
-
*/
|
|
15321
|
-
|
|
15322
15288
|
/**
|
|
15323
15289
|
* Mint the id for one client operation and remember it as ours.
|
|
15324
15290
|
*
|
|
@@ -15997,9 +15963,9 @@ declare const f: {
|
|
|
15997
15963
|
member: <const O extends FieldOptions = {}>(opts?: O) => AppField<string, Req<O>, false, Uniq<O>>;
|
|
15998
15964
|
/** A file. */
|
|
15999
15965
|
attachment: <const O extends FieldOptions = {}>(opts?: O) => AppField<string, Req<O>, false, Uniq<O>>;
|
|
16000
|
-
/** A platform thing (`note`, `agent` …) of one of `kinds
|
|
15966
|
+
/** A platform thing (`note`, `agent` …) of one of `kinds`: `{ token, id }`, the store's own shape. */
|
|
16001
15967
|
entityReference: <const O extends FieldOptions = {}>(kinds: readonly string[], opts?: O) => AppField<{
|
|
16002
|
-
|
|
15968
|
+
token: string;
|
|
16003
15969
|
id: Uuid;
|
|
16004
15970
|
}, Req<O>, false, Uniq<O>>;
|
|
16005
15971
|
/** Computed by the store from `formulaText` (`{Visit fee} - {Deposit taken}`); read-only. */
|
package/dist/react/index.d.ts
CHANGED
|
@@ -15285,40 +15285,6 @@ interface RecordsSeed {
|
|
|
15285
15285
|
answers: RecordsSeedAnswer[];
|
|
15286
15286
|
}
|
|
15287
15287
|
|
|
15288
|
-
/**
|
|
15289
|
-
* THE CLIENT OPERATION LEDGER — how a browser recognises the echo of its own write.
|
|
15290
|
-
*
|
|
15291
|
-
* Every private-channel notice the record store broadcasts reaches EVERY browser watching that
|
|
15292
|
-
* Table, including the one that caused it. Until now the writer had no way to tell its own
|
|
15293
|
-
* change from a colleague's: the notice carried ids and nothing about who moved them, so
|
|
15294
|
-
* `recordsRealtimePort.ts` declared `acceptEchoFromSelf: true` and the writer re-read a page it
|
|
15295
|
-
* had already updated. It was named honestly rather than faked, and this is the other half.
|
|
15296
|
-
*
|
|
15297
|
-
* THE SHAPE. Every write through `RecordsClient` mints a uuid, sends it to the store as the
|
|
15298
|
-
* platform envelope key `_op_id`, and remembers it here. The store lifts that key out before
|
|
15299
|
-
* storage — it is never written onto the record — carries it on the change-feed row, and puts
|
|
15300
|
-
* it in the notice as `op_id`. A notice whose `op_id` is in this ledger is this browser's own
|
|
15301
|
-
* echo and is dropped.
|
|
15302
|
-
*
|
|
15303
|
-
* WHY A TIME WINDOW RATHER THAN A DELETE-ON-MATCH. One write can produce MORE THAN ONE notice:
|
|
15304
|
-
* a record write that also moves a column produces a `record` notice and a `field` notice, and
|
|
15305
|
-
* a statement touching two Tables produces one per Table. Forgetting an op the first time it
|
|
15306
|
-
* came back would make the second notice look like somebody else's and cost the re-read this
|
|
15307
|
-
* exists to avoid. So an op is remembered for a window and expires on its own.
|
|
15308
|
-
*
|
|
15309
|
-
* 🚨 THE WINDOW IS A CORRECTNESS BOUNDARY, NOT A TUNING KNOB, AND IT FAILS IN THE SAFE
|
|
15310
|
-
* DIRECTION BY CONSTRUCTION. Too SHORT and a late echo is treated as a stranger's change: one
|
|
15311
|
-
* extra read of a page that is already correct — invisible. Too LONG and a stranger's change
|
|
15312
|
-
* that happened to reuse our uuid would be dropped — which cannot happen, because it is a v4
|
|
15313
|
-
* uuid nobody else has ever seen. The only real cost of a long window is memory, so it is
|
|
15314
|
-
* bounded by COUNT as well as by time.
|
|
15315
|
-
*
|
|
15316
|
-
* WHAT THIS IS NOT. It is not a replacement for the store's optimistic concurrency, and it
|
|
15317
|
-
* never decides what is true — a dropped echo means "I already know about this", and every
|
|
15318
|
-
* path that could be wrong about that (a reconnect, a tab wake, a notice with no `op_id` at
|
|
15319
|
-
* all) re-reads through the door as it always did.
|
|
15320
|
-
*/
|
|
15321
|
-
|
|
15322
15288
|
/**
|
|
15323
15289
|
* Mint the id for one client operation and remember it as ours.
|
|
15324
15290
|
*
|
|
@@ -15997,9 +15963,9 @@ declare const f: {
|
|
|
15997
15963
|
member: <const O extends FieldOptions = {}>(opts?: O) => AppField<string, Req<O>, false, Uniq<O>>;
|
|
15998
15964
|
/** A file. */
|
|
15999
15965
|
attachment: <const O extends FieldOptions = {}>(opts?: O) => AppField<string, Req<O>, false, Uniq<O>>;
|
|
16000
|
-
/** A platform thing (`note`, `agent` …) of one of `kinds
|
|
15966
|
+
/** A platform thing (`note`, `agent` …) of one of `kinds`: `{ token, id }`, the store's own shape. */
|
|
16001
15967
|
entityReference: <const O extends FieldOptions = {}>(kinds: readonly string[], opts?: O) => AppField<{
|
|
16002
|
-
|
|
15968
|
+
token: string;
|
|
16003
15969
|
id: Uuid;
|
|
16004
15970
|
}, Req<O>, false, Uniq<O>>;
|
|
16005
15971
|
/** Computed by the store from `formulaText` (`{Visit fee} - {Deposit taken}`); read-only. */
|
package/dist/react/index.js
CHANGED
|
@@ -70,7 +70,8 @@ function isWritingDoor(door) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// src/core/recordChanges.ts
|
|
73
|
-
|
|
73
|
+
import { globalSlot } from "@ai-matrx/kit/global-slot";
|
|
74
|
+
var listeners = globalSlot("ai-matrx.records.record-change-listeners", () => /* @__PURE__ */ new Set());
|
|
74
75
|
function announceRecordChanged(recordId) {
|
|
75
76
|
for (const listener of [...listeners]) {
|
|
76
77
|
try {
|
|
@@ -80,7 +81,7 @@ function announceRecordChanged(recordId) {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
|
-
var removed = /* @__PURE__ */ new Set();
|
|
84
|
+
var removed = globalSlot("ai-matrx.records.removed-records", () => /* @__PURE__ */ new Set());
|
|
84
85
|
function markRecordRemoved(recordId) {
|
|
85
86
|
removed.add(recordId);
|
|
86
87
|
}
|
|
@@ -94,7 +95,7 @@ function onRecordChanged(listener) {
|
|
|
94
95
|
listeners.add(listener);
|
|
95
96
|
return () => listeners.delete(listener);
|
|
96
97
|
}
|
|
97
|
-
var tableListeners = /* @__PURE__ */ new Set();
|
|
98
|
+
var tableListeners = globalSlot("ai-matrx.records.table-structure-listeners", () => /* @__PURE__ */ new Set());
|
|
98
99
|
function announceTableStructureChanged(tableId) {
|
|
99
100
|
for (const listener of [...tableListeners]) {
|
|
100
101
|
try {
|
|
@@ -496,9 +497,10 @@ function sourceArchivedOf(value) {
|
|
|
496
497
|
}
|
|
497
498
|
|
|
498
499
|
// src/ops.ts
|
|
500
|
+
import { globalSlot as globalSlot2 } from "@ai-matrx/kit/global-slot";
|
|
499
501
|
var REMEMBER_MS = 3e4;
|
|
500
502
|
var REMEMBER_MAX = 500;
|
|
501
|
-
var ours = /* @__PURE__ */ new Map();
|
|
503
|
+
var ours = globalSlot2("ai-matrx.records.op-ledger", () => /* @__PURE__ */ new Map());
|
|
502
504
|
function trim(now) {
|
|
503
505
|
for (const [id, at] of ours) {
|
|
504
506
|
if (now - at <= REMEMBER_MS) break;
|
|
@@ -10418,8 +10420,9 @@ function optionsTableOf(field2) {
|
|
|
10418
10420
|
return typeof id === "string" && id ? id : null;
|
|
10419
10421
|
}
|
|
10420
10422
|
async function readTableShape(client, tableId) {
|
|
10421
|
-
const fields = await client.fields({ table_id: tableId });
|
|
10423
|
+
const [fields, table] = await Promise.all([client.fields({ table_id: tableId }), client.tableRead({ table_id: tableId, organization_id: client.config.organizationId })]);
|
|
10422
10424
|
if (!fields.ok) return fields;
|
|
10425
|
+
const titleField = table.ok ? table.data?.title_field ?? null : null;
|
|
10423
10426
|
const choiceFields = fields.data.filter((f2) => optionsTableOf(f2) !== null);
|
|
10424
10427
|
const options = await Promise.all(choiceFields.map((f2) => client.fieldOptions({ field_id: f2.id })));
|
|
10425
10428
|
const choicesById = /* @__PURE__ */ new Map();
|
|
@@ -10443,8 +10446,10 @@ async function readTableShape(client, tableId) {
|
|
|
10443
10446
|
type,
|
|
10444
10447
|
origin: "custom",
|
|
10445
10448
|
required: Boolean(field2.required),
|
|
10449
|
+
multi: field2.multi === true,
|
|
10446
10450
|
readOnly: COMPUTED_TYPES.has(field2.type) || field2.source === "formula" || field2.compute_on !== null && field2.compute_on !== void 0
|
|
10447
10451
|
};
|
|
10452
|
+
if (titleField !== null && (titleField === field2.key || titleField === field2.id)) column.title = true;
|
|
10448
10453
|
const choices = choicesById.get(field2.id);
|
|
10449
10454
|
if (choices) column.choices = choices;
|
|
10450
10455
|
if (field2.type === "relation" && field2.relation_target) column.linksTo = [field2.relation_target];
|
|
@@ -10710,7 +10715,15 @@ function createTableDataPort({ client, aliases }) {
|
|
|
10710
10715
|
const binding = bindingOf(alias);
|
|
10711
10716
|
if (!binding) return noSuchAlias(alias);
|
|
10712
10717
|
const shape = await shapeOf(alias, binding);
|
|
10713
|
-
|
|
10718
|
+
if (!shape.ok) return shape;
|
|
10719
|
+
return {
|
|
10720
|
+
ok: true,
|
|
10721
|
+
data: shape.data.columns.map((c) => {
|
|
10722
|
+
const target = c.type === "relation" && c.linksTo?.length === 1 ? c.linksTo[0] : null;
|
|
10723
|
+
const served = target ? Object.entries(aliases).find(([, b]) => b.tableId === target)?.[0] : void 0;
|
|
10724
|
+
return served ? { ...c, linksToSource: served } : c;
|
|
10725
|
+
})
|
|
10726
|
+
};
|
|
10714
10727
|
},
|
|
10715
10728
|
async list(alias, q = {}, cursor) {
|
|
10716
10729
|
const binding = bindingOf(alias);
|
|
@@ -11557,7 +11570,7 @@ var f = {
|
|
|
11557
11570
|
member: (opts) => field("member", opts),
|
|
11558
11571
|
/** A file. */
|
|
11559
11572
|
attachment: (opts) => field("attachment", opts),
|
|
11560
|
-
/** A platform thing (`note`, `agent` …) of one of `kinds
|
|
11573
|
+
/** A platform thing (`note`, `agent` …) of one of `kinds`: `{ token, id }`, the store's own shape. */
|
|
11561
11574
|
entityReference: (kinds, opts) => field("entity_reference", opts, { allowedTypes: [...kinds] }),
|
|
11562
11575
|
/** Computed by the store from `formulaText` (`{Visit fee} - {Deposit taken}`); read-only. */
|
|
11563
11576
|
formula: (formulaText, opts) => field("formula", opts, { declaration: { formula_text: formulaText, ...opts?.declaration ?? {} } }, true),
|