admindb 2.1.1 → 2.3.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 (119) hide show
  1. package/README.md +368 -335
  2. package/dist/app.js +227 -1
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +109 -1
  5. package/dist/auth/crypto.js +98 -1
  6. package/dist/auth/index.js +21 -1
  7. package/dist/auth/middleware.js +78 -1
  8. package/dist/auth/routes.js +64 -1
  9. package/dist/auth/types.js +7 -1
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.js +309 -1
  12. package/dist/cli/config.js +118 -1
  13. package/dist/cli/index.js +20 -1
  14. package/dist/cli/runner.js +208 -1
  15. package/dist/cli.js +4 -1
  16. package/dist/data/chain.d.ts +1 -0
  17. package/dist/data/chain.js +130 -0
  18. package/dist/data/datasets.d.ts +1 -1
  19. package/dist/data/datasets.js +171 -1
  20. package/dist/data/detector.d.ts +1 -1
  21. package/dist/data/detector.js +535 -1
  22. package/dist/data/engine.d.ts +1 -1
  23. package/dist/data/engine.js +586 -1
  24. package/dist/data/index.d.ts +1 -1
  25. package/dist/data/index.js +30 -1
  26. package/dist/data/prng.d.ts +1 -0
  27. package/dist/data/prng.js +73 -0
  28. package/dist/data/registry.d.ts +1 -0
  29. package/dist/data/registry.js +805 -0
  30. package/dist/data/schema-graph.d.ts +1 -0
  31. package/dist/data/schema-graph.js +210 -0
  32. package/dist/data/strategies.d.ts +1 -1
  33. package/dist/data/strategies.js +100 -1
  34. package/dist/data/templates.d.ts +1 -0
  35. package/dist/data/templates.js +134 -0
  36. package/dist/data/types.d.ts +1 -1
  37. package/dist/data/types.js +5 -1
  38. package/dist/data/validator.d.ts +1 -0
  39. package/dist/data/validator.js +160 -0
  40. package/dist/db/database.d.ts +1 -1
  41. package/dist/db/database.js +472 -1
  42. package/dist/db/export.d.ts +1 -1
  43. package/dist/db/export.js +67 -1
  44. package/dist/db/filters.d.ts +1 -1
  45. package/dist/db/filters.js +119 -1
  46. package/dist/db/index.js +24 -1
  47. package/dist/db/introspection.js +164 -1
  48. package/dist/db/manager.js +327 -1
  49. package/dist/db/migrations.js +100 -1
  50. package/dist/db/postgres.d.ts +1 -1
  51. package/dist/db/postgres.js +687 -1
  52. package/dist/db/types.d.ts +1 -1
  53. package/dist/db/types.js +6 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +52 -1
  56. package/dist/public/css/app.css +1 -1
  57. package/dist/public/js/api.js +1 -1
  58. package/dist/public/js/browse.js +1 -1
  59. package/dist/public/js/common.js +1 -0
  60. package/dist/public/js/databases.js +1 -1
  61. package/dist/public/js/designer.js +1 -1
  62. package/dist/public/js/forms.js +1 -1
  63. package/dist/public/js/home.js +1 -0
  64. package/dist/public/js/inspector.js +1 -1
  65. package/dist/public/js/query.js +1 -1
  66. package/dist/public/js/schema.js +1 -1
  67. package/dist/public/js/seed.js +1 -1
  68. package/dist/routes/api/erd.d.ts +1 -0
  69. package/dist/routes/api/erd.js +45 -0
  70. package/dist/routes/api/helpers.d.ts +1 -1
  71. package/dist/routes/api/helpers.js +213 -1
  72. package/dist/routes/api/import-export.js +82 -1
  73. package/dist/routes/api/index.js +48 -1
  74. package/dist/routes/api/query.js +74 -1
  75. package/dist/routes/api/rows.js +358 -1
  76. package/dist/routes/api/seed.js +167 -1
  77. package/dist/routes/api/tables.js +216 -1
  78. package/dist/routes/databases.js +210 -1
  79. package/dist/routes/index.js +19 -1
  80. package/dist/routes/pages.js +446 -1
  81. package/dist/serverless.js +149 -1
  82. package/dist/sql/classifier.js +24 -1
  83. package/dist/sql/error-analyzer.d.ts +1 -0
  84. package/dist/sql/error-analyzer.js +550 -0
  85. package/dist/sql/generator.d.ts +1 -1
  86. package/dist/sql/generator.js +245 -1
  87. package/dist/sql/index.js +18 -1
  88. package/dist/types/api.d.ts +1 -1
  89. package/dist/types/api.js +2 -1
  90. package/dist/utils/colors.js +54 -1
  91. package/dist/utils/common.d.ts +1 -1
  92. package/dist/utils/common.js +185 -1
  93. package/dist/utils/csv.d.ts +1 -1
  94. package/dist/utils/csv.js +61 -1
  95. package/dist/utils/datatype.js +167 -1
  96. package/dist/utils/icons.js +81 -1
  97. package/dist/utils/index.js +20 -1
  98. package/dist/utils/logger.js +36 -1
  99. package/dist/views/layouts/main.hbs +1 -74
  100. package/dist/views/pages/databases.hbs +1 -233
  101. package/dist/views/pages/designer.hbs +1 -89
  102. package/dist/views/pages/erd.hbs +1 -0
  103. package/dist/views/pages/error.hbs +1 -10
  104. package/dist/views/pages/form.hbs +1 -59
  105. package/dist/views/pages/home.hbs +1 -124
  106. package/dist/views/pages/info.hbs +1 -0
  107. package/dist/views/pages/login.hbs +1 -98
  108. package/dist/views/pages/query.hbs +1 -87
  109. package/dist/views/pages/schema.hbs +1 -298
  110. package/dist/views/pages/seed-select.hbs +251 -0
  111. package/dist/views/pages/seed.hbs +273 -167
  112. package/dist/views/pages/table.hbs +1 -327
  113. package/dist/views/partials/navbar.hbs +1 -83
  114. package/dist/views/partials/sidebar.hbs +1 -117
  115. package/docs/API.md +149 -144
  116. package/docs/EXAMPLES.md +579 -576
  117. package/docs/SECURITY.md +265 -265
  118. package/package.json +79 -78
  119. package/dist/public/js/icons.js +0 -1
