@ai-matrx/records 0.87.32 → 0.92.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.
- package/CHANGELOG.md +92 -0
- package/README.md +115 -0
- package/dist/{client-E0VQ0NPO.d.ts → client-DE9Mq6YX.d.cts} +8 -5
- package/dist/{client-C3ZZNfGH.d.cts → client-vJAb7g9r.d.ts} +8 -5
- package/dist/core/index.cjs +25 -7
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +23 -12
- package/dist/core/index.d.ts +23 -12
- package/dist/core/index.js +25 -7
- package/dist/core/index.js.map +1 -1
- package/dist/data/index.cjs +19 -7
- package/dist/data/index.cjs.map +1 -1
- package/dist/data/index.d.cts +12 -142
- package/dist/data/index.d.ts +12 -142
- package/dist/data/index.js +19 -7
- package/dist/data/index.js.map +1 -1
- package/dist/{declareTable-BK3zufr7.d.cts → declareTable-Btg5bql1.d.cts} +4 -4
- package/dist/{declareTable-B12XybRp.d.ts → declareTable-JKt2q34d.d.ts} +4 -4
- package/dist/entity/index.cjs +984 -0
- package/dist/entity/index.cjs.map +1 -0
- package/dist/entity/index.d.cts +36 -0
- package/dist/entity/index.d.ts +36 -0
- package/dist/entity/index.js +964 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/react.cjs +1024 -0
- package/dist/entity/react.cjs.map +1 -0
- package/dist/entity/react.d.cts +511 -0
- package/dist/entity/react.d.ts +511 -0
- package/dist/entity/react.js +1015 -0
- package/dist/entity/react.js.map +1 -0
- package/dist/{field-BanTVGK2.d.cts → field-BJBnTyJ0.d.cts} +1 -1
- package/dist/{field-BPABOJ2Y.d.ts → field-Ti5iAxFr.d.ts} +1 -1
- package/dist/forms/index.cjs +984 -0
- package/dist/forms/index.cjs.map +1 -0
- package/dist/forms/index.d.cts +333 -0
- package/dist/forms/index.d.ts +333 -0
- package/dist/forms/index.js +961 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -8
- package/dist/index.d.ts +11 -8
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.cts +7 -6
- package/dist/memory/index.d.ts +7 -6
- package/dist/port-CPVBfDxS.d.cts +301 -0
- package/dist/port-D2i2o7Lg.d.ts +301 -0
- package/dist/portal-Db4CYyHe.d.ts +331 -0
- package/dist/portal-DfeY2Boy.d.cts +331 -0
- package/dist/{ports-TMWX-t9l.d.ts → ports-BWtxAnXh.d.ts} +2 -2
- package/dist/{ports-D8-iuY1P.d.cts → ports-DSU2RsR6.d.cts} +2 -2
- package/dist/react/index.cjs +55 -523
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +14 -1
- package/dist/react/index.d.ts +14 -1
- package/dist/react/index.js +52 -527
- package/dist/react/index.js.map +1 -1
- package/dist/realtime/index.d.cts +2 -2
- package/dist/realtime/index.d.ts +2 -2
- package/dist/{record-DpB2QgAZ.d.cts → record-BQi92d4-.d.cts} +7 -1
- package/dist/{record-DpB2QgAZ.d.ts → record-BQi92d4-.d.ts} +7 -1
- package/dist/relation-DMwkmzgN.d.cts +230 -0
- package/dist/relation-DlcqV_W5.d.ts +230 -0
- package/dist/rule-B8kR0l01.d.ts +250 -0
- package/dist/rule-BMViCLmW.d.cts +250 -0
- package/dist/search/index.cjs +283 -0
- package/dist/search/index.cjs.map +1 -0
- package/dist/search/index.d.cts +44 -0
- package/dist/search/index.d.ts +44 -0
- package/dist/search/index.js +260 -0
- package/dist/search/index.js.map +1 -0
- package/dist/setup/index.cjs +6854 -0
- package/dist/setup/index.cjs.map +1 -0
- package/dist/setup/index.d.cts +87 -0
- package/dist/setup/index.d.ts +87 -0
- package/dist/setup/index.js +6829 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/{subscription-D7KQBHXN.d.ts → subscription-5CLN3Jug.d.ts} +3 -249
- package/dist/{subscription-BD9DnJxX.d.cts → subscription-D-VEzvyy.d.cts} +3 -249
- package/dist/{table-kind-BZu8mtND.d.cts → table-kind-CVp43g6h.d.ts} +3 -558
- package/dist/{table-kind-xKxTHtrV.d.ts → table-kind-Cm6PXkWx.d.cts} +3 -558
- package/dist/templates/index.d.cts +10 -9
- package/dist/templates/index.d.ts +10 -9
- package/dist/typed-table/index.cjs +19 -7
- package/dist/typed-table/index.cjs.map +1 -1
- package/dist/typed-table/index.d.cts +10 -7
- package/dist/typed-table/index.d.ts +10 -7
- package/dist/typed-table/index.js +19 -7
- package/dist/typed-table/index.js.map +1 -1
- package/dist/{types-CLP5H4Wk.d.cts → types-9Q5SHRDJ.d.cts} +2 -2
- package/dist/{types-DmPkWQic.d.ts → types-CfYy-vdV.d.ts} +2 -2
- package/dist/{types-C2uspRtf.d.cts → types-Cs2MyE3u.d.cts} +2 -2
- package/dist/{types-Dv61jStB.d.ts → types-DF-dxB_l.d.ts} +2 -2
- package/dist/types-DTEGaxso.d.cts +139 -0
- package/dist/types-Dji5SpFS.d.ts +139 -0
- package/dist/use-cases/index.d.cts +5 -5
- package/dist/use-cases/index.d.ts +5 -5
- package/dist/{validate-CWVToe1T.d.ts → validate-BydKd-Le.d.ts} +1 -1
- package/dist/{validate-DEB_mn4Y.d.cts → validate-CqJakTKk.d.cts} +1 -1
- package/dist/versions/index.cjs +375 -0
- package/dist/versions/index.cjs.map +1 -0
- package/dist/versions/index.d.cts +160 -0
- package/dist/versions/index.d.ts +160 -0
- package/dist/versions/index.js +353 -0
- package/dist/versions/index.js.map +1 -0
- package/package.json +62 -2
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
/** A `uuid` as the store writes it. */
|
|
6
|
+
type Uuid = string;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What KIND of refusal it was. The mapping from SQLSTATE to code lives in
|
|
10
|
+
* `/core`, and it is checked against `STORE_REFUSAL_CODES` — the list of every
|
|
11
|
+
* SQLSTATE the store's own bodies raise, generated from the live catalogue. A
|
|
12
|
+
* code the store can raise and this union cannot name is a hole, and the
|
|
13
|
+
* real-door suite fails on it.
|
|
14
|
+
*/
|
|
15
|
+
type RecordsErrorCode =
|
|
16
|
+
/** `PT409` — somebody else's write won. The record clock moved under you. */
|
|
17
|
+
"stale_write"
|
|
18
|
+
/**
|
|
19
|
+
* The call needs an organization and none was carried (the platform's `organization_required`; the entity
|
|
20
|
+
* doors' MX002). Never resolved by guessing one: the person is shown their memberships and picks.
|
|
21
|
+
*/
|
|
22
|
+
| "organization_required"
|
|
23
|
+
/** `42501` — a door. Either the schema is revoked, or the store is switched off, or this principal may not pass. */
|
|
24
|
+
| "door"
|
|
25
|
+
/** `23514` — a Rule, a shape guard, or a validation the store enforces refused it. */
|
|
26
|
+
| "refused_by_rule"
|
|
27
|
+
/**
|
|
28
|
+
* `22004` / `22023` / `22P02` — the call itself was malformed: a missing id, a
|
|
29
|
+
* patch that is not an object, or a value that does not fit the type the
|
|
30
|
+
* store holds for that Field. The store's own message names the value and the
|
|
31
|
+
* type it could not be read as.
|
|
32
|
+
*/
|
|
33
|
+
| "invalid_argument"
|
|
34
|
+
/** `02000` — the record is not here (deleted, never existed, or another organization's). */
|
|
35
|
+
| "not_found"
|
|
36
|
+
/** `23503` — something it points at is not there. */
|
|
37
|
+
| "missing_reference"
|
|
38
|
+
/** `23505` — that thing is already here. */
|
|
39
|
+
| "already_exists"
|
|
40
|
+
/** `0A000` — the store does not do this yet, and says which part. */
|
|
41
|
+
| "not_supported"
|
|
42
|
+
/** `53400` / `22012` — the store ran out of something, or the arithmetic could not be done. */
|
|
43
|
+
| "store_limit"
|
|
44
|
+
/**
|
|
45
|
+
* `40001` — the read-access snapshot this connection carries is older than a
|
|
46
|
+
* write the reader has already observed, so the store refused to answer at
|
|
47
|
+
* all rather than hand back something they just lost. Retry on a fresh
|
|
48
|
+
* connection; it is the one refusal in this list that is worth repeating.
|
|
49
|
+
*/
|
|
50
|
+
| "stale_read"
|
|
51
|
+
/**
|
|
52
|
+
* `57014` — the store cancelled the statement at its own timeout. Nothing was
|
|
53
|
+
* refused on its merits and nothing was written; the same call can succeed on
|
|
54
|
+
* a smaller page or a quieter moment. With `stale_read`, one of the only two
|
|
55
|
+
* refusals in this list worth repeating.
|
|
56
|
+
*/
|
|
57
|
+
| "timed_out"
|
|
58
|
+
/**
|
|
59
|
+
* THE DOOR THAT DOES NOT EXIST YET. Not an error condition in the database —
|
|
60
|
+
* this package asked for a door that is not in the live catalogue, so it
|
|
61
|
+
* refused to guess. `door_absent` always names the door and the lane that
|
|
62
|
+
* owns it; it is never a silent no-op and never an empty list.
|
|
63
|
+
*/
|
|
64
|
+
| "door_absent"
|
|
65
|
+
/**
|
|
66
|
+
* `PGRST202` / `PGRST205` — PostgREST could not find the function or the
|
|
67
|
+
* schema. The database this client is pointed at is not serving the record
|
|
68
|
+
* store: the schema is not exposed, or the grants are not there.
|
|
69
|
+
*/
|
|
70
|
+
| "store_not_served"
|
|
71
|
+
/**
|
|
72
|
+
* `PT428` — the move was not refused and it did not happen: a stage gate whose
|
|
73
|
+
* `on_fail` is `require_approval` filed a request with the people who can
|
|
74
|
+
* answer it, and left the record exactly where it was. The store's own
|
|
75
|
+
* sentence is the gate's own sentence, and the DETAIL carries the approval.
|
|
76
|
+
*
|
|
77
|
+
* It is the one code in this list that is not a failure. A screen that drew it
|
|
78
|
+
* as one would tell somebody their work was rejected when it is being
|
|
79
|
+
* considered; a screen that drew it as success would move the card while
|
|
80
|
+
* somebody is still deciding. `custom.pipeline_move` answers an object on this
|
|
81
|
+
* path rather than raising, so a caller only meets PT428 when the move went
|
|
82
|
+
* through the ORDINARY write door — which is most of them.
|
|
83
|
+
*/
|
|
84
|
+
| "needs_approval"
|
|
85
|
+
/**
|
|
86
|
+
* THE CALL NEVER REACHED THE STORE. Not a refusal — a transport failure: the
|
|
87
|
+
* browser lost the network, the gateway answered with an HTTP status and no
|
|
88
|
+
* body, the request was cancelled. supabase-js hands these back with an EMPTY
|
|
89
|
+
* `code` (a `FetchError`) or with the HTTP status in it (`"404"`, `"502"`),
|
|
90
|
+
* neither of which is a SQLSTATE, and until 2026-09-22 they all landed in
|
|
91
|
+
* `internal` — which is the one classification that tells a screen nothing,
|
|
92
|
+
* and which made the package print a sentence about ITSELF to the person
|
|
93
|
+
* (lane TAILS-6). Nothing was written, and the same call can simply be made
|
|
94
|
+
* again, which is what separates it from `internal`.
|
|
95
|
+
*/
|
|
96
|
+
| "unreachable"
|
|
97
|
+
/** Anything the mapper could not place. It still carries the store's own words. */
|
|
98
|
+
| "internal";
|
|
99
|
+
/** A refusal, in the store's own words. */
|
|
100
|
+
interface RecordsError {
|
|
101
|
+
code: RecordsErrorCode;
|
|
102
|
+
/** THE STORE'S message, verbatim. Never paraphrased. */
|
|
103
|
+
message: string;
|
|
104
|
+
/** THE STORE'S hint, verbatim — this is where the remedy is written. */
|
|
105
|
+
hint?: string;
|
|
106
|
+
/** The SQLSTATE, so a caller can be exact when a code is not exact enough. */
|
|
107
|
+
sqlstate?: string;
|
|
108
|
+
/** Whatever the store put in DETAIL, parsed when it was json. */
|
|
109
|
+
detail?: unknown;
|
|
110
|
+
/**
|
|
111
|
+
* TRUE when this package did not recognise the class it was handed, so a
|
|
112
|
+
* caller can say so instead of reading the silence as "handled".
|
|
113
|
+
*
|
|
114
|
+
* 🚨 IT IS A FLAG AND A SEPARATE SENTENCE, NEVER AN APPENDIX TO `hint`
|
|
115
|
+
* (lane TAILS-6, 2026-09-22). `mapPgError` used to append "@ai-matrx/records
|
|
116
|
+
* did not recognise SQLSTATE … from checklistTemplates" ONTO `hint`, and
|
|
117
|
+
* `hint` is the field every screen treats as THE REMEDY — the sentence a
|
|
118
|
+
* person is told to act on. So a dropped connection on the Checklists tab
|
|
119
|
+
* showed a plumbing company "Something went wrong at our end" over a line
|
|
120
|
+
* about a TypeScript package and a SQLSTATE. A note this package writes about
|
|
121
|
+
* ITSELF is for whoever debugs it and belongs nowhere a person reads.
|
|
122
|
+
*/
|
|
123
|
+
unmapped?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* THIS PACKAGE'S OWN NOTE, for an engineer. Never the store's words and never
|
|
126
|
+
* a person's: screens render it out of sight (a `title` attribute, a
|
|
127
|
+
* screen-reader line) or not at all.
|
|
128
|
+
*/
|
|
129
|
+
diagnostic?: string;
|
|
130
|
+
}
|
|
131
|
+
type RecordsResult<T> = {
|
|
132
|
+
ok: true;
|
|
133
|
+
data: T;
|
|
134
|
+
} | {
|
|
135
|
+
ok: false;
|
|
136
|
+
error: RecordsError;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/** §0. One column, read live — never from a generated file. */
|
|
140
|
+
interface Column {
|
|
141
|
+
/** The store's stable field key — never derived from the label. No platform key starts with "_". */
|
|
142
|
+
key: string;
|
|
143
|
+
label: string;
|
|
144
|
+
/** The store's word for what the value is: text, number, date, datetime, boolean, list, relation, formula, … */
|
|
145
|
+
type: string;
|
|
146
|
+
origin: "platform" | "custom";
|
|
147
|
+
required: boolean;
|
|
148
|
+
/** For a choice column: the words a person picks from (what a cell reads as and a write takes). */
|
|
149
|
+
choices?: string[];
|
|
150
|
+
/** For a link column: what it may point at (a custom table's id, or entity tokens). */
|
|
151
|
+
linksTo?: string[];
|
|
152
|
+
/**
|
|
153
|
+
* For a link column: the alias, in the SAME port, that serves the table it points at — so a form can list
|
|
154
|
+
* the linked rows to pick from. Absent when no source of this Applet serves that table.
|
|
155
|
+
*/
|
|
156
|
+
linksToSource?: string;
|
|
157
|
+
/**
|
|
158
|
+
* The store's own answer to "does this column hold more than one value" (`custom.field.multi`). A choice
|
|
159
|
+
* column's type is `list` whether it takes one word or many — only this tells them apart (Status, one
|
|
160
|
+
* value, drew as four switches and the store refused the list it sent: social planner, 2026-10-08).
|
|
161
|
+
*/
|
|
162
|
+
multi?: boolean;
|
|
163
|
+
/** The column a row is named by (the table's `title_field`) — what a link to the row reads as. */
|
|
164
|
+
title?: boolean;
|
|
165
|
+
readOnly: boolean;
|
|
166
|
+
}
|
|
167
|
+
/** §0. One row, keyed by `Column.key`. Underscore keys are the runtime's own. */
|
|
168
|
+
interface Row {
|
|
169
|
+
_id: Uuid;
|
|
170
|
+
_version: number;
|
|
171
|
+
_organization_id: Uuid;
|
|
172
|
+
/** On a platform row only: the organization's custom fields (a real column may be named `custom`). */
|
|
173
|
+
_custom?: Record<string, unknown>;
|
|
174
|
+
[key: string]: unknown;
|
|
175
|
+
}
|
|
176
|
+
/** §0. What a write carries, keyed by `Column.key`; `_custom` on platform sources. */
|
|
177
|
+
type Values = Record<string, unknown> & {
|
|
178
|
+
_custom?: Record<string, unknown>;
|
|
179
|
+
};
|
|
180
|
+
type WhereOp = "eq" | "neq" | "lt" | "lte" | "gt" | "gte" | "contains" | "in" | "empty";
|
|
181
|
+
interface WhereClause {
|
|
182
|
+
column: string;
|
|
183
|
+
op: WhereOp;
|
|
184
|
+
value?: unknown;
|
|
185
|
+
}
|
|
186
|
+
type Lane = "all" | "mine" | "team" | "orgs" | "shared" | "public" | "system";
|
|
187
|
+
/** §4. A question of one source. Every clause must hold. */
|
|
188
|
+
interface Query {
|
|
189
|
+
where?: WhereClause[];
|
|
190
|
+
search?: string;
|
|
191
|
+
sort?: {
|
|
192
|
+
column: string;
|
|
193
|
+
direction?: "asc" | "desc";
|
|
194
|
+
}[];
|
|
195
|
+
pageSize?: number;
|
|
196
|
+
/** Default: the type's "Shown to by default" knob. A custom table has one lane: what row security shows. */
|
|
197
|
+
lane?: Lane;
|
|
198
|
+
/** A filter, never a lock. Default "all". */
|
|
199
|
+
org?: Uuid | "all";
|
|
200
|
+
}
|
|
201
|
+
/** §4. One page; `cursor` is null on the last one. */
|
|
202
|
+
interface Page {
|
|
203
|
+
rows: Row[];
|
|
204
|
+
total: number | null;
|
|
205
|
+
cursor: string | null;
|
|
206
|
+
}
|
|
207
|
+
interface DataSource {
|
|
208
|
+
alias: string;
|
|
209
|
+
label: string;
|
|
210
|
+
kind: "table" | "entity";
|
|
211
|
+
canCreate: boolean;
|
|
212
|
+
canUpdate: boolean;
|
|
213
|
+
}
|
|
214
|
+
/** The one event stream a source has: these ids moved — re-read only them. `null`: cannot tell which. */
|
|
215
|
+
interface RowsEvent {
|
|
216
|
+
ids: Uuid[] | null;
|
|
217
|
+
/** The table's columns moved (one added, retired or changed): re-read `columns()` too. */
|
|
218
|
+
shape?: true;
|
|
219
|
+
}
|
|
220
|
+
type DataPortEventName = `rows:${string}`;
|
|
221
|
+
/** §4. The host's data port, scoped to ONE mounted Applet's aliases. Every call answers, never throws. */
|
|
222
|
+
interface DataPort {
|
|
223
|
+
sources(): Promise<RecordsResult<DataSource[]>>;
|
|
224
|
+
columns(alias: string): Promise<RecordsResult<Column[]>>;
|
|
225
|
+
list(alias: string, q: Query, cursor?: string): Promise<RecordsResult<Page>>;
|
|
226
|
+
get(alias: string, id: Uuid): Promise<RecordsResult<Row | null>>;
|
|
227
|
+
create(alias: string, values: Values): Promise<RecordsResult<Row>>;
|
|
228
|
+
update(alias: string, id: Uuid, patch: Values, baseVersion: number): Promise<RecordsResult<Row>>;
|
|
229
|
+
archive(alias: string, id: Uuid): Promise<RecordsResult<void>>;
|
|
230
|
+
restore(alias: string, id: Uuid): Promise<RecordsResult<void>>;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The frame's side of the §1 event stream `rows:<alias>` (the AppletHost's `host.events`). The listener
|
|
234
|
+
* stays in the frame; only the event payload `{ids, shape?}` crosses the channel.
|
|
235
|
+
*/
|
|
236
|
+
interface DataEvents {
|
|
237
|
+
on(event: DataPortEventName, listener: (event: RowsEvent) => void): () => void;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The HOST's DataPort (contracts amendment 2.2): the port plus the producer of its event streams.
|
|
241
|
+
* `subscribe` never crosses the frame/host channel — the AppletHost turns it into `rows:<alias>`.
|
|
242
|
+
*/
|
|
243
|
+
interface HostDataPort extends DataPort {
|
|
244
|
+
subscribe(event: DataPortEventName, listener: (event: RowsEvent) => void): () => void;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** A host hook that owns its own port (no provider above it) passes it explicitly. */
|
|
248
|
+
interface DataScope {
|
|
249
|
+
port: DataPort;
|
|
250
|
+
portKey: string;
|
|
251
|
+
events?: DataEvents;
|
|
252
|
+
}
|
|
253
|
+
interface DataRead<T> {
|
|
254
|
+
data: T | undefined;
|
|
255
|
+
loading: boolean;
|
|
256
|
+
/** The store's own sentence when the read was refused. */
|
|
257
|
+
error: RecordsError | null;
|
|
258
|
+
refresh: () => void;
|
|
259
|
+
}
|
|
260
|
+
interface DataRows {
|
|
261
|
+
rows: Row[];
|
|
262
|
+
total: number | null;
|
|
263
|
+
loading: boolean;
|
|
264
|
+
/** A next page is being read. */
|
|
265
|
+
loadingMore: boolean;
|
|
266
|
+
hasMore: boolean;
|
|
267
|
+
error: RecordsError | null;
|
|
268
|
+
/** Read the page after the last row (keyset): never skips or repeats a row, whatever is written meanwhile. */
|
|
269
|
+
loadMore: () => void;
|
|
270
|
+
/** Rows written by others that belong somewhere in this (deep, filtered or sorted) list; `refresh()` reads them in. */
|
|
271
|
+
newer: number;
|
|
272
|
+
refresh: () => void;
|
|
273
|
+
}
|
|
274
|
+
interface DataMutations {
|
|
275
|
+
/** Drawn at once (marked `_pending`), replaced by the stored row; removed with the store's sentence on refusal. */
|
|
276
|
+
create(values: Values): Promise<RecordsResult<Row>>;
|
|
277
|
+
/** Sends the drawn row's `_version` as baseVersion (or `options.baseVersion`). A stale write is refused and the row re-read. */
|
|
278
|
+
update(id: Uuid, patch: Values, options?: {
|
|
279
|
+
baseVersion?: number;
|
|
280
|
+
}): Promise<RecordsResult<Row>>;
|
|
281
|
+
archive(id: Uuid): Promise<RecordsResult<void>>;
|
|
282
|
+
restore(id: Uuid): Promise<RecordsResult<void>>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** One row exactly as `platform.entity_list_scoped` / `entity_get` answer it. */
|
|
286
|
+
interface EntityWireRow {
|
|
287
|
+
id: Uuid;
|
|
288
|
+
version: number;
|
|
289
|
+
organization_id: Uuid;
|
|
290
|
+
/** The row's own organization's custom fields, already masked by the store (a hidden field is null). */
|
|
291
|
+
_custom?: Record<string, unknown>;
|
|
292
|
+
/** The token's own columns; link columns are `EntityWireRef[]`. */
|
|
293
|
+
[column: string]: unknown;
|
|
294
|
+
}
|
|
295
|
+
interface EntitySortApplied {
|
|
296
|
+
column: string;
|
|
297
|
+
dir: "asc" | "desc";
|
|
298
|
+
nulls: "first" | "last";
|
|
299
|
+
}
|
|
300
|
+
interface EntityListAnswer {
|
|
301
|
+
rows: EntityWireRow[];
|
|
302
|
+
/** null when not asked for. */
|
|
303
|
+
total: number | null;
|
|
304
|
+
/** The total stopped at the store's count cap; `total` is then a floor, not a count. */
|
|
305
|
+
total_is_capped: boolean;
|
|
306
|
+
page_size: number;
|
|
307
|
+
/** The keyset cursor of the next page; opaque. */
|
|
308
|
+
next_after: string | null;
|
|
309
|
+
has_more: boolean;
|
|
310
|
+
sort_applied: EntitySortApplied[];
|
|
311
|
+
/** Sort columns the door could not use (it keeps one key today). Said back, never hidden. */
|
|
312
|
+
sort_ignored: string[];
|
|
313
|
+
feed_cursor: null;
|
|
314
|
+
}
|
|
315
|
+
interface EntityGetAnswer {
|
|
316
|
+
rows: EntityWireRow[];
|
|
317
|
+
/** Ids that are not there or that this person cannot open. */
|
|
318
|
+
missing: Uuid[];
|
|
319
|
+
feed_cursor: null;
|
|
320
|
+
}
|
|
321
|
+
interface EntityColumnWire {
|
|
322
|
+
key: string;
|
|
323
|
+
label: string;
|
|
324
|
+
type: string;
|
|
325
|
+
origin: "platform" | "custom";
|
|
326
|
+
required: boolean;
|
|
327
|
+
readOnly: boolean;
|
|
328
|
+
linksTo?: string[];
|
|
329
|
+
choices?: string[];
|
|
330
|
+
/** Custom columns only. */
|
|
331
|
+
field_id?: Uuid;
|
|
332
|
+
organization?: string;
|
|
333
|
+
organization_id?: Uuid;
|
|
334
|
+
}
|
|
335
|
+
interface EntityColumnsAnswer {
|
|
336
|
+
label: string;
|
|
337
|
+
token: string;
|
|
338
|
+
columns: EntityColumnWire[];
|
|
339
|
+
can_create: boolean;
|
|
340
|
+
can_update: boolean;
|
|
341
|
+
/** The type's landing lane (`platform.entity_default_list_scope`). */
|
|
342
|
+
default_scope: Lane;
|
|
343
|
+
}
|
|
344
|
+
/** One filter predicate as the door reads it (CONTRACT §3.2). */
|
|
345
|
+
interface EntityWirePredicate {
|
|
346
|
+
column: string;
|
|
347
|
+
op: string;
|
|
348
|
+
value?: unknown;
|
|
349
|
+
values?: unknown[];
|
|
350
|
+
negated?: boolean;
|
|
351
|
+
}
|
|
352
|
+
interface EntityWireSort {
|
|
353
|
+
column: string;
|
|
354
|
+
dir: "asc" | "desc";
|
|
355
|
+
nulls: "last";
|
|
356
|
+
}
|
|
357
|
+
interface EntityWireQuery {
|
|
358
|
+
/** Absent = the type's own landing lane. `organization_id` narrows any lane and never locks it. */
|
|
359
|
+
scope?: {
|
|
360
|
+
kind?: Lane;
|
|
361
|
+
organization_id?: Uuid;
|
|
362
|
+
};
|
|
363
|
+
filter?: {
|
|
364
|
+
all: EntityWirePredicate[];
|
|
365
|
+
};
|
|
366
|
+
search?: string;
|
|
367
|
+
sort?: EntityWireSort[];
|
|
368
|
+
pageSize?: number;
|
|
369
|
+
}
|
|
370
|
+
/** What every write door answers (`entity_insert` / `entity_update` / `entity_archive` / `entity_unarchive`). */
|
|
371
|
+
interface EntityWriteAnswer {
|
|
372
|
+
status: string;
|
|
373
|
+
/** The rows as the read door now reads them, each with its NEW `version`. */
|
|
374
|
+
rows: EntityWireRow[];
|
|
375
|
+
validation: unknown[];
|
|
376
|
+
feed_cursor: null;
|
|
377
|
+
/** Archive and restore say what they did, in the store's words (trash words on these types). */
|
|
378
|
+
message?: string;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** What a write takes beyond its arguments: the op id minted for it (`mintOpId`). */
|
|
382
|
+
interface EntityWriteOptions {
|
|
383
|
+
opId?: string;
|
|
384
|
+
}
|
|
385
|
+
interface EntityListRequest extends EntityWireQuery {
|
|
386
|
+
token: string;
|
|
387
|
+
/** Keyset cursor from the previous answer's `next_after`. */
|
|
388
|
+
after?: string | null;
|
|
389
|
+
/** Ask the door to skip the count. Default: counted. */
|
|
390
|
+
withTotal?: boolean;
|
|
391
|
+
archived?: "active" | "archived" | "all";
|
|
392
|
+
}
|
|
393
|
+
interface EntityClient {
|
|
394
|
+
list(request: EntityListRequest): Promise<RecordsResult<EntityListAnswer>>;
|
|
395
|
+
get(request: {
|
|
396
|
+
token: string;
|
|
397
|
+
ids: readonly Uuid[];
|
|
398
|
+
}): Promise<RecordsResult<EntityGetAnswer>>;
|
|
399
|
+
/** `organizationId` null reads the custom fields of every organization the person belongs to. */
|
|
400
|
+
columns(request: {
|
|
401
|
+
token: string;
|
|
402
|
+
organizationId?: Uuid | null;
|
|
403
|
+
}): Promise<RecordsResult<EntityColumnsAnswer>>;
|
|
404
|
+
/** One `entity_data` knob as `platform.knob_resolve` answers it for an organization (live updates read three). */
|
|
405
|
+
knob(request: {
|
|
406
|
+
key: string;
|
|
407
|
+
organizationId?: Uuid | null;
|
|
408
|
+
}): Promise<RecordsResult<unknown>>;
|
|
409
|
+
/** `platform.entity_insert`: rows into the organization the host names (never guessed). */
|
|
410
|
+
insert(request: {
|
|
411
|
+
token: string;
|
|
412
|
+
organizationId: Uuid;
|
|
413
|
+
rows: readonly Record<string, unknown>[];
|
|
414
|
+
} & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;
|
|
415
|
+
/** `platform.entity_update`: one row, refused as stale (`MX001`) unless `baseVersion` is still current. */
|
|
416
|
+
update(request: {
|
|
417
|
+
token: string;
|
|
418
|
+
id: Uuid;
|
|
419
|
+
baseVersion: number;
|
|
420
|
+
patch: Record<string, unknown>;
|
|
421
|
+
} & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;
|
|
422
|
+
/** `platform.entity_archive` (on these types, the trash). */
|
|
423
|
+
archive(request: {
|
|
424
|
+
token: string;
|
|
425
|
+
ids: readonly Uuid[];
|
|
426
|
+
} & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;
|
|
427
|
+
/** `platform.entity_unarchive`. */
|
|
428
|
+
unarchive(request: {
|
|
429
|
+
token: string;
|
|
430
|
+
ids: readonly Uuid[];
|
|
431
|
+
} & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;
|
|
432
|
+
/** The organizations the person belongs to, as row security counts them (`iam.my_orgs()`: archived ones closed). */
|
|
433
|
+
memberOrganizations(): Promise<RecordsResult<Uuid[]>>;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
interface EntityNoticeSink {
|
|
437
|
+
/** `ids` to re-read, or `null` to re-read everything this reader shows. */
|
|
438
|
+
rows(ids: string[] | null): void;
|
|
439
|
+
}
|
|
440
|
+
interface EntityRealtime {
|
|
441
|
+
/** Hear `token` across `organizationIds`; the sink gets one coalesced call per burst. Returns the stop. */
|
|
442
|
+
subscribe(target: {
|
|
443
|
+
token: string;
|
|
444
|
+
organizationIds: readonly string[];
|
|
445
|
+
}, sink: EntityNoticeSink): () => void;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** A type as the person may use it: its name and whether she may add or change rows (`entity_columns`' own answer). */
|
|
449
|
+
interface EntityTypeFacts {
|
|
450
|
+
token: string;
|
|
451
|
+
label: string;
|
|
452
|
+
canCreate: boolean;
|
|
453
|
+
canUpdate: boolean;
|
|
454
|
+
}
|
|
455
|
+
/** The host's entity port: the DataPort, plus `describe` for a token-first screen that has no `sources()` entry. */
|
|
456
|
+
interface EntityDataPort extends HostDataPort {
|
|
457
|
+
/** The type behind an alias, from the same one `entity_columns` read `columns()` uses. */
|
|
458
|
+
describe(alias: string): Promise<RecordsResult<EntityTypeFacts>>;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** The cache prefix of the one entity port: `["@ai-matrx/records","data","entity-data",<token>,…]`. */
|
|
462
|
+
declare const ENTITY_PORT_KEY = "entity-data";
|
|
463
|
+
interface EntityDataProviderProps {
|
|
464
|
+
/** The supabase-js client carrying the signed-in person's session (or a records data seam). */
|
|
465
|
+
dataSource?: unknown;
|
|
466
|
+
/** … or an entity client already built. Give one of the two. */
|
|
467
|
+
client?: EntityClient;
|
|
468
|
+
/**
|
|
469
|
+
* Where a NEW row is saved and which organization's topic is joined first: the host's active
|
|
470
|
+
* organization, never guessed. `null` while none is chosen — `create` is then refused
|
|
471
|
+
* `organization_required` and nothing is sent. Lists never narrow by it.
|
|
472
|
+
*/
|
|
473
|
+
activeOrganizationId: Uuid | null;
|
|
474
|
+
/**
|
|
475
|
+
* Live notices. Left out: the app's ambient realtime manager (`createEntityRealtime()`), never a socket
|
|
476
|
+
* of our own. `false`: not live (the console says so once).
|
|
477
|
+
*/
|
|
478
|
+
realtime?: EntityRealtime | false;
|
|
479
|
+
/** A second, separate cache (tests, two seats on one page). Default `entity-data`. */
|
|
480
|
+
portKey?: string;
|
|
481
|
+
children?: ReactNode;
|
|
482
|
+
}
|
|
483
|
+
/** The engine's scope over the one entity port. */
|
|
484
|
+
interface EntityScope extends DataScope {
|
|
485
|
+
port: EntityDataPort;
|
|
486
|
+
}
|
|
487
|
+
declare function EntityDataProvider({ dataSource, client, activeOrganizationId, realtime, portKey, children }: EntityDataProviderProps): react.FunctionComponentElement<react.ProviderProps<EntityScope | null>> | react.FunctionComponentElement<_tanstack_react_query.QueryClientProviderProps>;
|
|
488
|
+
/** The entity port's scope when an `EntityDataProvider` is mounted above, else null (a host component deciding whether to mount one). */
|
|
489
|
+
declare function useOptionalEntityScope(): EntityScope | null;
|
|
490
|
+
/** The entity port's scope, or a loud failure naming what to mount. */
|
|
491
|
+
declare function useEntityScope(): EntityScope;
|
|
492
|
+
/** One type's list: keyset pages, `loadMore`, `newer`, live. `query.org` is a filter, never a lock. */
|
|
493
|
+
declare function useEntityList(token: string, query?: Query): DataRows;
|
|
494
|
+
/** One type's columns: platform columns and the organizations' custom fields, with `origin`. */
|
|
495
|
+
declare function useEntityColumns(token: string): DataRead<Column[]>;
|
|
496
|
+
/**
|
|
497
|
+
* The type itself: its name and whether this person may add or change rows. Read from the same
|
|
498
|
+
* `entity_columns` answer the columns come from (one request per type), cached under the type's own
|
|
499
|
+
* key, so a shape notice refreshes it with the columns.
|
|
500
|
+
*/
|
|
501
|
+
declare function useEntityType(token: string): DataRead<EntityTypeFacts>;
|
|
502
|
+
/** One row by id (drawn at once from a loaded page, confirmed by the read). */
|
|
503
|
+
declare function useEntityRow(token: string, id: Uuid | null): DataRead<Row | null>;
|
|
504
|
+
/**
|
|
505
|
+
* Writes for one type, optimistic with rollback: `create` (into the active organization), `update` (with
|
|
506
|
+
* the `_version` the person saw), `archive` / `restore`. Each answers `RecordsResult`; a refusal carries
|
|
507
|
+
* the store's sentence verbatim.
|
|
508
|
+
*/
|
|
509
|
+
declare function useEntityWrite(token: string): DataMutations;
|
|
510
|
+
|
|
511
|
+
export { type DataMutations, type DataRead, type DataRows, ENTITY_PORT_KEY, EntityDataProvider, type EntityDataProviderProps, type EntityScope, type EntityTypeFacts, useEntityColumns, useEntityList, useEntityRow, useEntityScope, useEntityType, useEntityWrite, useOptionalEntityScope };
|