MOBILE-4304 core: Replace WebSQL with sqlite-wasm

main
Noel De Martin 2024-02-01 16:14:53 +01:00
parent 368bf02bc2
commit a7bd1e5f89
12 changed files with 440 additions and 371 deletions

View File

@ -95,7 +95,11 @@
"options": {
"disableHostCheck": true,
"port": 8100,
"buildTarget": "app:build"
"buildTarget": "app:build",
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
},
"configurations": {
"production": {

9
package-lock.json generated
View File

@ -55,6 +55,7 @@
"@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@sqlite.org/sqlite-wasm": "^3.45.0-build1",
"@types/chart.js": "^2.9.31",
"@types/cordova": "0.0.34",
"@types/dom-mediacapture-record": "1.0.7",
@ -8997,6 +8998,14 @@
"@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@sqlite.org/sqlite-wasm": {
"version": "3.45.0-build1",
"resolved": "https://registry.npmjs.org/@sqlite.org/sqlite-wasm/-/sqlite-wasm-3.45.0-build1.tgz",
"integrity": "sha512-QAwE4n16t82g8kbhpuBzy6pzh7bm5VKziNKwQHmIPmtCBUk2AlUndsGS5qL8pAfOrrafXq9xILa0LdZkPFetgA==",
"bin": {
"sqlite-wasm": "bin/index.js"
}
},
"node_modules/@stencil/core": {
"version": "4.10.0",
"license": "MIT",

View File

@ -19,7 +19,7 @@
],
"scripts": {
"ng": "ng",
"start": "ionic serve --browser=$MOODLE_APP_BROWSER",
"start": "ionic serve --browser=$MOODLE_APP_BROWSER --ssl",
"serve:test": "NODE_ENV=testing ionic serve --no-open",
"build": "ionic build",
"build:prod": "NODE_ENV=production ionic build --prod",
@ -89,6 +89,7 @@
"@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@sqlite.org/sqlite-wasm": "^3.45.0-build1",
"@types/chart.js": "^2.9.31",
"@types/cordova": "0.0.34",
"@types/dom-mediacapture-record": "1.0.7",

View File

@ -0,0 +1,31 @@
diff --git a/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs b/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs
index b86a0aa..a9bf793 100644
--- a/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs
+++ b/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs
@@ -533,7 +533,7 @@ var sqlite3InitModule = (() => {
wasmBinaryFile = locateFile(wasmBinaryFile);
}
} else {
- wasmBinaryFile = new URL('sqlite3.wasm', import.meta.url).href;
+ wasmBinaryFile = '/assets/lib/sqlite3/sqlite3.wasm';
}
function getBinary(file) {
@@ -12522,7 +12522,7 @@ var sqlite3InitModule = (() => {
return promiseResolve_(sqlite3);
};
const W = new Worker(
- new URL('sqlite3-opfs-async-proxy.js', import.meta.url),
+ '/assets/lib/sqlite3/sqlite3-opfs-async-proxy.js',
);
setTimeout(() => {
if (undefined === promiseWasRejected) {
@@ -13445,7 +13445,7 @@ var sqlite3InitModule = (() => {
});
return thePromise;
};
- installOpfsVfs.defaultProxyUri = 'sqlite3-opfs-async-proxy.js';
+ installOpfsVfs.defaultProxyUri = '/assets/lib/sqlite3/sqlite3-opfs-async-proxy.js';
globalThis.sqlite3ApiBootstrap.initializersAsync.push(
async (sqlite3) => {
try {

View File

@ -31,6 +31,8 @@ const ASSETS = {
'/src/core/features/h5p/assets': '/lib/h5p',
'/node_modules/ogv/dist': '/lib/ogv',
'/node_modules/video.js/dist/video-js.min.css': '/lib/video.js/video-js.min.css',
'/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm': '/lib/sqlite3/sqlite3.wasm',
'/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3-opfs-async-proxy.js': '/lib/sqlite3/sqlite3-opfs-async-proxy.js',
};
module.exports = function(ctx) {

View File

@ -14,10 +14,7 @@
import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx';
import { SQLite } from '@singletons';
import { CoreError } from '@classes/errors/error';
import { CoreDB } from '@services/db';
import { CorePlatform } from '@services/platform';
type SQLiteDBColumnType = 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB';
@ -137,17 +134,13 @@ export interface SQLiteDBForeignKeySchema {
*/
export class SQLiteDB {
db?: SQLiteObject;
promise!: Promise<void>;
/**
* Create and open the database.
*
* @param name Database name.
* @param db Database connection.
*/
constructor(public name: string) {
this.init();
}
constructor(public name: string, private db: SQLiteObject) {}
/**
* Add a column to an existing table.
@ -277,9 +270,7 @@ export class SQLiteDB {
* @returns Promise resolved when done.
*/
async close(): Promise<void> {
await this.ready();
await this.db?.close();
await this.db.close();
}
/**
@ -455,9 +446,7 @@ export class SQLiteDB {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> {
await this.ready();
return this.db?.executeSql(sql, params);
return this.db.executeSql(sql, params);
}
/**
@ -470,9 +459,7 @@ export class SQLiteDB {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> {
await this.ready();
await this.db?.sqlBatch(sqlStatements);
await this.db.sqlBatch(sqlStatements);
}
/**
@ -753,25 +740,6 @@ export class SQLiteDB {
};
}
/**
* Initialize the database.
*/
init(): void {
this.promise = this.createDatabase().then(db => {
if (CoreDB.loggingEnabled()) {
const spies = this.getDatabaseSpies(db);
db = new Proxy(db, {
get: (target, property, receiver) => spies[property] ?? Reflect.get(target, property, receiver),
});
}
this.db = db;
return;
});
}
/**
* Insert a record into a table and return the "rowId" field.
*
@ -898,18 +866,7 @@ export class SQLiteDB {
* @returns Promise resolved when open.
*/
async open(): Promise<void> {
await this.ready();
await this.db?.open();
}
/**
* Wait for the DB to be ready.
*
* @returns Promise resolved when ready.
*/
ready(): Promise<void> {
return this.promise;
await this.db.open();
}
/**
@ -1094,83 +1051,6 @@ export class SQLiteDB {
return { sql, params };
}
/**
* Open a database connection.
*
* @returns Database.
*/
protected async createDatabase(): Promise<SQLiteObject> {
await CorePlatform.ready();
return SQLite.create({ name: this.name, location: 'default' });
}
/**
* Get database spy methods to intercept database calls and track logging information.
*
* @param db Database to spy.
* @returns Spy methods.
*/
protected getDatabaseSpies(db: SQLiteObject): Partial<SQLiteObject> {
const dbName = this.name;
return {
async executeSql(statement, params) {
const start = performance.now();
try {
const result = await db.executeSql(statement, params);
CoreDB.logQuery({
params,
sql: statement,
duration: performance.now() - start,
dbName,
});
return result;
} catch (error) {
CoreDB.logQuery({
params,
error,
sql: statement,
duration: performance.now() - start,
dbName,
});
throw error;
}
},
async sqlBatch(statements) {
const start = performance.now();
const sql = Array.isArray(statements)
? statements.join(' | ')
: String(statements);
try {
const result = await db.sqlBatch(statements);
CoreDB.logQuery({
sql,
duration: performance.now() - start,
dbName,
});
return result;
} catch (error) {
CoreDB.logQuery({
sql,
error,
duration: performance.now() - start,
dbName,
});
throw error;
}
},
};
}
}
export type SQLiteDBRecordValues = {

View File

@ -1,219 +0,0 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { SQLiteDB } from '@classes/sqlitedb';
import { DbTransaction, SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx';
import { CoreDB } from '@services/db';
/**
* Class to mock the interaction with the SQLite database.
*/
export class SQLiteDBMock extends SQLiteDB {
/**
* Create and open the database.
*
* @param name Database name.
*/
constructor(public name: string) {
super(name);
}
/**
* Close the database.
*
* @returns Promise resolved when done.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
close(): Promise<any> {
// WebSQL databases aren't closed.
return Promise.resolve();
}
/**
* Drop all the data in the database.
*
* @returns Promise resolved when done.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async emptyDatabase(): Promise<any> {
await this.ready();
return new Promise((resolve, reject): void => {
this.db?.transaction((tx) => {
// Query all tables from sqlite_master that we have created and can modify.
const args = [];
const query = `SELECT * FROM sqlite_master
WHERE name NOT LIKE 'sqlite\\_%' escape '\\' AND name NOT LIKE '\\_%' escape '\\'`;
tx.executeSql(query, args, (tx, result) => {
if (result.rows.length <= 0) {
// No tables to delete, stop.
resolve(null);
return;
}
// Drop all the tables.
const promises: Promise<void>[] = [];
for (let i = 0; i < result.rows.length; i++) {
promises.push(new Promise((resolve, reject): void => {
// Drop the table.
const name = JSON.stringify(result.rows.item(i).name);
tx.executeSql('DROP TABLE ' + name, [], resolve, reject);
}));
}
Promise.all(promises).then(resolve).catch(reject);
}, reject);
});
});
}
/**
* Execute a SQL query.
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
*
* @param sql SQL query to execute.
* @param params Query parameters.
* @returns Promise resolved with the result.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async execute(sql: string, params?: any[]): Promise<any> {
await this.ready();
return new Promise((resolve, reject): void => {
// With WebSQL, all queries must be run in a transaction.
this.db?.transaction((tx) => {
tx.executeSql(
sql,
params,
(_, results) => resolve(results),
(_, error) => reject(new Error(`SQL failed: ${sql}, reason: ${error?.message}`)),
);
});
});
}
/**
* Execute a set of SQL queries. This operation is atomic.
* IMPORTANT: Use this function only if you cannot use any of the other functions in this API. Please take into account that
* these query will be run in SQLite (Mobile) and Web SQL (desktop), so your query should work in both environments.
*
* @param sqlStatements SQL statements to execute.
* @returns Promise resolved with the result.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async executeBatch(sqlStatements: any[]): Promise<any> {
await this.ready();
return new Promise((resolve, reject): void => {
// Create a transaction to execute the queries.
this.db?.transaction((tx) => {
const promises: Promise<void>[] = [];
// Execute all the queries. Each statement can be a string or an array.
sqlStatements.forEach((statement) => {
promises.push(new Promise((resolve, reject): void => {
let query;
let params;
if (Array.isArray(statement)) {
query = statement[0];
params = statement[1];
} else {
query = statement;
params = null;
}
tx.executeSql(query, params, (_, results) => resolve(results), (_, error) => reject(error));
}));
});
// eslint-disable-next-line promise/catch-or-return
Promise.all(promises).then(resolve, reject);
});
});
}
/**
* Open the database. Only needed if it was closed before, a database is automatically opened when created.
*
* @returns Promise resolved when done.
*/
open(): Promise<void> {
// WebSQL databases can't closed, so the open method isn't needed.
return Promise.resolve();
}
/**
* @inheritdoc
*/
protected async createDatabase(): Promise<SQLiteObject> {
// This DB is for desktop apps, so use a big size to be sure it isn't filled.
return (window as unknown as WebSQLWindow).openDatabase(this.name, '1.0', this.name, 500 * 1024 * 1024);
}
/**
* @inheritdoc
*/
protected getDatabaseSpies(db: SQLiteObject): Partial<SQLiteObject> {
const dbName = this.name;
return {
transaction: (callback) => db.transaction((transaction) => {
const transactionSpy: DbTransaction = {
executeSql(sql, params, success, error) {
const start = performance.now();
return transaction.executeSql(
sql,
params,
(...args) => {
CoreDB.logQuery({
sql,
params,
duration: performance.now() - start,
dbName,
});
return success?.(...args);
},
(...args) => {
CoreDB.logQuery({
sql,
params,
error: args[0],
duration: performance.now() - start,
dbName,
});
return error?.(...args);
},
);
},
};
return callback(transactionSpy);
}),
};
}
}
interface WebSQLWindow extends Window {
openDatabase(name: string, version: string, displayName: string, estimatedSize: number): SQLiteObject;
}

View File

@ -0,0 +1,130 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx';
import { CorePromisedValue } from '@classes/promised-value';
import { Sqlite3Worker1Promiser, sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm';
/**
* Throw an error indicating that the given method hasn't been implemented.
*
* @param method Method name.
*/
function notImplemented(method: string): any {
throw new Error(`${method} method not implemented.`);
}
/**
* SQLiteObject adapter implemented using the sqlite-wasm package.
*/
export class WasmSQLiteObject implements SQLiteObject {
private name: string;
private promisedPromiser: CorePromisedValue<Sqlite3Worker1Promiser>;
private promiser: Sqlite3Worker1Promiser;
constructor(name: string) {
this.name = name;
this.promisedPromiser = new CorePromisedValue();
this.promiser = async (...args) => {
const promiser = await this.promisedPromiser;
return promiser.call(promiser, ...args);
};
}
/**
* Delete the database.
*/
async delete(): Promise<any> {
if (!this.promisedPromiser.isResolved()) {
await this.open();
}
await this.promiser('close', { unlink: true });
}
/**
* @inheritdoc
*/
async open(): Promise<any> {
const promiser = await new Promise<Sqlite3Worker1Promiser>((resolve) => {
const _promiser = sqlite3Worker1Promiser(() => resolve(_promiser));
});
await promiser('open', { filename: `file:${this.name}.sqlite3`, vfs: 'opfs' });
this.promisedPromiser.resolve(promiser);
}
/**
* @inheritdoc
*/
async close(): Promise<any> {
await this.promiser('close', {});
}
/**
* @inheritdoc
*/
async executeSql(statement: string, params?: any[] | undefined): Promise<any> {
const rows = [] as unknown[];
await this.promiser('exec', {
sql: statement,
bind: params,
callback({ row, columnNames }) {
if (!row) {
return;
}
rows.push(columnNames.reduce((record, column, index) => {
record[column] = row[index];
return record;
}, {}));
},
});
return {
rows: {
item: (i: number) => rows[i],
length: rows.length,
},
rowsAffected: rows.length,
};
}
/**
* @inheritdoc
*/
async sqlBatch(sqlStatements: any[]): Promise<any> {
await Promise.all(sqlStatements.map(sql => this.executeSql(sql)));
}
// These methods and properties are not used in our app,
// but still need to be declared to conform with the SQLiteObject interface.
_objectInstance = null; // eslint-disable-line @typescript-eslint/naming-convention
databaseFeatures = { isSQLitePluginDatabase: false };
openDBs = null;
addTransaction = () => notImplemented('SQLiteObject.addTransaction');
transaction = () => notImplemented('SQLiteObject.transaction');
readTransaction = () => notImplemented('SQLiteObject.readTransaction');
startNextTransaction = () => notImplemented('SQLiteObject.startNextTransaction');
abortallPendingTransactions = () => notImplemented('SQLiteObject.abortallPendingTransactions');
}

View File

@ -42,6 +42,8 @@ import { CorePlatform } from '@services/platform';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreNative } from '@features/native/services/native';
import { SecureStorageMock } from '@features/emulator/classes/SecureStorage';
import { CoreDbProvider } from '@services/db';
import { CoreDbProviderMock } from '@features/emulator/services/db';
/**
* This module handles the emulation of Cordova plugins in browser and desktop.
@ -95,6 +97,10 @@ import { SecureStorageMock } from '@features/emulator/classes/SecureStorage';
? new LocalNotifications()
: new LocalNotificationsMock(),
},
{
provide: CoreDbProvider,
useFactory: (): CoreDbProvider => CorePlatform.is('cordova') ? new CoreDbProvider() : new CoreDbProviderMock(),
},
{
provide: APP_INITIALIZER,
useValue: async () => {

View File

@ -0,0 +1,47 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { asyncInstance } from '@/core/utils/async-instance';
import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx';
import { WasmSQLiteObject } from '@features/emulator/classes/wasm-sqlite-object';
import { CoreDbProvider } from '@services/db';
/**
* Emulates the database provider in the browser.
*/
export class CoreDbProviderMock extends CoreDbProvider {
/**
* @inheritdoc
*/
protected createDatabase(name: string): SQLiteObject {
return asyncInstance(async () => {
const db = new WasmSQLiteObject(name);
await db.open();
return db;
});
}
/**
* @inheritdoc
*/
protected async deleteDatabase(name: string): Promise<void> {
const db = new WasmSQLiteObject(name);
await db.delete();
}
}

View File

@ -15,10 +15,11 @@
import { Injectable } from '@angular/core';
import { SQLiteDB } from '@classes/sqlitedb';
import { SQLiteDBMock } from '@features/emulator/classes/sqlitedb';
import { CoreBrowser } from '@singletons/browser';
import { makeSingleton, SQLite } from '@singletons';
import { SQLite, makeSingleton } from '@singletons';
import { CorePlatform } from '@services/platform';
import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx';
import { asyncInstance } from '@/core/utils/async-instance';
const tableNameRegex = new RegExp([
'^SELECT.*FROM ([^ ]+)',
@ -208,45 +209,129 @@ export class CoreDbProvider {
*/
getDB(name: string, forceNew?: boolean): SQLiteDB {
if (this.dbInstances[name] === undefined || forceNew) {
if (CorePlatform.is('cordova')) {
this.dbInstances[name] = new SQLiteDB(name);
} else {
this.dbInstances[name] = new SQLiteDBMock(name);
let db = this.createDatabase(name);
if (this.loggingEnabled()) {
const spies = this.getDatabaseSpies(name, db);
db = new Proxy(db, {
get: (target, property, receiver) => spies[property] ?? Reflect.get(target, property, receiver),
}) as unknown as SQLiteObject;
}
this.dbInstances[name] = new SQLiteDB(name, db);
}
return this.dbInstances[name];
}
/**
* Create database connection.
*
* @param name Database name.
* @returns Database connection.
*/
protected createDatabase(name: string): SQLiteObject {
// Ideally, this method would return a Promise instead of resorting to Duck typing;
// but doing so would mean that the getDB() method should also return a promise.
// Given that it is heavily used throughout the app, we want to avoid it for now.
return asyncInstance(async () => {
await CorePlatform.ready();
return SQLite.create({ name, location: 'default' });
});
}
/**
* Delete a DB.
*
* @param name DB name.
* @returns Promise resolved when the DB is deleted.
*/
async deleteDB(name: string): Promise<void> {
if (this.dbInstances[name] !== undefined) {
// Close the database first.
await this.dbInstances[name].close();
const db = this.dbInstances[name];
delete this.dbInstances[name];
if (db instanceof SQLiteDBMock) {
// In WebSQL we cannot delete the database, just empty it.
return db.emptyDatabase();
} else {
return SQLite.deleteDatabase({
name,
location: 'default',
});
}
} else if (CorePlatform.is('cordova')) {
return SQLite.deleteDatabase({
name,
location: 'default',
});
}
await this.deleteDatabase(name);
}
/**
* Delete database.
*
* @param name Database name.
*/
protected async deleteDatabase(name: string): Promise<void> {
await SQLite.deleteDatabase({
name,
location: 'default',
});
}
/**
* Get database spy methods to intercept database calls and track logging information.
*
* @param dbName Database name.
* @param db Database to spy.
* @returns Spy methods.
*/
protected getDatabaseSpies(dbName: string, db: SQLiteObject): Partial<SQLiteObject> {
return {
async executeSql(statement, params) {
const start = performance.now();
try {
const result = await db.executeSql(statement, params);
CoreDB.logQuery({
params,
sql: statement,
duration: performance.now() - start,
dbName,
});
return result;
} catch (error) {
CoreDB.logQuery({
params,
error,
sql: statement,
duration: performance.now() - start,
dbName,
});
throw error;
}
},
async sqlBatch(statements) {
const start = performance.now();
const sql = Array.isArray(statements)
? statements.join(' | ')
: String(statements);
try {
const result = await db.sqlBatch(statements);
CoreDB.logQuery({
sql,
duration: performance.now() - start,
dbName,
});
return result;
} catch (error) {
CoreDB.logQuery({
sql,
error,
duration: performance.now() - start,
dbName,
});
throw error;
}
},
};
}
}

93
src/types/sqlite-wasm.d.ts vendored 100644
View File

@ -0,0 +1,93 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Brand } from '@/core/utils/types';
// Can be removed when the following issue is fixed:
// https://github.com/sqlite/sqlite-wasm/issues/53
declare module '@sqlite.org/sqlite-wasm' {
export type SqliteDbId = Brand<unknown, 'SqliteDbId'>;
export interface SqliteRowData {
columnNames: string[];
row: SqlValue[] | undefined;
rowNumber: number | null;
}
export interface Sqlite3Worker1Messages {
close: {
args?: {
unlink?: boolean;
};
result: {
filename?: string;
};
};
'config-get': {
result: {
version: object;
bigIntEnabled: boolean;
vfsList: unknown;
};
};
exec: {
args: {
sql: string;
bind?: BindingSpec;
callback?(data: SqliteRowData): void | false;
};
};
open: {
args: {
filename: string;
vfs?: string;
};
result: {
dbId: SqliteDbId;
filename: string;
persistent: boolean;
vfs: string;
};
};
}
export interface Sqlite3Worker1PromiserConfig {
onready(): void;
worker?: unknown;
generateMessageId?(message: object): string;
debug?(...args: unknown[]): void;
onunhandled?(event: unknown): void;
}
export type Sqlite3Worker1PromiserMethodOptions<T extends keyof Sqlite3Worker1Messages> =
Sqlite3Worker1Messages[T] extends { args?: infer TArgs }
? { type: T; args: TArgs }
: { type: T; args?: Sqlite3Worker1Messages[T]['args'] };
export type Sqlite3Worker1Promiser =
(<T extends keyof Sqlite3Worker1Messages>(
type: T,
args: Sqlite3Worker1Messages[T]['args'],
) => Promise<Sqlite3Worker1Messages[T]['result']>) &
(<T extends keyof Sqlite3Worker1Messages>(
options: Sqlite3Worker1PromiserMethodOptions<T>,
) => Promise<Sqlite3Worker1Messages[T]['result']>);
export function sqlite3Worker1Promiser(): Sqlite3Worker1Promiser;
export function sqlite3Worker1Promiser(onready: () => void): Sqlite3Worker1Promiser;
export function sqlite3Worker1Promiser(config: Sqlite3Worker1PromiserOptions): Sqlite3Worker1Promiser;
}