@@ -1 +1,446 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerPages=function(router,ctx){const{db:db}=ctx,notFound=(res,message)=>{res.status(404).render("pages/error",{title:"Not found",status:404,error:message})};router.get("/",async(_req,res,next)=>{try{const names=res.locals.tables??[],internalNames=res.locals.internalTables??[];let colCountsMap={};if("postgres"===db.dialect){const colRes=await db.all("SELECT table_name, count(*)::int AS cols FROM information_schema.columns WHERE table_schema = 'public' GROUP BY table_name;");if(colRes.success&&colRes.data)for(const row of colRes.data)row&&"string"==typeof row.table_name&&(colCountsMap[row.table_name]=Number(row.cols??0))}const[stats,saved]=await Promise.all([Promise.all(names.map(async name=>{const countR=await db.getRowCount(name),cols=void 0!==colCountsMap[name]?colCountsMap[name]:(await db.getTableInfo(name)).data?.columns.length??0;return{name:name,count:countR.success?countR.data:0,cols:cols}})),db.listSavedQueries()]),counts={},cols={};let totalRows=0;for(const s of stats)counts[s.name]=s.count,cols[s.name]=s.cols,totalRows+=s.count;res.render("pages/home",{title:"Home",tables:names,counts:counts,cols:cols,totalRows:totalRows,savedQueries:saved.success?saved.data?.length??0:0,internalCount:internalNames.length,dbPath:db.path})}catch(err){next(err)}}),router.get("/tables/:table",async(req,res,next)=>{try{const table=req.params.table,info=await db.getTableInfo(table);if(!info.success||!info.data||0===info.data.columns.length)return notFound(res,`Table "${table}" does not exist.`);const page=Math.max(1,Number.parseInt(String(req.query.page??"1"),10)||1),pageSize=Math.max(1,Number.parseInt(String(req.query.size??"50"),10)||50),orderBy=req.query.orderBy?String(req.query.orderBy):void 0,orderDir="desc"===String(req.query.orderDir??"asc").toLowerCase()?"desc":"asc",filters=(0,common_1.parseFilters)(req.query.f),filterQS=(0,common_1.filtersToQS)(filters),[countR,rowsR,refsR]=await Promise.all([db.getRowCount(table,filters),db.getRows(table,{page:page,limit:pageSize,orderBy:orderBy,orderDir:orderDir,filters:filters}),db.getReferencingTables(table)]),count=countR.success?countR.data:0,rawRows=rowsR.data??[],pages=Math.max(1,Math.ceil(count/pageSize)),basePath=String(res.locals.basePath??""),sizes=[25,50,100,250],colHeaders=info.data.columns.map(c=>{const active=c.name===orderBy,nextDir=active&&"asc"===orderDir?"desc":"asc",qs=`page=1&size=${pageSize}&orderBy=${encodeURIComponent(c.name)}&orderDir=${nextDir}`;return{name:c.name,pk:c.pk,type:c.type,active:active,dir:active?orderDir:null,href:`${basePath}/tables/${encodeURIComponent(table)}?${qs}${filterQS?"&"+filterQS:""}`}}),refColumns=(refsR.data??[]).filter(r=>!r.table.startsWith("_")).map(rt=>{const ref=rt.refs[0];return{table:rt.table,from:ref?.from??"",to:ref?.to??""}}),countEntries=await Promise.all(refColumns.map(async col=>{const values=rawRows.map(r=>r[col.to]).filter(v=>null!=v);if(!values.length)return[col.table,new Map];const placeholders=values.map(()=>"?").join(", "),cR=await db.all(`SELECT ${(0,generator_1.quoteIdentifier)(col.from)} AS fk, COUNT(*) AS c FROM ${(0,generator_1.quoteIdentifier)(col.table)} WHERE ${(0,generator_1.quoteIdentifier)(col.from)} IN (${placeholders}) GROUP BY ${(0,generator_1.quoteIdentifier)(col.from)}`,values),map=new Map;for(const row of cR.data??[])map.set(String(row.fk),Number(row.c));return[col.table,map]})),countMap=new Map(countEntries),rows=function(rawRows,info,table,basePath=""){const pkCols=info.primaryKey.length?info.primaryKey:["_rowid_"];return rawRows.map(row=>{const pkEncoded=(0,common_1.encodePk)(pkCols.map(c=>row[c]));return{cells:info.columns.map(c=>{const raw=row[c.name],v=(0,common_1.normalizeCell)(raw),isNull=null==v,typeUpper=(c.type||"").toUpperCase(),isBlob=typeUpper.includes("BLOB")||typeUpper.includes("BYTEA")||Buffer.isBuffer(raw)||raw instanceof Uint8Array||"string"==typeof v&&(/^0x[0-9a-f]{8,}$/i.test(v)||/^\\x[0-9a-f]{8,}$/i.test(v));let isJson=!1,isImage=!1,isUrl=!1,isColor=!1,blobSize="",blobMime="",blobExt="",blobUrl="";if(!isNull){const str=String(v);if(isBlob){let buf;buf=Buffer.isBuffer(raw)||raw instanceof Uint8Array?Buffer.isBuffer(raw)?raw:Buffer.from(raw):"string"==typeof v&&/^0x[0-9a-f]*$/i.test(v)||"string"==typeof v&&/^\\x[0-9a-f]*$/i.test(v)?Buffer.from(v.slice(2),"hex"):Buffer.from(str,"utf8");const mimeInfo=(0,datatype_1.sniffMimeType)(buf);blobSize=(0,common_1.formatBytes)(buf.length),blobMime=mimeInfo.mime,blobExt=mimeInfo.ext,isImage=mimeInfo.isImage,table&&pkEncoded&&(blobUrl=`${basePath}/api/tables/${encodeURIComponent(table)}/row/${encodeURIComponent(pkEncoded)}/blob/${encodeURIComponent(c.name)}`)}else(0,datatype_1.isJsonString)(v)||typeUpper.includes("JSON")?isJson=(0,datatype_1.isJsonString)(v):/^https?:\/\/[^\s$.?#].[^\s]*$/i.test(str)?isUrl=!0:/^#(?:[0-9a-fA-F]{3}){1,2}$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+/i.test(str)&&(isColor=!0)}return{name:c.name,value:v,isNull:isNull,display:isNull?"":String(v),type:c.type,isBlob:isBlob,isJson:isJson,isImage:isImage,isUrl:isUrl,isColor:isColor,blobSize:blobSize,blobMime:blobMime,blobExt:blobExt,blobUrl:blobUrl}}),pkEncoded:pkEncoded}})}(rawRows,info.data,table,basePath).map((dr,i)=>{const raw=rawRows[i];return{...dr,refs:refColumns.map(col=>{const v=raw[col.to];return{table:col.table,from:col.from,to:col.to,value:null==v?"":String(v),count:null==v?0:countMap.get(col.table)?.get(String(v))??0}})}});res.locals.currentTable=table,res.render("pages/table",{title:table,table:table,info:info.data,rows:rows,count:count,page:page,pageSize:pageSize,pages:pages,pkCols:info.data.primaryKey,colNames:info.data.columns.map(c=>c.name),colHeaders:colHeaders,refColumns:refColumns,hasPk:!0,orderBy:orderBy??"",orderDir:orderDir,sizes:sizes,filters:filters,filterQS:filterQS,firstRow:0===count?0:(page-1)*pageSize+1,lastRow:Math.min(page*pageSize,count),browseConfig:{table:table,filters:filters,pkCols:info.data.primaryKey.length?info.data.primaryKey:["_rowid_"],hasPk:!0,readonly:db.isReadOnly}})}catch(err){next(err)}}),router.get("/tables/:table/schema",async(req,res,next)=>{try{const table=req.params.table,[info,schema]=await Promise.all([db.getTableInfo(table),db.getSchema(table)]);if(!info.success||!info.data||0===info.data.columns.length)return notFound(res,`Table "${table}" does not exist.`);if(!schema.success||!schema.data)return notFound(res,schema.error??"Failed to load schema.");res.locals.currentTable=table,res.render("pages/schema",{title:`Schema · ${table}`,table:table,schema:schema.data,isInternal:table.startsWith("_"),dialect:db.dialect,isSqlite:"sqlite"===db.dialect,isPostgres:"postgres"===db.dialect,config:{table:table,isInternal:table.startsWith("_"),dialect:db.dialect}})}catch(err){next(err)}}),router.get("/tables/:table/seed",async(req,res,next)=>{try{const table=req.params.table,[info,countRes]=await Promise.all([db.getTableInfo(table),db.getRowCount(table)]);if(!info.success||!info.data||0===info.data.columns.length)return notFound(res,`Table "${table}" does not exist.`);const rowCount=countRes.success?countRes.data:0,columns=(0,index_1.buildColumnConfigs)(info.data);res.locals.currentTable=table,res.render("pages/seed",{title:`Seed data · ${table}`,table:table,rowCount:rowCount,colCount:info.data.columns.length,maxRows:index_1.MAX_SEED_ROWS,quickCounts:[10,50,100,500,1e3,index_1.MAX_SEED_ROWS],seedConfig:{table:table,columns:columns,rowCount:rowCount,maxRows:index_1.MAX_SEED_ROWS}})}catch(err){next(err)}}),router.get("/tables/:table/rows/new",async(req,res,next)=>{try{const table=req.params.table,info=await db.getTableInfo(table);if(!info.success||!info.data||0===info.data.columns.length)return notFound(res,`Table "${table}" does not exist.`);const pkColumns=info.data.primaryKey;res.locals.currentTable=table,res.render("pages/form",{title:`New row · ${table}`,table:table,mode:"insert",pk:null,pkColumns:pkColumns,infoSummary:`${info.data.columns.length} column(s)`+(pkColumns.length?` · PK: ${pkColumns.join(", ")}`:""),config:{table:table,mode:"insert",pk:null,pkColumns:pkColumns}})}catch(err){next(err)}}),router.get("/tables/:table/rows/:id/edit",async(req,res,next)=>{try{const table=req.params.table,info=await db.getTableInfo(table);if(!info.success||!info.data||0===info.data.columns.length)return notFound(res,`Table "${table}" does not exist.`);const pkColumns=info.data.primaryKey.length?info.data.primaryKey:["_rowid_"];if((0,common_1.decodePk)(req.params.id).length!==pkColumns.length)return notFound(res,"Invalid row identifier.");res.locals.currentTable=table,res.render("pages/form",{title:`Edit row · ${table}`,table:table,mode:"edit",pk:req.params.id,pkColumns:pkColumns,infoSummary:`${info.data.columns.length} column(s)`+(info.data.primaryKey.length?` · PK: ${info.data.primaryKey.join(", ")}`:""),config:{table:table,mode:"edit",pk:req.params.id,pkColumns:pkColumns}})}catch(err){next(err)}}),router.get("/designer",(_req,res)=>{res.render("pages/designer",{title:"New table",dialect:db.dialect,isSqlite:"sqlite"===db.dialect,isPostgres:"postgres"===db.dialect})}),router.get("/query",async(_req,res,next)=>{try{const queries=await db.listSavedQueries();res.render("pages/query",{title:"Query editor",savedQueries:queries.success?queries.data:[]})}catch(err){next(err)}}),router.get("/export",async(_req,res,next)=>{try{const dump=await(0,export_1.generateSqlDump)(db);if(!dump.success||!dump.data)throw new Error(dump.error??"Failed to generate dump.");const stamp=(new Date).toISOString().replace(/[:.]/g,"-");res.setHeader("Content-Type","application/sql; charset=utf-8"),res.setHeader("Content-Disposition",`attachment; filename="admindb-${stamp}.sql"`),res.send(dump.data)}catch(err){next(err)}})};const export_1=require("../db/export"),generator_1=require("../sql/generator"),common_1=require("../utils/common"),datatype_1=require("../utils/datatype"),index_1=require("../data/index");
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPages = registerPages;
4
+ const export_1 = require("../db/export");
5
+ const generator_1 = require("../sql/generator");
6
+ const common_1 = require("../utils/common");
7
+ const datatype_1 = require("../utils/datatype");
8
+ const index_1 = require("../data/index");
9
+ function buildDisplayRows(rawRows, info, table, basePath = '') {
10
+ const pkCols = info.primaryKey.length ? info.primaryKey : ['_rowid_'];
11
+ return rawRows.map((row) => {
12
+ const pkEncoded = (0, common_1.encodePk)(pkCols.map((c) => row[c]));
13
+ return {
14
+ cells: info.columns.map((c) => {
15
+ const raw = row[c.name];
16
+ const v = (0, common_1.normalizeCell)(raw);
17
+ const isNull = v == null;
18
+ const typeUpper = (c.type || '').toUpperCase();
19
+ const isBlob = typeUpper.includes('BLOB') || typeUpper.includes('BYTEA') || Buffer.isBuffer(raw) || raw instanceof Uint8Array || (typeof v === 'string' && (/^0x[0-9a-f]{8,}$/i.test(v) || /^\\x[0-9a-f]{8,}$/i.test(v)));
20
+ let isJson = false;
21
+ let isImage = false;
22
+ let isUrl = false;
23
+ let isColor = false;
24
+ let isAudio = false;
25
+ let isVideo = false;
26
+ let isPdf = false;
27
+ let blobSize = '';
28
+ let blobMime = '';
29
+ let blobExt = '';
30
+ let blobUrl = '';
31
+ if (!isNull) {
32
+ const str = String(v);
33
+ if (isBlob) {
34
+ let buf;
35
+ if (Buffer.isBuffer(raw) || raw instanceof Uint8Array) {
36
+ buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
37
+ }
38
+ else if (typeof v === 'string' && (/^0x[0-9a-f]*$/i.test(v) || /^\\x[0-9a-f]*$/i.test(v))) {
39
+ buf = Buffer.from(v.slice(2), 'hex');
40
+ }
41
+ else {
42
+ buf = Buffer.from(str, 'utf8');
43
+ }
44
+ const mimeInfo = (0, datatype_1.sniffMimeType)(buf);
45
+ blobSize = (0, common_1.formatBytes)(buf.length);
46
+ blobMime = mimeInfo.mime;
47
+ blobExt = mimeInfo.ext;
48
+ isImage = mimeInfo.isImage;
49
+ isAudio = mimeInfo.isAudio;
50
+ isVideo = mimeInfo.isVideo;
51
+ isPdf = mimeInfo.isPdf;
52
+ if (table && pkEncoded) {
53
+ blobUrl = `${basePath}/api/tables/${encodeURIComponent(table)}/row/${encodeURIComponent(pkEncoded)}/blob/${encodeURIComponent(c.name)}`;
54
+ }
55
+ }
56
+ else if ((0, datatype_1.isJsonString)(v) || typeUpper.includes('JSON')) {
57
+ isJson = (0, datatype_1.isJsonString)(v);
58
+ }
59
+ else {
60
+ if (/^https?:\/\/[^\s$.?#].[^\s]*$/i.test(str))
61
+ isUrl = true;
62
+ else if (/^#(?:[0-9a-fA-F]{3}){1,2}$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+/i.test(str))
63
+ isColor = true;
64
+ }
65
+ }
66
+ return {
67
+ name: c.name,
68
+ value: v,
69
+ isNull,
70
+ display: isNull ? '' : String(v),
71
+ type: c.type,
72
+ isBlob,
73
+ isJson,
74
+ isImage,
75
+ isAudio,
76
+ isVideo,
77
+ isPdf,
78
+ isUrl,
79
+ isColor,
80
+ blobSize,
81
+ blobMime,
82
+ blobExt,
83
+ blobUrl,
84
+ };
85
+ }),
86
+ pkEncoded,
87
+ };
88
+ });
89
+ }
90
+ function registerPages(router, ctx) {
91
+ const { db } = ctx;
92
+ const notFound = (res, message) => {
93
+ res.status(404).render('pages/error', { title: 'Not found', status: 404, error: message });
94
+ };
95
+ router.get('/', async (_req, res, next) => {
96
+ try {
97
+ const names = res.locals.tables ?? [];
98
+ const internalNames = res.locals.internalTables ?? [];
99
+ const colCountsMap = {};
100
+ if (db.dialect === 'postgres') {
101
+ const colRes = await db.all(`SELECT table_name, count(*)::int AS cols FROM information_schema.columns WHERE table_schema = 'public' GROUP BY table_name;`);
102
+ if (colRes.success && colRes.data) {
103
+ for (const row of colRes.data) {
104
+ if (row && typeof row.table_name === 'string') {
105
+ colCountsMap[row.table_name] = Number(row.cols ?? 0);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ const [stats, saved] = await Promise.all([
111
+ Promise.all(names.map(async (name) => {
112
+ const countR = await db.getRowCount(name);
113
+ const cols = colCountsMap[name] !== undefined
114
+ ? colCountsMap[name]
115
+ : (await db.getTableInfo(name)).data?.columns.length ?? 0;
116
+ return {
117
+ name,
118
+ count: countR.success ? countR.data : 0,
119
+ cols,
120
+ };
121
+ })),
122
+ db.listSavedQueries(),
123
+ ]);
124
+ const counts = {};
125
+ const cols = {};
126
+ let totalRows = 0;
127
+ for (const s of stats) {
128
+ counts[s.name] = s.count;
129
+ cols[s.name] = s.cols;
130
+ totalRows += s.count;
131
+ }
132
+ res.render('pages/home', {
133
+ title: 'Home',
134
+ tables: names,
135
+ counts,
136
+ cols,
137
+ totalRows,
138
+ savedQueries: saved.success ? (saved.data?.length ?? 0) : 0,
139
+ internalCount: internalNames.length,
140
+ dbPath: db.path,
141
+ });
142
+ }
143
+ catch (err) {
144
+ next(err);
145
+ }
146
+ });
147
+ router.get('/tables/:table', async (req, res, next) => {
148
+ try {
149
+ const table = req.params.table;
150
+ const info = await db.getTableInfo(table);
151
+ if (!info.success || !info.data || info.data.columns.length === 0) {
152
+ return notFound(res, `Table "${table}" does not exist.`);
153
+ }
154
+ const page = Math.max(1, Number.parseInt(String(req.query.page ?? '1'), 10) || 1);
155
+ const pageSize = Math.max(1, Number.parseInt(String(req.query.size ?? '50'), 10) || 50);
156
+ const orderBy = req.query.orderBy ? String(req.query.orderBy) : undefined;
157
+ const orderDir = String(req.query.orderDir ?? 'asc').toLowerCase() === 'desc' ? 'desc' : 'asc';
158
+ const filters = (0, common_1.parseFilters)(req.query.f);
159
+ const filterQS = (0, common_1.filtersToQS)(filters);
160
+ const [countR, rowsR, refsR] = await Promise.all([
161
+ db.getRowCount(table, filters),
162
+ db.getRows(table, { page, limit: pageSize, orderBy, orderDir, filters }),
163
+ db.getReferencingTables(table),
164
+ ]);
165
+ const count = countR.success ? countR.data : 0;
166
+ const rawRows = (rowsR.data ?? []);
167
+ const pages = Math.max(1, Math.ceil(count / pageSize));
168
+ const basePath = String(res.locals.basePath ?? '');
169
+ const sizes = [25, 50, 100, 250];
170
+ const colHeaders = info.data.columns.map((c) => {
171
+ const active = c.name === orderBy;
172
+ const nextDir = active ? (orderDir === 'asc' ? 'desc' : 'asc') : 'asc';
173
+ const qs = `page=1&size=${pageSize}&orderBy=${encodeURIComponent(c.name)}&orderDir=${nextDir}`;
174
+ return {
175
+ name: c.name,
176
+ pk: c.pk,
177
+ type: c.type,
178
+ active,
179
+ dir: active ? orderDir : null,
180
+ href: `${basePath}/tables/${encodeURIComponent(table)}?${qs}${filterQS ? `&${filterQS}` : ''}`,
181
+ };
182
+ });
183
+ const refColumns = (refsR.data ?? [])
184
+ .filter((r) => !r.table.startsWith('_'))
185
+ .map((rt) => ({ table: rt.table, from: rt.refs[0]?.from ?? '', to: rt.refs[0]?.to ?? '' }));
186
+ const countEntries = await Promise.all(refColumns.map(async (col) => {
187
+ const values = rawRows
188
+ .map((r) => r[col.to])
189
+ .filter((v) => v !== null && v !== undefined);
190
+ if (!values.length)
191
+ return [col.table, new Map()];
192
+ const placeholders = values.map(() => '?').join(', ');
193
+ const cR = await db.all(`SELECT ${(0, generator_1.quoteIdentifier)(col.from)} AS fk, COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(col.table)} WHERE ${(0, generator_1.quoteIdentifier)(col.from)} IN (${placeholders}) GROUP BY ${(0, generator_1.quoteIdentifier)(col.from)}`, values);
194
+ const map = new Map();
195
+ for (const row of (cR.data ?? []))
196
+ map.set(String(row.fk), Number(row.c));
197
+ return [col.table, map];
198
+ }));
199
+ const countMap = new Map(countEntries);
200
+ const rows = buildDisplayRows(rawRows, info.data, table, basePath).map((dr, i) => {
201
+ const raw = rawRows[i];
202
+ const refs = refColumns.map((col) => {
203
+ const v = raw[col.to];
204
+ return {
205
+ table: col.table,
206
+ from: col.from,
207
+ to: col.to,
208
+ value: v == null ? '' : String(v),
209
+ count: v == null ? 0 : (countMap.get(col.table)?.get(String(v)) ?? 0),
210
+ };
211
+ });
212
+ return { ...dr, refs };
213
+ });
214
+ res.locals.currentTable = table;
215
+ res.render('pages/table', {
216
+ title: table,
217
+ table,
218
+ info: info.data,
219
+ rows,
220
+ count,
221
+ page,
222
+ pageSize,
223
+ pages,
224
+ pkCols: info.data.primaryKey,
225
+ colNames: info.data.columns.map((c) => c.name),
226
+ colHeaders,
227
+ refColumns,
228
+ hasPk: true,
229
+ orderBy: orderBy ?? '',
230
+ orderDir,
231
+ sizes,
232
+ filters,
233
+ filterQS,
234
+ firstRow: count === 0 ? 0 : (page - 1) * pageSize + 1,
235
+ lastRow: Math.min(page * pageSize, count),
236
+ browseConfig: {
237
+ table,
238
+ filters,
239
+ pkCols: info.data.primaryKey.length ? info.data.primaryKey : ['_rowid_'],
240
+ hasPk: true,
241
+ readonly: db.isReadOnly,
242
+ },
243
+ });
244
+ }
245
+ catch (err) {
246
+ next(err);
247
+ }
248
+ });
249
+ router.get('/tables/:table/schema', async (req, res, next) => {
250
+ try {
251
+ const table = req.params.table;
252
+ const [info, schema] = await Promise.all([db.getTableInfo(table), db.getSchema(table)]);
253
+ if (!info.success || !info.data || info.data.columns.length === 0) {
254
+ return notFound(res, `Table "${table}" does not exist.`);
255
+ }
256
+ if (!schema.success || !schema.data) {
257
+ return notFound(res, schema.error ?? 'Failed to load schema.');
258
+ }
259
+ res.locals.currentTable = table;
260
+ res.render('pages/schema', {
261
+ title: `Schema · ${table}`,
262
+ table,
263
+ schema: schema.data,
264
+ isInternal: table.startsWith('_'),
265
+ dialect: db.dialect,
266
+ isSqlite: db.dialect === 'sqlite',
267
+ isPostgres: db.dialect === 'postgres',
268
+ config: { table, isInternal: table.startsWith('_'), dialect: db.dialect },
269
+ });
270
+ }
271
+ catch (err) {
272
+ next(err);
273
+ }
274
+ });
275
+ router.get('/seed', async (req, res, next) => {
276
+ try {
277
+ const tableNames = res.locals.tables ?? [];
278
+ if (tableNames.length === 0) {
279
+ return res.render('pages/seed-select', {
280
+ title: 'Seed Data Generator',
281
+ tables: [],
282
+ hasTables: false,
283
+ dialect: db.dialect,
284
+ });
285
+ }
286
+ const tableStats = await Promise.all(tableNames.map(async (name) => {
287
+ const [info, countRes] = await Promise.all([db.getTableInfo(name), db.getRowCount(name)]);
288
+ const cols = info.success && info.data ? info.data.columns.length : 0;
289
+ const fks = info.success && info.data ? info.data.foreignKeys.length : 0;
290
+ const rowCount = countRes.success ? countRes.data : 0;
291
+ return {
292
+ name,
293
+ cols,
294
+ fks,
295
+ rowCount,
296
+ };
297
+ }));
298
+ const defaultTable = req.query.table ? String(req.query.table) : tableStats[0]?.name;
299
+ const defaultMode = req.query.mode === 'chain' ? 'chain' : 'single';
300
+ res.render('pages/seed-select', {
301
+ title: 'Seed Data Generator · Choose Table & Mode',
302
+ tables: tableStats,
303
+ hasTables: true,
304
+ defaultTable,
305
+ defaultMode,
306
+ dialect: db.dialect,
307
+ });
308
+ }
309
+ catch (err) {
310
+ next(err);
311
+ }
312
+ });
313
+ router.get('/tables/:table/seed', (req, res) => {
314
+ const table = req.params.table;
315
+ const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
316
+ const bp = res.locals.basePath || '';
317
+ return res.redirect(301, `${bp}/seed/${encodeURIComponent(table)}${qs}`);
318
+ });
319
+ router.get('/seed/:table', async (req, res, next) => {
320
+ try {
321
+ const table = req.params.table;
322
+ const allTables = res.locals.tables ?? [];
323
+ const initialMode = req.query.mode === 'chain' ? 'chain' : 'single';
324
+ const [info, countRes] = await Promise.all([db.getTableInfo(table), db.getRowCount(table)]);
325
+ if (!info.success || !info.data || info.data.columns.length === 0) {
326
+ return notFound(res, `Table "${table}" does not exist.`);
327
+ }
328
+ const rowCount = countRes.success ? countRes.data : 0;
329
+ const columns = (0, index_1.buildColumnConfigs)(info.data);
330
+ res.locals.currentTable = table;
331
+ res.render('pages/seed', {
332
+ title: `Seed data · ${table}`,
333
+ table,
334
+ allTables,
335
+ mode: initialMode,
336
+ isChainMode: initialMode === 'chain',
337
+ rowCount,
338
+ colCount: info.data.columns.length,
339
+ maxRows: index_1.MAX_SEED_ROWS,
340
+ quickCounts: [10, 50, 100, 500, 1000, index_1.MAX_SEED_ROWS],
341
+ seedConfig: { table, columns, rowCount, maxRows: index_1.MAX_SEED_ROWS, mode: initialMode },
342
+ });
343
+ }
344
+ catch (err) {
345
+ next(err);
346
+ }
347
+ });
348
+ router.get('/tables/:table/rows/new', async (req, res, next) => {
349
+ try {
350
+ const table = req.params.table;
351
+ const info = await db.getTableInfo(table);
352
+ if (!info.success || !info.data || info.data.columns.length === 0) {
353
+ return notFound(res, `Table "${table}" does not exist.`);
354
+ }
355
+ const pkColumns = info.data.primaryKey;
356
+ res.locals.currentTable = table;
357
+ res.render('pages/form', {
358
+ title: `New row · ${table}`,
359
+ table,
360
+ mode: 'insert',
361
+ pk: null,
362
+ pkColumns,
363
+ infoSummary: `${info.data.columns.length} column(s)` + (pkColumns.length ? ` · PK: ${pkColumns.join(', ')}` : ''),
364
+ config: { table, mode: 'insert', pk: null, pkColumns },
365
+ });
366
+ }
367
+ catch (err) {
368
+ next(err);
369
+ }
370
+ });
371
+ router.get('/tables/:table/rows/:id/edit', async (req, res, next) => {
372
+ try {
373
+ const table = req.params.table;
374
+ const info = await db.getTableInfo(table);
375
+ if (!info.success || !info.data || info.data.columns.length === 0) {
376
+ return notFound(res, `Table "${table}" does not exist.`);
377
+ }
378
+ const pkColumns = info.data.primaryKey.length ? info.data.primaryKey : ['_rowid_'];
379
+ const pk = (0, common_1.decodePk)(req.params.id);
380
+ if (pk.length !== pkColumns.length) {
381
+ return notFound(res, 'Invalid row identifier.');
382
+ }
383
+ res.locals.currentTable = table;
384
+ res.render('pages/form', {
385
+ title: `Edit row · ${table}`,
386
+ table,
387
+ mode: 'edit',
388
+ pk: req.params.id,
389
+ pkColumns,
390
+ infoSummary: `${info.data.columns.length} column(s)` + (info.data.primaryKey.length ? ` · PK: ${info.data.primaryKey.join(', ')}` : ''),
391
+ config: { table, mode: 'edit', pk: req.params.id, pkColumns },
392
+ });
393
+ }
394
+ catch (err) {
395
+ next(err);
396
+ }
397
+ });
398
+ router.get('/erd', (_req, res) => {
399
+ res.render('pages/erd', { title: 'ER Diagram' });
400
+ });
401
+ router.get('/designer', (_req, res) => {
402
+ res.render('pages/designer', {
403
+ title: 'New table',
404
+ dialect: db.dialect,
405
+ isSqlite: db.dialect === 'sqlite',
406
+ isPostgres: db.dialect === 'postgres',
407
+ });
408
+ });
409
+ router.get('/query', async (_req, res, next) => {
410
+ try {
411
+ const queries = await db.listSavedQueries();
412
+ res.render('pages/query', { title: 'Query editor', savedQueries: queries.success ? queries.data : [] });
413
+ }
414
+ catch (err) {
415
+ next(err);
416
+ }
417
+ });
418
+ router.get('/export', async (_req, res, next) => {
419
+ try {
420
+ const dump = await (0, export_1.generateSqlDump)(db);
421
+ if (!dump.success || !dump.data)
422
+ throw new Error(dump.error ?? 'Failed to generate dump.');
423
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
424
+ res.setHeader('Content-Type', 'application/sql; charset=utf-8');
425
+ res.setHeader('Content-Disposition', `attachment; filename="admindb-${stamp}.sql"`);
426
+ res.send(dump.data);
427
+ }
428
+ catch (err) {
429
+ next(err);
430
+ }
431
+ });
432
+ router.get('/info', async (_req, res, next) => {
433
+ try {
434
+ const settingsRes = await db.getSettings();
435
+ res.render('pages/info', {
436
+ title: 'Database Info',
437
+ settings: settingsRes.success ? settingsRes.data : {},
438
+ dialect: db.dialect,
439
+ dbPath: db.path,
440
+ });
441
+ }
442
+ catch (err) {
443
+ next(err);
444
+ }
445
+ });
446
+ }
@@ -1 +1,149 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.isServerlessEnvironment=function(env=process.env){if(["1","true","yes","on"].includes(String(env.ADMINDB_SERVERLESS??env.SERVERLESS??env.IS_SERVERLESS??"").trim().toLowerCase()))return!0;if(Boolean(env.VERCEL||env.VERCEL_ENV||env.NOW_REGION))return!0;if(Boolean(env.AWS_LAMBDA_FUNCTION_NAME||env.LAMBDA_TASK_ROOT||env.AWS_EXECUTION_ENV))return!0;if(Boolean(env.NETLIFY||env.NETLIFY_LOCAL))return!0;if(Boolean(env.FUNCTION_TARGET||env.FUNCTION_NAME||env.K_SERVICE))return!0;if(Boolean(env.FUNCTIONS_WORKER_RUNTIME||env.WEBSITE_INSTANCE_ID))return!0;if(Boolean(env.CF_PAGES))return!0;return!1},exports.createServerlessHandler=function(options={}){const rawTarget=options.connection||options.dbPath||"",defaultReadonly=!(Boolean(options.pgOptions)||"string"==typeof rawTarget&&(rawTarget.startsWith("postgres://")||rawTarget.startsWith("postgresql://"))||"postgres"===options.db?.dialect),app=(0,app_1.createRouter)({...options,serverless:options.serverless??!0,readonly:options.readonly??defaultReadonly});return(req,res)=>{app(req,res)}},exports.createLambdaHandler=function(options={}){const rawTarget=options.connection||options.dbPath||"",defaultReadonly=!(Boolean(options.pgOptions)||"string"==typeof rawTarget&&(rawTarget.startsWith("postgres://")||rawTarget.startsWith("postgresql://"))||"postgres"===options.db?.dialect),app=(0,app_1.createRouter)({...options,serverless:options.serverless??!0,readonly:options.readonly??defaultReadonly});return async(event,_context)=>new Promise(resolve=>{try{const isV2=Boolean("2.0"===event.version||event.rawPath),method=(isV2?event.requestContext?.http?.method:event.httpMethod)||"GET",rawPath=isV2?event.rawPath||"/":event.path||"/",qs=isV2?event.rawQueryString||"":event.queryStringParameters?Object.entries(event.queryStringParameters).map(([k,v])=>`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&"):"",url=qs?`${rawPath}?${qs}`:rawPath,rawHeaders=event.headers||{},headers={};for(const[k,v]of Object.entries(rawHeaders))null!=v&&(headers[k.toLowerCase()]=String(v));event.cookies&&Array.isArray(event.cookies)&&!headers.cookie&&(headers.cookie=event.cookies.join("; "));let bodyBuffer=null;event.body&&(bodyBuffer=event.isBase64Encoded?Buffer.from(event.body,"base64"):Buffer.from(event.body,"utf8"),headers["content-length"]||(headers["content-length"]=String(bodyBuffer.length)));const socket=new node_net_1.Socket,req=new node_http_1.IncomingMessage(socket);req.url=url,req.method=method,req.headers=headers,req.httpVersion="1.1",req.httpVersionMajor=1,req.httpVersionMinor=1,bodyBuffer&&req.push(bodyBuffer),req.push(null);const res=new node_http_1.ServerResponse(req),chunks=[];res.write=function(chunk,...args){chunk&&chunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk));const cb="function"==typeof args[0]?args[0]:"function"==typeof args[1]?args[1]:void 0;return cb&&cb(),!0},res.end=function(chunk,...args){chunk&&"function"!=typeof chunk&&chunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk));const cb="function"==typeof chunk?chunk:"function"==typeof args[0]?args[0]:"function"==typeof args[1]?args[1]:void 0;cb&&cb();const responseHeaders={},multiValueHeaders={},resHeaders=res.getHeaders();for(const[k,v]of Object.entries(resHeaders))Array.isArray(v)?(multiValueHeaders[k]=v.map(String),responseHeaders[k]=v.map(String).join(", ")):void 0!==v&&(responseHeaders[k]=String(v));const fullBody=Buffer.concat(chunks),contentType=String(responseHeaders["content-type"]||""),isBinary=!(contentType.startsWith("text/")||contentType.includes("application/json")||contentType.includes("application/javascript")||contentType.includes("application/xml")||contentType.includes("image/svg+xml"));return resolve({statusCode:res.statusCode||200,headers:responseHeaders,multiValueHeaders:Object.keys(multiValueHeaders).length>0?multiValueHeaders:void 0,body:isBinary?fullBody.toString("base64"):fullBody.toString("utf8"),isBase64Encoded:isBinary}),res},app(req,res)}catch(err){resolve({statusCode:500,headers:{"content-type":"application/json"},body:JSON.stringify({success:!1,error:err?.message||"Server error"}),isBase64Encoded:!1})}})};const node_http_1=require("node:http"),node_net_1=require("node:net"),app_1=require("./app");
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isServerlessEnvironment = isServerlessEnvironment;
4
+ exports.createServerlessHandler = createServerlessHandler;
5
+ exports.createLambdaHandler = createLambdaHandler;
6
+ const node_http_1 = require("node:http");
7
+ const node_net_1 = require("node:net");
8
+ const app_1 = require("./app");
9
+ const common_1 = require("./utils/common");
10
+ function isServerlessEnvironment(env = process.env) {
11
+ if (['1', 'true', 'yes', 'on'].includes(String(env.ADMINDB_SERVERLESS ?? env.SERVERLESS ?? env.IS_SERVERLESS ?? '').trim().toLowerCase())) {
12
+ return true;
13
+ }
14
+ return Boolean(env.VERCEL || env.VERCEL_ENV || env.NOW_REGION ||
15
+ env.AWS_LAMBDA_FUNCTION_NAME || env.LAMBDA_TASK_ROOT || env.AWS_EXECUTION_ENV ||
16
+ env.NETLIFY || env.NETLIFY_LOCAL ||
17
+ env.FUNCTION_TARGET || env.FUNCTION_NAME || env.K_SERVICE ||
18
+ env.FUNCTIONS_WORKER_RUNTIME || env.WEBSITE_INSTANCE_ID ||
19
+ env.CF_PAGES);
20
+ }
21
+ function createServerlessHandler(options = {}) {
22
+ const rawTarget = options.connection || options.dbPath || '';
23
+ const isPg = Boolean(options.pgOptions) ||
24
+ (0, common_1.isPostgresConnectionString)(rawTarget) ||
25
+ options.db?.dialect === 'postgres';
26
+ const defaultReadonly = !isPg;
27
+ const app = (0, app_1.createRouter)({
28
+ ...options,
29
+ serverless: options.serverless ?? true,
30
+ readonly: options.readonly ?? defaultReadonly,
31
+ });
32
+ return (req, res) => {
33
+ app(req, res);
34
+ };
35
+ }
36
+ function createLambdaHandler(options = {}) {
37
+ const rawTarget = options.connection || options.dbPath || '';
38
+ const isPg = Boolean(options.pgOptions) ||
39
+ (0, common_1.isPostgresConnectionString)(rawTarget) ||
40
+ options.db?.dialect === 'postgres';
41
+ const defaultReadonly = !isPg;
42
+ const app = (0, app_1.createRouter)({
43
+ ...options,
44
+ serverless: options.serverless ?? true,
45
+ readonly: options.readonly ?? defaultReadonly,
46
+ });
47
+ return async (event, _context) => {
48
+ return new Promise((resolve) => {
49
+ try {
50
+ const isV2 = Boolean(event.version === '2.0' || event.rawPath);
51
+ const method = (isV2 ? event.requestContext?.http?.method : event.httpMethod) || 'GET';
52
+ const rawPath = isV2 ? (event.rawPath || '/') : (event.path || '/');
53
+ const qs = isV2
54
+ ? (event.rawQueryString || '')
55
+ : (event.queryStringParameters
56
+ ? Object.entries(event.queryStringParameters)
57
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
58
+ .join('&')
59
+ : '');
60
+ const url = qs ? `${rawPath}?${qs}` : rawPath;
61
+ const rawHeaders = event.headers || {};
62
+ const headers = {};
63
+ for (const [k, v] of Object.entries(rawHeaders)) {
64
+ if (v !== undefined && v !== null)
65
+ headers[k.toLowerCase()] = String(v);
66
+ }
67
+ if (event.cookies && Array.isArray(event.cookies) && !headers['cookie']) {
68
+ headers['cookie'] = event.cookies.join('; ');
69
+ }
70
+ let bodyBuffer = null;
71
+ if (event.body) {
72
+ bodyBuffer = event.isBase64Encoded ? Buffer.from(event.body, 'base64') : Buffer.from(event.body, 'utf8');
73
+ if (!headers['content-length'])
74
+ headers['content-length'] = String(bodyBuffer.length);
75
+ }
76
+ const socket = new node_net_1.Socket();
77
+ const req = new node_http_1.IncomingMessage(socket);
78
+ req.url = url;
79
+ req.method = method;
80
+ req.headers = headers;
81
+ req.httpVersion = '1.1';
82
+ req.httpVersionMajor = 1;
83
+ req.httpVersionMinor = 1;
84
+ if (bodyBuffer)
85
+ req.push(bodyBuffer);
86
+ req.push(null);
87
+ const res = new node_http_1.ServerResponse(req);
88
+ const chunks = [];
89
+ res.write = function (chunk, ...args) {
90
+ if (chunk)
91
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
92
+ const cb = typeof args[0] === 'function' ? args[0] : typeof args[1] === 'function' ? args[1] : undefined;
93
+ if (cb)
94
+ cb();
95
+ return true;
96
+ };
97
+ res.end = function (chunk, ...args) {
98
+ if (chunk && typeof chunk !== 'function') {
99
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
100
+ }
101
+ const cb = typeof chunk === 'function'
102
+ ? chunk
103
+ : typeof args[0] === 'function'
104
+ ? args[0]
105
+ : typeof args[1] === 'function'
106
+ ? args[1]
107
+ : undefined;
108
+ if (cb)
109
+ cb();
110
+ const responseHeaders = {};
111
+ const multiValueHeaders = {};
112
+ for (const [k, v] of Object.entries(res.getHeaders())) {
113
+ if (Array.isArray(v)) {
114
+ multiValueHeaders[k] = v.map(String);
115
+ responseHeaders[k] = v.map(String).join(', ');
116
+ }
117
+ else if (v !== undefined) {
118
+ responseHeaders[k] = String(v);
119
+ }
120
+ }
121
+ const fullBody = Buffer.concat(chunks);
122
+ const contentType = String(responseHeaders['content-type'] || '');
123
+ const isBinary = !contentType.startsWith('text/') &&
124
+ !contentType.includes('application/json') &&
125
+ !contentType.includes('application/javascript') &&
126
+ !contentType.includes('application/xml') &&
127
+ !contentType.includes('image/svg+xml');
128
+ resolve({
129
+ statusCode: res.statusCode || 200,
130
+ headers: responseHeaders,
131
+ multiValueHeaders: Object.keys(multiValueHeaders).length > 0 ? multiValueHeaders : undefined,
132
+ body: isBinary ? fullBody.toString('base64') : fullBody.toString('utf8'),
133
+ isBase64Encoded: isBinary,
134
+ });
135
+ return res;
136
+ };
137
+ app(req, res);
138
+ }
139
+ catch (err) {
140
+ resolve({
141
+ statusCode: 500,
142
+ headers: { 'content-type': 'application/json' },
143
+ body: JSON.stringify({ success: false, error: err?.message || 'Server error' }),
144
+ isBase64Encoded: false,
145
+ });
146
+ }
147
+ });
148
+ };
149
+ }
@@ -1 +1,24 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.firstKeyword=firstKeyword,exports.classifySql=function(sql){const keyword=firstKeyword(sql);if("SELECT"===keyword||"WITH"===keyword){const body=String(sql).replace(LEADING_NOISE,"").trim();return{kind:/^select\b[\s\S]*?\bcount\s*\(/i.test(body)&&!/\bgroup\s+by\b/i.test(body)&&!/\bunion\b/i.test(body)?"count":"select",keyword:keyword}}if(["INSERT","UPDATE","DELETE","REPLACE","CREATE","DROP","ALTER"].includes(keyword))return{kind:"write",keyword:keyword};return{kind:"read",keyword:keyword||"UNKNOWN"}};const LEADING_NOISE=/^\s*((\/\*[\s\S]*?\*\/)|(--[^\n]*\n))*\s*/;function firstKeyword(sql){const m=String(sql).replace(LEADING_NOISE,"").trim().match(/^([a-zA-Z]+)/);return m?m[1].toUpperCase():""}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firstKeyword = firstKeyword;
4
+ exports.classifySql = classifySql;
5
+ const LEADING_NOISE = /^\s*((\/\*[\s\S]*?\*\/)|(--[^\n]*\n))*\s*/;
6
+ function firstKeyword(sql) {
7
+ const cleaned = String(sql).replace(LEADING_NOISE, '').trim();
8
+ const m = cleaned.match(/^([a-zA-Z]+)/);
9
+ return m ? m[1].toUpperCase() : '';
10
+ }
11
+ function classifySql(sql) {
12
+ const keyword = firstKeyword(sql);
13
+ if (keyword === 'SELECT' || keyword === 'WITH') {
14
+ const body = String(sql).replace(LEADING_NOISE, '').trim();
15
+ const isCount = /^select\b[\s\S]*?\bcount\s*\(/i.test(body) &&
16
+ !/\bgroup\s+by\b/i.test(body) &&
17
+ !/\bunion\b/i.test(body);
18
+ return { kind: isCount ? 'count' : 'select', keyword };
19
+ }
20
+ if (['INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'CREATE', 'DROP', 'ALTER'].includes(keyword)) {
21
+ return { kind: 'write', keyword };
22
+ }
23
+ return { kind: 'read', keyword: keyword || 'UNKNOWN' };
24
+ }
@@ -0,0 +1 @@
1
+ import type {IDatabase} from '../db/types';export interface SqlErrorDetails{message:string;line?:number;column?:number;token?:string;snippet?:string;cause?:string;suggestion?:string;raw?:string;}export declare function buildErrorSnippet(sql:string,line:number,column:number):string;export declare function offsetToLineCol(sql:string,offset:number):{line:number;column:number;};export declare function maskCommentsAndStrings(sql:string):string;export declare function findTokenPosition(sql:string,token:string):{line:number;column:number;}|null;export declare function analyzeSqlError(sql:string,rawError:unknown,db?:IDatabase):Promise<SqlErrorDetails>;