admindb 2.1.1 → 2.3.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/README.md +368 -335
- package/dist/app.js +227 -1
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +109 -1
- package/dist/auth/crypto.js +98 -1
- package/dist/auth/index.js +21 -1
- package/dist/auth/middleware.js +78 -1
- package/dist/auth/routes.js +64 -1
- package/dist/auth/types.js +7 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +309 -1
- package/dist/cli/config.js +118 -1
- package/dist/cli/index.js +20 -1
- package/dist/cli/runner.js +208 -1
- package/dist/cli.js +4 -1
- package/dist/data/chain.d.ts +1 -0
- package/dist/data/chain.js +130 -0
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +171 -1
- package/dist/data/detector.d.ts +1 -1
- package/dist/data/detector.js +535 -1
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +586 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +30 -1
- package/dist/data/prng.d.ts +1 -0
- package/dist/data/prng.js +73 -0
- package/dist/data/registry.d.ts +1 -0
- package/dist/data/registry.js +805 -0
- package/dist/data/schema-graph.d.ts +1 -0
- package/dist/data/schema-graph.js +210 -0
- package/dist/data/strategies.d.ts +1 -1
- package/dist/data/strategies.js +100 -1
- package/dist/data/templates.d.ts +1 -0
- package/dist/data/templates.js +134 -0
- package/dist/data/types.d.ts +1 -1
- package/dist/data/types.js +5 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +472 -1
- package/dist/db/export.d.ts +1 -1
- package/dist/db/export.js +67 -1
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +119 -1
- package/dist/db/index.js +24 -1
- package/dist/db/introspection.js +164 -1
- package/dist/db/manager.js +327 -1
- package/dist/db/migrations.js +100 -1
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +687 -1
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +52 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/api.js +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/common.js +1 -0
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -0
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.d.ts +1 -0
- package/dist/routes/api/erd.js +45 -0
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +213 -1
- package/dist/routes/api/import-export.js +82 -1
- package/dist/routes/api/index.js +48 -1
- package/dist/routes/api/query.js +74 -1
- package/dist/routes/api/rows.js +358 -1
- package/dist/routes/api/seed.js +167 -1
- package/dist/routes/api/tables.js +216 -1
- package/dist/routes/databases.js +210 -1
- package/dist/routes/index.js +19 -1
- package/dist/routes/pages.js +446 -1
- package/dist/serverless.js +149 -1
- package/dist/sql/classifier.js +24 -1
- package/dist/sql/error-analyzer.d.ts +1 -0
- package/dist/sql/error-analyzer.js +550 -0
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +245 -1
- package/dist/sql/index.js +18 -1
- package/dist/types/api.d.ts +1 -1
- package/dist/types/api.js +2 -1
- package/dist/utils/colors.js +54 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +185 -1
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +61 -1
- package/dist/utils/datatype.js +167 -1
- package/dist/utils/icons.js +81 -1
- package/dist/utils/index.js +20 -1
- package/dist/utils/logger.js +36 -1
- package/dist/views/layouts/main.hbs +1 -74
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -0
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -124
- package/dist/views/pages/info.hbs +1 -0
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -87
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +273 -167
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -117
- package/docs/API.md +149 -144
- package/docs/EXAMPLES.md +579 -576
- package/docs/SECURITY.md +265 -265
- package/package.json +79 -78
- package/dist/public/js/icons.js +0 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateGenerationPlan = validateGenerationPlan;
|
|
4
|
+
const templates_1 = require("./templates");
|
|
5
|
+
async function validateGenerationPlan(db, plan, tableInfos) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const infos = tableInfos ?? new Map();
|
|
8
|
+
for (const tableName of Object.keys(plan.tables)) {
|
|
9
|
+
if (!infos.has(tableName)) {
|
|
10
|
+
try {
|
|
11
|
+
const res = await db.getTableInfo(tableName);
|
|
12
|
+
if (res.success && res.data?.columns?.length) {
|
|
13
|
+
infos.set(tableName, res.data);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
issues.push({
|
|
17
|
+
type: 'error',
|
|
18
|
+
table: tableName,
|
|
19
|
+
message: `Table "${tableName}" does not exist in the database.`,
|
|
20
|
+
code: 'TABLE_NOT_FOUND',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
issues.push({
|
|
26
|
+
type: 'error',
|
|
27
|
+
table: tableName,
|
|
28
|
+
message: `Failed to inspect table "${tableName}": ${err instanceof Error ? err.message : String(err)}`,
|
|
29
|
+
code: 'TABLE_INSPECT_ERROR',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let estimatedTotalRows = 0;
|
|
35
|
+
let tablesToGenerate = 0;
|
|
36
|
+
let tablesToUseExisting = 0;
|
|
37
|
+
let tablesToSkip = 0;
|
|
38
|
+
for (const [tableName, spec] of Object.entries(plan.tables)) {
|
|
39
|
+
const info = infos.get(tableName);
|
|
40
|
+
if (!info)
|
|
41
|
+
continue;
|
|
42
|
+
const mode = spec.mode || 'generate';
|
|
43
|
+
const rowCount = Math.max(0, spec.rows ?? 10);
|
|
44
|
+
if (mode === 'generate') {
|
|
45
|
+
tablesToGenerate++;
|
|
46
|
+
estimatedTotalRows += rowCount;
|
|
47
|
+
}
|
|
48
|
+
else if (mode === 'use_existing') {
|
|
49
|
+
tablesToUseExisting++;
|
|
50
|
+
}
|
|
51
|
+
else if (mode === 'generate_if_empty') {
|
|
52
|
+
try {
|
|
53
|
+
const cRes = await db.getRowCount(tableName);
|
|
54
|
+
const curCount = cRes.success && typeof cRes.data === 'number' ? cRes.data : 0;
|
|
55
|
+
if (curCount === 0) {
|
|
56
|
+
tablesToGenerate++;
|
|
57
|
+
estimatedTotalRows += rowCount;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
tablesToUseExisting++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
tablesToGenerate++;
|
|
65
|
+
estimatedTotalRows += rowCount;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
tablesToSkip++;
|
|
70
|
+
}
|
|
71
|
+
if (mode === 'skip' || mode === 'use_existing')
|
|
72
|
+
continue;
|
|
73
|
+
const colMap = new Map(info.columns.map((c) => [c.name, c]));
|
|
74
|
+
const fkMap = new Map(info.foreignKeys.map((fk) => [fk.from, fk]));
|
|
75
|
+
const colNames = info.columns.map((c) => c.name);
|
|
76
|
+
const { hasCycle, cycleColumns } = (0, templates_1.orderColumnDependencies)(colNames, spec.columns || {});
|
|
77
|
+
if (hasCycle) {
|
|
78
|
+
issues.push({
|
|
79
|
+
type: 'error',
|
|
80
|
+
table: tableName,
|
|
81
|
+
message: `Circular column template dependency detected in table "${tableName}" between columns: ${cycleColumns.join(', ')}`,
|
|
82
|
+
code: 'TEMPLATE_CYCLE',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
for (const col of info.columns) {
|
|
86
|
+
const p = spec.columns[col.name] || { strategy: 'skip' };
|
|
87
|
+
if (p.strategy === 'template' && p.template) {
|
|
88
|
+
for (const dep of (0, templates_1.extractTemplateDependencies)(p.template)) {
|
|
89
|
+
if (!colMap.has(dep)) {
|
|
90
|
+
issues.push({
|
|
91
|
+
type: 'warning',
|
|
92
|
+
table: tableName,
|
|
93
|
+
column: col.name,
|
|
94
|
+
message: `Template in column "${col.name}" references non-existent column "${dep}".`,
|
|
95
|
+
code: 'TEMPLATE_INVALID_REF',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const isReq = Boolean(col.notnull || (col.pk ?? 0) > 0);
|
|
101
|
+
if (isReq && col.dflt_value == null) {
|
|
102
|
+
if (p.strategy === 'null') {
|
|
103
|
+
issues.push({
|
|
104
|
+
type: 'error',
|
|
105
|
+
table: tableName,
|
|
106
|
+
column: col.name,
|
|
107
|
+
message: `Column "${col.name}" is NOT NULL without a default value, but strategy is set to NULL.`,
|
|
108
|
+
code: 'NOT_NULL_VIOLATION',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (p.strategy === 'skip' && !col.pk) {
|
|
112
|
+
issues.push({
|
|
113
|
+
type: 'warning',
|
|
114
|
+
table: tableName,
|
|
115
|
+
column: col.name,
|
|
116
|
+
message: `Column "${col.name}" is NOT NULL without a default value, but strategy is set to Skip (database default).`,
|
|
117
|
+
code: 'NOT_NULL_SKIP',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const fk = fkMap.get(col.name);
|
|
122
|
+
if (p.strategy === 'fk' && fk) {
|
|
123
|
+
const parentSpec = plan.tables[fk.table];
|
|
124
|
+
if (parentSpec?.mode === 'skip') {
|
|
125
|
+
try {
|
|
126
|
+
const countRes = await db.getRowCount(fk.table);
|
|
127
|
+
const parentRows = countRes.success && typeof countRes.data === 'number' ? countRes.data : 0;
|
|
128
|
+
if (parentRows === 0 && (col.notnull || (col.pk ?? 0) > 0)) {
|
|
129
|
+
issues.push({
|
|
130
|
+
type: 'error',
|
|
131
|
+
table: tableName,
|
|
132
|
+
column: col.name,
|
|
133
|
+
message: `Foreign key "${col.name}" references table "${fk.table}" which is set to "Don't Touch" and is currently empty.`,
|
|
134
|
+
code: 'FK_EMPTY_PARENT',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch { }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const errors = issues.filter((i) => i.type === 'error');
|
|
144
|
+
const valid = errors.length === 0;
|
|
145
|
+
return {
|
|
146
|
+
valid,
|
|
147
|
+
canExecute: valid,
|
|
148
|
+
issues,
|
|
149
|
+
summary: {
|
|
150
|
+
totalTables: Object.keys(plan.tables).length,
|
|
151
|
+
tablesToGenerate,
|
|
152
|
+
tablesToUseExisting,
|
|
153
|
+
tablesToSkip,
|
|
154
|
+
estimatedTotalRows,
|
|
155
|
+
cyclesDetected: false,
|
|
156
|
+
cycleTables: [],
|
|
157
|
+
executionOrder: Object.keys(plan.tables),
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {Logger} from '../utils/logger';import{type ColumnDef,type IndexDef} from '../sql/generator';import type {DatabaseDialect,DbOpenOptions,IDatabase,ReferencingTableInfo,Result,RowFilters,SavedQuery,SchemaInfo,SQLInputValue,TableInfoData,TableListItem,WhereClause} from './types';export * from './types';export * from './filters';export declare class SqliteDatabase implements IDatabase{private db;private logger;readonly path:string;readonly isReadOnly:boolean;readonly dialect:DatabaseDialect;constructor(dbPath:string,logger:Logger,options?:DbOpenOptions);private readonlyBlocked;close():void;private initSchema;private tryRun;private transaction;private toBind;all(sql:string,params?:SQLInputValue[]):Promise<Result<unknown[]>>;run(sql:string,params?:SQLInputValue[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;execResult(sql:string):Promise<Result<{changes?:number;}>>;hasMultipleStatements(sql:string):boolean;runWrite(sql:string):Promise<Result<{changes?:number;}>>;listTables():Promise<Result<TableListItem[]>>;getTableInfo(table:string):Promise<Result<TableInfoData>>;getRowCount(table:string,filters?:RowFilters):Promise<Result<number>>;getRows(table:string,opts?:{page?:number;limit?:number;orderBy?:string;orderDir?:'asc'|'desc';filters?:RowFilters;}):Promise<Result<Record<string,unknown>[]>>;getAllRows(table:string):Promise<Result<Record<string,unknown>[]>>;getRow(table:string,where:WhereClause[]):Promise<Result<Record<string,unknown>|null>>;insertRow(table:string,fields:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;insertRows(table:string,rows:WhereClause[][]):Promise<Result<{inserted:number;skipped:number;}>>;updateRow(table:string,fields:WhereClause[],where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;updateRows(table:string,rows:{fields:WhereClause[];where:WhereClause[];}[]):Promise<Result<{updated:number;}>>;deleteRow(table:string,where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;deleteRows(table:string,rows:WhereClause[][]):Promise<Result<{deleted:number;}>>;getRowsByPks(table:string,pks:WhereClause[][]):Promise<Result<Record<string,unknown>[]>>;getCreateStatement(table:string):Promise<Result<string|null>>;listSavedQueries():Promise<Result<SavedQuery[]>>;saveQuery(name:string,sql:string):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;deleteSavedQuery(id:string|number):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;getSchema(table:string):Promise<Result<SchemaInfo>>;getReferencingTables(table:string):Promise<Result<ReferencingTableInfo[]>>;getRowsByFk(table:string,column:string,value:unknown,limit?:number):Promise<Result<{rows:Record<string,unknown>[];total:number;}>>;renameTable(oldName:string,newName:string):Promise<Result<{changes?:number;}>>;addColumn(table:string,col:ColumnDef):Promise<Result<{changes?:number;}>>;modifyColumn(table:string,oldColName:string,newColDef:ColumnDef):Promise<Result<{changes?:number;}>>;renameColumn(table:string,oldName:string,newName:string):Promise<Result<{changes?:number;}>>;dropColumn(table:string,column:string):Promise<Result<{changes?:number;}>>;dropTable(table:string):Promise<Result<{changes?:number;}>>;createIndex(table:string,index:IndexDef):Promise<Result<{changes?:number;}>>;dropIndex(indexName:string):Promise<Result<{changes?:number;}>>;private getTableInfoSync;}
|
|
1
|
+
import type {Logger} from '../utils/logger';import{type ColumnDef,type IndexDef} from '../sql/generator';import type {DatabaseDialect,DbOpenOptions,IDatabase,ReferencingTableInfo,Result,RowFilters,SavedQuery,SchemaInfo,SQLInputValue,TableInfoData,TableListItem,WhereClause} from './types';export * from './types';export * from './filters';export declare class SqliteDatabase implements IDatabase{private db;private logger;readonly path:string;readonly isReadOnly:boolean;readonly dialect:DatabaseDialect;constructor(dbPath:string,logger:Logger,options?:DbOpenOptions);private readonlyBlocked;close():void;private initSchema;private tryRun;private transaction;private toBind;all(sql:string,params?:SQLInputValue[]):Promise<Result<unknown[]>>;run(sql:string,params?:SQLInputValue[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;execResult(sql:string):Promise<Result<{changes?:number;}>>;hasMultipleStatements(sql:string):boolean;runWrite(sql:string):Promise<Result<{changes?:number;}>>;listTables():Promise<Result<TableListItem[]>>;getTableInfo(table:string):Promise<Result<TableInfoData>>;getRowCount(table:string,filters?:RowFilters):Promise<Result<number>>;getRows(table:string,opts?:{page?:number;limit?:number;orderBy?:string;orderDir?:'asc'|'desc';filters?:RowFilters;}):Promise<Result<Record<string,unknown>[]>>;getAllRows(table:string):Promise<Result<Record<string,unknown>[]>>;getRow(table:string,where:WhereClause[]):Promise<Result<Record<string,unknown>|null>>;insertRow(table:string,fields:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;insertRows(table:string,rows:WhereClause[][]):Promise<Result<{inserted:number;skipped:number;}>>;updateRow(table:string,fields:WhereClause[],where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;updateRows(table:string,rows:{fields:WhereClause[];where:WhereClause[];}[]):Promise<Result<{updated:number;}>>;deleteRow(table:string,where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;deleteRows(table:string,rows:WhereClause[][]):Promise<Result<{deleted:number;}>>;getRowsByPks(table:string,pks:WhereClause[][]):Promise<Result<Record<string,unknown>[]>>;getCreateStatement(table:string):Promise<Result<string|null>>;listSavedQueries():Promise<Result<SavedQuery[]>>;saveQuery(name:string,sql:string):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;deleteSavedQuery(id:string|number):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;getSettings():Promise<Result<Record<string,string>>>;getSchema(table:string):Promise<Result<SchemaInfo>>;getReferencingTables(table:string):Promise<Result<ReferencingTableInfo[]>>;getRowsByFk(table:string,column:string,value:unknown,limit?:number):Promise<Result<{rows:Record<string,unknown>[];total:number;}>>;renameTable(oldName:string,newName:string):Promise<Result<{changes?:number;}>>;addColumn(table:string,col:ColumnDef):Promise<Result<{changes?:number;}>>;modifyColumn(table:string,oldColName:string,newColDef:ColumnDef):Promise<Result<{changes?:number;}>>;renameColumn(table:string,oldName:string,newName:string):Promise<Result<{changes?:number;}>>;dropColumn(table:string,column:string):Promise<Result<{changes?:number;}>>;dropTable(table:string):Promise<Result<{changes?:number;}>>;createIndex(table:string,index:IndexDef):Promise<Result<{changes?:number;}>>;dropIndex(indexName:string):Promise<Result<{changes?:number;}>>;private getTableInfoSync;}
|