@ai-matrx/records 0.99.32 → 0.99.33

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 (62) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/{client-CrwedBYU.d.ts → client-CSfkP7Xf.d.ts} +5 -74
  3. package/dist/{client-D1EwyWfh.d.cts → client-jKf1Z-kp.d.cts} +5 -74
  4. package/dist/core/index.cjs +149 -105
  5. package/dist/core/index.cjs.map +1 -1
  6. package/dist/core/index.d.cts +9 -331
  7. package/dist/core/index.d.ts +9 -331
  8. package/dist/core/index.js +149 -105
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/data/index.cjs +280 -236
  11. package/dist/data/index.cjs.map +1 -1
  12. package/dist/data/index.d.cts +3 -2
  13. package/dist/data/index.d.ts +3 -2
  14. package/dist/data/index.js +280 -236
  15. package/dist/data/index.js.map +1 -1
  16. package/dist/{declareTable-CC5rTTIA.d.ts → declareTable-0emxEewn.d.ts} +1 -1
  17. package/dist/{declareTable-BIIt0Wqt.d.cts → declareTable-800bwUn3.d.cts} +1 -1
  18. package/dist/door-call/index.cjs +1287 -0
  19. package/dist/door-call/index.cjs.map +1 -0
  20. package/dist/door-call/index.d.cts +20 -0
  21. package/dist/door-call/index.d.ts +20 -0
  22. package/dist/door-call/index.js +1264 -0
  23. package/dist/door-call/index.js.map +1 -0
  24. package/dist/{doors-D1yAs0a0.d.cts → doors-BgjRUdxw.d.cts} +13 -1
  25. package/dist/{doors-D2z3kH2i.d.ts → doors-D7_jEKtg.d.ts} +13 -1
  26. package/dist/index.d.cts +5 -4
  27. package/dist/index.d.ts +5 -4
  28. package/dist/{objectOrganization-DazBoDAq.d.cts → objectOrganization-B5YFGVUB.d.cts} +1 -1
  29. package/dist/{objectOrganization-D1ox3gUx.d.ts → objectOrganization-DozoVIxS.d.ts} +1 -1
  30. package/dist/pickListDoors-C-_eAEGL.d.cts +135 -0
  31. package/dist/pickListDoors-DgWVnNRI.d.ts +135 -0
  32. package/dist/questions/index.d.cts +3 -2
  33. package/dist/questions/index.d.ts +3 -2
  34. package/dist/react/index.cjs +363 -319
  35. package/dist/react/index.cjs.map +1 -1
  36. package/dist/react/index.d.cts +37 -22
  37. package/dist/react/index.d.ts +37 -22
  38. package/dist/react/index.js +363 -319
  39. package/dist/react/index.js.map +1 -1
  40. package/dist/scopes/index.cjs +7 -0
  41. package/dist/scopes/index.cjs.map +1 -1
  42. package/dist/scopes/index.d.cts +2 -2
  43. package/dist/scopes/index.d.ts +2 -2
  44. package/dist/scopes/index.js +7 -0
  45. package/dist/scopes/index.js.map +1 -1
  46. package/dist/setup/index.cjs +289 -245
  47. package/dist/setup/index.cjs.map +1 -1
  48. package/dist/setup/index.d.cts +4 -3
  49. package/dist/setup/index.d.ts +4 -3
  50. package/dist/setup/index.js +289 -245
  51. package/dist/setup/index.js.map +1 -1
  52. package/dist/table-logic-BPquabNN.d.ts +328 -0
  53. package/dist/table-logic-CTh6lwaq.d.cts +328 -0
  54. package/dist/typed-table/index.cjs +280 -236
  55. package/dist/typed-table/index.cjs.map +1 -1
  56. package/dist/typed-table/index.d.cts +4 -3
  57. package/dist/typed-table/index.d.ts +4 -3
  58. package/dist/typed-table/index.js +280 -236
  59. package/dist/typed-table/index.js.map +1 -1
  60. package/dist/views/index.d.cts +3 -2
  61. package/dist/views/index.d.ts +3 -2
  62. package/package.json +13 -3
