@libsql/libsql-wasm-experimental
Version:
libSQL Wasm packaged as an ES module.
148 lines (135 loc) • 4.96 kB
JavaScript
/*
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.
*/
;
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;
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();
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 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 arugments 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)
};