UNPKG

@sqlite.org/sqlite-wasm

Version:

SQLite Wasm conveniently wrapped as an ES Module.

194 lines (184 loc) 6 kB
/* 2022-08-24 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. *********************************************************************** This file implements a Promise-based proxy for the sqlite3 Worker API #1. It is intended to be included either from the main thread or a Worker, but only if (A) the environment supports nested Workers and (B) it's _not_ a Worker which loads the sqlite3 WASM/JS module. This file's features will load that module and provide a slightly simpler client-side interface than the slightly-lower-level Worker API does. This script necessarily exposes one global symbol, but clients may freely `delete` that symbol after calling it. */ 'use strict'; globalThis.sqlite3Worker1Promiser = function callee( config = callee.defaultConfig, ) { if (1 === arguments.length && 'function' === typeof arguments[0]) { const f = config; config = Object.assign(Object.create(null), callee.defaultConfig); config.onready = f; } else { config = Object.assign(Object.create(null), callee.defaultConfig, config); } const handlerMap = Object.create(null); const noop = function () {}; const err = config.onerror || noop; const debug = config.debug || noop; const idTypeMap = config.generateMessageId ? undefined : Object.create(null); const genMsgId = config.generateMessageId || function (msg) { return ( msg.type + '#' + (idTypeMap[msg.type] = (idTypeMap[msg.type] || 0) + 1) ); }; const toss = (...args) => { throw new Error(args.join(' ')); }; if (!config.worker) config.worker = callee.defaultConfig.worker; if ('function' === typeof config.worker) config.worker = config.worker(); let dbId; let promiserFunc; config.worker.onmessage = function (ev) { ev = ev.data; debug('worker1.onmessage', ev); let msgHandler = handlerMap[ev.messageId]; if (!msgHandler) { if (ev && 'sqlite3-api' === ev.type && 'worker1-ready' === ev.result) { if (config.onready) config.onready(promiserFunc); return; } msgHandler = handlerMap[ev.type]; if (msgHandler && msgHandler.onrow) { msgHandler.onrow(ev); return; } if (config.onunhandled) config.onunhandled(arguments[0]); else err('sqlite3Worker1Promiser() unhandled worker message:', ev); return; } delete handlerMap[ev.messageId]; switch (ev.type) { case 'error': msgHandler.reject(ev); return; case 'open': if (!dbId) dbId = ev.dbId; break; case 'close': if (ev.dbId === dbId) dbId = undefined; break; default: break; } try { msgHandler.resolve(ev); } catch (e) { msgHandler.reject(e); } }; return (promiserFunc = function () { let msg; if (1 === arguments.length) { msg = arguments[0]; } else if (2 === arguments.length) { msg = Object.create(null); msg.type = arguments[0]; msg.args = arguments[1]; msg.dbId = msg.args.dbId; } else { toss('Invalid arguments for sqlite3Worker1Promiser()-created factory.'); } if (!msg.dbId && msg.type !== 'open') msg.dbId = dbId; msg.messageId = genMsgId(msg); msg.departureTime = performance.now(); const proxy = Object.create(null); proxy.message = msg; let rowCallbackId; if ('exec' === msg.type && msg.args) { if ('function' === typeof msg.args.callback) { rowCallbackId = msg.messageId + ':row'; proxy.onrow = msg.args.callback; msg.args.callback = rowCallbackId; handlerMap[rowCallbackId] = proxy; } else if ('string' === typeof msg.args.callback) { toss( 'exec callback may not be a string when using the Promise interface.', ); } } let p = new Promise(function (resolve, reject) { proxy.resolve = resolve; proxy.reject = reject; handlerMap[msg.messageId] = proxy; debug( 'Posting', msg.type, 'message to Worker dbId=' + (dbId || 'default') + ':', msg, ); config.worker.postMessage(msg); }); if (rowCallbackId) p = p.finally(() => delete handlerMap[rowCallbackId]); return p; }); }; globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function () { let theJs = 'sqlite3-worker1.js'; if (this.currentScript) { const src = this.currentScript.src.split('/'); src.pop(); theJs = src.join('/') + '/' + theJs; } else if (globalThis.location) { const urlParams = new URL(globalThis.location.href).searchParams; if (urlParams.has('sqlite3.dir')) { theJs = urlParams.get('sqlite3.dir') + '/' + theJs; } } return new Worker(theJs + globalThis.location.search); }.bind({ currentScript: globalThis?.document?.currentScript, }), onerror: (...args) => console.error('worker1 promiser error', ...args), }; sqlite3Worker1Promiser.v2 = function (config) { let oldFunc; if ('function' == typeof config) { oldFunc = config; config = {}; } else if ('function' === typeof config?.onready) { oldFunc = config.onready; delete config.onready; } const promiseProxy = Object.create(null); config = Object.assign(config || Object.create(null), { onready: async function (func) { try { if (oldFunc) await oldFunc(func); promiseProxy.resolve(func); } catch (e) { promiseProxy.reject(e); } }, }); const p = new Promise(function (resolve, reject) { promiseProxy.resolve = resolve; promiseProxy.reject = reject; }); try { this.original(config); } catch (e) { promiseProxy.reject(e); } return p; }.bind({ original: sqlite3Worker1Promiser, });