More work on how to configure the sqlite3 JS API bootstrapping process from higher-level code. Initial version of sqlite3-worker1-promiser, a Promise-based proxy for the Worker API #1.
FossilOrigin-Name: b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878
This commit is contained in:
parent
efeee19a95
commit
9a34509a06
@ -18,44 +18,24 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
if('undefined' !== typeof Module){ // presumably an Emscripten build
|
if('undefined' !== typeof Module){ // presumably an Emscripten build
|
||||||
/**
|
/**
|
||||||
Replace sqlite3ApiBootstrap() with a variant which plugs in the
|
Install a suitable default configuration for sqlite3ApiBootstrap().
|
||||||
Emscripten-based config for all config options which the client
|
|
||||||
does not provide.
|
|
||||||
*/
|
*/
|
||||||
const SAB = self.sqlite3ApiBootstrap;
|
const SABC = self.sqlite3ApiBootstrap.defaultConfig;
|
||||||
self.sqlite3ApiBootstrap = function(apiConfig){
|
SABC.Module = Module /* ==> Current needs to be exposed here for test code. NOT part
|
||||||
apiConfig = apiConfig || {};
|
of the public API. */;
|
||||||
const configDefaults = {
|
SABC.exports = Module['asm'];
|
||||||
Module: Module /* ==> Emscripten-style Module object. Currently
|
SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
|
||||||
needs to be exposed here for test code. NOT part
|
|
||||||
of the public API. */,
|
|
||||||
exports: Module['asm'],
|
|
||||||
memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */
|
|
||||||
};
|
|
||||||
const config = {};
|
|
||||||
Object.keys(configDefaults).forEach(function(k){
|
|
||||||
config[k] = Object.getOwnPropertyDescriptor(apiConfig, k)
|
|
||||||
? apiConfig[k] : configDefaults[k];
|
|
||||||
});
|
|
||||||
// Copy over any properties apiConfig defines but configDefaults does not...
|
|
||||||
Object.keys(apiConfig).forEach(function(k){
|
|
||||||
if(!Object.getOwnPropertyDescriptor(config, k)){
|
|
||||||
config[k] = apiConfig[k];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return SAB(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
For current (2022-08-22) purposes, automatically call
|
For current (2022-08-22) purposes, automatically call
|
||||||
sqlite3ApiBootstrap(). That decision will be revisited at some
|
sqlite3ApiBootstrap(). That decision will be revisited at some
|
||||||
point, as we really want client code to be able to call this to
|
point, as we really want client code to be able to call this to
|
||||||
configure certain parts. If the global sqliteApiConfig property
|
configure certain parts. Clients may modify
|
||||||
is available, it is assumed to be a config object for
|
self.sqlite3ApiBootstrap.defaultConfig to tweak the default
|
||||||
sqlite3ApiBootstrap().
|
configuration used by a no-args call to sqlite3ApiBootstrap().
|
||||||
*/
|
*/
|
||||||
//console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig);
|
//console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig);
|
||||||
const sqlite3 = self.sqlite3ApiBootstrap(self.sqlite3ApiConfig || Object.create(null));
|
const sqlite3 = self.sqlite3ApiBootstrap();
|
||||||
delete self.sqlite3ApiBootstrap;
|
delete self.sqlite3ApiBootstrap;
|
||||||
|
|
||||||
if(self.location && +self.location.port > 1024){
|
if(self.location && +self.location.port > 1024){
|
||||||
@ -67,4 +47,9 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
|
|||||||
delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */;
|
delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */;
|
||||||
//console.warn("Module.sqlite3 =",Module.sqlite3);
|
//console.warn("Module.sqlite3 =",Module.sqlite3);
|
||||||
Module.sqlite3 = sqlite3 /* Currently needed by test code and sqlite3-worker1.js */;
|
Module.sqlite3 = sqlite3 /* Currently needed by test code and sqlite3-worker1.js */;
|
||||||
|
}else{
|
||||||
|
console.warn("This is not running in an Emscripten module context, so",
|
||||||
|
"self.sqlite3ApiBootstrap() is _not_ being called due to lack",
|
||||||
|
"of config info for the WASM environment.",
|
||||||
|
"It must be called manually.");
|
||||||
}
|
}
|
||||||
|
@ -93,16 +93,16 @@
|
|||||||
|
|
||||||
The config object properties include:
|
The config object properties include:
|
||||||
|
|
||||||
- `Module`: Emscripten-style module object. Currently only required
|
- `Module`[^1]: Emscripten-style module object. Currently only required
|
||||||
by certain test code and is _not_ part of the public interface.
|
by certain test code and is _not_ part of the public interface.
|
||||||
(TODO: rename this to EmscriptenModule to be more explicit.)
|
(TODO: rename this to EmscriptenModule to be more explicit.)
|
||||||
|
|
||||||
- `exports`: the "exports" object for the current WASM
|
- `exports`[^1]: the "exports" object for the current WASM
|
||||||
environment. In an Emscripten build, this should be set to
|
environment. In an Emscripten build, this should be set to
|
||||||
`Module['asm']`.
|
`Module['asm']`.
|
||||||
|
|
||||||
- `memory`: optional WebAssembly.Memory object, defaulting to
|
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
|
||||||
`exports.memory`. In Emscripten environments this should be set
|
`exports.memory`. In Emscripten environments this should be set
|
||||||
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
|
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
|
||||||
left undefined/falsy to default to `exports.memory` when using
|
left undefined/falsy to default to `exports.memory` when using
|
||||||
WASM-exported memory.
|
WASM-exported memory.
|
||||||
@ -120,20 +120,26 @@
|
|||||||
the `free(3)`-compatible routine for the WASM
|
the `free(3)`-compatible routine for the WASM
|
||||||
environment. Defaults to `"free"`.
|
environment. Defaults to `"free"`.
|
||||||
|
|
||||||
- `persistentDirName`: if the environment supports persistent storage, this
|
- `persistentDirName`[^1]: if the environment supports persistent storage, this
|
||||||
directory names the "mount point" for that directory. It must be prefixed
|
directory names the "mount point" for that directory. It must be prefixed
|
||||||
by `/` and may currently contain only a single directory-name part. Using
|
by `/` and may currently contain only a single directory-name part. Using
|
||||||
the root directory name is not supported by any current persistent backend.
|
the root directory name is not supported by any current persistent backend.
|
||||||
|
|
||||||
|
|
||||||
|
[^1] = This property may optionally be a function, in which case this
|
||||||
|
function re-assigns it to the value returned from that function,
|
||||||
|
enabling delayed evaluation.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
|
'use strict';
|
||||||
'use strict';
|
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||||
|
apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig)
|
||||||
|
){
|
||||||
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
|
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
|
||||||
console.warn("sqlite3ApiBootstrap() called multiple times.",
|
console.warn("sqlite3ApiBootstrap() called multiple times.",
|
||||||
"Config and external initializers are ignored on calls after the first.");
|
"Config and external initializers are ignored on calls after the first.");
|
||||||
return sqlite3ApiBootstrap.sqlite3;
|
return sqlite3ApiBootstrap.sqlite3;
|
||||||
}
|
}
|
||||||
|
|
||||||
apiConfig = apiConfig || {};
|
apiConfig = apiConfig || {};
|
||||||
const config = Object.create(null);
|
const config = Object.create(null);
|
||||||
{
|
{
|
||||||
@ -158,6 +164,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
// If any of these config options are functions, replace them with
|
||||||
|
// the result of calling that function...
|
||||||
|
'Module', 'exports', 'memory', 'persistentDirName'
|
||||||
|
].forEach((k)=>{
|
||||||
|
if('function' === typeof config[k]){
|
||||||
|
config[k] = config[k]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/** Throws a new Error, the message of which is the concatenation
|
/** Throws a new Error, the message of which is the concatenation
|
||||||
all args with a space between each. */
|
all args with a space between each. */
|
||||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||||
@ -750,4 +766,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
|
|||||||
this array is deleted.
|
this array is deleted.
|
||||||
*/
|
*/
|
||||||
self.sqlite3ApiBootstrap.initializers = [];
|
self.sqlite3ApiBootstrap.initializers = [];
|
||||||
self.sqlite3ApiBootstrap.sqlite3 = undefined /* installed at first call */;
|
/**
|
||||||
|
Client code may assign sqlite3ApiBootstrap.defaultConfig an
|
||||||
|
object-type value before calling sqlite3ApiBootstrap() (without
|
||||||
|
arguments) in order to tell that call to use this object as its
|
||||||
|
default config value. The intention of this is to provide
|
||||||
|
downstream clients with a reasonably flexible approach for plugging in
|
||||||
|
an environment-suitable configuration without having to define a new
|
||||||
|
global-scope symbol.
|
||||||
|
*/
|
||||||
|
self.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
|
||||||
|
/** Placeholder: gets installed by the first call to
|
||||||
|
self.sqlite3ApiBootstrap(). */
|
||||||
|
self.sqlite3ApiBootstrap.sqlite3 = undefined;
|
||||||
|
@ -92,11 +92,8 @@ sqlite3.initWorker1API = function(){
|
|||||||
defaultDb: undefined,
|
defaultDb: undefined,
|
||||||
idSeq: 0,
|
idSeq: 0,
|
||||||
idMap: new WeakMap,
|
idMap: new WeakMap,
|
||||||
open: function(arg){
|
open: function(opt){
|
||||||
// TODO? if arg is a filename, look for a db in this.dbs with the
|
const db = new DB(opt.filename);
|
||||||
// same filename and close/reopen it (or just pass it back as is?).
|
|
||||||
if(!arg && this.defaultDb) return this.defaultDb;
|
|
||||||
const db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg));
|
|
||||||
this.dbs[getDbId(db)] = db;
|
this.dbs[getDbId(db)] = db;
|
||||||
if(!this.defaultDb) this.defaultDb = db;
|
if(!this.defaultDb) this.defaultDb = db;
|
||||||
return db;
|
return db;
|
||||||
@ -169,14 +166,26 @@ sqlite3.initWorker1API = function(){
|
|||||||
envelope to other calls in this API to tell them which
|
envelope to other calls in this API to tell them which
|
||||||
db to use. If it is not provided to future calls, they
|
db to use. If it is not provided to future calls, they
|
||||||
will default to operating on the first-opened db.
|
will default to operating on the first-opened db.
|
||||||
|
|
||||||
|
persistent: prepend sqlite3.capi.sqlite3_web_persistent_dir()
|
||||||
|
to the given filename so that it is stored
|
||||||
|
in persistent storage _if_ the environment supports it.
|
||||||
|
If persistent storage is not supported, the filename
|
||||||
|
is used as-is.
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
open: function(ev){
|
open: function(ev){
|
||||||
const oargs = [], args = (ev.args || {});
|
const oargs = Object.create(null), args = (ev.args || Object.create(null));
|
||||||
if(args.simulateError){ // undocumented internal testing option
|
if(args.simulateError){ // undocumented internal testing option
|
||||||
toss("Throwing because of simulateError flag.");
|
toss("Throwing because of simulateError flag.");
|
||||||
}
|
}
|
||||||
if(args.filename) oargs.push(args.filename);
|
if(args.persistent && args.filename){
|
||||||
|
oargs.filaname = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename;
|
||||||
|
}else if('' === args.filename){
|
||||||
|
oargs.filename = args.filename;
|
||||||
|
}else{
|
||||||
|
oargs.filename = args.filename || ':memory:';
|
||||||
|
}
|
||||||
const db = wState.open(oargs);
|
const db = wState.open(oargs);
|
||||||
return {
|
return {
|
||||||
filename: db.filename,
|
filename: db.filename,
|
||||||
@ -184,15 +193,15 @@ sqlite3.initWorker1API = function(){
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
Proxy for DB.close(). ev.args may either be a boolean or an
|
Proxy for DB.close(). ev.args may be elided or an object with
|
||||||
object with an `unlink` property. If that value is truthy then
|
an `unlink` property. If that value is truthy then the db file
|
||||||
the db file (if the db is currently open) will be unlinked from
|
(if the db is currently open) will be unlinked from the virtual
|
||||||
the virtual filesystem, else it will be kept intact. The
|
filesystem, else it will be kept intact. The result object is:
|
||||||
result object is:
|
|
||||||
|
|
||||||
{
|
{
|
||||||
filename: db filename _if_ the db is opened when this
|
filename: db filename _if_ the db is opened when this
|
||||||
is called, else the undefined value
|
is called, else the undefined value
|
||||||
|
dbId: the ID of the closed b, or undefined if none is closed
|
||||||
}
|
}
|
||||||
|
|
||||||
It does not error if the given db is already closed or no db is
|
It does not error if the given db is already closed or no db is
|
||||||
@ -356,6 +365,7 @@ sqlite3.initWorker1API = function(){
|
|||||||
dbId: DB handle ID,
|
dbId: DB handle ID,
|
||||||
[messageId: if set in the inbound message],
|
[messageId: if set in the inbound message],
|
||||||
result: {
|
result: {
|
||||||
|
operation: "inbound message's 'type' value",
|
||||||
message: error string,
|
message: error string,
|
||||||
errorClass: class name of the error type,
|
errorClass: class name of the error type,
|
||||||
input: ev.data
|
input: ev.data
|
||||||
@ -378,6 +388,7 @@ sqlite3.initWorker1API = function(){
|
|||||||
}catch(err){
|
}catch(err){
|
||||||
evType = 'error';
|
evType = 'error';
|
||||||
result = {
|
result = {
|
||||||
|
operation: ev.type,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
errorClass: err.name,
|
errorClass: err.name,
|
||||||
input: ev
|
input: ev
|
||||||
@ -405,7 +416,7 @@ sqlite3.initWorker1API = function(){
|
|||||||
result: result
|
result: result
|
||||||
}, wMsgHandler.xfer);
|
}, wMsgHandler.xfer);
|
||||||
};
|
};
|
||||||
setTimeout(()=>self.postMessage({type:'sqlite3-api',result:'worker1-ready'}), 0);
|
self.postMessage({type:'sqlite3-api',result:'worker1-ready'});
|
||||||
}.bind({self, sqlite3});
|
}.bind({self, sqlite3});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
232
ext/wasm/sqlite3-worker1-promiser.js
Normal file
232
ext/wasm/sqlite3-worker1-promiser.js
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
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 on 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 noted 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 this
|
||||||
|
function replaces the worker.onmessage property. This property
|
||||||
|
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 of work at the
|
||||||
|
earliest opportunity.
|
||||||
|
|
||||||
|
- `onerror` (optional): a callback to pass error-type events from
|
||||||
|
the worker. The object passed to it will be the error message
|
||||||
|
payload from the worker. This is _not_ the same as the
|
||||||
|
worker.onerror property!
|
||||||
|
|
||||||
|
- `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 so that dispatching can work. If not defined, a default
|
||||||
|
generator is used.
|
||||||
|
|
||||||
|
- `dbId` (optional): is the database ID to be used by the
|
||||||
|
worker. This must initially be unset or a falsy value. The
|
||||||
|
first `open` message sent to the worker will cause this config
|
||||||
|
entry to be assigned to the ID of the opened database. That ID
|
||||||
|
"should" be set as the `dbId` property of the message sent in
|
||||||
|
future requests, so that the worker uses that database.
|
||||||
|
However, if the worker is not given an explicit dbId, it will
|
||||||
|
use the first-opened database by default. If client code needs
|
||||||
|
to work with multiple database IDs, the client-level code will
|
||||||
|
need to juggle those themselves. A `close` message will clear
|
||||||
|
this property if it matches the ID of the closed db. Potential
|
||||||
|
TODO: add a config callback specifically for reporting `open`
|
||||||
|
and `close` message results, so that clients may track those
|
||||||
|
values.
|
||||||
|
|
||||||
|
- `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
|
||||||
|
config.dbId if it is _not_ set in the message object.
|
||||||
|
|
||||||
|
The function throws on error.
|
||||||
|
|
||||||
|
The function installs a temporarily 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 eventPromiser = sqlite3Worker1Promiser(config);
|
||||||
|
eventPromiser('open', {filename:"/foo.db"}).then(function(msg){
|
||||||
|
console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...}
|
||||||
|
// Recall that config.dbId will be set for the first 'open'
|
||||||
|
// call and cleared for a matching 'close' call.
|
||||||
|
});
|
||||||
|
eventPromiser({type:'close'}).then((msg)=>{
|
||||||
|
console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...}
|
||||||
|
// Recall that config.dbId will be used by default for the message's dbId if
|
||||||
|
// none is explicitly provided, and a 'close' op will clear config.dbId if it
|
||||||
|
// closes that exact 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
|
||||||
|
and once more, at the end, passed only `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.
|
||||||
|
|
||||||
|
|
||||||
|
TODO?: a config option which causes it to queue up events to fire
|
||||||
|
one at a time and flush the event queue on the first error. The
|
||||||
|
main use for this is test runs which must fail at the first error.
|
||||||
|
*/
|
||||||
|
self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
|
||||||
|
// Inspired by: https://stackoverflow.com/a/52439530
|
||||||
|
let idNumber = 0;
|
||||||
|
const handlerMap = Object.create(null);
|
||||||
|
const noop = function(){};
|
||||||
|
const err = config.onerror || noop;
|
||||||
|
const debug = config.debug || noop;
|
||||||
|
const genMsgId = config.generateMessageId || function(msg){
|
||||||
|
return msg.type+'#'+(++idNumber);
|
||||||
|
};
|
||||||
|
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||||
|
if('function'===typeof config.worker) config.worker = config.worker();
|
||||||
|
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.row);
|
||||||
|
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(!config.dbId) config.dbId = ev.dbId;
|
||||||
|
break;
|
||||||
|
case 'close':
|
||||||
|
if(config.dbId === ev.dbId) config.dbId = undefined;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
msgHandler.resolve(ev);
|
||||||
|
}/*worker.onmessage()*/;
|
||||||
|
return function(/*(msgType, msgArgs) || (msg)*/){
|
||||||
|
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 = config.dbId;
|
||||||
|
msg.messageId = genMsgId(msg);
|
||||||
|
msg.departureTime = performance.now();
|
||||||
|
const proxy = Object.create(null);
|
||||||
|
proxy.message = msg;
|
||||||
|
let cbId /* message handler ID for exec on-row callback proxy */;
|
||||||
|
if('exec'===msg.type && msg.args){
|
||||||
|
if('function'===typeof msg.args.callback){
|
||||||
|
cbId = genMsgId(msg)+':row';
|
||||||
|
proxy.onrow = msg.args.callback;
|
||||||
|
msg.args.callback = cbId;
|
||||||
|
handlerMap[cbId] = proxy;
|
||||||
|
}else if('string' === typeof msg.args.callback){
|
||||||
|
toss("exec callback may not be a string when using the Promise interface.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//debug("requestWork", msg);
|
||||||
|
const p = new Promise(function(resolve, reject){
|
||||||
|
proxy.resolve = resolve;
|
||||||
|
proxy.reject = reject;
|
||||||
|
handlerMap[msg.messageId] = proxy;
|
||||||
|
debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg);
|
||||||
|
config.worker.postMessage(msg);
|
||||||
|
});
|
||||||
|
if(cbId) p.finally(()=>delete handlerMap[cbId]);
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
}/*sqlite3Worker1Promiser()*/;
|
||||||
|
self.sqlite3Worker1Promiser.defaultConfig = {
|
||||||
|
worker: ()=>new Worker('sqlite3-worker1.js'),
|
||||||
|
onerror: console.error.bind(console),
|
||||||
|
dbId: undefined
|
||||||
|
};
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
2022-08-23
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
***********************************************************************
|
|
||||||
|
|
||||||
|
|
||||||
UNDER CONSTRUCTION: a Promise-based proxy for for the sqlite3 Worker
|
|
||||||
#1 API.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
(function(){
|
|
||||||
const T = self.SqliteTestUtil;
|
|
||||||
const DbState = {
|
|
||||||
id: undefined
|
|
||||||
};
|
|
||||||
const eOutput = document.querySelector('#test-output');
|
|
||||||
const log = console.log.bind(console);
|
|
||||||
const logHtml = async function(cssClass,...args){
|
|
||||||
log.apply(this, args);
|
|
||||||
const ln = document.createElement('div');
|
|
||||||
if(cssClass) ln.classList.add(cssClass);
|
|
||||||
ln.append(document.createTextNode(args.join(' ')));
|
|
||||||
eOutput.append(ln);
|
|
||||||
};
|
|
||||||
const warn = console.warn.bind(console);
|
|
||||||
const error = console.error.bind(console);
|
|
||||||
|
|
||||||
let startTime;
|
|
||||||
const logEventResult = async function(evd){
|
|
||||||
logHtml(evd.errorClass ? 'error' : '',
|
|
||||||
"response to",evd.messageId,"Worker time =",
|
|
||||||
(evd.workerRespondTime - evd.workerReceivedTime),"ms.",
|
|
||||||
"Round-trip event time =",
|
|
||||||
(performance.now() - evd.departureTime),"ms.",
|
|
||||||
(evd.errorClass ? evd.message : "")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const testCount = async ()=>{
|
|
||||||
logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inspiration: https://stackoverflow.com/a/52439530
|
|
||||||
const worker = new Worker("sqlite3-worker1.js");
|
|
||||||
worker.onerror = function(event){
|
|
||||||
error("worker.onerror",event);
|
|
||||||
};
|
|
||||||
const WorkerPromiseHandler = Object.create(null);
|
|
||||||
WorkerPromiseHandler.nextId = function f(){
|
|
||||||
return 'msg#'+(f._ = (f._ || 0) + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Posts a worker message as {type:eventType, data:eventData}. */
|
|
||||||
const requestWork = async function(eventType, eventData){
|
|
||||||
//log("requestWork", eventType, eventData);
|
|
||||||
T.assert(eventData && 'object'===typeof eventData);
|
|
||||||
/* ^^^ that is for the testing and messageId-related code, not
|
|
||||||
a hard requirement of all of the Worker-exposed APIs. */
|
|
||||||
const wph = WorkerPromiseHandler;
|
|
||||||
const msgId = wph.nextId();
|
|
||||||
const proxy = wph[msgId] = Object.create(null);
|
|
||||||
proxy.promise = new Promise(function(resolve, reject){
|
|
||||||
proxy.resolve = resolve;
|
|
||||||
proxy.reject = reject;
|
|
||||||
const msg = {
|
|
||||||
type: eventType,
|
|
||||||
args: eventData,
|
|
||||||
dbId: DbState.id,
|
|
||||||
messageId: msgId,
|
|
||||||
departureTime: performance.now()
|
|
||||||
};
|
|
||||||
log("Posting",eventType,"message to worker dbId="+(DbState.id||'default')+':',msg);
|
|
||||||
worker.postMessage(msg);
|
|
||||||
});
|
|
||||||
log("Set up promise",proxy);
|
|
||||||
return proxy.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const runOneTest = async function(eventType, eventData, callback){
|
|
||||||
T.assert(eventData && 'object'===typeof eventData);
|
|
||||||
/* ^^^ that is for the testing and messageId-related code, not
|
|
||||||
a hard requirement of all of the Worker-exposed APIs. */
|
|
||||||
let p = requestWork(eventType, eventData);
|
|
||||||
if(callback) p.then(callback).finally(testCount);
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
const runTests = async function(){
|
|
||||||
logHtml('',
|
|
||||||
"Sending 'open' message and waiting for its response before continuing.");
|
|
||||||
startTime = performance.now();
|
|
||||||
runOneTest('open', {
|
|
||||||
filename:'testing2.sqlite3',
|
|
||||||
simulateError: 0 /* if true, fail the 'open' */
|
|
||||||
}, function(ev){
|
|
||||||
log("then open result",ev);
|
|
||||||
T.assert('testing2.sqlite3'===ev.result.filename)
|
|
||||||
.assert(ev.dbId)
|
|
||||||
.assert(ev.messageId)
|
|
||||||
.assert(DbState.id === ev.dbId);
|
|
||||||
}).catch((err)=>error("error response:",err));
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.onmessage = function(ev){
|
|
||||||
ev = ev.data;
|
|
||||||
(('error'===ev.type) ? error : log)('worker.onmessage',ev);
|
|
||||||
const msgHandler = WorkerPromiseHandler[ev.messageId];
|
|
||||||
if(!msgHandler){
|
|
||||||
if('worker1-ready'===ev.result) {
|
|
||||||
/*sqlite3-api/worker1-ready is fired when the Worker1 API initializes*/
|
|
||||||
self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
|
|
||||||
runTests();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error("Unhandled worker message:",ev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logEventResult(ev);
|
|
||||||
delete WorkerPromiseHandler[ev.messageId];
|
|
||||||
if('error'===ev.type){
|
|
||||||
msgHandler.reject(ev);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if(!DbState.id && ev.dbId) DbState.id = ev.dbId;
|
|
||||||
msgHandler.resolve(ev); // async, so testCount() results on next line are out of order
|
|
||||||
//testCount();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
log("Init complete, but async init bits may still be running.");
|
|
||||||
})();
|
|
@ -28,6 +28,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div id='test-output'></div>
|
<div id='test-output'></div>
|
||||||
<script src="common/SqliteTestUtil.js"></script>
|
<script src="common/SqliteTestUtil.js"></script>
|
||||||
<script src="testing-worker-promise.js"></script>
|
<script src="sqlite3-worker1-promiser.js"></script>
|
||||||
|
<script src="testing-worker1-promiser.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
214
ext/wasm/testing-worker1-promiser.js
Normal file
214
ext/wasm/testing-worker1-promiser.js
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
2022-08-23
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
***********************************************************************
|
||||||
|
|
||||||
|
Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based
|
||||||
|
proxy for for the sqlite3 Worker #1 API.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
(function(){
|
||||||
|
const T = self.SqliteTestUtil;
|
||||||
|
const eOutput = document.querySelector('#test-output');
|
||||||
|
const warn = console.warn.bind(console);
|
||||||
|
const error = console.error.bind(console);
|
||||||
|
const log = console.log.bind(console);
|
||||||
|
const logHtml = async function(cssClass,...args){
|
||||||
|
log.apply(this, args);
|
||||||
|
const ln = document.createElement('div');
|
||||||
|
if(cssClass) ln.classList.add(cssClass);
|
||||||
|
ln.append(document.createTextNode(args.join(' ')));
|
||||||
|
eOutput.append(ln);
|
||||||
|
};
|
||||||
|
|
||||||
|
let startTime;
|
||||||
|
const logEventResult = async function(evd){
|
||||||
|
logHtml(evd.errorClass ? 'error' : '',
|
||||||
|
"response to",evd.messageId,"Worker time =",
|
||||||
|
(evd.workerRespondTime - evd.workerReceivedTime),"ms.",
|
||||||
|
"Round-trip event time =",
|
||||||
|
(performance.now() - evd.departureTime),"ms.",
|
||||||
|
(evd.errorClass ? evd.message : "")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const testCount = async ()=>{
|
||||||
|
logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
|
||||||
|
};
|
||||||
|
|
||||||
|
//why is this triggered even when we catch() a Promise?
|
||||||
|
//window.addEventListener('unhandledrejection', function(event) {
|
||||||
|
// warn('unhandledrejection',event);
|
||||||
|
//});
|
||||||
|
|
||||||
|
const promiserConfig = {
|
||||||
|
worker: ()=>{
|
||||||
|
const w = new Worker("sqlite3-worker1.js");
|
||||||
|
w.onerror = (event)=>error("worker.onerror",event);
|
||||||
|
return w;
|
||||||
|
},
|
||||||
|
//debug: (...args)=>console.debug('worker debug',...args),
|
||||||
|
onunhandled: function(ev){
|
||||||
|
error("Unhandled worker message:",ev.data);
|
||||||
|
},
|
||||||
|
onready: function(){
|
||||||
|
self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
|
||||||
|
runTests();
|
||||||
|
},
|
||||||
|
onerror: function(ev){
|
||||||
|
error("worker1 error:",ev);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const workerPromise = self.sqlite3Worker1Promiser(promiserConfig);
|
||||||
|
delete self.sqlite3Worker1Promiser;
|
||||||
|
|
||||||
|
const wtest = async function(msgType, msgArgs, callback){
|
||||||
|
let p = workerPromise({type: msgType, args:msgArgs});
|
||||||
|
if(callback) p.then(callback).finally(testCount);
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
const runTests = async function(){
|
||||||
|
logHtml('',
|
||||||
|
"Sending 'open' message and waiting for its response before continuing.");
|
||||||
|
startTime = performance.now();
|
||||||
|
wtest('open', {
|
||||||
|
filename:'testing2.sqlite3',
|
||||||
|
simulateError: 0 /* if true, fail the 'open' */
|
||||||
|
}, function(ev){
|
||||||
|
log("then open result",ev);
|
||||||
|
T.assert('testing2.sqlite3'===ev.result.filename)
|
||||||
|
.assert(ev.dbId)
|
||||||
|
.assert(ev.messageId)
|
||||||
|
.assert(promiserConfig.dbId === ev.dbId);
|
||||||
|
}).then(runTests2)
|
||||||
|
.catch((err)=>error("error response:",err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const runTests2 = async function(){
|
||||||
|
const mustNotReach = ()=>toss("This is not supposed to be reached.");
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
sql: ["create table t(a,b)",
|
||||||
|
"insert into t(a,b) values(1,2),(3,4),(5,6)"
|
||||||
|
].join(';'),
|
||||||
|
multi: true,
|
||||||
|
resultRows: [], columnNames: []
|
||||||
|
}, function(ev){
|
||||||
|
ev = ev.result;
|
||||||
|
T.assert(0===ev.resultRows.length)
|
||||||
|
.assert(0===ev.columnNames.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
sql: 'select a a, b b from t order by a',
|
||||||
|
resultRows: [], columnNames: [],
|
||||||
|
}, function(ev){
|
||||||
|
ev = ev.result;
|
||||||
|
T.assert(3===ev.resultRows.length)
|
||||||
|
.assert(1===ev.resultRows[0][0])
|
||||||
|
.assert(6===ev.resultRows[2][1])
|
||||||
|
.assert(2===ev.columnNames.length)
|
||||||
|
.assert('b'===ev.columnNames[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
sql: 'select a a, b b from t order by a',
|
||||||
|
resultRows: [], columnNames: [],
|
||||||
|
rowMode: 'object'
|
||||||
|
}, function(ev){
|
||||||
|
ev = ev.result;
|
||||||
|
T.assert(3===ev.resultRows.length)
|
||||||
|
.assert(1===ev.resultRows[0].a)
|
||||||
|
.assert(6===ev.resultRows[2].b)
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest(
|
||||||
|
'exec',
|
||||||
|
{sql:'intentional_error'},
|
||||||
|
mustNotReach
|
||||||
|
).catch((e)=>{
|
||||||
|
warn("Intentional error:",e);
|
||||||
|
// Why does the browser report console.error "Uncaught (in
|
||||||
|
// promise)" when we catch(), and does so _twice_ if we don't
|
||||||
|
// catch()? According to all docs, that error must be supressed
|
||||||
|
// if we explicitly catch().
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
sql:'select 1 union all select 3',
|
||||||
|
resultRows: [],
|
||||||
|
//rowMode: 'array', // array is the default in the Worker interface
|
||||||
|
}, function(ev){
|
||||||
|
ev = ev.result;
|
||||||
|
T.assert(2 === ev.resultRows.length)
|
||||||
|
.assert(1 === ev.resultRows[0][0])
|
||||||
|
.assert(3 === ev.resultRows[1][0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultRowTest1 = function f(row){
|
||||||
|
if(undefined === f.counter) f.counter = 0;
|
||||||
|
if(row) ++f.counter;
|
||||||
|
//log("exec() result row:",row);
|
||||||
|
T.assert(null===row || 'number' === typeof row.b);
|
||||||
|
};
|
||||||
|
await wtest('exec',{
|
||||||
|
sql: 'select a a, b b from t order by a',
|
||||||
|
callback: resultRowTest1,
|
||||||
|
rowMode: 'object'
|
||||||
|
}, function(ev){
|
||||||
|
T.assert(3===resultRowTest1.counter);
|
||||||
|
resultRowTest1.counter = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
multi: true,
|
||||||
|
sql:[
|
||||||
|
'pragma foreign_keys=0;',
|
||||||
|
// ^^^ arbitrary query with no result columns
|
||||||
|
'select a, b from t order by a desc; select a from t;'
|
||||||
|
// multi-exec only honors results from the first
|
||||||
|
// statement with result columns (regardless of whether)
|
||||||
|
// it has any rows).
|
||||||
|
],
|
||||||
|
rowMode: 1,
|
||||||
|
resultRows: []
|
||||||
|
},function(ev){
|
||||||
|
const rows = ev.result.resultRows;
|
||||||
|
T.assert(3===rows.length).
|
||||||
|
assert(6===rows[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('exec',{sql: 'delete from t where a>3'});
|
||||||
|
|
||||||
|
await wtest('exec',{
|
||||||
|
sql: 'select count(a) from t',
|
||||||
|
resultRows: []
|
||||||
|
},function(ev){
|
||||||
|
ev = ev.result;
|
||||||
|
T.assert(1===ev.resultRows.length)
|
||||||
|
.assert(2===ev.resultRows[0][0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
/***** close() tests must come last. *****/
|
||||||
|
await wtest('close',{unlink:true},function(ev){
|
||||||
|
T.assert(!promiserConfig.dbId);
|
||||||
|
T.assert('string' === typeof ev.result.filename);
|
||||||
|
});
|
||||||
|
|
||||||
|
await wtest('close').then((ev)=>{
|
||||||
|
T.assert(undefined === ev.result.filename);
|
||||||
|
log("That's all, folks!");
|
||||||
|
});
|
||||||
|
}/*runTests2()*/;
|
||||||
|
|
||||||
|
|
||||||
|
log("Init complete, but async init bits may still be running.");
|
||||||
|
})();
|
21
manifest
21
manifest
@ -1,5 +1,5 @@
|
|||||||
C The\svery\sbasics\sof\sa\sPromise-based\sproxy\sfor\sthe\sWorker\s#1\sAPI.\sStill\srequires\sconsiderable\scleanup,\stesting,\sand\sa\ssolution\sfor\sthe\sexec-callback-via-event-type-name\sproblem.
|
C More\swork\son\show\sto\sconfigure\sthe\ssqlite3\sJS\sAPI\sbootstrapping\sprocess\sfrom\shigher-level\scode.\sInitial\sversion\sof\ssqlite3-worker1-promiser,\sa\sPromise-based\sproxy\sfor\sthe\sWorker\sAPI\s#1.
|
||||||
D 2022-08-24T00:51:39.887
|
D 2022-08-24T05:59:23.851
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -481,12 +481,12 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de
|
|||||||
F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b
|
F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b
|
||||||
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
|
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
|
||||||
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
|
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
|
||||||
F ext/wasm/api/sqlite3-api-cleanup.js acf798ce96285c0d52738466a96c9deb9d66647f711a40caecab90b5ce66ac3c
|
F ext/wasm/api/sqlite3-api-cleanup.js 4c353bdc2452623f0c1c1e55ae1a0589db9cbaed9756760bb15179ef9b58bc98
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3
|
F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c
|
F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c
|
||||||
F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
|
F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 6e0e7787ed955ea2b6158e0bb7608f63b54236847700d183e49e1f10d0525b8f
|
F ext/wasm/api/sqlite3-api-prologue.js 4a279604272851696975837534739597206c0800c8ea78810fe8e211ee101374
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js c9e4edb89f41a4fa65d136ae180c1bc0beb694eb95f7d9e6936fbb702914c160
|
F ext/wasm/api/sqlite3-api-worker1.js 9691e144a77490f482caa2c0f0bd38a8f955c6dc9c10b2f39c6491e817aefd8c
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
|
F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
|
||||||
F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
|
F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
|
||||||
@ -508,9 +508,10 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43
|
|||||||
F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
|
F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
|
||||||
F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9
|
F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9
|
||||||
F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0
|
F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0
|
||||||
|
F ext/wasm/sqlite3-worker1-promiser.js 291f89330bc856e7ef8a321b4891554633c6407b52efc69c9b1d1b3e7c69d4a6
|
||||||
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
||||||
F ext/wasm/testing-worker-promise.html ba3d5423cfbdc96c332af3632dfcb61527ba8fd7e487b3bf3f07542f890c3e08
|
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 w ext/wasm/testing-worker-promise.html
|
||||||
F ext/wasm/testing-worker-promise.js c05c46a3a22b1910f6a1db11f3da6df701259eaa1277ddba085247b7f9059423
|
F ext/wasm/testing-worker1-promiser.js 3c13fda53cc8b5d148ae34f621eba99aff393d66718b216bfd9d3f9075dd83bc w ext/wasm/testing-worker-promise.js
|
||||||
F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
|
F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
|
||||||
F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
|
F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
|
||||||
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
|
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
|
||||||
@ -2008,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121
|
P 1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda
|
||||||
R 5b16a785d8c414a8eb9d618c8d9d8cea
|
R 20d6ad983c84af7c7f2e33fe283b134d
|
||||||
U stephan
|
U stephan
|
||||||
Z fdaa5cf9f1bfd4215c6ebf07223819c1
|
Z 996279c2387a16066b296229c9c99a7d
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda
|
b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878
|
Loading…
Reference in New Issue
Block a user