QEMU with VirtIO GPU Vulkan Support · GitHub

SQLite-backed key-value store with JS-like object manipulation and automatic JSON serialization. · GitHub

import Database from ‘better-sqlite3’; //////////////////////////////////////// // 1. Types //////////////////////////////////////// export type SchemaDefinition = Record<string, any>; export interface CreateDBOptions { idColumn?: string; jsonColumn?: string; debugSql?: boolean; } //////////////////////////////////////// // 2. The shape of what we return //////////////////////////////////////// export interface DBClient<TSchema extends SchemaDefinition> { /** Reader: returns plain JS objects, no Proxy. */ rdr: { [TableName in keyof TSchema]: TableReader<TSchema[TableName]> }; /** Writer: partial updates (Proxies). */ wtr: { [TableName in keyof TSchema]: TableWriter<TSchema[TableName]> }; } /** Reader interface: bracket-get returns plain objects from memory. */ export interface TableReader<TRow> { [rowId: string]: TRow | undefined; forEach(callback: (id: string, rowData: TRow) => void): void; keys(): string[]; values(): TRow[]; entries(): Array<[string, TRow]>; dict(): Record<string, TRow>; has(id: string): boolean; } /** Writer interface: bracket-get returns a nested Proxy for partial JSON updates. */ export interface TableWriter<TRow> { [rowId: string]: TRowProxy<TRow>; forEach(callback: (id: string, rowProxy: TRowProxy<TRow>) => void): void; keys(): string[]; entries(): Array<[string, TRowProxy<TRow>]>; has(id: string): boolean; } /** * A nested Proxy that allows partial updates to single fields. * If you do `writer.users[‘bob’].nested.foo = 123`, * it calls `json_set(…, ‘$.nested.foo’, 123)` in the DB. */ export type TRowProxy<TRow> = TRow & { [nestedKey: string]: any; }; //////////////////////////////////////// // 3. Main entry point //////////////////////////////////////// export function createDatabaseClient<TSchema extends SchemaDefinition>( db: Database.Database, schema: TSchema, options: CreateDBOptions = {} ): DBClient<TSchema> { const idColumn = options.idColumn ?? ‘id’; const jsonColumn = options.jsonColumn ?? ‘data’; const debugSql = !!options.debugSql; //////////////////////////////////////// // A) In-memory cache: Map> //////////////////////////////////////// const memoryCache = new Map<string, Map<string, any>>(); for (const tableName of Object.keys(schema)) { memoryCache.set(tableName, new Map()); } //////////////////////////////////////// // B) Precompiled statements for each table //////////////////////////////////////// function wrapStmt(stmt: ReturnType<Database.Database[‘prepare’]>, label: string) { return { get(args: any[]) { if (debugSql) { console.log(`[SQL GET] ${label}, params: ${JSON.stringify(args)}`); } return stmt.get(args); }, run(args: any[]) { if (debugSql) { console.log(`[SQL RUN] ${label}, params: ${JSON.stringify(args)}`); } return stmt.run(args); }, all(args: any[]) { if (debugSql) { console.log(`[SQL ALL] ${label}, params: ${JSON.stringify(args)}`); } return stmt.all(args); }, }; } const stmts = new Map< string, { selectRow: ReturnType<typeof wrapStmt>; upsertWholeRow: ReturnType<typeof wrapStmt>; deleteRow: ReturnType<typeof wrapStmt>; jsonSet: ReturnType<typeof wrapStmt>; jsonRemove: ReturnType<typeof wrapStmt>; checkExistence: ReturnType<typeof wrapStmt>; selectAllIds: ReturnType<typeof wrapStmt>; } >(); function getStatementsForTable(tableName: string) { if (stmts.has(tableName)) { return stmts.get(tableName)!; } const selectRowSQL = ` SELECT ${jsonColumn} AS jsonData FROM ${tableName} WHERE ${idColumn} = ?`; const upsertWholeRowSQL = ` INSERT OR REPLACE INTO ${tableName} (${idColumn}, ${jsonColumn}) VALUES (?, json(?))`; const deleteRowSQL = ` DELETE FROM ${tableName} WHERE ${idColumn} = ?`; const jsonSetSQL = ` UPDATE ${tableName} SET ${jsonColumn} = json_set(${jsonColumn}, ?, json(?)) WHERE ${idColumn} = ?`; const jsonRemoveSQL = ` UPDATE ${tableName} SET ${jsonColumn} = json_remove(${jsonColumn}, ?) WHERE ${idColumn} = ?`; const checkExistenceSQL = ` SELECT 1 FROM ${tableName} WHERE ${idColumn} = ?`; const selectAllIdsSQL = ` SELECT ${idColumn} AS id FROM ${tableName}`; const prepared = { selectRow: wrapStmt(db.prepare(selectRowSQL), `${tableName}:selectRow`), upsertWholeRow: wrapStmt(db.prepare(upsertWholeRowSQL), `${tableName}:upsertWholeRow`), deleteRow: wrapStmt(db.prepare(deleteRowSQL), `${tableName}:deleteRow`), jsonSet: wrapStmt(db.prepare(jsonSetSQL), `${tableName}:jsonSet`), jsonRemove: wrapStmt(db.prepare(jsonRemoveSQL), `${tableName}:jsonRemove`), checkExistence: wrapStmt(db.prepare(checkExistenceSQL), `${tableName}:checkExistence`), selectAllIds: wrapStmt(db.prepare(selectAllIdsSQL), `${tableName}:selectAllIds`), }; stmts.set(tableName, prepared); return prepared; } //////////////////////////////////////// // C) Helper: load a row’s JSON into memory cache if not loaded //////////////////////////////////////// function loadRow(tableName: string, rowId: string) { const cacheForTable = memoryCache.get(tableName)!; if (cacheForTable.has(rowId)) { return; // already in memory } const { selectRow } = getStatementsForTable(tableName); const row = selectRow.get(rowId); if (!row) return; // not found in DB try { cacheForTable.set(rowId, JSON.parse(row.jsonData)); } catch { cacheForTable.set(rowId, null); } } //////////////////////////////////////// // D) JSON path helpers for partial updates //////////////////////////////////////// function pathToJsonPathString(path: string[]) { if (!path.length) return ‘$’; return ‘$.’ + path.map(escapeJsonKey).join(‘.’); } function escapeJsonKey(k: string): string { // naive return k.replace(//g, ‘\\”‘); } //////////////////////////////////////// // E) Row-level Proxy for partial updates //////////////////////////////////////// function createRowProxy(tableName: string, rowId: string, pathSoFar: string[] = []): any { return new Proxy( {}, { get(_, propKey) { if (typeof propKey === ‘symbol’) { return Reflect.get(_, propKey); } loadRow(tableName, rowId); const cacheForTable = memoryCache.get(tableName)!; if (!cacheForTable.has(rowId)) { throw new Error(`Row ‘${rowId}‘ not found in table ‘${tableName}‘ (read).`); } const rowData = cacheForTable.get(rowId); const newPath = [pathSoFar, propKey.toString()]; let current: any = rowData; for (const p of newPath) { if (current == null || typeof current !== ‘object’) { return undefined; } current = current[p]; } // If object or array, return deeper proxy so we can do partial updates if (current && typeof current === ‘object’) { return createRowProxy(tableName, rowId, newPath); } return current; }, set(_, propKey, value) { loadRow(tableName, rowId); const cacheForTable = memoryCache.get(tableName)!; if (!cacheForTable.has(rowId)) { throw new Error(`Row ‘${rowId}‘ not found in table ‘${tableName}‘ (write).`); } const { jsonSet } = getStatementsForTable(tableName); const newPath = [pathSoFar, propKey.toString()]; const jsonPath = pathToJsonPathString(newPath); jsonSet.run(jsonPath, JSON.stringify(value), rowId); // Update local cache const rowData = cacheForTable.get(rowId); let cursor: any = rowData; for (let i = 0; i < newPath.length 1; i++) { const seg = newPath[i]; if (cursor[seg] == null || typeof cursor[seg] !== ‘object’) { cursor[seg] = {}; } cursor = cursor[seg]; } cursor[newPath[newPath.length 1]] = value; return true; }, deleteProperty(_, propKey) { loadRow(tableName, rowId); const cacheForTable = memoryCache.get(tableName)!; if (!cacheForTable.has(rowId)) { throw new Error(`Row ‘${rowId}‘ not found in table ‘${tableName}‘ (delete).`); } // If it looks like a numeric index => forbid const keyString = propKey.toString(); if (/^\d+$/.test(keyString)) { throw new Error( `Deleting array elements by index is not allowed: .${keyString}` ); } const { jsonRemove } = getStatementsForTable(tableName); const newPath = [pathSoFar, keyString]; const jsonPath = pathToJsonPathString(newPath); jsonRemove.run(jsonPath, rowId); // Update in-memory object const rowData = cacheForTable.get(rowId); let cursor: any = rowData; for (let i = 0; i < newPath.length 1; i++) { const seg = newPath[i]; if (cursor[seg] == null || typeof cursor[seg] !== ‘object’) { return true; } cursor = cursor[seg]; } delete cursor[newPath[newPath.length 1]]; return true; }, has(_, propKey) { if (typeof propKey === ‘symbol’) { return Reflect.has(_, propKey); } loadRow(tableName, rowId); const cacheForTable = memoryCache.get(tableName)!; if (!cacheForTable.has(rowId)) { return false; } const rowData = cacheForTable.get(rowId); let current = rowData; for (const p of pathSoFar) { if (current == null || typeof current !== ‘object’) { return false; } current = current[p]; } if (current && typeof current === ‘object’) { return Object.prototype.hasOwnProperty.call(current, propKey); } return false; }, } ); } //////////////////////////////////////// // F) Create the “Reader” table object //////////////////////////////////////// function createTableReader(tableName: string): TableReader<any> { const { selectAllIds, checkExistence } = getStatementsForTable(tableName); const cacheForTable = memoryCache.get(tableName)!; const readerImplementation = { forEach(callback: (id: string, data: any) => void) { const rows = selectAllIds.all() as Array<{ id: string }>; for (const r of rows) { loadRow(tableName, r.id); const cached = cacheForTable.get(r.id); if (cached !== undefined) { callback(r.id, cached); } } }, keys(): string[] { return selectAllIds.all().map((r: any) => r.id); }, values(): any[] { return selectAllIds.all().map((r: any) => cacheForTable.get(r.id)); }, dict(): Record<string, any> { return selectAllIds.all().reduce((acc, r: any) => { acc[r.id] = cacheForTable.get(r.id); return acc; }, {} as Record<string, any>); }, entries(): Array<[string, any]> { return selectAllIds.all().map((r: any) => { loadRow(tableName, r.id); return [r.id, cacheForTable.get(r.id)] as [string, any]; }); }, has(id: string) { if (cacheForTable.has(id)) return true; const row = checkExistence.get(id); return !!row; }, }; return new Proxy(readerImplementation, { get(target, propKey, receiver) { if (typeof propKey === ‘symbol’) { return Reflect.get(target, propKey, receiver); } if (Reflect.has(target, propKey)) { return Reflect.get(target, propKey, receiver); } // otherwise treat propKey as rowId const rowId = propKey.toString(); loadRow(tableName, rowId); return cacheForTable.get(rowId); }, set() { throw new Error(`Cannot write via Reader API`); }, deleteProperty() { throw new Error(`Cannot delete via Reader API`); }, has(target, propKey) { if (typeof propKey === ‘symbol’) { return Reflect.has(target, propKey); } if (Reflect.has(target, propKey)) { return true; } const rowId = propKey.toString(); if (cacheForTable.has(rowId)) { return true; } const row = checkExistence.get(rowId); return !!row; }, }) as TableReader<any>; } //////////////////////////////////////// // G) Create the “Writer” table object //////////////////////////////////////// function createTableWriter(tableName: string): TableWriter<any> { const { checkExistence, selectAllIds, upsertWholeRow, deleteRow } = getStatementsForTable(tableName); const cacheForTable = memoryCache.get(tableName)!; const writerImplementation = { forEach(callback: (id: string, rowProxy: any) => void) { const rows = selectAllIds.all() as Array<{ id: string }>; for (const r of rows) { loadRow(tableName, r.id); callback(r.id, createRowProxy(tableName, r.id)); } }, keys(): string[] { return selectAllIds.all().map((r: any) => r.id); }, entries(): Array<[string, any]> { return selectAllIds.all().map((r: any) => { loadRow(tableName, r.id); return [r.id, createRowProxy(tableName, r.id)] as [string, any]; }); }, has(id: string) { if (cacheForTable.has(id)) return true; const row = checkExistence.get(id); return !!row; }, }; return new Proxy(writerImplementation, { get(target, propKey, receiver) { if (typeof propKey === ‘symbol’) { return Reflect.get(target, propKey, receiver); } if (Reflect.has(target, propKey)) { return Reflect.get(target, propKey, receiver); } const rowId = propKey.toString(); loadRow(tableName, rowId); return createRowProxy(tableName, rowId); }, set(_, rowId, value) { // upsert entire row const idString = rowId.toString(); cacheForTable.set(idString, value); upsertWholeRow.run(idString, JSON.stringify(value)); return true; }, deleteProperty(_, rowId) { const idString = rowId.toString(); cacheForTable.delete(idString); deleteRow.run(idString); return true; }, has(target, propKey) { if (typeof propKey === ‘symbol’) { return Reflect.has(target, propKey); } if (Reflect.has(target, propKey)) { return true; } const rowId = propKey.toString(); if (cacheForTable.has(rowId)) { return true; } const row = checkExistence.get(rowId); return !!row; }, }) as TableWriter<any>; } //////////////////////////////////////// // H) Build the overall “rdr” and “wtr” objects //////////////////////////////////////// const rdrObj = {} as DBClient<TSchema>[‘rdr’]; const wtrObj = {} as DBClient<TSchema>[‘wtr’]; for (const tableName of Object.keys(schema)) { Object.defineProperty(rdrObj, tableName, { value: createTableReader(tableName), enumerable: true, configurable: false, writable: false, }); Object.defineProperty(wtrObj, tableName, { value: createTableWriter(tableName), enumerable: true, configurable: false, writable: false, }); } return { rdr: rdrObj, wtr: wtrObj, }; }

Leave a Comment

Your email address will not be published. Required fields are marked *