Got the sqlite3-api JS bits wrapped up in deferred-load module. Whether that's going to be easier to use, in practice, remains to be seen. Consolidated two of the test-related JS files.

FossilOrigin-Name: dd83cc05f2522d221641807dd66b33df48ac9264f27e5b6f63f312084f109801
This commit is contained in:
stephan
2022-05-25 08:51:07 +00:00
parent e599cc427f
commit 26542e722b
9 changed files with 230 additions and 238 deletions
+6 -1
View File
@@ -1540,18 +1540,23 @@ $(fiddle_module_js): Makefile sqlite3.c shell.c \
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
$(fiddle_dir)/EXPORTED_FUNCTIONS.fiddle
emcc -o $@ $(emcc_flags) \
-sENVIRONMENT=web \
-sMODULARIZE \
-sEXPORT_NAME=initFiddleModule \
-sENVIRONMENT=web \
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.fiddle \
sqlite3.c shell.c
$(sqlite3_wasm_js): Makefile sqlite3.c \
$(fiddle_dir)/sqlite3-api.js \
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
$(fiddle_dir)/EXPORTED_FUNCTIONS.sqlite3-api
emcc -o $@ $(emcc_flags) \
-sENVIRONMENT=web \
-sMODULARIZE \
-sEXPORT_NAME=initSqlite3Module \
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.sqlite3-api \
--post-js=$(fiddle_dir)/sqlite3-api.js \
--no-entry \
sqlite3.c
fiddle: $(fiddle_module_js)
+2 -2
View File
@@ -1,10 +1,10 @@
# This GNU makefile exists primarily to simplify/speed up development
# from emacs. It is not part of the canonical build process.
default:
make -C ../.. wasm -e emcc_opt=-O0
$(MAKE) -C ../.. wasm -e emcc_opt=-O0
clean:
make -C ../../ clean-wasm
$(MAKE) -C ../../ clean-wasm
fiddle_files = emscripten.css fiddle.html \
fiddle.js fiddle-module.js \
+140 -50
View File
@@ -1,54 +1,144 @@
/**
Helpers for writing sqlite3-specific tests.
/*
2022-05-22
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 contains bootstrapping code used by various test scripts
which live in this file's directory.
*/
self/*window or worker*/.SqliteTestUtil = {
/** Running total of the number of tests run via
this API. */
counter: 0,
(function(){
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelectorAll(arguments[arguments.length-1]);
};
/* querySelector() proxy */
const E = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]);
};
/**
If expr is a function, it is called and its result
is returned, coerced to a bool, else expr, coerced to
a bool, is returned.
Helpers for writing sqlite3-specific tests.
*/
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.
self/*window or worker*/.SqliteTestUtil = {
/** Running total of the number of tests run via
this API. */
counter: 0,
/**
If expr is a function, it is called and its result
is returned, coerced to a bool, else expr, coerced to
a bool, is returned.
*/
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.
*/
assert: function f(expr, msg){
if(!f._){
f._ = ('undefined'===typeof abort
? (msg)=>{throw new Error(msg)}
: abort);
}
++this.counter;
if(!this.toBool(expr)){
f._(msg || "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){
++this.counter;
let err;
try{ f(); } catch(e){err=e;}
if(!err) throw new Error(msg || "Expected exception.");
return this;
},
/** Throws if expr is truthy or expr is a function and expr()
returns truthy. */
throwIf: function(expr, msg){
++this.counter;
if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
return this;
},
/** Throws if expr is falsy or expr is a function and expr()
returns falsy. */
throwUnless: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
return this;
}
};
/**
This is a module object for use with the emscripten-installed
initSqlite3Module() factory function.
*/
assert: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) abort(msg || "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){
++this.counter;
let err;
try{ f(); } catch(e){err=e;}
if(!err) throw new Error(msg || "Expected exception.");
return this;
},
/** Throws if expr is truthy or expr is a function and expr()
returns truthy. */
throwIf: function(expr, msg){
++this.counter;
if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
return this;
},
/** Throws if expr is falsy or expr is a function and expr()
returns falsy. */
throwUnless: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
return this;
}
};
self.sqlite3TestModule = {
postRun: [
/* function(theModule){...} */
],
//onRuntimeInitialized: function(){},
/* Proxy for C-side stdout output. */
print: function(){
console.log.apply(console, Array.prototype.slice.call(arguments));
},
/* Proxy for C-side stderr output. */
printErr: function(){
console.error.apply(console, Array.prototype.slice.call(arguments));
},
/**
Called by the module init bits to report loading
progress. It gets passed an empty argument when loading is
done (after onRuntimeInitialized() and any this.postRun
callbacks have been run).
*/
setStatus: function f(text){
if(!f.last){
f.last = { text: '', step: 0 };
f.ui = {
status: E('#module-status'),
progress: E('#module-progress'),
spinner: E('#module-spinner')
};
}
if(text === f.last.text) return;
f.last.text = text;
if(f.ui.progress){
f.ui.progress.value = f.last.step;
f.ui.progress.max = f.last.step + 1;
}
++f.last.step;
if(text) {
f.ui.status.classList.remove('hidden');
f.ui.status.innerText = text;
}else{
if(f.ui.progress){
f.ui.progress.remove();
f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
}
}
};
})(self/*window or worker*/);
+46 -36
View File
@@ -10,40 +10,45 @@
***********************************************************************
This file is intended to be loaded after loading sqlite3.wasm. It
sets up one of any number of potential bindings using that API, this
one as closely matching the C-native API as is feasible in JS.
This file is intended to be appended to the emcc-generated
sqlite3.js via emcc:
Note that this file is not named sqlite3.js because that file gets
generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
emcc ... -sMODULARIZE -sEXPORT_NAME=initSqlite3Module --post-js=THIS_FILE
This code installs an object named self.Module.sqlite3, where self
is expected to be either the global window or Worker object and
Module is the object set up by the emscripten infrastructure. The
sqlite3 object looks like:
It is loaded by importing the emcc-generated sqlite3.js, then:
{
api: bindings for much of the core sqlite3 APIs,
SQLite3: high-level OO API wrapper
}
initSqlite3Module({module object}).then(
function(theModule){
theModule.sqlite3 == an object containing this file's
deliverables:
{
api: bindings for much of the core sqlite3 APIs,
SQLite3: high-level OO API wrapper
}
});
The way we export this module is not _really_ modern-JS-friendly
because it exports/relies on a global symbol (which is admittedly
not ideal). Exporting it "cleanly," without introducing any
global-scope symbols, requires using a module loader in all client
code. As there are several different approaches, none of which this
developer is currently truly familiar with, the current approach
will have to do for the time being.
It is up to the caller to provide a module object compatible with
emcc, but it can be a plain empty object. The object passed to
initSqlite3Module() will get populated by the emscripten-generated
bits and, in part, by the code from this file. Specifically, this file
installs the `theModule.sqlite3` part shown above.
The resulting sqlite3.api object wraps the standard sqlite3 C API in
a way as close to its native form as JS allows for. The
sqlite3.SQLite3 object provides a higher-level wrapper more
appropriate for general client-side use in JS.
Because using certain parts of the low-level API properly requires
some degree of WASM-related magic, it is not recommended that that
API be used as-is in client-level code. Rather, client code should
use the higher-level OO API or write a custom wrapper on top of the
lower-level API. In short, using any C-style APIs which take
lower-level API. In short, most of the C-style API is used in an
intuitive manner from JS but any C-style APIs which take
pointers-to-pointer arguments require WASM-specific interfaces
installed by emcscripten-generated code. Those which take or return
only integers, doubles, strings, or "plain" pointers to db or
statement objects can be used in a straightforward manner.
statement objects can be used in "as normal," noting that "pointers"
in wasm are simply 32-bit integers.
# Goals and Non-goals of this API
@@ -77,7 +82,10 @@
modern web and requires modern platforms.
*/
(function(namespace){
if(!Module.postRun) Module.postRun = [];
/* ^^^^ the name Module is, in this setup, scope-local in the generated
file sqlite3.js, with which this file gets combined at build-time. */
Module.postRun.push(function(namespace){
'use strict';
/* For reference: sql.js does essentially everything we want and
it solves much of the wasm-related voodoo, but we'll need a
@@ -111,6 +119,8 @@
clear and much experimentation is pending.
*/
const SQM = namespace/*the sqlite module object */;
/**
Set up the main sqlite3 binding API here, mimicking the C API as
closely as we can.
@@ -157,7 +167,7 @@
will only support UTF8. */
SQLITE_UTF8: 1
};
const cwrap = Module.cwrap;
const cwrap = SQM.cwrap;
[/* C-side functions to bind. Each entry is an array with 3 or 4
elements:
@@ -237,7 +247,6 @@
const k = (4==a.length) ? a.shift() : a[0];
api[k] = cwrap.apply(this, a);
});
//console.debug("libversion =",api.sqlite3_libversion());
/* What follows is colloquially known as "OO API #1". It is a
binding of the sqlite3 API which is designed to be run within
@@ -418,7 +427,7 @@
delete that._statements[k];
if(s && s._pStmt) s.finalize();
});
Object.values(this._udfs).forEach(Module.removeFunction);
Object.values(this._udfs).forEach(SQM.removeFunction);
delete this._udfs;
delete this._statements;
delete this.filename;
@@ -562,7 +571,7 @@
(opt.callback && opt.rowMode)
? opt.rowMode : false);
try{
let pSql = allocateUTF8OnStack(arg.sql)
let pSql = SQM.allocateUTF8OnStack(arg.sql)
const pzTail = stackAlloc(4);
while(getValue(pSql, "i8")){
setValue(pPtrArg, 0, "i32");
@@ -731,9 +740,10 @@
api.sqlite3_result_null(pCx);
break;
}else if(undefined!==val.length){
const pBlob = Module.allocate(val, ALLOC_NORMAL);
const pBlob =
SQM.allocate(val, SQM.ALLOC_NORMAL);
api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/);
Module._free(blobptr);
SQM._free(blobptr);
break;
}
// else fall through
@@ -749,7 +759,7 @@
api.sqlite3_result_error(pCx, e.message, -1);
}
};
const pUdf = Module.addFunction(wrapper, "viii");
const pUdf = SQM.addFunction(wrapper, "viii");
let fFlags = 0;
if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC;
if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY;
@@ -762,11 +772,11 @@
api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
}catch(e){
Module.removeFunction(pUdf);
SQM.removeFunction(pUdf);
throw e;
}
if(this._udfs.hasOwnProperty(name)){
Module.removeFunction(this._udfs[name]);
SQM.removeFunction(this._udfs[name]);
}
this._udfs[name] = pUdf;
return this;
@@ -911,7 +921,7 @@
f._ = {
string: function(stmt, ndx, val, asBlob){
const bytes = intArrayFromString(val,true);
const pStr = Module.allocate(bytes, ALLOC_NORMAL);
const pStr = SQM.allocate(bytes, ALLOC_NORMAL);
stmt._allocs.push(pStr);
const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
@@ -950,7 +960,7 @@
toss("Binding a value as a blob requires",
"that it have a length member.");
}
const pBlob = Module.allocate(val, ALLOC_NORMAL);
const pBlob = SQM.allocate(val, ALLOC_NORMAL);
stmt._allocs.push(pBlob);
rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
}
@@ -966,7 +976,7 @@
const freeBindMemory = function(stmt){
let m;
while(undefined !== (m = stmt._allocs.pop())){
Module._free(m);
SQM._free(m);
}
return stmt;
};
@@ -1383,4 +1393,4 @@
api: api,
SQLite3
};
})(self/*worker or window*/.Module);
});
-121
View File
@@ -1,121 +0,0 @@
/*
2022-05-22
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 contains bootstrapping code used by various test scripts
which live in this file's directory.
*/
(function(){
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelectorAll(arguments[arguments.length-1]);
};
/* querySelector() proxy */
const E = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]);
};
/* emscripten-related bits... */
self.Module = {
/* ^^^ cannot declare that const because sqlite3.js
(auto-generated) includes a decl for it and runs in this
scope. */
preRun: [],
postRun: [],
//onRuntimeInitialized: function(){},
print: function(){
console.log.apply(console, Array.prototype.slice.call(arguments));
},
printErr: function(){
console.error.apply(console, Array.prototype.slice.call(arguments));
},
setStatus: function f(text){
if(!f.last){
f.last = { text: '', step: 0 };
f.ui = {
status: E('#module-status'),
progress: E('#module-progress'),
spinner: E('#module-spinner')
};
}
if(text === f.last.text) return;
f.last.text = text;
if(f.ui.progress){
f.ui.progress.value = f.last.step;
f.ui.progress.max = f.last.step + 1;
}
++f.last.step;
if(text) {
f.ui.status.classList.remove('hidden');
f.ui.status.innerText = text;
}else{
if(f.ui.progress){
f.ui.progress.remove();
f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
}
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
this.setStatus(left
? ('Preparing... (' + (this.totalDependencies-left)
+ '/' + this.totalDependencies + ')')
: 'All downloads complete.');
},
/**
Loads sqlite3-api.js and calls the given callback (if
provided), passing it an object:
{
api:sqlite3_c-like API wrapper,
SQLite3: OO wrapper
}
Whether this is synchronous or async depends on whether
it's run in the main thread (async) or a worker
(synchronous).
If called after the module has been loaded, it uses a
cached reference, noting that multiple async calls may end
up loading it multiple times.
*/
loadSqliteAPI: function f(callback){
const namespace = self.Module;
if(namespace.sqlite3){
if(callback) callback(namespace.sqlite3);
return;
}
const theScript = 'sqlite3-api.js';
if(self.importScripts){/*worker*/
importScripts(theScript);
if(callback) callback(namespace.sqlite3);
}else{/*main thread*/
new Promise((resolve, reject) => {
const script = document.createElement('script');
document.body.appendChild(script);
script.onload = resolve;
script.onerror = reject;
script.async = true;
script.src = theScript;
}).then(() => {
if(callback) callback(namespace.sqlite3);
});
}
}
};
})(self/*window or worker*/);
+2 -3
View File
@@ -26,9 +26,8 @@
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div>Everything on this page happens in the dev console.</div>
<script src="SqliteTestUtil.js"></script>
<script src="testing-common.js"></script>
<script src="testing1.js"></script>
<script src="sqlite3.js"></script>
<script src="SqliteTestUtil.js"></script>
<script src="testing1.js"></script>
</body>
</html>
+21 -11
View File
@@ -16,7 +16,14 @@
const T = self.SqliteTestUtil;
const log = console.log.bind(console);
const test1 = function(db,api){
const assert = function(condition, text) {
if (!condition) {
throw new Error('Assertion failed' + (text ? ': ' + text : ''));
}
};
const test1 = function(db,sqlite3){
const api = sqlite3.api;
log("Basic sanity tests...");
T.assert(db._pDb);
let st = db.prepare("select 3 as a");
@@ -114,13 +121,14 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
assert(null === db.selectValue("select $a",{$a:null}));
};
const runTests = function(namespace){
const runTests = function(Module){
T.assert(Module._free instanceof Function).
assert(Module.allocate instanceof Function).
assert(Module.addFunction instanceof Function).
assert(Module.removeFunction instanceof Function);
const api = namespace.api;
const oo = namespace.SQLite3;
const sqlite3 = Module.sqlite3;
const api = sqlite3.api;
const oo = sqlite3.SQLite3;
console.log("Loaded module:",api.sqlite3_libversion(),
api.sqlite3_sourceid());
log("Build options:",oo.compileOptionUsed());
@@ -129,17 +137,19 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
log("DB:",db.filename);
[
test1, testUDF
].forEach((f)=>f(db, api));
].forEach((f)=>f(db, sqlite3));
}finally{
db.close();
}
log("Total Test count:",T.counter);
};
self.Module.postRun.push(function(theModule){
/** Use a timeout so that we are (hopefully) out from under the
module init stack when our setup gets run. Just on principle,
not because we _need_ to be. */
setTimeout(()=>theModule.loadSqliteAPI(runTests), 0);
initSqlite3Module(self.sqlite3TestModule).then(function(theModule){
/** Use a timeout so that we are (hopefully) out from
under the module init stack when our setup gets
run. Just on principle, not because we _need_ to
be. */
//console.debug("theModule =",theModule);
setTimeout(()=>runTests(theModule), 0);
});
})(self/*window or worker*/);
})();
+12 -13
View File
@@ -1,9 +1,9 @@
C Renamed\sEXPORTED_FUNCTIONS.sqlite3\sto\sEXPORTED_FUNCTIONS.sqlite3-api\sto\savoid\sany\spotential\sconfusion\sabout\sthat\sfile\s(not)\sbeing\san\ssqlite3\sdatabase.
D 2022-05-25T04:38:35.081
C Got\sthe\ssqlite3-api\sJS\sbits\swrapped\sup\sin\sdeferred-load\smodule.\sWhether\sthat's\sgoing\sto\sbe\seasier\sto\suse,\sin\spractice,\sremains\sto\sbe\sseen.\sConsolidated\stwo\sof\sthe\stest-related\sJS\sfiles.
D 2022-05-25T08:51:07.626
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 335ac492dc602e7a7da3be3df020eac3b439a07cbdcf00a71b58304d7068257f
F Makefile.in 7ad689fdbe11297af09488b00c88b133b1505d6559bdad1f1829b6b7388c05be
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
@@ -56,20 +56,19 @@ F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 2f7c561af85e6d711fb42f395bc0074b6e6fcf16bc57d495ce4e1c3d0484c5d2
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api 540b9dec63a3a62a256e2f030827848a92e9b9d9b6fa5c0188295a4a1c5382cd w ext/fiddle/EXPORTED_FUNCTIONS.sqlite3
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api 540b9dec63a3a62a256e2f030827848a92e9b9d9b6fa5c0188295a4a1c5382cd
F ext/fiddle/EXPORTED_RUNTIME_METHODS b831017ba67ba993b34a27400cef2f6095bd6789c0fc4eba7e7a251c207be31c
F ext/fiddle/Makefile 2608fe0c56fa8f9cdf17e28d2be6def550a2fe987db5f7fc06d0210bfc868258
F ext/fiddle/SqliteTestUtil.js e3094833660a6ddd40766b802901b5861b37f0b89c6c577ee0ce4c9d36399e61
F ext/fiddle/Makefile de65d04bfb312e94dbd7a0e7d99fb126f0abc1db62f920159c4124b5a42347d8
F ext/fiddle/SqliteTestUtil.js 559731c3e8e0de330ec7d292e6c1846566408caee6637acc8a119ac338a8781c
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/fiddle/fiddle-worker.js 6000da12965319bed53d546f87885a6717a0cd8de0b4832edde7a95e63d1f33e
F ext/fiddle/fiddle.html 70796dc8a867448b41bc7e2c5fd6b1865ed8010b3abe22ba0678e8915c062e9a
F ext/fiddle/fiddle.js 45f96ac7f7d6678503568dded46afaa741d841a2464519035636da0fd77aec50
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/sqlite3-api.js ce08520b8117e4fbbbeb02d8d047defd4e8507d687e76d20a39f12401bad0219
F ext/fiddle/testing-common.js a2527fd8dfb500bad9b434ae2645bb91489792115ee1e1b4b53cac4e9198992a
F ext/fiddle/sqlite3-api.js 8500698d2163f4a25f8e5e6810ad826487342579d6a321d82b244dbc8e6f6db6
F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
F ext/fiddle/testing1.html 0a59d6cfb4cb16982c45ae3fabb1e090d122ca59285be97f3c01743eb75d6d73
F ext/fiddle/testing1.js a2cee7ee12c2e1756e775125b0f9950dc5e5faeeeb4979c6d9894626d90cb5d9
F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
F ext/fiddle/testing1.js 94a7597955c8fdbd15839a70d9b8279bc690205dda65ff175f688f13bf315745
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@@ -1969,8 +1968,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 e0c30438a4f1372afb93a0488bae17b3f85d535717b215f494a83ae909871d2c
R 9374645be385492e5cf981b1ed91722a
P 3d6245c6f9f2ef4ca6746639d300cc5795598b119034439dfed671de3da638fb
R 0cd793b0df7e1a958b5a7fb6c581456e
U stephan
Z 0e306e7b329adee899bf0b24535c7b65
Z 24d1fc51d53037dbed19059815815fc9
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
3d6245c6f9f2ef4ca6746639d300cc5795598b119034439dfed671de3da638fb
dd83cc05f2522d221641807dd66b33df48ac9264f27e5b6f63f312084f109801