admindb 2.1.0 → 2.2.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/README.md +41 -12
- package/dist/app.d.ts +1 -69
- package/dist/app.js +3 -30
- package/dist/auth/config.d.ts +1 -19
- package/dist/auth/config.js +0 -26
- package/dist/auth/crypto.d.ts +1 -27
- package/dist/auth/crypto.js +1 -26
- package/dist/auth/index.d.ts +1 -5
- package/dist/auth/index.js +0 -1
- package/dist/auth/middleware.d.ts +1 -14
- package/dist/auth/middleware.js +0 -15
- package/dist/auth/routes.d.ts +1 -7
- package/dist/auth/routes.js +0 -8
- package/dist/auth/types.d.ts +1 -35
- package/dist/auth/types.js +1 -5
- package/dist/cli/args.d.ts +1 -45
- package/dist/cli/args.js +3 -11
- package/dist/cli/config.d.ts +1 -56
- package/dist/cli/config.js +0 -5
- package/dist/cli/index.d.ts +1 -4
- package/dist/cli/index.js +0 -1
- package/dist/cli/runner.d.ts +1 -1
- package/dist/cli/runner.js +6 -17
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +0 -1
- package/dist/data/chain.d.ts +1 -0
- package/dist/data/chain.js +707 -0
- package/dist/data/datasets.d.ts +1 -17
- package/dist/data/datasets.js +0 -4
- package/dist/data/detector.d.ts +1 -30
- package/dist/data/detector.js +53 -14
- package/dist/data/engine.d.ts +1 -10
- package/dist/data/engine.js +82 -48
- package/dist/data/index.d.ts +1 -5
- package/dist/data/index.js +1 -1
- package/dist/data/strategies.d.ts +1 -30
- package/dist/data/strategies.js +4 -8
- package/dist/data/types.d.ts +1 -73
- package/dist/data/types.js +0 -3
- package/dist/db/database.d.ts +1 -119
- package/dist/db/database.js +20 -14
- package/dist/db/export.d.ts +1 -12
- package/dist/db/export.js +54 -6
- package/dist/db/filters.d.ts +1 -22
- package/dist/db/filters.js +0 -16
- package/dist/db/index.d.ts +1 -8
- package/dist/db/index.js +0 -1
- package/dist/db/introspection.d.ts +1 -6
- package/dist/db/introspection.js +0 -5
- package/dist/db/manager.d.ts +1 -81
- package/dist/db/manager.js +13 -59
- package/dist/db/migrations.d.ts +1 -5
- package/dist/db/migrations.js +2 -5
- package/dist/db/postgres.d.ts +1 -85
- package/dist/db/postgres.js +21 -31
- package/dist/db/types.d.ts +1 -206
- package/dist/db/types.js +0 -1
- package/dist/index.d.ts +1 -23
- package/dist/index.js +0 -14
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/api.js +1 -47
- package/dist/public/js/app.js +1 -262
- package/dist/public/js/browse.js +1 -1371
- package/dist/public/js/common.js +1 -0
- package/dist/public/js/databases.js +1 -235
- package/dist/public/js/designer.js +1 -258
- package/dist/public/js/forms.js +1 -826
- package/dist/public/js/home.js +1 -0
- package/dist/public/js/inspector.js +1 -638
- package/dist/public/js/query.js +1 -322
- package/dist/public/js/schema.js +1 -447
- package/dist/public/js/seed.js +1 -1090
- package/dist/routes/api/erd.d.ts +1 -0
- package/dist/routes/api/erd.js +50 -0
- package/dist/routes/api/helpers.d.ts +1 -42
- package/dist/routes/api/helpers.js +33 -3
- package/dist/routes/api/import-export.d.ts +1 -3
- package/dist/routes/api/import-export.js +0 -2
- package/dist/routes/api/index.d.ts +1 -4
- package/dist/routes/api/index.js +15 -4
- package/dist/routes/api/query.d.ts +1 -3
- package/dist/routes/api/query.js +13 -11
- package/dist/routes/api/rows.d.ts +1 -3
- package/dist/routes/api/rows.js +0 -11
- package/dist/routes/api/seed.d.ts +1 -3
- package/dist/routes/api/seed.js +79 -8
- package/dist/routes/api/tables.d.ts +1 -3
- package/dist/routes/api/tables.js +77 -6
- package/dist/routes/databases.d.ts +1 -26
- package/dist/routes/databases.js +1 -3
- package/dist/routes/index.d.ts +1 -3
- package/dist/routes/index.js +0 -1
- package/dist/routes/pages.d.ts +1 -9
- package/dist/routes/pages.js +18 -11
- package/dist/serverless.d.ts +1 -40
- package/dist/serverless.js +3 -39
- package/dist/sql/classifier.d.ts +1 -19
- package/dist/sql/classifier.js +0 -13
- package/dist/sql/error-analyzer.d.ts +1 -0
- package/dist/sql/error-analyzer.js +550 -0
- package/dist/sql/generator.d.ts +1 -81
- package/dist/sql/generator.js +0 -29
- package/dist/sql/index.d.ts +1 -2
- package/dist/sql/index.js +0 -1
- package/dist/types/api.d.ts +1 -412
- package/dist/types/api.js +0 -7
- package/dist/utils/colors.d.ts +1 -40
- package/dist/utils/colors.js +0 -10
- package/dist/utils/common.d.ts +1 -50
- package/dist/utils/common.js +19 -23
- package/dist/utils/csv.d.ts +1 -15
- package/dist/utils/csv.js +0 -13
- package/dist/utils/datatype.d.ts +1 -49
- package/dist/utils/datatype.js +0 -41
- package/dist/utils/icons.d.ts +1 -11
- package/dist/utils/icons.js +25 -16
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +0 -1
- package/dist/utils/logger.d.ts +1 -10
- package/dist/utils/logger.js +0 -4
- package/dist/views/layouts/main.hbs +14 -5
- package/dist/views/pages/databases.hbs +0 -18
- package/dist/views/pages/designer.hbs +0 -10
- package/dist/views/pages/erd.hbs +1241 -0
- package/dist/views/pages/error.hbs +0 -2
- package/dist/views/pages/form.hbs +0 -5
- package/dist/views/pages/home.hbs +98 -10
- package/dist/views/pages/info.hbs +107 -0
- package/dist/views/pages/login.hbs +0 -13
- package/dist/views/pages/query.hbs +54 -21
- package/dist/views/pages/schema.hbs +0 -20
- package/dist/views/pages/seed.hbs +435 -202
- package/dist/views/pages/table.hbs +0 -14
- package/dist/views/partials/icon.hbs +0 -1
- package/dist/views/partials/navbar.hbs +0 -6
- package/dist/views/partials/sidebar.hbs +12 -8
- package/docs/API.md +6 -1
- package/docs/EXAMPLES.md +3 -0
- package/package.json +7 -5
- package/dist/app.js.map +0 -1
- package/dist/auth/config.js.map +0 -1
- package/dist/auth/crypto.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/middleware.js.map +0 -1
- package/dist/auth/routes.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/config.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/runner.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/data/datasets.js.map +0 -1
- package/dist/data/detector.js.map +0 -1
- package/dist/data/engine.js.map +0 -1
- package/dist/data/index.js.map +0 -1
- package/dist/data/strategies.js.map +0 -1
- package/dist/data/types.js.map +0 -1
- package/dist/db/database.js.map +0 -1
- package/dist/db/export.js.map +0 -1
- package/dist/db/filters.js.map +0 -1
- package/dist/db/index.js.map +0 -1
- package/dist/db/introspection.js.map +0 -1
- package/dist/db/manager.js.map +0 -1
- package/dist/db/migrations.js.map +0 -1
- package/dist/db/postgres.js.map +0 -1
- package/dist/db/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/public/css/input.css +0 -302
- package/dist/public/js/icons.js +0 -51
- package/dist/routes/api/helpers.js.map +0 -1
- package/dist/routes/api/import-export.js.map +0 -1
- package/dist/routes/api/index.js.map +0 -1
- package/dist/routes/api/query.js.map +0 -1
- package/dist/routes/api/rows.js.map +0 -1
- package/dist/routes/api/seed.js.map +0 -1
- package/dist/routes/api/tables.js.map +0 -1
- package/dist/routes/databases.js.map +0 -1
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/pages.js.map +0 -1
- package/dist/serverless.js.map +0 -1
- package/dist/sql/classifier.js.map +0 -1
- package/dist/sql/generator.js.map +0 -1
- package/dist/sql/index.js.map +0 -1
- package/dist/types/api.js.map +0 -1
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/common.js.map +0 -1
- package/dist/utils/csv.js.map +0 -1
- package/dist/utils/datatype.js.map +0 -1
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
package/dist/db/manager.js
CHANGED
|
@@ -11,25 +11,17 @@ const postgres_1 = require("./postgres");
|
|
|
11
11
|
const logger_1 = require("../utils/logger");
|
|
12
12
|
const common_1 = require("../utils/common");
|
|
13
13
|
const DB_EXTENSIONS = ['.db', '.sqlite', '.sqlite3'];
|
|
14
|
-
/**
|
|
15
|
-
* Manages multiple SQLite database files and PostgreSQL database connections.
|
|
16
|
-
*
|
|
17
|
-
* Databases come from three sources:
|
|
18
|
-
* - a `connections` map — named PostgreSQL or SQLite connection strings (e.g. from JSON config),
|
|
19
|
-
* - a `dir` — every SQLite file with a known extension is scanned in,
|
|
20
|
-
* - an explicit `files[]` array — specific file locations or connection strings.
|
|
21
|
-
*/
|
|
22
14
|
class DbManager {
|
|
23
15
|
logger;
|
|
24
16
|
dir;
|
|
25
17
|
createDir;
|
|
26
18
|
readonlyMode;
|
|
27
|
-
files = [];
|
|
28
|
-
idByPath = new Map();
|
|
29
|
-
pathById = new Map();
|
|
30
|
-
nameById = new Map();
|
|
31
|
-
readonlyById = new Map();
|
|
32
|
-
openDbs = new Map();
|
|
19
|
+
files = [];
|
|
20
|
+
idByPath = new Map();
|
|
21
|
+
pathById = new Map();
|
|
22
|
+
nameById = new Map();
|
|
23
|
+
readonlyById = new Map();
|
|
24
|
+
openDbs = new Map();
|
|
33
25
|
constructor(options, logger) {
|
|
34
26
|
const opts = typeof options === 'string' ? { dir: options } : (options ?? {});
|
|
35
27
|
this.logger = logger ?? (0, logger_1.createLogger)('info', 'admindb');
|
|
@@ -47,7 +39,6 @@ class DbManager {
|
|
|
47
39
|
get directory() {
|
|
48
40
|
return this.dir ?? this.createDir;
|
|
49
41
|
}
|
|
50
|
-
/** True when this manager opens every database read-only. */
|
|
51
42
|
get isReadOnly() {
|
|
52
43
|
return this.readonlyMode;
|
|
53
44
|
}
|
|
@@ -85,8 +76,7 @@ class DbManager {
|
|
|
85
76
|
const raw = String(f ?? '').trim();
|
|
86
77
|
if (!raw)
|
|
87
78
|
continue;
|
|
88
|
-
|
|
89
|
-
if (isPg) {
|
|
79
|
+
if ((0, common_1.isPostgresConnectionString)(raw)) {
|
|
90
80
|
let name = 'postgres';
|
|
91
81
|
try {
|
|
92
82
|
const u = new URL(raw);
|
|
@@ -135,7 +125,6 @@ class DbManager {
|
|
|
135
125
|
this.pathById.set(id, abs);
|
|
136
126
|
this.nameById.set(id, base);
|
|
137
127
|
}
|
|
138
|
-
/** List all managed databases (metadata only — does not open them). */
|
|
139
128
|
list() {
|
|
140
129
|
const entries = [];
|
|
141
130
|
const seen = new Set();
|
|
@@ -143,22 +132,11 @@ class DbManager {
|
|
|
143
132
|
if (seen.has(id))
|
|
144
133
|
continue;
|
|
145
134
|
seen.add(id);
|
|
146
|
-
|
|
147
|
-
if (isPg) {
|
|
148
|
-
let sanitized = target;
|
|
149
|
-
try {
|
|
150
|
-
const u = new URL(target);
|
|
151
|
-
if (u.password)
|
|
152
|
-
u.password = '****';
|
|
153
|
-
sanitized = u.toString();
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
sanitized = target.replace(/:([^:@]+)@/, ':****@');
|
|
157
|
-
}
|
|
135
|
+
if ((0, common_1.isPostgresConnectionString)(target)) {
|
|
158
136
|
entries.push({
|
|
159
137
|
id,
|
|
160
138
|
name: this.nameById.get(id) ?? id,
|
|
161
|
-
path:
|
|
139
|
+
path: (0, common_1.sanitizeConnectionString)(target),
|
|
162
140
|
dialect: 'postgres',
|
|
163
141
|
size: 0,
|
|
164
142
|
modified: 'Connected',
|
|
@@ -174,7 +152,6 @@ class DbManager {
|
|
|
174
152
|
modified = st.mtime.toISOString();
|
|
175
153
|
}
|
|
176
154
|
catch {
|
|
177
|
-
/* ignore */
|
|
178
155
|
}
|
|
179
156
|
entries.push({
|
|
180
157
|
id,
|
|
@@ -192,11 +169,9 @@ class DbManager {
|
|
|
192
169
|
has(id) {
|
|
193
170
|
return this.pathById.has(id);
|
|
194
171
|
}
|
|
195
|
-
/** True if global manager is in readonly mode or this database is explicitly marked read-only. */
|
|
196
172
|
isDbReadOnly(id) {
|
|
197
173
|
return this.readonlyMode || (this.readonlyById.get(id) ?? false);
|
|
198
174
|
}
|
|
199
|
-
/** Set or toggle read-only mode for a specific database id. */
|
|
200
175
|
setReadonly(id, readonly) {
|
|
201
176
|
if (this.readonlyMode && !readonly) {
|
|
202
177
|
throw new Error('Manager is in global read-only mode — databases cannot be made writable.');
|
|
@@ -205,19 +180,16 @@ class DbManager {
|
|
|
205
180
|
if (current === readonly)
|
|
206
181
|
return;
|
|
207
182
|
this.readonlyById.set(id, readonly);
|
|
208
|
-
// Close existing connection instance so it is re-opened with new readonly mode on next use
|
|
209
183
|
const db = this.openDbs.get(id);
|
|
210
184
|
if (db) {
|
|
211
185
|
try {
|
|
212
186
|
db.close();
|
|
213
187
|
}
|
|
214
188
|
catch {
|
|
215
|
-
/* ignore */
|
|
216
189
|
}
|
|
217
190
|
this.openDbs.delete(id);
|
|
218
191
|
}
|
|
219
192
|
}
|
|
220
|
-
/** Open (and cache) a database by id; creates the file if it is new SQLite database. */
|
|
221
193
|
open(id, readonlyOverride) {
|
|
222
194
|
const target = this.pathById.get(id);
|
|
223
195
|
if (!target)
|
|
@@ -229,14 +201,12 @@ class DbManager {
|
|
|
229
201
|
db.close();
|
|
230
202
|
}
|
|
231
203
|
catch {
|
|
232
|
-
/* ignore */
|
|
233
204
|
}
|
|
234
205
|
this.openDbs.delete(id);
|
|
235
206
|
db = undefined;
|
|
236
207
|
}
|
|
237
208
|
if (!db) {
|
|
238
|
-
|
|
239
|
-
if (isPg) {
|
|
209
|
+
if ((0, common_1.isPostgresConnectionString)(target)) {
|
|
240
210
|
db = new postgres_1.PostgresDatabase(target, this.logger.child(`db:${id}`), { readonly: isRo });
|
|
241
211
|
}
|
|
242
212
|
else {
|
|
@@ -246,11 +216,6 @@ class DbManager {
|
|
|
246
216
|
}
|
|
247
217
|
return db;
|
|
248
218
|
}
|
|
249
|
-
/**
|
|
250
|
-
* Register and open an existing database file by path, returning its id.
|
|
251
|
-
* Used by the "Open an existing database" file browser. Validates that the
|
|
252
|
-
* file is a readable SQLite database before keeping it registered.
|
|
253
|
-
*/
|
|
254
219
|
openFile(absPath, readonly) {
|
|
255
220
|
const abs = node_path_1.default.resolve(String(absPath ?? '').trim());
|
|
256
221
|
if (!abs)
|
|
@@ -271,10 +236,9 @@ class DbManager {
|
|
|
271
236
|
if (readonly !== undefined)
|
|
272
237
|
this.readonlyById.set(id, readonly);
|
|
273
238
|
try {
|
|
274
|
-
this.open(id);
|
|
239
|
+
this.open(id);
|
|
275
240
|
}
|
|
276
241
|
catch (err) {
|
|
277
|
-
// Roll the registration back so a bad file doesn't linger in the list.
|
|
278
242
|
this.pathById.delete(id);
|
|
279
243
|
this.idByPath.delete(abs);
|
|
280
244
|
this.nameById.delete(id);
|
|
@@ -287,7 +251,6 @@ class DbManager {
|
|
|
287
251
|
get(id) {
|
|
288
252
|
return this.openDbs.get(id);
|
|
289
253
|
}
|
|
290
|
-
/** Count the tables in a database (opens it if necessary). */
|
|
291
254
|
async countTables(id) {
|
|
292
255
|
try {
|
|
293
256
|
const db = this.open(id);
|
|
@@ -298,7 +261,6 @@ class DbManager {
|
|
|
298
261
|
return 0;
|
|
299
262
|
}
|
|
300
263
|
}
|
|
301
|
-
/** Create a new (empty) SQLite database. Returns its id. */
|
|
302
264
|
create(name) {
|
|
303
265
|
if (this.readonlyMode)
|
|
304
266
|
throw new Error('Read-only mode — creating databases is disabled.');
|
|
@@ -310,10 +272,9 @@ class DbManager {
|
|
|
310
272
|
const id = this.idByPath.get(abs);
|
|
311
273
|
if (!id)
|
|
312
274
|
throw new Error('Failed to register new database.');
|
|
313
|
-
this.open(id);
|
|
275
|
+
this.open(id);
|
|
314
276
|
return id;
|
|
315
277
|
}
|
|
316
|
-
/** Dynamically register a named PostgreSQL or SQLite connection string. Returns its id. */
|
|
317
278
|
addConnection(name, connectionString, readonly) {
|
|
318
279
|
if (this.readonlyMode)
|
|
319
280
|
throw new Error('Read-only mode — adding connections is disabled.');
|
|
@@ -338,7 +299,6 @@ class DbManager {
|
|
|
338
299
|
this.readonlyById.set(id, readonly);
|
|
339
300
|
return id;
|
|
340
301
|
}
|
|
341
|
-
/** Close and delete a database file (including WAL/SHM sidecars). */
|
|
342
302
|
remove(id) {
|
|
343
303
|
if (this.readonlyMode)
|
|
344
304
|
throw new Error('Read-only mode — deleting databases is disabled.');
|
|
@@ -351,7 +311,6 @@ class DbManager {
|
|
|
351
311
|
db.close();
|
|
352
312
|
}
|
|
353
313
|
catch {
|
|
354
|
-
/* ignore */
|
|
355
314
|
}
|
|
356
315
|
this.openDbs.delete(id);
|
|
357
316
|
}
|
|
@@ -360,14 +319,12 @@ class DbManager {
|
|
|
360
319
|
this.nameById.delete(id);
|
|
361
320
|
this.readonlyById.delete(id);
|
|
362
321
|
this.files = this.files.filter((p) => p !== target);
|
|
363
|
-
|
|
364
|
-
if (!isPg) {
|
|
322
|
+
if (!(0, common_1.isPostgresConnectionString)(target)) {
|
|
365
323
|
for (const suffix of ['', '-wal', '-shm']) {
|
|
366
324
|
try {
|
|
367
325
|
(0, node_fs_1.unlinkSync)(target + suffix);
|
|
368
326
|
}
|
|
369
327
|
catch {
|
|
370
|
-
/* ignore */
|
|
371
328
|
}
|
|
372
329
|
}
|
|
373
330
|
}
|
|
@@ -378,14 +335,12 @@ class DbManager {
|
|
|
378
335
|
db.close();
|
|
379
336
|
}
|
|
380
337
|
catch {
|
|
381
|
-
/* ignore */
|
|
382
338
|
}
|
|
383
339
|
}
|
|
384
340
|
this.openDbs.clear();
|
|
385
341
|
}
|
|
386
342
|
}
|
|
387
343
|
exports.DbManager = DbManager;
|
|
388
|
-
/** Turn a friendly name into a safe database file name (always ends in `.db`). */
|
|
389
344
|
function sanitizeFileName(name) {
|
|
390
345
|
let n = String(name ?? '').trim().replace(/[\\/]/g, '');
|
|
391
346
|
if (!n)
|
|
@@ -394,4 +349,3 @@ function sanitizeFileName(name) {
|
|
|
394
349
|
n += '.db';
|
|
395
350
|
return n;
|
|
396
351
|
}
|
|
397
|
-
//# sourceMappingURL=manager.js.map
|
package/dist/db/migrations.d.ts
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
import type Database from 'better-sqlite3';
|
|
2
|
-
import { type ColumnDef } from '../sql/generator';
|
|
3
|
-
export declare function modifyTableStructureSync(db: Database.Database, table: string, transformColumns: (columns: ColumnDef[]) => ColumnDef[], renamedOldCol?: string, renamedNewCol?: string): {
|
|
4
|
-
changes: number;
|
|
5
|
-
};
|
|
1
|
+
import type Database from 'better-sqlite3';import{type ColumnDef} from '../sql/generator';export declare function modifyTableStructureSync(db:Database.Database,table:string,transformColumns:(columns:ColumnDef[])=>ColumnDef[],renamedOldCol?:string,renamedNewCol?:string):{changes:number;};
|
package/dist/db/migrations.js
CHANGED
|
@@ -8,7 +8,6 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
8
8
|
throw new Error(`Table "${table}" does not exist.`);
|
|
9
9
|
const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
|
|
10
10
|
const indexRows = db.prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
|
|
11
|
-
// Discover which single columns are UNIQUE across the whole table (so existing uniqueness is preserved)
|
|
12
11
|
const uniqueCols = new Set();
|
|
13
12
|
for (const ix of indexRows) {
|
|
14
13
|
if (ix.unique) {
|
|
@@ -82,7 +81,6 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
82
81
|
db.exec(`CREATE ${uq}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(ix.name)} ON ${(0, generator_1.quoteIdentifier)(table)} (${ixColsStr});`);
|
|
83
82
|
}
|
|
84
83
|
catch {
|
|
85
|
-
/* ignore index recreation error */
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
const fkCheck = db.prepare('PRAGMA foreign_key_check;').all();
|
|
@@ -96,15 +94,14 @@ function modifyTableStructureSync(db, table, transformColumns, renamedOldCol, re
|
|
|
96
94
|
try {
|
|
97
95
|
db.exec('ROLLBACK;');
|
|
98
96
|
}
|
|
99
|
-
catch {
|
|
97
|
+
catch { }
|
|
100
98
|
try {
|
|
101
99
|
db.exec(`DROP TABLE IF EXISTS ${(0, generator_1.quoteIdentifier)(tempTable)};`);
|
|
102
100
|
}
|
|
103
|
-
catch {
|
|
101
|
+
catch { }
|
|
104
102
|
throw err;
|
|
105
103
|
}
|
|
106
104
|
finally {
|
|
107
105
|
db.exec('PRAGMA foreign_keys = ON;');
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
|
-
//# sourceMappingURL=migrations.js.map
|
package/dist/db/postgres.d.ts
CHANGED
|
@@ -1,85 +1 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ColumnDef, type IndexDef } from '../sql/generator';
|
|
3
|
-
import type { DatabaseDialect, IDatabase, MutationResult, PostgresOptions, QueryOptions, ReferencingTableInfo, Result, RowFilters, SavedQuery, SchemaInfo, SQLInputValue, TableInfoData, TableListItem, WhereClause } from './types';
|
|
4
|
-
export declare class PostgresDatabase implements IDatabase {
|
|
5
|
-
private pool;
|
|
6
|
-
private logger;
|
|
7
|
-
readonly path: string;
|
|
8
|
-
readonly isReadOnly: boolean;
|
|
9
|
-
readonly dialect: DatabaseDialect;
|
|
10
|
-
readonly schema: string;
|
|
11
|
-
constructor(connection: string | PostgresOptions, logger: Logger, options?: PostgresOptions);
|
|
12
|
-
private sanitizeConnectionString;
|
|
13
|
-
private readonlyBlocked;
|
|
14
|
-
close(): Promise<void>;
|
|
15
|
-
private tryRun;
|
|
16
|
-
/** Run a callback inside an atomic transaction on a dedicated client. */
|
|
17
|
-
private transaction;
|
|
18
|
-
private initSchema;
|
|
19
|
-
hasMultipleStatements(sql: string): boolean;
|
|
20
|
-
all(sql: string, params?: SQLInputValue[]): Promise<Result<unknown[]>>;
|
|
21
|
-
run(sql: string, params?: SQLInputValue[]): Promise<Result<MutationResult>>;
|
|
22
|
-
execResult(sql: string): Promise<Result<{
|
|
23
|
-
changes?: number;
|
|
24
|
-
}>>;
|
|
25
|
-
runWrite(sql: string): Promise<Result<{
|
|
26
|
-
changes?: number;
|
|
27
|
-
}>>;
|
|
28
|
-
listTables(): Promise<Result<TableListItem[]>>;
|
|
29
|
-
getTableInfo(table: string): Promise<Result<TableInfoData>>;
|
|
30
|
-
getRowCount(table: string, filters?: RowFilters): Promise<Result<number>>;
|
|
31
|
-
getRows(table: string, opts?: QueryOptions): Promise<Result<Record<string, unknown>[]>>;
|
|
32
|
-
getAllRows(table: string): Promise<Result<Record<string, unknown>[]>>;
|
|
33
|
-
getRow(table: string, where: WhereClause[]): Promise<Result<Record<string, unknown> | null>>;
|
|
34
|
-
insertRow(table: string, fields: WhereClause[]): Promise<Result<MutationResult>>;
|
|
35
|
-
insertRows(table: string, rows: WhereClause[][]): Promise<Result<{
|
|
36
|
-
inserted: number;
|
|
37
|
-
skipped: number;
|
|
38
|
-
}>>;
|
|
39
|
-
updateRow(table: string, fields: WhereClause[], where: WhereClause[]): Promise<Result<MutationResult>>;
|
|
40
|
-
updateRows(table: string, rows: {
|
|
41
|
-
fields: WhereClause[];
|
|
42
|
-
where: WhereClause[];
|
|
43
|
-
}[]): Promise<Result<{
|
|
44
|
-
updated: number;
|
|
45
|
-
}>>;
|
|
46
|
-
deleteRow(table: string, where: WhereClause[]): Promise<Result<MutationResult>>;
|
|
47
|
-
deleteRows(table: string, rows: WhereClause[][]): Promise<Result<{
|
|
48
|
-
deleted: number;
|
|
49
|
-
}>>;
|
|
50
|
-
getRowsByPks(table: string, pks: WhereClause[][]): Promise<Result<Record<string, unknown>[]>>;
|
|
51
|
-
getRowsByFk(table: string, column: string, value: unknown, limit?: number): Promise<Result<{
|
|
52
|
-
rows: Record<string, unknown>[];
|
|
53
|
-
total: number;
|
|
54
|
-
}>>;
|
|
55
|
-
getCreateStatement(table: string): Promise<Result<string | null>>;
|
|
56
|
-
listSavedQueries(): Promise<Result<SavedQuery[]>>;
|
|
57
|
-
saveQuery(name: string, sql: string): Promise<Result<MutationResult>>;
|
|
58
|
-
deleteSavedQuery(id: string | number): Promise<Result<MutationResult>>;
|
|
59
|
-
getSchema(table: string): Promise<Result<SchemaInfo>>;
|
|
60
|
-
getReferencingTables(table: string): Promise<Result<ReferencingTableInfo[]>>;
|
|
61
|
-
renameTable(oldName: string, newName: string): Promise<Result<{
|
|
62
|
-
changes?: number;
|
|
63
|
-
}>>;
|
|
64
|
-
addColumn(table: string, col: ColumnDef): Promise<Result<{
|
|
65
|
-
changes?: number;
|
|
66
|
-
}>>;
|
|
67
|
-
modifyColumn(table: string, oldColName: string, newColDef: ColumnDef): Promise<Result<{
|
|
68
|
-
changes?: number;
|
|
69
|
-
}>>;
|
|
70
|
-
renameColumn(table: string, oldName: string, newName: string): Promise<Result<{
|
|
71
|
-
changes?: number;
|
|
72
|
-
}>>;
|
|
73
|
-
dropColumn(table: string, column: string): Promise<Result<{
|
|
74
|
-
changes?: number;
|
|
75
|
-
}>>;
|
|
76
|
-
dropTable(table: string): Promise<Result<{
|
|
77
|
-
changes?: number;
|
|
78
|
-
}>>;
|
|
79
|
-
createIndex(table: string, index: IndexDef): Promise<Result<{
|
|
80
|
-
changes?: number;
|
|
81
|
-
}>>;
|
|
82
|
-
dropIndex(indexName: string): Promise<Result<{
|
|
83
|
-
changes?: number;
|
|
84
|
-
}>>;
|
|
85
|
-
}
|
|
1
|
+
import type {Logger} from '../utils/logger';import{type ColumnDef,type IndexDef} from '../sql/generator';import type {DatabaseDialect,IDatabase,MutationResult,PostgresOptions,QueryOptions,ReferencingTableInfo,Result,RowFilters,SavedQuery,SchemaInfo,SQLInputValue,TableInfoData,TableListItem,WhereClause} from './types';export declare class PostgresDatabase implements IDatabase{private pool;private logger;readonly path:string;readonly isReadOnly:boolean;readonly dialect:DatabaseDialect;readonly schema:string;constructor(connection:string|PostgresOptions,logger:Logger,options?:PostgresOptions);private readonlyBlocked;close():Promise<void>;private tryRun;private transaction;private initSchema;hasMultipleStatements(sql:string):boolean;all(sql:string,params?:SQLInputValue[]):Promise<Result<unknown[]>>;run(sql:string,params?:SQLInputValue[]):Promise<Result<MutationResult>>;execResult(sql:string):Promise<Result<{changes?:number;}>>;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?:QueryOptions):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<MutationResult>>;insertRows(table:string,rows:WhereClause[][]):Promise<Result<{inserted:number;skipped:number;}>>;updateRow(table:string,fields:WhereClause[],where:WhereClause[]):Promise<Result<MutationResult>>;updateRows(table:string,rows:{fields:WhereClause[];where:WhereClause[];}[]):Promise<Result<{updated:number;}>>;deleteRow(table:string,where:WhereClause[]):Promise<Result<MutationResult>>;deleteRows(table:string,rows:WhereClause[][]):Promise<Result<{deleted:number;}>>;getRowsByPks(table:string,pks:WhereClause[][]):Promise<Result<Record<string,unknown>[]>>;getRowsByFk(table:string,column:string,value:unknown,limit?:number):Promise<Result<{rows:Record<string,unknown>[];total:number;}>>;getCreateStatement(table:string):Promise<Result<string|null>>;listSavedQueries():Promise<Result<SavedQuery[]>>;saveQuery(name:string,sql:string):Promise<Result<MutationResult>>;deleteSavedQuery(id:string|number):Promise<Result<MutationResult>>;getSettings():Promise<Result<Record<string,string>>>;getSchema(table:string):Promise<Result<SchemaInfo>>;getReferencingTables(table:string):Promise<Result<ReferencingTableInfo[]>>;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;}>>;}
|
package/dist/db/postgres.js
CHANGED
|
@@ -31,15 +31,12 @@ class PostgresDatabase {
|
|
|
31
31
|
sslConfig = false;
|
|
32
32
|
}
|
|
33
33
|
else if (sslConfig === undefined) {
|
|
34
|
-
// Auto-enable SSL for remote/cloud hosts (e.g. Neon, Supabase, Render, Railway, AWS RDS, etc.)
|
|
35
|
-
// or whenever sslmode is specified or not explicitly local
|
|
36
34
|
if (sslMode === 'require' || sslMode === 'prefer' || sslMode === 'no-verify' || !isLocal) {
|
|
37
35
|
sslConfig = { rejectUnauthorized: false };
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
39
|
catch {
|
|
42
|
-
/* ignore URL parse error */
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
else if (opts.host && sslConfig === undefined) {
|
|
@@ -63,7 +60,7 @@ class PostgresDatabase {
|
|
|
63
60
|
keepAlive: true,
|
|
64
61
|
};
|
|
65
62
|
this.path = opts.connectionString
|
|
66
|
-
?
|
|
63
|
+
? (0, common_1.sanitizeConnectionString)(opts.connectionString)
|
|
67
64
|
: `${opts.user || 'postgres'}@${opts.host || 'localhost'}:${opts.port || 5432}/${opts.database || 'postgres'}`;
|
|
68
65
|
this.pool = new pg_1.Pool(poolConfig);
|
|
69
66
|
this.pool.on('error', (err) => {
|
|
@@ -76,17 +73,6 @@ class PostgresDatabase {
|
|
|
76
73
|
}
|
|
77
74
|
this.logger.info(`Initialized PostgreSQL pool for ${this.path}${this.isReadOnly ? ' (read-only)' : ''}`);
|
|
78
75
|
}
|
|
79
|
-
sanitizeConnectionString(conn) {
|
|
80
|
-
try {
|
|
81
|
-
const url = new URL(conn);
|
|
82
|
-
if (url.password)
|
|
83
|
-
url.password = '****';
|
|
84
|
-
return url.toString();
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
return conn.replace(/:([^@]+)@/, ':****@');
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
76
|
readonlyBlocked() {
|
|
91
77
|
return { success: false, error: 'Database is open in read-only mode — write operations are disabled.' };
|
|
92
78
|
}
|
|
@@ -109,7 +95,6 @@ class PostgresDatabase {
|
|
|
109
95
|
return { success: false, error: msg };
|
|
110
96
|
}
|
|
111
97
|
}
|
|
112
|
-
/** Run a callback inside an atomic transaction on a dedicated client. */
|
|
113
98
|
async transaction(fn) {
|
|
114
99
|
if (this.isReadOnly)
|
|
115
100
|
return this.readonlyBlocked();
|
|
@@ -125,7 +110,6 @@ class PostgresDatabase {
|
|
|
125
110
|
await client.query('ROLLBACK');
|
|
126
111
|
}
|
|
127
112
|
catch {
|
|
128
|
-
/* ignore */
|
|
129
113
|
}
|
|
130
114
|
const msg = (0, common_1.errorMessage)(err);
|
|
131
115
|
this.logger.error(msg);
|
|
@@ -162,7 +146,6 @@ class PostgresDatabase {
|
|
|
162
146
|
inString = !inString;
|
|
163
147
|
}
|
|
164
148
|
else if (ch === ';' && !inString) {
|
|
165
|
-
// Only count if there are non-whitespace characters following
|
|
166
149
|
const rest = s.slice(i + 1).trim();
|
|
167
150
|
if (rest.length > 0)
|
|
168
151
|
count++;
|
|
@@ -222,9 +205,7 @@ class PostgresDatabase {
|
|
|
222
205
|
}
|
|
223
206
|
async getTableInfo(table) {
|
|
224
207
|
return this.tryRun(async () => {
|
|
225
|
-
// Execute all 4 metadata queries concurrently in parallel
|
|
226
208
|
const [colRes, pkRes, fkRes, idxRes] = await Promise.all([
|
|
227
|
-
// 1. Columns
|
|
228
209
|
this.pool.query(`SELECT
|
|
229
210
|
ordinal_position AS cid,
|
|
230
211
|
column_name AS name,
|
|
@@ -235,7 +216,6 @@ class PostgresDatabase {
|
|
|
235
216
|
FROM information_schema.columns
|
|
236
217
|
WHERE table_schema = $1 AND table_name = $2
|
|
237
218
|
ORDER BY ordinal_position;`, [this.schema, table]),
|
|
238
|
-
// 2. Primary Keys
|
|
239
219
|
this.pool.query(`SELECT kcu.column_name
|
|
240
220
|
FROM information_schema.table_constraints tc
|
|
241
221
|
JOIN information_schema.key_column_usage kcu
|
|
@@ -245,7 +225,6 @@ class PostgresDatabase {
|
|
|
245
225
|
AND tc.table_schema = $1
|
|
246
226
|
AND tc.table_name = $2
|
|
247
227
|
ORDER BY kcu.ordinal_position;`, [this.schema, table]),
|
|
248
|
-
// 3. Foreign Keys
|
|
249
228
|
this.pool.query(`SELECT
|
|
250
229
|
row_number() OVER () AS id,
|
|
251
230
|
kcu.position_in_unique_constraint AS seq,
|
|
@@ -267,7 +246,6 @@ class PostgresDatabase {
|
|
|
267
246
|
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
268
247
|
AND tc.table_schema = $1
|
|
269
248
|
AND tc.table_name = $2;`, [this.schema, table]),
|
|
270
|
-
// 4. Indexes
|
|
271
249
|
this.pool.query(`SELECT
|
|
272
250
|
i.relname AS name,
|
|
273
251
|
ix.indisunique AS unique,
|
|
@@ -304,7 +282,6 @@ class PostgresDatabase {
|
|
|
304
282
|
columns: Array.isArray(ix.columns) ? ix.columns : [],
|
|
305
283
|
sql: ix.sql,
|
|
306
284
|
}));
|
|
307
|
-
// Approximate DDL for display
|
|
308
285
|
const colDefs = columns.map((c) => {
|
|
309
286
|
let def = ` ${(0, generator_1.quoteIdentifier)(c.name)} ${c.type}`;
|
|
310
287
|
if (c.notnull)
|
|
@@ -532,13 +509,32 @@ class PostgresDatabase {
|
|
|
532
509
|
};
|
|
533
510
|
});
|
|
534
511
|
}
|
|
512
|
+
async getSettings() {
|
|
513
|
+
return this.tryRun(async () => {
|
|
514
|
+
const keys = ['server_version', 'max_connections', 'port', 'timezone', 'shared_buffers', 'work_mem'];
|
|
515
|
+
const settings = {};
|
|
516
|
+
try {
|
|
517
|
+
const res = await this.pool.query(`SELECT name, setting FROM pg_settings WHERE name = ANY($1::text[])`, [keys]);
|
|
518
|
+
for (const row of res.rows) {
|
|
519
|
+
settings[row.name] = String(row.setting);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
try {
|
|
524
|
+
const ver = await this.pool.query('SHOW server_version');
|
|
525
|
+
settings['server_version'] = String(ver.rows[0]?.server_version);
|
|
526
|
+
}
|
|
527
|
+
catch { }
|
|
528
|
+
}
|
|
529
|
+
return settings;
|
|
530
|
+
});
|
|
531
|
+
}
|
|
535
532
|
async getSchema(table) {
|
|
536
533
|
return this.tryRun(async () => {
|
|
537
534
|
const infoR = await this.getTableInfo(table);
|
|
538
535
|
if (!infoR.success || !infoR.data)
|
|
539
536
|
throw new Error(infoR.error || `Table ${table} not found.`);
|
|
540
537
|
const { columns, foreignKeys, indexes, primaryKey } = infoR.data;
|
|
541
|
-
// Find references in other tables that point to this table
|
|
542
538
|
const refRes = await this.pool.query(`SELECT
|
|
543
539
|
tc.table_name AS table,
|
|
544
540
|
kcu.column_name AS "from",
|
|
@@ -661,21 +657,17 @@ class PostgresDatabase {
|
|
|
661
657
|
return this.readonlyBlocked();
|
|
662
658
|
return this.tryRun(async () => {
|
|
663
659
|
const newName = String(newColDef.name || oldColName).trim();
|
|
664
|
-
// Rename column first if name changed
|
|
665
660
|
if (oldColName !== newName) {
|
|
666
661
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} RENAME COLUMN ${(0, generator_1.quoteIdentifier)(oldColName)} TO ${(0, generator_1.quoteIdentifier)(newName)};`);
|
|
667
662
|
}
|
|
668
663
|
const colToModify = newName;
|
|
669
|
-
// Alter Type if provided
|
|
670
664
|
if (newColDef.type) {
|
|
671
665
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} TYPE ${newColDef.type} USING ${(0, generator_1.quoteIdentifier)(colToModify)}::${newColDef.type};`);
|
|
672
666
|
}
|
|
673
|
-
// Alter NOT NULL
|
|
674
667
|
if (newColDef.notNull !== undefined) {
|
|
675
668
|
const action = newColDef.notNull ? 'SET NOT NULL' : 'DROP NOT NULL';
|
|
676
669
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} ${action};`);
|
|
677
670
|
}
|
|
678
|
-
// Alter DEFAULT
|
|
679
671
|
if (newColDef.defaultValue !== undefined) {
|
|
680
672
|
if (newColDef.defaultValue === null || newColDef.defaultValue === '') {
|
|
681
673
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} DROP DEFAULT;`);
|
|
@@ -684,7 +676,6 @@ class PostgresDatabase {
|
|
|
684
676
|
await this.pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} SET DEFAULT ${newColDef.defaultValue};`);
|
|
685
677
|
}
|
|
686
678
|
}
|
|
687
|
-
// Handle unique index
|
|
688
679
|
if (newColDef.unique !== undefined) {
|
|
689
680
|
const idxName = `idx_${table}_${colToModify}_unique`;
|
|
690
681
|
if (newColDef.unique) {
|
|
@@ -742,4 +733,3 @@ class PostgresDatabase {
|
|
|
742
733
|
}
|
|
743
734
|
}
|
|
744
735
|
exports.PostgresDatabase = PostgresDatabase;
|
|
745
|
-
//# sourceMappingURL=postgres.js.map
|