188 lines
5.5 KiB
JavaScript
188 lines
5.5 KiB
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.
|
|
*/
|
|
'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 () {
|
|
return new Worker(
|
|
new URL('sqlite3-worker1-bundler-friendly.mjs', import.meta.url),
|
|
{
|
|
type: 'module',
|
|
},
|
|
);
|
|
},
|
|
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,
|
|
});
|
|
|
|
export default sqlite3Worker1Promiser.v2;
|