admindb 2.4.0 → 2.5.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.
Files changed (135) hide show
  1. package/LICENSE +20 -20
  2. package/dist/app.js +36 -25
  3. package/dist/core/context.d.ts +1 -0
  4. package/dist/core/context.js +2 -0
  5. package/dist/core/errors.d.ts +1 -0
  6. package/dist/core/errors.js +39 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +20 -0
  9. package/dist/core/result.d.ts +1 -0
  10. package/dist/core/result.js +10 -0
  11. package/dist/core/router.d.ts +1 -0
  12. package/dist/core/router.js +37 -0
  13. package/dist/data/chain.js +1 -1
  14. package/dist/data/engine.js +1 -1
  15. package/dist/db/database.d.ts +1 -1
  16. package/dist/db/database.js +9 -31
  17. package/dist/db/dialects/index.d.ts +1 -0
  18. package/dist/db/dialects/index.js +58 -0
  19. package/dist/db/dialects/postgres/ddl.d.ts +1 -0
  20. package/dist/db/dialects/postgres/ddl.js +107 -0
  21. package/dist/db/dialects/postgres/dialect.d.ts +1 -0
  22. package/dist/db/dialects/postgres/dialect.js +28 -0
  23. package/dist/db/dialects/postgres/introspector.d.ts +1 -0
  24. package/dist/db/dialects/postgres/introspector.js +265 -0
  25. package/dist/db/dialects/postgres/types.d.ts +1 -0
  26. package/dist/db/dialects/postgres/types.js +83 -0
  27. package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
  28. package/dist/db/dialects/sqlite/ddl.js +244 -0
  29. package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
  30. package/dist/db/dialects/sqlite/dialect.js +25 -0
  31. package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
  32. package/dist/db/dialects/sqlite/introspector.js +214 -0
  33. package/dist/db/dialects/sqlite/types.d.ts +1 -0
  34. package/dist/db/dialects/sqlite/types.js +69 -0
  35. package/dist/db/dialects/types.d.ts +1 -0
  36. package/dist/db/dialects/types.js +15 -0
  37. package/dist/db/export.js +1 -1
  38. package/dist/db/index.d.ts +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/manager.d.ts +1 -1
  41. package/dist/db/manager.js +101 -21
  42. package/dist/db/postgres.d.ts +1 -1
  43. package/dist/db/postgres.js +20 -284
  44. package/dist/db/types.d.ts +1 -1
  45. package/dist/db/types.js +0 -4
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/modules/databases/databases.routes.d.ts +1 -0
  49. package/dist/modules/databases/databases.routes.js +150 -0
  50. package/dist/modules/databases/databases.service.d.ts +1 -0
  51. package/dist/modules/databases/databases.service.js +134 -0
  52. package/dist/modules/databases/index.d.ts +1 -0
  53. package/dist/{routes → modules/databases}/index.js +2 -3
  54. package/dist/modules/erd/erd.routes.d.ts +1 -0
  55. package/dist/modules/erd/erd.routes.js +32 -0
  56. package/dist/modules/erd/erd.service.d.ts +1 -0
  57. package/dist/modules/erd/erd.service.js +39 -0
  58. package/dist/modules/erd/index.d.ts +1 -0
  59. package/dist/modules/erd/index.js +18 -0
  60. package/dist/modules/index.d.ts +1 -0
  61. package/dist/modules/index.js +62 -0
  62. package/dist/modules/query/index.d.ts +1 -0
  63. package/dist/modules/query/index.js +18 -0
  64. package/dist/modules/query/query.routes.d.ts +1 -0
  65. package/dist/modules/query/query.routes.js +54 -0
  66. package/dist/modules/query/query.service.d.ts +1 -0
  67. package/dist/modules/query/query.service.js +95 -0
  68. package/dist/modules/rows/blob.helper.d.ts +1 -0
  69. package/dist/modules/rows/blob.helper.js +40 -0
  70. package/dist/modules/rows/display.helper.d.ts +1 -0
  71. package/dist/modules/rows/display.helper.js +73 -0
  72. package/dist/modules/rows/index.d.ts +1 -0
  73. package/dist/modules/rows/index.js +20 -0
  74. package/dist/modules/rows/rows.routes.d.ts +1 -0
  75. package/dist/modules/rows/rows.routes.js +555 -0
  76. package/dist/modules/rows/rows.service.d.ts +1 -0
  77. package/dist/modules/rows/rows.service.js +433 -0
  78. package/dist/modules/schema/index.d.ts +1 -0
  79. package/dist/modules/schema/index.js +18 -0
  80. package/dist/modules/schema/schema.routes.d.ts +1 -0
  81. package/dist/modules/schema/schema.routes.js +171 -0
  82. package/dist/modules/schema/schema.service.d.ts +1 -0
  83. package/dist/modules/schema/schema.service.js +58 -0
  84. package/dist/modules/seed/index.d.ts +1 -0
  85. package/dist/modules/seed/index.js +18 -0
  86. package/dist/modules/seed/seed.routes.d.ts +1 -0
  87. package/dist/modules/seed/seed.routes.js +212 -0
  88. package/dist/modules/seed/seed.service.d.ts +1 -0
  89. package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
  90. package/dist/modules/tables/index.d.ts +1 -0
  91. package/dist/modules/tables/index.js +18 -0
  92. package/dist/modules/tables/tables.routes.d.ts +1 -0
  93. package/dist/modules/tables/tables.routes.js +194 -0
  94. package/dist/modules/tables/tables.service.d.ts +1 -0
  95. package/dist/modules/tables/tables.service.js +218 -0
  96. package/dist/public/js/browse.js +1 -1
  97. package/dist/public/js/query.js +1 -1
  98. package/dist/types/api.d.ts +1 -1
  99. package/dist/utils/common.d.ts +1 -1
  100. package/dist/utils/common.js +1 -3
  101. package/dist/utils/stream.d.ts +1 -0
  102. package/dist/utils/stream.js +131 -0
  103. package/dist/views/pages/error.hbs +10 -1
  104. package/dist/views/pages/home.hbs +1 -1
  105. package/dist/views/pages/query.hbs +1 -1
  106. package/dist/views/pages/seed-select.hbs +1 -252
  107. package/dist/views/pages/seed.hbs +1 -307
  108. package/dist/views/pages/table.hbs +1 -1
  109. package/dist/views/partials/sidebar.hbs +1 -1
  110. package/docs/API.md +4 -17
  111. package/docs/EXAMPLES.md +579 -579
  112. package/docs/SECURITY.md +265 -265
  113. package/package.json +4 -2
  114. package/dist/db/introspection.d.ts +0 -1
  115. package/dist/db/introspection.js +0 -164
  116. package/dist/routes/api/erd.d.ts +0 -1
  117. package/dist/routes/api/erd.js +0 -45
  118. package/dist/routes/api/helpers.d.ts +0 -1
  119. package/dist/routes/api/import-export.d.ts +0 -1
  120. package/dist/routes/api/import-export.js +0 -84
  121. package/dist/routes/api/index.d.ts +0 -1
  122. package/dist/routes/api/index.js +0 -55
  123. package/dist/routes/api/query.d.ts +0 -1
  124. package/dist/routes/api/query.js +0 -77
  125. package/dist/routes/api/rows.d.ts +0 -1
  126. package/dist/routes/api/rows.js +0 -373
  127. package/dist/routes/api/seed.d.ts +0 -1
  128. package/dist/routes/api/seed.js +0 -180
  129. package/dist/routes/api/tables.d.ts +0 -1
  130. package/dist/routes/api/tables.js +0 -230
  131. package/dist/routes/databases.d.ts +0 -1
  132. package/dist/routes/databases.js +0 -212
  133. package/dist/routes/index.d.ts +0 -1
  134. package/dist/routes/pages.d.ts +0 -1
  135. package/dist/routes/pages.js +0 -540
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresIntrospector = void 0;
4
+ const generator_1 = require("../../../sql/generator");
5
+ class PostgresIntrospector {
6
+ schema;
7
+ constructor(schema = 'public') {
8
+ this.schema = schema;
9
+ }
10
+ getPool(driver) {
11
+ if (driver && typeof driver === 'object' && 'pool' in driver) {
12
+ return driver.pool;
13
+ }
14
+ return driver;
15
+ }
16
+ async listTables(driver) {
17
+ try {
18
+ const pool = this.getPool(driver);
19
+ const res = await pool.query(`SELECT table_name
20
+ FROM information_schema.tables
21
+ WHERE table_schema = $1 AND table_type = 'BASE TABLE'
22
+ ORDER BY table_name;`, [this.schema]);
23
+ return { success: true, data: res.rows.map((r) => ({ name: r.table_name })) };
24
+ }
25
+ catch (err) {
26
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
27
+ }
28
+ }
29
+ async getColumnCountMap(driver) {
30
+ try {
31
+ const pool = this.getPool(driver);
32
+ const colRes = await pool.query(`SELECT table_name, count(*)::int AS cols FROM information_schema.columns WHERE table_schema = $1 GROUP BY table_name;`, [this.schema]);
33
+ const map = {};
34
+ for (const row of colRes.rows) {
35
+ if (row && typeof row.table_name === 'string') {
36
+ map[row.table_name] = Number(row.cols ?? 0);
37
+ }
38
+ }
39
+ return { success: true, data: map };
40
+ }
41
+ catch (err) {
42
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
43
+ }
44
+ }
45
+ async getTableInfo(driver, table) {
46
+ try {
47
+ const pool = this.getPool(driver);
48
+ const colRes = await pool.query(`SELECT ordinal_position, column_name, data_type, udt_name, is_nullable, column_default
49
+ FROM information_schema.columns
50
+ WHERE table_schema = $1 AND table_name = $2
51
+ ORDER BY ordinal_position;`, [this.schema, table]);
52
+ if (!colRes.rows.length) {
53
+ return { success: false, error: `Table "${table}" does not exist.` };
54
+ }
55
+ const pkRes = await pool.query(`SELECT kcu.column_name
56
+ FROM information_schema.table_constraints tc
57
+ JOIN information_schema.key_column_usage kcu
58
+ ON tc.constraint_name = kcu.constraint_name
59
+ AND tc.table_schema = kcu.table_schema
60
+ WHERE tc.constraint_type = 'PRIMARY KEY'
61
+ AND tc.table_schema = $1
62
+ AND tc.table_name = $2
63
+ ORDER BY kcu.ordinal_position;`, [this.schema, table]);
64
+ const primaryKey = pkRes.rows.map((r) => r.column_name);
65
+ const columns = colRes.rows.map((r) => {
66
+ const pkIdx = primaryKey.indexOf(r.column_name);
67
+ const colType = r.data_type === 'USER-DEFINED' ? r.udt_name : (r.udt_name || r.data_type).toUpperCase();
68
+ return {
69
+ cid: r.ordinal_position,
70
+ name: r.column_name,
71
+ type: colType,
72
+ notnull: r.is_nullable === 'NO' ? 1 : 0,
73
+ dflt_value: r.column_default,
74
+ pk: pkIdx >= 0 ? pkIdx + 1 : 0,
75
+ };
76
+ });
77
+ const fkRes = await pool.query(`SELECT
78
+ row_number() OVER () AS id,
79
+ ccu.table_name AS table,
80
+ kcu.column_name AS "from",
81
+ ccu.column_name AS "to",
82
+ rc.update_rule AS on_update,
83
+ rc.delete_rule AS on_delete
84
+ FROM information_schema.table_constraints AS tc
85
+ JOIN information_schema.key_column_usage AS kcu
86
+ ON tc.constraint_name = kcu.constraint_name
87
+ AND tc.table_schema = kcu.table_schema
88
+ JOIN information_schema.constraint_column_usage AS ccu
89
+ ON ccu.constraint_name = tc.constraint_name
90
+ AND ccu.table_schema = tc.table_schema
91
+ JOIN information_schema.referential_constraints AS rc
92
+ ON rc.constraint_name = tc.constraint_name
93
+ WHERE tc.constraint_type = 'FOREIGN KEY'
94
+ AND tc.table_schema = $1
95
+ AND tc.table_name = $2;`, [this.schema, table]);
96
+ const foreignKeys = fkRes.rows.map((r, idx) => ({
97
+ id: Number(r.id || idx + 1),
98
+ seq: idx,
99
+ table: r.table,
100
+ from: r.from,
101
+ to: r.to,
102
+ on_update: r.on_update,
103
+ on_delete: r.on_delete,
104
+ }));
105
+ const idxRes = await pool.query(`SELECT
106
+ i.relname AS indexname,
107
+ ix.indisunique,
108
+ ix.indisprimary,
109
+ array_agg(a.attname ORDER BY array_position(ix.indkey, a.attnum)) AS columns,
110
+ pg_get_indexdef(ix.indexrelid) AS indexdef
111
+ FROM pg_class t
112
+ JOIN pg_index ix ON t.oid = ix.indrelid
113
+ JOIN pg_class i ON i.oid = ix.indexrelid
114
+ JOIN pg_namespace n ON n.oid = t.relnamespace
115
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
116
+ WHERE n.nspname = $1 AND t.relname = $2
117
+ GROUP BY i.relname, ix.indisunique, ix.indisprimary, ix.indexrelid;`, [this.schema, table]);
118
+ const indexes = idxRes.rows.map((r) => ({
119
+ name: r.indexname,
120
+ unique: Boolean(r.indisunique),
121
+ partial: 0,
122
+ origin: r.indisprimary ? 'pk' : r.indisunique ? 'u' : 'c',
123
+ columns: r.columns || [],
124
+ sql: r.indexdef,
125
+ }));
126
+ const ddlCols = columns.map((c) => {
127
+ let def = ` ${(0, generator_1.quoteIdentifier)(c.name)} ${c.type}`;
128
+ if (c.notnull)
129
+ def += ' NOT NULL';
130
+ if (c.dflt_value)
131
+ def += ` DEFAULT ${c.dflt_value}`;
132
+ return def;
133
+ });
134
+ if (primaryKey.length) {
135
+ ddlCols.push(` PRIMARY KEY (${primaryKey.map(generator_1.quoteIdentifier).join(', ')})`);
136
+ }
137
+ for (const fk of foreignKeys) {
138
+ ddlCols.push(` FOREIGN KEY (${(0, generator_1.quoteIdentifier)(fk.from)}) REFERENCES ${(0, generator_1.quoteIdentifier)(fk.table)}(${(0, generator_1.quoteIdentifier)(fk.to || fk.from)})`);
139
+ }
140
+ const fakeSql = `CREATE TABLE ${(0, generator_1.quoteIdentifier)(table)} (\n${ddlCols.join(',\n')}\n);`;
141
+ return {
142
+ success: true,
143
+ data: {
144
+ table,
145
+ columns,
146
+ foreignKeys,
147
+ primaryKey,
148
+ indexes,
149
+ sql: fakeSql,
150
+ },
151
+ };
152
+ }
153
+ catch (err) {
154
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
155
+ }
156
+ }
157
+ async getSchema(driver, table) {
158
+ try {
159
+ const pool = this.getPool(driver);
160
+ const infoR = await this.getTableInfo(driver, table);
161
+ if (!infoR.success || !infoR.data)
162
+ throw new Error(infoR.error || `Table ${table} not found.`);
163
+ const { columns, foreignKeys, indexes, primaryKey } = infoR.data;
164
+ const refRes = await pool.query(`SELECT
165
+ tc.table_name AS table,
166
+ kcu.column_name AS "from",
167
+ ccu.column_name AS "to"
168
+ FROM information_schema.table_constraints AS tc
169
+ JOIN information_schema.key_column_usage AS kcu
170
+ ON tc.constraint_name = kcu.constraint_name
171
+ AND tc.table_schema = kcu.table_schema
172
+ JOIN information_schema.constraint_column_usage AS ccu
173
+ ON ccu.constraint_name = tc.constraint_name
174
+ AND ccu.table_schema = tc.table_schema
175
+ WHERE tc.constraint_type = 'FOREIGN KEY'
176
+ AND tc.table_schema = $1
177
+ AND ccu.table_name = $2;`, [this.schema, table]);
178
+ const references = refRes.rows;
179
+ const columnDetails = columns.map((col) => {
180
+ const isPk = primaryKey.includes(col.name);
181
+ const fk = foreignKeys.find((f) => f.from === col.name) ?? null;
182
+ const matchingIndexes = indexes.filter((i) => i.columns.includes(col.name));
183
+ const unique = matchingIndexes.some((i) => i.unique && i.columns.length === 1 && i.columns[0] === col.name);
184
+ const indexed = matchingIndexes.length > 0;
185
+ const referencedBy = references
186
+ .filter((r) => r.to === col.name)
187
+ .map((r) => ({ table: r.table, from: r.from }));
188
+ const dropBlockers = [];
189
+ if (isPk && primaryKey.length === 1)
190
+ dropBlockers.push('Primary key');
191
+ if (referencedBy.length > 0) {
192
+ dropBlockers.push(`Referenced by ${referencedBy.map((r) => `${r.table}.${r.from}`).join(', ')}`);
193
+ }
194
+ return {
195
+ ...col,
196
+ unique,
197
+ indexed,
198
+ referencedBy,
199
+ fk,
200
+ canDrop: dropBlockers.length === 0,
201
+ dropBlockers,
202
+ };
203
+ });
204
+ return {
205
+ success: true,
206
+ data: {
207
+ table,
208
+ columns: columnDetails,
209
+ foreignKeys,
210
+ indexes,
211
+ references,
212
+ },
213
+ };
214
+ }
215
+ catch (err) {
216
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
217
+ }
218
+ }
219
+ async getReferencingTables(driver, table) {
220
+ try {
221
+ const pool = this.getPool(driver);
222
+ const refRes = await pool.query(`SELECT
223
+ tc.table_name AS table,
224
+ kcu.column_name AS "from",
225
+ ccu.column_name AS "to"
226
+ FROM information_schema.table_constraints AS tc
227
+ JOIN information_schema.key_column_usage AS kcu
228
+ ON tc.constraint_name = kcu.constraint_name
229
+ AND tc.table_schema = kcu.table_schema
230
+ JOIN information_schema.constraint_column_usage AS ccu
231
+ ON ccu.constraint_name = tc.constraint_name
232
+ AND ccu.table_schema = tc.table_schema
233
+ WHERE tc.constraint_type = 'FOREIGN KEY'
234
+ AND tc.table_schema = $1
235
+ AND ccu.table_name = $2;`, [this.schema, table]);
236
+ const byTable = new Map();
237
+ for (const r of refRes.rows) {
238
+ if (!byTable.has(r.table))
239
+ byTable.set(r.table, []);
240
+ byTable.get(r.table).push({ from: r.from, to: r.to });
241
+ }
242
+ const results = [];
243
+ for (const [tbl, refs] of byTable.entries()) {
244
+ try {
245
+ const countRes = await pool.query(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(tbl)} WHERE ${refs.map((r) => `${(0, generator_1.quoteIdentifier)(r.from)} IS NOT NULL`).join(' OR ')}`);
246
+ results.push({ table: tbl, refs, refCount: Number(countRes.rows[0]?.c ?? 0) });
247
+ }
248
+ catch {
249
+ results.push({ table: tbl, refs, refCount: 0 });
250
+ }
251
+ }
252
+ return { success: true, data: results };
253
+ }
254
+ catch (err) {
255
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
256
+ }
257
+ }
258
+ async getCreateStatement(driver, table) {
259
+ const info = await this.getTableInfo(driver, table);
260
+ if (!info.success || !info.data)
261
+ return { success: false, error: info.error };
262
+ return { success: true, data: info.data.sql };
263
+ }
264
+ }
265
+ exports.PostgresIntrospector = PostgresIntrospector;
@@ -0,0 +1 @@
1
+ export declare const POSTGRES_DESIGNER_TYPES:readonly ["TEXT","VARCHAR(255)","INTEGER","BIGINT","SMALLINT","SERIAL","BIGSERIAL","REAL","DECIMAL(10,2)","NUMERIC","BOOLEAN","DATE","DATETIME","TIMESTAMP","TIMESTAMPTZ","TIME","INTERVAL","JSON","JSONB","UUID","BYTEA","INET"];import type {MappedType} from '../types';export type {MappedType};export declare function mapPostgresColumnType(type:string):MappedType;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POSTGRES_DESIGNER_TYPES = void 0;
4
+ exports.mapPostgresColumnType = mapPostgresColumnType;
5
+ exports.POSTGRES_DESIGNER_TYPES = [
6
+ 'TEXT',
7
+ 'VARCHAR(255)',
8
+ 'INTEGER',
9
+ 'BIGINT',
10
+ 'SMALLINT',
11
+ 'SERIAL',
12
+ 'BIGSERIAL',
13
+ 'REAL',
14
+ 'DECIMAL(10,2)',
15
+ 'NUMERIC',
16
+ 'BOOLEAN',
17
+ 'DATE',
18
+ 'DATETIME',
19
+ 'TIMESTAMP',
20
+ 'TIMESTAMPTZ',
21
+ 'TIME',
22
+ 'INTERVAL',
23
+ 'JSON',
24
+ 'JSONB',
25
+ 'UUID',
26
+ 'BYTEA',
27
+ 'INET',
28
+ ];
29
+ function mapPostgresColumnType(type) {
30
+ const t = String(type ?? '').trim().toUpperCase();
31
+ if (!t)
32
+ return { sqlType: 'TEXT' };
33
+ switch (t) {
34
+ case 'INT':
35
+ case 'INTEGER':
36
+ return { sqlType: 'INTEGER' };
37
+ case 'BIGINT':
38
+ case 'SMALLINT':
39
+ case 'SERIAL':
40
+ case 'BIGSERIAL':
41
+ case 'SMALLSERIAL':
42
+ return { sqlType: t };
43
+ case 'TEXT':
44
+ case 'VARCHAR':
45
+ case 'CHAR':
46
+ case 'CITEXT':
47
+ return { sqlType: 'TEXT' };
48
+ case 'REAL':
49
+ case 'FLOAT':
50
+ case 'DOUBLE PRECISION':
51
+ case 'DECIMAL':
52
+ case 'NUMERIC':
53
+ case 'MONEY':
54
+ return { sqlType: 'NUMERIC' };
55
+ case 'BYTEA':
56
+ case 'BLOB':
57
+ return { sqlType: 'BYTEA' };
58
+ case 'BOOLEAN':
59
+ case 'BOOL':
60
+ return { sqlType: 'BOOLEAN' };
61
+ case 'DATE':
62
+ return { sqlType: 'DATE', defaultAuto: 'CURRENT_DATE' };
63
+ case 'DATETIME':
64
+ case 'TIMESTAMP':
65
+ return { sqlType: 'TIMESTAMP' };
66
+ case 'TIMESTAMPTZ':
67
+ return { sqlType: 'TIMESTAMPTZ', defaultAuto: 'CURRENT_TIMESTAMP' };
68
+ case 'TIME':
69
+ case 'TIMETZ':
70
+ case 'INTERVAL':
71
+ case 'JSON':
72
+ case 'JSONB':
73
+ case 'UUID':
74
+ case 'INET':
75
+ case 'CIDR':
76
+ case 'MACADDR':
77
+ return { sqlType: t };
78
+ default:
79
+ if (/^[A-Za-z0-9_(),\s\[\]]+$/.test(t))
80
+ return { sqlType: t };
81
+ return { sqlType: 'TEXT' };
82
+ }
83
+ }
@@ -0,0 +1 @@
1
+ import{type ColumnDef,type IndexDef} from '../../../sql/generator';import type {IDdlGenerator} from '../types';import type {Result} from '../../types';export declare function renderSqliteColumnDef(col:ColumnDef,opts?:{forAddColumn?:boolean;}):string;export declare class SqliteDdlGenerator implements IDdlGenerator{createTable(tableName:string,columns:ColumnDef[]):string;renameTable(oldName:string,newName:string):string;dropTable(table:string,options?:{cascade?:boolean;ifExists?:boolean;}):string;truncateTable(table:string,_options?:{cascade?:boolean;restartIdentity?:boolean;}):string;setForeignKeys(enabled:boolean):string;addColumn(table:string,col:ColumnDef):string;renameColumn(table:string,oldName:string,newName:string):string;dropColumn(table:string,column:string):string;createIndex(table:string,index:IndexDef):string;dropIndex(indexName:string):string;modifyColumn(driver:unknown,table:string,oldCol:string,newCol:ColumnDef):Promise<Result<{changes?:number;}>>;}
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SqliteDdlGenerator = void 0;
4
+ exports.renderSqliteColumnDef = renderSqliteColumnDef;
5
+ const generator_1 = require("../../../sql/generator");
6
+ const types_1 = require("./types");
7
+ const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_$]*$/;
8
+ function validateIdentifier(kind, name) {
9
+ const label = kind.charAt(0).toUpperCase() + kind.slice(1);
10
+ const trimmed = String(name ?? '').trim();
11
+ if (!trimmed)
12
+ throw new Error(`${label} name is required.`);
13
+ if (!IDENTIFIER_RE.test(trimmed)) {
14
+ throw new Error(`Invalid ${label.toLowerCase()} name: "${trimmed}". Use letters, digits and underscores.`);
15
+ }
16
+ }
17
+ function formatSqlDefault(raw, colType) {
18
+ const s = String(raw ?? '').trim().replace(/;/g, '');
19
+ if (!s)
20
+ return '';
21
+ if (/^null$/i.test(s))
22
+ return 'NULL';
23
+ if (/^[-+]?\d+(\.\d+)?$/.test(s))
24
+ return s;
25
+ if (/^(true|false)$/i.test(s))
26
+ return s.toUpperCase() === 'TRUE' ? '1' : '0';
27
+ if (/^'.*'$/s.test(s) || /^".*"$/s.test(s) || /^[a-zA-Z_]\w*\s*\(.*\)$/s.test(s))
28
+ return s;
29
+ if (/^CURRENT_TIMESTAMP|CURRENT_DATE|CURRENT_TIME$/i.test(s))
30
+ return s.toUpperCase();
31
+ const t = (colType || '').toUpperCase();
32
+ if (t.includes('TEXT') || t.includes('CHAR') || t.includes('CLOB') || t.includes('VARCHAR') || t.includes('STRING')) {
33
+ return (0, generator_1.escapeString)(s);
34
+ }
35
+ return s;
36
+ }
37
+ function renderSqliteColumnDef(col, opts = {}) {
38
+ const colName = String(col?.name ?? '').trim();
39
+ validateIdentifier('column', colName);
40
+ const mapped = (0, types_1.mapSqliteColumnType)(col.type);
41
+ if (opts.forAddColumn) {
42
+ if (col.primaryKey) {
43
+ throw new Error(`Cannot add "${colName}" as PRIMARY KEY: ALTER TABLE ADD COLUMN does not support primary keys.`);
44
+ }
45
+ if (col.unique) {
46
+ throw new Error(`Cannot add "${colName}" with UNIQUE: ALTER TABLE ADD COLUMN does not support UNIQUE constraints.`);
47
+ }
48
+ }
49
+ let def = `${(0, generator_1.quoteIdentifier)(colName)} ${mapped.sqlType}`;
50
+ if (!opts.forAddColumn && col.primaryKey)
51
+ def += ' PRIMARY KEY';
52
+ if (col.notNull)
53
+ def += ' NOT NULL';
54
+ if (!opts.forAddColumn && col.unique)
55
+ def += ' UNIQUE';
56
+ const rawDefault = formatSqlDefault(String(col.defaultValue ?? ''), col.type);
57
+ const dflt = rawDefault || mapped.defaultAuto || '';
58
+ if (dflt) {
59
+ def += ` DEFAULT ${dflt}`;
60
+ }
61
+ else if (opts.forAddColumn && col.notNull) {
62
+ throw new Error(`Cannot add "${colName}" as NOT NULL without a default value.`);
63
+ }
64
+ if (col.foreignKey?.table && col.foreignKey.column) {
65
+ if (opts.forAddColumn && (col.notNull || dflt)) {
66
+ throw new Error(`Cannot add "${colName}" as a foreign key with NOT NULL or a non-NULL default.`);
67
+ }
68
+ def += ` REFERENCES ${(0, generator_1.quoteIdentifier)(col.foreignKey.table)}(${(0, generator_1.quoteIdentifier)(col.foreignKey.column)})`;
69
+ }
70
+ return def;
71
+ }
72
+ class SqliteDdlGenerator {
73
+ createTable(tableName, columns) {
74
+ const table = String(tableName ?? '').trim();
75
+ validateIdentifier('table', table);
76
+ if (!Array.isArray(columns) || columns.length === 0) {
77
+ throw new Error('At least one column is required.');
78
+ }
79
+ const seen = new Set();
80
+ let pkCount = 0;
81
+ const parts = [];
82
+ for (const col of columns) {
83
+ const colName = String(col?.name ?? '').trim();
84
+ validateIdentifier('column', colName);
85
+ if (seen.has(colName))
86
+ throw new Error(`Duplicate column name: "${colName}".`);
87
+ seen.add(colName);
88
+ if (col.primaryKey)
89
+ pkCount++;
90
+ parts.push(renderSqliteColumnDef(col));
91
+ }
92
+ if (pkCount > 1)
93
+ throw new Error('At most one primary key column is allowed.');
94
+ return `CREATE TABLE ${(0, generator_1.quoteIdentifier)(table)} (\n ${parts.join(',\n ')}\n);`;
95
+ }
96
+ renameTable(oldName, newName) {
97
+ validateIdentifier('table', oldName);
98
+ validateIdentifier('table', newName);
99
+ return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(oldName)} RENAME TO ${(0, generator_1.quoteIdentifier)(newName)};`;
100
+ }
101
+ dropTable(table, options) {
102
+ validateIdentifier('table', table);
103
+ const ifExists = options?.ifExists ? 'IF EXISTS ' : '';
104
+ return `DROP TABLE ${ifExists}${(0, generator_1.quoteIdentifier)(table)};`;
105
+ }
106
+ truncateTable(table, _options) {
107
+ validateIdentifier('table', table);
108
+ return `DELETE FROM ${(0, generator_1.quoteIdentifier)(table)};`;
109
+ }
110
+ setForeignKeys(enabled) {
111
+ return `PRAGMA foreign_keys = ${enabled ? 'ON' : 'OFF'};`;
112
+ }
113
+ addColumn(table, col) {
114
+ validateIdentifier('table', table);
115
+ return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ADD COLUMN ${renderSqliteColumnDef(col, { forAddColumn: true })};`;
116
+ }
117
+ renameColumn(table, oldName, newName) {
118
+ validateIdentifier('table', table);
119
+ validateIdentifier('column', oldName);
120
+ validateIdentifier('column', newName);
121
+ return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} RENAME COLUMN ${(0, generator_1.quoteIdentifier)(oldName)} TO ${(0, generator_1.quoteIdentifier)(newName)};`;
122
+ }
123
+ dropColumn(table, column) {
124
+ validateIdentifier('table', table);
125
+ validateIdentifier('column', column);
126
+ return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} DROP COLUMN ${(0, generator_1.quoteIdentifier)(column)};`;
127
+ }
128
+ createIndex(table, index) {
129
+ const unique = index.unique ? 'UNIQUE ' : '';
130
+ const cols = index.columns.map(generator_1.quoteIdentifier).join(', ');
131
+ const name = index.name || `idx_${table}_${index.columns.join('_')}`;
132
+ return `CREATE ${unique}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(name)} ON ${(0, generator_1.quoteIdentifier)(table)} (${cols});`;
133
+ }
134
+ dropIndex(indexName) {
135
+ return `DROP INDEX IF EXISTS ${(0, generator_1.quoteIdentifier)(indexName)};`;
136
+ }
137
+ async modifyColumn(driver, table, oldCol, newCol) {
138
+ try {
139
+ const db = (driver && typeof driver === 'object' && 'db' in driver)
140
+ ? driver.db
141
+ : driver;
142
+ const rawCols = db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
143
+ if (!rawCols.length)
144
+ throw new Error(`Table "${table}" does not exist.`);
145
+ const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
146
+ const indexRows = db
147
+ .prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`)
148
+ .all();
149
+ const uniqueCols = new Set();
150
+ for (const ix of indexRows) {
151
+ if (ix.unique) {
152
+ const ixCols = db.prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`).all().map((c) => c.name);
153
+ if (ixCols.length === 1)
154
+ uniqueCols.add(ixCols[0]);
155
+ }
156
+ }
157
+ const userIndexes = [];
158
+ for (const ix of indexRows) {
159
+ if (ix.origin === 'c') {
160
+ const ixCols = db.prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`).all().map((c) => c.name);
161
+ let ixName = ix.name;
162
+ const renamedNewCol = newCol.name;
163
+ if (oldCol && renamedNewCol) {
164
+ if (ixName === `idx_${table}_${oldCol}`) {
165
+ ixName = `idx_${table}_${renamedNewCol}`;
166
+ }
167
+ else if (ixName === `idx_${table}_${oldCol}_unique`) {
168
+ ixName = `idx_${table}_${renamedNewCol}_unique`;
169
+ }
170
+ }
171
+ userIndexes.push({
172
+ name: ixName,
173
+ unique: Boolean(ix.unique),
174
+ columns: ixCols.map((c) => (oldCol && renamedNewCol && c === oldCol ? renamedNewCol : c)),
175
+ });
176
+ }
177
+ }
178
+ const currentCols = rawCols.map((c) => {
179
+ const existingFk = fks.find((f) => f.from === c.name);
180
+ return {
181
+ name: c.name,
182
+ type: c.type,
183
+ primaryKey: c.pk > 0,
184
+ notNull: Boolean(c.notnull),
185
+ unique: uniqueCols.has(c.name),
186
+ defaultValue: c.dflt_value,
187
+ foreignKey: existingFk ? { table: existingFk.table, column: existingFk.to ?? '' } : null,
188
+ };
189
+ });
190
+ const newCols = currentCols.map((c) => (c.name === oldCol ? { ...c, ...newCol } : c));
191
+ const tempTable = `_admindb_tmp_${table}_${Date.now()}`;
192
+ const createSql = this.createTable(tempTable, newCols);
193
+ const oldColMap = new Set(rawCols.map((c) => c.name));
194
+ const matchingOldCols = [];
195
+ const matchingNewCols = [];
196
+ for (const nc of newCols) {
197
+ const sourceColName = oldCol && newCol.name && nc.name === newCol.name ? oldCol : nc.name;
198
+ if (oldColMap.has(sourceColName)) {
199
+ matchingOldCols.push((0, generator_1.quoteIdentifier)(sourceColName));
200
+ matchingNewCols.push((0, generator_1.quoteIdentifier)(nc.name));
201
+ }
202
+ }
203
+ db.exec('PRAGMA foreign_keys = OFF;\nBEGIN TRANSACTION;');
204
+ try {
205
+ db.exec(createSql);
206
+ if (matchingOldCols.length > 0) {
207
+ db.exec(`INSERT INTO ${(0, generator_1.quoteIdentifier)(tempTable)} (${matchingNewCols.join(', ')}) SELECT ${matchingOldCols.join(', ')} FROM ${(0, generator_1.quoteIdentifier)(table)};`);
208
+ }
209
+ db.exec(`DROP TABLE ${(0, generator_1.quoteIdentifier)(table)};`);
210
+ db.exec(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(tempTable)} RENAME TO ${(0, generator_1.quoteIdentifier)(table)};`);
211
+ for (const ix of userIndexes) {
212
+ const uq = ix.unique ? 'UNIQUE ' : '';
213
+ const ixColsStr = ix.columns.map(generator_1.quoteIdentifier).join(', ');
214
+ try {
215
+ db.exec(`CREATE ${uq}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(ix.name)} ON ${(0, generator_1.quoteIdentifier)(table)} (${ixColsStr});`);
216
+ }
217
+ catch { }
218
+ }
219
+ if (db.prepare('PRAGMA foreign_key_check;').all().length > 0) {
220
+ throw new Error('Foreign key constraint check failed after schema modification.');
221
+ }
222
+ db.exec('COMMIT;');
223
+ return { success: true, data: { changes: 1 } };
224
+ }
225
+ catch (err) {
226
+ try {
227
+ db.exec('ROLLBACK;');
228
+ }
229
+ catch { }
230
+ throw err;
231
+ }
232
+ finally {
233
+ try {
234
+ db.exec('PRAGMA foreign_keys = ON;');
235
+ }
236
+ catch { }
237
+ }
238
+ }
239
+ catch (err) {
240
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
241
+ }
242
+ }
243
+ }
244
+ exports.SqliteDdlGenerator = SqliteDdlGenerator;
@@ -0,0 +1 @@
1
+ import type {DialectCapabilities,IDialect} from '../types';import{SqliteDdlGenerator} from './ddl';import{SqliteIntrospector} from './introspector';export declare class SqliteDialect implements IDialect{readonly name:"sqlite";readonly capabilities:DialectCapabilities;readonly introspector:SqliteIntrospector;readonly ddl:SqliteDdlGenerator;quoteIdentifier(identifier:string):string;mapType(type:string):import("./types").MappedType;getDesignerTypes():readonly string[];}export declare const sqliteDialect:SqliteDialect;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sqliteDialect = exports.SqliteDialect = void 0;
4
+ const generator_1 = require("../../../sql/generator");
5
+ const types_1 = require("../types");
6
+ const ddl_1 = require("./ddl");
7
+ const introspector_1 = require("./introspector");
8
+ const types_2 = require("./types");
9
+ class SqliteDialect {
10
+ name = 'sqlite';
11
+ capabilities = types_1.SQLITE_CAPABILITIES;
12
+ introspector = new introspector_1.SqliteIntrospector();
13
+ ddl = new ddl_1.SqliteDdlGenerator();
14
+ quoteIdentifier(identifier) {
15
+ return (0, generator_1.quoteIdentifier)(identifier);
16
+ }
17
+ mapType(type) {
18
+ return (0, types_2.mapSqliteColumnType)(type);
19
+ }
20
+ getDesignerTypes() {
21
+ return types_2.SQLITE_DESIGNER_TYPES;
22
+ }
23
+ }
24
+ exports.SqliteDialect = SqliteDialect;
25
+ exports.sqliteDialect = new SqliteDialect();
@@ -0,0 +1 @@
1
+ import type {ISchemaIntrospector} from '../types';import type {ReferencingTableInfo,Result,SchemaInfo,TableInfoData,TableListItem} from '../../types';export declare class SqliteIntrospector implements ISchemaIntrospector{private getNativeDb;listTables(driver:unknown):Promise<Result<TableListItem[]>>;private getTableNames;private getIncomingFks;getTableInfo(driver:unknown,table:string):Promise<Result<TableInfoData>>;getSchema(driver:unknown,table:string):Promise<Result<SchemaInfo>>;getReferencingTables(driver:unknown,table:string):Promise<Result<ReferencingTableInfo[]>>;getCreateStatement(driver:unknown,table:string):Promise<Result<string|null>>;}