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,477 @@
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)},__importDefault=this&&this.__importDefault||function(mod){return mod&&mod.__esModule?mod:{default:mod}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.SqliteDatabase=void 0;const better_sqlite3_1=__importDefault(require("better-sqlite3")),generator_1=require("../sql/generator"),common_1=require("../utils/common"),types_1=require("./types"),filters_1=require("./filters"),introspection_1=require("./introspection"),migrations_1=require("./migrations");__exportStar(require("./types"),exports),__exportStar(require("./filters"),exports);exports.SqliteDatabase=class{db;logger;path;isReadOnly;dialect="sqlite";constructor(dbPath,logger,options={}){this.path=dbPath,this.logger=logger,this.isReadOnly=!!options.readonly,this.db=new better_sqlite3_1.default(dbPath,{readonly:this.isReadOnly}),this.db.exec("PRAGMA foreign_keys = ON;"),this.isReadOnly?this.db.exec("PRAGMA query_only = ON;"):(this.db.exec("PRAGMA journal_mode = WAL;"),this.initSchema()),this.logger.info(`Opened SQLite database at ${dbPath}${this.isReadOnly?" (read-only)":""}`)}readonlyBlocked(){return{success:!1,error:"Database is open in read-only mode — write operations are disabled."}}close(){try{this.db.close()}catch(err){this.logger.warn(`Failed to close database: ${(0,common_1.errorMessage)(err)}`)}}initSchema(){this.db.exec(`\n CREATE TABLE IF NOT EXISTS ${(0,generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (\n ${(0,generator_1.quoteIdentifier)("id")} INTEGER PRIMARY KEY AUTOINCREMENT,\n ${(0,generator_1.quoteIdentifier)("name")} TEXT NOT NULL UNIQUE,\n ${(0,generator_1.quoteIdentifier)("sql")} TEXT NOT NULL,\n ${(0,generator_1.quoteIdentifier)("created_at")} TEXT NOT NULL DEFAULT (datetime('now'))\n );\n `)}async tryRun(fn){try{return{success:!0,data:await Promise.resolve(fn())}}catch(err){const msg=(0,common_1.errorMessage)(err);return this.logger.error(msg),{success:!1,error:msg}}}transaction(fn){return this.isReadOnly?Promise.resolve(this.readonlyBlocked()):this.tryRun(()=>{this.db.exec("BEGIN");try{const res=fn();return this.db.exec("COMMIT"),res}catch(err){try{this.db.exec("ROLLBACK")}catch{}throw err}})}toBind(value){return void 0===value?null:"boolean"==typeof value?value?1:0:(value instanceof Uint8Array||Buffer.isBuffer(value),value)}async all(sql,params=[]){return this.tryRun(()=>this.db.prepare(sql).all(...params))}async run(sql,params=[]){return this.isReadOnly?this.readonlyBlocked():this.tryRun(()=>{const info=this.db.prepare(sql).run(...params);return{changes:Number(info.changes),lastInsertRowid:void 0===info.lastInsertRowid||null===info.lastInsertRowid?null:Number(info.lastInsertRowid)}})}async execResult(sql){return this.isReadOnly?this.readonlyBlocked():this.tryRun(()=>(this.db.exec(sql),{changes:void 0}))}hasMultipleStatements(sql){const s=String(sql??"").trim();if(!s)return!1;try{return this.db.prepare(s),!1}catch(err){return/more than one statement/i.test((0,common_1.errorMessage)(err))}}async runWrite(sql){if(this.isReadOnly)return this.readonlyBlocked();const trimmed=String(sql).trim().replace(/;+\s*$/,"");try{const info=this.db.prepare(trimmed).run();return{success:!0,data:{changes:Number(info.changes)}}}catch{try{return this.db.exec(String(sql)),{success:!0,data:{changes:void 0}}}catch(err2){const msg=(0,common_1.errorMessage)(err2);return this.logger.error(msg),{success:!1,error:msg}}}}async listTables(){return this.tryRun(()=>(0,introspection_1.listTablesSync)(this.db))}async getTableInfo(table){return this.tryRun(()=>(0,introspection_1.getTableInfoSync)(this.db,table))}async getRowCount(table,filters){return this.tryRun(()=>{const info=this.getTableInfoSync(table),{where:where,params:params}=(0,filters_1.buildFilterClause)(filters,info?.columns.map(c=>c.name)??[]),row=this.db.prepare(`SELECT COUNT(*) AS c FROM ${(0,generator_1.quoteIdentifier)(table)}${where}`).get(...params);return Number(row.c)})}async getRows(table,opts={}){return this.tryRun(()=>{const info=this.getTableInfoSync(table),cols=info?.columns??[],orderCol=opts.orderBy||info?.primaryKey?.[0]||cols[0]?.name,orderDir="desc"===opts.orderDir?"DESC":"ASC",limit=opts.limit&&opts.limit>0?opts.limit:200,offset=((opts.page&&opts.page>0?opts.page:1)-1)*limit,{where:where,params:params}=(0,filters_1.buildFilterClause)(opts.filters,cols.map(c=>c.name));let sql=`SELECT rowid AS _rowid_, * FROM ${(0,generator_1.quoteIdentifier)(table)}${where}`;return orderCol&&(sql+=` ORDER BY ${(0,generator_1.quoteIdentifier)(orderCol)} ${orderDir}`),sql+=` LIMIT ${limit} OFFSET ${offset}`,this.db.prepare(sql).all(...params)})}async getAllRows(table){return this.tryRun(()=>this.db.prepare(`SELECT rowid AS _rowid_, * FROM ${(0,generator_1.quoteIdentifier)(table)}`).all())}async getRow(table,where){return this.tryRun(()=>{const conds=where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`),sql=`SELECT rowid AS _rowid_, * FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${conds.join(" AND ")} LIMIT 1`,row=this.db.prepare(sql).get(...where.map(w=>this.toBind(w.value)));return row||null})}async insertRow(table,fields){const cols=fields.map(f=>(0,generator_1.quoteIdentifier)(f.column)),sql=`INSERT INTO ${(0,generator_1.quoteIdentifier)(table)} (${cols.join(", ")}) VALUES (${fields.map(()=>"?").join(", ")})`;return this.run(sql,fields.map(f=>this.toBind(f.value)))}async insertRows(table,rows){return this.transaction(()=>{let inserted=0;for(const fields of rows){if(!fields.length)continue;const cols=fields.map(f=>(0,generator_1.quoteIdentifier)(f.column)),sql=`INSERT INTO ${(0,generator_1.quoteIdentifier)(table)} (${cols.join(", ")}) VALUES (${fields.map(()=>"?").join(", ")})`;this.db.prepare(sql).run(...fields.map(f=>this.toBind(f.value))),inserted+=1}return{inserted:inserted,skipped:rows.length-inserted}})}async updateRow(table,fields,where){const sets=fields.map(f=>`${(0,generator_1.quoteIdentifier)(f.column)} = ?`),conds=where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`),sql=`UPDATE ${(0,generator_1.quoteIdentifier)(table)} SET ${sets.join(", ")} WHERE ${conds.join(" AND ")}`;return this.run(sql,[...fields.map(f=>this.toBind(f.value)),...where.map(w=>this.toBind(w.value))])}async updateRows(table,rows){return this.transaction(()=>{let updated=0;for(const{fields:fields,where:where}of rows){if(!fields.length||!where.length)continue;const sets=fields.map(f=>`${(0,generator_1.quoteIdentifier)(f.column)} = ?`),conds=where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`),sql=`UPDATE ${(0,generator_1.quoteIdentifier)(table)} SET ${sets.join(", ")} WHERE ${conds.join(" AND ")}`,info=this.db.prepare(sql).run(...fields.map(f=>this.toBind(f.value)),...where.map(w=>this.toBind(w.value)));updated+=Number(info.changes)}return{updated:updated}})}async deleteRow(table,where){const conds=where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`),sql=`DELETE FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${conds.join(" AND ")}`;return this.run(sql,where.map(w=>this.toBind(w.value)))}async deleteRows(table,rows){return this.transaction(()=>{let deleted=0;for(const where of rows){if(!where.length)continue;const conds=where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`),sql=`DELETE FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${conds.join(" AND ")}`,info=this.db.prepare(sql).run(...where.map(w=>this.toBind(w.value)));deleted+=Number(info.changes)}return{deleted:deleted}})}async getRowsByPks(table,pks){return this.tryRun(()=>{const groups=[],params=[];for(const where of pks)where.length&&(groups.push(where.map(w=>`${(0,generator_1.quoteIdentifier)(w.column)} = ?`).join(" AND ")),params.push(...where.map(w=>this.toBind(w.value))));if(!groups.length)return[];const sql=`SELECT rowid AS _rowid_, * FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${groups.map(g=>`(${g})`).join(" OR ")}`;return this.db.prepare(sql).all(...params)})}async getCreateStatement(table){return this.tryRun(()=>{const row=this.db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(table);return row?.sql??null})}async listSavedQueries(){return this.all(`SELECT id, name, sql, created_at FROM ${(0,generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} ORDER BY name COLLATE NOCASE`)}async saveQuery(name,sql){return this.run(`INSERT INTO ${(0,generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (name, sql) VALUES (?, ?)`,[name,sql])}async deleteSavedQuery(id){return this.run(`DELETE FROM ${(0,generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} WHERE id = ?`,[Number(id)])}async getSchema(table){return this.tryRun(()=>(0,introspection_1.getSchemaSync)(this.db,table))}async getReferencingTables(table){return this.tryRun(()=>(0,introspection_1.getReferencingTablesSync)(this.db,table))}async getRowsByFk(table,column,value,limit=50){return this.tryRun(()=>{const n=Math.max(1,Math.min(500,Number(limit)||50)),countRow=this.db.prepare(`SELECT COUNT(*) AS c FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${(0,generator_1.quoteIdentifier)(column)} = ?`).get(value);return{rows:this.db.prepare(`SELECT * FROM ${(0,generator_1.quoteIdentifier)(table)} WHERE ${(0,generator_1.quoteIdentifier)(column)} = ? LIMIT ${n}`).all(value),total:Number(countRow.c)}})}async renameTable(oldName,newName){return this.execResult((0,generator_1.generateRenameTable)(oldName,newName))}async addColumn(table,col){return this.isReadOnly?this.readonlyBlocked():this.tryRun(()=>{if(col.primaryKey)return(0,migrations_1.modifyTableStructureSync)(this.db,table,existingCols=>[...existingCols,col]);try{const colCopy={...col,unique:!1},sql=(0,generator_1.generateAddColumn)(table,colCopy);if(this.db.exec(sql),col.unique){const idxName=`idx_${table}_${col.name}_unique`;this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS ${(0,generator_1.quoteIdentifier)(idxName)} ON ${(0,generator_1.quoteIdentifier)(table)} (${(0,generator_1.quoteIdentifier)(col.name)});`)}return{changes:1}}catch{return(0,migrations_1.modifyTableStructureSync)(this.db,table,existingCols=>[...existingCols,col])}})}async modifyColumn(table,oldColName,newColDef){return this.isReadOnly?this.readonlyBlocked():this.tryRun(()=>{const newName=String(newColDef.name||oldColName).trim(),existing=this.db.prepare(`PRAGMA table_info(${(0,generator_1.quoteIdentifier)(table)})`).all().find(c=>c.name===oldColName);if(!existing)throw new Error(`Column "${oldColName}" does not exist in table "${table}".`);const existingFk=this.db.prepare(`PRAGMA foreign_key_list(${(0,generator_1.quoteIdentifier)(table)})`).all().find(f=>f.from===oldColName),indexRows=this.db.prepare(`PRAGMA index_list(${(0,generator_1.quoteIdentifier)(table)})`).all();let existingIsUnique=!1;for(const ix of indexRows)if(ix.unique){const ixCols=this.db.prepare(`PRAGMA index_info(${(0,generator_1.quoteIdentifier)(ix.name)})`).all().map(c=>c.name);if(1===ixCols.length&&ixCols[0]===oldColName){existingIsUnique=!0;break}}const typeMatches=!newColDef.type||newColDef.type.toUpperCase()===existing.type.toUpperCase(),pkMatches=void 0===newColDef.primaryKey||newColDef.primaryKey===existing.pk>0,notNullMatches=void 0===newColDef.notNull||newColDef.notNull===!!existing.notnull,uniqueMatches=void 0===newColDef.unique||newColDef.unique===existingIsUnique,defaultMatches=void 0===newColDef.defaultValue||String(newColDef.defaultValue??"").trim()===String(existing.dflt_value??"").trim(),fkMatches=void 0===newColDef.foreignKey||!newColDef.foreignKey&&!existingFk||newColDef.foreignKey&&existingFk&&newColDef.foreignKey.table===existingFk.table&&newColDef.foreignKey.column===existingFk.to;if(oldColName!==newName&&typeMatches&&pkMatches&&notNullMatches&&uniqueMatches&&defaultMatches&&fkMatches){this.db.exec((0,generator_1.generateRenameColumn)(table,oldColName,newName));const existingIdxNames=new Set(indexRows.map(i=>i.name));for(const suffix of["","_unique"]){const defaultOldIdx=`idx_${table}_${oldColName}${suffix}`;if(existingIdxNames.has(defaultOldIdx)){const newIdxName=`idx_${table}_${newName}${suffix}`,uniqueKeyword="_unique"===suffix?"UNIQUE ":"";try{this.db.exec(`DROP INDEX ${(0,generator_1.quoteIdentifier)(defaultOldIdx)};`),this.db.exec(`CREATE ${uniqueKeyword}INDEX IF NOT EXISTS ${(0,generator_1.quoteIdentifier)(newIdxName)} ON ${(0,generator_1.quoteIdentifier)(table)} (${(0,generator_1.quoteIdentifier)(newName)});`)}catch{}}}return{changes:1}}return oldColName===newName&&typeMatches&&pkMatches&&notNullMatches&&uniqueMatches&&defaultMatches&&fkMatches?{changes:0}:(0,migrations_1.modifyTableStructureSync)(this.db,table,cols=>{const idx=cols.findIndex(c=>c.name===oldColName);if(-1===idx)throw new Error(`Column "${oldColName}" does not exist in table "${table}".`);const updated=[...cols];return updated[idx]={...updated[idx],name:newName,type:newColDef.type||updated[idx].type,primaryKey:void 0!==newColDef.primaryKey?newColDef.primaryKey:updated[idx].primaryKey,notNull:void 0!==newColDef.notNull?newColDef.notNull:updated[idx].notNull,unique:void 0!==newColDef.unique?newColDef.unique:updated[idx].unique,defaultValue:void 0!==newColDef.defaultValue?newColDef.defaultValue:updated[idx].defaultValue,foreignKey:void 0!==newColDef.foreignKey?newColDef.foreignKey:updated[idx].foreignKey},updated},oldColName,newName)})}async renameColumn(table,oldName,newName){return this.isReadOnly?this.readonlyBlocked():this.tryRun(()=>{this.db.exec((0,generator_1.generateRenameColumn)(table,oldName,newName));const indexRows=this.db.prepare(`PRAGMA index_list(${(0,generator_1.quoteIdentifier)(table)})`).all(),existingIdxNames=new Set(indexRows.map(i=>i.name));for(const suffix of["","_unique"]){const defaultOldIdx=`idx_${table}_${oldName}${suffix}`;if(existingIdxNames.has(defaultOldIdx)){const newIdxName=`idx_${table}_${newName}${suffix}`,uniqueKeyword="_unique"===suffix?"UNIQUE ":"";try{this.db.exec(`DROP INDEX ${(0,generator_1.quoteIdentifier)(defaultOldIdx)};`),this.db.exec(`CREATE ${uniqueKeyword}INDEX IF NOT EXISTS ${(0,generator_1.quoteIdentifier)(newIdxName)} ON ${(0,generator_1.quoteIdentifier)(table)} (${(0,generator_1.quoteIdentifier)(newName)});`)}catch{}}}return{changes:1}})}async dropColumn(table,column){return this.execResult((0,generator_1.generateDropColumn)(table,column))}async dropTable(table){return this.execResult((0,generator_1.generateDropTable)(table))}async createIndex(table,index){return this.execResult((0,generator_1.generateCreateIndex)(table,index))}async dropIndex(indexName){return this.execResult((0,generator_1.generateDropIndex)(indexName))}getTableInfoSync(table){try{const cols=this.db.prepare(`PRAGMA table_info(${(0,generator_1.quoteIdentifier)(table)})`).all();if(!cols.length)return null;const primaryKey=cols.filter(c=>c.pk>0).sort((a,b)=>a.pk-b.pk).map(c=>c.name);return{table:table,columns:cols,foreignKeys:[],primaryKey:primaryKey,indexes:[],sql:null}}catch{return null}}};
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.SqliteDatabase = void 0;
21
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
22
+ const generator_1 = require("../sql/generator");
23
+ const common_1 = require("../utils/common");
24
+ const types_1 = require("./types");
25
+ const filters_1 = require("./filters");
26
+ const introspection_1 = require("./introspection");
27
+ const migrations_1 = require("./migrations");
28
+ __exportStar(require("./types"), exports);
29
+ __exportStar(require("./filters"), exports);
30
+ class SqliteDatabase {
31
+ db;
32
+ logger;
33
+ path;
34
+ isReadOnly;
35
+ dialect = 'sqlite';
36
+ constructor(dbPath, logger, options = {}) {
37
+ this.path = dbPath;
38
+ this.logger = logger;
39
+ this.isReadOnly = !!options.readonly;
40
+ this.db = new better_sqlite3_1.default(dbPath, { readonly: this.isReadOnly });
41
+ this.db.exec('PRAGMA foreign_keys = ON;');
42
+ if (this.isReadOnly) {
43
+ this.db.exec('PRAGMA query_only = ON;');
44
+ }
45
+ else {
46
+ this.db.exec('PRAGMA journal_mode = WAL;');
47
+ this.initSchema();
48
+ }
49
+ this.logger.info(`Opened SQLite database at ${dbPath}${this.isReadOnly ? ' (read-only)' : ''}`);
50
+ }
51
+ readonlyBlocked() {
52
+ return { success: false, error: 'Database is open in read-only mode — write operations are disabled.' };
53
+ }
54
+ close() {
55
+ try {
56
+ this.db.close();
57
+ }
58
+ catch (err) {
59
+ this.logger.warn(`Failed to close database: ${(0, common_1.errorMessage)(err)}`);
60
+ }
61
+ }
62
+ initSchema() {
63
+ this.db.exec(`
64
+ CREATE TABLE IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (
65
+ ${(0, generator_1.quoteIdentifier)('id')} INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ ${(0, generator_1.quoteIdentifier)('name')} TEXT NOT NULL UNIQUE,
67
+ ${(0, generator_1.quoteIdentifier)('sql')} TEXT NOT NULL,
68
+ ${(0, generator_1.quoteIdentifier)('created_at')} TEXT NOT NULL DEFAULT (datetime('now'))
69
+ );
70
+ `);
71
+ }
72
+ async tryRun(fn) {
73
+ try {
74
+ const data = await Promise.resolve(fn());
75
+ return { success: true, data };
76
+ }
77
+ catch (err) {
78
+ const msg = (0, common_1.errorMessage)(err);
79
+ this.logger.error(msg);
80
+ return { success: false, error: msg };
81
+ }
82
+ }
83
+ transaction(fn) {
84
+ if (this.isReadOnly)
85
+ return Promise.resolve(this.readonlyBlocked());
86
+ return this.tryRun(() => {
87
+ this.db.exec('BEGIN');
88
+ try {
89
+ const res = fn();
90
+ this.db.exec('COMMIT');
91
+ return res;
92
+ }
93
+ catch (err) {
94
+ try {
95
+ this.db.exec('ROLLBACK');
96
+ }
97
+ catch { }
98
+ throw err;
99
+ }
100
+ });
101
+ }
102
+ toBind(value) {
103
+ if (value === undefined)
104
+ return null;
105
+ if (typeof value === 'boolean')
106
+ return value ? 1 : 0;
107
+ if (value instanceof Uint8Array || Buffer.isBuffer(value))
108
+ return value;
109
+ return value;
110
+ }
111
+ async all(sql, params = []) {
112
+ return this.tryRun(() => this.db.prepare(sql).all(...params));
113
+ }
114
+ async run(sql, params = []) {
115
+ if (this.isReadOnly)
116
+ return this.readonlyBlocked();
117
+ return this.tryRun(() => {
118
+ const info = this.db.prepare(sql).run(...params);
119
+ return {
120
+ changes: Number(info.changes),
121
+ lastInsertRowid: info.lastInsertRowid === undefined || info.lastInsertRowid === null ? null : Number(info.lastInsertRowid),
122
+ };
123
+ });
124
+ }
125
+ async execResult(sql) {
126
+ if (this.isReadOnly)
127
+ return this.readonlyBlocked();
128
+ return this.tryRun(() => {
129
+ this.db.exec(sql);
130
+ return { changes: undefined };
131
+ });
132
+ }
133
+ hasMultipleStatements(sql) {
134
+ const s = String(sql ?? '').trim();
135
+ if (!s)
136
+ return false;
137
+ try {
138
+ this.db.prepare(s);
139
+ return false;
140
+ }
141
+ catch (err) {
142
+ return /more than one statement/i.test((0, common_1.errorMessage)(err));
143
+ }
144
+ }
145
+ async runWrite(sql) {
146
+ if (this.isReadOnly)
147
+ return this.readonlyBlocked();
148
+ const trimmed = String(sql).trim().replace(/;+\s*$/, '');
149
+ try {
150
+ const stmt = this.db.prepare(trimmed);
151
+ const info = stmt.run();
152
+ return { success: true, data: { changes: Number(info.changes) } };
153
+ }
154
+ catch {
155
+ try {
156
+ this.db.exec(String(sql));
157
+ return { success: true, data: { changes: undefined } };
158
+ }
159
+ catch (err2) {
160
+ const msg = (0, common_1.errorMessage)(err2);
161
+ this.logger.error(msg);
162
+ return { success: false, error: msg };
163
+ }
164
+ }
165
+ }
166
+ async listTables() {
167
+ return this.tryRun(() => (0, introspection_1.listTablesSync)(this.db));
168
+ }
169
+ async getTableInfo(table) {
170
+ return this.tryRun(() => (0, introspection_1.getTableInfoSync)(this.db, table));
171
+ }
172
+ async getRowCount(table, filters) {
173
+ return this.tryRun(() => {
174
+ const info = this.getTableInfoSync(table);
175
+ const { where, params } = (0, filters_1.buildFilterClause)(filters, info?.columns.map((c) => c.name) ?? []);
176
+ const row = this.db
177
+ .prepare(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(table)}${where}`)
178
+ .get(...params);
179
+ return Number(row.c);
180
+ });
181
+ }
182
+ async getRows(table, opts = {}) {
183
+ return this.tryRun(() => {
184
+ const info = this.getTableInfoSync(table);
185
+ const cols = info?.columns ?? [];
186
+ const orderCol = opts.orderBy || info?.primaryKey?.[0] || cols[0]?.name;
187
+ const orderDir = opts.orderDir === 'desc' ? 'DESC' : 'ASC';
188
+ const limit = opts.limit && opts.limit > 0 ? opts.limit : 200;
189
+ const page = opts.page && opts.page > 0 ? opts.page : 1;
190
+ const offset = (page - 1) * limit;
191
+ const { where, params } = (0, filters_1.buildFilterClause)(opts.filters, cols.map((c) => c.name));
192
+ let sql = `SELECT rowid AS _rowid_, * FROM ${(0, generator_1.quoteIdentifier)(table)}${where}`;
193
+ if (orderCol)
194
+ sql += ` ORDER BY ${(0, generator_1.quoteIdentifier)(orderCol)} ${orderDir}`;
195
+ sql += ` LIMIT ${limit} OFFSET ${offset}`;
196
+ return this.db.prepare(sql).all(...params);
197
+ });
198
+ }
199
+ async getAllRows(table) {
200
+ return this.tryRun(() => this.db.prepare(`SELECT rowid AS _rowid_, * FROM ${(0, generator_1.quoteIdentifier)(table)}`).all());
201
+ }
202
+ async getRow(table, where) {
203
+ return this.tryRun(() => {
204
+ const conds = where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`);
205
+ const sql = `SELECT rowid AS _rowid_, * FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${conds.join(' AND ')} LIMIT 1`;
206
+ const row = this.db.prepare(sql).get(...where.map((w) => this.toBind(w.value)));
207
+ return row ? row : null;
208
+ });
209
+ }
210
+ async insertRow(table, fields) {
211
+ const cols = fields.map((f) => (0, generator_1.quoteIdentifier)(f.column));
212
+ const sql = `INSERT INTO ${(0, generator_1.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${fields.map(() => '?').join(', ')})`;
213
+ return this.run(sql, fields.map((f) => this.toBind(f.value)));
214
+ }
215
+ async insertRows(table, rows) {
216
+ return this.transaction(() => {
217
+ let inserted = 0;
218
+ for (const fields of rows) {
219
+ if (!fields.length)
220
+ continue;
221
+ const cols = fields.map((f) => (0, generator_1.quoteIdentifier)(f.column));
222
+ const sql = `INSERT INTO ${(0, generator_1.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${fields.map(() => '?').join(', ')})`;
223
+ this.db.prepare(sql).run(...fields.map((f) => this.toBind(f.value)));
224
+ inserted += 1;
225
+ }
226
+ return { inserted, skipped: rows.length - inserted };
227
+ });
228
+ }
229
+ async updateRow(table, fields, where) {
230
+ const sets = fields.map((f) => `${(0, generator_1.quoteIdentifier)(f.column)} = ?`);
231
+ const conds = where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`);
232
+ const sql = `UPDATE ${(0, generator_1.quoteIdentifier)(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')}`;
233
+ return this.run(sql, [...fields.map((f) => this.toBind(f.value)), ...where.map((w) => this.toBind(w.value))]);
234
+ }
235
+ async updateRows(table, rows) {
236
+ return this.transaction(() => {
237
+ let updated = 0;
238
+ for (const { fields, where } of rows) {
239
+ if (!fields.length || !where.length)
240
+ continue;
241
+ const sets = fields.map((f) => `${(0, generator_1.quoteIdentifier)(f.column)} = ?`);
242
+ const conds = where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`);
243
+ const sql = `UPDATE ${(0, generator_1.quoteIdentifier)(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')}`;
244
+ const info = this.db.prepare(sql).run(...[...fields.map((f) => this.toBind(f.value)), ...where.map((w) => this.toBind(w.value))]);
245
+ updated += Number(info.changes);
246
+ }
247
+ return { updated };
248
+ });
249
+ }
250
+ async deleteRow(table, where) {
251
+ const conds = where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`);
252
+ const sql = `DELETE FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${conds.join(' AND ')}`;
253
+ return this.run(sql, where.map((w) => this.toBind(w.value)));
254
+ }
255
+ async deleteRows(table, rows) {
256
+ return this.transaction(() => {
257
+ let deleted = 0;
258
+ for (const where of rows) {
259
+ if (!where.length)
260
+ continue;
261
+ const conds = where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`);
262
+ const sql = `DELETE FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${conds.join(' AND ')}`;
263
+ const info = this.db.prepare(sql).run(...where.map((w) => this.toBind(w.value)));
264
+ deleted += Number(info.changes);
265
+ }
266
+ return { deleted };
267
+ });
268
+ }
269
+ async getRowsByPks(table, pks) {
270
+ return this.tryRun(() => {
271
+ const groups = [];
272
+ const params = [];
273
+ for (const where of pks) {
274
+ if (!where.length)
275
+ continue;
276
+ groups.push(where.map((w) => `${(0, generator_1.quoteIdentifier)(w.column)} = ?`).join(' AND '));
277
+ params.push(...where.map((w) => this.toBind(w.value)));
278
+ }
279
+ if (!groups.length)
280
+ return [];
281
+ const sql = `SELECT rowid AS _rowid_, * FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${groups.map((g) => `(${g})`).join(' OR ')}`;
282
+ return this.db.prepare(sql).all(...params);
283
+ });
284
+ }
285
+ async getCreateStatement(table) {
286
+ return this.tryRun(() => {
287
+ const row = this.db
288
+ .prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?")
289
+ .get(table);
290
+ return row?.sql ?? null;
291
+ });
292
+ }
293
+ async listSavedQueries() {
294
+ return this.all(`SELECT id, name, sql, created_at FROM ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} ORDER BY name COLLATE NOCASE`);
295
+ }
296
+ async saveQuery(name, sql) {
297
+ return this.run(`INSERT INTO ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (name, sql) VALUES (?, ?)`, [name, sql]);
298
+ }
299
+ async deleteSavedQuery(id) {
300
+ return this.run(`DELETE FROM ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} WHERE id = ?`, [Number(id)]);
301
+ }
302
+ async getSettings() {
303
+ return this.tryRun(() => {
304
+ const pragmas = ['journal_mode', 'synchronous', 'foreign_keys', 'page_size', 'encoding', 'auto_vacuum', 'mmap_size', 'temp_store'];
305
+ const settings = {};
306
+ for (const p of pragmas) {
307
+ try {
308
+ const row = this.db.prepare(`PRAGMA ${p}`).get();
309
+ if (row && p in row) {
310
+ settings[p] = String(row[p]);
311
+ }
312
+ }
313
+ catch {
314
+ }
315
+ }
316
+ return settings;
317
+ });
318
+ }
319
+ async getSchema(table) {
320
+ return this.tryRun(() => (0, introspection_1.getSchemaSync)(this.db, table));
321
+ }
322
+ async getReferencingTables(table) {
323
+ return this.tryRun(() => (0, introspection_1.getReferencingTablesSync)(this.db, table));
324
+ }
325
+ async getRowsByFk(table, column, value, limit = 50) {
326
+ return this.tryRun(() => {
327
+ const n = Math.max(1, Math.min(500, Number(limit) || 50));
328
+ const countRow = this.db
329
+ .prepare(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${(0, generator_1.quoteIdentifier)(column)} = ?`)
330
+ .get(value);
331
+ const rows = this.db
332
+ .prepare(`SELECT * FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${(0, generator_1.quoteIdentifier)(column)} = ? LIMIT ${n}`)
333
+ .all(value);
334
+ return { rows, total: Number(countRow.c) };
335
+ });
336
+ }
337
+ async renameTable(oldName, newName) {
338
+ return this.execResult((0, generator_1.generateRenameTable)(oldName, newName));
339
+ }
340
+ async addColumn(table, col) {
341
+ if (this.isReadOnly)
342
+ return this.readonlyBlocked();
343
+ return this.tryRun(() => {
344
+ if (col.primaryKey) {
345
+ return (0, migrations_1.modifyTableStructureSync)(this.db, table, (existingCols) => [...existingCols, col]);
346
+ }
347
+ try {
348
+ const colCopy = { ...col, unique: false };
349
+ const sql = (0, generator_1.generateAddColumn)(table, colCopy);
350
+ this.db.exec(sql);
351
+ if (col.unique) {
352
+ const idxName = `idx_${table}_${col.name}_unique`;
353
+ this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(idxName)} ON ${(0, generator_1.quoteIdentifier)(table)} (${(0, generator_1.quoteIdentifier)(col.name)});`);
354
+ }
355
+ return { changes: 1 };
356
+ }
357
+ catch {
358
+ return (0, migrations_1.modifyTableStructureSync)(this.db, table, (existingCols) => [...existingCols, col]);
359
+ }
360
+ });
361
+ }
362
+ async modifyColumn(table, oldColName, newColDef) {
363
+ if (this.isReadOnly)
364
+ return this.readonlyBlocked();
365
+ return this.tryRun(() => {
366
+ const newName = String(newColDef.name || oldColName).trim();
367
+ const rawCols = this.db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
368
+ const existing = rawCols.find((c) => c.name === oldColName);
369
+ if (!existing)
370
+ throw new Error(`Column "${oldColName}" does not exist in table "${table}".`);
371
+ const fks = this.db.prepare(`PRAGMA foreign_key_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
372
+ const existingFk = fks.find((f) => f.from === oldColName);
373
+ const indexRows = this.db.prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
374
+ let existingIsUnique = false;
375
+ for (const ix of indexRows) {
376
+ if (ix.unique) {
377
+ const ixCols = this.db.prepare(`PRAGMA index_info(${(0, generator_1.quoteIdentifier)(ix.name)})`).all().map((c) => c.name);
378
+ if (ixCols.length === 1 && ixCols[0] === oldColName) {
379
+ existingIsUnique = true;
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ const typeMatches = !newColDef.type || newColDef.type.toUpperCase() === existing.type.toUpperCase();
385
+ const pkMatches = newColDef.primaryKey === undefined || newColDef.primaryKey === (existing.pk > 0);
386
+ const notNullMatches = newColDef.notNull === undefined || newColDef.notNull === (!!existing.notnull);
387
+ const uniqueMatches = newColDef.unique === undefined || newColDef.unique === existingIsUnique;
388
+ const defaultMatches = newColDef.defaultValue === undefined || String(newColDef.defaultValue ?? '').trim() === String(existing.dflt_value ?? '').trim();
389
+ const fkMatches = newColDef.foreignKey === undefined || ((!newColDef.foreignKey && !existingFk) ||
390
+ (newColDef.foreignKey && existingFk && newColDef.foreignKey.table === existingFk.table && newColDef.foreignKey.column === existingFk.to));
391
+ if (oldColName !== newName && typeMatches && pkMatches && notNullMatches && uniqueMatches && defaultMatches && fkMatches) {
392
+ this.db.exec((0, generator_1.generateRenameColumn)(table, oldColName, newName));
393
+ const existingIdxNames = new Set(indexRows.map((i) => i.name));
394
+ for (const suffix of ['', '_unique']) {
395
+ const defaultOldIdx = `idx_${table}_${oldColName}${suffix}`;
396
+ if (existingIdxNames.has(defaultOldIdx)) {
397
+ const newIdxName = `idx_${table}_${newName}${suffix}`;
398
+ const uniqueKeyword = suffix === '_unique' ? 'UNIQUE ' : '';
399
+ try {
400
+ this.db.exec(`DROP INDEX ${(0, generator_1.quoteIdentifier)(defaultOldIdx)};`);
401
+ this.db.exec(`CREATE ${uniqueKeyword}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(newIdxName)} ON ${(0, generator_1.quoteIdentifier)(table)} (${(0, generator_1.quoteIdentifier)(newName)});`);
402
+ }
403
+ catch { }
404
+ }
405
+ }
406
+ return { changes: 1 };
407
+ }
408
+ if (oldColName === newName && typeMatches && pkMatches && notNullMatches && uniqueMatches && defaultMatches && fkMatches) {
409
+ return { changes: 0 };
410
+ }
411
+ return (0, migrations_1.modifyTableStructureSync)(this.db, table, (cols) => {
412
+ const idx = cols.findIndex((c) => c.name === oldColName);
413
+ if (idx === -1)
414
+ throw new Error(`Column "${oldColName}" does not exist in table "${table}".`);
415
+ const updated = [...cols];
416
+ updated[idx] = {
417
+ ...updated[idx],
418
+ name: newName,
419
+ type: newColDef.type || updated[idx].type,
420
+ primaryKey: newColDef.primaryKey !== undefined ? newColDef.primaryKey : updated[idx].primaryKey,
421
+ notNull: newColDef.notNull !== undefined ? newColDef.notNull : updated[idx].notNull,
422
+ unique: newColDef.unique !== undefined ? newColDef.unique : updated[idx].unique,
423
+ defaultValue: newColDef.defaultValue !== undefined ? newColDef.defaultValue : updated[idx].defaultValue,
424
+ foreignKey: newColDef.foreignKey !== undefined ? newColDef.foreignKey : updated[idx].foreignKey,
425
+ };
426
+ return updated;
427
+ }, oldColName, newName);
428
+ });
429
+ }
430
+ async renameColumn(table, oldName, newName) {
431
+ if (this.isReadOnly)
432
+ return this.readonlyBlocked();
433
+ return this.tryRun(() => {
434
+ this.db.exec((0, generator_1.generateRenameColumn)(table, oldName, newName));
435
+ const indexRows = this.db.prepare(`PRAGMA index_list(${(0, generator_1.quoteIdentifier)(table)})`).all();
436
+ const existingIdxNames = new Set(indexRows.map((i) => i.name));
437
+ for (const suffix of ['', '_unique']) {
438
+ const defaultOldIdx = `idx_${table}_${oldName}${suffix}`;
439
+ if (existingIdxNames.has(defaultOldIdx)) {
440
+ const newIdxName = `idx_${table}_${newName}${suffix}`;
441
+ const uniqueKeyword = suffix === '_unique' ? 'UNIQUE ' : '';
442
+ try {
443
+ this.db.exec(`DROP INDEX ${(0, generator_1.quoteIdentifier)(defaultOldIdx)};`);
444
+ this.db.exec(`CREATE ${uniqueKeyword}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(newIdxName)} ON ${(0, generator_1.quoteIdentifier)(table)} (${(0, generator_1.quoteIdentifier)(newName)});`);
445
+ }
446
+ catch { }
447
+ }
448
+ }
449
+ return { changes: 1 };
450
+ });
451
+ }
452
+ async dropColumn(table, column) {
453
+ return this.execResult((0, generator_1.generateDropColumn)(table, column));
454
+ }
455
+ async dropTable(table) {
456
+ return this.execResult((0, generator_1.generateDropTable)(table));
457
+ }
458
+ async createIndex(table, index) {
459
+ return this.execResult((0, generator_1.generateCreateIndex)(table, index));
460
+ }
461
+ async dropIndex(indexName) {
462
+ return this.execResult((0, generator_1.generateDropIndex)(indexName));
463
+ }
464
+ getTableInfoSync(table) {
465
+ try {
466
+ const cols = this.db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
467
+ if (!cols.length)
468
+ return null;
469
+ const primaryKey = cols.filter((c) => c.pk > 0).sort((a, b) => a.pk - b.pk).map((c) => c.name);
470
+ return { table, columns: cols, foreignKeys: [], primaryKey, indexes: [], sql: null };
471
+ }
472
+ catch {
473
+ return null;
474
+ }
475
+ }
476
+ }
477
+ exports.SqliteDatabase = SqliteDatabase;
@@ -1 +1 @@
1
- import type {IDatabase} from './types';export interface DumpResult{success:boolean;data?:string;error?:string;}export declare function generateSqlDump(db:IDatabase):Promise<DumpResult>;
1
+ import type {IDatabase} from './types';export interface DumpResult{success:boolean;data?:string;error?:string;}export declare function generateSqlDump(db:IDatabase):Promise<DumpResult>;export declare function generateSchemaDump(db:IDatabase):Promise<DumpResult>;
package/dist/db/export.js CHANGED
@@ -1 +1,90 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.generateSqlDump=async function(db){const out=[];"sqlite"===db.dialect?(out.push("PRAGMA foreign_keys=OFF;"),out.push("BEGIN TRANSACTION;")):out.push("BEGIN;");const tables=await db.listTables();if(!tables.success)return{success:!1,error:tables.error??"Failed to list tables."};for(const t of tables.data??[]){const name=t.name,create=await db.getCreateStatement(name);create.success&&create.data&&out.push(create.data.trim());const info=await db.getTableInfo(name),rows=await db.getAllRows(name);if(info.success&&rows.success){const cols=info.data?.columns??[];for(const row of rows.data??[]){const fields=cols.map(c=>({column:c.name,value:row[c.name]}));out.push((0,generator_1.generateInsert)(name,fields))}}out.push("")}return out.push("COMMIT;"),{success:!0,data:out.join("\n")}};const generator_1=require("../sql/generator");
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSqlDump = generateSqlDump;
4
+ exports.generateSchemaDump = generateSchemaDump;
5
+ const generator_1 = require("../sql/generator");
6
+ async function generateSqlDump(db) {
7
+ const out = [];
8
+ if (db.dialect === 'sqlite') {
9
+ out.push('PRAGMA foreign_keys=OFF;');
10
+ out.push('BEGIN TRANSACTION;');
11
+ }
12
+ else {
13
+ out.push('BEGIN;');
14
+ }
15
+ const tables = await db.listTables();
16
+ if (!tables.success)
17
+ return { success: false, error: tables.error ?? 'Failed to list tables.' };
18
+ for (const t of tables.data ?? []) {
19
+ const name = t.name;
20
+ const create = await db.getCreateStatement(name);
21
+ if (create.success && create.data) {
22
+ out.push(create.data.trim());
23
+ }
24
+ const info = await db.getTableInfo(name);
25
+ const rows = await db.getAllRows(name);
26
+ if (info.success && rows.success) {
27
+ const cols = info.data?.columns ?? [];
28
+ for (const row of rows.data ?? []) {
29
+ const fields = cols.map((c) => ({ column: c.name, value: row[c.name] }));
30
+ out.push((0, generator_1.generateInsert)(name, fields));
31
+ }
32
+ }
33
+ out.push('');
34
+ }
35
+ out.push('COMMIT;');
36
+ return { success: true, data: out.join('\n') };
37
+ }
38
+ async function generateSchemaDump(db) {
39
+ const out = [];
40
+ if (db.dialect === 'sqlite') {
41
+ const allSchemas = await db.all(`SELECT sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_saved_queries'`);
42
+ if (allSchemas.success && allSchemas.data) {
43
+ for (const row of allSchemas.data) {
44
+ let stmt = String(row.sql).trim();
45
+ if (!stmt.endsWith(';'))
46
+ stmt += ';';
47
+ out.push(stmt);
48
+ out.push('');
49
+ }
50
+ }
51
+ else {
52
+ return { success: false, error: allSchemas.error ?? 'Failed to read sqlite_master.' };
53
+ }
54
+ }
55
+ else {
56
+ const tables = await db.listTables();
57
+ if (!tables.success)
58
+ return { success: false, error: tables.error ?? 'Failed to list tables.' };
59
+ for (const t of tables.data ?? []) {
60
+ const name = t.name;
61
+ const info = await db.getTableInfo(name);
62
+ if (info.success && info.data) {
63
+ if (info.data.sql) {
64
+ out.push(info.data.sql.trim());
65
+ }
66
+ if (info.data.foreignKeys && info.data.foreignKeys.length > 0) {
67
+ for (const fk of info.data.foreignKeys) {
68
+ if (fk.from && fk.to && fk.table) {
69
+ let action = '';
70
+ if (fk.on_delete && fk.on_delete !== 'NO ACTION')
71
+ action += ` ON DELETE ${fk.on_delete}`;
72
+ if (fk.on_update && fk.on_update !== 'NO ACTION')
73
+ action += ` ON UPDATE ${fk.on_update}`;
74
+ out.push(`ALTER TABLE "${name}" ADD FOREIGN KEY ("${fk.from}") REFERENCES "${fk.table}" ("${fk.to}")${action};`);
75
+ }
76
+ }
77
+ }
78
+ if (info.data.indexes && info.data.indexes.length > 0) {
79
+ for (const idx of info.data.indexes) {
80
+ if (idx.sql) {
81
+ out.push(idx.sql.trim() + ';');
82
+ }
83
+ }
84
+ }
85
+ out.push('');
86
+ }
87
+ }
88
+ }
89
+ return { success: true, data: out.join('\n').trim() };
90
+ }