@ai-matrx/data 0.24.0 → 0.26.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 +22 -0
- package/bin/generate.mjs +1 -0
- package/dist/db.cjs +32 -2
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +29 -2
- package/dist/db.d.ts +29 -2
- package/dist/db.js +32 -2
- package/dist/db.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.0 — callAll: every row of a set-returning function
|
|
4
|
+
|
|
5
|
+
- **`callAll(db, fn, args, { order })`** — the complete read of a set-returning database function:
|
|
6
|
+
`rpc(…, { count: "exact" })` paged by `range` over the named `order` (`"id"` or
|
|
7
|
+
`{ column, ascending, nullsFirst }`; end on a key), past the 1,000-row cap; a `DatabaseError` keeps the
|
|
8
|
+
database's own error (code kept); a short read throws `IncompleteReadError`; an empty order is refused.
|
|
9
|
+
- `SupabaseLike.rpc` takes PostgREST's optional third argument (`{ count }`).
|
|
10
|
+
|
|
11
|
+
Consumer action: none (additive). Packages that paged `.rpc(...).order().range()` by hand move to `callAll`.
|
|
12
|
+
|
|
13
|
+
## 0.25.0 — created_by stamped; listAll keeps the database's error
|
|
14
|
+
|
|
15
|
+
- **A signed-in `insert` stamps `created_by`** with the session's person when the table has a
|
|
16
|
+
`created_by` column and the row omits it (row security on `workbench.notes` / `note_folders` and
|
|
17
|
+
similar tables refuses an insert whose `created_by` is not `auth.uid()`). An explicit value is kept;
|
|
18
|
+
the service role never stamps. Descriptors carry `createdBy: boolean` (generated from the columns).
|
|
19
|
+
- **`listAll` throws a `DatabaseError`** carrying the database's own error (message, code, details) —
|
|
20
|
+
it used to surface as a re-worded `readAllRows(...)` `Error` without the code.
|
|
21
|
+
|
|
22
|
+
Consumer action: run `matrx-data generate` again (`pnpm db:generate`) so descriptors carry `createdBy`;
|
|
23
|
+
code that matched `listAll` failures by the `readAllRows(` message should check `instanceof DatabaseError`.
|
|
24
|
+
|
|
3
25
|
## 0.24.0 — doors hardened (independent review)
|
|
4
26
|
|
|
5
27
|
- **Every write on a versioned table is guarded.** `remove` (and `archive` / `restore`) take the same
|
package/bin/generate.mjs
CHANGED
|
@@ -94,6 +94,7 @@ function tableDescriptor(schema, name, t) {
|
|
|
94
94
|
readOnlyColumns: reg?.clientReadOnlyColumns ?? [],
|
|
95
95
|
listScope: reg?.listScope ?? null,
|
|
96
96
|
realtime: (t.realtime ?? []).includes("supabase_realtime"),
|
|
97
|
+
createdBy: t.columns.some((c) => c.name === "created_by"),
|
|
97
98
|
};
|
|
98
99
|
return flags;
|
|
99
100
|
}
|
package/dist/db.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(db_exports, {
|
|
|
36
36
|
assertDoor: () => assertDoor,
|
|
37
37
|
assertWriteLanded: () => assertWriteLanded,
|
|
38
38
|
call: () => call,
|
|
39
|
+
callAll: () => callAll,
|
|
39
40
|
changesOf: () => changesOf,
|
|
40
41
|
count: () => count,
|
|
41
42
|
createActiveOrgCookie: () => createActiveOrgCookie,
|
|
@@ -1074,7 +1075,10 @@ async function listAll(db, table, where, opts = {}) {
|
|
|
1074
1075
|
({ from: lo, to: hi }) => {
|
|
1075
1076
|
let q = live(from(db, table).select(selectOf(opts.columns), { count: "exact" }), table, opts.includeDeleted);
|
|
1076
1077
|
if (where) q = where(q);
|
|
1077
|
-
return ordered(q, table, opts.orderBy).range(lo, hi)
|
|
1078
|
+
return Promise.resolve(ordered(q, table, opts.orderBy).range(lo, hi)).then((res) => {
|
|
1079
|
+
if (res.error) throw new DatabaseError(`listAll ${qualified(table)}`, res.error);
|
|
1080
|
+
return res;
|
|
1081
|
+
});
|
|
1078
1082
|
},
|
|
1079
1083
|
{
|
|
1080
1084
|
label: `listAll ${qualified(table)}`,
|
|
@@ -1120,7 +1124,12 @@ function withOrganization(db, table, values) {
|
|
|
1120
1124
|
async function insert(db, table, values) {
|
|
1121
1125
|
assertDoor(db, table, "insert");
|
|
1122
1126
|
const many = Array.isArray(values);
|
|
1123
|
-
|
|
1127
|
+
let rows = (many ? values : [values]).map((v) => withOrganization(db, table, v));
|
|
1128
|
+
if (table.createdBy && db.role === "user" && rows.some((r) => r.created_by == null) && db.client.auth) {
|
|
1129
|
+
const { data } = await db.client.auth.getSession();
|
|
1130
|
+
const person = data.session?.user?.id;
|
|
1131
|
+
if (person) rows = rows.map((r) => r.created_by == null ? { ...r, created_by: person } : r);
|
|
1132
|
+
}
|
|
1124
1133
|
const res = await from(db, table).insert(rows).select("*");
|
|
1125
1134
|
const stored = check(`insert ${qualified(table)}`, res);
|
|
1126
1135
|
return many ? stored : stored[0];
|
|
@@ -1195,6 +1204,27 @@ async function call(db, fn, args) {
|
|
|
1195
1204
|
}
|
|
1196
1205
|
return check(door, await db.client.schema(fn.schema).rpc(fn.name, args));
|
|
1197
1206
|
}
|
|
1207
|
+
async function callAll(db, fn, args, opts) {
|
|
1208
|
+
const door = `callAll ${fn.schema}.${fn.name}`;
|
|
1209
|
+
const role = db.role === "service" ? "service_role" : "authenticated";
|
|
1210
|
+
if (!fn.roles.includes(role) && !(role === "authenticated" && fn.roles.includes("anon"))) {
|
|
1211
|
+
throw new DoorRefusedError(door, `not executable by ${role} (executable by: ${fn.roles.join(", ") || "nobody"})`);
|
|
1212
|
+
}
|
|
1213
|
+
if (!opts.order.length) throw new DoorRefusedError(door, "a paged read needs an order (end on a key) \u2014 pages over no order can skip or repeat rows");
|
|
1214
|
+
return readAllRows(
|
|
1215
|
+
({ from: lo, to: hi }) => {
|
|
1216
|
+
let q = db.client.schema(fn.schema).rpc(fn.name, args, { count: "exact" });
|
|
1217
|
+
for (const o of opts.order) {
|
|
1218
|
+
q = typeof o === "string" ? q.order(o) : q.order(o.column, { ascending: o.ascending ?? true, ...o.nullsFirst !== void 0 ? { nullsFirst: o.nullsFirst } : {} });
|
|
1219
|
+
}
|
|
1220
|
+
return Promise.resolve(q.range(lo, hi)).then((res) => {
|
|
1221
|
+
if (res.error) throw new DatabaseError(door, res.error);
|
|
1222
|
+
return res;
|
|
1223
|
+
});
|
|
1224
|
+
},
|
|
1225
|
+
{ label: door, ...opts.pageSize !== void 0 ? { pageSize: opts.pageSize } : {}, ...opts.maxRows !== void 0 ? { maxRows: opts.maxRows } : {} }
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1198
1228
|
function tableByName(index, name) {
|
|
1199
1229
|
const t = index[name];
|
|
1200
1230
|
if (!t) throw new DoorRefusedError(`tableByName ${name}`, "no such table in the database description (schema-qualify it: schema.table)");
|