admindb 2.4.1 → 2.6.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/LICENSE +20 -20
- package/dist/app.js +48 -25
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.js +2 -0
- package/dist/core/diff/data-differ.d.ts +1 -0
- package/dist/core/diff/data-differ.js +243 -0
- package/dist/core/diff/patch-generator.d.ts +1 -0
- package/dist/core/diff/patch-generator.js +77 -0
- package/dist/core/diff/schema-differ.d.ts +1 -0
- package/dist/core/diff/schema-differ.js +229 -0
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +39 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/result.d.ts +1 -0
- package/dist/core/result.js +10 -0
- package/dist/core/router.d.ts +1 -0
- package/dist/core/router.js +37 -0
- package/dist/core/transfer/dialect-mapper.d.ts +1 -0
- package/dist/core/transfer/dialect-mapper.js +303 -0
- package/dist/core/transfer/masking.d.ts +1 -0
- package/dist/core/transfer/masking.js +49 -0
- package/dist/core/transfer/sync-engine.d.ts +1 -0
- package/dist/core/transfer/sync-engine.js +234 -0
- package/dist/core/transfer/topological-sort.d.ts +1 -0
- package/dist/core/transfer/topological-sort.js +55 -0
- package/dist/data/chain.js +1 -1
- package/dist/data/engine.js +1 -1
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +9 -31
- package/dist/db/dialects/index.d.ts +1 -0
- package/dist/db/dialects/index.js +58 -0
- package/dist/db/dialects/postgres/ddl.d.ts +1 -0
- package/dist/db/dialects/postgres/ddl.js +107 -0
- package/dist/db/dialects/postgres/dialect.d.ts +1 -0
- package/dist/db/dialects/postgres/dialect.js +28 -0
- package/dist/db/dialects/postgres/introspector.d.ts +1 -0
- package/dist/db/dialects/postgres/introspector.js +265 -0
- package/dist/db/dialects/postgres/types.d.ts +1 -0
- package/dist/db/dialects/postgres/types.js +83 -0
- package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
- package/dist/db/dialects/sqlite/ddl.js +244 -0
- package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
- package/dist/db/dialects/sqlite/dialect.js +25 -0
- package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
- package/dist/db/dialects/sqlite/introspector.js +217 -0
- package/dist/db/dialects/sqlite/types.d.ts +1 -0
- package/dist/db/dialects/sqlite/types.js +69 -0
- package/dist/db/dialects/types.d.ts +1 -0
- package/dist/db/dialects/types.js +15 -0
- package/dist/db/export.js +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +101 -21
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +20 -284
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +0 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/dist/modules/databases/databases.routes.d.ts +1 -0
- package/dist/modules/databases/databases.routes.js +150 -0
- package/dist/modules/databases/databases.service.d.ts +1 -0
- package/dist/modules/databases/databases.service.js +134 -0
- package/dist/modules/databases/index.d.ts +1 -0
- package/dist/{routes → modules/databases}/index.js +2 -3
- package/dist/modules/erd/erd.routes.d.ts +1 -0
- package/dist/modules/erd/erd.routes.js +32 -0
- package/dist/modules/erd/erd.service.d.ts +1 -0
- package/dist/modules/erd/erd.service.js +39 -0
- package/dist/modules/erd/index.d.ts +1 -0
- package/dist/modules/erd/index.js +18 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +70 -0
- package/dist/modules/query/index.d.ts +1 -0
- package/dist/modules/query/index.js +18 -0
- package/dist/modules/query/query.routes.d.ts +1 -0
- package/dist/modules/query/query.routes.js +54 -0
- package/dist/modules/query/query.service.d.ts +1 -0
- package/dist/modules/query/query.service.js +95 -0
- package/dist/modules/rows/blob.helper.d.ts +1 -0
- package/dist/modules/rows/blob.helper.js +40 -0
- package/dist/modules/rows/display.helper.d.ts +1 -0
- package/dist/modules/rows/display.helper.js +73 -0
- package/dist/modules/rows/index.d.ts +1 -0
- package/dist/modules/rows/index.js +20 -0
- package/dist/modules/rows/rows.routes.d.ts +1 -0
- package/dist/modules/rows/rows.routes.js +555 -0
- package/dist/modules/rows/rows.service.d.ts +1 -0
- package/dist/modules/rows/rows.service.js +433 -0
- package/dist/modules/schema/index.d.ts +1 -0
- package/dist/modules/schema/index.js +18 -0
- package/dist/modules/schema/schema.routes.d.ts +1 -0
- package/dist/modules/schema/schema.routes.js +171 -0
- package/dist/modules/schema/schema.service.d.ts +1 -0
- package/dist/modules/schema/schema.service.js +58 -0
- package/dist/modules/search/index.d.ts +1 -0
- package/dist/modules/search/index.js +19 -0
- package/dist/modules/search/search.routes.d.ts +1 -0
- package/dist/modules/search/search.routes.js +38 -0
- package/dist/modules/search/search.service.d.ts +1 -0
- package/dist/modules/search/search.service.js +458 -0
- package/dist/modules/search/search.types.d.ts +1 -0
- package/dist/modules/search/search.types.js +2 -0
- package/dist/modules/seed/index.d.ts +1 -0
- package/dist/modules/seed/index.js +18 -0
- package/dist/modules/seed/seed.routes.d.ts +1 -0
- package/dist/modules/seed/seed.routes.js +212 -0
- package/dist/modules/seed/seed.service.d.ts +1 -0
- package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
- package/dist/modules/sync/index.d.ts +1 -0
- package/dist/modules/sync/index.js +18 -0
- package/dist/modules/sync/sync.routes.d.ts +1 -0
- package/dist/modules/sync/sync.routes.js +135 -0
- package/dist/modules/sync/sync.service.d.ts +1 -0
- package/dist/modules/sync/sync.service.js +41 -0
- package/dist/modules/tables/index.d.ts +1 -0
- package/dist/modules/tables/index.js +18 -0
- package/dist/modules/tables/tables.routes.d.ts +1 -0
- package/dist/modules/tables/tables.routes.js +194 -0
- package/dist/modules/tables/tables.service.d.ts +1 -0
- package/dist/modules/tables/tables.service.js +218 -0
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/search.js +1 -0
- package/dist/public/js/sync.js +1 -0
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +1 -3
- package/dist/utils/icons.js +3 -0
- package/dist/utils/stream.d.ts +1 -0
- package/dist/utils/stream.js +131 -0
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/error.hbs +10 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/query.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +1 -252
- package/dist/views/pages/seed.hbs +1 -307
- package/dist/views/pages/sync.hbs +409 -0
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +4 -17
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +10 -9
- package/dist/db/introspection.d.ts +0 -1
- package/dist/db/introspection.js +0 -164
- package/dist/routes/api/erd.d.ts +0 -1
- package/dist/routes/api/erd.js +0 -45
- package/dist/routes/api/helpers.d.ts +0 -1
- package/dist/routes/api/import-export.d.ts +0 -1
- package/dist/routes/api/import-export.js +0 -84
- package/dist/routes/api/index.d.ts +0 -1
- package/dist/routes/api/index.js +0 -55
- package/dist/routes/api/query.d.ts +0 -1
- package/dist/routes/api/query.js +0 -77
- package/dist/routes/api/rows.d.ts +0 -1
- package/dist/routes/api/rows.js +0 -373
- package/dist/routes/api/seed.d.ts +0 -1
- package/dist/routes/api/seed.js +0 -180
- package/dist/routes/api/tables.d.ts +0 -1
- package/dist/routes/api/tables.js +0 -230
- package/dist/routes/databases.d.ts +0 -1
- package/dist/routes/databases.js +0 -212
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/pages.d.ts +0 -1
- package/dist/routes/pages.js +0 -540
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compareSchemas = compareSchemas;
|
|
4
|
+
const dialect_mapper_1 = require("../transfer/dialect-mapper");
|
|
5
|
+
async function compareSchemas(sourceDb, sourceDbId, targetDb, targetDbId) {
|
|
6
|
+
const [sourceTableListRes, targetTableListRes] = await Promise.all([
|
|
7
|
+
sourceDb.listTables(),
|
|
8
|
+
targetDb.listTables(),
|
|
9
|
+
]);
|
|
10
|
+
if (!sourceTableListRes.success)
|
|
11
|
+
throw new Error(sourceTableListRes.error || 'Failed to list source tables');
|
|
12
|
+
if (!targetTableListRes.success)
|
|
13
|
+
throw new Error(targetTableListRes.error || 'Failed to list target tables');
|
|
14
|
+
const sourceTableNames = (sourceTableListRes.data || []).map((t) => t.name).filter((n) => !n.startsWith('sqlite_') && !n.startsWith('_admindb_'));
|
|
15
|
+
const targetTableNames = (targetTableListRes.data || []).map((t) => t.name).filter((n) => !n.startsWith('sqlite_') && !n.startsWith('_admindb_'));
|
|
16
|
+
const [sourceInfos, targetInfos] = await Promise.all([
|
|
17
|
+
Promise.all(sourceTableNames.map(async (name) => (await sourceDb.getTableInfo(name)).data)),
|
|
18
|
+
Promise.all(targetTableNames.map(async (name) => (await targetDb.getTableInfo(name)).data)),
|
|
19
|
+
]);
|
|
20
|
+
const sourceDialect = sourceDb.dialect === 'postgres' ? 'postgres' : 'sqlite';
|
|
21
|
+
const targetDialect = targetDb.dialect === 'postgres' ? 'postgres' : 'sqlite';
|
|
22
|
+
const sourceMap = new Map();
|
|
23
|
+
for (const info of sourceInfos) {
|
|
24
|
+
if (info)
|
|
25
|
+
sourceMap.set(info.table, (0, dialect_mapper_1.normalizeTableInfo)(info, sourceDialect));
|
|
26
|
+
}
|
|
27
|
+
const targetMap = new Map();
|
|
28
|
+
for (const info of targetInfos) {
|
|
29
|
+
if (info)
|
|
30
|
+
targetMap.set(info.table, (0, dialect_mapper_1.normalizeTableInfo)(info, targetDialect));
|
|
31
|
+
}
|
|
32
|
+
const allNames = Array.from(new Set([...sourceTableNames, ...targetTableNames])).sort();
|
|
33
|
+
const tableDiffs = [];
|
|
34
|
+
let addedTables = 0;
|
|
35
|
+
let removedTables = 0;
|
|
36
|
+
let modifiedTables = 0;
|
|
37
|
+
let identicalTables = 0;
|
|
38
|
+
let totalColumnsChanged = 0;
|
|
39
|
+
for (const name of allNames) {
|
|
40
|
+
const src = sourceMap.get(name);
|
|
41
|
+
const tgt = targetMap.get(name);
|
|
42
|
+
if (src && !tgt) {
|
|
43
|
+
addedTables++;
|
|
44
|
+
tableDiffs.push({
|
|
45
|
+
name,
|
|
46
|
+
action: 'added',
|
|
47
|
+
sourceTable: src,
|
|
48
|
+
columnDiffs: src.columns.map((c) => ({ name: c.name, action: 'added', sourceCol: c })),
|
|
49
|
+
pkChanged: true,
|
|
50
|
+
indexesAdded: src.indexes.map((i) => i.name),
|
|
51
|
+
indexesRemoved: [],
|
|
52
|
+
fksAdded: src.foreignKeys.map((f) => `${f.from} -> ${f.table}.${f.to}`),
|
|
53
|
+
fksRemoved: [],
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!src && tgt) {
|
|
58
|
+
removedTables++;
|
|
59
|
+
tableDiffs.push({
|
|
60
|
+
name,
|
|
61
|
+
action: 'removed',
|
|
62
|
+
targetTable: tgt,
|
|
63
|
+
columnDiffs: tgt.columns.map((c) => ({ name: c.name, action: 'removed', targetCol: c })),
|
|
64
|
+
pkChanged: true,
|
|
65
|
+
indexesAdded: [],
|
|
66
|
+
indexesRemoved: tgt.indexes.map((i) => i.name),
|
|
67
|
+
fksAdded: [],
|
|
68
|
+
fksRemoved: tgt.foreignKeys.map((f) => `${f.from} -> ${f.table}.${f.to}`),
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (src && tgt) {
|
|
73
|
+
const colDiffs = [];
|
|
74
|
+
const srcColMap = new Map(src.columns.map((c) => [c.name, c]));
|
|
75
|
+
const tgtColMap = new Map(tgt.columns.map((c) => [c.name, c]));
|
|
76
|
+
const colNames = Array.from(new Set([...srcColMap.keys(), ...tgtColMap.keys()]));
|
|
77
|
+
for (const colName of colNames) {
|
|
78
|
+
const sc = srcColMap.get(colName);
|
|
79
|
+
const tc = tgtColMap.get(colName);
|
|
80
|
+
if (sc && !tc) {
|
|
81
|
+
colDiffs.push({ name: colName, action: 'added', sourceCol: sc });
|
|
82
|
+
totalColumnsChanged++;
|
|
83
|
+
}
|
|
84
|
+
else if (!sc && tc) {
|
|
85
|
+
colDiffs.push({ name: colName, action: 'removed', targetCol: tc });
|
|
86
|
+
totalColumnsChanged++;
|
|
87
|
+
}
|
|
88
|
+
else if (sc && tc) {
|
|
89
|
+
const changes = [];
|
|
90
|
+
if (sc.genericType !== tc.genericType) {
|
|
91
|
+
changes.push(`Type: target "${tc.type}" (${tc.genericType}) -> source "${sc.type}" (${sc.genericType})`);
|
|
92
|
+
}
|
|
93
|
+
if (sc.notNull !== tc.notNull) {
|
|
94
|
+
changes.push(`NotNull: target ${tc.notNull} -> source ${sc.notNull}`);
|
|
95
|
+
}
|
|
96
|
+
if (sc.primaryKey !== tc.primaryKey) {
|
|
97
|
+
changes.push(`PK: target ${tc.primaryKey} -> source ${sc.primaryKey}`);
|
|
98
|
+
}
|
|
99
|
+
if (sc.defaultValue !== tc.defaultValue) {
|
|
100
|
+
changes.push(`Default: target "${tc.defaultValue}" -> source "${sc.defaultValue}"`);
|
|
101
|
+
}
|
|
102
|
+
if (changes.length > 0) {
|
|
103
|
+
colDiffs.push({ name: colName, action: 'modified', sourceCol: sc, targetCol: tc, changes });
|
|
104
|
+
totalColumnsChanged++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const pkChanged = JSON.stringify(src.primaryKeys.sort()) !== JSON.stringify(tgt.primaryKeys.sort());
|
|
109
|
+
const srcIdxNames = new Set(src.indexes.map((i) => i.name));
|
|
110
|
+
const tgtIdxNames = new Set(tgt.indexes.map((i) => i.name));
|
|
111
|
+
const indexesAdded = src.indexes.filter((i) => !tgtIdxNames.has(i.name)).map((i) => i.name);
|
|
112
|
+
const indexesRemoved = tgt.indexes.filter((i) => !srcIdxNames.has(i.name)).map((i) => i.name);
|
|
113
|
+
const srcFkDefs = src.foreignKeys.map((f) => `${f.from} -> ${f.table}.${f.to}`);
|
|
114
|
+
const tgtFkDefs = tgt.foreignKeys.map((f) => `${f.from} -> ${f.table}.${f.to}`);
|
|
115
|
+
const fksAdded = srcFkDefs.filter((f) => !tgtFkDefs.includes(f));
|
|
116
|
+
const fksRemoved = tgtFkDefs.filter((f) => !srcFkDefs.includes(f));
|
|
117
|
+
const isModified = colDiffs.length > 0 ||
|
|
118
|
+
pkChanged ||
|
|
119
|
+
indexesAdded.length > 0 ||
|
|
120
|
+
indexesRemoved.length > 0 ||
|
|
121
|
+
fksAdded.length > 0 ||
|
|
122
|
+
fksRemoved.length > 0;
|
|
123
|
+
if (isModified) {
|
|
124
|
+
modifiedTables++;
|
|
125
|
+
tableDiffs.push({
|
|
126
|
+
name,
|
|
127
|
+
action: 'modified',
|
|
128
|
+
sourceTable: src,
|
|
129
|
+
targetTable: tgt,
|
|
130
|
+
columnDiffs: colDiffs,
|
|
131
|
+
pkChanged,
|
|
132
|
+
indexesAdded,
|
|
133
|
+
indexesRemoved,
|
|
134
|
+
fksAdded,
|
|
135
|
+
fksRemoved,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
identicalTables++;
|
|
140
|
+
tableDiffs.push({
|
|
141
|
+
name,
|
|
142
|
+
action: 'unchanged',
|
|
143
|
+
sourceTable: src,
|
|
144
|
+
targetTable: tgt,
|
|
145
|
+
columnDiffs: [],
|
|
146
|
+
pkChanged: false,
|
|
147
|
+
indexesAdded: [],
|
|
148
|
+
indexesRemoved: [],
|
|
149
|
+
fksAdded: [],
|
|
150
|
+
fksRemoved: [],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const unifiedDiff = generateGitUnifiedDiff(sourceDbId, targetDbId, tableDiffs);
|
|
156
|
+
return {
|
|
157
|
+
sourceDbId,
|
|
158
|
+
targetDbId,
|
|
159
|
+
sourceDialect,
|
|
160
|
+
targetDialect,
|
|
161
|
+
tables: tableDiffs,
|
|
162
|
+
stats: {
|
|
163
|
+
addedTables,
|
|
164
|
+
removedTables,
|
|
165
|
+
modifiedTables,
|
|
166
|
+
identicalTables,
|
|
167
|
+
totalColumnsChanged,
|
|
168
|
+
},
|
|
169
|
+
unifiedDiff,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function generateGitUnifiedDiff(sourceDbId, targetDbId, diffs) {
|
|
173
|
+
const lines = [
|
|
174
|
+
`diff --git a/${targetDbId}/schema.sql b/${sourceDbId}/schema.sql`,
|
|
175
|
+
`--- a/${targetDbId} (target)`,
|
|
176
|
+
`+++ b/${sourceDbId} (source)`,
|
|
177
|
+
];
|
|
178
|
+
for (const t of diffs) {
|
|
179
|
+
if (t.action === 'unchanged')
|
|
180
|
+
continue;
|
|
181
|
+
lines.push(`@@ table: ${t.name} (${t.action.toUpperCase()}) @@`);
|
|
182
|
+
if (t.action === 'added') {
|
|
183
|
+
lines.push(`+ CREATE TABLE ${t.name}`);
|
|
184
|
+
for (const col of t.sourceTable?.columns || []) {
|
|
185
|
+
lines.push(`+ ${col.name}: ${col.type}${col.primaryKey ? ' PRIMARY KEY' : ''}${col.notNull ? ' NOT NULL' : ''}`);
|
|
186
|
+
}
|
|
187
|
+
for (const idx of t.indexesAdded) {
|
|
188
|
+
lines.push(`+ INDEX ${idx}`);
|
|
189
|
+
}
|
|
190
|
+
for (const fk of t.fksAdded) {
|
|
191
|
+
lines.push(`+ FK ${fk}`);
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (t.action === 'removed') {
|
|
196
|
+
lines.push(`- DROP TABLE ${t.name}`);
|
|
197
|
+
for (const col of t.targetTable?.columns || []) {
|
|
198
|
+
lines.push(`- ${col.name}: ${col.type}`);
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
for (const c of t.columnDiffs) {
|
|
203
|
+
if (c.action === 'added') {
|
|
204
|
+
lines.push(`+ column: ${c.name} (${c.sourceCol?.type || 'UNKNOWN'})${c.sourceCol?.notNull ? ' NOT NULL' : ''}`);
|
|
205
|
+
}
|
|
206
|
+
else if (c.action === 'removed') {
|
|
207
|
+
lines.push(`- column: ${c.name} (${c.targetCol?.type || 'UNKNOWN'})`);
|
|
208
|
+
}
|
|
209
|
+
else if (c.action === 'modified') {
|
|
210
|
+
lines.push(`~ column: ${c.name} [${c.changes?.join('; ')}]`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (t.pkChanged) {
|
|
214
|
+
lines.push(`~ PK changed: target [${t.targetTable?.primaryKeys.join(', ')}] -> source [${t.sourceTable?.primaryKeys.join(', ')}]`);
|
|
215
|
+
}
|
|
216
|
+
for (const idx of t.indexesAdded)
|
|
217
|
+
lines.push(`+ INDEX ${idx}`);
|
|
218
|
+
for (const idx of t.indexesRemoved)
|
|
219
|
+
lines.push(`- INDEX ${idx}`);
|
|
220
|
+
for (const fk of t.fksAdded)
|
|
221
|
+
lines.push(`+ FK ${fk}`);
|
|
222
|
+
for (const fk of t.fksRemoved)
|
|
223
|
+
lines.push(`- FK ${fk}`);
|
|
224
|
+
}
|
|
225
|
+
if (lines.length === 3) {
|
|
226
|
+
lines.push(' Schemas are identical. No differences found.');
|
|
227
|
+
}
|
|
228
|
+
return lines.join('\n');
|
|
229
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare class AppError extends Error{readonly status:number;readonly details?:Record<string,unknown>;constructor(message:string,status?:number,details?:Record<string,unknown>);}export declare class NotFoundError extends AppError{constructor(message?:string,details?:Record<string,unknown>);}export declare class BadRequestError extends AppError{constructor(message:string,details?:Record<string,unknown>);}export declare class ForbiddenError extends AppError{constructor(message?:string,details?:Record<string,unknown>);}export declare class ConflictError extends AppError{constructor(message:string,details?:Record<string,unknown>);}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConflictError = exports.ForbiddenError = exports.BadRequestError = exports.NotFoundError = exports.AppError = void 0;
|
|
4
|
+
class AppError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
details;
|
|
7
|
+
constructor(message, status = 500, details) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = this.constructor.name;
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.details = details;
|
|
12
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.AppError = AppError;
|
|
16
|
+
class NotFoundError extends AppError {
|
|
17
|
+
constructor(message = 'Resource not found.', details) {
|
|
18
|
+
super(message, 404, details);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.NotFoundError = NotFoundError;
|
|
22
|
+
class BadRequestError extends AppError {
|
|
23
|
+
constructor(message, details) {
|
|
24
|
+
super(message, 400, details);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.BadRequestError = BadRequestError;
|
|
28
|
+
class ForbiddenError extends AppError {
|
|
29
|
+
constructor(message = 'Database is open in read-only mode — write operations are disabled.', details) {
|
|
30
|
+
super(message, 403, details);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.ForbiddenError = ForbiddenError;
|
|
34
|
+
class ConflictError extends AppError {
|
|
35
|
+
constructor(message, details) {
|
|
36
|
+
super(message, 409, details);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.ConflictError = ConflictError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './result';export * from './errors';export * from './context';export * from './router';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./result"), exports);
|
|
18
|
+
__exportStar(require("./errors"), exports);
|
|
19
|
+
__exportStar(require("./context"), exports);
|
|
20
|
+
__exportStar(require("./router"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type SQLInputValue=null|number|bigint|string|boolean|Uint8Array|Buffer|Date;export interface Result<T=unknown>{success:boolean;data?:T;error?:string;}export interface MutationResult{changes?:number;lastInsertRowid?:number|null;}export declare function okResult<T>(data?:T):Result<T>;export declare function errResult<T=never>(error:string):Result<T>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.okResult = okResult;
|
|
4
|
+
exports.errResult = errResult;
|
|
5
|
+
function okResult(data) {
|
|
6
|
+
return { success: true, ...(data !== undefined ? { data } : {}) };
|
|
7
|
+
}
|
|
8
|
+
function errResult(error) {
|
|
9
|
+
return { success: false, error };
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type {Request,Response,NextFunction} from 'express';import type {DatabaseContext} from './context';import type {Logger} from '../utils/logger';import type {DbManager} from '../db/manager';export type RequestParams=Record<string,string>;export interface RouteContext{getContext:(req:Request<any>)=>DatabaseContext;getNamedContext?:(id:string,readonlyOverride?:boolean)=>DatabaseContext;manager?:DbManager;logger:Logger;}export declare const ok:(res:Response,data:unknown,status?:number)=>Response;export declare const fail:(res:Response,error:string,status?:number,details?:Record<string,unknown>|null)=>Response;export declare const notFound:(res:Response,message:string)=>void;export declare const wrap:<P=RequestParams>(fn:(req:Request<P>,res:Response,next?:NextFunction)=>unknown)=>(req:Request<P>,res:Response,next?:NextFunction)=>Promise<void>;export declare function initPageLocals(res:Response,dbOrCtx:{dialect:string|{name:string;};path:string;isReadOnly:boolean;},dbId:string,allNames:string[]):{tables:string[];};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrap = exports.notFound = exports.fail = exports.ok = void 0;
|
|
4
|
+
exports.initPageLocals = initPageLocals;
|
|
5
|
+
const common_1 = require("../utils/common");
|
|
6
|
+
const ok = (res, data, status = 200) => res.status(status).json({ success: true, data });
|
|
7
|
+
exports.ok = ok;
|
|
8
|
+
const fail = (res, error, status = 400, details) => res.status(status).json({ success: false, error, ...(details ? { details } : {}) });
|
|
9
|
+
exports.fail = fail;
|
|
10
|
+
const notFound = (res, message) => {
|
|
11
|
+
res.status(404).render('pages/error', { title: 'Not found', status: 404, error: message });
|
|
12
|
+
};
|
|
13
|
+
exports.notFound = notFound;
|
|
14
|
+
const wrap = (fn) => async (req, res, next) => {
|
|
15
|
+
try {
|
|
16
|
+
await fn(req, res, next);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
(0, exports.fail)(res, (0, common_1.errorMessage)(err), 500);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
exports.wrap = wrap;
|
|
23
|
+
function initPageLocals(res, dbOrCtx, dbId, allNames) {
|
|
24
|
+
const dialectName = typeof dbOrCtx.dialect === 'string' ? dbOrCtx.dialect : dbOrCtx.dialect.name;
|
|
25
|
+
const isPostgres = dialectName === 'postgres';
|
|
26
|
+
const isSqlite = dialectName === 'sqlite';
|
|
27
|
+
res.locals.dbId = dbId;
|
|
28
|
+
res.locals.dbPath = dbOrCtx.path;
|
|
29
|
+
res.locals.tables = allNames;
|
|
30
|
+
res.locals.dialect = dialectName;
|
|
31
|
+
res.locals.isPostgres = isPostgres;
|
|
32
|
+
res.locals.isSqlite = isSqlite;
|
|
33
|
+
res.locals.dialectName = isPostgres ? 'PostgreSQL' : 'SQLite';
|
|
34
|
+
res.locals.readonly = dbOrCtx.isReadOnly;
|
|
35
|
+
res.locals.databasesMode = false;
|
|
36
|
+
return { tables: allNames };
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type {ForeignKeyInfo,TableInfoData} from '../../db/types';export type SupportedDialect='sqlite'|'postgres';export interface NormalizedColumn{name:string;type:string;genericType:'integer'|'text'|'boolean'|'real'|'blob'|'datetime'|'json';primaryKey:boolean;autoIncrement:boolean;notNull:boolean;defaultValue:string|null;unique:boolean;foreignKey:{table:string;column:string;onDelete?:string;onUpdate?:string;}|null;}export interface NormalizedIndex{name:string;unique:boolean;columns:string[];}export interface NormalizedTable{name:string;columns:NormalizedColumn[];primaryKeys:string[];foreignKeys:ForeignKeyInfo[];indexes:NormalizedIndex[];}export declare function categorizeType(rawType:string):NormalizedColumn['genericType'];export declare function mapColumnToDialect(col:NormalizedColumn,targetDialect:SupportedDialect):string;export declare function normalizeTableInfo(info:TableInfoData,dialect:SupportedDialect):NormalizedTable;export declare function convertValueForDialect(value:unknown,genericType:NormalizedColumn['genericType'],targetDialect:SupportedDialect):unknown;export declare function mapDefaultValueForDialect(defaultValue:string|null|undefined,genericType:NormalizedColumn['genericType'],targetDialect:SupportedDialect):string|null;export declare function generateCreateTableForDialect(table:NormalizedTable,targetDialect:SupportedDialect):string;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.categorizeType = categorizeType;
|
|
4
|
+
exports.mapColumnToDialect = mapColumnToDialect;
|
|
5
|
+
exports.normalizeTableInfo = normalizeTableInfo;
|
|
6
|
+
exports.convertValueForDialect = convertValueForDialect;
|
|
7
|
+
exports.mapDefaultValueForDialect = mapDefaultValueForDialect;
|
|
8
|
+
exports.generateCreateTableForDialect = generateCreateTableForDialect;
|
|
9
|
+
function categorizeType(rawType) {
|
|
10
|
+
const t = (rawType || '').toLowerCase().trim();
|
|
11
|
+
if (t.includes('int') || t.includes('serial') || t === 'smallint' || t === 'bigint')
|
|
12
|
+
return 'integer';
|
|
13
|
+
if (t.includes('bool'))
|
|
14
|
+
return 'boolean';
|
|
15
|
+
if (t.includes('float') || t.includes('double') || t.includes('real') || t.includes('numeric') || t.includes('decimal'))
|
|
16
|
+
return 'real';
|
|
17
|
+
if (t.includes('blob') || t.includes('bytea') || t.includes('binary'))
|
|
18
|
+
return 'blob';
|
|
19
|
+
if (t.includes('date') || t.includes('time'))
|
|
20
|
+
return 'datetime';
|
|
21
|
+
if (t.includes('json'))
|
|
22
|
+
return 'json';
|
|
23
|
+
return 'text';
|
|
24
|
+
}
|
|
25
|
+
function mapColumnToDialect(col, targetDialect) {
|
|
26
|
+
if (targetDialect === 'postgres') {
|
|
27
|
+
if (col.autoIncrement) {
|
|
28
|
+
return col.type.toLowerCase().includes('big') ? 'BIGSERIAL' : 'SERIAL';
|
|
29
|
+
}
|
|
30
|
+
switch (col.genericType) {
|
|
31
|
+
case 'integer':
|
|
32
|
+
return col.type.toLowerCase().includes('big') ? 'BIGINT' : 'INTEGER';
|
|
33
|
+
case 'boolean':
|
|
34
|
+
return 'BOOLEAN';
|
|
35
|
+
case 'real':
|
|
36
|
+
return 'DOUBLE PRECISION';
|
|
37
|
+
case 'blob':
|
|
38
|
+
return 'BYTEA';
|
|
39
|
+
case 'datetime':
|
|
40
|
+
return 'TIMESTAMP WITH TIME ZONE';
|
|
41
|
+
case 'json':
|
|
42
|
+
return 'JSONB';
|
|
43
|
+
case 'text':
|
|
44
|
+
default:
|
|
45
|
+
return 'TEXT';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (col.primaryKey && col.autoIncrement) {
|
|
49
|
+
return 'INTEGER PRIMARY KEY AUTOINCREMENT';
|
|
50
|
+
}
|
|
51
|
+
switch (col.genericType) {
|
|
52
|
+
case 'integer':
|
|
53
|
+
return 'INTEGER';
|
|
54
|
+
case 'boolean':
|
|
55
|
+
return 'INTEGER';
|
|
56
|
+
case 'real':
|
|
57
|
+
return 'REAL';
|
|
58
|
+
case 'blob':
|
|
59
|
+
return 'BLOB';
|
|
60
|
+
case 'datetime':
|
|
61
|
+
case 'json':
|
|
62
|
+
case 'text':
|
|
63
|
+
default:
|
|
64
|
+
return 'TEXT';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function normalizeTableInfo(info, dialect) {
|
|
68
|
+
const uniqueCols = new Set();
|
|
69
|
+
const pks = new Set(info.primaryKey || []);
|
|
70
|
+
for (const idx of info.indexes || []) {
|
|
71
|
+
const cols = Array.isArray(idx.columns) ? idx.columns.filter(Boolean) : [];
|
|
72
|
+
if (idx.unique && cols.length === 1 && !pks.has(cols[0])) {
|
|
73
|
+
uniqueCols.add(cols[0]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const columns = (info.columns || []).map((col) => {
|
|
77
|
+
const isPk = col.pk > 0 || pks.has(col.name);
|
|
78
|
+
const rawType = (col.type || '').toUpperCase();
|
|
79
|
+
const genericType = categorizeType(rawType);
|
|
80
|
+
const isAutoIncrement = (isPk && genericType === 'integer' && dialect === 'sqlite' && Boolean(info.sql?.toLowerCase().includes('autoincrement'))) ||
|
|
81
|
+
rawType.includes('SERIAL') ||
|
|
82
|
+
Boolean(col.dflt_value?.includes('nextval(')) ||
|
|
83
|
+
Boolean(info.sql?.toLowerCase().includes('generated always as identity'));
|
|
84
|
+
const fk = (info.foreignKeys || []).find((f) => f.from === col.name);
|
|
85
|
+
return {
|
|
86
|
+
name: col.name,
|
|
87
|
+
type: col.type,
|
|
88
|
+
genericType,
|
|
89
|
+
primaryKey: isPk,
|
|
90
|
+
autoIncrement: isAutoIncrement,
|
|
91
|
+
notNull: Boolean(col.notnull),
|
|
92
|
+
defaultValue: col.dflt_value,
|
|
93
|
+
unique: uniqueCols.has(col.name),
|
|
94
|
+
foreignKey: fk
|
|
95
|
+
? {
|
|
96
|
+
table: fk.table,
|
|
97
|
+
column: fk.to || '',
|
|
98
|
+
onDelete: fk.on_delete,
|
|
99
|
+
onUpdate: fk.on_update,
|
|
100
|
+
}
|
|
101
|
+
: null,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const indexes = (info.indexes || [])
|
|
105
|
+
.filter((ix) => !ix.name?.startsWith('sqlite_') && ix.origin !== 'pk' && (!ix.name?.endsWith('_pkey') || !ix.unique))
|
|
106
|
+
.map((ix) => {
|
|
107
|
+
let cols = [];
|
|
108
|
+
if (Array.isArray(ix.columns)) {
|
|
109
|
+
cols = ix.columns.filter((c) => typeof c === 'string' && c.trim().length > 0);
|
|
110
|
+
}
|
|
111
|
+
else if (typeof ix.columns === 'string') {
|
|
112
|
+
cols = [ix.columns];
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
name: ix.name || `idx_${info.table}_${cols.join('_') || 'col'}`,
|
|
116
|
+
unique: Boolean(ix.unique),
|
|
117
|
+
columns: cols,
|
|
118
|
+
};
|
|
119
|
+
})
|
|
120
|
+
.filter((ix) => ix.columns.length > 0);
|
|
121
|
+
return {
|
|
122
|
+
name: info.table,
|
|
123
|
+
columns,
|
|
124
|
+
primaryKeys: info.primaryKey || [],
|
|
125
|
+
foreignKeys: info.foreignKeys || [],
|
|
126
|
+
indexes,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function convertValueForDialect(value, genericType, targetDialect) {
|
|
130
|
+
if (value === null || value === undefined)
|
|
131
|
+
return null;
|
|
132
|
+
if (targetDialect === 'postgres') {
|
|
133
|
+
if (genericType === 'boolean') {
|
|
134
|
+
if (typeof value === 'boolean')
|
|
135
|
+
return value;
|
|
136
|
+
if (typeof value === 'number')
|
|
137
|
+
return value !== 0;
|
|
138
|
+
if (typeof value === 'string')
|
|
139
|
+
return value === '1' || value.toLowerCase() === 'true';
|
|
140
|
+
return Boolean(value);
|
|
141
|
+
}
|
|
142
|
+
if (genericType === 'json') {
|
|
143
|
+
if (typeof value === 'object')
|
|
144
|
+
return JSON.stringify(value);
|
|
145
|
+
return String(value);
|
|
146
|
+
}
|
|
147
|
+
if (genericType === 'blob') {
|
|
148
|
+
if (Buffer.isBuffer(value))
|
|
149
|
+
return value;
|
|
150
|
+
if (value instanceof Uint8Array)
|
|
151
|
+
return Buffer.from(value);
|
|
152
|
+
return Buffer.from(String(value));
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
if (genericType === 'boolean') {
|
|
157
|
+
if (typeof value === 'boolean')
|
|
158
|
+
return value ? 1 : 0;
|
|
159
|
+
if (typeof value === 'number')
|
|
160
|
+
return value ? 1 : 0;
|
|
161
|
+
if (typeof value === 'string')
|
|
162
|
+
return value === 'true' || value === '1' ? 1 : 0;
|
|
163
|
+
return value ? 1 : 0;
|
|
164
|
+
}
|
|
165
|
+
if (genericType === 'json') {
|
|
166
|
+
if (typeof value === 'object')
|
|
167
|
+
return JSON.stringify(value);
|
|
168
|
+
return String(value);
|
|
169
|
+
}
|
|
170
|
+
if (genericType === 'datetime') {
|
|
171
|
+
if (value instanceof Date)
|
|
172
|
+
return value.toISOString();
|
|
173
|
+
return String(value);
|
|
174
|
+
}
|
|
175
|
+
if (genericType === 'blob') {
|
|
176
|
+
if (Buffer.isBuffer(value))
|
|
177
|
+
return value;
|
|
178
|
+
if (value instanceof Uint8Array)
|
|
179
|
+
return Buffer.from(value);
|
|
180
|
+
}
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
function mapDefaultValueForDialect(defaultValue, genericType, targetDialect) {
|
|
184
|
+
if (defaultValue === null || defaultValue === undefined)
|
|
185
|
+
return null;
|
|
186
|
+
let def = String(defaultValue).trim();
|
|
187
|
+
if (!def)
|
|
188
|
+
return null;
|
|
189
|
+
if (targetDialect === 'sqlite') {
|
|
190
|
+
def = def.replace(/::[a-zA-Z0-9_ "]+(\[\])?/g, '').trim();
|
|
191
|
+
if (/nextval\(.*?\)/i.test(def)) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (/^(now\(\)|current_timestamp|current_timestamp\(\)|clock_timestamp\(\)|statement_timestamp\(\)|transaction_timestamp\(\)|\('now'.*?\))$/i.test(def)) {
|
|
195
|
+
return 'CURRENT_TIMESTAMP';
|
|
196
|
+
}
|
|
197
|
+
if (/^(gen_random_uuid\(\)|uuid_generate_v\d\(\)|random\(\)|uuid\(\))$/i.test(def)) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
if (/^true$/i.test(def))
|
|
201
|
+
return '1';
|
|
202
|
+
if (/^false$/i.test(def))
|
|
203
|
+
return '0';
|
|
204
|
+
while (def.startsWith('(') && def.endsWith(')')) {
|
|
205
|
+
const inner = def.slice(1, -1).trim();
|
|
206
|
+
let depth = 0;
|
|
207
|
+
let okToStrip = true;
|
|
208
|
+
for (let i = 0; i < inner.length; i++) {
|
|
209
|
+
if (inner[i] === '(')
|
|
210
|
+
depth++;
|
|
211
|
+
else if (inner[i] === ')') {
|
|
212
|
+
depth--;
|
|
213
|
+
if (depth < 0) {
|
|
214
|
+
okToStrip = false;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (okToStrip && depth === 0) {
|
|
220
|
+
def = inner;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (/^(now\(\)|current_timestamp|current_timestamp\(\)|clock_timestamp\(\)|\('now'.*?\))$/i.test(def)) {
|
|
227
|
+
return 'CURRENT_TIMESTAMP';
|
|
228
|
+
}
|
|
229
|
+
if (/^true$/i.test(def))
|
|
230
|
+
return '1';
|
|
231
|
+
if (/^false$/i.test(def))
|
|
232
|
+
return '0';
|
|
233
|
+
if (/nextval\(.*?\)/i.test(def))
|
|
234
|
+
return null;
|
|
235
|
+
if (/^(gen_random_uuid\(\)|uuid_generate_v\d\(\))$/i.test(def))
|
|
236
|
+
return null;
|
|
237
|
+
if (/^[a-zA-Z0-9_]+\(.*\)$/.test(def) && !/^(current_timestamp|current_date|current_time)$/i.test(def)) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return def;
|
|
241
|
+
}
|
|
242
|
+
if (targetDialect === 'postgres') {
|
|
243
|
+
if (genericType === 'boolean') {
|
|
244
|
+
if (def === '1' || def === "'1'")
|
|
245
|
+
return 'TRUE';
|
|
246
|
+
if (def === '0' || def === "'0'")
|
|
247
|
+
return 'FALSE';
|
|
248
|
+
}
|
|
249
|
+
if (/nextval\(.*?\)/i.test(def)) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return def;
|
|
253
|
+
}
|
|
254
|
+
return def;
|
|
255
|
+
}
|
|
256
|
+
function generateCreateTableForDialect(table, targetDialect) {
|
|
257
|
+
const lines = [];
|
|
258
|
+
const q = targetDialect === 'postgres' ? (s) => `"${s}"` : (s) => `\`${s}\``;
|
|
259
|
+
const singleAutoPk = targetDialect === 'sqlite' &&
|
|
260
|
+
table.columns.filter((c) => c.primaryKey).length === 1 &&
|
|
261
|
+
table.columns.find((c) => c.primaryKey)?.autoIncrement;
|
|
262
|
+
for (const col of table.columns) {
|
|
263
|
+
let colSql = `${q(col.name)} ${mapColumnToDialect(col, targetDialect)}`;
|
|
264
|
+
const isPgSerial = targetDialect === 'postgres' && col.autoIncrement;
|
|
265
|
+
if (singleAutoPk && col.primaryKey) {
|
|
266
|
+
}
|
|
267
|
+
else if (isPgSerial) {
|
|
268
|
+
if (col.unique && !col.primaryKey)
|
|
269
|
+
colSql += ' UNIQUE';
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
if (col.notNull)
|
|
273
|
+
colSql += ' NOT NULL';
|
|
274
|
+
if (col.unique && !col.primaryKey)
|
|
275
|
+
colSql += ' UNIQUE';
|
|
276
|
+
const mappedDefault = mapDefaultValueForDialect(col.defaultValue, col.genericType, targetDialect);
|
|
277
|
+
if (mappedDefault !== null && mappedDefault !== undefined) {
|
|
278
|
+
colSql += ` DEFAULT ${mappedDefault}`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
lines.push(colSql);
|
|
282
|
+
}
|
|
283
|
+
if (!singleAutoPk && table.primaryKeys.length > 0) {
|
|
284
|
+
const validPks = table.primaryKeys.filter((pk) => table.columns.some((c) => c.name === pk));
|
|
285
|
+
if (validPks.length > 0) {
|
|
286
|
+
lines.push(`PRIMARY KEY (${validPks.map(q).join(', ')})`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (targetDialect === 'sqlite') {
|
|
290
|
+
for (const fk of table.foreignKeys) {
|
|
291
|
+
if (fk.from && fk.to && fk.table) {
|
|
292
|
+
const targetTable = fk.table.includes('.') ? fk.table.split('.').pop() : fk.table;
|
|
293
|
+
let fkSql = `FOREIGN KEY (${q(fk.from)}) REFERENCES ${q(targetTable)} (${q(fk.to)})`;
|
|
294
|
+
if (fk.on_delete && fk.on_delete !== 'NO ACTION')
|
|
295
|
+
fkSql += ` ON DELETE ${fk.on_delete}`;
|
|
296
|
+
if (fk.on_update && fk.on_update !== 'NO ACTION')
|
|
297
|
+
fkSql += ` ON UPDATE ${fk.on_update}`;
|
|
298
|
+
lines.push(fkSql);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return `CREATE TABLE ${q(table.name)} (\n ${lines.join(',\n ')}\n);`;
|
|
303
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export interface MaskingRule{columnPattern:string;strategy:'email'|'password'|'phone'|'name'|'redact'|'hash';customValue?:string;}export declare const DEFAULT_MASKING_RULES:MaskingRule[];export declare function maskRowData(row:Record<string,unknown>,rules?:MaskingRule[],rowIndex?:number):Record<string,unknown>;
|