admindb 2.1.1 → 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 (91) hide show
  1. package/README.md +41 -12
  2. package/dist/app.js +230 -1
  3. package/dist/auth/config.js +121 -1
  4. package/dist/auth/crypto.js +108 -1
  5. package/dist/auth/index.js +21 -1
  6. package/dist/auth/middleware.js +85 -1
  7. package/dist/auth/routes.js +70 -1
  8. package/dist/auth/types.js +7 -1
  9. package/dist/cli/args.js +320 -1
  10. package/dist/cli/config.js +128 -1
  11. package/dist/cli/index.js +20 -1
  12. package/dist/cli/runner.js +211 -1
  13. package/dist/cli.js +4 -1
  14. package/dist/data/chain.d.ts +1 -0
  15. package/dist/data/chain.js +707 -0
  16. package/dist/data/datasets.js +129 -1
  17. package/dist/data/detector.d.ts +1 -1
  18. package/dist/data/detector.js +511 -1
  19. package/dist/data/engine.d.ts +1 -1
  20. package/dist/data/engine.js +245 -1
  21. package/dist/data/index.d.ts +1 -1
  22. package/dist/data/index.js +22 -1
  23. package/dist/data/strategies.d.ts +1 -1
  24. package/dist/data/strategies.js +339 -1
  25. package/dist/data/types.d.ts +1 -1
  26. package/dist/data/types.js +5 -1
  27. package/dist/db/database.d.ts +1 -1
  28. package/dist/db/database.js +477 -1
  29. package/dist/db/export.d.ts +1 -1
  30. package/dist/db/export.js +90 -1
  31. package/dist/db/filters.js +142 -1
  32. package/dist/db/index.js +24 -1
  33. package/dist/db/introspection.js +167 -1
  34. package/dist/db/manager.js +351 -1
  35. package/dist/db/migrations.js +107 -1
  36. package/dist/db/postgres.d.ts +1 -1
  37. package/dist/db/postgres.js +735 -1
  38. package/dist/db/types.d.ts +1 -1
  39. package/dist/db/types.js +6 -1
  40. package/dist/index.js +52 -1
  41. package/dist/public/css/app.css +1 -1
  42. package/dist/public/js/api.js +1 -1
  43. package/dist/public/js/browse.js +1 -1
  44. package/dist/public/js/common.js +1 -0
  45. package/dist/public/js/databases.js +1 -1
  46. package/dist/public/js/designer.js +1 -1
  47. package/dist/public/js/forms.js +1 -1
  48. package/dist/public/js/home.js +1 -0
  49. package/dist/public/js/inspector.js +1 -1
  50. package/dist/public/js/query.js +1 -1
  51. package/dist/public/js/schema.js +1 -1
  52. package/dist/public/js/seed.js +1 -1
  53. package/dist/routes/api/erd.d.ts +1 -0
  54. package/dist/routes/api/erd.js +50 -0
  55. package/dist/routes/api/helpers.d.ts +1 -1
  56. package/dist/routes/api/helpers.js +163 -1
  57. package/dist/routes/api/import-export.js +83 -1
  58. package/dist/routes/api/index.js +48 -1
  59. package/dist/routes/api/query.js +78 -1
  60. package/dist/routes/api/rows.js +362 -1
  61. package/dist/routes/api/seed.js +153 -1
  62. package/dist/routes/api/tables.js +231 -1
  63. package/dist/routes/databases.js +228 -1
  64. package/dist/routes/index.js +19 -1
  65. package/dist/routes/pages.js +397 -1
  66. package/dist/serverless.js +174 -1
  67. package/dist/sql/classifier.js +24 -1
  68. package/dist/sql/error-analyzer.d.ts +1 -0
  69. package/dist/sql/error-analyzer.js +550 -0
  70. package/dist/sql/generator.js +319 -1
  71. package/dist/sql/index.js +18 -1
  72. package/dist/types/api.js +2 -1
  73. package/dist/utils/colors.js +54 -1
  74. package/dist/utils/common.d.ts +1 -1
  75. package/dist/utils/common.js +225 -1
  76. package/dist/utils/csv.js +67 -1
  77. package/dist/utils/datatype.js +181 -1
  78. package/dist/utils/icons.js +81 -1
  79. package/dist/utils/index.js +20 -1
  80. package/dist/utils/logger.js +36 -1
  81. package/dist/views/layouts/main.hbs +14 -1
  82. package/dist/views/pages/erd.hbs +1241 -0
  83. package/dist/views/pages/home.hbs +98 -2
  84. package/dist/views/pages/info.hbs +107 -0
  85. package/dist/views/pages/query.hbs +54 -12
  86. package/dist/views/pages/seed.hbs +435 -171
  87. package/dist/views/partials/sidebar.hbs +12 -0
  88. package/docs/API.md +6 -1
  89. package/docs/EXAMPLES.md +3 -0
  90. package/package.json +5 -4
  91. package/dist/public/js/icons.js +0 -1
