Introducing JS worker1 promiser v2, which initializes via Promise (instead of a callback function) and can be loaded as an ESM module.

FossilOrigin-Name: 2fbaf2f51d37f70ee26d45f0c62f32c15a9e03f68b6d2e2892115e7dc028b929
This commit is contained in:
stephan 2024-03-07 19:29:53 +00:00
commit 67fc3e0ac2
9 changed files with 172 additions and 58 deletions

View File

@ -307,8 +307,9 @@ DISTCLEAN_FILES += $(bin.stripccomments)
########################################################################
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via ./c-pp -f
# $(1) ...
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via:
#
# ./c-pp -f $(1) -o $(2) $(3)
#
# Historical notes:
#
@ -452,13 +453,14 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
# the first OPFS VFS and necessarily an external file.
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
sqlite3-api.ext.jses += $(SOAP.js.bld)
#
# $(sqlite3-api.ext.jses) = API-related files which are standalone files,
# not part of the amalgamation.
#
sqlite3-api.ext.jses := $(SOAP.js.bld)
$(SOAP.js.bld): $(SOAP.js)
cp $< $@
all quick: $(sqlite3-api.ext.jses)
q: quick
########################################################################
# $(sqlite3-api*.*js) contain the core library code but not the
# Emscripten-related glue which deals with loading sqlite3.wasm. In
@ -825,13 +827,13 @@ pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
# $4 = resulting sqlite-api JS/MJS file
# $5 = resulting JS/MJS file
# $6 = -D... flags for $(bin.c-pp)
# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
# $7 = optional extra flags for emcc
#
# Maintenance reminder: be careful not to introduce spaces around args
# ($1, $2), otherwise string concatenation will malfunction.
#
# emcc.environment.$(2) must be set to a value for emcc's
# -sENVIRONMENT flag.
# Before calling this, emcc.environment.$(2) must be set to a value
# for emcc's -sENVIRONMENT flag.
#
# $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append
# CFLAGS to a given build mode.
@ -938,18 +940,39 @@ sqlite3-worker1.js.in := $(dir.api)/sqlite3-worker1.c-pp.js
sqlite3-worker1-promiser.js.in := $(dir.api)/sqlite3-worker1-promiser.c-pp.js
sqlite3-worker1.js := $(dir.dout)/sqlite3-worker1.js
sqlite3-worker1-promiser.js := $(dir.dout)/sqlite3-worker1-promiser.js
sqlite3-worker1-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs
sqlite3-worker1-promiser.mjs := $(dir.dout)/sqlite3-worker1-promiser.mjs
sqlite3-worker1-bundler-friendly.mjs := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs
sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.js),\
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\
$(c-pp.D.sqlite3-bundler-friendly)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\
$(sqlite3-worker1-promiser-bundler-friendly.js),\
$(c-pp.D.sqlite3-bundler-friendly)))
$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.js) \
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\
-Dtarget=es6-module -Dtarget=es6-bundler-friendly))
$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.mjs) \
$(sqlite3-worker1-promiser-bundler-friendly.js)
$(sqlite3.js) $(sqlite3.mjs): $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js)
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\
-Dtarget=es6-module))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\
-Dtarget=es6-module))
all: $(sqlite3-worker1.js) \
$(sqlite3-worker1-promiser.js) $(sqlite3-worker1-promiser.mjs)
demo-worker1-promiser.html: $(sqlite3-worker1-promiser.js) demo-worker1-promiser.js
demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs
all: demo-worker1-promiser.html demo-worker1-promiser-esm.html
sqlite3-api.ext.jses += \
$(sqlite3-worker1-promiser.mjs) \
$(sqlite3-worker1-bundler-friendly.mjs) \
$(sqlite3-worker1.js)
all quick: $(sqlite3-api.ext.jses)
q: quick
########################################################################
# batch-runner.js is part of one of the test apps which reads in SQL

View File

