@protobase/cli 0.1.1 → 0.1.3
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/dist/db/introspect.js +13 -6
- package/dist/db/queries.js +11 -11
- package/dist/doctor/check.js +3 -2
- package/dist/doctor/run.js +1 -1
- package/dist/package.js +1 -1
- package/package.json +6 -6
package/dist/db/introspect.js
CHANGED
|
@@ -12,12 +12,18 @@ const byTable = (rows) => {
|
|
|
12
12
|
}
|
|
13
13
|
return groups;
|
|
14
14
|
};
|
|
15
|
-
const
|
|
15
|
+
const kinds = {
|
|
16
|
+
r: "table",
|
|
17
|
+
p: "table",
|
|
18
|
+
v: "view",
|
|
19
|
+
m: "materialized view"
|
|
20
|
+
};
|
|
21
|
+
const introspect = async (sql, relations = {}) => {
|
|
16
22
|
const [columns, foreignKeys, checks, indexes] = await Promise.all([
|
|
17
|
-
selectColumns(sql),
|
|
18
|
-
selectForeignKeys(sql),
|
|
19
|
-
selectChecks(sql),
|
|
20
|
-
selectIndexes(sql)
|
|
23
|
+
selectColumns(sql, relations),
|
|
24
|
+
selectForeignKeys(sql, relations),
|
|
25
|
+
selectChecks(sql, relations),
|
|
26
|
+
selectIndexes(sql, relations)
|
|
21
27
|
]);
|
|
22
28
|
const fks = byTable(foreignKeys);
|
|
23
29
|
const chks = byTable(checks);
|
|
@@ -25,7 +31,8 @@ const introspect = async (sql) => {
|
|
|
25
31
|
return [...byTable(columns)].map(([key, rows]) => ({
|
|
26
32
|
schema: rows[0].schema,
|
|
27
33
|
name: rows[0].table,
|
|
28
|
-
|
|
34
|
+
kind: kinds[rows[0].relkind],
|
|
35
|
+
columns: rows.map(({ schema, table, relkind, enumValues, defaultExpr, ...column }) => ({
|
|
29
36
|
...column,
|
|
30
37
|
enumValues: enumValues ?? [],
|
|
31
38
|
...defaultExpr !== null && { defaultExpr }
|
package/dist/db/queries.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
//#region src/db/queries.ts
|
|
2
|
-
const
|
|
3
|
-
c.relkind in ('r', 'p')
|
|
2
|
+
const userRelations = ({ views = false }) => `
|
|
3
|
+
c.relkind in ('r', 'p'${views ? ", 'v', 'm'" : ""})
|
|
4
4
|
and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')
|
|
5
5
|
and n.nspname not like 'pg_temp%'
|
|
6
6
|
`;
|
|
7
|
-
const selectColumns = (sql) => sql`
|
|
8
|
-
select n.nspname as schema, c.relname as table, a.attname as name, a.attnum::int as position,
|
|
7
|
+
const selectColumns = (sql, relations) => sql`
|
|
8
|
+
select n.nspname as schema, c.relname as table, c.relkind as relkind, a.attname as name, a.attnum::int as position,
|
|
9
9
|
bt.typname as "typeName", format_type(a.atttypid, a.atttypmod) as formatted,
|
|
10
10
|
(bt.typtype = 'e') as "isEnum",
|
|
11
11
|
(select array_agg(e.enumlabel::text order by e.enumsortorder) from pg_enum e where e.enumtypid = bt.oid) as "enumValues",
|
|
@@ -17,10 +17,10 @@ const selectColumns = (sql) => sql`
|
|
|
17
17
|
join pg_type t on t.oid = a.atttypid
|
|
18
18
|
join pg_type bt on bt.oid = case when t.typtype = 'd' then t.typbasetype else t.oid end
|
|
19
19
|
left join pg_attrdef d on d.adrelid = a.attrelid and d.adnum = a.attnum
|
|
20
|
-
where ${sql.unsafe(
|
|
20
|
+
where ${sql.unsafe(userRelations(relations))} and a.attnum > 0 and not a.attisdropped
|
|
21
21
|
order by n.nspname, c.relname, a.attnum
|
|
22
22
|
`;
|
|
23
|
-
const selectForeignKeys = (sql) => sql`
|
|
23
|
+
const selectForeignKeys = (sql, relations) => sql`
|
|
24
24
|
select n.nspname as schema, c.relname as table,
|
|
25
25
|
(select array_agg(a.attname::text order by k.ord) from unnest(con.conkey) with ordinality k(attnum, ord)
|
|
26
26
|
join pg_attribute a on a.attrelid = con.conrelid and a.attnum = k.attnum) as columns,
|
|
@@ -32,9 +32,9 @@ const selectForeignKeys = (sql) => sql`
|
|
|
32
32
|
join pg_namespace n on n.oid = c.relnamespace
|
|
33
33
|
join pg_class rc on rc.oid = con.confrelid
|
|
34
34
|
join pg_namespace rn on rn.oid = rc.relnamespace
|
|
35
|
-
where con.contype = 'f' and ${sql.unsafe(
|
|
35
|
+
where con.contype = 'f' and ${sql.unsafe(userRelations(relations))}
|
|
36
36
|
`;
|
|
37
|
-
const selectChecks = (sql) => sql`
|
|
37
|
+
const selectChecks = (sql, relations) => sql`
|
|
38
38
|
select n.nspname as schema, c.relname as table,
|
|
39
39
|
(select array_agg(a.attname::text order by k.ord) from unnest(con.conkey) with ordinality k(attnum, ord)
|
|
40
40
|
join pg_attribute a on a.attrelid = con.conrelid and a.attnum = k.attnum) as columns,
|
|
@@ -42,9 +42,9 @@ const selectChecks = (sql) => sql`
|
|
|
42
42
|
from pg_constraint con
|
|
43
43
|
join pg_class c on c.oid = con.conrelid
|
|
44
44
|
join pg_namespace n on n.oid = c.relnamespace
|
|
45
|
-
where con.contype = 'c' and con.conkey is not null and ${sql.unsafe(
|
|
45
|
+
where con.contype = 'c' and con.conkey is not null and ${sql.unsafe(userRelations(relations))}
|
|
46
46
|
`;
|
|
47
|
-
const selectIndexes = (sql) => sql`
|
|
47
|
+
const selectIndexes = (sql, relations) => sql`
|
|
48
48
|
select n.nspname as schema, c.relname as table, ic.relname as name,
|
|
49
49
|
(select array_agg(case when k.attnum = 0 then null else a.attname::text end order by k.ord)
|
|
50
50
|
from unnest(i.indkey::int2[]) with ordinality k(attnum, ord)
|
|
@@ -56,7 +56,7 @@ const selectIndexes = (sql) => sql`
|
|
|
56
56
|
join pg_namespace n on n.oid = c.relnamespace
|
|
57
57
|
join pg_class ic on ic.oid = i.indexrelid
|
|
58
58
|
join pg_am am on am.oid = ic.relam
|
|
59
|
-
where i.indisvalid and ${sql.unsafe(
|
|
59
|
+
where i.indisvalid and ${sql.unsafe(userRelations(relations))}
|
|
60
60
|
`;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { selectChecks, selectColumns, selectForeignKeys, selectIndexes };
|
package/dist/doctor/check.js
CHANGED
|
@@ -39,7 +39,7 @@ const checkField = (model, table, name) => {
|
|
|
39
39
|
resource: model.name,
|
|
40
40
|
message: `${name}: declared .dbDefault() but ${column.name} has no default the database computes`
|
|
41
41
|
});
|
|
42
|
-
if (field.filterable && !leadingIndex(table, column.name, model.tenant && model.fields[model.tenant]?.column)) issues.push({
|
|
42
|
+
if (field.filterable && table.kind !== "view" && !leadingIndex(table, column.name, model.tenant && model.fields[model.tenant]?.column)) issues.push({
|
|
43
43
|
severity: "warning",
|
|
44
44
|
resource: model.name,
|
|
45
45
|
message: `${name}: filterable but ${column.name} has no index`,
|
|
@@ -48,7 +48,8 @@ const checkField = (model, table, name) => {
|
|
|
48
48
|
return issues;
|
|
49
49
|
};
|
|
50
50
|
const checkKey = (model, table) => {
|
|
51
|
-
if (
|
|
51
|
+
if (table.kind === "view") return [];
|
|
52
|
+
if (table.kind === "table" && !keyColumns(table)) return [{
|
|
52
53
|
severity: "error",
|
|
53
54
|
resource: model.name,
|
|
54
55
|
message: `${tableKey(table)} has no primary key or unique NOT NULL index`,
|
package/dist/doctor/run.js
CHANGED
|
@@ -8,7 +8,7 @@ const runDoctor = async (options, out) => {
|
|
|
8
8
|
const models = await loadResourceModels(options.configDir);
|
|
9
9
|
if (models.length === 0) throw new Error(`No resources exported from ${options.configDir}/index.ts`);
|
|
10
10
|
const sql = connect(options.url);
|
|
11
|
-
const tables = await introspect(sql).finally(() => sql.end());
|
|
11
|
+
const tables = await introspect(sql, { views: true }).finally(() => sql.end());
|
|
12
12
|
const issues = models.flatMap((model) => checkResource(model, tables));
|
|
13
13
|
for (const issue of issues) out(format(issue));
|
|
14
14
|
const errors = issues.filter((i) => i.severity === "error").length;
|
package/dist/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"tsx": "^4.23.15",
|
|
47
47
|
"vite": "^8.3.3",
|
|
48
48
|
"zod": "^4.6.5",
|
|
49
|
-
"@protobase/layout": "0.1.
|
|
50
|
-
"@protobase/
|
|
51
|
-
"@protobase/
|
|
52
|
-
"@protobase/
|
|
53
|
-
"@protobase/
|
|
49
|
+
"@protobase/layout": "0.1.3",
|
|
50
|
+
"@protobase/query": "0.1.3",
|
|
51
|
+
"@protobase/server": "0.1.3",
|
|
52
|
+
"@protobase/ui": "0.1.3",
|
|
53
|
+
"@protobase/schema": "0.1.3"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsdown"
|