From c4d6f31747bc7e19fc13b04ed912a1a30bb1472f Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 24 Nov 2025 20:14:23 +0000 Subject: [PATCH] Add kvvfs URI flag transient=1 to cause the storage object to be removed when the last handle to the db is closed. By default it behaves like a filesystem would, retaining the storage objects. FossilOrigin-Name: c6183261be77d03451aed563a5e4c19aa69706aa311cfdaf5db5e9b0a167d814 --- ext/wasm/api/sqlite3-api-oo1.c-pp.js | 2 +- ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js | 291 ++++++++++++++++--------- ext/wasm/tester1.c-pp.js | 37 ++-- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 217 insertions(+), 131 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.c-pp.js b/ext/wasm/api/sqlite3-api-oo1.c-pp.js index f2bd5e3c86..154454ffee 100644 --- a/ext/wasm/api/sqlite3-api-oo1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-oo1.c-pp.js @@ -213,7 +213,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ properties: - `.filename`: the db filename. It may be a special name like ":memory:" - or "". + or "". It may also be a URI-style name. - `.flags`: as documented in the DB constructor. diff --git a/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js index f0dd633402..eaf548b5b5 100644 --- a/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js @@ -90,6 +90,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ delete capi.KVVfsFile; if( !pKvvfs ) return /* nothing to do */; + if( 0 ){ + /* This working would be our proverbial holy grail. */ + capi.sqlite3_vfs_register(pKvvfs, 1); + } const util = sqlite3.util, wasm = sqlite3.wasm, @@ -180,20 +184,44 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Create a new instance of the objects which go into cache.storagePool. */ - const createStorageObj = (name,storage)=>Object.assign(Object.create(null),{ + const newStorageObj = (name,storage)=>Object.assign(Object.create(null),{ + /** + JS string value of this KVVfsFile::$zClass. i.e. the storage's + name. + */ jzClass: name, + /** + Refcount to keep dbs and journals pointing to the same storage + for the life of both. Managed by xOpen() and xClose(). + */ refc: 1, + /** + isTransient objects will be removed by xClose() when refc + reaches 0. The others will persist, to give the illusion of + real back-end storage. Managed by xOpen(). + */ + isTransient: false, + /** + The backing store. Must implement the Storage interface. + */ storage: storage || new KVVfsStorage, - /* This is the storage prefix used for kvvfs keys. It is + /** + The storage prefix used for kvvfs keys. It is "kvvfs-STORAGENAME-" for local/session storage and an empty - string for transient storage. local/session storage must - use the long form for backwards compatibility. + string for transient storage. local/session storage must use + the long form (A) for backwards compatibility and (B) so that + kvvfs can coexist with non-db client data in those backends. + Neither (A) nor (B) are concerns for KVVfsStorage objects. This prefix mirrors the one generated by os_kv.c's kvrecordMakeKey() and must stay in sync with that one. */ keyPrefix: kvvfsKeyPrefix(name), - files: [/*KVVfsFile instances currently using this storage*/] + /** + KVVfsFile instances currently using this storage. Managed by + xOpen() and xClose(). + */ + files: [] }); @@ -205,17 +233,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ that concurrent active xOpen()s on a given name, and within a given thread, use the same storage object. */ - /* Start off with mappings for well-known names. */ cache.storagePool = Object.assign(Object.create(null),{ - localThread: createStorageObj('localThread') + /* Start off with mappings for well-known names. */ + localThread: newStorageObj('localThread') }); if( globalThis.Storage ){ if( globalThis.localStorage instanceof globalThis.Storage ){ - cache.storagePool.local = createStorageObj('local'); + cache.storagePool.local = newStorageObj('local', globalThis.localStorage); } if( globalThis.sessionStorage instanceof globalThis.Storage ){ - cache.storagePool.session = createStorageObj('session'); + cache.storagePool.session = newStorageObj('session', globalThis.sessionStorage); } } @@ -242,6 +270,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ argument should be one of ('local','session',"") or the name of an opened transient kvvfs db. + Its handling of which=='' is historical: it means to consider + both local and session storage. which=='' is also a valid kvvfs + storage unit name, though. Prior to that possibility, this API + was only availbe in the main UI thread, so the empty string + continues to unconditionally work that way in the main UI + thread. When, however, it's running where local/session storage + are not availble, it treats which=='' as a storage unit name. + It returns an object in the form: .prefix = the key prefix for this storage. Typically @@ -259,13 +295,13 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ in the current environment (e.g. a worker thread). .cts = the underlying storagePool entry. This will only be set - of which is not empty. + if which is not empty. */ const kvvfsWhich = function callee(which){ const rc = Object.assign(Object.create(null),{ stores: [] }); - if( which ){ + if( which || !globalThis.Storage ){ const s = storageForZClass(which); if( s ){ //debug("kvvfsWhich",s.jzClass,rc.prefix, s.s); @@ -276,10 +312,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ rc.prefix = undefined; } }else{ - // Legacy behavior: return both local and session storage. rc.prefix = 'kvvfs-'; - if( globalThis.sessionStorage ) rc.stores.push(globalThis.sessionStorage); - if( globalThis.localStorage ) rc.stores.push(globalThis.localStorage); + // Legacy behavior: return both local and session storage. + if( cache.storagePool.local ) { + rc.stores.push(cache.storagePool.local.storage); + } + if( cache.storagePool.session ) { + rc.stores.push(cache.storagePool.session.storage); + } } //debug("kvvfsWhich",which,rc); return rc; @@ -292,8 +332,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ (sqlite3_file*). On success it returns a new KVVfsFile instance wrapping that pointer, which the caller must eventual call dispose() on (which won't free the underlying pointer, just the - wrapper). - */ + wrapper). Returns null if no handle is found (which would + indicate either that pDb is not using kvvfs or a severe bug in + its management). + */ const fileForDb = function(pDb){ const stack = pstack.pointer; try{ @@ -307,7 +349,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ pstack.restore(stack); } }; -//#endif nope /** Expects an object from the storagePool map. The $szPage and @@ -327,89 +368,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ throw e; } }; - - /** - Clears all storage used by the kvvfs DB backend, deleting any - DB(s) stored there. - - Its argument must be either 'session', 'local', "", or the name - of a transient kvvfs storage object file. In the first two cases, - only sessionStorage resp. localStorage is cleared. If which is an - empty string (the default) then both localStorage and - sessionStorage are cleared. Only storage keys which match the - pattern used by kvvfs are cleared: any other client-side data are - retained. - - This function only manipulates localStorage and sessionStorage in - the main UI thread (they don't exist in Worker threads). - It affects transient kvvfs objects in any thread. - - Returns the number of entries cleared. - */ - capi.sqlite3_js_kvvfs_clear = function(which=""){ - let rc = 0; - const store = kvvfsWhich(which); - const keyPrefix = store.prefix; - /** - Historically this code had no way to check whether the storage - was in use before wiping it, so could not error in that - case. Whether or not it should, not that it can (as of late - 2025-11), is TBD. - */ - store.stores.forEach((s)=>{ - const toRm = [] /* keys to remove */; - let i, n = s.length; - //debug("kvvfs_clear",store,s); - for( i = 0; i < n; ++i ){ - const k = s.key(i); - //debug("kvvfs_clear ?",k); - if(!keyPrefix || k.startsWith(keyPrefix)) toRm.push(k); - } - toRm.forEach((kk)=>s.removeItem(kk)); - rc += toRm.length; - }); - if( store.cts ) alertFilesToReload(store.cts); - return rc; - }; - - /** - This routine guesses the approximate amount of - storage used by the given kvvfs back-end. - - The 'which' argument is as documented for - sqlite3_js_kvvfs_clear(), only the operation this performs is - different: - - The returned value is twice the "length" value of every matching - key and value, noting that JavaScript stores each character in 2 - bytes. - - If passed 'local' or 'session' or '' from a thread other than the - main UI thread, this is effectively a no-op and returns 0. - - The returned size is not authoritative from the perspective of - how much data can fit into localStorage and sessionStorage, as - the precise algorithms for determining those limits are - unspecified and may include per-entry overhead invisible to - clients. - */ - capi.sqlite3_js_kvvfs_size = function(which=""){ - let sz = 0; - const store = kvvfsWhich(which); - //warn("kvvfs_size storage",store); - store?.stores?.forEach?.((s)=>{ - //warn("kvvfs_size backend",s); - let i; - for(i = 0; i < s.length; ++i){ - const k = s.key(i); - if(k.startsWith(store.prefix)){ - sz += k.length; - sz += s.getItem(k).length; - } - } - }); - return sz * 2 /* because JS uses 2-byte char encoding */; - }; +//#endif nope /** pstack-allocates a key. Caller must eventually restore the pstack to free the memory. */ @@ -611,13 +570,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ zName = (cache.zEmpty ??= wasm.allocCString("")); } const n = wasm.cstrlen(zName); - if( n > kvvfsMethods.$nKeySize - 8 /*"-journal"*/ - 1 ){ + if( !n ){ + warn("file name may not be empty (backwards compatibilty constraint)"); + return capi.SQLITE_RANGE; + }else if( n > kvvfsMethods.$nKeySize - 8 /*"-journal"*/ - 1 ){ warn("file name is too long:", wasm.cstrToJs(zName)); return capi.SQLITE_RANGE; } const jzClass = wasm.cstrToJs(zName); if( jzClass?.length != n ){ - warn("kvvfs file name must be ASCII-only"); + warn("kvvfs file name must be ASCII-only:",jzClass); /* This limitation is to avoide any issues with truncating multi-byte characters in kvvfs's key-size limit. */ @@ -626,12 +588,23 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags); if( rc ) return rc; + let transient = false; + if(n && wasm.isPtr(zName)){ + if(capi.sqlite3_uri_boolean(zName, "transient", 0)){ + transient = true; + //warn("transient=",transient); + } + if(capi.sqlite3_uri_boolean(zName, "wipe-before-open", 0)){ + // TODO + } + } const f = new KVVfsFile(pProtoFile); util.assert(f.$zClass, "Missing f.$zClass"); let s = storageForZClass(jzClass); //debug("xOpen", jzClass, s); if( s ){ ++s.refc; + if( true===transient ) s.isTransient = true; s.files.push(f); }else{ /* TODO: a url flag which tells it to keep the storage @@ -646,8 +619,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ : jzClass + '-journal'; s = cache.storagePool[jzClass] = cache.storagePool[other] - = createStorageObj(jzClass); + = newStorageObj(jzClass); s.files.push(f); + s.isTransient = transient; debug("xOpen installed storage handles [", jzClass, other,"]", s); } @@ -763,7 +737,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ pFileHandles.delete(pFile); const s = storageForZClass(h.jzClass); s.files = s.files.filter((v)=>v!==h.file); - if( 0===--s.refc ){ + if( --s.refc<=0 && s.isTransient ){ const other = h.file.$isJournal ? h.jzClass.replace(cache.rxJournalSuffix,'') : h.jzClass+'-journal'; @@ -874,6 +848,111 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }/*native method overrides*/ + /** + Clears all storage used by the kvvfs DB backend, deleting any + DB(s) stored there. + + Its argument must be the name of a kvvfs storage object: + + - 'session' + - 'local' + - An empty string means both of 'local' and 'session' storage. + - A transient kvvfs storage object name. + + In the first two cases, only sessionStorage resp. localStorage is + cleared. If passed an an empty string (the default) then its + behavior depends on: + + - If called in the main thread an empty stream means to act on + both localStorage and sessionStorage. Only storage keys which + match the pattern used by kvvfs are cleared: any other + client-side data are retained. Version 1 of this API always + behaves this way. (This is backwards-compatibility behavior to + account for an admitted misuse of "".) + + - If called from a thread where globalThis.Storage is not available + then an empty string is considered to be a legal storage unit name. + + Returns the number of entries cleared. + + As of kvvfs version 2: + + This API is available in Worker threads but does not have access + to localStorage or sessionStorage in them. Prior versions did not + include this API in Worker threads. + + Differences in this function in version 2: + + - It accepts an arbitrary storage name. In v1 this was a silent + no-op for any names other than ('local','session',''). + + - In Worker threads, an empty string argument is treated as a + storage unit name. + */ + capi.sqlite3_js_kvvfs_clear = function(which=""){ + let rc = 0; + const store = kvvfsWhich(which); + const keyPrefix = store.prefix; + /** + Historically this code had no way to check whether the storage + was in use before wiping it, so could not error in that + case. Whether or not it should, now that it can (as of late + 2025-11), is TBD. + */ + store.stores.forEach((s)=>{ + const toRm = [] /* keys to remove */; + let i, n = s.length; + //debug("kvvfs_clear",store,s); + for( i = 0; i < n; ++i ){ + const k = s.key(i); + //debug("kvvfs_clear ?",k); + if(!keyPrefix || k.startsWith(keyPrefix)) toRm.push(k); + } + toRm.forEach((kk)=>s.removeItem(kk)); + rc += toRm.length; + }); + //if( store.cts ) alertFilesToReload(store.cts); + return rc; + }; + + /** + This routine guesses the approximate amount of + storage used by the given kvvfs back-end. + + The 'which' argument is as documented for + sqlite3_js_kvvfs_clear(), only the operation this performs is + different: + + The returned value is twice the "length" value of every matching + key and value, noting that JavaScript stores each character in 2 + bytes. + + If passed 'local' or 'session' or '' from a thread other than the + main UI thread, this is effectively a no-op and returns 0. + + The returned size is not authoritative from the perspective of + how much data can fit into localStorage and sessionStorage, as + the precise algorithms for determining those limits are + unspecified and may include per-entry overhead invisible to + clients. + */ + capi.sqlite3_js_kvvfs_size = function(which=""){ + let sz = 0; + const store = kvvfsWhich(which); + //warn("kvvfs_size storage",store); + store?.stores?.forEach?.((s)=>{ + //warn("kvvfs_size backend",s); + let i; + for(i = 0; i < s.length; ++i){ + const k = s.key(i); + if(!store.prefix || k.startsWith(store.prefix)){ + sz += k.length; + sz += s.getItem(k).length; + } + } + }); + return sz * 2 /* because JS uses 2-byte char encoding */; + }; /** Copies the entire contents of the given transient storage object @@ -912,7 +991,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ util.toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named",storageName); } - debug("store to export=",store); + //debug("store to export=",store); const s = store.storage; const rc = Object.assign(Object.create(null),{ name: store.jzClass, @@ -1012,7 +1091,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "Cowardly refusing to create storage with a", "'-journal' suffix."); } - store = createStorageObj(exp.name); + store = newStorageObj(exp.name); cache.storagePool[exp.name] = cache.storagePool[exp.name+'-journal'] = store; //warn("Installing new storage:",store); diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 3176d0a9dd..dddcfbf0ba 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -210,8 +210,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule; filter.test(error.message) passes. If it's a function, the test passes if filter(error) returns truthy. If it's a string, the test passes if the filter matches the exception message - precisely. In all other cases the test fails, throwing an - Error. + precisely. If filter is a number then it is compared against + the resultCode property of the exception. In all other cases + the test fails, throwing an Error. If it throws, msg is used as the error report unless it's falsy, in which case a default is used. @@ -225,7 +226,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule; if(filter instanceof RegExp) pass = filter.test(err.message); else if(filter instanceof Function) pass = filter(err); else if('string' === typeof filter) pass = (err.message === filter); + else if('number' === typeof filter) pass = (err.resultCode === filter); if(!pass){ + console.error("Filter",filter,"rejected exception",err); throw new Error(msg || ("Filter rejected this exception: <<"+err.message+">>")); } return this; @@ -2918,6 +2921,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; test: function(sqlite3){ const filename = 'localThread' /* preinstalled instance */; const JDb = sqlite3.oo1.JsStorageDb; + const DB = sqlite3.oo1.DB; JDb.clearStorage(filename); let db = new JDb(filename); const sqlSetup = [ @@ -2952,16 +2956,19 @@ globalThis.sqlite3InitModule = sqlite3InitModule; console.debug("kvvfs to Object:",exp); close(); - db = new JDb('new-storage'); + const dbFileRaw = 'file:new-storage?vfs=kvvfs&transient=1'; + db = new DB(dbFileRaw); db.exec(sqlSetup); + const dbFilename = db.dbFilename(); + console.warn("db.dbFilename() =",dbFilename); T.assert(3 === db.selectValue('select count(*) from kvvfs')); - console.debug("kvvfs to Object:",exportDb(db.filename)); - const n = db.storageSize(); + console.debug("kvvfs to Object:",exportDb(dbFilename)); + const n = capi.sqlite3_js_kvvfs_size( dbFilename ); T.assert( n>0, "Db size count failed" ); if( 1 ){ // Concurrent open of that same name uses the same storage - const x = new JDb(db.filename); + const x = new JDb(dbFilename); T.assert(3 === db.selectValue('select count(*) from kvvfs')); x.close(); } @@ -2970,14 +2977,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule; // disappears... T.mustThrowMatching(function(){ /* Ensure that 'new-storage' was deleted when its refcount - went to 0. TODO is a way to tell these instances to - hang around after that, such that 'new-instance' could - be semi-persistent (until the page is reloaded). - */ - let ddb = new JDb('new-storage'); + went to 0, because of its 'transient' flag. By default + the objects are retained, like a filesystem would. */ + let ddb = new JDb(dbFilename); try{ddb.selectValue('select a from kvvfs')} finally{ddb.close()} - }, /.*no such table: kvvfs.*/); + }, /no such table: kvvfs/); }finally{ if( db ) db.close(); } @@ -2988,6 +2993,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; //predicate: ()=>false, test: function(sqlite3){ const filename = 'my'; + const DB = sqlite3.oo1.DB; const JDb = sqlite3.oo1.JsStorageDb; let db; let duo; @@ -2998,8 +3004,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule; const sqlCount = 'select count(*) from kvvfs'; try { const exportDb = capi.sqlite3_js_kvvfs_export_storage; - db = new JDb(filename); - db.clearStorage(/*must not throw*/); + const dbFileRaw = 'file:'+filename+'?vfs=kvvfs&transient=1'; + db = new DB(dbFileRaw); + capi.sqlite3_js_kvvfs_clear(filename); db.exec(sqlSetup); T.assert(3 === db.selectValue(sqlCount)); @@ -3021,7 +3028,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; duo = new JDb(filename); T.mustThrowMatching(()=>importDb(exp,true), /.*in use.*/); duo.close(); - importDb(exp); + importDb(exp, true); duo = new JDb(filename); T.assert(6 === duo.selectValue(sqlCount)); duo.close(); diff --git a/manifest b/manifest index cfded82acd..4db57235f2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\stypeof\scheck\sso\sthat\sit\sworks\sin\sWorker\sthreads. -D 2025-11-24T18:17:07.960 +C Add\skvvfs\sURI\sflag\stransient=1\sto\scause\sthe\sstorage\sobject\sto\sbe\sremoved\swhen\sthe\slast\shandle\sto\sthe\sdb\sis\sclosed.\sBy\sdefault\sit\sbehaves\slike\sa\sfilesystem\swould,\sretaining\sthe\sstorage\sobjects. +D 2025-11-24T20:14:23.332 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -594,13 +594,13 @@ F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8a F ext/wasm/api/post-js-header.js d24bd0d065f3489c8b78ddf3ead6321e5d047187a162cd503c41700e03dd1f06 F ext/wasm/api/pre-js.c-pp.js ad2546290e0c8ce5ca2081bff6e85cc25eeb904a3303921f1184290a7ff1b32f F ext/wasm/api/sqlite3-api-glue.c-pp.js 9b33e3ee467791dec4fd1b444b12a8545dfbb6c8b28ac651c7bdc7661a3b5a5c -F ext/wasm/api/sqlite3-api-oo1.c-pp.js 7d8850f754b4a9aecd5a4a92a357e05f720cd7f5cf962909343b40c914237256 +F ext/wasm/api/sqlite3-api-oo1.c-pp.js f3bc80f3d6b75456ea21c9ef8ad565e48376a49e2d15e1226a7a9b22edc2cd0e F ext/wasm/api/sqlite3-api-prologue.js 2ac62b41dd8d66859c86a6af126690851e5e557dad61ef59692389762c9bd2ed F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966 F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d -F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js a23002998d4cd2cdd4b6eb8884b399507d7160574bbe5665dfdfcbe53161d2ca +F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js 6a85c98875479f8630bede48c9f5fd1e23e2a073b4d00b968c77ae2b3ac98887 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3 F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86 @@ -647,7 +647,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb -F ext/wasm/tester1.c-pp.js eaa1ed5ee1e5e8f12b4cbd9462bc8f3841a1deff3232486f72cf7914c02a3c29 +F ext/wasm/tester1.c-pp.js c864070e09a140576ef96ab92117f152e167ea1fdb58da2a561fb2ecd2c5957f F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2178,8 +2178,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 03cb2a13c7f9081132390b5169cf22ffeced6ebdc5f01654360f951720310916 -R 7358b9115b27e85b1625d74ca82dd891 +P 3f2ed39e40e9c76c7d035b819d06c316ec4fba4e112b8a293822a8648147594b +R c4273f921b278213e0e850729894dac9 U stephan -Z 9ea3ccd4adc9fd767d4ce3fb6cf488b3 +Z 42d0fbf71d4bbf24651067a6368dccc7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6ecfc9201a..349ac0e0b3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f2ed39e40e9c76c7d035b819d06c316ec4fba4e112b8a293822a8648147594b +c6183261be77d03451aed563a5e4c19aa69706aa311cfdaf5db5e9b0a167d814 -- 2.47.3