MOBILE-4304 core: Replace WebSQL with sqlite-wasm
This commit is contained in:
		
							parent
							
								
									368bf02bc2
								
							
						
					
					
						commit
						a7bd1e5f89
					
				| @ -95,7 +95,11 @@ | |||||||
|           "options": { |           "options": { | ||||||
|             "disableHostCheck": true, |             "disableHostCheck": true, | ||||||
|             "port": 8100, |             "port": 8100, | ||||||
|             "buildTarget": "app:build" |             "buildTarget": "app:build", | ||||||
|  |             "headers": { | ||||||
|  |                 "Cross-Origin-Opener-Policy": "same-origin", | ||||||
|  |                 "Cross-Origin-Embedder-Policy": "require-corp" | ||||||
|  |             } | ||||||
|           }, |           }, | ||||||
|           "configurations": { |           "configurations": { | ||||||
|             "production": { |             "production": { | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -55,6 +55,7 @@ | |||||||
|         "@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7", |         "@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7", | ||||||
|         "@ngx-translate/core": "^15.0.0", |         "@ngx-translate/core": "^15.0.0", | ||||||
|         "@ngx-translate/http-loader": "^8.0.0", |         "@ngx-translate/http-loader": "^8.0.0", | ||||||
|  |         "@sqlite.org/sqlite-wasm": "^3.45.0-build1", | ||||||
|         "@types/chart.js": "^2.9.31", |         "@types/chart.js": "^2.9.31", | ||||||
|         "@types/cordova": "0.0.34", |         "@types/cordova": "0.0.34", | ||||||
|         "@types/dom-mediacapture-record": "1.0.7", |         "@types/dom-mediacapture-record": "1.0.7", | ||||||
| @ -8997,6 +8998,14 @@ | |||||||
|         "@sinonjs/commons": "^3.0.0" |         "@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": { |     "node_modules/@stencil/core": { | ||||||
|       "version": "4.10.0", |       "version": "4.10.0", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ | |||||||
|   ], |   ], | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "ng": "ng", |     "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", |     "serve:test": "NODE_ENV=testing ionic serve --no-open", | ||||||
|     "build": "ionic build", |     "build": "ionic build", | ||||||
|     "build:prod": "NODE_ENV=production ionic build --prod", |     "build:prod": "NODE_ENV=production ionic build --prod", | ||||||
| @ -89,6 +89,7 @@ | |||||||
|     "@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7", |     "@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7", | ||||||
|     "@ngx-translate/core": "^15.0.0", |     "@ngx-translate/core": "^15.0.0", | ||||||
|     "@ngx-translate/http-loader": "^8.0.0", |     "@ngx-translate/http-loader": "^8.0.0", | ||||||
|  |     "@sqlite.org/sqlite-wasm": "^3.45.0-build1", | ||||||
|     "@types/chart.js": "^2.9.31", |     "@types/chart.js": "^2.9.31", | ||||||
|     "@types/cordova": "0.0.34", |     "@types/cordova": "0.0.34", | ||||||
|     "@types/dom-mediacapture-record": "1.0.7", |     "@types/dom-mediacapture-record": "1.0.7", | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								patches/@sqlite.org+sqlite-wasm+3.45.0-build1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								patches/@sqlite.org+sqlite-wasm+3.45.0-build1.patch
									
									
									
									
									
										Normal 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 { | ||||||
| @ -31,6 +31,8 @@ const ASSETS = { | |||||||
|     '/src/core/features/h5p/assets': '/lib/h5p', |     '/src/core/features/h5p/assets': '/lib/h5p', | ||||||
|     '/node_modules/ogv/dist': '/lib/ogv', |     '/node_modules/ogv/dist': '/lib/ogv', | ||||||
|     '/node_modules/video.js/dist/video-js.min.css': '/lib/video.js/video-js.min.css', |     '/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) { | module.exports = function(ctx) { | ||||||
|  | |||||||
| @ -14,10 +14,7 @@ | |||||||
| 
 | 
 | ||||||
| import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx'; | import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx'; | ||||||
| 
 | 
 | ||||||
| import { SQLite } from '@singletons'; |  | ||||||
| import { CoreError } from '@classes/errors/error'; | import { CoreError } from '@classes/errors/error'; | ||||||
| import { CoreDB } from '@services/db'; |  | ||||||
| import { CorePlatform } from '@services/platform'; |  | ||||||
| 
 | 
 | ||||||
| type SQLiteDBColumnType = 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB'; | type SQLiteDBColumnType = 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB'; | ||||||
| 
 | 
 | ||||||
| @ -137,17 +134,13 @@ export interface SQLiteDBForeignKeySchema { | |||||||
|  */ |  */ | ||||||
| export class SQLiteDB { | export class SQLiteDB { | ||||||
| 
 | 
 | ||||||
|     db?: SQLiteObject; |  | ||||||
|     promise!: Promise<void>; |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Create and open the database. |      * Create and open the database. | ||||||
|      * |      * | ||||||
|      * @param name Database name. |      * @param name Database name. | ||||||
|  |      * @param db Database connection. | ||||||
|      */ |      */ | ||||||
|     constructor(public name: string) { |     constructor(public name: string, private db: SQLiteObject) {} | ||||||
|         this.init(); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Add a column to an existing table. |      * Add a column to an existing table. | ||||||
| @ -277,9 +270,7 @@ export class SQLiteDB { | |||||||
|      * @returns Promise resolved when done. |      * @returns Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     async close(): Promise<void> { |     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
 |     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
|     async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<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
 |     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
|     async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> { |     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. |      * Insert a record into a table and return the "rowId" field. | ||||||
|      * |      * | ||||||
| @ -898,18 +866,7 @@ export class SQLiteDB { | |||||||
|      * @returns Promise resolved when open. |      * @returns Promise resolved when open. | ||||||
|      */ |      */ | ||||||
|     async open(): Promise<void> { |     async open(): Promise<void> { | ||||||
|         await this.ready(); |         await this.db.open(); | ||||||
| 
 |  | ||||||
|         await this.db?.open(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Wait for the DB to be ready. |  | ||||||
|      * |  | ||||||
|      * @returns Promise resolved when ready. |  | ||||||
|      */ |  | ||||||
|     ready(): Promise<void> { |  | ||||||
|         return this.promise; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -1094,83 +1051,6 @@ export class SQLiteDB { | |||||||
|         return { sql, params }; |         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 = { | export type SQLiteDBRecordValues = { | ||||||
|  | |||||||
| @ -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; |  | ||||||
| } |  | ||||||
							
								
								
									
										130
									
								
								src/core/features/emulator/classes/wasm-sqlite-object.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/core/features/emulator/classes/wasm-sqlite-object.ts
									
									
									
									
									
										Normal 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'); | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -42,6 +42,8 @@ import { CorePlatform } from '@services/platform'; | |||||||
| import { CoreLocalNotifications } from '@services/local-notifications'; | import { CoreLocalNotifications } from '@services/local-notifications'; | ||||||
| import { CoreNative } from '@features/native/services/native'; | import { CoreNative } from '@features/native/services/native'; | ||||||
| import { SecureStorageMock } from '@features/emulator/classes/SecureStorage'; | 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. |  * 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 LocalNotifications() | ||||||
|                 : new LocalNotificationsMock(), |                 : new LocalNotificationsMock(), | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             provide: CoreDbProvider, | ||||||
|  |             useFactory: (): CoreDbProvider => CorePlatform.is('cordova') ? new CoreDbProvider() : new CoreDbProviderMock(), | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             provide: APP_INITIALIZER, |             provide: APP_INITIALIZER, | ||||||
|             useValue: async () => { |             useValue: async () => { | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								src/core/features/emulator/services/db.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/core/features/emulator/services/db.ts
									
									
									
									
									
										Normal 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(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -15,10 +15,11 @@ | |||||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| import { SQLiteDB } from '@classes/sqlitedb'; | import { SQLiteDB } from '@classes/sqlitedb'; | ||||||
| import { SQLiteDBMock } from '@features/emulator/classes/sqlitedb'; |  | ||||||
| import { CoreBrowser } from '@singletons/browser'; | import { CoreBrowser } from '@singletons/browser'; | ||||||
| import { makeSingleton, SQLite } from '@singletons'; | import { SQLite, makeSingleton } from '@singletons'; | ||||||
| import { CorePlatform } from '@services/platform'; | import { CorePlatform } from '@services/platform'; | ||||||
|  | import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx'; | ||||||
|  | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
| 
 | 
 | ||||||
| const tableNameRegex = new RegExp([ | const tableNameRegex = new RegExp([ | ||||||
|     '^SELECT.*FROM ([^ ]+)', |     '^SELECT.*FROM ([^ ]+)', | ||||||
| @ -208,45 +209,129 @@ export class CoreDbProvider { | |||||||
|      */ |      */ | ||||||
|     getDB(name: string, forceNew?: boolean): SQLiteDB { |     getDB(name: string, forceNew?: boolean): SQLiteDB { | ||||||
|         if (this.dbInstances[name] === undefined || forceNew) { |         if (this.dbInstances[name] === undefined || forceNew) { | ||||||
|             if (CorePlatform.is('cordova')) { |             let db = this.createDatabase(name); | ||||||
|                 this.dbInstances[name] = new SQLiteDB(name); | 
 | ||||||
|             } else { |             if (this.loggingEnabled()) { | ||||||
|                 this.dbInstances[name] = new SQLiteDBMock(name); |                 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]; |         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. |      * Delete a DB. | ||||||
|      * |      * | ||||||
|      * @param name DB name. |      * @param name DB name. | ||||||
|      * @returns Promise resolved when the DB is deleted. |  | ||||||
|      */ |      */ | ||||||
|     async deleteDB(name: string): Promise<void> { |     async deleteDB(name: string): Promise<void> { | ||||||
|         if (this.dbInstances[name] !== undefined) { |         if (this.dbInstances[name] !== undefined) { | ||||||
|             // Close the database first.
 |  | ||||||
|             await this.dbInstances[name].close(); |             await this.dbInstances[name].close(); | ||||||
| 
 | 
 | ||||||
|             const db = this.dbInstances[name]; |  | ||||||
|             delete 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
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/types/sqlite-wasm.d.ts
									
									
									
									
										vendored
									
									
										Normal 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; | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user