1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Significant cleanups and expansion of the sqlite3.opfs utilities. Add oo1.DB.dbVfsName(). Add VFS name to worker1:open's arguments and result.

FossilOrigin-Name: 86a341d7e061f946b39e8647ddd4743013b851b33ae9e6e755d8dbc53fba5286
This commit is contained in:
stephan
2022-11-01 07:49:49 +00:00
parent c7dd9b60eb
commit 49048b148e
14 changed files with 341 additions and 106 deletions

View File

@ -258,9 +258,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
in the form of a single configuration object with the following
properties:
- `.filename`: database file name
- `.flags`: open-mode flags
- `.vfs`: the VFS fname
- `filename`: database file name
- `flags`: open-mode flags
- `vfs`: the VFS fname
The `filename` and `vfs` arguments may be either JS strings or
C-strings allocated via WASM. `flags` is required to be a JS
@ -560,6 +560,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
dbName: function(dbNumber=0){
return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber);
},
/**
Returns the name of the sqlite3_vfs used by the given database
of this connection (defaulting to 'main'). The argument may be
either a JS string or a WASM C-string. Returns undefined if the
given db name is invalid. Throws if this object has been
close()d.
*/
dbVfsName: function(dbName=0){
let rc;
const pVfs = capi.sqlite3_js_db_vfs(
affirmDbOpen(this).pointer, dbName
);
if(pVfs){
const v = new capi.sqlite3_vfs(pVfs);
try{ rc = wasm.cstringToJs(v.$zName) }
finally { v.dispose() }
}
return rc;
},
/**
Compiles the given SQL and returns a prepared Stmt. This is
the only way to create new Stmt objects. Throws on error.

View File

@ -371,6 +371,20 @@ const installOpfsVfs = function callee(options){
return rc;
};
/**
Not part of the public API. Only for test/development use.
*/
opfsUtil.debug = {
asyncShutdown: ()=>{
warn("Shutting down OPFS async listener. The OPFS VFS will no longer work.");
opRun('opfs-async-shutdown');
},
asyncRestart: ()=>{
warn("Attempting to restart OPFS VFS async listener. Might work, might not.");
W.postMessage({type: 'opfs-async-restart'});
}
};
const initS11n = ()=>{
/**
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@ -876,37 +890,61 @@ const installOpfsVfs = function callee(options){
}
/**
Syncronously deletes the given OPFS filesystem entry, ignoring
any errors. As this environment has no notion of "current
directory", the given name must be an absolute path. If the 2nd
argument is truthy, deletion is recursive (use with caution!).
Returns true if the deletion succeeded and false if it fails,
but cannot report the nature of the failure.
Expects an OPFS file path. It gets resolved, such that ".."
components are properly expanded, and returned. If the 2nd arg
is true, the result is returned as an array of path elements,
else an absolute path string is returned.
*/
opfsUtil.deleteEntry = function(fsEntryName,recursive=false){
mTimeStart('xDelete');
const rc = opRun('xDelete', fsEntryName, 0, recursive);
mTimeEnd();
return 0===rc;
opfsUtil.getResolvedPath = function(filename,splitIt){
const p = new URL(filename, "file://irrelevant").pathname;
return splitIt ? p.split('/').filter((v)=>!!v) : p;
};
/**
Synchronously creates the given directory name, recursively, in
Takes the absolute path to a filesystem element. Returns an
array of [handleOfContainingDir, filename]. If the 2nd argument
is truthy then each directory element leading to the file is
created along the way. Throws if any creation or resolution
fails.
*/
opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){
const path = opfsUtil.getResolvedPath(absFilename, true);
const filename = path.pop();
let dh = opfsUtil.rootDirectory;
for(const dirName of path){
if(dirName){
dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs});
}
}
return [dh, filename];
};
/**
Creates the given directory name, recursively, in
the OPFS filesystem. Returns true if it succeeds or the
directory already exists, else false.
*/
opfsUtil.mkdir = function(absDirName){
mTimeStart('mkdir');
const rc = opRun('mkdir', absDirName);
mTimeEnd();
return 0===rc;
opfsUtil.mkdir = async function(absDirName){
try {
await opfsUtil.getDirForFilename(absDirName+"/filepart", true);
return true;
}catch(e){
//console.warn("mkdir(",absDirName,") failed:",e);
return false;
}
};
/**
Synchronously checks whether the given OPFS filesystem exists,
Checks whether the given OPFS filesystem entry exists,
returning true if it does, false if it doesn't.
*/
opfsUtil.entryExists = function(fsEntryName){
return 0===opRun('xAccess', fsEntryName);
opfsUtil.entryExists = async function(fsEntryName){
try {
const [dh, fn] = await opfsUtil.getDirForFilename(filename);
await dh.getFileHandle(fn);
return true;
}catch(e){
return false;
}
};
/**
@ -938,20 +976,160 @@ const installOpfsVfs = function callee(options){
};
/**
Only for test/development use.
Returns a promise which resolves to an object which represents
all files and directories in the OPFS tree. The top-most object
has two properties: `dirs` is an array of directory entries
(described below) and `files` is a list of file names for all
files in that directory.
Traversal starts at sqlite3.opfs.rootDirectory.
Each `dirs` entry is an object in this form:
```
{ name: directoryName,
dirs: [...subdirs],
files: [...file names]
}
```
The `files` and `subdirs` entries are always set but may be
empty arrays.
The returned object has the same structure but its `name` is
an empty string. All returned objects are created with
Object.create(null), so have no prototype.
Design note: the entries do not contain more information,
e.g. file sizes, because getting such info is not only
expensive but is subject to locking-related errors.
*/
opfsUtil.debug = {
asyncShutdown: ()=>{
warn("Shutting down OPFS async listener. OPFS will no longer work.");
opRun('opfs-async-shutdown');
},
asyncRestart: ()=>{
warn("Attempting to restart OPFS async listener. Might work, might not.");
W.postMessage({type: 'opfs-async-restart'});
opfsUtil.treeList = async function(){
const doDir = async function callee(dirHandle,tgt){
tgt.name = dirHandle.name;
tgt.dirs = [];
tgt.files = [];
for await (const handle of dirHandle.values()){
if('directory' === handle.kind){
const subDir = Object.create(null);
tgt.dirs.push(subDir);
await callee(handle, subDir);
}else{
tgt.files.push(handle.name);
}
}
};
const root = Object.create(null);
await doDir(opfsUtil.rootDirectory, root);
return root;
};
/**
Irrevocably deletes _all_ files in the current origin's OPFS.
Obviously, this must be used with great caution. It may throw
an exception if removal of anything fails (e.g. a file is
locked), but the precise conditions under which it will throw
are not documented (so we cannot tell you what they are).
*/
opfsUtil.rmfr = async function(){
const dir = opfsUtil.rootDirectory, opt = {recurse: true};
for await (const handle of dir.values()){
dir.removeEntry(handle.name, opt);
}
};
//TODO to support fiddle db upload:
/**
Deletes the given OPFS filesystem entry. As this environment
has no notion of "current directory", the given name must be an
absolute path. If the 2nd argument is truthy, deletion is
recursive (use with caution!).
The returned Promise resolves to true if the deletion was
successful, else false (but...). The OPFS API reports the
reason for the failure only in human-readable form, not
exceptions which can be type-checked to determine the
failure. Because of that...
If the final argument is truthy then this function will
propagate any exception on error, rather than returning false.
*/
opfsUtil.unlink = async function(fsEntryName, recursive = false,
throwOnError = false){
try {
const [hDir, filenamePart] =
await opfsUtil.getDirForFilename(fsEntryName, false);
await hDir.removeEntry(filenamePart, {recursive});
return true;
}catch(e){
if(throwOnError){
throw new Error("unlink(",arguments[0],") failed: "+e.message,{
cause: e
});
}
return false;
}
};
/**
Traverses the OPFS filesystem, calling a callback for each one.
The argument may be either a callback function or an options object
with any of the following properties:
- `callback`: function which gets called for each filesystem
entry. It gets passed 3 arguments: 1) the
FileSystemFileHandle or FileSystemDirectoryHandle of each
entry (noting that both are instanceof FileSystemHandle). 2)
the FileSystemDirectoryHandle of the parent directory. 3) the
current depth level, with 0 being at the top of the tree
relative to the starting directory. If the callback returns a
literal false, as opposed to any other falsy value, traversal
stops without an error. Any exceptions it throws are
propagated. Results are undefined if the callback manipulate
the filesystem (e.g. removing or adding entries) because the
how OPFS iterators behave in the face of such changes is
undocumented.
- `recursive` [bool=true]: specifies whether to recurse into
subdirectories or not. Whether recursion is depth-first or
breadth-first is unspecified!
- `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory]
specifies the starting directory.
If this function is passed a function, it is assumed to be the
callback.
Returns a promise because it has to (by virtue of being async)
but that promise has no specific meaning: the traversal it
performs is synchronous. The promise must be used to catch any
exceptions propagated by the callback, however.
TODO: add an option which specifies whether to traverse
depth-first or breadth-first. We currently do depth-first but
an incremental file browsing widget would benefit more from
breadth-first.
*/
opfsUtil.traverse = async function(opt){
const defaultOpt = {
recursive: true,
directory: opfsUtil.rootDirectory
};
if('function'===typeof opt){
opt = {callback:opt};
}
opt = Object.assign(defaultOpt, opt||{});
const doDir = async function callee(dirHandle, depth){
for await (const handle of dirHandle.values()){
if(false === opt.callback(handle, dirHandle, depth)) return false;
else if(opt.recursive && 'directory' === handle.kind){
if(false === await callee(handle, depth + 1)) break;
}
}
};
doDir(opt.directory, 0);
};
//TODO to support fiddle and worker1 db upload:
//opfsUtil.createFile = function(absName, content=undefined){...}
if(sqlite3.oo1){

View File

@ -1187,14 +1187,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
}
/**
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db
name, returns a truthy value (see below) if that db handle uses
that VFS, else returns false. If pDb is falsy then the 3rd
argument is ignored and this function returns a truthy value if
the default VFS name matches that of the 2nd argument. Results
are undefined if pDb is truthy but refers to an invalid
pointer. The 3rd argument specifies the database name of the
given database connection to check, defaulting to the main db.
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name
(defaulting to "main"), returns a truthy value (see below) if
that db uses that VFS, else returns false. If pDb is falsy then
the 3rd argument is ignored and this function returns a truthy
value if the default VFS name matches that of the 2nd
argument. Results are undefined if pDb is truthy but refers to an
invalid pointer. The 3rd argument specifies the database name of
the given database connection to check, defaulting to the main
db.
The 2nd and 3rd arguments may either be a JS string or a WASM
C-string. If the 2nd argument is a NULL WASM pointer, the default
@ -1209,14 +1210,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
bad arguments cause a conversion error when passing into
wasm-space, false is returned.
*/
capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName="main"){
capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName=0){
try{
const pK = capi.sqlite3_vfs_find(vfsName);
if(!pK) return false;
else if(!pDb){
return pK===capi.sqlite3_vfs_find(0) ? pK : false;
}else{
return pK===capi.sqlite3_js_db_vfs(pDb) ? pK : false;
return pK===capi.sqlite3_js_db_vfs(pDb,dbName) ? pK : false;
}
}catch(e){
/* Ignore - probably bad args to a wasm-bound function. */

View File

@ -188,6 +188,8 @@
See the sqlite3.oo1.DB constructor for peculiarities and
transformations,
vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "".
This may change how the given filename is resolved.
}
}
```
@ -212,6 +214,7 @@
persistent: true if the given filename resides in the
known-persistent storage, else false.
vfs: name of the VFS the "main" db is using.
}
}
```
@ -362,7 +365,7 @@ sqlite3.initWorker1API = function(){
/** Temp holder for "transferable" postMessage() state. */
xfer: [],
open: function(opt){
const db = new DB(opt.filename);
const db = new DB(opt);
this.dbs[getDbId(db)] = db;
if(this.dbList.indexOf(db)<0) this.dbList.push(db);
return db;
@ -442,12 +445,14 @@ sqlite3.initWorker1API = function(){
oargs.filename = args.filename || '';
}else{
oargs.filename = args.filename;
oargs.vfs = args.vfs;
}
const db = wState.open(oargs);
rc.filename = db.filename;
rc.persistent = (!!pDir && db.filename.startsWith(pDir+'/'))
|| !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs");
rc.dbId = getDbId(db);
rc.vfs = db.dbVfsName();
return rc;
},
@ -528,6 +533,7 @@ sqlite3.initWorker1API = function(){
rc.wasmfsOpfsEnabled = !!sqlite3.capi.sqlite3_wasmfs_opfs_dir();
rc.version = sqlite3.version;
rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list();
rc.opfsEnabled = !!sqlite3.opfs;
return rc;
},
@ -542,11 +548,6 @@ sqlite3.initWorker1API = function(){
}
*/
export: function(ev){
/**
We need to reimplement this to use the Emscripten FS
interface. That part used to be in the OO#1 API but that
dependency was removed from that level of the API.
*/
const db = getMsgDb(ev);
const response = {
bytearray: sqlite3.capi.sqlite3_js_db_export(db.pointer),
@ -559,17 +560,23 @@ sqlite3.initWorker1API = function(){
toss: function(ev){
toss("Testing worker exception");
},
'opfs-tree': async function(ev){
if(!sqlite3.opfs) toss("OPFS support is unavailable.");
const response = await sqlite3.opfs.treeList();
return response;
}
}/*wMsgHandler*/;
self.onmessage = function(ev){
self.onmessage = async function(ev){
ev = ev.data;
let result, dbId = ev.dbId, evType = ev.type;
const arrivalTime = performance.now();
try {
if(wMsgHandler.hasOwnProperty(evType) &&
wMsgHandler[evType] instanceof Function){
result = wMsgHandler[evType](ev);
result = await wMsgHandler[evType](ev);
}else{
toss("Unknown db worker message type:",ev.type);
}

View File

@ -377,6 +377,7 @@ const vfsAsyncImpls = {
if(!filenamePart) break;
await hDir.removeEntry(filenamePart, {recursive});
if(0x1234 !== syncDir) break;
recursive = false;
filename = getResolvedPath(filename, true);
filename.pop();
filename = filename.join('/');

View File

@ -37,6 +37,8 @@ span.labeled-input {
.strong { font-weight: 700 }
.warning { color: firebrick; }
.green { color: darkgreen; }
.tests-pass { background-color: green; color: white }
.tests-fail { background-color: red; color: yellow }
.faded { opacity: 0.5; }
.group-start { color: blue; }
.group-end { color: blue; }

View File

@ -91,7 +91,8 @@
const r = ev.result;
log("then open result",r);
T.assert(ev.dbId === r.dbId)
.assert(ev.messageId);
.assert(ev.messageId)
.assert('string' === typeof r.vfs);
promiserConfig.dbId = ev.dbId;
}).then(runTests2);
};

View File

@ -291,7 +291,8 @@
log("open result",ev);
T.assert('testing2.sqlite3'===ev.result.filename)
.assert(ev.dbId)
.assert(ev.messageId);
.assert(ev.messageId)
.assert('string' === typeof ev.result.vfs);
DbState.id = ev.dbId;
if(waitForOpen) setTimeout(runTests2, 0);
});

View File

@ -37,7 +37,7 @@ const tryOpfsVfs = async function(sqlite3){
const urlArgs = new URL(self.location.href).searchParams;
const dbFile = "my-persistent.db";
if(urlArgs.has('delete')) sqlite3.opfs.deleteEntry(dbFile);
if(urlArgs.has('delete')) sqlite3.opfs.unlink(dbFile);
const db = new opfs.OpfsDb(dbFile,'ct');
log("db file:",db.filename);
@ -62,13 +62,14 @@ const tryOpfsVfs = async function(sqlite3){
// Some sanity checks of the opfs utility functions...
const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
const aDir = testDir+'/test/dir';
opfs.mkdir(aDir) || toss("mkdir failed");
opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists");
opfs.deleteEntry(testDir+'/test') && toss("delete 1 should have failed (dir not empty)");
opfs.deleteEntry(testDir+'/test/dir') || toss("delete 2 failed");
opfs.deleteEntry(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)");
opfs.deleteEntry(testDir,true) || toss("delete 3 failed");
opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed");
await opfs.mkdir(aDir) || toss("mkdir failed");
await opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists");
await opfs.unlink(testDir+'/test') && toss("delete 1 should have failed (dir not empty)");
//await opfs.entryExists(testDir)
await opfs.unlink(testDir+'/test/dir') || toss("delete 2 failed");
await opfs.unlink(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)");
await opfs.unlink(testDir, true) || toss("delete 3 failed");
await opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed");
}finally{
db.close();
}

View File

@ -14,7 +14,7 @@
</style>
</head>
<body>
<h1>sqlite3 WASM/JS tester #1 (Worker thread)</h1>
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (Worker thread)</h1>
<div>See <a href='tester1.html' target='tester1.html'>tester1.html</a>
for the UI-thread variant.</div>
<div class='input-wrapper'>
@ -46,6 +46,14 @@
case 'log':
logHtml(data.payload.cssClass, ...data.payload.args);
break;
case 'error':
logHtml('error', ...data.payload.args);
break;
case 'test-result':
document.querySelector('#color-target').classList.add(
data.payload.pass ? 'tests-pass' : 'tests-fail'
);
break;
default:
logHtml('error',"Unhandled message:",data.type);
};

View File

@ -14,7 +14,7 @@
</style>
</head>
<body>
<h1>sqlite3 WASM/JS tester #1 (UI thread)</h1>
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (UI thread)</h1>
<div>See <a href='tester1-worker.html' target='tester1-worker.html'>tester1-worker.html</a>
for the Worker-thread variant.</div>
<div class='input-wrapper'>

View File

@ -99,6 +99,14 @@
};
}
}
const reportFinalTestStatus = function(pass){
if(isUIThread()){
const e = document.querySelector('#color-target');
e.classList.add(pass ? 'tests-pass' : 'tests-fail');
}else{
postMessage({type:'test-result', payload:{pass}});
}
};
const log = (...args)=>{
//console.log(...args);
logClass('',...args);
@ -139,28 +147,19 @@
toBool: function(expr){
return (expr instanceof Function) ? !!expr() : !!expr;
},
/** abort() if expr is false. If expr is a function, it
is called and its result is evaluated.
/** Throws if expr is false. If expr is a function, it is called
and its result is evaluated. If passed multiple arguments,
those after the first are a message string which get applied
as an exception message if the assertion fails. The message
arguments are concatenated together with a space between each.
*/
assert: function f(expr, msg){
if(!f._){
f._ = ('undefined'===typeof abort
? (msg)=>{throw new Error(msg)}
: abort);
}
assert: function f(expr, ...msg){
++this.counter;
if(!this.toBool(expr)){
f._(msg || "Assertion failed.");
throw new Error(msg.length ? msg.join(' ') : "Assertion failed.");
}
return this;
},
/** Identical to assert() but throws instead of calling
abort(). */
affirm: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed.");
return this;
},
/** Calls f() and squelches any exception it throws. If it
does not throw, this function throws. */
mustThrow: function(f, msg){
@ -301,9 +300,11 @@
"Done running tests.",TestUtil.counter,"assertions in",
roundMs(runtime),'ms');
pok();
reportFinalTestStatus(true);
}catch(e){
error(e);
pnok(e);
reportFinalTestStatus(false);
}
}.bind(this));
}
@ -1117,10 +1118,11 @@
const dbFile = '/tester1.db';
wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
const db = this.db = new sqlite3.oo1.DB(dbFile);
T.assert(Number.isInteger(db.pointer)).
mustThrowMatching(()=>db.pointer=1, /read-only/).
assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)).
assert('main'===db.dbName(0));
T.assert(Number.isInteger(db.pointer))
.mustThrowMatching(()=>db.pointer=1, /read-only/)
.assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1))
.assert('main'===db.dbName(0))
.assert('string' === typeof db.dbVfsName());
// Custom db error message handling via sqlite3_prepare_v2/v3()
let rc = capi.sqlite3_prepare_v3(db.pointer, {/*invalid*/}, -1, 0, null, null);
T.assert(capi.SQLITE_MISUSE === rc)
@ -1737,13 +1739,14 @@
(sqlite3)=>{return !!sqlite3.opfs})
.t({
name: 'OPFS sanity checks',
test: function(sqlite3){
test: async function(sqlite3){
const opfs = sqlite3.opfs;
const filename = 'sqlite3-tester1.db';
const pVfs = capi.sqlite3_vfs_find('opfs');
T.assert(pVfs);
const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
unlink();
let db = new sqlite3.opfs.OpfsDb(filename);
let db = new opfs.OpfsDb(filename);
try {
db.exec([
'create table p(a);',
@ -1751,13 +1754,26 @@
]);
T.assert(3 === db.selectValue('select count(*) from p'));
db.close();
db = new sqlite3.opfs.OpfsDb(filename);
db = new opfs.OpfsDb(filename);
db.exec('insert into p(a) values(4),(5),(6)');
T.assert(6 === db.selectValue('select count(*) from p'));
}finally{
db.close();
unlink();
}
// Some sanity checks of the opfs utility functions...
const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
const aDir = testDir+'/test/dir';
T.assert(await opfs.mkdir(aDir), "mkdir failed")
.assert(await opfs.mkdir(aDir), "mkdir must pass if the dir exists")
.assert(!(await opfs.unlink(testDir+'/test')), "delete 1 should have failed (dir not empty)")
.assert((await opfs.unlink(testDir+'/test/dir')), "delete 2 failed")
.assert(!(await opfs.unlink(testDir+'/test/dir')),
"delete 2b should have failed (dir already deleted)")
.assert((await opfs.unlink(testDir, true)), "delete 3 failed")
.assert(!(await opfs.entryExists(testDir)),
"entryExists(",testDir,") should have failed");
}
}/*OPFS sanity checks*/)
;/* end OPFS tests */

View File

@ -1,5 +1,5 @@
C Make\sthe\sUPDATE\smethod\sof\sthe\ssqlite_dbpage\svirtual\stable\srebust\sagainst\nOOM\serrors.\s\s[forum:/forumpost/bbcf0dd6ca|Forum\spost\sbbcf0dd6ca].\s\sTest\scase\nin\sTH3.
D 2022-10-31T18:01:05.280
C Significant\scleanups\sand\sexpansion\sof\sthe\ssqlite3.opfs\sutilities.\sAdd\soo1.DB.dbVfsName().\sAdd\sVFS\sname\sto\sworker1:open's\sarguments\sand\sresult.
D 2022-11-01T07:49:49.018
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -500,12 +500,12 @@ F ext/wasm/api/post-js-header.js d6ab3dfef4a06960d28a7eaa338d4e2a1a5981e9b387181
F ext/wasm/api/pre-js.js 287e462f969342b032c03900e668099fa1471d852df7a472de5bc349161d9c04
F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
F ext/wasm/api/sqlite3-api-glue.js ebfd1d01b249c705482bfe3bdef3b1feaecf55821c46abf79b8105ec9b1c2b1a
F ext/wasm/api/sqlite3-api-oo1.js 4028bc2bac7e3ae2d23b7c99828155b4a06da006b51dc2a929bc0db26337370d
F ext/wasm/api/sqlite3-api-opfs.js 6880cc79a4d1b6075942298d9d1ab07e24d81fbd9e5fe6b7c797b86e4b2af596
F ext/wasm/api/sqlite3-api-prologue.js 873986ca150c79510f647b910f8349bc71b14db21e444cab3b9fad9c4f39ffc7
F ext/wasm/api/sqlite3-api-worker1.js 7de5160ba86ed216e8b99ba1ae0c39c1ff85c11a4818252bfc8dd0dc21109b84
F ext/wasm/api/sqlite3-api-oo1.js ce8db98ab7c394fd8fb99ae135c46269a1a0162250a70e39e7975b4317a03640
F ext/wasm/api/sqlite3-api-opfs.js 59b278ed00764fc47ba88be0582ab3fc3ce725e02b6d86459464cc029b9ac356
F ext/wasm/api/sqlite3-api-prologue.js 850581aa0c759db25c04d64b340b0c5272f253fb195377acc8d851e376710ad3
F ext/wasm/api/sqlite3-api-worker1.js 4f920a54fb97d4ca50632d45bd7d011a55016eb5a5883725033abb450903bc6f
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
F ext/wasm/api/sqlite3-opfs-async-proxy.js b2264efef84c4a0af5dab426d7573d8b4ee5af3a25ba108b709a835a613c5e36
F ext/wasm/api/sqlite3-opfs-async-proxy.js 2627bf8dc04c24783a55de40d8a6d18da31e81a9bb926a5b672b402f582099b5
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/api/sqlite3-wasm.c 14ac9c03f6585332f882703f3427f11ffe8ffe8b6c0e252be2c518f7aac6ab6a
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
@ -514,7 +514,7 @@ F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d
F ext/wasm/batch-runner.js 49609e89aaac9989d6c1ad3fae268e4878e1ad7bc5fd3e5c2f44959660780b2e
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 739b58c44511f642f16f57b701c84dc9ee412d8bc47b3d8a99d947babfa69d9d
F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
F ext/wasm/common/whwasmutil.js 77930367c2a65cf6fd6f99ad3644ede33e4d20466f5e506eb87b8d101a0a7655
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
@ -522,9 +522,9 @@ F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb0
F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e
F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8
F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98ab22f5786620b3354ed15f
F ext/wasm/demo-worker1-promiser.js f68ffbbe1c6086e18ce7961b8fc2b40dd88db174f59052e228c06b07484945ca
F ext/wasm/demo-worker1-promiser.js 988ce92220c1cf1dd95fcb6b59734f0ae677942469390ddd8a64f4bbb5f99821
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
F ext/wasm/demo-worker1.js 8ba51d94c4601fa5c313d9e59b63b238f5305b5d5739ad21f4782a0161e6682e
F ext/wasm/demo-worker1.js 117e4eedc62e103e287f0e4a694add7e13a200a4d7056e718645032288c4a8ab
F ext/wasm/dist.make 481289899a07958439d07ee4302ff86235fa0fbb72f17ea05db2be90a94abf90
F ext/wasm/fiddle.make e570ec1bfc7d803507a2e514fe32f673fe001b2114b85c73c3964a462ba8bcfc
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
@ -546,10 +546,10 @@ F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f129
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
F ext/wasm/tester1-worker.html d02b9d38876b023854cf8955e77a40912f7e516956b4dbe1ec7f215faac273ee
F ext/wasm/tester1.html c6c47e5a8071eb09cb1301104435c8e44fbb5719c92411f5b2384a461f9793c5
F ext/wasm/tester1.js 2427ac48e255f658ad81163b5dc6372a8609ab6ab60e295e371d1e5fe9a495ab
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
F ext/wasm/tester1-worker.html 51bf39e2b87f974ae3d5bc3086e2fb36d258f3698c54f6e21ba4b3b99636fa27
F ext/wasm/tester1.html 624ec41cd9f78a1f2b6d7df70aaa7a6394396b1f2455ecbd6de5775c1275b121
F ext/wasm/tester1.js 157eb499aad3365e33a7d95d6847c1d58d533335bc19d79bd3bc700b6350d1a5
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
F ext/wasm/wasmfs.make fb2d3c4a298b12cf1ec994ad1d0f1d027ae297449b364cde43d2eb807d68048f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@ -2054,8 +2054,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 c7750c101d9b7baa31496731bc34ea0a9b2bad0c11e2b3e92a8c7b327135c1bb
R 196f3951f65687c9f90c3fbde15932cd
U drh
Z 5dce30cfcdbfc3a472a813e27f5eb193
P d15c9a4a323b825eb80e706e12e9df95e5db458024f51e6e537940efc8234d8b
R 095ddbe256b2c65febf55a9bc126a8d9
U stephan
Z 25b125153b5de3e07d69ba6b59e3eede
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
d15c9a4a323b825eb80e706e12e9df95e5db458024f51e6e537940efc8234d8b
86a341d7e061f946b39e8647ddd4743013b851b33ae9e6e755d8dbc53fba5286