diff options
Diffstat (limited to 'ext/wasm/api/sqlite3-worker1-promiser.js')
-rw-r--r-- | ext/wasm/api/sqlite3-worker1-promiser.js | 263 |
1 files changed, 0 insertions, 263 deletions
diff --git a/ext/wasm/api/sqlite3-worker1-promiser.js b/ext/wasm/api/sqlite3-worker1-promiser.js deleted file mode 100644 index 1689d3480..000000000 --- a/ext/wasm/api/sqlite3-worker1-promiser.js +++ /dev/null @@ -1,263 +0,0 @@ -/* - 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'; -/** - Configures an sqlite3 Worker API #1 Worker such that it can be - manipulated via a Promise-based interface and returns a factory - function which returns Promises for communicating with the worker. - This proxy has an _almost_ identical interface to the normal - worker API, with any exceptions documented below. - - It requires a configuration object with the following properties: - - - `worker` (required): a Worker instance which loads - `sqlite3-worker1.js` or a functional equivalent. Note that the - promiser factory replaces the worker.onmessage property. This - config option may alternately be a function, in which case this - function re-assigns this property with the result of calling that - function, enabling delayed instantiation of a Worker. - - - `onready` (optional, but...): this callback is called with no - arguments when the worker fires its initial - 'sqlite3-api'/'worker1-ready' message, which it does when - sqlite3.initWorker1API() completes its initialization. This is - the simplest way to tell the worker to kick off work at the - earliest opportunity. - - - `onunhandled` (optional): a callback which gets passed the - message event object for any worker.onmessage() events which - are not handled by this proxy. Ideally that "should" never - happen, as this proxy aims to handle all known message types. - - - `generateMessageId` (optional): a function which, when passed an - about-to-be-posted message object, generates a _unique_ message ID - for the message, which this API then assigns as the messageId - property of the message. It _must_ generate unique IDs on each call - so that dispatching can work. If not defined, a default generator - is used (which should be sufficient for most or all cases). - - - `debug` (optional): a console.debug()-style function for logging - information about messages. - - This function returns a stateful factory function with the - following interfaces: - - - Promise function(messageType, messageArgs) - - Promise function({message object}) - - The first form expects the "type" and "args" values for a Worker - message. The second expects an object in the form {type:..., - args:...} plus any other properties the client cares to set. This - function will always set the `messageId` property on the object, - even if it's already set, and will set the `dbId` property to the - current database ID if it is _not_ set in the message object. - - The function throws on error. - - The function installs a temporary message listener, posts a - message to the configured Worker, and handles the message's - response via the temporary message listener. The then() callback - of the returned Promise is passed the `message.data` property from - the resulting message, i.e. the payload from the worker, stripped - of the lower-level event state which the onmessage() handler - receives. - - Example usage: - - ``` - const config = {...}; - const sq3Promiser = sqlite3Worker1Promiser(config); - sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ - console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} - }); - sq3Promiser({type:'close'}).then((msg)=>{ - console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} - }); - ``` - - Differences from Worker API #1: - - - exec's {callback: STRING} option does not work via this - interface (it triggers an exception), but {callback: function} - does and works exactly like the STRING form does in the Worker: - the callback is called one time for each row of the result set, - passed the same worker message format as the worker API emits: - - {type:typeString, - row:VALUE, - rowNumber:1-based-#, - columnNames: array} - - Where `typeString` is an internally-synthesized message type string - used temporarily for worker message dispatching. It can be ignored - by all client code except that which tests this API. The `row` - property contains the row result in the form implied by the - `rowMode` option (defaulting to `'array'`). The `rowNumber` is a - 1-based integer value incremented by 1 on each call into th - callback. - - At the end of the result set, the same event is fired with - (row=undefined, rowNumber=null) to indicate that - the end of the result set has been reached. Note that the rows - arrive via worker-posted messages, with all the implications - of that. -*/ -self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ - // Inspired by: https://stackoverflow.com/a/52439530 - 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 /* config.onerror is intentionally undocumented - pending finding a less ambiguous name */; - 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; - 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) { - /*fired one time when the Worker1 API initializes*/ - if(config.onready) config.onready(); - return; - } - msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; - 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)} - }/*worker.onmessage()*/; - return function(/*(msgType, msgArgs) || (msgEnvelope)*/){ - let msg; - if(1===arguments.length){ - msg = arguments[0]; - }else if(2===arguments.length){ - msg = { - type: arguments[0], - args: arguments[1] - }; - }else{ - toss("Invalid arugments for sqlite3Worker1Promiser()-created factory."); - } - if(!msg.dbId) msg.dbId = dbId; - msg.messageId = genMsgId(msg); - msg.departureTime = performance.now(); - const proxy = Object.create(null); - proxy.message = msg; - let rowCallbackId /* message handler ID for exec on-row callback proxy */; - 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."); - /** - Design note: the reason for this limitation is that this - API takes over worker.onmessage() and the client has no way - of adding their own message-type handlers to it. Per-row - callbacks are implemented as short-lived message.type - mappings for worker.onmessage(). - - We "could" work around this by providing a new - config.fallbackMessageHandler (or some such) which contains - a map of event type names to callbacks. Seems like overkill - for now, seeing as the client can pass callback functions - to this interface (whereas the string-form "callback" is - needed for the over-the-Worker interface). - */ - } - } - //debug("requestWork", msg); - 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; - }; -}/*sqlite3Worker1Promiser()*/; -self.sqlite3Worker1Promiser.defaultConfig = { - worker: function(){ -//#if target=es6-bundler-friendly - return new Worker("sqlite3-worker1.js"); -//#else - let theJs = "sqlite3-worker1.js"; - if(this.currentScript){ - const src = this.currentScript.src.split('/'); - src.pop(); - theJs = src.join('/')+'/' + theJs; - //console.warn("promiser currentScript, theJs =",this.currentScript,theJs); - }else{ - //console.warn("promiser self.location =",self.location); - const urlParams = new URL(self.location.href).searchParams; - if(urlParams.has('sqlite3.dir')){ - theJs = urlParams.get('sqlite3.dir') + '/' + theJs; - } - } - return new Worker(theJs + self.location.search); -//#endif - }.bind({ - currentScript: self?.document?.currentScript - }), - onerror: (...args)=>console.error('worker1 promiser error',...args) -}; |