@ai-matrx/records-ui 0.3.2 → 0.5.4
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 +137 -0
- package/dist/index.cjs +44 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +44 -9
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,115 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.5.4
|
|
4
|
+
|
|
5
|
+
Makes the live FormBuilder certification wait for the editor state that follows
|
|
6
|
+
the saved-form tab, instead of asserting synchronously between the two React
|
|
7
|
+
effects.
|
|
8
|
+
|
|
9
|
+
### Consumer action
|
|
10
|
+
|
|
11
|
+
None. This changes only the live certification harness.
|
|
12
|
+
|
|
13
|
+
## 0.5.3
|
|
14
|
+
|
|
15
|
+
Makes the certification harness's raw record selector hide soft-deleted rows
|
|
16
|
+
by default, matching the active-record behavior of the store's public read
|
|
17
|
+
doors. An explicit `deleted_at` filter still allows a test to inspect
|
|
18
|
+
tombstones intentionally.
|
|
19
|
+
|
|
20
|
+
### Consumer action
|
|
21
|
+
|
|
22
|
+
None. This changes only the live certification harness.
|
|
23
|
+
|
|
24
|
+
## 0.5.2
|
|
25
|
+
|
|
26
|
+
Resets records inside the test organization's harness-owned `records_ui_*`
|
|
27
|
+
package Tables through the real delete door when certification starts. A view,
|
|
28
|
+
form, or action edited by an earlier run can no longer override the next run's
|
|
29
|
+
declared seed while product behavior continues to preserve real user edits.
|
|
30
|
+
|
|
31
|
+
### Consumer action
|
|
32
|
+
|
|
33
|
+
None. This changes only the live certification harness.
|
|
34
|
+
|
|
35
|
+
## 0.5.1
|
|
36
|
+
|
|
37
|
+
Builds the 0.5.0 system-table reconciliation against the refreshed 240-door
|
|
38
|
+
`@ai-matrx/records` contract after the dependency's live release guard caught
|
|
39
|
+
two newly registered store doors.
|
|
40
|
+
|
|
41
|
+
### Consumer action
|
|
42
|
+
|
|
43
|
+
None. The public API is unchanged.
|
|
44
|
+
|
|
45
|
+
## 0.5.0
|
|
46
|
+
|
|
47
|
+
**A package Table that changed SHAPE under an organization that already had a
|
|
48
|
+
copy.** `ActionInbox` could not record an outcome in one organization: its copy
|
|
49
|
+
of `records_ui_action` held `status` as a uuid-shaped Field while this package
|
|
50
|
+
declares it `text`, so every outcome write was refused `22P02 invalid input
|
|
51
|
+
syntax for type uuid: "open"`. The store was right; this package was the one out
|
|
52
|
+
of date. `ensureSystemTable` reconciled a MISSING Field on every load and never
|
|
53
|
+
looked at a Field it already had, so the organization that got its copy first
|
|
54
|
+
was the one where the package quietly stopped working.
|
|
55
|
+
|
|
56
|
+
Reconciliation is now two questions, not one — which Fields are missing, and
|
|
57
|
+
which are there with the wrong shape. Shape means exactly the two properties
|
|
58
|
+
that decide what a value is MADE OF, and therefore what the store will accept:
|
|
59
|
+
the behaviour (FLD-1) and whether it is multi (FLD-2). Label, sort, required and
|
|
60
|
+
config are deliberately left alone — they are presentation and policy, an
|
|
61
|
+
organization may legitimately have moved them, and none of them can make a write
|
|
62
|
+
fail. Nothing is ever renamed or removed.
|
|
63
|
+
|
|
64
|
+
Also fixed alongside it: a refusal from the FIRST declaration of a package Table
|
|
65
|
+
was both returned as this package's never-throw envelope and left as an
|
|
66
|
+
unhandled promise rejection, so a refusal that had in fact been reported
|
|
67
|
+
properly also produced a red console (and, under a test runner, a failed run).
|
|
68
|
+
|
|
69
|
+
Proved against the LIVE store in `demo/system-table-shape.test.tsx`: the Table
|
|
70
|
+
is declared with the old behaviour, the module is reloaded — which is what a
|
|
71
|
+
page load is, and the only way an organization ever meets a newer package — and
|
|
72
|
+
the new spec is ensured over it. RED without the fix: `expected 'range' to be
|
|
73
|
+
'text'`.
|
|
74
|
+
|
|
75
|
+
### Consumer action
|
|
76
|
+
|
|
77
|
+
None. The public API is unchanged, and the reconciliation is automatic on the
|
|
78
|
+
next load of any screen that opens a package-owned Table. An organization whose
|
|
79
|
+
copy drifted is levelled the first time a person opens that screen — there is
|
|
80
|
+
nothing to run and nothing to migrate.
|
|
81
|
+
|
|
82
|
+
## 0.4.3
|
|
83
|
+
|
|
84
|
+
Resets the harness-owned demo table through the store's real read and delete
|
|
85
|
+
doors before a certification run. Repeated releases can no longer push the
|
|
86
|
+
three canonical Grid rows beyond the first page. The demo port is also
|
|
87
|
+
configurable for isolated local proof while remaining `3005` by default.
|
|
88
|
+
|
|
89
|
+
### Consumer action
|
|
90
|
+
|
|
91
|
+
None. The public package API is unchanged.
|
|
92
|
+
|
|
93
|
+
## 0.4.2
|
|
94
|
+
|
|
95
|
+
Uses the same bounded live-store wait as `@ai-matrx/records`, so a legitimate
|
|
96
|
+
overlapping rollback-only verification cannot make the demo proof fail after
|
|
97
|
+
twenty seconds.
|
|
98
|
+
|
|
99
|
+
### Consumer action
|
|
100
|
+
|
|
101
|
+
None beyond taking `latest`; the public API is unchanged.
|
|
102
|
+
|
|
103
|
+
## 0.4.1
|
|
104
|
+
|
|
105
|
+
Restores monotonic versioning after a concurrent release plan overwrote the
|
|
106
|
+
already-staged 0.4.0 version, and shares the serialized record-store release
|
|
107
|
+
lane with `@ai-matrx/records`.
|
|
108
|
+
|
|
109
|
+
### Consumer action
|
|
110
|
+
|
|
111
|
+
None beyond taking `latest`; the 0.4.0 API remains intact.
|
|
112
|
+
|
|
3
113
|
## 0.3.2
|
|
4
114
|
|
|
5
115
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -28,6 +138,33 @@ table page's records plus its five admin rails, with the settings rail really
|
|
|
28
138
|
opening the panel; and the same page giving a plain member the records and NONE
|
|
29
139
|
of the administering controls — absent, not disabled.
|
|
30
140
|
|
|
141
|
+
**THE WALK, and the three defects walking it found.** `demo/walk.test.tsx` runs
|
|
142
|
+
the whole product against the live store in one file — declare a table with three
|
|
143
|
+
field types, write a record through the generated form, open the table page and
|
|
144
|
+
peek the record, save a view, file an agent's field proposal and accept it,
|
|
145
|
+
export the CSV, then walk the same table as a plain member and be offered nothing
|
|
146
|
+
to administer. Seven cases, green, and the table it makes is soft-deleted after.
|
|
147
|
+
It found:
|
|
148
|
+
|
|
149
|
+
· **A table nobody had saved a view for showed NO RECORDS.** A view is a record,
|
|
150
|
+
so a brand-new table has none, and `TablePage` then rendered its own honest
|
|
151
|
+
"Waiting for this table's views" and nothing else — a dead end on the first
|
|
152
|
+
table anybody makes. The page now seeds `DEFAULT_VIEW_NAME` ("All records",
|
|
153
|
+
grid), an ordinary saved-view record a person can rename or delete, never a
|
|
154
|
+
hidden built-in mode. A host with its own idea passes `seedViews`.
|
|
155
|
+
|
|
156
|
+
· **`ProposedChange.table` was required for all three acts and unfillable for
|
|
157
|
+
two.** `update` and `remove` name a record and ignore `table`, so a proposer
|
|
158
|
+
wanting to change an existing record had to supply the id of the kernel Table
|
|
159
|
+
that record lives in — a fact the client has no door to ask for. It is optional
|
|
160
|
+
now, and an `add` without it is refused by name instead of writing nowhere.
|
|
161
|
+
|
|
162
|
+
· **The demo harness's pages were dead in a browser while `pnpm test` was
|
|
163
|
+
green.** Two copies of React in one vite graph (the demo's own dependency tree
|
|
164
|
+
plus the workspace's, through design-system and alchemy): React reported
|
|
165
|
+
"Invalid hook call … more than one copy of React" and the page rendered
|
|
166
|
+
nothing. `demo/vite.config.ts` now dedupes react and react-dom.
|
|
167
|
+
|
|
31
168
|
**Consumer action:** none. `recordsDataSource(client)` is unchanged at the call
|
|
32
169
|
site; a host that deliberately wants a different fallback passes it second.
|
|
33
170
|
|
package/dist/index.cjs
CHANGED
|
@@ -980,17 +980,18 @@ async function ensureSystemTable(client, spec) {
|
|
|
980
980
|
if (found) {
|
|
981
981
|
const grown = await addMissingFields(client, spec, found.id);
|
|
982
982
|
if (!grown.ok) return grown;
|
|
983
|
+
const levelled = await reconcileFieldShapes(client, spec, found.id);
|
|
984
|
+
if (!levelled.ok) return levelled;
|
|
983
985
|
return { ok: true, data: found.id };
|
|
984
986
|
}
|
|
985
987
|
let settle = () => void 0;
|
|
986
988
|
let fail = () => void 0;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
);
|
|
989
|
+
const pending = new Promise((resolve, reject) => {
|
|
990
|
+
settle = resolve;
|
|
991
|
+
fail = reject;
|
|
992
|
+
});
|
|
993
|
+
pending.catch(() => void 0);
|
|
994
|
+
inFlight.set(cacheKey, pending);
|
|
994
995
|
const declared = await declare(client, spec);
|
|
995
996
|
if (declared.ok) settle(declared.data);
|
|
996
997
|
else {
|
|
@@ -1019,6 +1020,22 @@ async function addMissingFields(client, spec, tableId) {
|
|
|
1019
1020
|
}
|
|
1020
1021
|
return { ok: true, data: true };
|
|
1021
1022
|
}
|
|
1023
|
+
async function reconcileFieldShapes(client, spec, tableId) {
|
|
1024
|
+
const held = await client.fields({ table_id: tableId });
|
|
1025
|
+
if (!held.ok) return held;
|
|
1026
|
+
const byKey = new Map(held.data.map((f) => [f.key, f]));
|
|
1027
|
+
for (const field of spec.fields) {
|
|
1028
|
+
const have = byKey.get(field.key);
|
|
1029
|
+
if (!have) continue;
|
|
1030
|
+
const patch = {};
|
|
1031
|
+
if (have.type !== field.type) patch.type = field.type;
|
|
1032
|
+
if (have.multi !== (field.multi ?? false)) patch.multi = field.multi ?? false;
|
|
1033
|
+
if (Object.keys(patch).length === 0) continue;
|
|
1034
|
+
const written = await client.recordUpdate({ record_id: have.id, patch });
|
|
1035
|
+
if (!written.ok) return written;
|
|
1036
|
+
}
|
|
1037
|
+
return { ok: true, data: true };
|
|
1038
|
+
}
|
|
1022
1039
|
function fieldDocument(field, tableId) {
|
|
1023
1040
|
return {
|
|
1024
1041
|
key: field.key,
|
|
@@ -2190,6 +2207,12 @@ function ProposalRow({
|
|
|
2190
2207
|
const [confirming, setConfirming] = (0, import_react32.useState)(false);
|
|
2191
2208
|
async function applyThroughTheStore() {
|
|
2192
2209
|
if (change.act === "add") {
|
|
2210
|
+
if (!change.table) {
|
|
2211
|
+
return {
|
|
2212
|
+
settled: "refused",
|
|
2213
|
+
sentence: "This proposal asks to add a record but does not name the table it goes in, so nothing was written."
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2193
2216
|
const written = await client.recordWrite({ table_id: change.table, data: change.data ?? {} });
|
|
2194
2217
|
return written.ok ? { settled: "accepted", sentence: "Written." } : { settled: "refused", sentence: written.error.message };
|
|
2195
2218
|
}
|
|
@@ -5895,7 +5918,11 @@ var import_react77 = require("react");
|
|
|
5895
5918
|
var import_react78 = require("@ai-matrx/records/react");
|
|
5896
5919
|
var import_design_system36 = require("@ai-matrx/design-system");
|
|
5897
5920
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
5898
|
-
|
|
5921
|
+
var DEFAULT_VIEW_NAME = "All records";
|
|
5922
|
+
function defaultView(tableId) {
|
|
5923
|
+
return { name: DEFAULT_VIEW_NAME, subject: tableId, layout: "grid", isDefault: true };
|
|
5924
|
+
}
|
|
5925
|
+
function TablePage({ tableId, pageSize = 100, seedViews, className }) {
|
|
5899
5926
|
const table = (0, import_react78.useTable)(tableId);
|
|
5900
5927
|
const rights = useTableRights(table.data);
|
|
5901
5928
|
const [view, setView] = (0, import_react77.useState)(null);
|
|
@@ -5907,7 +5934,15 @@ function TablePage({ tableId, pageSize = 100, className }) {
|
|
|
5907
5934
|
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: (0, import_design_system36.cn)("flex min-h-0 gap-4", className), children: [
|
|
5908
5935
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
|
|
5909
5936
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
5910
|
-
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
5937
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
5938
|
+
ViewBar,
|
|
5939
|
+
{
|
|
5940
|
+
tableId,
|
|
5941
|
+
onActiveView: setView,
|
|
5942
|
+
seed: seedViews ?? [defaultView(tableId)],
|
|
5943
|
+
className: "min-w-0 flex-1"
|
|
5944
|
+
}
|
|
5945
|
+
),
|
|
5911
5946
|
rights.write ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_design_system36.Button, { size: "sm", onClick: () => show("new-record"), children: "New record" }) : null,
|
|
5912
5947
|
rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
5913
5948
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_design_system36.Button, { size: "sm", variant: "outline", onClick: () => show("field"), children: "Add field" }),
|