UNPKG

@flavoai/fastfold

Version:

Zero-boilerplate backend for React apps with auto-generated CRUD and declarative security

163 lines 5 kB
import Database from 'better-sqlite3'; import { BaseDatabaseAdapter } from './base'; export class SQLiteAdapter extends BaseDatabaseAdapter { db; filename; constructor(filename = ':memory:') { super(); this.filename = filename; } async connect() { try { this.db = new Database(this.filename); this.connected = true; } catch (err) { throw err; } } async disconnect() { if (!this.db) return; try { this.db.close(); this.connected = false; } catch (err) { throw err; } } async createTable(tableName, schema) { this.ensureConnected(); const fields = Object.entries(schema) .map(([name, type]) => `${name} ${this.mapFieldType(type)}`) .join(', '); const sql = ` CREATE TABLE IF NOT EXISTS ${tableName} ( id INTEGER PRIMARY KEY AUTOINCREMENT, ${fields}, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `; try { this.db.exec(sql); } catch (err) { throw err; } } async query(tableName, params) { this.ensureConnected(); let sql = `SELECT ${params.select ? params.select.join(', ') : '*'} FROM ${tableName}`; let values = []; if (params.where) { const where = this.buildWhereClause(params.where); sql += ` ${where.sql}`; values = where.values; } if (params.orderBy) { sql += ` ${this.buildOrderByClause(params.orderBy)}`; } if (params.limit) { sql += ` LIMIT ${params.limit}`; } if (params.offset) { sql += ` OFFSET ${params.offset}`; } try { const stmt = this.db.prepare(sql); const rows = stmt.all(...values); return rows; } catch (err) { throw err; } } async create(tableName, data) { this.ensureConnected(); const fields = Object.keys(data); const placeholders = fields.map(() => '?').join(', '); const values = Object.values(data); const sql = ` INSERT INTO ${tableName} (${fields.join(', ')}) VALUES (${placeholders}) `; try { const insertStmt = this.db.prepare(sql); const result = insertStmt.run(...values); // Fetch the created record const selectSql = `SELECT * FROM ${tableName} WHERE id = ?`; const selectStmt = this.db.prepare(selectSql); const row = selectStmt.get(result.lastInsertRowid); return row; } catch (err) { throw err; } } async update(tableName, id, data) { this.ensureConnected(); const fields = Object.keys(data); const setClause = fields.map(field => `${field} = ?`).join(', '); const values = [...Object.values(data), id]; const sql = ` UPDATE ${tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ? `; try { const updateStmt = this.db.prepare(sql); updateStmt.run(...values); // Fetch the updated record const selectSql = `SELECT * FROM ${tableName} WHERE id = ?`; const selectStmt = this.db.prepare(selectSql); const row = selectStmt.get(id); return row; } catch (err) { throw err; } } async delete(tableName, id) { this.ensureConnected(); const sql = `DELETE FROM ${tableName} WHERE id = ?`; try { const deleteStmt = this.db.prepare(sql); const result = deleteStmt.run(id); return result.changes > 0; } catch (err) { throw err; } } async count(tableName, where) { this.ensureConnected(); let sql = `SELECT COUNT(*) as count FROM ${tableName}`; let values = []; if (where) { const whereClause = this.buildWhereClause(where); sql += ` ${whereClause.sql}`; values = whereClause.values; } try { const countStmt = this.db.prepare(sql); const row = countStmt.get(...values); return row.count; } catch (err) { throw err; } } mapFieldType(fieldType) { switch (fieldType) { case 'string': return 'TEXT'; case 'number': return 'REAL'; case 'boolean': return 'INTEGER'; // SQLite doesn't have native boolean case 'date': return 'DATETIME'; case 'json': return 'TEXT'; // Store as JSON string default: return 'TEXT'; } } } //# sourceMappingURL=sqlite.js.map