@@ -0,0 +1,1264 @@
1
+ // src/core/supabase.ts
2
+ function refuse(code, message, hint) {
3
+ return { data: null, error: { code, message, hint, details: "" } };
4
+ }
5
+ function supabaseDataSource(client) {
6
+ if (isBound(client)) return client;
7
+ const supabase = client;
8
+ const hasSchema = typeof supabase.schema === "function";
9
+ const bound = {
10
+ // PAGE-BUNDLE: the real adapter opens a table page with one `custom.table_page_bundle` call.
11
+ opensInOneCall: true,
12
+ rpc(fn, args, options) {
13
+ if (!hasSchema) {
14
+ return Promise.resolve(
15
+ refuse(
16
+ "data_source_unbound",
17
+ `The records client was given something that is not a supabase client: it has no schema() method, so the door ${fn} cannot be called in the schema it lives in.`,
18
+ "Pass a supabase-js v2 client (createClient(...)) to supabaseDataSource()."
19
+ )
20
+ );
21
+ }
22
+ const name = options?.schema;
23
+ if (!name) {
24
+ return Promise.resolve(
25
+ refuse(
26
+ "schema_unsaid",
27
+ `The door ${fn} was called without saying which schema it lives in. Every door of this store is custom.${fn} and every trust door is iam.${fn}; an unqualified call reaches PostgREST's default profile (public) and is refused there.`,
28
+ "This is a bug in the caller, not in your data: /core says the schema on every call."
29
+ )
30
+ );
31
+ }
32
+ return supabase.schema(name).rpc(fn, args);
33
+ },
34
+ schema(name) {
35
+ return supabase.schema(name);
36
+ }
37
+ };
38
+ Object.defineProperty(bound, BOUND, { value: true, enumerable: false });
39
+ return bound;
40
+ }
41
+ var BOUND = /* @__PURE__ */ Symbol.for("@ai-matrx/records/supabaseDataSource");
42
+ function isBound(source) {
43
+ return source[BOUND] === true;
44
+ }
45
+ function asRecordsDataSource(source) {
46
+ if (source === null || typeof source !== "object") return supabaseDataSource({});
47
+ if (isBound(source)) return source;
48
+ const candidate = source;
49
+ const looksLikeSupabaseJs = typeof candidate.schema === "function" && typeof candidate.from === "function" && candidate.auth !== null && typeof candidate.auth === "object";
50
+ if (looksLikeSupabaseJs || typeof candidate.rpc !== "function") return supabaseDataSource(source);
51
+ return source;
52
+ }
53
+
54
+ // src/results.ts
55
+ var ok = (data) => ({ ok: true, data });
56
+ var err = (error) => ({ ok: false, error });
57
+
58
+ // src/core/pgError.ts
59
+ var BY_SQLSTATE = {
60
+ PT409: "stale_write",
61
+ "42501": "door",
62
+ "23514": "refused_by_rule",
63
+ "22004": "invalid_argument",
64
+ "22023": "invalid_argument",
65
+ // MX005 — a value the store cannot take as asked: since 2026-10-06 (AP-3 U3) a word that names more than one
66
+ // choice or linked record raises it (it used to borrow PT409 and read as somebody else's write winning).
67
+ MX005: "invalid_argument",
68
+ // MXD01 — the held-change door tried the proposed values before filing an
69
+ // approval request and the store refused them. Nothing is waiting for a
70
+ // person to decide; correct the value before asking again, which is the same
71
+ // caller-side condition as the other invalid-argument refusals.
72
+ MXD01: "invalid_argument",
73
+ // 22P02 — a value does not fit the type the store holds for it. The class is
74
+ // real and was hit live: an organization whose copy of a package table grew
75
+ // `status` as a uuid-valued Field was sent the text "open", and the store
76
+ // answered `invalid input syntax for type uuid: "open"`. It is the CALL that
77
+ // is wrong for the shape the store holds, which is what `invalid_argument`
78
+ // already means — the store's own sentence names the value and the type.
79
+ "22P02": "invalid_argument",
80
+ // 42703 — a column that is not there. The store raises it from
81
+ // `custom.entity_table`, which is asked for a column by NAME, so the class is
82
+ // exactly "the call named something the shape does not have": the same thing
83
+ // `invalid_argument` already means, and the store's own sentence names the
84
+ // column. Added 2026-09-20 (lane FORMS) because the package's live-door suite
85
+ // caught it raised by a door this package did not know about — which is the
86
+ // suite working, and is why the map is a census rather than a guess.
87
+ "42703": "invalid_argument",
88
+ // 22007 — a date or a time the store could not read as one. It is raised by
89
+ // `custom.dashboard_window_sql`, which is handed a window a caller wrote
90
+ // ("last 30 days", a literal date), so the class is again "the call named
91
+ // something the shape does not accept" and the store's own sentence names the
92
+ // text it could not read. Added 2026-09-20 (lane SCREEN-BREAK) after the
93
+ // live-door census caught it — the same way 42703 arrived, which is the
94
+ // census working rather than a guess.
95
+ "22007": "invalid_argument",
96
+ // 22003 — a number the store could not hold at the size it was handed. It is
97
+ // raised by `custom.context_resolve`, which is given a window or a depth a
98
+ // caller wrote, so the class is the same one 22P02, 42703 and 22007 already
99
+ // are: the CALL named a value the shape does not accept, and the store's own
100
+ // sentence names the number and the range. Added 2026-09-20 (lane GRID-TWO)
101
+ // when the live-door census caught the release gate red on it — which is the
102
+ // census working, and the fourth time it has been the thing that found a new
103
+ // refusal class rather than a person guessing one.
104
+ "22003": "invalid_argument",
105
+ // 42883 — `custom._template_call` (the template family, 2026-10-03) was handed a step that
106
+ // names a door the store does not have, or an argument that door does not take. The TEMPLATE
107
+ // named something the shape does not have — the same class 42703 is — and the store's own
108
+ // sentence names the door or the argument. Caught by the live-door census on records 0.68.1's
109
+ // tag, the eighth time the census found a class before a person did. (A bare 42883 on a door
110
+ // this package itself asks for is still `door_absent`, mapped before this funnel in
111
+ // `owedDoor`.)
112
+ "42883": "invalid_argument",
113
+ "02000": "not_found",
114
+ // P0002 — `custom.inbox_snooze` / `custom.inbox_clear` (lane S5-PRIME-2, 2026-09-24): "There is
115
+ // nothing in your inbox with that id". An invented id, another person's item and another
116
+ // organization's all answer it alike, so it reads exactly as 02000 does: not found.
117
+ "P0002": "not_found",
118
+ "23503": "missing_reference",
119
+ "23505": "already_exists",
120
+ "0A000": "not_supported",
121
+ "53400": "store_limit",
122
+ "22012": "store_limit",
123
+ // 54001 — `custom.record_delete` stops a cascade that nests past sixty-four
124
+ // levels of containment, because that is a loop somebody built rather than a
125
+ // hierarchy, and saying so beats recursing until the server runs out of
126
+ // stack. It is a LIMIT of the store, and the store's own sentence names what
127
+ // to do about it, so it reads as one.
128
+ "54001": "store_limit",
129
+ // 54000 — the grid primitives' organization ceilings (GRID-PRIMITIVES, 2026-09-23):
130
+ // `custom.action_run` on a selection past its knob, `custom.table_decorate` past the
131
+ // rule count, `custom.table_from_example` past `custom/example_rows_max`,
132
+ // `custom.table_webhook_declare` past `custom/table_webhooks_max`, and
133
+ // `custom.hub_changed_by`. Each names its knob in the hint and writes nothing, so it
134
+ // reads exactly like 54001: a limit of the store, in the store's own sentence.
135
+ "54000": "store_limit",
136
+ // VIS: `custom.has_visibility_at` refuses to answer from a snapshot older
137
+ // than a write this reader has already seen, rather than returning something
138
+ // they just lost access to. It is retryable on a fresher connection, which is
139
+ // a different instruction from every other refusal here.
140
+ // 55000 — `custom.table_move` (SC-1', 2026-09-24) refuses a move the table's present state
141
+ // does not allow: a destination that cannot take it, a table something else holds, contents
142
+ // that point at themselves in a circle. The store's own sentence names the reason and nothing
143
+ // moves, so it reads as a refusal on the merits. Caught by the live-door census on records
144
+ // 0.58.3's tag, the seventh time the census found a class before a person did.
145
+ "55000": "refused_by_rule",
146
+ // 42P17 — a worked-out column that would read itself round a circle (STORE-TAILS-3,
147
+ // 2026-09-25): `custom._field_reads_what_it_reads` refuses the declaration or edit that would
148
+ // close the circle, naming every column in it, and `custom.far_value` refuses a circle stored
149
+ // before that rule when a lookup or rollup is worked out. Refused on its merits, in the store's
150
+ // own sentence, nothing written — so it reads as 55000 does.
151
+ "42P17": "refused_by_rule",
152
+ // P0001 — `custom.context_scope_write` (2026-10-03) refuses a scope whose parent breaks the
153
+ // parent rule in the old trigger's own class and words (manager ruling H6). Refused on its
154
+ // merits, nothing written. Caught by the live-door census on records 0.65.0's tag.
155
+ P0001: "refused_by_rule",
156
+ "40001": "stale_read",
157
+ // 57014 — the store cancelled the statement at its own timeout. Like
158
+ // `stale_read` and unlike every other refusal here, the call was never
159
+ // refused on its merits: the same call on a smaller page, or once the
160
+ // organization's kernel is not being rewritten, can succeed. It gets its own
161
+ // code so a screen can offer "try again" honestly instead of showing a
162
+ // failure sentence the caller cannot act on.
163
+ "57014": "timed_out",
164
+ // PostgREST's own two: the store is not being SERVED, which is a different
165
+ // thing from the store refusing. `custom` absent from `pgrst.db_schemas`, or
166
+ // the grants not copied, land here.
167
+ PGRST202: "store_not_served",
168
+ PGRST205: "store_not_served",
169
+ PGRST301: "door",
170
+ // PT428 — NOT A FAILURE, and the release gate's live census is what found it
171
+ // missing: the store raises it and this package answered `internal`, which is
172
+ // the one classification that tells a screen nothing. A stage gate whose
173
+ // `on_fail` is `require_approval` files the request and leaves the record
174
+ // where it was; the transaction is rolled back to the pipeline's own block, so
175
+ // "nothing was written" is a fact rather than a promise. Landed by lane
176
+ // STAGE-RULES; mapped here by STAGE-RULES-2 the day after, when the census
177
+ // caught it — the sixth time that census has been the thing that found a
178
+ // refusal class rather than a person guessing one.
179
+ PT428: "needs_approval"
180
+ };
181
+ var MAPPED_SQLSTATES = Object.keys(BY_SQLSTATE);
182
+ function parseDetail(details) {
183
+ if (!details) return void 0;
184
+ const trimmed = details.trim();
185
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return details;
186
+ try {
187
+ return JSON.parse(trimmed);
188
+ } catch {
189
+ return details;
190
+ }
191
+ }
192
+ function isNotASqlstate(code) {
193
+ if (!code) return true;
194
+ return /^[0-9]{3}$/.test(code);
195
+ }
196
+ function mapPgError(error, where) {
197
+ const raw = error.code ?? void 0;
198
+ const sqlstate = raw && raw !== "" ? raw : void 0;
199
+ const mapped = {
200
+ code: "internal",
201
+ message: error.message,
202
+ ...error.hint ? { hint: error.hint } : {},
203
+ ...sqlstate ? { sqlstate } : {}
204
+ };
205
+ if (isNotASqlstate(sqlstate)) {
206
+ mapped.code = "unreachable";
207
+ } else {
208
+ mapped.code = BY_SQLSTATE[sqlstate] ?? "internal";
209
+ }
210
+ const detail = parseDetail(error.details);
211
+ if (detail !== void 0) mapped.detail = detail;
212
+ if (mapped.code === "internal") {
213
+ mapped.unmapped = true;
214
+ mapped.diagnostic = `@ai-matrx/records did not recognise SQLSTATE ${sqlstate ?? "(none)"} from ${where}; the store's own message is the thing to act on.`;
215
+ } else if (mapped.code === "unreachable") {
216
+ mapped.unmapped = true;
217
+ mapped.diagnostic = `@ai-matrx/records never reached the store from ${where}: ${raw ? `the transport answered ${raw}` : "no response"} \u2014 ${error.message}`;
218
+ mapped.message = "We could not reach your data.";
219
+ delete mapped.hint;
220
+ }
221
+ return mapped;
222
+ }
223
+
224
+ // src/core/primedBundle.ts
225
+ var REGISTRY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/primed-table-page-bundles");
226
+ function registry() {
227
+ const g = globalThis;
228
+ return g[REGISTRY] ??= /* @__PURE__ */ new Map();
229
+ }
230
+ function forgetPrimedTablePageBundlesOf(tableId) {
231
+ const primed = registry();
232
+ for (const key of [...primed.keys()]) if (tableId === null || key.split("|")[1] === tableId) primed.delete(key);
233
+ }
234
+ var DOOR_REGISTRY = /* @__PURE__ */ Symbol.for("@ai-matrx/records/primed-door-answers");
235
+ function doorRegistry() {
236
+ const g = globalThis;
237
+ return g[DOOR_REGISTRY] ??= /* @__PURE__ */ new Map();
238
+ }
239
+ var PRIMED_DOOR_TTL_MS = 1e4;
240
+ function takePrimedDoorAnswer(key) {
241
+ const registry2 = doorRegistry();
242
+ const held = registry2.get(key);
243
+ if (!held) return null;
244
+ if (Date.now() - held.at < PRIMED_DOOR_TTL_MS) return { data: held.data };
245
+ registry2.delete(key);
246
+ return null;
247
+ }
248
+ function dropPrimedDoorAnswers(doors) {
249
+ const registry2 = doorRegistry();
250
+ if (!doors) {
251
+ registry2.clear();
252
+ return;
253
+ }
254
+ for (const key of [...registry2.keys()]) if (doors.some((door) => key.startsWith(`${door}|`))) registry2.delete(key);
255
+ }
256
+ var LOOK_WRITES = {
257
+ view_declare: ["views", "view_look_read", "grid_layout", "table_page_bundle"],
258
+ view_designate: ["views", "view_look_read", "grid_layout", "table_page_bundle"],
259
+ view_look_set: ["views", "view_look_read", "grid_layout", "table_page_bundle"],
260
+ view_record_order_set: ["views", "view_look_read", "grid_layout", "table_page_bundle"]
261
+ };
262
+ function isWritingDoor(door) {
263
+ return /(_declare|_set(_|$)|_update|_write|_delete|_restore|_archive|_upsert|_reparent|_change|_duplicate|_move|_decide|_run$|_submit|_publish|_import_|_start$|_complete|_sign$|_add$|_retire|_propose|_promote|_land$|_pin$|_mute|_snooze|_unsnooze|_clear|_unclear|_revoke|_issue|_bind|_unbind|_apply|_backfill|_sync$|_decorate|_designate|_ensure|^migrate_|^set_table_|^home_add|^capture_|^claim_|^record_stage|^anon_|^io_import_|^io_comment_resolve|^enrich_(declare|land|pin)|^field_(declare|duplicate|propose|restore|retire|update)|^table_(declare|ensure|from_example|move|sync)|^value_restore|^relation_own)/.test(door);
264
+ }
265
+
266
+ // src/core/doorCall.ts
267
+ var DEFAULT_DOOR_SCHEMA = "custom";
268
+ function canonicalArgs(value) {
269
+ return JSON.stringify(
270
+ value,
271
+ (_key, held) => held && typeof held === "object" && !Array.isArray(held) ? Object.fromEntries(Object.keys(held).sort().map((k) => [k, held[k]])) : held
272
+ );
273
+ }
274
+ var A_PERSONS_REFUSAL = /* @__PURE__ */ new Set([
275
+ "refused_by_rule",
276
+ "stale_write",
277
+ "invalid_argument",
278
+ "already_exists",
279
+ "missing_reference"
280
+ ]);
281
+ function isAPersonsRefusal(code) {
282
+ return A_PERSONS_REFUSAL.has(code);
283
+ }
284
+ function recordsErrorSink(onError) {
285
+ return onError ?? ((e) => {
286
+ const line = `[@ai-matrx/records] ${e.where}: ${e.code} \u2014 ${e.message}${e.hint ? ` (${e.hint})` : ""}`;
287
+ if (isAPersonsRefusal(e.code)) console.info(line);
288
+ else console.error(line);
289
+ });
290
+ }
291
+ function doorAbsentRefusal(name, owedBy) {
292
+ return {
293
+ code: "door_absent",
294
+ message: `The record store has no door \`custom.${name}\` yet, so this call was not made. Nothing was read and nothing was written.`,
295
+ hint: owedBy ? `That door is ${owedBy}. Until it lands, this call refuses rather than guessing: an empty answer here would read like "there is nothing", which is a different sentence.` : `No lane in the build book claims this door. Either the name is wrong, or the door was removed from the store and this package has not been regenerated \u2014 run \`pnpm records:generate\`.`
296
+ };
297
+ }
298
+ function primedDoorAnswer(door, args) {
299
+ const look = LOOK_WRITES[door];
300
+ if (look) {
301
+ dropPrimedDoorAnswers(look);
302
+ return null;
303
+ }
304
+ if (isWritingDoor(door)) {
305
+ dropPrimedDoorAnswers();
306
+ return null;
307
+ }
308
+ return takePrimedDoorAnswer(`${door}|${canonicalArgs(args)}`);
309
+ }
310
+ function createDoorCall(options) {
311
+ const { dataSource, doorReady, held, wrote } = options;
312
+ const schema = options.schema ?? DEFAULT_DOOR_SCHEMA;
313
+ const scream = recordsErrorSink(options.onError);
314
+ const absent = options.absent ?? ((door) => doorAbsentRefusal(door));
315
+ const readsInFlight = /* @__PURE__ */ new Map();
316
+ const say = (refusal, where) => scream({ code: refusal.code, message: refusal.message, ...refusal.hint ? { hint: refusal.hint } : {}, where });
317
+ return async function callDoor(door, args, where, opts) {
318
+ const primed = primedDoorAnswer(door, args);
319
+ if (primed) return ok(primed.data);
320
+ if (held) {
321
+ const bundled = await held(door, args);
322
+ if (bundled) return ok(bundled.data);
323
+ }
324
+ if (doorReady && !doorReady(door)) {
325
+ const refusal = absent(door);
326
+ say(refusal, where);
327
+ return err(refusal);
328
+ }
329
+ const flightKey = isWritingDoor(door) ? null : `${door}|${canonicalArgs(args)}`;
330
+ const flying = flightKey ? readsInFlight.get(flightKey) : void 0;
331
+ const response = await (flying ?? (() => {
332
+ const call = Promise.resolve(dataSource.rpc(`${door}`, args, { schema }));
333
+ if (flightKey) {
334
+ readsInFlight.set(flightKey, call);
335
+ const forget = () => {
336
+ if (readsInFlight.get(flightKey) === call) readsInFlight.delete(flightKey);
337
+ };
338
+ call.then(forget, forget);
339
+ }
340
+ return call;
341
+ })());
342
+ if (response.error) {
343
+ const code = response.error.code;
344
+ const refusal = !doorReady && (code === "PGRST202" || code === "42883") ? absent(door) : mapPgError(response.error, where);
345
+ if (opts?.quiet) console.info(`[@ai-matrx/records] ${where}: ${refusal.code} - ${refusal.message}`);
346
+ else say(refusal, where);
347
+ return err(refusal);
348
+ }
349
+ if (isWritingDoor(door) || LOOK_WRITES[door]) {
350
+ const tableId = args["p_table_id"];
351
+ wrote?.(door, args);
352
+ forgetPrimedTablePageBundlesOf(typeof tableId === "string" ? tableId : null);
353
+ dropPrimedDoorAnswers(LOOK_WRITES[door]);
354
+ }
355
+ return ok(response.data);
356
+ };
357
+ }
358
+
359
+ // src/core/pickListDoors.ts
360
+ var PICK_LIST_DOORS = {
361
+ pickListGet: "pick_list_get",
362
+ pickListForSelection: "pick_list_for_selection",
363
+ pickListUpdate: "pick_list_update"
364
+ };
365
+ function createPickListDoors(callDoor) {
366
+ return {
367
+ async pickListGet({ list_id }) {
368
+ return callDoor(PICK_LIST_DOORS.pickListGet, { p_list_id: list_id }, "pickListGet");
369
+ },
370
+ async pickListForSelection({ list_id }) {
371
+ return callDoor(
372
+ PICK_LIST_DOORS.pickListForSelection,
373
+ { p_list_id: list_id },
374
+ "pickListForSelection"
375
+ );
376
+ },
377
+ async pickListUpdate({ list_id, list_name, description, items }) {
378
+ return callDoor(
379
+ PICK_LIST_DOORS.pickListUpdate,
380
+ {
381
+ p_list_id: list_id,
382
+ p_list_name: list_name ?? null,
383
+ p_description: description ?? null,
384
+ p_items: items ? [...items] : null
385
+ },
386
+ "pickListUpdate"
387
+ );
388
+ }
389
+ };
390
+ }
391
+
392
+ // src/scopes/types.ts
393
+ var SYSTEM_CONTEXT_TYPE_ID = "__system__";
394
+
395
+ // src/scopes/codec.ts
396
+ import { isUuidShape } from "@ai-matrx/kit/uuid";
397
+ var str = (v, fallback = "") => typeof v === "string" ? v : fallback;
398
+ var strOrNull = (v) => typeof v === "string" ? v : null;
399
+ var num = (v, fallback = 0) => {
400
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
401
+ return Number.isFinite(n) ? n : fallback;
402
+ };
403
+ function scopeTypeFromStore(row) {
404
+ const singular = row.label_singular ?? row.name ?? "";
405
+ return {
406
+ id: row.id,
407
+ organization_id: row.organization_id,
408
+ label_singular: singular,
409
+ label_plural: row.label_plural ?? singular,
410
+ icon: row.icon ?? "folder",
411
+ color: row.color ?? "",
412
+ slug: row.slug ?? null,
413
+ description: row.description ?? "",
414
+ sort_order: num(row.sort_order),
415
+ max_assignments_per_entity: row.max_assignments_per_entity ?? null,
416
+ default_variable_keys: Array.isArray(row.default_variable_keys) ? row.default_variable_keys : [],
417
+ created_by: row.created_by ?? null,
418
+ created_at: row.created_at ?? "",
419
+ updated_at: row.updated_at ?? ""
420
+ };
421
+ }
422
+ function scopeTypeLabelFromStore(t) {
423
+ const singular = t.label_singular ?? t.name ?? "";
424
+ return { id: t.id, slug: t.slug ?? null, label_singular: singular, label_plural: t.label_plural ?? singular, icon: t.icon ?? null, color: t.color ?? null };
425
+ }
426
+ function settingFromStore(value) {
427
+ if (!Array.isArray(value)) return value;
428
+ return value.map((el) => {
429
+ if (typeof el !== "string") return el;
430
+ const t = el.trim();
431
+ if (!(t.startsWith("{") && t.endsWith("}")) && !(t.startsWith("[") && t.endsWith("]"))) return el;
432
+ try {
433
+ return JSON.parse(t);
434
+ } catch {
435
+ return el;
436
+ }
437
+ });
438
+ }
439
+ function scopeFromStore(row) {
440
+ const settings = {};
441
+ for (const [k, v] of Object.entries(row.settings ?? {})) settings[k] = settingFromStore(v);
442
+ return {
443
+ id: row.id,
444
+ scope_type_id: row.scope_type_id,
445
+ organization_id: row.organization_id,
446
+ name: row.name ?? "",
447
+ description: row.description ?? "",
448
+ slug: row.slug ?? null,
449
+ sort_order: num(row.sort_order),
450
+ parent_scope_id: row.parent_scope_id ?? null,
451
+ settings,
452
+ created_by: row.created_by ?? null,
453
+ created_at: row.created_at ?? "",
454
+ updated_at: row.updated_at ?? ""
455
+ };
456
+ }
457
+ function scopeWithTypeFromStore(row) {
458
+ return { ...scopeFromStore(row), scope_type: row.scope_type ? scopeTypeLabelFromStore(row.scope_type) : null };
459
+ }
460
+ function archivedScopeTypeFromStore(row) {
461
+ const singular = row.label_singular ?? "";
462
+ return {
463
+ id: row.id,
464
+ organization_id: row.organization_id,
465
+ label_singular: singular,
466
+ label_plural: row.label_plural ?? singular,
467
+ icon: row.icon ?? "folder",
468
+ color: row.color ?? "",
469
+ archived_at: row.deleted_at ?? "",
470
+ created_by: row.created_by ?? null,
471
+ archived_scope_count: row.archived_scope_count ?? 0
472
+ };
473
+ }
474
+ var FILE_KERNEL_ID = "11111111-0000-4000-8000-000000000006";
475
+ var KINDS = /* @__PURE__ */ new Set([
476
+ "string",
477
+ "markdown",
478
+ "email",
479
+ "url",
480
+ "phone",
481
+ "color",
482
+ "number",
483
+ "percent",
484
+ "currency",
485
+ "boolean",
486
+ "date",
487
+ "datetime",
488
+ "time",
489
+ "object",
490
+ "array",
491
+ "reference",
492
+ "document"
493
+ ]);
494
+ function contextFieldKind(doc, carried) {
495
+ if (carried?.as_text && carried.value_type && KINDS.has(carried.value_type)) return carried.value_type;
496
+ const behavior = String(doc.type ?? "text");
497
+ const fmt = doc.format ? String(doc.format) : "";
498
+ const shown = doc.display_format && typeof doc.display_format === "object" ? String(doc.display_format.id ?? "") : "";
499
+ const kind = String(doc.config?.kind ?? "");
500
+ const multi = Boolean(doc.multi);
501
+ if (behavior === "boolean") return "boolean";
502
+ if (behavior === "relation") return doc.relation_target === FILE_KERNEL_ID && !multi ? "document" : "reference";
503
+ if (behavior === "range") {
504
+ if (kind === "date") return "date";
505
+ if (kind === "datetime") return "datetime";
506
+ if (fmt === "percent" || fmt === "currency") return fmt;
507
+ return "number";
508
+ }
509
+ if (behavior === "text") {
510
+ if (multi) return "array";
511
+ if (fmt === "json") return "object";
512
+ for (const word of [fmt, shown]) {
513
+ if (word === "time" || word === "email" || word === "url" || word === "phone" || word === "color" || word === "markdown") return word;
514
+ }
515
+ }
516
+ return "string";
517
+ }
518
+ var POLICIES = /* @__PURE__ */ new Set(["include", "on_request", "exclude"]);
519
+ var SENSITIVITIES = /* @__PURE__ */ new Set(["public", "internal", "confidential", "restricted"]);
520
+ function contextFieldFromStore(row) {
521
+ const d = row.data ?? {};
522
+ const key = str(d.key);
523
+ const policy = str(d.context_policy, "include");
524
+ const sensitivity = str(d.sensitivity, "internal");
525
+ return {
526
+ id: row.id,
527
+ scope_type_id: row.scope_type_id,
528
+ organization_id: row.organization_id,
529
+ key,
530
+ label: str(d.label, key),
531
+ description: str(d.description),
532
+ kind: contextFieldKind(d, row.carried),
533
+ type: str(d.type, "text"),
534
+ multi: Boolean(d.multi),
535
+ format: strOrNull(d.format),
536
+ config: d.config ?? {},
537
+ relation_target: strOrNull(d.relation_target),
538
+ // The store's sort counts the scope's own two columns first (custom._ctx_store_item: + 2).
539
+ sort: Math.max(0, num(d.sort) - 2),
540
+ context_policy: POLICIES.has(policy) ? policy : "include",
541
+ sensitivity: SENSITIVITIES.has(sensitivity) ? sensitivity : "internal",
542
+ source: str(d.source, "manual"),
543
+ status: str(d.status, "active"),
544
+ status_note: strOrNull(d.status_note),
545
+ category: strOrNull(d.category),
546
+ tags: Array.isArray(d.tags) ? d.tags : [],
547
+ max_items: typeof d.max_items === "number" ? d.max_items : 1,
548
+ custom_component: d.custom_component ?? null,
549
+ reference_source: d.reference_source ?? null,
550
+ allowed_scope_type_ids: Array.isArray(d.allowed_scope_type_ids) ? d.allowed_scope_type_ids : null,
551
+ allowed_reference_types: Array.isArray(d.allowed_reference_types) ? d.allowed_reference_types : null,
552
+ review_interval_days: typeof d.review_interval_days === "number" ? d.review_interval_days : null,
553
+ depends_on: Array.isArray(d.depends_on) ? d.depends_on : [],
554
+ version: num(row.version, 1),
555
+ created_by: row.created_by ?? null,
556
+ created_at: row.created_at ?? "",
557
+ updated_at: row.updated_at ?? ""
558
+ };
559
+ }
560
+ var POLICY_TO_DOOR = { include: "always", on_request: "on_demand", exclude: "never" };
561
+ var SENSITIVITY_TO_DOOR = {
562
+ public: "public",
563
+ internal: "internal",
564
+ confidential: "restricted",
565
+ restricted: "privileged"
566
+ };
567
+ var SENSITIVITY_FROM_DOOR = {
568
+ public: "public",
569
+ internal: "internal",
570
+ restricted: "confidential",
571
+ privileged: "restricted"
572
+ };
573
+ function systemContextFieldFromStore(row, index) {
574
+ const key = str(row.key);
575
+ const kind = str(row.value_type, "string");
576
+ return {
577
+ id: str(row.id),
578
+ scope_type_id: SYSTEM_CONTEXT_TYPE_ID,
579
+ organization_id: "",
580
+ key,
581
+ label: str(row.display_name, key),
582
+ description: str(row.description),
583
+ kind: KINDS.has(kind) ? kind : "string",
584
+ type: "system",
585
+ multi: kind === "array",
586
+ format: null,
587
+ config: {},
588
+ relation_target: null,
589
+ sort: typeof row.sort_order === "number" ? row.sort_order : index,
590
+ context_policy: "include",
591
+ // The system table keeps the old sensitivity words (`restricted`/`privileged`), like the write doors.
592
+ sensitivity: SENSITIVITY_FROM_DOOR[str(row.sensitivity)] ?? "public",
593
+ source: "system",
594
+ status: "active",
595
+ status_note: null,
596
+ category: null,
597
+ tags: [],
598
+ max_items: 0,
599
+ custom_component: null,
600
+ reference_source: null,
601
+ allowed_scope_type_ids: null,
602
+ allowed_reference_types: null,
603
+ review_interval_days: null,
604
+ depends_on: [],
605
+ version: 0,
606
+ created_by: null,
607
+ created_at: "",
608
+ updated_at: "",
609
+ item_class: strOrNull(row.item_class)
610
+ };
611
+ }
612
+ function referencesOf(row) {
613
+ const v = row.value;
614
+ const list = Array.isArray(v) ? v : v == null ? [] : [v];
615
+ const labels = row.labels ?? {};
616
+ const files = row.files ?? {};
617
+ const toFile = row.field?.relation_target === FILE_KERNEL_ID;
618
+ const out = [];
619
+ for (const el of list) {
620
+ let ref = null;
621
+ if (typeof el === "string" && isUuidShape(el)) ref = { id: el, type: toFile ? "file" : "scope" };
622
+ else if (el && typeof el === "object" && typeof el.id === "string") {
623
+ const token = String(el.token ?? "");
624
+ const own = el.label;
625
+ ref = { id: el.id, type: token === "dataset" ? "table" : token || (toFile ? "file" : "scope") };
626
+ if (typeof own === "string" && own) ref.label = own;
627
+ }
628
+ if (!ref) continue;
629
+ const label = labels[ref.id];
630
+ if (label) ref.label = label;
631
+ const fileId = files[ref.id];
632
+ if (fileId) ref.file_id = fileId;
633
+ out.push(ref);
634
+ }
635
+ return out;
636
+ }
637
+ function contextValueFromStore(row) {
638
+ const kind = contextFieldKind(row.field ?? {}, row.field?.carried);
639
+ const carriedText = Boolean(row.field?.carried?.as_text);
640
+ const isRef = !carriedText && (kind === "reference" || kind === "document");
641
+ return {
642
+ scope_id: row.scope_id,
643
+ field_id: row.context_item_id,
644
+ key: row.key,
645
+ kind,
646
+ value: row.value ?? null,
647
+ references: isRef ? referencesOf(row) : [],
648
+ version: num(row.version, 1),
649
+ set_at: row.set_at ?? null,
650
+ source_type: row.source_type ?? null,
651
+ authored_by: row.authored_by ?? null,
652
+ whole_value: row.whole_value ?? null,
653
+ incomplete: null
654
+ };
655
+ }
656
+ async function sha256Hex(text) {
657
+ const subtle = globalThis.crypto?.subtle;
658
+ if (!subtle) return null;
659
+ const digest = await subtle.digest("SHA-256", new TextEncoder().encode(text));
660
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
661
+ }
662
+ async function completeWholeValues(values, read) {
663
+ const texts = /* @__PURE__ */ new Map();
664
+ await Promise.all(
665
+ values.map(async (cell) => {
666
+ const w = cell.whole_value;
667
+ if (!w || w.in_value || typeof cell.value !== "string") return;
668
+ const head = cell.value;
669
+ const chars = typeof w.chars === "number" ? w.chars : null;
670
+ const incomplete = (fileId, why) => {
671
+ cell.incomplete = { head, chars, file_id: fileId, why };
672
+ };
673
+ if (!w.file_id) return incomplete(null, "The rest of this text is still being saved.");
674
+ if (!read) return incomplete(w.file_id, "This place cannot open the file that holds the whole text.");
675
+ if (!texts.has(w.file_id)) texts.set(w.file_id, read(w.file_id));
676
+ try {
677
+ const text = await texts.get(w.file_id);
678
+ if (chars !== null && [...text].length !== chars) {
679
+ return incomplete(w.file_id, `The file holds ${[...text].length} characters, not ${chars}.`);
680
+ }
681
+ if (w.sha256) {
682
+ const sha = await sha256Hex(text);
683
+ if (sha === null) return incomplete(w.file_id, "This browser cannot check the file.");
684
+ if (sha !== w.sha256) return incomplete(w.file_id, "The file's content is not the text the cell points at.");
685
+ }
686
+ cell.value = text;
687
+ } catch (thrown) {
688
+ incomplete(w.file_id, thrown instanceof Error ? thrown.message : String(thrown));
689
+ }
690
+ })
691
+ );
692
+ }
693
+ function definedOnly(fields) {
694
+ const out = {};
695
+ for (const [k, v] of Object.entries(fields)) if (v !== void 0) out[k] = v;
696
+ return out;
697
+ }
698
+ function fieldSpecForDoor(spec) {
699
+ return definedOnly({
700
+ key: spec.key,
701
+ display_name: spec.label,
702
+ value_type: spec.kind,
703
+ description: spec.description,
704
+ category: spec.category,
705
+ fetch_hint: spec.context_policy ? POLICY_TO_DOOR[spec.context_policy] : void 0,
706
+ sensitivity: spec.sensitivity ? SENSITIVITY_TO_DOOR[spec.sensitivity] : void 0,
707
+ tags: spec.tags,
708
+ sort_order: spec.sort,
709
+ status: spec.status,
710
+ status_note: spec.status_note,
711
+ custom_component: spec.custom_component,
712
+ review_interval_days: spec.review_interval_days,
713
+ allowed_reference_types: spec.allowed_reference_types,
714
+ max_items: spec.max_items,
715
+ allowed_scope_type_ids: spec.allowed_scope_type_ids,
716
+ reference_source: spec.reference_source
717
+ });
718
+ }
719
+ function referenceFence(references) {
720
+ const kinds = [...new Set(references.map((r) => r.type))];
721
+ const type = kinds.length === 1 ? kinds[0] : "mixed";
722
+ const items = references.map((r) => {
723
+ if (r.type === "file" && r.file_id) return r.label ? { file_id: r.file_id, label: r.label } : { file_id: r.file_id };
724
+ const item = { id: r.id };
725
+ if (r.label) item.label = r.label;
726
+ if (kinds.length > 1) item.type = r.type;
727
+ return item;
728
+ });
729
+ return "```matrx\n" + JSON.stringify({ matrx_version: 1, kind: "reference", type, items }, null, 2) + "\n```";
730
+ }
731
+ function valuePayloadForDoor(write) {
732
+ const base = definedOnly({
733
+ scope_id: write.scope_id,
734
+ context_item_id: write.field_id,
735
+ source_type: write.source_type,
736
+ change_summary: write.change_summary
737
+ });
738
+ const v = write.value;
739
+ if (write.kind === "reference" || write.kind === "document") {
740
+ if (write.references) return { ...base, value_text: write.references.length ? referenceFence(write.references) : null };
741
+ if (write.kind === "document" && typeof v === "string") return { ...base, value_document_url: v || null };
742
+ return { ...base, value_text: typeof v === "string" ? v || null : null };
743
+ }
744
+ if (v === null || v === void 0 || v === "") {
745
+ return { ...base, value_text: null };
746
+ }
747
+ switch (write.kind) {
748
+ case "number":
749
+ case "percent":
750
+ return typeof v === "number" ? { ...base, value_number: v } : { ...base, value_text: String(v) };
751
+ case "currency":
752
+ return typeof v === "object" ? { ...base, value_json: v } : typeof v === "number" ? { ...base, value_number: v } : { ...base, value_text: String(v) };
753
+ case "boolean":
754
+ return typeof v === "boolean" ? { ...base, value_boolean: v } : { ...base, value_text: String(v) };
755
+ case "date":
756
+ return { ...base, value_date: String(v) };
757
+ case "datetime":
758
+ return { ...base, value_timestamp: String(v) };
759
+ case "time":
760
+ return { ...base, value_time: String(v) };
761
+ case "object":
762
+ case "array":
763
+ return typeof v === "object" ? { ...base, value_json: v } : { ...base, value_text: String(v) };
764
+ default:
765
+ return typeof v === "object" ? { ...base, value_json: v } : { ...base, value_text: String(v) };
766
+ }
767
+ }
768
+
769
+ // src/scopes/rules.ts
770
+ function toSlug(name) {
771
+ return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
772
+ }
773
+ function isValidSlug(s) {
774
+ return /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(s);
775
+ }
776
+ var RESERVED_SCOPE_SLUGS = /* @__PURE__ */ new Set(["context-items", "scopes", "edit", "manage", "new", "settings"]);
777
+ function isReservedSlug(s) {
778
+ return RESERVED_SCOPE_SLUGS.has(s.toLowerCase().replace(/_/g, "-"));
779
+ }
780
+ function resolveSlug(supplied, fallbackName) {
781
+ const slug = supplied?.trim() || toSlug(fallbackName);
782
+ if (!slug || !isValidSlug(slug)) {
783
+ return {
784
+ ok: false,
785
+ error: { code: "invalid_argument", message: "Use letters, numbers and single hyphens for the short name." }
786
+ };
787
+ }
788
+ if (isReservedSlug(slug)) {
789
+ return { ok: false, error: { code: "invalid_argument", message: `"${slug}" is a page name here. Pick another short name.` } };
790
+ }
791
+ return { ok: true, data: slug };
792
+ }
793
+
794
+ // src/scopes/doors.ts
795
+ var FIELD_KERNEL_ID = "11111111-0000-4000-8000-000000000002";
796
+ var SCOPES_PER_CALL = 500;
797
+ var SCOPE_TYPES_PER_FIELDS_CALL = 100;
798
+ var SCOPES_PER_VALUES_CALL = 200;
799
+ var TYPE_SCOPES_PAGE = 200;
800
+ var ARCHIVED_SCOPES_PAGE = 200;
801
+ var uniq = (ids) => [...new Set(ids.filter(Boolean))];
802
+ async function inBatches(ids, size, call) {
803
+ const unique = uniq(ids);
804
+ const batches = [];
805
+ for (let i = 0; i < unique.length; i += size) batches.push(unique.slice(i, i + size));
806
+ const answers = await Promise.all(batches.map(call));
807
+ const out = [];
808
+ for (const a of answers) {
809
+ if (!a.ok) return a;
810
+ out.push(...a.data ?? []);
811
+ }
812
+ return { ok: true, data: out };
813
+ }
814
+ function noRow(door) {
815
+ return { ok: false, error: { code: "internal", message: `${door} answered without the row it wrote.`, diagnostic: `@ai-matrx/records/scopes: ${door}` } };
816
+ }
817
+ var VALUE_DOOR_CODES = {
818
+ unauthorized: "door",
819
+ forbidden: "door",
820
+ forbidden_org: "door",
821
+ not_found: "not_found",
822
+ invalid_argument: "invalid_argument",
823
+ conflict: "stale_write",
824
+ unavailable: "not_supported"
825
+ };
826
+ function createScopeDoors(call) {
827
+ async function write(door, args) {
828
+ const res = await call(door, args, `scopes.${door}`);
829
+ if (!res.ok) return res;
830
+ const row = res.data?.row;
831
+ if (!row || typeof row !== "object") return noRow(door);
832
+ return { ok: true, data: row };
833
+ }
834
+ async function archiveOrRestore(door, args, id) {
835
+ const res = await call(door, args, `scopes.${door}`);
836
+ return res.ok ? { ok: true, data: { id } } : res;
837
+ }
838
+ async function fieldById(scopeTypeId, fieldId, door) {
839
+ const fields = await doors.fields([scopeTypeId]);
840
+ if (!fields.ok) return fields;
841
+ const field = fields.data.find((f) => f.id === fieldId);
842
+ return field ? { ok: true, data: field } : noRow(door);
843
+ }
844
+ const doors = {
845
+ async tree(organizationIds) {
846
+ const ids = uniq(organizationIds);
847
+ if (ids.length === 0) return { ok: true, data: [] };
848
+ const res = await call("context_tree", { p_organization_ids: ids }, "scopes.tree");
849
+ if (!res.ok) return res;
850
+ const byType = /* @__PURE__ */ new Map();
851
+ for (const s of res.data?.scopes ?? []) {
852
+ const scope = scopeFromStore(s);
853
+ const list = byType.get(scope.scope_type_id) ?? [];
854
+ list.push(scope);
855
+ byType.set(scope.scope_type_id, list);
856
+ }
857
+ return { ok: true, data: (res.data?.types ?? []).map((t) => ({ ...scopeTypeFromStore(t), scopes: byType.get(t.id) ?? [] })) };
858
+ },
859
+ async types(organizationIds, options) {
860
+ const withCounts = options?.withCounts ?? true;
861
+ const ids = uniq(organizationIds);
862
+ if (ids.length === 0) return { ok: true, data: { types: [], counts: withCounts ? {} : null } };
863
+ const res = await call("context_tree_types", { p_organization_ids: ids, p_with_counts: withCounts }, "scopes.types");
864
+ if (!res.ok) return res;
865
+ const rows = res.data?.types ?? [];
866
+ const counts = withCounts ? {} : null;
867
+ if (counts) for (const r of rows) counts[r.id] = Number(r.scope_count ?? 0);
868
+ return { ok: true, data: { types: rows.map(scopeTypeFromStore), counts } };
869
+ },
870
+ async typeScopesPage(scopeTypeId, offset = 0, limit = TYPE_SCOPES_PAGE) {
871
+ const res = await call(
872
+ "context_tree_type_scopes",
873
+ { p_scope_type_id: scopeTypeId, p_offset: offset, p_limit: limit },
874
+ "scopes.typeScopesPage"
875
+ );
876
+ if (!res.ok) return res;
877
+ return {
878
+ ok: true,
879
+ data: { scopes: (res.data?.scopes ?? []).map(scopeFromStore), total: Number(res.data?.total ?? 0), next_offset: res.data?.next_offset ?? null }
880
+ };
881
+ },
882
+ async search(organizationIds, query, limit = 100) {
883
+ const ids = uniq(organizationIds);
884
+ const q = query.trim();
885
+ if (ids.length === 0 || q === "") return { ok: true, data: { scopes: [], total: 0 } };
886
+ const res = await call(
887
+ "context_tree_search",
888
+ { p_organization_ids: ids, p_query: q, p_limit: limit },
889
+ "scopes.search"
890
+ );
891
+ if (!res.ok) return res;
892
+ return { ok: true, data: { scopes: (res.data?.scopes ?? []).map(scopeFromStore), total: Number(res.data?.total ?? 0) } };
893
+ },
894
+ async scopes(ids) {
895
+ const res = await inBatches(ids, SCOPES_PER_CALL, (batch) => call("context_scopes", { p_scope_ids: batch }, "scopes.scopes"));
896
+ return res.ok ? { ok: true, data: res.data.map(scopeWithTypeFromStore) } : res;
897
+ },
898
+ async fields(scopeTypeIds) {
899
+ const res = await inBatches(
900
+ scopeTypeIds,
901
+ SCOPE_TYPES_PER_FIELDS_CALL,
902
+ (batch) => call("context_items", { p_scope_type_ids: batch }, "scopes.fields")
903
+ );
904
+ return res.ok ? { ok: true, data: res.data.map(contextFieldFromStore) } : res;
905
+ },
906
+ async field(fieldId, organizationIds) {
907
+ const orgs = uniq(organizationIds);
908
+ const answers = await Promise.all(
909
+ orgs.map(
910
+ (org) => call(
911
+ "read_records_by_ids",
912
+ { p_organization_id: org, p_table_id: FIELD_KERNEL_ID, p_record_ids: [fieldId], p_by_id: false },
913
+ "scopes.field"
914
+ )
915
+ )
916
+ );
917
+ let refusal = null;
918
+ for (const answer of answers) {
919
+ if (!answer.ok) {
920
+ refusal ??= answer;
921
+ continue;
922
+ }
923
+ const typeId = answer.data?.find((r) => r.id === fieldId)?.document?.entity_definition_id;
924
+ if (typeof typeId !== "string") continue;
925
+ const fields = await doors.fields([typeId]);
926
+ if (!fields.ok) return fields;
927
+ const field = fields.data.find((f) => f.id === fieldId);
928
+ if (field) return { ok: true, data: field };
929
+ }
930
+ return refusal ?? {
931
+ ok: false,
932
+ error: { code: "not_found", message: "This context field could not be found.", diagnostic: `@ai-matrx/records/scopes: field ${fieldId}` }
933
+ };
934
+ },
935
+ async values(scopeIds, options) {
936
+ const res = await inBatches(
937
+ scopeIds,
938
+ SCOPES_PER_VALUES_CALL,
939
+ (batch) => call("context_values", { p_scope_ids: batch }, "scopes.values")
940
+ );
941
+ if (!res.ok) return res;
942
+ const cells = res.data.map(contextValueFromStore);
943
+ await completeWholeValues(cells, options?.readFileText ?? null);
944
+ return { ok: true, data: cells };
945
+ },
946
+ async archivedTypes(organizationId) {
947
+ const res = await call("context_archived_types", { p_organization_id: organizationId }, "scopes.archivedTypes");
948
+ return res.ok ? { ok: true, data: (res.data ?? []).map(archivedScopeTypeFromStore) } : res;
949
+ },
950
+ async archivedScopesOfType(organizationId, scopeTypeId, wanted) {
951
+ const out = [];
952
+ for (let offset = 0; out.length < wanted.size; offset += ARCHIVED_SCOPES_PAGE) {
953
+ const res = await call(
954
+ "read_records_archived",
955
+ { p_organization_id: organizationId, p_table_id: scopeTypeId, p_lane: "org", p_by_id: false, p_limit: ARCHIVED_SCOPES_PAGE, p_offset: offset },
956
+ "scopes.archivedScopesOfType"
957
+ );
958
+ if (!res.ok) return res;
959
+ const rows = res.data ?? [];
960
+ for (const r of rows) {
961
+ if (!wanted.has(r.id)) continue;
962
+ out.push({
963
+ id: r.id,
964
+ name: typeof r.document?.name === "string" ? r.document.name : null,
965
+ slug: typeof r.document?.slug === "string" ? r.document.slug : null,
966
+ archived_at: r.archived_at ?? null
967
+ });
968
+ }
969
+ if (rows.length < ARCHIVED_SCOPES_PAGE) break;
970
+ }
971
+ return { ok: true, data: out };
972
+ },
973
+ async systemItems() {
974
+ const res = await call("context_system_items", {}, "scopes.systemItems");
975
+ return res.ok ? { ok: true, data: (res.data ?? []).map(systemContextFieldFromStore) } : res;
976
+ },
977
+ async tableTemplates(organizationId) {
978
+ const res = await call("table_templates", { p_org_id: organizationId }, "scopes.tableTemplates");
979
+ return res.ok ? { ok: true, data: Array.isArray(res.data) ? res.data : [] } : res;
980
+ },
981
+ async templates() {
982
+ const res = await call("context_templates", {}, "scopes.templates");
983
+ return res.ok ? { ok: true, data: res.data ?? [] } : res;
984
+ },
985
+ async createType(organizationId, spec) {
986
+ const slug = resolveSlug(spec.slug, spec.label_plural || spec.label_singular || "");
987
+ if (!slug.ok) return slug;
988
+ const res = await write("context_type_write", {
989
+ p_organization_id: organizationId,
990
+ p_type_id: null,
991
+ p_spec: definedOnly({
992
+ label_singular: spec.label_singular,
993
+ label_plural: spec.label_plural,
994
+ icon: spec.icon ?? "folder",
995
+ description: spec.description ?? "",
996
+ sort_order: spec.sort_order ?? 0,
997
+ max_assignments: spec.max_assignments_per_entity ?? void 0,
998
+ default_variable_keys: spec.default_variable_keys ?? [],
999
+ color: spec.color,
1000
+ slug: slug.data
1001
+ })
1002
+ });
1003
+ return res.ok ? { ok: true, data: scopeTypeFromStore(res.data) } : res;
1004
+ },
1005
+ async updateType(typeId, spec) {
1006
+ let slug;
1007
+ if (spec.slug !== void 0) {
1008
+ const resolved = resolveSlug(spec.slug, spec.label_plural ?? spec.label_singular ?? "");
1009
+ if (!resolved.ok) return resolved;
1010
+ slug = resolved.data;
1011
+ }
1012
+ const res = await write("context_type_write", {
1013
+ p_organization_id: null,
1014
+ p_type_id: typeId,
1015
+ p_spec: definedOnly({
1016
+ label_singular: spec.label_singular,
1017
+ label_plural: spec.label_plural,
1018
+ icon: spec.icon,
1019
+ description: spec.description,
1020
+ sort_order: spec.sort_order,
1021
+ max_assignments: spec.max_assignments_per_entity,
1022
+ color: spec.color,
1023
+ slug
1024
+ })
1025
+ });
1026
+ return res.ok ? { ok: true, data: scopeTypeFromStore(res.data) } : res;
1027
+ },
1028
+ archiveType: (typeId) => archiveOrRestore("context_type_archive", { p_type_id: typeId }, typeId),
1029
+ restoreType: (typeId) => archiveOrRestore("context_type_restore", { p_type_id: typeId }, typeId),
1030
+ async createScope(organizationId, typeId, spec) {
1031
+ const slug = resolveSlug(spec.slug, spec.name ?? "");
1032
+ if (!slug.ok) return slug;
1033
+ const res = await write("context_scope_write", {
1034
+ p_organization_id: organizationId,
1035
+ p_scope_id: null,
1036
+ p_type_id: typeId,
1037
+ p_spec: definedOnly({
1038
+ name: spec.name,
1039
+ parent_scope_id: spec.parent_scope_id,
1040
+ description: spec.description ?? "",
1041
+ settings: spec.settings ?? {},
1042
+ slug: slug.data,
1043
+ sort_order: spec.sort_order
1044
+ })
1045
+ });
1046
+ return res.ok ? { ok: true, data: scopeFromStore(res.data) } : res;
1047
+ },
1048
+ async updateScope(scopeId, spec) {
1049
+ let slug;
1050
+ if (spec.slug !== void 0) {
1051
+ const resolved = resolveSlug(spec.slug, spec.name ?? "");
1052
+ if (!resolved.ok) return resolved;
1053
+ slug = resolved.data;
1054
+ }
1055
+ const res = await write("context_scope_write", {
1056
+ p_organization_id: null,
1057
+ p_scope_id: scopeId,
1058
+ p_type_id: null,
1059
+ p_spec: definedOnly({ name: spec.name, description: spec.description, settings: spec.settings, slug, sort_order: spec.sort_order })
1060
+ });
1061
+ return res.ok ? { ok: true, data: scopeFromStore(res.data) } : res;
1062
+ },
1063
+ archiveScope: (scopeId) => archiveOrRestore("context_scope_archive", { p_scope_id: scopeId }, scopeId),
1064
+ restoreScope: (scopeId) => archiveOrRestore("context_scope_restore", { p_scope_id: scopeId }, scopeId),
1065
+ async createField(scopeTypeId, spec) {
1066
+ const res = await write("context_item_write", {
1067
+ p_item_id: null,
1068
+ p_scope_type_id: scopeTypeId,
1069
+ p_spec: fieldSpecForDoor({ kind: "string", description: "", context_policy: "on_request", sensitivity: "internal", tags: [], ...spec })
1070
+ });
1071
+ if (!res.ok) return res;
1072
+ return fieldById(scopeTypeId, String(res.data.id), "context_item_write");
1073
+ },
1074
+ async updateField(fieldId, scopeTypeId, spec) {
1075
+ const res = await write("context_item_write", { p_item_id: fieldId, p_scope_type_id: null, p_spec: fieldSpecForDoor(spec) });
1076
+ if (!res.ok) return res;
1077
+ return fieldById(scopeTypeId, fieldId, "context_item_write");
1078
+ },
1079
+ archiveField: (fieldId) => archiveOrRestore("context_item_archive", { p_item_id: fieldId }, fieldId),
1080
+ restoreField: (fieldId) => archiveOrRestore("context_item_restore", { p_item_id: fieldId }, fieldId),
1081
+ async writeValue(cell) {
1082
+ const res = await call(
1083
+ "context_value_write",
1084
+ { p_payload: { source_type: "ai_enriched", ...valuePayloadForDoor(cell) } },
1085
+ "scopes.writeValue"
1086
+ );
1087
+ if (!res.ok) return res;
1088
+ const envelope = res.data ?? {};
1089
+ if (!envelope.ok) {
1090
+ const code = VALUE_DOOR_CODES[envelope.error?.code ?? ""] ?? "internal";
1091
+ return { ok: false, error: { code, message: envelope.error?.message ?? "The value was not saved." } };
1092
+ }
1093
+ return {
1094
+ ok: true,
1095
+ data: {
1096
+ scope_id: cell.scope_id,
1097
+ field_id: cell.field_id,
1098
+ version: Number(envelope.data?.version ?? 1),
1099
+ source_type: String(envelope.data?.source_type ?? cell.source_type ?? "ai_enriched")
1100
+ }
1101
+ };
1102
+ },
1103
+ tableProvision({ organization_id, item_id, scope_id, home_id }) {
1104
+ return call(
1105
+ "scope_table_provision",
1106
+ { p_organization_id: organization_id, p_item_id: item_id, p_scope_id: scope_id, p_home_id: home_id ?? null },
1107
+ "scopeTableProvision"
1108
+ );
1109
+ }
1110
+ };
1111
+ return doors;
1112
+ }
1113
+
1114
+ // src/core/storeDoors.ts
1115
+ var STORE_SCHEMA = "custom";
1116
+ function storeDoors(client) {
1117
+ const source = supabaseDataSource(client ?? {});
1118
+ const call = (fn, args = {}) => source.rpc(fn, args, { schema: STORE_SCHEMA });
1119
+ const typed = (fn, args) => call(fn, args);
1120
+ return {
1121
+ rpc: call,
1122
+ async whereIdOpens(id) {
1123
+ const raw = await call("where_id_opens", { p_id: id });
1124
+ const e = raw.error;
1125
+ return {
1126
+ data: raw.data ?? null,
1127
+ error: e ? { code: typeof e.code === "string" ? e.code : null, message: typeof e.message === "string" ? e.message : "The record store refused." } : null
1128
+ };
1129
+ },
1130
+ tableKernelId: () => typed("table_kernel_id", {}),
1131
+ readRecordsByIds: (organizationId, tableId, ids) => typed("read_records_by_ids", { p_organization_id: organizationId, p_table_id: tableId, p_record_ids: ids }),
1132
+ readRecordsPage: (args) => typed("read_records_page", args),
1133
+ recordTable: (organizationId, recordId) => typed("record_table", { p_organization_id: organizationId, p_record_id: recordId }),
1134
+ applicableFields: (organizationId, tableId) => typed("applicable_fields", { p_organization_id: organizationId, p_table_id: tableId }),
1135
+ fieldDeclare: (organizationId, tableId, spec) => typed("field_declare", { p_organization_id: organizationId, p_table_id: tableId, p_spec: spec }),
1136
+ recordUpdate: (organizationId, recordId, patch) => typed("record_update", { p_organization_id: organizationId, p_record_id: recordId, p_patch: patch }),
1137
+ entityReferenceWords: (organizationId, refs) => typed("entity_reference_words", { p_organization_id: organizationId, p_refs: refs.map((r) => ({ token: r.token, id: r.id })) }),
1138
+ templates: (filter) => typed("templates", { p_filter: filter }),
1139
+ templateFromTables: ({ organizationId, tableIds, includeRows, rowsPerTable }) => typed("template_from_tables", {
1140
+ p_organization_id: organizationId,
1141
+ p_table_ids: tableIds,
1142
+ p_include_rows: includeRows,
1143
+ p_rows_per_table: rowsPerTable
1144
+ }),
1145
+ templateDeclare: (scope, spec) => typed("template_declare", { p_scope: scope, p_spec: spec }),
1146
+ templateKeep: (templateId) => typed("template_keep", { p_template_id: templateId }),
1147
+ templateArchive: (templateId, restore) => typed("template_archive", restore === void 0 ? { p_template_id: templateId } : { p_template_id: templateId, p_restore: restore }),
1148
+ templateInstallClaim: ({ organizationId, installId, kind, label, sourceId, leaseSeconds }) => typed("template_install_claim", {
1149
+ p_organization_id: organizationId,
1150
+ p_install_id: installId,
1151
+ p_kind: kind,
1152
+ p_label: label,
1153
+ ...sourceId ? { p_source_id: sourceId } : {},
1154
+ p_lease_seconds: leaseSeconds
1155
+ }),
1156
+ templateInstallNote: ({ organizationId, installId, kind, id, label }) => typed("template_install_note", {
1157
+ p_organization_id: organizationId,
1158
+ p_install_id: installId,
1159
+ p_kind: kind,
1160
+ p_id: id,
1161
+ p_label: label
1162
+ })
1163
+ };
1164
+ }
1165
+
1166
+ // src/core/table-logic.ts
1167
+ function isBulkOpError(r) {
1168
+ return typeof r === "object" && r !== null && "error" in r;
1169
+ }
1170
+ function describeBulkFailures(failed) {
1171
+ const inTrash = failed.filter((f) => f.error === "row_in_trash").length;
1172
+ const missing = failed.length - inTrash;
1173
+ const parts = [];
1174
+ if (inTrash > 0) {
1175
+ parts.push(
1176
+ `${inTrash} row${inTrash === 1 ? " is" : "s are"} in Trash \u2014 restore ${inTrash === 1 ? "it" : "them"} from Trash to edit`
1177
+ );
1178
+ }
1179
+ if (missing > 0) {
1180
+ parts.push(
1181
+ `${missing} row${missing === 1 ? "" : "s"} could not be found \u2014 ${missing === 1 ? "it" : "they"} may have been removed by someone else`
1182
+ );
1183
+ }
1184
+ return `${parts.join("; ")}.`;
1185
+ }
1186
+ function isServiceFailure(r) {
1187
+ return r.success === false;
1188
+ }
1189
+
1190
+ // src/objectOrganization.ts
1191
+ var WHERE_ID_OPENS_DOOR = "where_id_opens";
1192
+ function doorIsAbsent(error) {
1193
+ if (error.code === "PGRST202" || error.code === "42883") return true;
1194
+ return /could not find the function/i.test(error.message ?? "");
1195
+ }
1196
+ 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.";
1197
+ async function resolveObjectOrganization(dataSource, id) {
1198
+ let answered;
1199
+ try {
1200
+ answered = await dataSource.rpc(WHERE_ID_OPENS_DOOR, { p_id: id }, { schema: "custom" });
1201
+ } catch (thrown) {
1202
+ return { state: "unavailable", why: thrown instanceof Error ? thrown.message : "The record store did not answer." };
1203
+ }
1204
+ return readObjectOrganizationAnswer(answered, id);
1205
+ }
1206
+ function readObjectOrganizationAnswer(answered, id) {
1207
+ if (answered.error) {
1208
+ if (doorIsAbsent(answered.error)) {
1209
+ console.warn(`[@ai-matrx/records] ${DOOR_ABSENT_WHY}`);
1210
+ return { state: "unavailable", why: DOOR_ABSENT_WHY };
1211
+ }
1212
+ return { state: "unavailable", why: answered.error.message ?? "The record store refused to say where this is." };
1213
+ }
1214
+ const row = answered.data;
1215
+ if (row === null || row === void 0) return { state: "not-given" };
1216
+ if (typeof row !== "object" || typeof row.organization_id !== "string" || typeof row.kind !== "string") {
1217
+ return { state: "unavailable", why: "The record store answered something this page cannot read." };
1218
+ }
1219
+ return {
1220
+ state: "found",
1221
+ organizationId: row.organization_id,
1222
+ kind: row.kind,
1223
+ path: typeof row.path === "string" ? row.path : null,
1224
+ live: row.live !== false,
1225
+ resolvedId: typeof row.resolved_id === "string" ? row.resolved_id : id
1226
+ };
1227
+ }
1228
+
1229
+ // src/door-call/index.ts
1230
+ function lightDoorCall(client, options = {}) {
1231
+ return createDoorCall({ ...options, dataSource: asRecordsDataSource(client) });
1232
+ }
1233
+ function lightScopeDoors(client, options = {}) {
1234
+ return createScopeDoors(lightDoorCall(client, options));
1235
+ }
1236
+ function lightPickListDoors(client, options = {}) {
1237
+ return createPickListDoors(lightDoorCall(client, options));
1238
+ }
1239
+ export {
1240
+ A_PERSONS_REFUSAL,
1241
+ DEFAULT_DOOR_SCHEMA,
1242
+ PICK_LIST_DOORS,
1243
+ STORE_SCHEMA,
1244
+ asRecordsDataSource,
1245
+ canonicalArgs,
1246
+ createDoorCall,
1247
+ createPickListDoors,
1248
+ createScopeDoors,
1249
+ describeBulkFailures,
1250
+ doorAbsentRefusal,
1251
+ isAPersonsRefusal,
1252
+ isBulkOpError,
1253
+ isServiceFailure,
1254
+ lightDoorCall,
1255
+ lightPickListDoors,
1256
+ lightScopeDoors,
1257
+ primedDoorAnswer,
1258
+ readObjectOrganizationAnswer,
1259
+ recordsErrorSink,
1260
+ resolveObjectOrganization,
1261
+ storeDoors,
1262
+ supabaseDataSource
1263
+ };
1264
+ //# sourceMappingURL=index.js.map