forked from jsnbuchanan/crowd-funder-for-time-pwa
add more to the inital migration, and refactor the locations of types
This commit is contained in:
97
src/services/database.ts
Normal file
97
src/services/database.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
// Add type declarations for external modules
|
||||
declare module '@jlongster/sql.js';
|
||||
declare module 'absurd-sql';
|
||||
declare module 'absurd-sql/dist/indexeddb-backend';
|
||||
|
||||
import initSqlJs from '@jlongster/sql.js';
|
||||
import { SQLiteFS } from 'absurd-sql';
|
||||
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
|
||||
import { runMigrations } from '../db-sql/migration';
|
||||
import { QueryExecResult } from './migrationService';
|
||||
|
||||
interface SQLDatabase {
|
||||
exec: (sql: string, params?: any[]) => Promise<QueryExecResult[]>;
|
||||
run: (sql: string, params?: any[]) => Promise<{ changes: number; lastId?: number }>;
|
||||
}
|
||||
|
||||
class DatabaseService {
|
||||
private db: SQLDatabase | null;
|
||||
private initialized: boolean;
|
||||
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
if (this.initialized) return;
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file: string) => {
|
||||
return new URL(`/node_modules/@jlongster/sql.js/dist/${file}`, import.meta.url).href;
|
||||
}
|
||||
});
|
||||
|
||||
let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
|
||||
SQL.register_for_idb(sqlFS);
|
||||
|
||||
SQL.FS.mkdir('/sql');
|
||||
SQL.FS.mount(sqlFS, {}, '/sql');
|
||||
|
||||
const path = '/sql/db.sqlite';
|
||||
if (typeof SharedArrayBuffer === 'undefined') {
|
||||
let stream = SQL.FS.open(path, 'a+');
|
||||
await stream.node.contents.readIfFallback();
|
||||
SQL.FS.close(stream);
|
||||
}
|
||||
|
||||
this.db = new SQL.Database(path, { filename: true });
|
||||
if (!this.db) {
|
||||
throw new Error('Failed to initialize database');
|
||||
}
|
||||
|
||||
await this.db.exec(`
|
||||
PRAGMA journal_mode=MEMORY;
|
||||
`);
|
||||
const sqlExec = this.db.exec.bind(this.db);
|
||||
|
||||
// Run migrations
|
||||
await runMigrations(sqlExec);
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
private ensureInitialized(): void {
|
||||
if (!this.initialized || !this.db) {
|
||||
throw new Error('Database not initialized');
|
||||
}
|
||||
}
|
||||
|
||||
// Used for inserts, updates, and deletes
|
||||
async run(sql: string, params: any[] = []): Promise<{ changes: number; lastId?: number }> {
|
||||
this.ensureInitialized();
|
||||
return this.db!.run(sql, params);
|
||||
}
|
||||
|
||||
async query(sql: string, params: any[] = []): Promise<QueryExecResult[]> {
|
||||
this.ensureInitialized();
|
||||
return this.db!.exec(sql, params);
|
||||
}
|
||||
|
||||
async get(sql: string, params: any[] = []): Promise<any[] | undefined> {
|
||||
this.ensureInitialized();
|
||||
const result = await this.db!.exec(sql, params);
|
||||
return result[0]?.values[0];
|
||||
}
|
||||
|
||||
async all(sql: string, params: any[] = []): Promise<any[][]> {
|
||||
this.ensureInitialized();
|
||||
const result = await this.db!.exec(sql, params);
|
||||
return result[0]?.values || [];
|
||||
}
|
||||
}
|
||||
|
||||
// Create a singleton instance
|
||||
const databaseService = new DatabaseService();
|
||||
|
||||
export default databaseService;
|
||||
Reference in New Issue
Block a user