]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Expand the worker1 'exec' op handling for per-row callbacks for API-level consistency...
authorstephan <stephan@noemail.net>
Wed, 24 Aug 2022 18:39:46 +0000 (18:39 +0000)
committerstephan <stephan@noemail.net>
Wed, 24 Aug 2022 18:39:46 +0000 (18:39 +0000)
FossilOrigin-Name: 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7

ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-worker1.js
ext/wasm/sqlite3-worker1-promiser.js
ext/wasm/testing-worker1-promiser.js
ext/wasm/testing2.js
manifest
manifest.uuid

index be9d8af5a83648789d2baab770c5057fc68595cc..e16b45bb5eb775fa9f0598369f057b6ead8b3bfd 100644 (file)
@@ -252,6 +252,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
               out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
               break;
             }
+            /*
+              TODO?: how can we define rowMode such that it uses
+              rowMode of 'object' and returns a given named field from
+              the object. Something like:
+
+              if(?what goes here?){
+                out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]}
+                  .bind({obj:{}, colName: ???what goes here???}});
+                break;
+              }
+
+              Maybe rowMode:['colName1',... 'colNameN']? That could be
+              ambiguous: might mean "return an object with just these
+              columns".
+            */
             toss3("Invalid rowMode:",out.opt.rowMode);
       }
     }
index c63ab1117e720185e507e95a14a591740caba878..afb2e7812472fe69112564886180aef32d982dfa 100644 (file)
   initialization is complete, as the initialization is synchronous.
   In some contexts, however, listening for the above message is
   a better fit.