@@ -1 +1,142 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.escapeLike=escapeLike,exports.pushFilterCondition=pushFilterCondition,exports.buildFilterClause=function(filters,availableCols){const conds=[],params=[],colSet=new Set(availableCols);for(const[col,raw]of Object.entries(filters??{})){if(!colSet.has(col))continue;const q=(0,generator_1.quoteIdentifier)(col);if(Array.isArray(raw)){for(const c of raw)c&&"object"==typeof c&&pushFilterCondition(conds,params,q,c);continue}if(raw&&"object"==typeof raw){pushFilterCondition(conds,params,q,raw);continue}const value=String(raw??"").trim();""!==value&&(value.startsWith(">=")?(conds.push(`${q} >= ?`),params.push(value.slice(2).trim())):value.startsWith("<=")?(conds.push(`${q} <= ?`),params.push(value.slice(2).trim())):value.startsWith("!=")?(conds.push(`${q} != ?`),params.push(value.slice(2).trim())):value.startsWith("=")?(conds.push(`${q} = ?`),params.push(value.slice(1).trim())):value.startsWith(">")?(conds.push(`${q} > ?`),params.push(value.slice(1).trim())):value.startsWith("<")?(conds.push(`${q} < ?`),params.push(value.slice(1).trim())):value.endsWith("*")?(conds.push(`${q} LIKE ? ESCAPE '\\'`),params.push(escapeLike(value.slice(0,-1))+"%")):(conds.push(`${q} LIKE ? ESCAPE '\\'`),params.push("%"+escapeLike(value)+"%")))}return{where:conds.length?` WHERE ${conds.join(" AND ")}`:"",params:params}},exports.convertPlaceholdersToPostgres=function(sql,startIdx=1){let idx=startIdx,inString=!1,result="";for(let i=0;i<sql.length;i++){const ch=sql[i];if("'"===ch){if(inString&&"'"===sql[i+1]){result+="''",i++;continue}inString=!inString,result+=ch}else result+="?"!==ch||inString?ch:"$"+idx++}return result};const generator_1=require("../sql/generator");function escapeLike(s){return String(s).replace(/[\\%_]/g,m=>"\\"+m)}function pushFilterCondition(conds,params,quotedCol,c){const value=String(c.value??"");switch(c.op){case"eq":conds.push(`${quotedCol} = ?`),params.push(value);break;case"neq":conds.push(`${quotedCol} != ?`),params.push(value);break;case"gt":conds.push(`${quotedCol} > ?`),params.push(value);break;case"gte":conds.push(`${quotedCol} >= ?`),params.push(value);break;case"lt":conds.push(`${quotedCol} < ?`),params.push(value);break;case"lte":conds.push(`${quotedCol} <= ?`),params.push(value);break;case"like":conds.push(`${quotedCol} LIKE ? ESCAPE '\\'`),params.push("%"+escapeLike(value)+"%");break;case"prefix":conds.push(`${quotedCol} LIKE ? ESCAPE '\\'`),params.push(escapeLike(value)+"%");break;case"between":conds.push(`${quotedCol} >= ?`),params.push(value),conds.push(`${quotedCol} <= ?`),params.push(String(c.max??""));break;case"null":conds.push(`${quotedCol} IS NULL`);break;case"notnull":conds.push(`${quotedCol} IS NOT NULL`)}}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeLike = escapeLike;
4
+ exports.pushFilterCondition = pushFilterCondition;
5
+ exports.buildFilterClause = buildFilterClause;
6
+ exports.convertPlaceholdersToPostgres = convertPlaceholdersToPostgres;
7
+ const generator_1 = require("../sql/generator");
8
+ function escapeLike(s) {
9
+ return String(s).replace(/[\\%_]/g, (m) => '\\' + m);
10
+ }
11
+ function pushFilterCondition(conds, params, quotedCol, c) {
12
+ const value = String(c.value ?? '');
13
+ switch (c.op) {
14
+ case 'eq':
15
+ conds.push(`${quotedCol} = ?`);
16
+ params.push(value);
17
+ break;
18
+ case 'neq':
19
+ conds.push(`${quotedCol} != ?`);
20
+ params.push(value);
21
+ break;
22
+ case 'gt':
23
+ conds.push(`${quotedCol} > ?`);
24
+ params.push(value);
25
+ break;
26
+ case 'gte':
27
+ conds.push(`${quotedCol} >= ?`);
28
+ params.push(value);
29
+ break;
30
+ case 'lt':
31
+ conds.push(`${quotedCol} < ?`);
32
+ params.push(value);
33
+ break;
34
+ case 'lte':
35
+ conds.push(`${quotedCol} <= ?`);
36
+ params.push(value);
37
+ break;
38
+ case 'like':
39
+ conds.push(`${quotedCol} LIKE ? ESCAPE '\\'`);
40
+ params.push('%' + escapeLike(value) + '%');
41
+ break;
42
+ case 'prefix':
43
+ conds.push(`${quotedCol} LIKE ? ESCAPE '\\'`);
44
+ params.push(escapeLike(value) + '%');
45
+ break;
46
+ case 'between':
47
+ conds.push(`${quotedCol} >= ?`);
48
+ params.push(value);
49
+ conds.push(`${quotedCol} <= ?`);
50
+ params.push(String(c.max ?? ''));
51
+ break;
52
+ case 'null':
53
+ conds.push(`${quotedCol} IS NULL`);
54
+ break;
55
+ case 'notnull':
56
+ conds.push(`${quotedCol} IS NOT NULL`);
57
+ break;
58
+ default:
59
+ break;
60
+ }
61
+ }
62
+ function buildFilterClause(filters, availableCols) {
63
+ const conds = [];
64
+ const params = [];
65
+ const colSet = new Set(availableCols);
66
+ for (const [col, raw] of Object.entries(filters ?? {})) {
67
+ if (!colSet.has(col))
68
+ continue;
69
+ const q = (0, generator_1.quoteIdentifier)(col);
70
+ if (Array.isArray(raw)) {
71
+ for (const c of raw) {
72
+ if (c && typeof c === 'object')
73
+ pushFilterCondition(conds, params, q, c);
74
+ }
75
+ continue;
76
+ }
77
+ if (raw && typeof raw === 'object') {
78
+ pushFilterCondition(conds, params, q, raw);
79
+ continue;
80
+ }
81
+ const value = String(raw ?? '').trim();
82
+ if (value === '')
83
+ continue;
84
+ if (value.startsWith('>=')) {
85
+ conds.push(`${q} >= ?`);
86
+ params.push(value.slice(2).trim());
87
+ }
88
+ else if (value.startsWith('<=')) {
89
+ conds.push(`${q} <= ?`);
90
+ params.push(value.slice(2).trim());
91
+ }
92
+ else if (value.startsWith('!=')) {
93
+ conds.push(`${q} != ?`);
94
+ params.push(value.slice(2).trim());
95
+ }
96
+ else if (value.startsWith('=')) {
97
+ conds.push(`${q} = ?`);
98
+ params.push(value.slice(1).trim());
99
+ }
100
+ else if (value.startsWith('>')) {
101
+ conds.push(`${q} > ?`);
102
+ params.push(value.slice(1).trim());
103
+ }
104
+ else if (value.startsWith('<')) {
105
+ conds.push(`${q} < ?`);
106
+ params.push(value.slice(1).trim());
107
+ }
108
+ else if (value.endsWith('*')) {
109
+ conds.push(`${q} LIKE ? ESCAPE '\\'`);
110
+ params.push(escapeLike(value.slice(0, -1)) + '%');
111
+ }
112
+ else {
113
+ conds.push(`${q} LIKE ? ESCAPE '\\'`);
114
+ params.push('%' + escapeLike(value) + '%');
115
+ }
116
+ }
117
+ return { where: conds.length ? ` WHERE ${conds.join(' AND ')}` : '', params };
118
+ }
119
+ function convertPlaceholdersToPostgres(sql, startIdx = 1) {
120
+ let idx = startIdx;
121
+ let inString = false;
122
+ let result = '';
123
+ for (let i = 0; i < sql.length; i++) {
124
+ const ch = sql[i];
125
+ if (ch === "'") {
126
+ if (inString && sql[i + 1] === "'") {
127
+ result += "''";
128
+ i++;
129
+ continue;
130
+ }
131
+ inString = !inString;
132
+ result += ch;
133
+ }
134
+ else if (ch === '?' && !inString) {
135
+ result += `$${idx++}`;
136
+ }
137
+ else {
138
+ result += ch;
139
+ }
140
+ }
141
+ return result;
142
+ }
package/dist/db/index.js CHANGED
@@ -1 +1,24 @@
1
- var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__exportStar=this&&this.__exportStar||function(m,exports){for(var p in m)"default"===p||Object.prototype.hasOwnProperty.call(exports,p)||__createBinding(exports,m,p)};Object.defineProperty(exports,"__esModule",{value:!0}),__exportStar(require("./types"),exports),__exportStar(require("./filters"),exports),__exportStar(require("./introspection"),exports),__exportStar(require("./migrations"),exports),__exportStar(require("./database"),exports),__exportStar(require("./postgres"),exports),__exportStar(require("./manager"),exports),__exportStar(require("./export"),exports);
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./filters"), exports);
19
+ __exportStar(require("./introspection"), exports);
20
+ __exportStar(require("./migrations"), exports);
21
+ __exportStar(require("./database"), exports);
22
+ __exportStar(require("./postgres"), exports);
23
+ __exportStar(require("./manager"), exports);
24
+ __exportStar(require("./export"), exports);
@@ -1 +1,167 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.listTablesSync=function(db){return db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all().map(r=>({name:r.name}))},exports.getTableInfoSync=getTableInfoSync,exports.getSchemaSync=function(db,table){const info=getTableInfoSync(db,table);if(!info||0===info.columns.length)throw new Error(`Table "${table}" does not exist.`);const foreignKeys=info.foreignKeys,indexes=info.indexes,uniqueCols=new Set,indexedCols=new Set;for(const ix of indexes)ix.unique&&ix.columns.forEach(c=>uniqueCols.add(c)),"c"===ix.origin&&ix.columns.forEach(c=>indexedCols.add(c));const refs=getIncomingFks(db,table),refsByTo=new Map;for(const r of refs){const arr=refsByTo.get(r.to)??[];arr.push({table:r.table,from:r.from}),refsByTo.set(r.to,arr)}const fkByFrom=new Map(foreignKeys.map(fk=>[fk.from,fk])),columns=info.columns.map(c=>{const unique=c.pk>0||uniqueCols.has(c.name),indexed=indexedCols.has(c.name),referencedBy=refsByTo.get(c.name)??[],fk=fkByFrom.get(c.name)??null,dropBlockers=[];return c.pk>0&&dropBlockers.push("Is part of the primary key"),unique&&0===c.pk&&dropBlockers.push("Has a UNIQUE constraint"),indexed&&dropBlockers.push("Is used by an index"),referencedBy.length&&dropBlockers.push(`Referenced by a foreign key in "${referencedBy[0].table}"`),fk&&dropBlockers.push("Is part of a foreign key"),{...c,unique:unique,indexed:indexed,referencedBy:referencedBy,fk:fk,canDrop:0===dropBlockers.length,dropBlockers:dropBlockers}});return{table:table,columns:columns,foreignKeys:foreignKeys,indexes:indexes,references:refs}},exports.getReferencingTablesSync=function(db,table){const refs=getIncomingFks(db,table),byTable=new Map;for(const r of refs){const arr=byTable.get(r.table)??[];arr.push({from:r.from,to:r.to}),byTable.set(r.table,arr)}const result=[];for(const[t,tRefs]of byTable){let refCount=0;try{const conds=tRefs.map(r=>`${(0,generator_1.quoteIdentifier)(r.from)} IS NOT NULL`),row=db.prepare(`SELECT COUNT(*) AS c FROM ${(0,generator_1.quoteIdentifier)(t)} WHERE ${conds.join(" OR ")}`).get();refCount=Number(row.c)}catch{}result.push({table:t,refs:tRefs,refCount:refCount})}return result.sort((a,b)=>a.table.localeCompare(b.table)),result};const generator_1=require("../sql/generator");function getIncomingFks(db,targetTable){const refs=[];for(const t of function(db){return db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").all().map(r=>r.name)}(db))if(t!==targetTable)try{const fks=db.prepare(`PRAGMA foreign_key_list(${(0,generator_1.quoteIdentifier)(t)})`).all();for(const fk of fks)fk.table===targetTable&&refs.push({table:t,from:fk.from,to:fk.to??""})}catch{}return refs}function getTableInfoSync(db,table){const cols=db.prepare(`PRAGMA table_info(${(0,generator_1.quoteIdentifier)(table)})`).all(),fks=db.prepare(`PRAGMA foreign_key_list(${(0,generator_1.quoteIdentifier)(table)})`).all(),primaryKey=cols.filter(c=>c.pk>0).sort((a,b)=>a.pk-b.pk).map(c=>c.name),indexes=db.prepare(`PRAGMA index_list(${(0,generator_1.quoteIdentifier)(table)})`).all().map(ix=>{const ixCols=db.prepare(`PRAGMA index_info(${(0,generator_1.quoteIdentifier)(ix.name)})`).all().map(c=>c.name),sqlRow=db.prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ?").get(ix.name);return{name:ix.name,unique:!!ix.unique,partial:ix.partial,origin:ix.origin,columns:ixCols,sql:sqlRow?.sql??null}}),ddlRow=db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(table);return{table:table,columns:cols.map(c=>({cid:c.cid,name:c.name,type:c.type,notnull:c.notnull,dflt_value:c.dflt_value,pk:c.pk})),foreignKeys:fks.map(f=>({id:f.id,seq:f.seq,table:f.table,from:f.from,to:f.to,on_update:f.on_update,on_delete:f.on_delete})),primaryKey:primaryKey,indexes:indexes,sql:ddlRow?.sql??null}}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listTablesSync = listTablesSync;
4
+ exports.getTableInfoSync = getTableInfoSync;
5
+ exports.getSchemaSync = getSchemaSync;
6
+ exports.getReferencingTablesSync = getReferencingTablesSync;
7
+ const generator_1 = require("../sql/generator");
8
+ function listTablesSync(db) {
9
+ const rows = db
10
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
11
+ .all();
12
+ return rows.map((r) => ({ name: r.name }));
13
+ }
14
+ function getTableNames(db) {
15
+ return db
16
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
17
+ .all().map((r) => r.name);
18
+ }
19
+ function getIncomingFks(db, targetTable) {
20
+ const refs = [];
21
+ for (const t of getTableNames(db)) {
22
+ if (t === targetTable)
23
+ continue;
24
+ try {
25
+ const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(t)})`).all();
26
+ for (const fk of fks) {
27
+ if (fk.table === targetTable)
28
+ refs.push({ table: t, from: fk.from, to: fk.to ?? '' });
29
+ }
30
+ }
31
+ catch {
32
+ }
33
+ }
34
+ return refs;
35
+ }
36
+ function getTableInfoSync(db, table) {
37
+ const cols = db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
38
+ const fks = db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
39
+ const primaryKey = cols
40
+ .filter((c) => c.pk > 0)
41
+ .sort((a, b) => a.pk - b.pk)
42
+ .map((c) => c.name);
43
+ const indexRows = db
44
+ .prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`)
45
+ .all();
46
+ const indexes = indexRows.map((ix) => {
47
+ const ixCols = db
48
+ .prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`)
49
+ .all().map((c) => c.name);
50
+ const sqlRow = db
51
+ .prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ?")
52
+ .get(ix.name);
53
+ return {
54
+ name: ix.name,
55
+ unique: !!ix.unique,
56
+ partial: ix.partial,
57
+ origin: ix.origin,
58
+ columns: ixCols,
59
+ sql: sqlRow?.sql ?? null,
60
+ };
61
+ });
62
+ const ddlRow = db
63
+ .prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?")
64
+ .get(table);
65
+ return {
66
+ table,
67
+ columns: cols.map((c) => ({
68
+ cid: c.cid,
69
+ name: c.name,
70
+ type: c.type,
71
+ notnull: c.notnull,
72
+ dflt_value: c.dflt_value,
73
+ pk: c.pk,
74
+ })),
75
+ foreignKeys: fks.map((f) => ({
76
+ id: f.id,
77
+ seq: f.seq,
78
+ table: f.table,
79
+ from: f.from,
80
+ to: f.to,
81
+ on_update: f.on_update,
82
+ on_delete: f.on_delete,
83
+ })),
84
+ primaryKey,
85
+ indexes,
86
+ sql: ddlRow?.sql ?? null,
87
+ };
88
+ }
89
+ function getSchemaSync(db, table) {
90
+ const info = getTableInfoSync(db, table);
91
+ if (!info || info.columns.length === 0)
92
+ throw new Error(`Table "${table}" does not exist.`);
93
+ const foreignKeys = info.foreignKeys;
94
+ const indexes = info.indexes;
95
+ const uniqueCols = new Set();
96
+ const indexedCols = new Set();
97
+ for (const ix of indexes) {
98
+ if (ix.unique)
99
+ ix.columns.forEach((c) => uniqueCols.add(c));
100
+ if (ix.origin === 'c')
101
+ ix.columns.forEach((c) => indexedCols.add(c));
102
+ }
103
+ const refs = getIncomingFks(db, table);
104
+ const refsByTo = new Map();
105
+ for (const r of refs) {
106
+ const arr = refsByTo.get(r.to) ?? [];
107
+ arr.push({ table: r.table, from: r.from });
108
+ refsByTo.set(r.to, arr);
109
+ }
110
+ const fkByFrom = new Map(foreignKeys.map((fk) => [fk.from, fk]));
111
+ const columns = info.columns.map((c) => {
112
+ const unique = c.pk > 0 || uniqueCols.has(c.name);
113
+ const indexed = indexedCols.has(c.name);
114
+ const referencedBy = refsByTo.get(c.name) ?? [];
115
+ const fk = fkByFrom.get(c.name) ?? null;
116
+ const dropBlockers = [];
117
+ if (c.pk > 0)
118
+ dropBlockers.push('Is part of the primary key');
119
+ if (unique && c.pk === 0)
120
+ dropBlockers.push('Has a UNIQUE constraint');
121
+ if (indexed)
122
+ dropBlockers.push('Is used by an index');
123
+ if (referencedBy.length)
124
+ dropBlockers.push(`Referenced by a foreign key in "${referencedBy[0].table}"`);
125
+ if (fk)
126
+ dropBlockers.push('Is part of a foreign key');
127
+ return {
128
+ ...c,
129
+ unique,
130
+ indexed,
131
+ referencedBy,
132
+ fk,
133
+ canDrop: dropBlockers.length === 0,
134
+ dropBlockers,
135
+ };
136
+ });
137
+ return {
138
+ table,
139
+ columns,
140
+ foreignKeys,
141
+ indexes,
142
+ references: refs,
143
+ };
144
+ }
145
+ function getReferencingTablesSync(db, table) {
146
+ const refs = getIncomingFks(db, table);
147
+ const byTable = new Map();
148
+ for (const r of refs) {
149
+ const arr = byTable.get(r.table) ?? [];
150
+ arr.push({ from: r.from, to: r.to });
151
+ byTable.set(r.table, arr);
152
+ }
153
+ const result = [];
154
+ for (const [t, tRefs] of byTable) {
155
+ let refCount = 0;
156
+ try {
157
+ const conds = tRefs.map((r) => `${(0, generator_1.quoteIdentifier)(r.from)} IS NOT NULL`);
158
+ const row = db.prepare(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(t)} WHERE ${conds.join(' OR ')}`).get();
159
+ refCount = Number(row.c);
160
+ }
161
+ catch {
162
+ }
163
+ result.push({ table: t, refs: tRefs, refCount });
164
+ }
165
+ result.sort((a, b) => a.table.localeCompare(b.table));
166
+ return result;
167
+ }