@ai-matrx/data 0.25.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/bin/generate.mjs +16 -2
- package/bin/matrx-data.mjs +2 -1
- package/dist/db.cjs +22 -0
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +20 -2
- package/dist/db.d.ts +20 -2
- package/dist/db.js +22 -0
- package/dist/db.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.1 — generate --only
|
|
4
|
+
|
|
5
|
+
- `matrx-data generate --only a,b,schema.c` keeps only the named tables, views and functions (a package
|
|
6
|
+
that calls 29 functions of `public` generates 13 KB, not the schema's 517 KB); an unknown name fails.
|
|
7
|
+
|
|
8
|
+
Consumer action: none.
|
|
9
|
+
|
|
10
|
+
## 0.26.0 — callAll: every row of a set-returning function
|
|
11
|
+
|
|
12
|
+
- **`callAll(db, fn, args, { order })`** — the complete read of a set-returning database function:
|
|
13
|
+
`rpc(…, { count: "exact" })` paged by `range` over the named `order` (`"id"` or
|
|
14
|
+
`{ column, ascending, nullsFirst }`; end on a key), past the 1,000-row cap; a `DatabaseError` keeps the
|
|
15
|
+
database's own error (code kept); a short read throws `IncompleteReadError`; an empty order is refused.
|
|
16
|
+
- `SupabaseLike.rpc` takes PostgREST's optional third argument (`{ count }`).
|
|
17
|
+
|
|
18
|
+
Consumer action: none (additive). Packages that paged `.rpc(...).order().range()` by hand move to `callAll`.
|
|
19
|
+
|
|
3
20
|
## 0.25.0 — created_by stamped; listAll keeps the database's error
|
|
4
21
|
|
|
5
22
|
- **A signed-in `insert` stamps `created_by`** with the session's person when the table has a
|
package/bin/generate.mjs
CHANGED
|
@@ -195,17 +195,31 @@ function readDescriptionFile(dir, name) {
|
|
|
195
195
|
throw new Error(`${name} not found in ${dir} (neither ${name} nor ${name}.gz)`);
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
/** Keep only the named tables/views/functions (`--only a,b,schema.c`); an unknown name is an error. */
|
|
199
|
+
function pick(desc, only) {
|
|
200
|
+
if (!only?.length) return desc;
|
|
201
|
+
const want = new Set(only.filter((n) => !n.includes(".") || n.startsWith(`${desc.schema}.`)).map((n) => n.slice(n.indexOf(".") + 1)));
|
|
202
|
+
const keep = (rec) => Object.fromEntries(Object.entries(rec ?? {}).filter(([n]) => want.has(n)));
|
|
203
|
+
const out = { ...desc, tables: keep(desc.tables), views: keep(desc.views), functions: keep(desc.functions) };
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function generate({ from = BUNDLED_DESCRIPTION, out, schemas, only, check, importFrom, flagsIndex = process.argv.includes("--flags-index") }) {
|
|
199
208
|
const index = JSON.parse(readDescriptionFile(from, "index.json"));
|
|
200
209
|
const names = schemas?.length ? schemas : Object.keys(index.schemas).sort();
|
|
201
210
|
const files = {};
|
|
202
211
|
const descs = [];
|
|
203
212
|
for (const s of names) {
|
|
204
213
|
if (!index.schemas[s]) throw new Error(`schema "${s}" is not in the description (${Object.keys(index.schemas).length} schemas)`);
|
|
205
|
-
const desc = JSON.parse(readDescriptionFile(from, `${s}.json`));
|
|
214
|
+
const desc = pick(JSON.parse(readDescriptionFile(from, `${s}.json`)), only);
|
|
206
215
|
descs.push(desc);
|
|
207
216
|
files[`${s}.ts`] = renderSchema(desc, index.schemas[s].sha256, importFrom);
|
|
208
217
|
}
|
|
218
|
+
if (only?.length) {
|
|
219
|
+
const found = new Set(descs.flatMap((d) => [...Object.keys(d.tables), ...Object.keys(d.views), ...Object.keys(d.functions)].flatMap((n) => [n, `${d.schema}.${n}`])));
|
|
220
|
+
const missing = only.filter((n) => !found.has(n));
|
|
221
|
+
if (missing.length) throw new Error(`--only names nothing in the description: ${missing.join(", ")}`);
|
|
222
|
+
}
|
|
209
223
|
if (flagsIndex) {
|
|
210
224
|
for (const d of descs) files[`${d.schema}.flags.ts`] = renderSchemaFlags(d, importFrom);
|
|
211
225
|
files["flags-index.ts"] = renderFlagLoaders(descs.map((d) => d.schema), importFrom);
|
package/bin/matrx-data.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* matrx-data — @ai-matrx/data's build-time CLI.
|
|
4
4
|
*
|
|
5
5
|
* matrx-data emit [--out <dir>] [--check] live database → the database description (read-only)
|
|
6
|
-
* matrx-data generate --out <dir> [--from <dir>] [--schemas a,b] [--flags-index] [--check]
|
|
6
|
+
* matrx-data generate --out <dir> [--from <dir>] [--schemas a,b] [--only t1,fn2] [--flags-index] [--check]
|
|
7
7
|
* description → typed doors in THIS app (--from defaults to the description
|
|
8
8
|
* shipped inside this package version)
|
|
9
9
|
*
|
|
@@ -35,6 +35,7 @@ async function main() {
|
|
|
35
35
|
...(from ? { from: path.resolve(from) } : {}),
|
|
36
36
|
out: path.resolve(out),
|
|
37
37
|
schemas: flag("--schemas")?.split(",").map((s) => s.trim()).filter(Boolean),
|
|
38
|
+
only: flag("--only")?.split(",").map((s) => s.trim()).filter(Boolean),
|
|
38
39
|
check: rest.includes("--check"),
|
|
39
40
|
importFrom: flag("--import-from") ?? "@ai-matrx/data/db",
|
|
40
41
|
});
|
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,
|
|
@@ -1203,6 +1204,27 @@ async function call(db, fn, args) {
|
|
|
1203
1204
|
}
|
|
1204
1205
|
return check(door, await db.client.schema(fn.schema).rpc(fn.name, args));
|
|
1205
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
|
+
}
|
|
1206
1228
|
function tableByName(index, name) {
|
|
1207
1229
|
const t = index[name];
|
|
1208
1230
|
if (!t) throw new DoorRefusedError(`tableByName ${name}`, "no such table in the database description (schema-qualify it: schema.table)");
|