diff --git a/web/database/worker/db-worker.js b/web/database/worker/db-worker.js index 98c6776e4..f8c8427e7 100644 --- a/web/database/worker/db-worker.js +++ b/web/database/worker/db-worker.js @@ -1,102 +1,106 @@ // @flow import localforage from 'localforage'; -import initSqlJs from 'sql.js'; +import initSqlJs, { type SqliteDatabase } from 'sql.js'; import { type SharedWorkerMessageEvent, type WorkerRequestMessage, type WorkerResponseMessage, workerRequestMessageTypes, workerResponseMessageTypes, type WorkerRequestProxyMessage, } from '../../types/worker-types.js'; import { SQLITE_CONTENT } from '../utils/constants.js'; const localforageConfig: PartialConfig = { driver: localforage.INDEXEDDB, name: 'comm', storeName: 'commStorage', description: 'Comm encrypted database storage', version: '1.0', }; localforage.config(localforageConfig); -let sqliteDb = null; +let sqliteDb: ?SqliteDatabase = null; async function initDatabase(sqljsFilePath: string, sqljsFilename: ?string) { const content = await localforage.getItem(SQLITE_CONTENT); const locateFile = defaultFilename => { if (sqljsFilename) { return `${sqljsFilePath}/${sqljsFilename}`; } return `${sqljsFilePath}/${defaultFilename}`; }; const SQL = await initSqlJs({ locateFile, }); if (content) { sqliteDb = new SQL.Database(new Uint8Array(content)); } else { sqliteDb = new SQL.Database(); } const versionData = sqliteDb.exec('PRAGMA user_version;'); - if (versionData.length && versionData[0].values.length) { - console.info(`Db version: ${versionData[0].values[0]}`); + if (!versionData.length || !versionData[0].values.length) { + throw new Error('Error while retrieving database version'); + } + const [dbVersion] = versionData[0].values[0]; + if (typeof dbVersion === 'number') { + console.info(`Db version: ${dbVersion}`); } else { throw new Error('Error while retrieving database version'); } } async function processAppRequest( message: WorkerRequestMessage, ): Promise { if (message.type === workerRequestMessageTypes.PING) { return { type: workerResponseMessageTypes.PONG, text: 'PONG', }; } else if (message.type === workerRequestMessageTypes.INIT) { await initDatabase(message.sqljsFilePath, message.sqljsFilename); return; } throw new Error('Request type not supported'); } function connectHandler(event: SharedWorkerMessageEvent) { if (!event.ports.length) { return; } const port: MessagePort = event.ports[0]; console.log('Web database worker alive!'); port.onmessage = async function (messageEvent: MessageEvent) { const data: WorkerRequestProxyMessage = (messageEvent.data: any); const { id, message } = data; if (!id) { port.postMessage({ error: new Error('Request without identifier'), }); } try { const result = await processAppRequest(message); port.postMessage({ id, message: result, }); } catch (e) { port.postMessage({ id, error: e, }); } }; } self.addEventListener('connect', connectHandler); diff --git a/web/flow-typed/npm/sql.js_vx.x.x.js b/web/flow-typed/npm/sql.js_vx.x.x.js new file mode 100644 index 000000000..a8b06bf4a --- /dev/null +++ b/web/flow-typed/npm/sql.js_vx.x.x.js @@ -0,0 +1,89 @@ +// @flow + +// flow-typed signature: f355493becad56880bf30832260c5460 +// flow-typed version: <>/sql.js_v1.8.0/flow_v0.182.0 + +declare module 'sql.js' { + declare export type SqlValue = number | string | Uint8Array | null; + declare export type ParamsObject = { +[key: string]: SqlValue }; + declare export type ParamsCallback = (obj: ParamsObject) => void; + + declare export type BindParams = + | $ReadOnlyArray + | ParamsObject + | null; + + declare export type QueryExecResult = { + columns: string[], + values: SqlValue[][], + }; + + declare export type StatementIteratorResult = { + +done: boolean, + +value: SqliteStatement, + }; + + declare export type SqlJsStatic = { + +Database: Class, + +Statement: Class, + }; + + declare type EmscriptenModule = { + +locateFile: (path: string, prefix?: string) => string, + ... + }; + + declare type InitSqlJsStatic = { + (config?: EmscriptenModule): Promise, + }; + + declare export class SqliteDatabase { + constructor(data?: Array | Uint8Array | Buffer | null): this; + close(): void; + + create_function( + name: string, + func: (...args: $ReadOnlyArray) => R, + ): SqliteDatabase; + each( + sql: string, + params: BindParams, + callback: ParamsCallback, + done: () => void, + ): SqliteDatabase; + each( + sql: string, + callback: ParamsCallback, + done: () => void, + ): SqliteDatabase; + exec(sql: string, params?: BindParams): QueryExecResult[]; + export(): Uint8Array; + getRowsModified(): number; + handleError(): null | empty; + iterateStatements(sql: string): StatementIterator; + prepare(sql: string, params?: BindParams): SqliteStatement; + run(sql: string, params?: BindParams): SqliteDatabase; + } + + declare export class SqliteStatement { + bind(values?: BindParams): boolean; + free(): boolean; + freemem(): void; + get(params?: BindParams): $ReadOnlyArray; + getAsObject(params?: BindParams): ParamsObject; + getColumnNames(): $ReadOnlyArray; + getNormalizedSQL(): string; + getSQL(): string; + reset(): void; + run(values?: BindParams): void; + step(): boolean; + } + + declare export class StatementIterator implements Iterable { + @@iterator: () => Iterator; + getRemainingSql(): string; + next(): StatementIteratorResult; + } + + declare export default InitSqlJsStatic; +}