@ -42,9 +42,13 @@
- `onready` (optional, but...): this callback is called with no
arguments when the worker fires its initial
'sqlite3-api'/'worker1-ready' message, which it does when
sqlite3.initWorker1API() completes its initialization. This is
the simplest way to tell the worker to kick off work at the
earliest opportunity.
sqlite3.initWorker1API() completes its initialization. This is the
simplest way to tell the worker to kick off work at the earliest
opportunity, and the only way to know when the worker module has
completed loading. The irony of using a callback for this, instead
of returning a promise from sqlite3Worker1Promiser() is not lost on
the developers: see sqlite3Worker1Promiser.v2() which uses a
Promise instead.
- `onunhandled` (optional): a callback which gets passed the
message event object for any worker.onmessage() events which
@ -247,9 +251,10 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
return p;
};
}/*sqlite3Worker1Promiser()*/;
globalThis.sqlite3Worker1Promiser.defaultConfig = {
worker: function(){
//#if target=es6-bundler-friendly
//#if target=es6-module
return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{
type: 'module'
});
@ -270,14 +275,72 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = {
return new Worker(theJs + globalThis.location.search);
//#endif
}
//#ifnot target=es6-bundler-friendly
//#ifnot target=es6-module
.bind({
currentScript: globalThis?.document?.currentScript
})
//#endif
,
onerror: (...args)=>console.error('worker1 promiser error',...args)
};
}/*defaultConfig*/;
/**
sqlite3Worker1Promiser.v2() works identically to
sqlite3Worker1Promiser() except that it returns a Promise instead
of relying an an onready callback in the config object. The Promise
resolves to the same factory function which
sqlite3Worker1Promiser() returns.
If config is-a function or is an object which contains an onready
function, that function is replaced by a proxy which will resolve
after calling the original function and will reject if that
function throws.
*/
sqlite3Worker1Promiser.v2 = function(config){
let oldFunc;
if( 'function' == typeof config ){
oldFunc = config;
config = {};
}else if('function'===typeof config?.onready){
oldFunc = config.onready;
delete config.onready;
}
const promiseProxy = Object.create(null);
config = Object.assign((config || Object.create(null)),{
onready: async function(func){
try {
if( oldFunc ) await oldFunc(func);
promiseProxy.resolve(func);
}
catch(e){promiseProxy.reject(e)}
}
});
const p = new Promise(function(resolve,reject){
promiseProxy.resolve = resolve;
promiseProxy.reject = reject;
});
try{
this.original(config);
}catch(e){
promiseProxy.reject(e);
}
return p;
}.bind({
/* We do this because clients are
recommended to delete globalThis.sqlite3Worker1Promiser. */
original: sqlite3Worker1Promiser
});
//#if target=es6-module
/**
When built as a module, we export sqlite3Worker1Promiser.v2()
instead of sqlite3Worker1Promise() because (A) its interface is more
conventional for ESM usage and (B) the ESM option export option for
this API did not exist until v2 was created, so there's no backwards
incompatibility.
*/
export default sqlite3Worker1Promiser.v2;
//#endif /* target=es6-module */
//#else
/* Built with the omit-oo1 flag. */
//#endif ifnot omit-oo1

View File

@ -6,7 +6,11 @@
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="common/emscripten.css"/>
<link rel="stylesheet" href="common/testing.css"/>
//#if target=es6-module
<title>worker-promise (via ESM) tests</title>
//#else
<title>worker-promise tests</title>
//#endif
</head>
<body>
<header id='titlebar'><span>worker-promise tests</span></header>
@ -22,13 +26,17 @@
</figure>
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div>Most stuff on this page happens in the dev console.</div>
<hr>
<div id='test-output'></div>
<script src="common/SqliteTestUtil.js"></script>
//#if target=es6-module
<script src="demo-worker1-promiser.mjs" type="module"></script>
//#else
<script src="jswasm/sqlite3-worker1-promiser.js"></script>
<script src="demo-worker1-promiser.js"></script>
//#endif
</body>
</html>

View File