+
+  Note that the worker-based interface can be slightly quirky because
+  of its async nature. In particular, any number of messages may be posted
+  to the worker before it starts handling any of them. If, e.g., an
+  "open" operation fails, any subsequent messages will fail. The
+  Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`)
+  is more comfortable to use in that regard.
+
+
+  TODO: hoist the message API docs from deep in this code to here.
+
 */
 self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
 sqlite3.initWorker1API = function(){
   'use strict';
-  /**
-     UNDER CONSTRUCTION
-
-     We need an API which can proxy the DB API via a Worker message
-     interface. The primary quirky factor in such an API is that we
-     cannot pass callback functions between the window thread and a
-     worker thread, so we have to receive all db results via
-     asynchronous message-passing. That requires an asychronous API
-     with a distinctly different shape than OO API #1.
-
-     TODOs include, but are not necessarily limited to:
-
-     - Support for handling multiple DBs via this interface is under
-     development.
-  */
   const toss = (...args)=>{throw new Error(args.join(' '))};
   if('function' !== typeof importScripts){
     toss("Cannot initalize the sqlite3 worker API in the main thread.");
@@ -86,12 +82,13 @@ sqlite3.initWorker1API = function(){
   };
 
   /**
-     Helper for managing Worker-level state.
+     Internal helper for managing Worker-level state.
   */
   const wState = {
     defaultDb: undefined,
     idSeq: 0,
     idMap: new WeakMap,
+    xfer: [/*Temp holder for "transferable" postMessage() state.*/],
     open: function(opt){
       const db = new DB(opt.filename);
       this.dbs[getDbId(db)] = db;
@@ -109,9 +106,15 @@ sqlite3.initWorker1API = function(){
         }
       }
     },
+    /**
+       Posts the given worker message value. If xferList is provided,
+       it must be an array, in which case a copy of it passed as
+       postMessage()'s second argument and xferList.length is set to
+       0.
+    */
     post: function(msg,xferList){
-      if(xferList){
-        self.postMessage( msg, xferList );
+      if(xferList && xferList.length){
+        self.postMessage( msg, Array.from(xferList) );
         xferList.length = 0;
       }else{
         self.postMessage(msg);
@@ -146,32 +149,40 @@ sqlite3.initWorker1API = function(){
      message type key. The onmessage() dispatcher attempts to
      dispatch all inbound messages to a method of this object,
      passing it the event.data part of the inbound event object. All
-     methods must return a plain Object containing any response
+     methods must return a plain Object containing any result
      state, which the dispatcher may amend. All methods must throw
      on error.
   */
   const wMsgHandler = {
-    xfer: [/*Temp holder for "transferable" postMessage() state.*/],
     /**
        Proxy for the DB constructor. Expects to be passed a single
-       object or a falsy value to use defaults. The object may have a
-       filename property to name the db file (see the DB constructor
-       for peculiarities and transformations). The response is an
-       object:
+       object or a falsy value to use defaults:
 
        {
-         filename: db filename (possibly differing from the input),
+         filename [=":memory:" or "" (unspecified)]: the db filename.
+         See the sqlite3.oo1.DB constructor for peculiarities and transformations,
+
+         persistent [=false]: if true and filename is not one of ("",
+         ":memory:"), 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.
+       }
+
+       The response object looks like:
+
+       {
+         filename: db filename, possibly differing from the input.
 
          dbId: an opaque ID value which must be passed in the message
-               envelope to other calls in this API to tell them which
-               db to use. If it is not provided to future calls, they
-               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.
+         envelope to other calls in this API to tell them which db to
+         use. If it is not provided to future calls, they will default
+         to operating on the first-opened db.
+
+         persistent: true if the given filename resides in the
+         known-persistent storage, else false. This determination is
+         independent of the `persistent` input argument.
        }
     */
     open: function(ev){
@@ -179,28 +190,32 @@ sqlite3.initWorker1API = function(){
       if(args.simulateError){ // undocumented internal testing option
         toss("Throwing because of simulateError flag.");
       }
-      if(args.persistent && args.filename){
-        oargs.filename = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename;
-      }else if('' === args.filename){
-        oargs.filename = args.filename;
+      const rc = Object.create(null);
+      const pDir = sqlite3.capi.sqlite3_web_persistent_dir();
+      if(!args.filename || ':memory:'===args.filename){
+        oargs.filename = args.filename || '';
+      }else if(pDir){
+        oargs.filename = pDir + ('/'===args.filename[0] ? args.filename : ('/'+args.filename));
       }else{
-        oargs.filename = args.filename || ':memory:';
-      }      
+        oargs.filename = args.filename;
+      }
       const db = wState.open(oargs);
-      return {
-        filename: db.filename,
-        dbId: getDbId(db)
-      };
+      rc.filename = db.filename;
+      rc.persistent = !!pDir && db.filename.startsWith(pDir);
+      rc.dbId = getDbId(db);
+      return rc;
     },
     /**
        Proxy for DB.close(). ev.args may be elided or an object with
        an `unlink` property. If that value is truthy then the db file
        (if the db is currently open) will be unlinked from the virtual
-       filesystem, else it will be kept intact. The result object is:
+       filesystem, else it will be kept intact, noting that unlink
+       failure is ignored. The result object is:
 
        {
          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
        }
 
@@ -211,7 +226,7 @@ sqlite3.initWorker1API = function(){
       const db = getMsgDb(ev,false);
       const response = {
         filename: db && db.filename,
-        dbId: db ? getDbId(db) : undefined
+        dbId: db && getDbId(db)
       };
       if(db){
         wState.close(db, ((ev.args && 'object'===typeof ev.args)
@@ -220,7 +235,7 @@ sqlite3.initWorker1API = function(){
       return response;
     },
     /**
-       Proxy for DB.exec() which expects a single argument of type
+       Proxy for oo1.DB.exec() which expects a single argument of type
        string (SQL to execute) or an options object in the form
        expected by exec(). The notable differences from exec()
        include:
@@ -234,12 +249,21 @@ sqlite3.initWorker1API = function(){
        message type key, in which case a callback function will be
        applied which posts each row result via:
 
-       postMessage({type: thatKeyType, row: theRow})
+       postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow})
 
-       And, at the end of the result set (whether or not any
-       result rows were produced), it will post an identical
-       message with row:null to alert the caller than the result
-       set is completed.
+       And, at the end of the result set (whether or not any result
+       rows were produced), it will post an identical message with
+       (row=undefined, rowNumber=null) to alert the caller than the
+       result set is completed. Note that a row value of `null` is
+       a legal row result for certain `rowMode` values.
+
+       (Design note: we don't use (row=undefined, rowNumber=undefined)
+       to indicate end-of-results because fetching those would be
+       indistinguishable from fetching from an empty object unless the
+       client used hasOwnProperty() (or similar) to distinguish
+       "missing property" from "property with the undefined value".
+       Similarly, `null` is a legal value for `row` in some case ,
+       whereas the db layer won't emit a result value of `undefined`.)
 
        The callback proxy must not recurse into this interface, or
        results are undefined. (It hypothetically cannot recurse
@@ -250,52 +274,73 @@ sqlite3.initWorker1API = function(){
        The response is the input options object (or a synthesized
        one if passed only a string), noting that
        options.resultRows and options.columnNames may be populated
-       by the call to exec().
-
-       This opens/creates the Worker's db if needed.
+       by the call to db.exec().
     */
     exec: function(ev){
-      const opt = (
+      const rc = (
         'string'===typeof ev.args
       ) ? {sql: ev.args} : (ev.args || Object.create(null));
-      if(undefined===opt.rowMode){
+      if(undefined===rc.rowMode){
         /* Since the default rowMode of 'stmt' is not useful
            for the Worker interface, we'll default to
            something else. */
-        opt.rowMode = 'array';
-      }else if('stmt'===opt.rowMode){
-        toss("Invalid rowMode for exec(): stmt mode",
+        rc.rowMode = 'array';
+      }else if('stmt'===rc.rowMode){
+        toss("Invalid rowMode for 'exec': stmt mode",
              "does not work in the Worker API.");
       }
       const db = getMsgDb(ev);
-      if(opt.callback || Array.isArray(opt.resultRows)){
+      if(rc.callback || Array.isArray(rc.resultRows)){
         // Part of a copy-avoidance optimization for blobs
-        db._blobXfer = this.xfer;
+        db._blobXfer = wState.xfer;
       }
-      const callbackMsgType = opt.callback;
+      const callbackMsgType = rc.callback;
+      let rowNumber = 0;
       if('string' === typeof callbackMsgType){
         /* Treat this as a worker message type and post each
            row as a message of that type. */
-        const that = this;
-        opt.callback =
-          (row)=>wState.post({type: callbackMsgType, row:row}, this.xfer);
+        rc.callback =
+          (row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer);
       }
       try {
-        db.exec(opt);
-        if(opt.callback instanceof Function){
-          opt.callback = callbackMsgType;
-          wState.post({type: callbackMsgType, row: null});
+        db.exec(rc);
+        if(rc.callback instanceof Function){
+          rc.callback = callbackMsgType;
+          wState.post({type: callbackMsgType, rowNumber: null, row: undefined});
+        }
+      }finally{
+        delete db._blobXfer;
+        if(rc.callback){
+          rc.callback = callbackMsgType;
         }
-      }/*catch(e){
-         console.warn("Worker is propagating:",e);throw e;
-         }*/finally{
-           delete db._blobXfer;
-           if(opt.callback){
-             opt.callback = callbackMsgType;
-           }
-         }
-      return opt;
+      }
+      return rc;
     }/*exec()*/,
+    /**
+       Returns a JSON-friendly form of a _subset_ of sqlite3.config,
+       sans any parts which cannot be serialized. Because we cannot,
+       from here, distingush whether or not certain objects can be
+       serialized, this routine selectively copies certain properties
+       rather than trying JSON.stringify() and seeing what happens
+       (the results are horrid if the config object contains an
+       Emscripten module object).
+
+       In addition to the "real" config properties, it sythesizes
+       the following:
+
+       - persistenceEnabled: true if persistent dir support is available,
+       else false.
+    */
+    'config-get': function(){
+      const rc = Object.create(null), src = sqlite3.config;
+      [
+        'persistentDirName', 'bigIntEnabled'
+      ].forEach(function(k){
+        if(Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k];
+      });
+      rc.persistenceEnabled = !!sqlite3.capi.sqlite3_web_persistent_dir();
+      return rc;
+    },
     /**
        TO(RE)DO, once we can abstract away access to the
        JS environment's virtual filesystem. Currently this
@@ -329,7 +374,7 @@ sqlite3.initWorker1API = function(){
         filename: db.filename,
         mimetype: 'application/x-sqlite3'
       };
-      this.xfer.push(response.buffer.buffer);
+      wState.xfer.push(response.buffer.buffer);
       return response;**/
     }/*export()*/,
     toss: function(ev){
@@ -344,22 +389,32 @@ sqlite3.initWorker1API = function(){
      form:
 
      { type: apiCommand,
-       dbId: optional DB ID value (else uses a default db handle),
        args: apiArguments,
+       dbId: optional DB ID value (else uses a default db handle),
        messageId: optional client-specific value
      }
 
      As a rule, these commands respond with a postMessage() of their
-     own in the form:
+     own. The responses always have a `type` property equal to the
+     input message's type and an object-format `result` part. If
+     the inbound object has a `messageId` property, that property is
+     always mirrored in the result object, for use in client-side
+     dispatching of these asynchronous results. For example:
 
-     TODO: refactoring is underway.
+     {
+       type: 'open',
+       messageId: ...copied from inbound message...,
+       dbId: ID of db which was opened,
+       result: {
+         dbId: repeat of ^^^, for API consistency's sake,
+         filename: ...,
+         persistent: false
+       },
+       ...possibly other framework-internal/testing/debugging info...
+     }
 
-     The responses always have an object-format `result` part. If the
-     inbound object has a `messageId` property, that property is
-     always mirrored in the result object, for use in client-side
-     dispatching of these asynchronous results. Exceptions thrown
-     during processing result in an `error`-type event with a payload
-     in the form:
+     Exceptions thrown during processing result in an `error`-type
+     event with a payload in the form:
 
      { type: 'error',
        dbId: DB handle ID,
@@ -413,10 +468,15 @@ sqlite3.initWorker1API = function(){
       workerReceivedTime: arrivalTime,
       workerRespondTime: performance.now(),
       departureTime: ev.departureTime,
+      // TODO: move the timing bits into...
+      //timing:{
+      //  departure: ev.departureTime,
+      //  workerReceived: arrivalTime,
+      //  workerResponse: performance.now();
+      //},
       result: result
-    }, wMsgHandler.xfer);
+    }, wState.xfer);
   };
   self.postMessage({type:'sqlite3-api',result:'worker1-ready'});
 }.bind({self, sqlite3});
 });
-
index d023f863667fe2e33bd00e74238ab632c7f98bdd..7327e14c70f8a3aecd97b81870d39968f6d7f544 100644 (file)
@@ -27,7 +27,7 @@
    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.
+   worker API, with any exceptions documented below.
 
    It requires a configuration object with the following properties:
 
    - 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 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-#}
+
+   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.
-
-
-   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 idTypeMap = config.generateMessageId ? undefined : Object.create(null);
   const genMsgId = config.generateMessageId || function(msg){
-    return msg.type+'#'+(++idNumber);
+    return msg.type+'#'+(idTypeMap[msg.type] = (idTypeMap[msg.type]||0) + 1);
   };
   const toss = (...args)=>{throw new Error(args.join(' '))};
   if('function'===typeof config.worker) config.worker = config.worker();
@@ -162,7 +170,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
       }
       msgHandler = handlerMap[ev.type] /* check for exec per-row callback */;
       if(msgHandler && msgHandler.onrow){
-        msgHandler.onrow(ev.row);
+        msgHandler.onrow(ev);
         return;
       }        
       if(config.onunhandled) config.onunhandled(arguments[0]);
@@ -183,13 +191,10 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
         default:
           break;
     }
-    try {
-      msgHandler.resolve(ev);
-    }catch(e){
-      msgHandler.reject(e);
-    }
+    try {msgHandler.resolve(ev)}
+    catch(e){msgHandler.reject(e)}
   }/*worker.onmessage()*/;
-  return function(/*(msgType, msgArgs) || (msg)*/){
+  return function(/*(msgType, msgArgs) || (msgEnvelope)*/){
     let msg;
     if(1===arguments.length){
       msg = arguments[0];
@@ -206,15 +211,29 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
     msg.departureTime = performance.now();
     const proxy = Object.create(null);
     proxy.message = msg;
-    let cbId /* message handler ID for exec on-row callback proxy */;
+    let rowCallbackId /* 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';
+        rowCallbackId = msg.messageId+':row';
         proxy.onrow = msg.args.callback;
-        msg.args.callback = cbId;
-        handlerMap[cbId] = proxy;
+        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);
@@ -225,7 +244,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
       debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg);
       config.worker.postMessage(msg);
     });
-    if(cbId) p = p.finally(()=>delete handlerMap[cbId]);
+    if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]);
     return p;
   };
 }/*sqlite3Worker1Promiser()*/;
index cfae443d06a8a80833c3b2d9310a50f33d8d944f..a2f3a2523d770a6a9af98c585142a3d8ac1173c8 100644 (file)
   delete self.sqlite3Worker1Promiser;
 
   const wtest = async function(msgType, msgArgs, callback){
+    if(2===arguments.length && 'function'===typeof msgArgs){
+      callback = msgArgs;
+      msgArgs = undefined;
+    }
     const p = workerPromise({type: msgType, args:msgArgs});
     return callback ? p.then(callback).finally(testCount) : p;
   };
 
   const runTests = async function(){
     const dbFilename = '/testing2.sqlite3';
-    logHtml('',
-            "Sending 'open' message and waiting for its response before continuing.");
     startTime = performance.now();
+
+    let sqConfig;
+    await wtest('config-get', (ev)=>{
+      const r = ev.result;
+      log('sqlite3.config subset:', r);
+      T.assert('boolean' === typeof r.bigIntEnabled)
+        .assert('string'===typeof r.persistentDirName)
+        .assert('boolean' === typeof r.persistenceEnabled);
+      sqConfig = r;
+    });
+    logHtml('',
+            "Sending 'open' message and waiting for its response before continuing...");
+    
     await wtest('open', {
       filename: dbFilename,
-      persistent: true,
+      persistent: sqConfig.persistenceEnabled,
       simulateError: 0 /* if true, fail the 'open' */,
     }, function(ev){
-      log("then open result",ev);
-      T.assert(1 && (dbFilename===ev.result.filename
-               || (sqlite3TestModule.sqlite3ApiConfig.persistentDirName
-                   + dbFilename)==ev.result.filename))
+      const r = ev.result;
+      log("then open result",r);
+      T.assert(r.persistent === sqConfig.persistenceEnabled)
+        .assert(r.persistent
+               ? (dbFilename!==r.filename)
+               : (dbFilename==r.filename))
         .assert(ev.dbId)
         .assert(ev.messageId)
         .assert(promiserConfig.dbId === ev.dbId);
         .assert(3 === ev.resultRows[1][0]);
     });
 
-    const resultRowTest1 = function f(row){
+    const resultRowTest1 = function f(ev){
       if(undefined === f.counter) f.counter = 0;
-      if(row) ++f.counter;
-      //log("exec() result row:",row);
-      T.assert(null===row || 'number' === typeof row.b);
+      if(null === ev.rowNumber){
+        /* End of result set. */
+        T.assert(undefined === ev.row);
+      }else{
+        T.assert(ev.rowNumber > 0);
+        ++f.counter;
+      }
+      log("exec() result row:",ev);
+      T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
     };
     await wtest('exec',{
       sql: 'select a a, b b from t order by a',
       T.assert('string' === typeof ev.result.filename);
     });
 
-    await wtest('close').then((ev)=>{
+    await wtest('close'(ev)=>{
       T.assert(undefined === ev.result.filename);
-      log("That's all, folks!");
-    });
+    }).finally(()=>log("That's all, folks!"));
   }/*runTests2()*/;
 
   
index b051cc04ccccf4b91fb39681855bfe70b7fabc0f..d64fd6e89ad055531370db89702f09d961a42357 100644 (file)
     },
     resultRowTest1: function f(ev){
       if(undefined === f.counter) f.counter = 0;
-      if(ev.row) ++f.counter;
-      //log("exec() result row:",ev.row);
-      T.assert(null===ev.row || 'number' === typeof ev.row.b);
+      if(null === ev.rowNumber){
+        /* End of result set. */
+        T.assert(undefined === ev.row);
+      }else{
+        T.assert(ev.rowNumber > 0);
+        ++f.counter;
+      }
+      //log("exec() result row:",ev);
+      T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
     }
   };
 
index ca5ed7147e0127531b2d0f0ec1d6007d6c06b215..2b3fc24b379677ce582dd9278d4d2c400cfd7150 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C js:\sresolve\sthe\smysterious\s"extra"\sunhandled\sexception\snotification,\scaused\sby\sinadvertently\sforking\sone\spromise\sinto\stwo\sseparate\sones\s(failing\sto\sproperly\sreassign\sa\sthen()\sresult).\sFix\sa\stypo\sin\snew\sWorker\s1\scode\swhich\scaused\sthe\sDB(filename)\sname\sto\sbe\sincorrect.
-D 2022-08-24T14:50:10.920
+C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker).
+D 2022-08-24T18:39:46.246
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
 F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de
 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 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc
 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a
-F ext/wasm/api/sqlite3-api-worker1.js b23f66ef5afd350a17fbadb795007098e518a40e5c7c439cd83ef34aa55a45af
+F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
@@ -508,14 +508,14 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43
 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9
 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0
-F ext/wasm/sqlite3-worker1-promiser.js 9638b0ced7f02806c3220b616f08729dde9eb13fb56e125cd4759f40bfa81210
+F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
-F ext/wasm/testing-worker1-promiser.js 931d909c769c57292f1cafdf10c7dab402d17cd16a6d0ec32089f67b559b058f
+F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d
 F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
-F ext/wasm/testing2.js f01e8d7b32f6d4790f8352bf8dc17d2b860afa58f6c8ea1f824ef1c0d2068138
+F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
@@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878
-R a11ec2b23afafa9cff8838dfde907f0c
+P 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
+R 4d53a1266e528a954f73a792c3648256
 U stephan
-Z 64f88270da37ddd3ce54e6e07b8ab1af
+Z cffa6daad399e2d401f0fd69f4f04b4f
 # Remove this line to create a well-formed Fossil manifest.
index 521621a6a1905a9005e91d8a0a8b4226fe0efc4c..7400cbe1778ab9332806d7991326e2304d9eb303 100644 (file)
@@ -1 +1 @@
-7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
\ No newline at end of file
+509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
\ No newline at end of file