11/// <reference path="../../../../../types/index.d.ts" />
22
33import type { Database } from "elide:sqlite" ;
4+ import { Database as DatabaseConstructor } from "elide:sqlite" ;
45import type { DiscoveredDatabase } from "./database.ts" ;
56import { getDatabaseInfo , getTables , getTableData } from "./database.ts" ;
67
@@ -12,7 +13,7 @@ type ApiResponse = {
1213
1314type RouteContext = {
1415 databases : DiscoveredDatabase [ ] ;
15- Database : typeof Database ;
16+ Database : typeof DatabaseConstructor ;
1617} ;
1718
1819type RouteHandler = ( params : Record < string , string > , context : RouteContext , body : string ) => Promise < ApiResponse > ;
@@ -23,6 +24,15 @@ type Route = {
2324 handler : RouteHandler ;
2425} ;
2526
27+ type DatabaseHandlerContext = {
28+ database : DiscoveredDatabase ;
29+ db : Database ;
30+ databases : DiscoveredDatabase [ ] ;
31+ Database : typeof DatabaseConstructor ;
32+ } ;
33+
34+ type DatabaseHandler = ( params : Record < string , string > , context : DatabaseHandlerContext , body : string ) => Promise < ApiResponse > ;
35+
2636function jsonResponse ( data : unknown , status : number = 200 ) : ApiResponse {
2737 console . log ( "returning json response" , data ) ;
2838 return {
@@ -36,7 +46,7 @@ function errorResponse(message: string, status: number = 500): ApiResponse {
3646 return jsonResponse ( { error : message } , status ) ;
3747}
3848
39- function validateAndGetDatabase (
49+ function validateDatabaseIndex (
4050 dbIndexStr : string ,
4151 databases : DiscoveredDatabase [ ]
4252) : { database : DiscoveredDatabase } | { error : ApiResponse } {
@@ -53,6 +63,31 @@ function validateAndGetDatabase(
5363 return { database : databases [ dbIndex ] } ;
5464}
5565
66+ function withDatabase ( handler : DatabaseHandler ) : RouteHandler {
67+ return async ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > => {
68+ const result = validateDatabaseIndex ( params . dbIndex , context . databases ) ;
69+ if ( "error" in result ) return result . error ;
70+
71+ const { database } = result ;
72+ const db = new context . Database ( database . path ) ;
73+
74+ return handler ( params , { ...context , database, db } , body ) ;
75+ } ;
76+ }
77+
78+ function requireTableName ( params : Record < string , string > ) : ApiResponse | null {
79+ const tableName = params . tableName ;
80+ if ( ! tableName ) {
81+ return errorResponse ( "Table name is required" , 400 ) ;
82+ }
83+ return null ;
84+ }
85+
86+ function handleDatabaseError ( err : unknown , operation : string ) : ApiResponse {
87+ const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
88+ return errorResponse ( `Failed to ${ operation } : ${ errorMessage } ` , 500 ) ;
89+ }
90+
5691function buildWhereClause ( where : Record < string , unknown > ) : { clause : string ; values : unknown [ ] } {
5792 const conditions : string [ ] = [ ] ;
5893 const values : unknown [ ] = [ ] ;
@@ -121,61 +156,36 @@ async function listDatabases(_params: Record<string, string>, context: RouteCont
121156 return jsonResponse ( { databases : context . databases } ) ;
122157}
123158
124- async function getDatabaseInfoRoute ( params : Record < string , string > , context : RouteContext , _body : string ) : Promise < ApiResponse > {
125- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
126- if ( "error" in result ) return result . error ;
127-
128- const { database } = result ;
129- const db = new context . Database ( database . path ) ;
130- const info = getDatabaseInfo ( db , database . path ) ;
159+ const getDatabaseInfoRoute = withDatabase ( async ( _params , context , _body ) => {
160+ const info = getDatabaseInfo ( context . db , context . database . path ) ;
131161
132162 const fullInfo = {
133163 ...info ,
134- size : database . size ,
135- lastModified : database . lastModified ,
164+ size : context . database . size ,
165+ lastModified : context . database . lastModified ,
136166 tableCount : info . tableCount ,
137167 } ;
138168
139169 return jsonResponse ( fullInfo ) ;
140- }
141-
142- async function getTablesRoute ( params : Record < string , string > , context : RouteContext , _body : string ) : Promise < ApiResponse > {
143- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
144- if ( "error" in result ) return result . error ;
145-
146- const { database } = result ;
147- const db = new context . Database ( database . path ) ;
148- const tables = getTables ( db ) ;
170+ } ) ;
149171
172+ const getTablesRoute = withDatabase ( async ( _params , context , _body ) => {
173+ const tables = getTables ( context . db ) ;
150174 return jsonResponse ( { tables } ) ;
151- }
152-
153- async function getTableDataRoute ( params : Record < string , string > , context : RouteContext , _body : string ) : Promise < ApiResponse > {
154- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
155- if ( "error" in result ) return result . error ;
175+ } ) ;
156176
157- const tableName = params . tableName ;
158- if ( ! tableName ) {
159- return errorResponse ( "Table name is required" , 400 ) ;
160- }
161-
162- const { database } = result ;
163- const db = new context . Database ( database . path ) ;
164- const tableData = getTableData ( db , tableName ) ;
177+ const getTableDataRoute = withDatabase ( async ( params , context , _body ) => {
178+ const tableNameError = requireTableName ( params ) ;
179+ if ( tableNameError ) return tableNameError ;
165180
181+ const tableData = getTableData ( context . db , params . tableName ) ;
166182 console . log ( tableData ) ;
167-
168183 return jsonResponse ( tableData ) ;
169- }
170-
171- async function insertRowsRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
172- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
173- if ( "error" in result ) return result . error ;
184+ } ) ;
174185
175- const tableName = params . tableName ;
176- if ( ! tableName ) {
177- return errorResponse ( "Table name is required" , 400 ) ;
178- }
186+ const insertRowsRoute = withDatabase ( async ( params , context , body ) => {
187+ const tableNameError = requireTableName ( params ) ;
188+ if ( tableNameError ) return tableNameError ;
179189
180190 const data = parseRequestBody ( body ) ;
181191 const values = data . values as Record < string , unknown > | undefined ;
@@ -184,31 +194,22 @@ async function insertRowsRoute(params: Record<string, string>, context: RouteCon
184194 return errorResponse ( "Request body must contain 'values' object" , 400 ) ;
185195 }
186196
187- const { database } = result ;
188- const db = new context . Database ( database . path ) ;
189-
190197 const columns = Object . keys ( values ) ;
191198 const placeholders = columns . map ( ( ) => "?" ) . join ( ", " ) ;
192- const sql = `INSERT INTO ${ tableName } (${ columns . join ( ", " ) } ) VALUES (${ placeholders } )` ;
199+ const sql = `INSERT INTO ${ params . tableName } (${ columns . join ( ", " ) } ) VALUES (${ placeholders } )` ;
193200
194201 try {
195- const stmt = db . prepare ( sql ) ;
196- stmt . run ( ...Object . values ( values ) ) ;
202+ const stmt = context . db . prepare ( sql ) ;
203+ stmt . run ( ...( Object . values ( values ) as any ) ) ;
197204 return jsonResponse ( { success : true , message : "Row inserted successfully" } ) ;
198205 } catch ( err ) {
199- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
200- return errorResponse ( `Failed to insert row: ${ errorMessage } ` , 500 ) ;
206+ return handleDatabaseError ( err , "insert row" ) ;
201207 }
202- }
208+ } ) ;
203209
204- async function updateRowsRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
205- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
206- if ( "error" in result ) return result . error ;
207-
208- const tableName = params . tableName ;
209- if ( ! tableName ) {
210- return errorResponse ( "Table name is required" , 400 ) ;
211- }
210+ const updateRowsRoute = withDatabase ( async ( params , context , body ) => {
211+ const tableNameError = requireTableName ( params ) ;
212+ if ( tableNameError ) return tableNameError ;
212213
213214 const data = parseRequestBody ( body ) ;
214215 const values = data . values as Record < string , unknown > | undefined ;
@@ -222,32 +223,23 @@ async function updateRowsRoute(params: Record<string, string>, context: RouteCon
222223 return errorResponse ( "Request body must contain 'where' object with at least one condition" , 400 ) ;
223224 }
224225
225- const { database } = result ;
226- const db = new context . Database ( database . path ) ;
227-
228226 const setColumns = Object . keys ( values ) . map ( key => `${ key } = ?` ) . join ( ", " ) ;
229227 const { clause : whereClause , values : whereValues } = buildWhereClause ( where ) ;
230- const sql = `UPDATE ${ tableName } SET ${ setColumns } ${ whereClause } ` ;
228+ const sql = `UPDATE ${ params . tableName } SET ${ setColumns } ${ whereClause } ` ;
231229
232230 try {
233- const stmt = db . prepare ( sql ) ;
231+ const stmt = context . db . prepare ( sql ) ;
234232 const allValues = [ ...Object . values ( values ) , ...whereValues ] ;
235- const info = stmt . run ( ...allValues ) ;
233+ const info = stmt . run ( ...( allValues as any ) ) ;
236234 return jsonResponse ( { success : true , rowsAffected : info . changes , message : "Rows updated successfully" } ) ;
237235 } catch ( err ) {
238- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
239- return errorResponse ( `Failed to update rows: ${ errorMessage } ` , 500 ) ;
236+ return handleDatabaseError ( err , "update rows" ) ;
240237 }
241- }
238+ } ) ;
242239
243- async function deleteRowsRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
244- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
245- if ( "error" in result ) return result . error ;
246-
247- const tableName = params . tableName ;
248- if ( ! tableName ) {
249- return errorResponse ( "Table name is required" , 400 ) ;
250- }
240+ const deleteRowsRoute = withDatabase ( async ( params , context , body ) => {
241+ const tableNameError = requireTableName ( params ) ;
242+ if ( tableNameError ) return tableNameError ;
251243
252244 const data = parseRequestBody ( body ) ;
253245 const where = data . where as Record < string , unknown > | undefined ;
@@ -256,26 +248,19 @@ async function deleteRowsRoute(params: Record<string, string>, context: RouteCon
256248 return errorResponse ( "Request body must contain 'where' object with at least one condition (safety check)" , 400 ) ;
257249 }
258250
259- const { database } = result ;
260- const db = new context . Database ( database . path ) ;
261-
262251 const { clause : whereClause , values : whereValues } = buildWhereClause ( where ) ;
263- const sql = `DELETE FROM ${ tableName } ${ whereClause } ` ;
252+ const sql = `DELETE FROM ${ params . tableName } ${ whereClause } ` ;
264253
265254 try {
266- const stmt = db . prepare ( sql ) ;
267- const info = stmt . run ( ...whereValues ) ;
255+ const stmt = context . db . prepare ( sql ) ;
256+ const info = stmt . run ( ...( whereValues as any ) ) ;
268257 return jsonResponse ( { success : true , rowsAffected : info . changes , message : "Rows deleted successfully" } ) ;
269258 } catch ( err ) {
270- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
271- return errorResponse ( `Failed to delete rows: ${ errorMessage } ` , 500 ) ;
259+ return handleDatabaseError ( err , "delete rows" ) ;
272260 }
273- }
274-
275- async function createTableRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
276- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
277- if ( "error" in result ) return result . error ;
261+ } ) ;
278262
263+ const createTableRoute = withDatabase ( async ( _params , context , body ) => {
279264 const data = parseRequestBody ( body ) ;
280265 const tableName = data . name as string | undefined ;
281266 const schema = data . schema as Array < { name : string ; type : string ; constraints ?: string } > | undefined ;
@@ -288,9 +273,6 @@ async function createTableRoute(params: Record<string, string>, context: RouteCo
288273 return errorResponse ( "Request body must contain 'schema' array with at least one column" , 400 ) ;
289274 }
290275
291- const { database } = result ;
292- const db = new context . Database ( database . path ) ;
293-
294276 const columns = schema . map ( col => {
295277 const constraints = col . constraints ? ` ${ col . constraints } ` : "" ;
296278 return `${ col . name } ${ col . type } ${ constraints } ` ;
@@ -299,22 +281,16 @@ async function createTableRoute(params: Record<string, string>, context: RouteCo
299281 const sql = `CREATE TABLE ${ tableName } (${ columns } )` ;
300282
301283 try {
302- db . exec ( sql ) ;
284+ context . db . exec ( sql ) ;
303285 return jsonResponse ( { success : true , message : `Table '${ tableName } ' created successfully` } ) ;
304286 } catch ( err ) {
305- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
306- return errorResponse ( `Failed to create table: ${ errorMessage } ` , 500 ) ;
287+ return handleDatabaseError ( err , "create table" ) ;
307288 }
308- }
289+ } ) ;
309290
310- async function dropTableRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
311- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
312- if ( "error" in result ) return result . error ;
313-
314- const tableName = params . tableName ;
315- if ( ! tableName ) {
316- return errorResponse ( "Table name is required" , 400 ) ;
317- }
291+ const dropTableRoute = withDatabase ( async ( params , context , body ) => {
292+ const tableNameError = requireTableName ( params ) ;
293+ if ( tableNameError ) return tableNameError ;
318294
319295 const data = parseRequestBody ( body ) ;
320296 const confirm = data . confirm as boolean | undefined ;
@@ -323,24 +299,17 @@ async function dropTableRoute(params: Record<string, string>, context: RouteCont
323299 return errorResponse ( "Must set 'confirm: true' in request body to drop table (safety check)" , 400 ) ;
324300 }
325301
326- const { database } = result ;
327- const db = new context . Database ( database . path ) ;
328-
329- const sql = `DROP TABLE ${ tableName } ` ;
302+ const sql = `DROP TABLE ${ params . tableName } ` ;
330303
331304 try {
332- db . exec ( sql ) ;
333- return jsonResponse ( { success : true , message : `Table '${ tableName } ' dropped successfully` } ) ;
305+ context . db . exec ( sql ) ;
306+ return jsonResponse ( { success : true , message : `Table '${ params . tableName } ' dropped successfully` } ) ;
334307 } catch ( err ) {
335- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
336- return errorResponse ( `Failed to drop table: ${ errorMessage } ` , 500 ) ;
308+ return handleDatabaseError ( err , "drop table" ) ;
337309 }
338- }
339-
340- async function executeQueryRoute ( params : Record < string , string > , context : RouteContext , body : string ) : Promise < ApiResponse > {
341- const result = validateAndGetDatabase ( params . dbIndex , context . databases ) ;
342- if ( "error" in result ) return result . error ;
310+ } ) ;
343311
312+ const executeQueryRoute = withDatabase ( async ( _params , context , body ) => {
344313 const data = parseRequestBody ( body ) ;
345314 const sql = data . sql as string | undefined ;
346315 const queryParams = data . params as unknown [ ] | undefined ;
@@ -353,33 +322,29 @@ async function executeQueryRoute(params: Record<string, string>, context: RouteC
353322 console . warn ( `Warning: Executing potentially destructive query: ${ sql } ` ) ;
354323 }
355324
356- const { database } = result ;
357- const db = new context . Database ( database . path ) ;
358-
359325 try {
360- const stmt = db . prepare ( sql ) ;
326+ const stmt = context . db . prepare ( sql ) ;
361327 const params = queryParams || [ ] ;
362328
363329 if ( sql . trim ( ) . toLowerCase ( ) . startsWith ( "select" ) ) {
364- const rows = stmt . all ( ...params ) ;
330+ const rows = stmt . all ( ...( params as any ) ) ;
365331 return jsonResponse ( {
366332 success : true ,
367333 rows,
368334 rowCount : Array . isArray ( rows ) ? rows . length : 0 ,
369335 } ) ;
370336 } else {
371- const info = stmt . run ( ...params ) ;
337+ const info = stmt . run ( ...( params as any ) ) ;
372338 return jsonResponse ( {
373339 success : true ,
374340 rowsAffected : info . changes ,
375341 lastInsertRowid : info . lastInsertRowid ,
376342 } ) ;
377343 }
378344 } catch ( err ) {
379- const errorMessage = err instanceof Error ? err . message : "Unknown error" ;
380- return errorResponse ( `Failed to execute query: ${ errorMessage } ` , 500 ) ;
345+ return handleDatabaseError ( err , "execute query" ) ;
381346 }
382- }
347+ } ) ;
383348
384349/**
385350 * Route Registry
@@ -412,7 +377,7 @@ export async function handleApiRequest(
412377 method : string ,
413378 body : string ,
414379 databases : DiscoveredDatabase [ ] ,
415- Database : typeof Database
380+ Database : typeof DatabaseConstructor
416381) : Promise < ApiResponse > {
417382 // Parse URL path (remove query string if present)
418383 const path = url . split ( '?' ) [ 0 ] ;
0 commit comments