@ -13,9 +13,15 @@
Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based
proxy for for the sqlite3 Worker #1 API.
*/
'use strict';
(function(){
const T = self.SqliteTestUtil;
//#if target=es6-module
import {default as promiserFactory} from "./jswasm/sqlite3-worker1-promiser.mjs";
//#else
"use strict";
const promiserFactory = globalThis.sqlite3Worker1Promiser.v2;
delete globalThis.sqlite3Worker1Promiser;
//#endif
(async function(){
const T = globalThis.SqliteTestUtil;
const eOutput = document.querySelector('#test-output');
const warn = console.warn.bind(console);
const error = console.error.bind(console);
@ -33,33 +39,35 @@
logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
};
//why is this triggered even when we catch() a Promise?
//window.addEventListener('unhandledrejection', function(event) {
// warn('unhandledrejection',event);
//});
const promiserConfig = {
worker: ()=>{
const w = new Worker("jswasm/sqlite3-worker1.js");
w.onerror = (event)=>error("worker.onerror",event);
return w;
//#ifnot target=es6-module
/**
The v1 interfaces uses an onready function. The v2 interface optionally
accepts one but does not require it. If provided, it is called _before_
the promise is resolved, and the promise is rejected if onready() throws.
*/
onready: function(f){
/* f === the function returned by promiserFactory().
Ostensibly (f === workerPromise) but this function is
called before the promiserFactory() Promise resolves, so
before workerPromise is set. */
console.warn("This is the v2 interface - you don't need an onready() function.");
},
//#endif
debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args),
onunhandled: function(ev){
error("Unhandled worker message:",ev.data);
},
onready: function(){
T.affirm(arguments[0] === workerPromise
/* as of version 3.46. Prior to that this callback had no arguments */);
self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
runTests();
},
onerror: function(ev){
error("worker1 error:",ev);
}
};
const workerPromise = self.sqlite3Worker1Promiser(promiserConfig);
delete self.sqlite3Worker1Promiser;
const workerPromise = await promiserFactory(promiserConfig)
.then((func)=>{
console.log("Init complete. Starting tests momentarily.");
globalThis.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
return func;
});
const wtest = async function(msgType, msgArgs, callback){
if(2===arguments.length && 'function'===typeof msgArgs){
@ -273,5 +281,5 @@
}).finally(()=>logHtml('',"That's all, folks!"));
}/*runTests2()*/;
log("Init complete, but async init bits may still be running.");
runTests();
})();

View File

