UNPKG

sqlocal

Version:

SQLocal makes it easy to run SQLite3 in the browser, backed by the origin private file system.

454 lines 17.4 kB
import coincident from 'coincident'; import { createMutex } from './lib/create-mutex.js'; import { SQLiteMemoryDriver } from './drivers/sqlite-memory-driver.js'; export class SQLocalProcessor { constructor(driver) { Object.defineProperty(this, "driver", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "userFunctions", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "initMutex", { enumerable: true, configurable: true, writable: true, value: createMutex() }); Object.defineProperty(this, "transactionMutex", { enumerable: true, configurable: true, writable: true, value: createMutex() }); Object.defineProperty(this, "transactionKey", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "proxy", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "reinitChannel", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "onmessage", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "init", { enumerable: true, configurable: true, writable: true, value: async (reason) => { if (!this.config.databasePath) return; await this.initMutex.lock(); try { try { await this.driver.init(this.config); } catch { console.warn(`Persistence failed, so ${this.config.databasePath} will not be saved. For origin private file system persistence, make sure your web server is configured to use the correct HTTP response headers (See https://sqlocal.dev/guide/setup#cross-origin-isolation).`); this.config.databasePath = ':memory:'; this.driver = new SQLiteMemoryDriver(); await this.driver.init(this.config); } if (this.driver.storageType !== 'memory') { this.reinitChannel = new BroadcastChannel(`_sqlocal_reinit_(${this.config.databasePath})`); this.reinitChannel.onmessage = (event) => { const message = event.data; if (this.config.clientKey === message.clientKey) return; switch (message.type) { case 'reinit': this.init(message.reason); break; case 'close': this.driver.destroy(); break; } }; } await Promise.all(Array.from(this.userFunctions.values()).map((fn) => { return this.initUserFunction(fn); })); await this.execInitStatements(); this.emitMessage({ type: 'event', event: 'connect', reason }); } catch (error) { this.emitMessage({ type: 'error', error, queryKey: null, }); await this.destroy(); } finally { await this.initMutex.unlock(); } } }); Object.defineProperty(this, "postMessage", { enumerable: true, configurable: true, writable: true, value: async (event, _transfer) => { const message = event instanceof MessageEvent ? event.data : event; await this.initMutex.lock(); switch (message.type) { case 'config': this.editConfig(message); break; case 'query': case 'batch': case 'transaction': this.exec(message); break; case 'function': this.createUserFunction(message); break; case 'getinfo': this.getDatabaseInfo(message); break; case 'import': this.importDb(message); break; case 'export': this.exportDb(message); break; case 'delete': this.deleteDb(message); break; case 'destroy': this.destroy(message); break; } await this.initMutex.unlock(); } }); Object.defineProperty(this, "emitMessage", { enumerable: true, configurable: true, writable: true, value: (message, transfer = []) => { if (this.onmessage) { this.onmessage(message, transfer); } } }); Object.defineProperty(this, "editConfig", { enumerable: true, configurable: true, writable: true, value: (message) => { this.config = message.config; this.init('initial'); } }); Object.defineProperty(this, "exec", { enumerable: true, configurable: true, writable: true, value: async (message) => { try { const response = { type: 'data', queryKey: message.queryKey, data: [], }; switch (message.type) { case 'query': const partOfTransaction = this.transactionKey !== null && this.transactionKey === message.transactionKey; try { if (!partOfTransaction) { await this.transactionMutex.lock(); } const statementData = await this.driver.exec(message); response.data.push(statementData); } finally { if (!partOfTransaction) { await this.transactionMutex.unlock(); } } break; case 'batch': try { await this.transactionMutex.lock(); const results = await this.driver.execBatch(message.statements); response.data.push(...results); } finally { await this.transactionMutex.unlock(); } break; case 'transaction': if (message.action === 'begin') { await this.transactionMutex.lock(); this.transactionKey = message.transactionKey; await this.driver.exec({ sql: 'BEGIN' }); } if ((message.action === 'commit' || message.action === 'rollback') && this.transactionKey !== null && this.transactionKey === message.transactionKey) { const sql = message.action === 'commit' ? 'COMMIT' : 'ROLLBACK'; await this.driver.exec({ sql }); this.transactionKey = null; await this.transactionMutex.unlock(); } break; } this.emitMessage(response); } catch (error) { this.emitMessage({ type: 'error', error, queryKey: message.queryKey, }); } } }); Object.defineProperty(this, "execInitStatements", { enumerable: true, configurable: true, writable: true, value: async () => { if (this.config.onInitStatements) { for (let statement of this.config.onInitStatements) { await this.driver.exec(statement); } } } }); Object.defineProperty(this, "getDatabaseInfo", { enumerable: true, configurable: true, writable: true, value: async (message) => { try { this.emitMessage({ type: 'info', queryKey: message.queryKey, info: { databasePath: this.config.databasePath, storageType: this.driver.storageType, databaseSizeBytes: await this.driver.getDatabaseSizeBytes(), persisted: await this.driver.isDatabasePersisted(), }, }); } catch (error) { this.emitMessage({ type: 'error', queryKey: message.queryKey, error, }); } } }); Object.defineProperty(this, "createUserFunction", { enumerable: true, configurable: true, writable: true, value: async (message) => { const { functionName: name, functionType: type, queryKey } = message; let fn; if (this.userFunctions.has(name)) { this.emitMessage({ type: 'error', error: new Error(`A user-defined function with the name "${name}" has already been created for this SQLocal instance.`), queryKey, }); return; } switch (type) { case 'callback': fn = { type, name, func: (...args) => { this.emitMessage({ type: 'callback', name, args }); }, }; break; case 'scalar': fn = { type, name, func: this.proxy[`_sqlocal_func_${name}`], }; break; case 'aggregate': fn = { type, name, func: { step: this.proxy[`_sqlocal_func_${name}_step`], final: this.proxy[`_sqlocal_func_${name}_final`], }, }; break; } try { await this.initUserFunction(fn); this.emitMessage({ type: 'success', queryKey, }); } catch (error) { this.emitMessage({ type: 'error', error, queryKey, }); } } }); Object.defineProperty(this, "initUserFunction", { enumerable: true, configurable: true, writable: true, value: async (fn) => { await this.driver.createFunction(fn); this.userFunctions.set(fn.name, fn); } }); Object.defineProperty(this, "importDb", { enumerable: true, configurable: true, writable: true, value: async (message) => { const { queryKey, database } = message; let errored = false; try { await this.driver.import(database); if (this.driver.storageType === 'memory') { await this.execInitStatements(); } } catch (error) { this.emitMessage({ type: 'error', error, queryKey, }); errored = true; } finally { if (this.driver.storageType !== 'memory') { await this.init('overwrite'); } } if (!errored) { this.emitMessage({ type: 'success', queryKey, }); } } }); Object.defineProperty(this, "exportDb", { enumerable: true, configurable: true, writable: true, value: async (message) => { const { queryKey } = message; try { const { name, data } = await this.driver.export(); this.emitMessage({ type: 'buffer', queryKey, bufferName: name, buffer: data, }, [data]); } catch (error) { this.emitMessage({ type: 'error', error, queryKey, }); } } }); Object.defineProperty(this, "deleteDb", { enumerable: true, configurable: true, writable: true, value: async (message) => { const { queryKey } = message; let errored = false; try { await this.driver.clear(); } catch (error) { this.emitMessage({ type: 'error', error, queryKey, }); errored = true; } finally { await this.init('delete'); } if (!errored) { this.emitMessage({ type: 'success', queryKey, }); } } }); Object.defineProperty(this, "destroy", { enumerable: true, configurable: true, writable: true, value: async (message) => { await this.driver.exec({ sql: 'PRAGMA optimize' }); await this.driver.destroy(); if (this.reinitChannel) { this.reinitChannel.close(); this.reinitChannel = undefined; } if (message) { this.emitMessage({ type: 'success', queryKey: message.queryKey, }); } } }); const isInWorker = typeof WorkerGlobalScope !== 'undefined' && globalThis instanceof WorkerGlobalScope; const proxy = isInWorker ? coincident(globalThis) : globalThis; this.proxy = proxy; this.driver = driver; } } //# sourceMappingURL=processor.js.map