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.
Files changed (191) hide show
  1. package/README.md +41 -12
  2. package/dist/app.d.ts +1 -69
  3. package/dist/app.js +3 -30
  4. package/dist/auth/config.d.ts +1 -19
  5. package/dist/auth/config.js +0 -26
  6. package/dist/auth/crypto.d.ts +1 -27
  7. package/dist/auth/crypto.js +1 -26
  8. package/dist/auth/index.d.ts +1 -5
  9. package/dist/auth/index.js +0 -1
  10. package/dist/auth/middleware.d.ts +1 -14
  11. package/dist/auth/middleware.js +0 -15
  12. package/dist/auth/routes.d.ts +1 -7
  13. package/dist/auth/routes.js +0 -8
  14. package/dist/auth/types.d.ts +1 -35
  15. package/dist/auth/types.js +1 -5
  16. package/dist/cli/args.d.ts +1 -45
  17. package/dist/cli/args.js +3 -11
  18. package/dist/cli/config.d.ts +1 -56
  19. package/dist/cli/config.js +0 -5
  20. package/dist/cli/index.d.ts +1 -4
  21. package/dist/cli/index.js +0 -1
  22. package/dist/cli/runner.d.ts +1 -1
  23. package/dist/cli/runner.js +6 -17
  24. package/dist/cli.d.ts +1 -2
  25. package/dist/cli.js +0 -1
  26. package/dist/data/chain.d.ts +1 -0
  27. package/dist/data/chain.js +707 -0
  28. package/dist/data/datasets.d.ts +1 -17
  29. package/dist/data/datasets.js +0 -4
  30. package/dist/data/detector.d.ts +1 -30
  31. package/dist/data/detector.js +53 -14
  32. package/dist/data/engine.d.ts +1 -10
  33. package/dist/data/engine.js +82 -48
  34. package/dist/data/index.d.ts +1 -5
  35. package/dist/data/index.js +1 -1
  36. package/dist/data/strategies.d.ts +1 -30
  37. package/dist/data/strategies.js +4 -8
  38. package/dist/data/types.d.ts +1 -73
  39. package/dist/data/types.js +0 -3
  40. package/dist/db/database.d.ts +1 -119
  41. package/dist/db/database.js +20 -14
  42. package/dist/db/export.d.ts +1 -12
  43. package/dist/db/export.js +54 -6
  44. package/dist/db/filters.d.ts +1 -22
  45. package/dist/db/filters.js +0 -16
  46. package/dist/db/index.d.ts +1 -8
  47. package/dist/db/index.js +0 -1
  48. package/dist/db/introspection.d.ts +1 -6
  49. package/dist/db/introspection.js +0 -5
  50. package/dist/db/manager.d.ts +1 -81
  51. package/dist/db/manager.js +13 -59
  52. package/dist/db/migrations.d.ts +1 -5
  53. package/dist/db/migrations.js +2 -5
  54. package/dist/db/postgres.d.ts +1 -85
  55. package/dist/db/postgres.js +21 -31
  56. package/dist/db/types.d.ts +1 -206
  57. package/dist/db/types.js +0 -1
  58. package/dist/index.d.ts +1 -23
  59. package/dist/index.js +0 -14
  60. package/dist/public/css/app.css +1 -1
  61. package/dist/public/js/api.js +1 -47
  62. package/dist/public/js/app.js +1 -262
  63. package/dist/public/js/browse.js +1 -1371
  64. package/dist/public/js/common.js +1 -0
  65. package/dist/public/js/databases.js +1 -235
  66. package/dist/public/js/designer.js +1 -258
  67. package/dist/public/js/forms.js +1 -826
  68. package/dist/public/js/home.js +1 -0
  69. package/dist/public/js/inspector.js +1 -638
  70. package/dist/public/js/query.js +1 -322
  71. package/dist/public/js/schema.js +1 -447
  72. package/dist/public/js/seed.js +1 -1090
  73. package/dist/routes/api/erd.d.ts +1 -0
  74. package/dist/routes/api/erd.js +50 -0
  75. package/dist/routes/api/helpers.d.ts +1 -42
  76. package/dist/routes/api/helpers.js +33 -3
  77. package/dist/routes/api/import-export.d.ts +1 -3
  78. package/dist/routes/api/import-export.js +0 -2
  79. package/dist/routes/api/index.d.ts +1 -4
  80. package/dist/routes/api/index.js +15 -4
  81. package/dist/routes/api/query.d.ts +1 -3
  82. package/dist/routes/api/query.js +13 -11
  83. package/dist/routes/api/rows.d.ts +1 -3
  84. package/dist/routes/api/rows.js +0 -11
  85. package/dist/routes/api/seed.d.ts +1 -3
  86. package/dist/routes/api/seed.js +79 -8
  87. package/dist/routes/api/tables.d.ts +1 -3
  88. package/dist/routes/api/tables.js +77 -6
  89. package/dist/routes/databases.d.ts +1 -26
  90. package/dist/routes/databases.js +1 -3
  91. package/dist/routes/index.d.ts +1 -3
  92. package/dist/routes/index.js +0 -1
  93. package/dist/routes/pages.d.ts +1 -9
  94. package/dist/routes/pages.js +18 -11
  95. package/dist/serverless.d.ts +1 -40
  96. package/dist/serverless.js +3 -39
  97. package/dist/sql/classifier.d.ts +1 -19
  98. package/dist/sql/classifier.js +0 -13
  99. package/dist/sql/error-analyzer.d.ts +1 -0
  100. package/dist/sql/error-analyzer.js +550 -0
  101. package/dist/sql/generator.d.ts +1 -81
  102. package/dist/sql/generator.js +0 -29
  103. package/dist/sql/index.d.ts +1 -2
  104. package/dist/sql/index.js +0 -1
  105. package/dist/types/api.d.ts +1 -412
  106. package/dist/types/api.js +0 -7
  107. package/dist/utils/colors.d.ts +1 -40
  108. package/dist/utils/colors.js +0 -10
  109. package/dist/utils/common.d.ts +1 -50
  110. package/dist/utils/common.js +19 -23
  111. package/dist/utils/csv.d.ts +1 -15
  112. package/dist/utils/csv.js +0 -13
  113. package/dist/utils/datatype.d.ts +1 -49
  114. package/dist/utils/datatype.js +0 -41
  115. package/dist/utils/icons.d.ts +1 -11
  116. package/dist/utils/icons.js +25 -16
  117. package/dist/utils/index.d.ts +1 -4
  118. package/dist/utils/index.js +0 -1
  119. package/dist/utils/logger.d.ts +1 -10
  120. package/dist/utils/logger.js +0 -4
  121. package/dist/views/layouts/main.hbs +14 -5
  122. package/dist/views/pages/databases.hbs +0 -18
  123. package/dist/views/pages/designer.hbs +0 -10
  124. package/dist/views/pages/erd.hbs +1241 -0
  125. package/dist/views/pages/error.hbs +0 -2
  126. package/dist/views/pages/form.hbs +0 -5
  127. package/dist/views/pages/home.hbs +98 -10
  128. package/dist/views/pages/info.hbs +107 -0
  129. package/dist/views/pages/login.hbs +0 -13
  130. package/dist/views/pages/query.hbs +54 -21
  131. package/dist/views/pages/schema.hbs +0 -20
  132. package/dist/views/pages/seed.hbs +435 -202
  133. package/dist/views/pages/table.hbs +0 -14
  134. package/dist/views/partials/icon.hbs +0 -1
  135. package/dist/views/partials/navbar.hbs +0 -6
  136. package/dist/views/partials/sidebar.hbs +12 -8
  137. package/docs/API.md +6 -1
  138. package/docs/EXAMPLES.md +3 -0
  139. package/package.json +7 -5
  140. package/dist/app.js.map +0 -1
  141. package/dist/auth/config.js.map +0 -1
  142. package/dist/auth/crypto.js.map +0 -1
  143. package/dist/auth/index.js.map +0 -1
  144. package/dist/auth/middleware.js.map +0 -1
  145. package/dist/auth/routes.js.map +0 -1
  146. package/dist/auth/types.js.map +0 -1
  147. package/dist/cli/args.js.map +0 -1
  148. package/dist/cli/config.js.map +0 -1
  149. package/dist/cli/index.js.map +0 -1
  150. package/dist/cli/runner.js.map +0 -1
  151. package/dist/cli.js.map +0 -1
  152. package/dist/data/datasets.js.map +0 -1
  153. package/dist/data/detector.js.map +0 -1
  154. package/dist/data/engine.js.map +0 -1
  155. package/dist/data/index.js.map +0 -1
  156. package/dist/data/strategies.js.map +0 -1
  157. package/dist/data/types.js.map +0 -1
  158. package/dist/db/database.js.map +0 -1
  159. package/dist/db/export.js.map +0 -1
  160. package/dist/db/filters.js.map +0 -1
  161. package/dist/db/index.js.map +0 -1
  162. package/dist/db/introspection.js.map +0 -1
  163. package/dist/db/manager.js.map +0 -1
  164. package/dist/db/migrations.js.map +0 -1
  165. package/dist/db/postgres.js.map +0 -1
  166. package/dist/db/types.js.map +0 -1
  167. package/dist/index.js.map +0 -1
  168. package/dist/public/css/input.css +0 -302
  169. package/dist/public/js/icons.js +0 -51
  170. package/dist/routes/api/helpers.js.map +0 -1
  171. package/dist/routes/api/import-export.js.map +0 -1
  172. package/dist/routes/api/index.js.map +0 -1
  173. package/dist/routes/api/query.js.map +0 -1
  174. package/dist/routes/api/rows.js.map +0 -1
  175. package/dist/routes/api/seed.js.map +0 -1
  176. package/dist/routes/api/tables.js.map +0 -1
  177. package/dist/routes/databases.js.map +0 -1
  178. package/dist/routes/index.js.map +0 -1
  179. package/dist/routes/pages.js.map +0 -1
  180. package/dist/serverless.js.map +0 -1
  181. package/dist/sql/classifier.js.map +0 -1
  182. package/dist/sql/generator.js.map +0 -1
  183. package/dist/sql/index.js.map +0 -1
  184. package/dist/types/api.js.map +0 -1
  185. package/dist/utils/colors.js.map +0 -1
  186. package/dist/utils/common.js.map +0 -1
  187. package/dist/utils/csv.js.map +0 -1
  188. package/dist/utils/datatype.js.map +0 -1
  189. package/dist/utils/icons.js.map +0 -1
  190. package/dist/utils/index.js.map +0 -1
  191. package/dist/utils/logger.js.map +0 -1
@@ -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 = []; // targets (paths or URIs), registry order
28
- idByPath = new Map(); // target -> id
29
- pathById = new Map(); // id -> target
30
- nameById = new Map(); // id -> display name
31
- readonlyById = new Map(); // id -> readonly override
32
- openDbs = new Map(); // id -> db
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
- const isPg = raw.startsWith('postgres://') || raw.startsWith('postgresql://');
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
- const isPg = target.startsWith('postgres://') || target.startsWith('postgresql://');
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: sanitized,
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
- const isPg = target.startsWith('postgres://') || target.startsWith('postgresql://');
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); // throws if the file is not a readable SQLite database
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); // better-sqlite3 creates the file on first open
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
- const isPg = target.startsWith('postgres://') || target.startsWith('postgresql://');
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
@@ -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;};
@@ -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 { /* ignore */ }
97
+ catch { }
100
98
  try {
101
99
  db.exec(`DROP TABLE IF EXISTS ${(0, generator_1.quoteIdentifier)(tempTable)};`);
102
100
  }
103
- catch { /* ignore */ }
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
@@ -1,85 +1 @@
1
- import type { Logger } from '../utils/logger';
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;}>>;}
@@ -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
- ? this.sanitizeConnectionString(opts.connectionString)
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