@ -49,12 +49,18 @@ dist.top.extras := \
tester1.js tester1.mjs \
demo-jsstorage.html demo-jsstorage.js \
demo-worker1.html demo-worker1.js \
demo-worker1-promiser.html demo-worker1-promiser.js
dist.jswasm.extras := $(sqlite3-api.ext.jses) $(sqlite3.wasm)
demo-worker1-promiser.html demo-worker1-promiser.js \
demo-worker1-promiser-esm.html demo-worker1-promiser.mjs
dist.jswasm.extras := $(sqlite3.wasm) \
$(sqlite3-api.ext.jses)
dist.common.extras := \
$(wildcard $(dir.common)/*.css) \
$(dir.common)/SqliteTestUtil.js
#$(info sqlite3-worker1-promiser.mjs = $(sqlite3-worker1-promiser.mjs))
#$(info sqlite3-worker1.js = $(sqlite3-worker1.js))
#$(info sqlite3-api.ext.jses = $(sqlite3-api.ext.jses))
#$(info dist.jswasm.extras = $(dist.jswasm.extras))
.PHONY: dist snapshot
# DIST_STRIP_COMMENTS $(call)able to be used in stripping C-style
# from the dist copies of certain files.
@ -67,7 +73,8 @@ endef
# STRIP_K1.js = list of JS files which need to be passed through
# $(bin.stripcomments) with a single -k flag.
STRIP_K1.js := $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) \
$(sqlite3-worker1-bundler-friendly.js) $(sqlite3-worker1-promiser-bundler-friendly.js)
$(sqlite3-worker1-bundler-friendly.js) \
$(sqlite3-api.ext.jses)
# STRIP_K2.js = list of JS files which need to be passed through
# $(bin.stripcomments) with two -k flags.
STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
@ -88,6 +95,7 @@ STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
dist: \
$(bin.stripccomments) $(bin.version-info) \
$(dist.build) $(STRIP_K1.js) $(STRIP_K2.js) \
$(dist.jswasm.extras) $(dist.common.extras) \
$(MAKEFILE) $(MAKEFILE.dist)
@echo "Making end-user deliverables..."
@rm -fr $(dist-dir.top)

View File

@ -97,6 +97,8 @@
wrapper is significantly easier to use, however.</li>
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
a demo of the Promise-based wrapper of the Worker1 API.</li>
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li>
</ul>
</li>
</ul>

View File

@ -84,6 +84,8 @@
wrapper is significantly easier to use, however.</li>
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
a demo of the Promise-based wrapper of the Worker1 API.</li>
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li>
</ul>
</li>
<li>speedtest1 ports (sqlite3's primary benchmarking tool)...

View File

@ -1,5 +1,5 @@
C Fix\sharmless\scompiler\swarnings\sin\stest\scode\sfor\sthe\sintck\sextension.
D 2024-03-07T15:58:06.345
C Introducing\sJS\sworker1\spromiser\sv2,\swhich\sinitializes\svia\sPromise\s(instead\sof\sa\scallback\sfunction)\sand\scan\sbe\sloaded\sas\san\sESM\smodule.
D 2024-03-07T19:29:53.171
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -587,7 +587,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt ca7e9ee82ca4e1c1744295f8184dd70edfae1992865d26c64303f539eb6c084c
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile 92e929315c3f1e0ea389fc9666b87a67a61fa1ecbe37e44c5ad226bda3bc6abe
F ext/wasm/GNUmakefile 4bb4cf70a8153dd5b5fee17d724075c54174da630b424bbcf48c744633396f62
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@ -616,7 +616,7 @@ F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 5a430874906ff3f4a6ca69aadf0c2aae
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js fe427645e1499618f5fa7bc670af850577d8bcc132df982078690c9bf8400baa
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js a2fcbc3fecdd0eea229283584ebc122f29d98194083675dbe5cb2cf3a17fe309
F ext/wasm/api/sqlite3-wasm.c d33a16495ca871781e78812d3a18fed78b797468fffee657b8d7199b277ff359
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js e8135b44a568badfe197e2379f6b42899f2240b5c3a77fa044331110f7ce8e50
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bd89edfe42a4d7122a6d6d405c5423cf00aabba1f76f6ea8e2dba9c628ddd91a
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
@ -632,19 +632,19 @@ F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b823
F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32e7be5bf
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 786ae8a3214c2a29f6fb2c80eb4f90cc401fcc5b524d95c35fdc66a454e32bad
F ext/wasm/demo-worker1-promiser.c-pp.html 635cf90685805e21772a5f7a35d1ace80f98a9ef7c42ff04d7a125ddca7e5db8 w ext/wasm/demo-worker1-promiser.html
F ext/wasm/demo-worker1-promiser.c-pp.js fcc628cb42fcfaf07d250477801de1e6deb1e319d003976612a0db8d76b9fccc w ext/wasm/demo-worker1-promiser.js
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
F ext/wasm/demo-worker1.js 836bece8615b17b1b572584f7b15912236a5947fe8c68b98d2737d7e287447ef
F ext/wasm/dist.make 3a851858aad72e246a5d9c5aaf6b6a144305f1bf898ac1846760ea7bab95c9a3
F ext/wasm/dist.make f2ce42305268fe33d4b50f6e4bb3daf4a60302a90736eee382f1b8af9ff32ec1
F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f
F ext/wasm/fiddle.make 3c2eace29255d6ddd219f5d8cc2728cb28b9fe717ea80b6062c2a6178947a16b
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce
F ext/wasm/fiddle/fiddle.js b444a5646a9aac9f3fc06c53d78af5e1912eb235d69a8e6010723e4eb0e9d4a1
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
F ext/wasm/index-dist.html e91d76e4581185238fd3d42ed86ec600f7023ed3e3a944c5c356f25304bf1263
F ext/wasm/index.html b31ce41c0da476d5ffcef23069b9d3415b419d65af5779096ebcfbcbade453a9
F ext/wasm/index-dist.html 564b5ec5669676482c5a25dea9e721d8eafed426ecb155f93d29aeff8507511f
F ext/wasm/index.html 4337f495416756802669f69f9f9f3df9f87ee4c1918e6718719b4b5718e4713a
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
F ext/wasm/jaccwabyt/jaccwabyt.md 59a20df389abcc3606eb4eaea7fb7ba14504beb3e345dbea9b99a0618ba3bec8
F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337
@ -2177,8 +2177,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 720ce06d93a9e4cc25c34c873c82165d8801f208c22701e51538f3210de84f65
R 78475c96717bed229515f020acd7cf0b
U drh
Z b05395e7bb390bd5a3c76ba7c1373e1f
P 7fbdc1a849af3440579459bbb8797ebc7f9cce7b34d95675b8baa82db194ea9c 9347d9b9a69277e40ea2f3ec6e1ff37ea19d24b4af80c6230b10624173f2f17c
R 45d7a69c537ca325c17486c899557483
U stephan
Z 1167a057df3ec3b4a36537840625ce5c
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
7fbdc1a849af3440579459bbb8797ebc7f9cce7b34d95675b8baa82db194ea9c
2fbaf2f51d37f70ee26d45f0c62f32c15a9e03f68b6d2e2892115e7dc028b929