From b95a826fd0a0b51083426089112d330f1f5e6b51 Mon Sep 17 00:00:00 2001 From: Orual Date: Wed, 17 Dec 2025 12:37:09 -0500 Subject: [PATCH] working through somee editor bugs, etc --- Cargo.lock | 2 + Cargo.toml | 2 +- build-workers.sh | 2 +- crates/weaver-app/Dockerfile | 2 +- crates/weaver-app/public/editor_worker.js | 650 +++++++++++++++++- crates/weaver-app/public/embed_worker.js | 245 ++++++- .../src/components/editor/component.rs | 33 +- .../src/components/editor/document.rs | 19 + .../weaver-app/src/components/editor/mod.rs | 4 +- .../src/components/editor/publish.rs | 50 +- .../src/components/editor/storage.rs | 81 ++- .../weaver-app/src/components/editor/sync.rs | 286 ++++++-- .../src/components/editor/worker.rs | 8 + crates/weaver-app/src/components/entry.rs | 3 +- crates/weaver-app/src/views/drafts.rs | 19 +- crates/weaver-app/src/views/home.rs | 7 +- crates/weaver-cli/src/main.rs | 2 +- crates/weaver-common/src/agent.rs | 124 +++- crates/weaver-index/Cargo.toml | 4 + .../clickhouse/040_draft_titles.sql | 22 + crates/weaver-index/src/bin/weaver_indexer.rs | 17 +- crates/weaver-index/src/clickhouse.rs | 4 +- crates/weaver-index/src/clickhouse/queries.rs | 2 +- .../src/clickhouse/queries/edit.rs | 191 ++++- .../src/clickhouse/queries/notebooks.rs | 4 +- crates/weaver-index/src/endpoints/edit.rs | 8 + crates/weaver-index/src/endpoints/notebook.rs | 19 +- crates/weaver-index/src/error.rs | 4 + crates/weaver-index/src/lib.rs | 2 + crates/weaver-index/src/tasks/draft_titles.rs | 380 ++++++++++ crates/weaver-index/src/tasks/mod.rs | 5 + weaver_notes/.obsidian/workspace.json | 11 +- weaver_notes/Untitled.md | 256 +++++++ weaver_notes/cargo_bloated.png | Bin 0 -> 182092 bytes 34 files changed, 2324 insertions(+), 144 deletions(-) create mode 100644 crates/weaver-index/migrations/clickhouse/040_draft_titles.sql create mode 100644 crates/weaver-index/src/tasks/draft_titles.rs create mode 100644 crates/weaver-index/src/tasks/mod.rs create mode 100644 weaver_notes/Untitled.md create mode 100644 weaver_notes/cargo_bloated.png diff --git a/Cargo.lock b/Cargo.lock index 257f768..a0dab6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11788,7 +11788,9 @@ dependencies = [ "jacquard-common", "jacquard-repo", "k256", + "loro", "miette 7.6.0", + "mini-moka 0.11.0", "n0-future 0.1.3", "rand 0.8.5", "regex", diff --git a/Cargo.toml b/Cargo.toml index a605e59..c63aea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ jacquard-repo = { git = "https://tangled.org/@nonbinary.computer/jacquard" } # [patch.crates-io] # #secp256k1-zkp = { git = "https://github.com/dpc/rust-secp256k1-zkp/", branch = "sanket-pr" } # ring = { git = "https://github.com/dpc/ring", rev = "5493e7e76d0d8fb1d3cbb0be9c4944700741b802" } - +loro = "1.9.1" [profile] diff --git a/build-workers.sh b/build-workers.sh index 2284298..89f5308 100755 --- a/build-workers.sh +++ b/build-workers.sh @@ -5,7 +5,7 @@ echo "==> Building worker WASMs" export RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo build -p weaver-app --bin editor_worker --bin embed_worker \ --target wasm32-unknown-unknown --release \ - --no-default-features --features "web","collab-worker" + --no-default-features --features "web","collab-worker","use-index" echo "==> Running wasm-bindgen" wasm-bindgen target/wasm32-unknown-unknown/release/editor_worker.wasm \ diff --git a/crates/weaver-app/Dockerfile b/crates/weaver-app/Dockerfile index 9982bd5..81f9825 100644 --- a/crates/weaver-app/Dockerfile +++ b/crates/weaver-app/Dockerfile @@ -32,7 +32,7 @@ RUN cp crates/weaver-app/.env-prod crates/weaver-app/.env # Build wasm workers RUN RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo build -p weaver-app --bin editor_worker --bin embed_worker \ --target wasm32-unknown-unknown --release \ - --no-default-features --features "web, use-index" + --no-default-features --features "web","collab-worker","use-index" # Run wasm-bindgen on workers RUN wasm-bindgen target/wasm32-unknown-unknown/release/editor_worker.wasm \ diff --git a/crates/weaver-app/public/editor_worker.js b/crates/weaver-app/public/editor_worker.js index d221a9a..95796b2 100644 --- a/crates/weaver-app/public/editor_worker.js +++ b/crates/weaver-app/public/editor_worker.js @@ -17,6 +17,71 @@ let wasm_bindgen; ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry(state => state.dtor(state.a, state.b)); + function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches && builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; + } + function getArrayU8FromWasm0(ptr, len) { ptr = ptr >>> 0; return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); @@ -167,18 +232,182 @@ let wasm_bindgen; let WASM_VECTOR_LEN = 0; - function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { - wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent______1_(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent______1_(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______3_(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______3_(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); } function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2) { wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2); } + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_CloseEvent__CloseEvent_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_CloseEvent__CloseEvent_____(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______2_(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______2_(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3); + } + + const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"]; + + const __wbindgen_enum_ReadableStreamType = ["bytes"]; + + const __wbindgen_enum_RequestCache = ["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]; + + const __wbindgen_enum_RequestCredentials = ["omit", "same-origin", "include"]; + + const __wbindgen_enum_RequestMode = ["same-origin", "no-cors", "cors", "navigate"]; + + const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0, 1)); + + const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0, 1)); + + const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0, 1)); + const JSOwnerFinalization = (typeof FinalizationRegistry === 'undefined') ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry(ptr => wasm.__wbg_jsowner_free(ptr >>> 0, 1)); + class IntoUnderlyingByteSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr, 0); + } + /** + * @returns {number} + */ + get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {ReadableByteStreamController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, controller); + return ret; + } + /** + * @param {ReadableByteStreamController} controller + */ + start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, controller); + } + /** + * @returns {ReadableStreamType} + */ + get type() { + const ret = wasm.intounderlyingbytesource_type(this.__wbg_ptr); + return __wbindgen_enum_ReadableStreamType[ret]; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingByteSource.prototype[Symbol.dispose] = IntoUnderlyingByteSource.prototype.free; + __exports.IntoUnderlyingByteSource = IntoUnderlyingByteSource; + + class IntoUnderlyingSink { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr, 0); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, reason); + return ret; + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return ret; + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, chunk); + return ret; + } + } + if (Symbol.dispose) IntoUnderlyingSink.prototype[Symbol.dispose] = IntoUnderlyingSink.prototype.free; + __exports.IntoUnderlyingSink = IntoUnderlyingSink; + + class IntoUnderlyingSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr, 0); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, controller); + return ret; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingSource.prototype[Symbol.dispose] = IntoUnderlyingSource.prototype.free; + __exports.IntoUnderlyingSource = IntoUnderlyingSource; + class JSOwner { __destroy_into_raw() { const ptr = this.__wbg_ptr; @@ -229,6 +458,18 @@ let wasm_bindgen; function __wbg_get_imports() { const imports = {}; imports.wbg = {}; + imports.wbg.__wbg___wbindgen_boolean_get_dea25b33882b895b = function(arg0) { + const v = arg0; + const ret = typeof(v) === 'boolean' ? v : undefined; + return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0; + }; + imports.wbg.__wbg___wbindgen_debug_string_adfb662ae34724b6 = function(arg0, arg1) { + const ret = debugString(arg1); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; imports.wbg.__wbg___wbindgen_is_function_8d400b8b1af978cd = function(arg0) { const ret = typeof(arg0) === 'function'; return ret; @@ -246,12 +487,56 @@ let wasm_bindgen; const ret = arg0 === undefined; return ret; }; + imports.wbg.__wbg___wbindgen_string_get_a2a31e16edf96e42 = function(arg0, arg1) { + const obj = arg1; + const ret = typeof(obj) === 'string' ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; imports.wbg.__wbg___wbindgen_throw_dd24417ed36fc46e = function(arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; imports.wbg.__wbg__wbg_cb_unref_87dfb5aaa0cbcea7 = function(arg0) { arg0._wbg_cb_unref(); }; + imports.wbg.__wbg_abort_07646c894ebbf2bd = function(arg0) { + arg0.abort(); + }; + imports.wbg.__wbg_abort_399ecbcfd6ef3c8e = function(arg0, arg1) { + arg0.abort(arg1); + }; + imports.wbg.__wbg_addEventListener_e792423147a80626 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); + }, arguments) }; + imports.wbg.__wbg_append_c5cbdf46455cc776 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + arg0.append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + }, arguments) }; + imports.wbg.__wbg_arrayBuffer_c04af4fce566092d = function() { return handleError(function (arg0) { + const ret = arg0.arrayBuffer(); + return ret; + }, arguments) }; + imports.wbg.__wbg_body_947b901c33f7fe32 = function(arg0) { + const ret = arg0.body; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_buffer_6cb2fecb1f253d71 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + imports.wbg.__wbg_byobRequest_f8e3517f5f8ad284 = function(arg0) { + const ret = arg0.byobRequest; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_byteLength_faa9938885bdeee6 = function(arg0) { + const ret = arg0.byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_3868b6a19ba01dea = function(arg0) { + const ret = arg0.byteOffset; + return ret; + }; imports.wbg.__wbg_call_3020136f7a2d6e44 = function() { return handleError(function (arg0, arg1, arg2) { const ret = arg0.call(arg1, arg2); return ret; @@ -260,9 +545,41 @@ let wasm_bindgen; const ret = arg0.call(arg1); return ret; }, arguments) }; + imports.wbg.__wbg_cancel_a65cf45dca50ba4c = function(arg0) { + const ret = arg0.cancel(); + return ret; + }; + imports.wbg.__wbg_catch_b9db41d97d42bd02 = function(arg0, arg1) { + const ret = arg0.catch(arg1); + return ret; + }; + imports.wbg.__wbg_clearTimeout_b716ecb44bea14ed = function(arg0) { + const ret = clearTimeout(arg0); + return ret; + }; + imports.wbg.__wbg_clearTimeout_f7a6c75a3d228439 = function() { return handleError(function (arg0, arg1) { + arg0.clearTimeout(arg1); + }, arguments) }; + imports.wbg.__wbg_close_0af5661bf3d335f2 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; imports.wbg.__wbg_close_0b472ca2d13f54f7 = function(arg0) { arg0.close(); }; + imports.wbg.__wbg_close_1db3952de1b5b1cf = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_close_3ec111e7b23d94d8 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_code_85a811fe6ca962be = function(arg0) { + const ret = arg0.code; + return ret; + }; + imports.wbg.__wbg_code_c2a85f2863ec11b3 = function(arg0) { + const ret = arg0.code; + return ret; + }; imports.wbg.__wbg_crypto_574e78ad8b13b65f = function(arg0) { const ret = arg0.crypto; return ret; @@ -271,6 +588,13 @@ let wasm_bindgen; const ret = arg0.data; return ret; }; + imports.wbg.__wbg_done_62ea16af4ce34b24 = function(arg0) { + const ret = arg0.done; + return ret; + }; + imports.wbg.__wbg_enqueue_a7e6b1ee87963aad = function() { return handleError(function (arg0, arg1) { + arg0.enqueue(arg1); + }, arguments) }; imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { let deferred0_0; let deferred0_1; @@ -282,9 +606,74 @@ let wasm_bindgen; wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); } }; + imports.wbg.__wbg_fetch_7fb7602a1bf647ec = function(arg0) { + const ret = fetch(arg0); + return ret; + }; + imports.wbg.__wbg_fetch_90447c28cc0b095e = function(arg0, arg1) { + const ret = arg0.fetch(arg1); + return ret; + }; + imports.wbg.__wbg_getRandomValues_1c61fac11405ffdc = function() { return handleError(function (arg0, arg1) { + globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); + }, arguments) }; imports.wbg.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) { arg0.getRandomValues(arg1); }, arguments) }; + imports.wbg.__wbg_getReader_48e00749fe3f6089 = function() { return handleError(function (arg0) { + const ret = arg0.getReader(); + return ret; + }, arguments) }; + imports.wbg.__wbg_get_af9dab7e9603ea93 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_get_done_f98a6e62c4e18fb9 = function(arg0) { + const ret = arg0.done; + return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0; + }; + imports.wbg.__wbg_get_value_63e39884ef11812e = function(arg0) { + const ret = arg0.value; + return ret; + }; + imports.wbg.__wbg_has_0e670569d65d3a45 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.has(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_headers_654c30e1bcccc552 = function(arg0) { + const ret = arg0.headers; + return ret; + }; + imports.wbg.__wbg_instanceof_ArrayBuffer_f3320d2419cd0355 = function(arg0) { + let result; + try { + result = arg0 instanceof ArrayBuffer; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_instanceof_Blob_e9c51ce33a4b6181 = function(arg0) { + let result; + try { + result = arg0 instanceof Blob; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_instanceof_Response_cd74d1c2ac92cb0b = function(arg0) { + let result; + try { + result = arg0 instanceof Response; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; imports.wbg.__wbg_instanceof_Window_b5cf7783caa68180 = function(arg0) { let result; try { @@ -295,6 +684,10 @@ let wasm_bindgen; const ret = result; return ret; }; + imports.wbg.__wbg_iterator_27b7c8b35ab3e86b = function() { + const ret = Symbol.iterator; + return ret; + }; imports.wbg.__wbg_length_22ac23eaec9d8053 = function(arg0) { const ret = arg0.length; return ret; @@ -340,14 +733,67 @@ let wasm_bindgen; wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); } }, arguments) }; + imports.wbg.__wbg_message_a4e9a39ee8f92b17 = function(arg0, arg1) { + const ret = arg1.message; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) { const ret = arg0.msCrypto; return ret; }; + imports.wbg.__wbg_new_1ba21ce319a06297 = function() { + const ret = new Object(); + return ret; + }; + imports.wbg.__wbg_new_25f239778d6112b9 = function() { + const ret = new Array(); + return ret; + }; + imports.wbg.__wbg_new_3c79b3bb1b32b7d3 = function() { return handleError(function () { + const ret = new Headers(); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_6421f6084cc5bc5a = function(arg0) { + const ret = new Uint8Array(arg0); + return ret; + }; + imports.wbg.__wbg_new_7c30d1f874652e62 = function() { return handleError(function (arg0, arg1) { + const ret = new WebSocket(getStringFromWasm0(arg0, arg1)); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_881a222c65f168fc = function() { return handleError(function () { + const ret = new AbortController(); + return ret; + }, arguments) }; imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { const ret = new Error(); return ret; }; + imports.wbg.__wbg_new_df1173567d5ff028 = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_ff12d2b041fb48f1 = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return ret; + } finally { + state0.a = state0.b = 0; + } + }; imports.wbg.__wbg_new_from_slice_f9c22b9153b26992 = function(arg0, arg1) { const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1)); return ret; @@ -356,14 +802,42 @@ let wasm_bindgen; const ret = new Function(getStringFromWasm0(arg0, arg1)); return ret; }; + imports.wbg.__wbg_new_with_byte_offset_and_length_d85c3da1fd8df149 = function(arg0, arg1, arg2) { + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); + return ret; + }; imports.wbg.__wbg_new_with_length_aa5eaf41d35235e5 = function(arg0) { const ret = new Uint8Array(arg0 >>> 0); return ret; }; + imports.wbg.__wbg_new_with_str_and_init_c5748f76f5108934 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = new Request(getStringFromWasm0(arg0, arg1), arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_with_str_sequence_073466a4a5387941 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = new WebSocket(getStringFromWasm0(arg0, arg1), arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_next_138a17bbf04e926c = function(arg0) { + const ret = arg0.next; + return ret; + }; + imports.wbg.__wbg_next_3cfe5c0fe2a4cc53 = function() { return handleError(function (arg0) { + const ret = arg0.next(); + return ret; + }, arguments) }; imports.wbg.__wbg_node_905d3e251edff8a2 = function(arg0) { const ret = arg0.node; return ret; }; + imports.wbg.__wbg_now_2c95c9de01293173 = function(arg0) { + const ret = arg0.now(); + return ret; + }; + imports.wbg.__wbg_now_69d776cd24f5215b = function() { + const ret = Date.now(); + return ret; + }; imports.wbg.__wbg_now_8cf15d6e317793e1 = function(arg0) { const ret = arg0.now(); return ret; @@ -372,6 +846,10 @@ let wasm_bindgen; const ret = Date.now(); return ret; }; + imports.wbg.__wbg_performance_7a3ffd0b17f663ad = function(arg0) { + const ret = arg0.performance; + return ret; + }; imports.wbg.__wbg_performance_c77a440eff2efd9b = function(arg0) { const ret = arg0.performance; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); @@ -386,6 +864,10 @@ let wasm_bindgen; imports.wbg.__wbg_prototypesetcall_dfe9b766cdc1f1fd = function(arg0, arg1, arg2) { Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2); }; + imports.wbg.__wbg_push_7d9be8f38fc13975 = function(arg0, arg1) { + const ret = arg0.push(arg1); + return ret; + }; imports.wbg.__wbg_queueMicrotask_9b549dfce8865860 = function(arg0) { const ret = arg0.queueMicrotask; return ret; @@ -396,6 +878,27 @@ let wasm_bindgen; imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) { arg0.randomFillSync(arg1); }, arguments) }; + imports.wbg.__wbg_read_39c4b35efcd03c25 = function(arg0) { + const ret = arg0.read(); + return ret; + }; + imports.wbg.__wbg_readyState_9d0976dcad561aa9 = function(arg0) { + const ret = arg0.readyState; + return ret; + }; + imports.wbg.__wbg_reason_d4eb9e40592438c2 = function(arg0, arg1) { + const ret = arg1.reason; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_releaseLock_a5912f590b185180 = function(arg0) { + arg0.releaseLock(); + }; + imports.wbg.__wbg_removeEventListener_54bf92f4a849bd7d = function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3); + }, arguments) }; imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () { const ret = module.require; return ret; @@ -404,9 +907,72 @@ let wasm_bindgen; const ret = Promise.resolve(arg0); return ret; }; + imports.wbg.__wbg_respond_9f7fc54636c4a3af = function() { return handleError(function (arg0, arg1) { + arg0.respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_send_7cc36bb628044281 = function() { return handleError(function (arg0, arg1, arg2) { + arg0.send(getStringFromWasm0(arg1, arg2)); + }, arguments) }; + imports.wbg.__wbg_send_ea59e150ab5ebe08 = function() { return handleError(function (arg0, arg1, arg2) { + arg0.send(getArrayU8FromWasm0(arg1, arg2)); + }, arguments) }; + imports.wbg.__wbg_setTimeout_4302406184dcc5be = function(arg0, arg1) { + const ret = setTimeout(arg0, arg1); + return ret; + }; + imports.wbg.__wbg_setTimeout_ceaa8eadc563d26e = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.setTimeout(arg1, arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_set_169e13b608078b7b = function(arg0, arg1, arg2) { + arg0.set(getArrayU8FromWasm0(arg1, arg2)); + }; + imports.wbg.__wbg_set_binaryType_73e8c75df97825f8 = function(arg0, arg1) { + arg0.binaryType = __wbindgen_enum_BinaryType[arg1]; + }; + imports.wbg.__wbg_set_body_8e743242d6076a4f = function(arg0, arg1) { + arg0.body = arg1; + }; + imports.wbg.__wbg_set_cache_0e437c7c8e838b9b = function(arg0, arg1) { + arg0.cache = __wbindgen_enum_RequestCache[arg1]; + }; + imports.wbg.__wbg_set_credentials_55ae7c3c106fd5be = function(arg0, arg1) { + arg0.credentials = __wbindgen_enum_RequestCredentials[arg1]; + }; + imports.wbg.__wbg_set_handle_event_14baa3949ef6909d = function(arg0, arg1) { + arg0.handleEvent = arg1; + }; + imports.wbg.__wbg_set_headers_5671cf088e114d2b = function(arg0, arg1) { + arg0.headers = arg1; + }; + imports.wbg.__wbg_set_method_76c69e41b3570627 = function(arg0, arg1, arg2) { + arg0.method = getStringFromWasm0(arg1, arg2); + }; + imports.wbg.__wbg_set_mode_611016a6818fc690 = function(arg0, arg1) { + arg0.mode = __wbindgen_enum_RequestMode[arg1]; + }; + imports.wbg.__wbg_set_onclose_032729b3d7ed7a9e = function(arg0, arg1) { + arg0.onclose = arg1; + }; + imports.wbg.__wbg_set_onerror_7819daa6af176ddb = function(arg0, arg1) { + arg0.onerror = arg1; + }; imports.wbg.__wbg_set_onmessage_5fe29d0fb54cb575 = function(arg0, arg1) { arg0.onmessage = arg1; }; + imports.wbg.__wbg_set_onmessage_71321d0bed69856c = function(arg0, arg1) { + arg0.onmessage = arg1; + }; + imports.wbg.__wbg_set_onopen_6d4abedb27ba5656 = function(arg0, arg1) { + arg0.onopen = arg1; + }; + imports.wbg.__wbg_set_signal_e89be862d0091009 = function(arg0, arg1) { + arg0.signal = arg1; + }; + imports.wbg.__wbg_signal_3c14fbdc89694b39 = function(arg0) { + const ret = arg0.signal; + return ret; + }; imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { const ret = arg1.stack; const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -430,31 +996,74 @@ let wasm_bindgen; const ret = typeof window === 'undefined' ? null : window; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; + imports.wbg.__wbg_status_9bfc680efca4bdfd = function(arg0) { + const ret = arg0.status; + return ret; + }; + imports.wbg.__wbg_stringify_655a6390e1f5eb6b = function() { return handleError(function (arg0) { + const ret = JSON.stringify(arg0); + return ret; + }, arguments) }; imports.wbg.__wbg_subarray_845f2f5bce7d061a = function(arg0, arg1, arg2) { const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); return ret; }; + imports.wbg.__wbg_then_429f7caf1026411d = function(arg0, arg1, arg2) { + const ret = arg0.then(arg1, arg2); + return ret; + }; imports.wbg.__wbg_then_4f95312d68691235 = function(arg0, arg1) { const ret = arg0.then(arg1); return ret; }; + imports.wbg.__wbg_url_b6d11838a4f95198 = function(arg0, arg1) { + const ret = arg1.url; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_url_df28eef824b04410 = function(arg0, arg1) { + const ret = arg1.url; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_value_57b7b035e117f7ee = function(arg0) { + const ret = arg0.value; + return ret; + }; imports.wbg.__wbg_versions_c01dfd4722a88165 = function(arg0) { const ret = arg0.versions; return ret; }; + imports.wbg.__wbg_view_788aaf149deefd2f = function(arg0) { + const ret = arg0.view; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_wasClean_4154a2d59fdb4dd7 = function(arg0) { + const ret = arg0.wasClean; + return ret; + }; imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) { // Cast intrinsic for `Ref(String) -> Externref`. const ret = getStringFromWasm0(arg0, arg1); return ret; }; - imports.wbg.__wbindgen_cast_3fda284bdcf7704e = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 941, function: Function { arguments: [Externref], shim_idx: 942, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__wasm_bindgen_1add006a0ed82fd3___JsValue____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____); + imports.wbg.__wbindgen_cast_4d50504c7960c7db = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 5089, function: Function { arguments: [], shim_idx: 5090, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output________2_, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______3_); return ret; }; - imports.wbg.__wbindgen_cast_56c1ebb4e8528c2a = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 132, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 133, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. - const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____); + imports.wbg.__wbindgen_cast_4f4fadff60f85046 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 4976, function: Function { arguments: [], shim_idx: 4977, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output________1_, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______2_); + return ret; + }; + imports.wbg.__wbindgen_cast_75c82a975d6b40cd = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 5269, function: Function { arguments: [Externref], shim_idx: 5270, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__wasm_bindgen_1add006a0ed82fd3___JsValue____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____); return ret; }; imports.wbg.__wbindgen_cast_cb9088102bce6b30 = function(arg0, arg1) { @@ -462,6 +1071,31 @@ let wasm_bindgen; const ret = getArrayU8FromWasm0(arg0, arg1); return ret; }; + imports.wbg.__wbindgen_cast_ccbfef0f15d2f2b2 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 3405, function: Function { arguments: [NamedExternref("CloseEvent")], shim_idx: 3406, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__web_sys_1984c39bba2ffe3a___features__gen_CloseEvent__CloseEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_CloseEvent__CloseEvent_____); + return ret; + }; + imports.wbg.__wbindgen_cast_d086004c1192dab2 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 3461, function: Function { arguments: [], shim_idx: 3462, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. + const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_); + return ret; + }; + imports.wbg.__wbindgen_cast_d9e01159fa505611 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 3438, function: Function { arguments: [], shim_idx: 3439, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); + return ret; + }; + imports.wbg.__wbindgen_cast_ec4f04da93f4f5b8 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 3959, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 3960, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent______1_); + return ret; + }; + imports.wbg.__wbindgen_cast_f7b350875fdc9e82 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 145, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 146, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. + const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____); + return ret; + }; imports.wbg.__wbindgen_init_externref_table = function() { const table = wasm.__wbindgen_externrefs; const offset = table.grow(4); diff --git a/crates/weaver-app/public/embed_worker.js b/crates/weaver-app/public/embed_worker.js index 0501146..ff61832 100644 --- a/crates/weaver-app/public/embed_worker.js +++ b/crates/weaver-app/public/embed_worker.js @@ -232,12 +232,12 @@ let wasm_bindgen; let WASM_VECTOR_LEN = 0; - function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1) { - wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1); + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); } - function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { - wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1); } function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1) { @@ -248,16 +248,148 @@ let wasm_bindgen; wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2); } + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3); + } + + const __wbindgen_enum_ReadableStreamType = ["bytes"]; + const __wbindgen_enum_RequestCache = ["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]; const __wbindgen_enum_RequestCredentials = ["omit", "same-origin", "include"]; const __wbindgen_enum_RequestMode = ["same-origin", "no-cors", "cors", "navigate"]; + const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0, 1)); + + const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0, 1)); + + const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0, 1)); + const JSOwnerFinalization = (typeof FinalizationRegistry === 'undefined') ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry(ptr => wasm.__wbg_jsowner_free(ptr >>> 0, 1)); + class IntoUnderlyingByteSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr, 0); + } + /** + * @returns {number} + */ + get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {ReadableByteStreamController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, controller); + return ret; + } + /** + * @param {ReadableByteStreamController} controller + */ + start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, controller); + } + /** + * @returns {ReadableStreamType} + */ + get type() { + const ret = wasm.intounderlyingbytesource_type(this.__wbg_ptr); + return __wbindgen_enum_ReadableStreamType[ret]; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingByteSource.prototype[Symbol.dispose] = IntoUnderlyingByteSource.prototype.free; + __exports.IntoUnderlyingByteSource = IntoUnderlyingByteSource; + + class IntoUnderlyingSink { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr, 0); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, reason); + return ret; + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return ret; + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, chunk); + return ret; + } + } + if (Symbol.dispose) IntoUnderlyingSink.prototype[Symbol.dispose] = IntoUnderlyingSink.prototype.free; + __exports.IntoUnderlyingSink = IntoUnderlyingSink; + + class IntoUnderlyingSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr, 0); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, controller); + return ret; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingSource.prototype[Symbol.dispose] = IntoUnderlyingSource.prototype.free; + __exports.IntoUnderlyingSource = IntoUnderlyingSource; + class JSOwner { __destroy_into_raw() { const ptr = this.__wbg_ptr; @@ -355,6 +487,26 @@ let wasm_bindgen; const ret = arg0.arrayBuffer(); return ret; }, arguments) }; + imports.wbg.__wbg_buffer_6cb2fecb1f253d71 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + imports.wbg.__wbg_byobRequest_f8e3517f5f8ad284 = function(arg0) { + const ret = arg0.byobRequest; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_byteLength_faa9938885bdeee6 = function(arg0) { + const ret = arg0.byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_3868b6a19ba01dea = function(arg0) { + const ret = arg0.byteOffset; + return ret; + }; + imports.wbg.__wbg_call_3020136f7a2d6e44 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.call(arg1, arg2); + return ret; + }, arguments) }; imports.wbg.__wbg_call_abb4ff46ce38be40 = function() { return handleError(function (arg0, arg1) { const ret = arg0.call(arg1); return ret; @@ -362,13 +514,19 @@ let wasm_bindgen; imports.wbg.__wbg_clearTimeout_15dfc3d1dcb635c6 = function() { return handleError(function (arg0, arg1) { arg0.clearTimeout(arg1); }, arguments) }; - imports.wbg.__wbg_clearTimeout_3b6a9a13d4bde075 = function(arg0) { + imports.wbg.__wbg_clearTimeout_b716ecb44bea14ed = function(arg0) { const ret = clearTimeout(arg0); return ret; }; + imports.wbg.__wbg_close_0af5661bf3d335f2 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; imports.wbg.__wbg_close_0b472ca2d13f54f7 = function(arg0) { arg0.close(); }; + imports.wbg.__wbg_close_3ec111e7b23d94d8 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; imports.wbg.__wbg_data_8bf4ae669a78a688 = function(arg0) { const ret = arg0.data; return ret; @@ -377,6 +535,9 @@ let wasm_bindgen; const ret = arg0.done; return ret; }; + imports.wbg.__wbg_enqueue_a7e6b1ee87963aad = function() { return handleError(function (arg0, arg1) { + arg0.enqueue(arg1); + }, arguments) }; imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { let deferred0_0; let deferred0_1; @@ -388,16 +549,12 @@ let wasm_bindgen; wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); } }; - imports.wbg.__wbg_fetch_90447c28cc0b095e = function(arg0, arg1) { - const ret = arg0.fetch(arg1); - return ret; - }; - imports.wbg.__wbg_fetch_df3fa17a5772dafb = function(arg0) { + imports.wbg.__wbg_fetch_7fb7602a1bf647ec = function(arg0) { const ret = fetch(arg0); return ret; }; - imports.wbg.__wbg_getTime_ad1e9878a735af08 = function(arg0) { - const ret = arg0.getTime(); + imports.wbg.__wbg_fetch_90447c28cc0b095e = function(arg0, arg1) { + const ret = arg0.fetch(arg1); return ret; }; imports.wbg.__wbg_get_af9dab7e9603ea93 = function() { return handleError(function (arg0, arg1) { @@ -440,10 +597,6 @@ let wasm_bindgen; const ret = arg0.length; return ret; }; - imports.wbg.__wbg_new_0_23cedd11d9b40c9d = function() { - const ret = new Date(); - return ret; - }; imports.wbg.__wbg_new_1ba21ce319a06297 = function() { const ret = new Object(); return ret; @@ -464,6 +617,28 @@ let wasm_bindgen; const ret = new Error(); return ret; }; + imports.wbg.__wbg_new_df1173567d5ff028 = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_ff12d2b041fb48f1 = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return ret; + } finally { + state0.a = state0.b = 0; + } + }; imports.wbg.__wbg_new_from_slice_f9c22b9153b26992 = function(arg0, arg1) { const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1)); return ret; @@ -472,6 +647,10 @@ let wasm_bindgen; const ret = new Function(getStringFromWasm0(arg0, arg1)); return ret; }; + imports.wbg.__wbg_new_with_byte_offset_and_length_d85c3da1fd8df149 = function(arg0, arg1, arg2) { + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); + return ret; + }; imports.wbg.__wbg_new_with_str_and_init_c5748f76f5108934 = function() { return handleError(function (arg0, arg1, arg2) { const ret = new Request(getStringFromWasm0(arg0, arg1), arg2); return ret; @@ -517,7 +696,10 @@ let wasm_bindgen; const ret = Promise.resolve(arg0); return ret; }; - imports.wbg.__wbg_setTimeout_35a07631c669fbee = function(arg0, arg1) { + imports.wbg.__wbg_respond_9f7fc54636c4a3af = function() { return handleError(function (arg0, arg1) { + arg0.respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_setTimeout_4302406184dcc5be = function(arg0, arg1) { const ret = setTimeout(arg0, arg1); return ret; }; @@ -525,6 +707,9 @@ let wasm_bindgen; const ret = arg0.setTimeout(arg1, arg2); return ret; }, arguments) }; + imports.wbg.__wbg_set_169e13b608078b7b = function(arg0, arg1, arg2) { + arg0.set(getArrayU8FromWasm0(arg1, arg2)); + }; imports.wbg.__wbg_set_body_8e743242d6076a4f = function(arg0, arg1) { arg0.body = arg1; }; @@ -607,28 +792,32 @@ let wasm_bindgen; const ret = arg0.value; return ret; }; - imports.wbg.__wbindgen_cast_067e3c0104867449 = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 225, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 226, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. - const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_8b37e013ecf136cb___features__gen_MessageEvent__MessageEvent_____); - return ret; + imports.wbg.__wbg_view_788aaf149deefd2f = function(arg0) { + const ret = arg0.view; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) { // Cast intrinsic for `Ref(String) -> Externref`. const ret = getStringFromWasm0(arg0, arg1); return ret; }; - imports.wbg.__wbindgen_cast_4c40eebfb345262b = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 1169, function: Function { arguments: [], shim_idx: 1170, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); + imports.wbg.__wbindgen_cast_6e929a04fb6adb86 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 389, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 390, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. + const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____); return ret; }; - imports.wbg.__wbindgen_cast_ba06f0048889102c = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 2202, function: Function { arguments: [Externref], shim_idx: 2203, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + imports.wbg.__wbindgen_cast_a206bfcc4c940d5e = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 2181, function: Function { arguments: [Externref], shim_idx: 2182, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__wasm_bindgen_1add006a0ed82fd3___JsValue____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____); return ret; }; - imports.wbg.__wbindgen_cast_dd8568660229206d = function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { dtor_idx: 1434, function: Function { arguments: [], shim_idx: 1435, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + imports.wbg.__wbindgen_cast_e6dcebc0071508b8 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1096, function: Function { arguments: [], shim_idx: 1097, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); + return ret; + }; + imports.wbg.__wbindgen_cast_ee2795d3f0e223fa = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1359, function: Function { arguments: [], shim_idx: 1360, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output________1_, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_); return ret; }; diff --git a/crates/weaver-app/src/components/editor/component.rs b/crates/weaver-app/src/components/editor/component.rs index fc31122..e4b6d97 100644 --- a/crates/weaver-app/src/components/editor/component.rs +++ b/crates/weaver-app/src/components/editor/component.rs @@ -93,11 +93,36 @@ pub fn MarkdownEditor( let draft_key = draft_key.clone(); let entry_uri = parsed_uri.clone(); let initial_content = initial_content.clone(); + let target_notebook = target_notebook.clone(); async move { + // Resolve target_notebook to a URI if provided + let notebook_uri: Option = if let Some(ref title) = target_notebook { + if let Some(did) = fetcher.current_did().await { + let ident = jacquard::types::ident::AtIdentifier::Did(did); + match fetcher.get_notebook(ident, title.clone()).await { + Ok(Some(notebook_data)) => { + Some(notebook_data.0.uri.to_smolstr()) + } + Ok(None) | Err(_) => { + tracing::debug!("Could not resolve notebook '{}' to URI", title); + None + } + } + } else { + None + } + } else { + None + }; + match load_and_merge_document(&fetcher, &draft_key, entry_uri.as_ref()).await { - Ok(Some(state)) => { + Ok(Some(mut state)) => { tracing::debug!("Loaded merged document state"); + // If we resolved a notebook URI and state doesn't have one, use it + if state.notebook_uri.is_none() { + state.notebook_uri = notebook_uri; + } return LoadResult::Loaded(state); } Ok(None) => { @@ -302,6 +327,7 @@ pub fn MarkdownEditor( synced_version: None, // Fresh from entry, never synced last_seen_diffs: std::collections::HashMap::new(), resolved_content, + notebook_uri: notebook_uri.clone(), }); } Err(e) => { @@ -327,6 +353,7 @@ pub fn MarkdownEditor( synced_version: None, // New doc, never synced last_seen_diffs: std::collections::HashMap::new(), resolved_content: weaver_common::ResolvedContent::default(), + notebook_uri, }) } Err(e) => { @@ -732,6 +759,7 @@ fn MarkdownEditorInner( cursor_offset, editing_uri, editing_cid, + notebook_uri, export_ms, encode_ms, } => { @@ -744,6 +772,7 @@ fn MarkdownEditorInner( cursor_offset, editing_uri, editing_cid, + notebook_uri, }; let write_start = crate::perf::now(); let _ = gloo_storage::LocalStorage::set( @@ -848,6 +877,7 @@ fn MarkdownEditorInner( let cursor_offset = doc.cursor.read().offset; let editing_uri = doc.entry_ref().map(|r| r.uri.to_smolstr()); let editing_cid = doc.entry_ref().map(|r| r.cid.to_smolstr()); + let notebook_uri = doc.notebook_uri(); let sink_clone = worker_sink.clone(); @@ -865,6 +895,7 @@ fn MarkdownEditorInner( cursor_offset, editing_uri, editing_cid, + notebook_uri, }) .await; } diff --git a/crates/weaver-app/src/components/editor/document.rs b/crates/weaver-app/src/components/editor/document.rs index 8f1c4cc..228819e 100644 --- a/crates/weaver-app/src/components/editor/document.rs +++ b/crates/weaver-app/src/components/editor/document.rs @@ -23,6 +23,7 @@ use loro::{ use jacquard::IntoStatic; use jacquard::from_json_value; +use jacquard::smol_str::SmolStr; use jacquard::types::string::AtUri; use weaver_api::com_atproto::repo::strong_ref::StrongRef; use weaver_api::sh_weaver::embed::images::Image; @@ -91,6 +92,9 @@ pub struct EditorDocument { /// Signal so cloned docs share the same state after publish. pub entry_ref: Signal>>, + /// AT-URI of the notebook this draft belongs to (for re-publishing) + pub notebook_uri: Signal>, + // --- Edit sync state (for PDS sync) --- /// StrongRef to the sh.weaver.edit.root record for this edit session. /// None if we haven't synced to PDS yet. @@ -235,6 +239,8 @@ pub struct LoadedDocState { /// Pre-resolved embed content fetched during load. /// Avoids embed pop-in on initial render. pub resolved_content: weaver_common::ResolvedContent, + /// Notebook URI for re-publishing to the same notebook. + pub notebook_uri: Option, } impl PartialEq for LoadedDocState { @@ -316,6 +322,7 @@ impl EditorDocument { tags, embeds, entry_ref: Signal::new(None), + notebook_uri: Signal::new(None), edit_root: Signal::new(None), last_diff: Signal::new(None), last_synced_version: Signal::new(None), @@ -492,6 +499,16 @@ impl EditorDocument { self.entry_ref.set(entry); } + /// Get the notebook URI if this draft belongs to a notebook. + pub fn notebook_uri(&self) -> Option { + self.notebook_uri.read().clone() + } + + /// Set the notebook URI for re-publishing to the same notebook. + pub fn set_notebook_uri(&mut self, uri: Option) { + self.notebook_uri.set(uri); + } + // --- Tags accessors --- /// Get all tags as a vector of strings. @@ -1090,6 +1107,7 @@ impl EditorDocument { tags, embeds, entry_ref: Signal::new(None), + notebook_uri: Signal::new(None), edit_root: Signal::new(None), last_diff: Signal::new(None), last_synced_version: Signal::new(None), @@ -1148,6 +1166,7 @@ impl EditorDocument { tags, embeds, entry_ref: Signal::new(state.entry_ref), + notebook_uri: Signal::new(state.notebook_uri), edit_root: Signal::new(state.edit_root), last_diff: Signal::new(state.last_diff), // Use the synced version from state (tracks the PDS version vector) diff --git a/crates/weaver-app/src/components/editor/mod.rs b/crates/weaver-app/src/components/editor/mod.rs index 4a78ce9..be1609d 100644 --- a/crates/weaver-app/src/components/editor/mod.rs +++ b/crates/weaver-app/src/components/editor/mod.rs @@ -58,8 +58,8 @@ pub use writer::{EditorImageResolver, ImageResolver, SyntaxSpanInfo, SyntaxType, // Storage #[allow(unused_imports)] pub use storage::{ - DRAFT_KEY_PREFIX, EditorSnapshot, clear_all_drafts, delete_draft, list_drafts, - load_from_storage, load_snapshot_from_storage, save_to_storage, + DRAFT_KEY_PREFIX, EditorSnapshot, clear_all_drafts, delete_draft, delete_draft_from_pds, + list_drafts, load_from_storage, load_snapshot_from_storage, save_to_storage, }; // Sync diff --git a/crates/weaver-app/src/components/editor/publish.rs b/crates/weaver-app/src/components/editor/publish.rs index 76c232d..9d6d5f7 100644 --- a/crates/weaver-app/src/components/editor/publish.rs +++ b/crates/weaver-app/src/components/editor/publish.rs @@ -4,6 +4,7 @@ use dioxus::prelude::*; use jacquard::cowstr::ToCowStr; +use jacquard::smol_str::ToSmolStr; use jacquard::types::collection::Collection; use jacquard::types::ident::AtIdentifier; use jacquard::types::recordkey::RecordKey; @@ -252,12 +253,43 @@ pub async fn publish_entry( .maybe_embeds(entry_embeds) .build(); + // Check if we have a stored notebook URI (for re-publishing to same notebook) + // This avoids duplicate notebook creation when re-publishing + let (notebook_uri, entry_refs) = if let Some(stored_uri) = doc.notebook_uri() { + // Try to fetch notebook directly by URI to avoid duplicate creation + match client.get_notebook_by_uri(&stored_uri).await { + Ok(Some((uri, refs))) => { + tracing::debug!("Found notebook by stored URI: {}", uri); + (uri, refs) + } + Ok(None) | Err(_) => { + // Stored URI invalid or notebook deleted, fall back to title lookup + tracing::warn!("Stored notebook URI invalid, falling back to title lookup"); + let (did, _) = client + .session_info() + .await + .ok_or_else(|| WeaverError::InvalidNotebook("Not authenticated".into()))?; + client.upsert_notebook(notebook, &did).await? + } + } + } else { + // No stored URI, use title-based lookup/creation + let (did, _) = client + .session_info() + .await + .ok_or_else(|| WeaverError::InvalidNotebook("Not authenticated".into()))?; + client.upsert_notebook(notebook, &did).await? + }; + // Pass existing rkey if re-publishing (to allow title changes without creating new entry) let doc_entry_ref = doc.entry_ref(); let existing_rkey = doc_entry_ref.as_ref().and_then(|r| r.uri.rkey()); - let (entry_ref, was_created) = client - .upsert_entry( - notebook, + + // Use upsert_entry_with_notebook since we already have notebook data + let (entry_ref, notebook_uri_final, was_created) = client + .upsert_entry_with_notebook( + notebook_uri, + entry_refs, &doc.title(), entry, existing_rkey.map(|r| r.0.as_str()), @@ -268,6 +300,9 @@ pub async fn publish_entry( // Set entry_ref so subsequent publishes update this record doc.set_entry_ref(Some(entry_ref)); + // Store the notebook URI for future re-publishing + doc.set_notebook_uri(Some(notebook_uri_final.to_smolstr())); + if was_created { PublishResult::Created(uri) } else { @@ -288,11 +323,9 @@ pub async fn publish_entry( // Check if we're the owner or a collaborator let owner_did = match existing_ref.uri.authority() { AtIdentifier::Did(d) => d.clone(), - AtIdentifier::Handle(h) => fetcher - .client - .resolve_handle(h) - .await - .map_err(|e| WeaverError::InvalidNotebook(format!("Failed to resolve handle: {}", e)))?, + AtIdentifier::Handle(h) => fetcher.client.resolve_handle(h).await.map_err(|e| { + WeaverError::InvalidNotebook(format!("Failed to resolve handle: {}", e)) + })?, }; let is_collaborator = owner_did != current_did; @@ -304,6 +337,7 @@ pub async fn publish_entry( .title(doc.title()) .path(path) .created_at(Datetime::now()) + .updated_at(Datetime::now()) .maybe_tags(tags) .maybe_embeds(entry_embeds) .build(); diff --git a/crates/weaver-app/src/components/editor/storage.rs b/crates/weaver-app/src/components/editor/storage.rs index e0a0bcd..bb3ed6c 100644 --- a/crates/weaver-app/src/components/editor/storage.rs +++ b/crates/weaver-app/src/components/editor/storage.rs @@ -22,9 +22,9 @@ use gloo_storage::{LocalStorage, Storage}; use jacquard::IntoStatic; use jacquard::smol_str::{SmolStr, ToSmolStr}; use jacquard::types::string::{AtUri, Cid}; -use weaver_api::com_atproto::repo::strong_ref::StrongRef; use loro::cursor::Cursor; use serde::{Deserialize, Serialize}; +use weaver_api::com_atproto::repo::strong_ref::StrongRef; use super::document::EditorDocument; @@ -71,6 +71,10 @@ pub struct EditorSnapshot { /// CID of the entry if editing an existing entry #[serde(default, skip_serializing_if = "Option::is_none")] pub editing_cid: Option, + + /// AT-URI of the notebook this draft belongs to (for re-publishing) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub notebook_uri: Option, } /// Build the full storage key from a draft key. @@ -104,6 +108,7 @@ pub fn save_to_storage( cursor_offset: doc.cursor.read().offset, editing_uri: doc.entry_ref().map(|r| r.uri.to_smolstr()), editing_cid: doc.entry_ref().map(|r| r.cid.to_smolstr()), + notebook_uri: doc.notebook_uri(), }; let write_start = crate::perf::now(); @@ -151,6 +156,9 @@ pub fn load_from_storage(key: &str) -> Option { // Verify the content matches (sanity check) if doc.content() == snapshot.content { doc.set_entry_ref(entry_ref.clone()); + if let Some(notebook_uri) = snapshot.notebook_uri { + doc.set_notebook_uri(Some(notebook_uri)); + } return Some(doc); } tracing::warn!("Snapshot content mismatch, falling back to text content"); @@ -162,6 +170,9 @@ pub fn load_from_storage(key: &str) -> Option { doc.cursor.write().offset = snapshot.cursor_offset.min(doc.len_chars()); doc.sync_loro_cursor(); doc.set_entry_ref(entry_ref); + if let Some(notebook_uri) = snapshot.notebook_uri { + doc.set_notebook_uri(Some(notebook_uri)); + } Some(doc) } @@ -171,6 +182,8 @@ pub struct LocalSnapshotData { pub snapshot: Vec, /// Entry StrongRef if editing an existing entry pub entry_ref: Option>, + /// Notebook URI for re-publishing + pub notebook_uri: Option, } /// Load snapshot data from LocalStorage (WASM only). @@ -201,6 +214,7 @@ pub fn load_snapshot_from_storage(key: &str) -> Option { Some(LocalSnapshotData { snapshot: snapshot_bytes, entry_ref, + notebook_uri: snapshot.notebook_uri, }) } @@ -250,6 +264,71 @@ pub fn list_drafts() -> Vec<(String, String, Option)> { drafts } +/// Delete a draft stub record from PDS. +/// +/// This deletes the sh.weaver.edit.draft record, making the draft +/// invisible in listDrafts. Edit history (edit.root, edit.diff) is +/// preserved for potential recovery. +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +pub async fn delete_draft_from_pds( + fetcher: &crate::fetch::Fetcher, + draft_key: &str, +) -> Result<(), weaver_common::WeaverError> { + use jacquard::prelude::XrpcClient; + use jacquard::types::ident::AtIdentifier; + use jacquard::types::recordkey::RecordKey; + use jacquard::types::string::Nsid; + use weaver_api::com_atproto::repo::delete_record::DeleteRecord; + + // Only delete if authenticated + let Some(did) = fetcher.current_did().await else { + tracing::debug!("Not authenticated, skipping PDS draft deletion"); + return Ok(()); + }; + + // Extract rkey from draft_key + let rkey_str = if let Some(tid) = draft_key.strip_prefix("new:") { + tid.to_string() + } else if draft_key.starts_with("at://") { + draft_key.split('/').last().unwrap_or(draft_key).to_string() + } else { + draft_key.to_string() + }; + + let rkey = RecordKey::any(&rkey_str) + .map_err(|e| weaver_common::WeaverError::InvalidNotebook(e.to_string()))?; + + // Build the delete request + let request = DeleteRecord::new() + .repo(AtIdentifier::Did(did)) + .collection(Nsid::new_static("sh.weaver.edit.draft").unwrap()) + .rkey(rkey) + .build(); + + // Execute deletion + let client = fetcher.get_client(); + match client.send(request).await { + Ok(_) => { + tracing::info!("Deleted draft stub from PDS: {}", draft_key); + Ok(()) + } + Err(e) => { + // Log but don't fail - draft may not exist on PDS + tracing::warn!("Failed to delete draft from PDS (may not exist): {}", e); + Ok(()) + } + } +} + +/// Non-WASM stub for delete_draft_from_pds +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] +pub async fn delete_draft_from_pds( + _fetcher: &crate::fetch::Fetcher, + _draft_key: &str, +) -> Result<(), weaver_common::WeaverError> { + Ok(()) +} + /// Clear all editor drafts from LocalStorage (WASM only). #[cfg(all(target_family = "wasm", target_os = "unknown"))] #[allow(dead_code)] diff --git a/crates/weaver-app/src/components/editor/sync.rs b/crates/weaver-app/src/components/editor/sync.rs index 0515eea..09c5bbb 100644 --- a/crates/weaver-app/src/components/editor/sync.rs +++ b/crates/weaver-app/src/components/editor/sync.rs @@ -239,6 +239,42 @@ fn build_doc_ref( } } +/// Convert a DocRef to an entry_ref StrongRef. +/// +/// For EntryRef: returns the entry's StrongRef directly +/// For DraftRef: parses the draft_key as AT-URI, fetches the draft record to get CID, builds StrongRef +/// For NotebookRef: returns the notebook's StrongRef +async fn doc_ref_to_entry_ref( + fetcher: &Fetcher, + doc_ref: &DocRef<'_>, +) -> Option> { + match &doc_ref.value { + DocRefValue::EntryRef(entry_ref) => Some(entry_ref.entry.clone().into_static()), + DocRefValue::DraftRef(draft_ref) => { + // draft_key contains the canonical AT-URI: at://{did}/sh.weaver.edit.draft/{rkey} + let draft_uri = AtUri::new(&draft_ref.draft_key).ok()?.into_static(); + + // Fetch the draft record to get its CID + match fetcher.client.get_record::(&draft_uri).await { + Ok(response) => { + let output = response.into_output().ok()?; + let cid = output.cid?.into_static(); + Some(StrongRef::new().uri(draft_uri).cid(cid).build()) + } + Err(e) => { + tracing::warn!("Failed to fetch draft record for entry_ref: {}", e); + None + } + } + } + DocRefValue::NotebookRef(notebook_ref) => Some(notebook_ref.notebook.clone().into_static()), + DocRefValue::Unknown(_) => { + tracing::warn!("Unknown DocRefValue variant, cannot convert to entry_ref"); + None + } + } +} + /// Result of a sync operation. #[derive(Clone, Debug)] pub enum SyncResult { @@ -629,19 +665,30 @@ pub async fn find_diffs_for_root( Ok(all_diffs) } +/// Result of creating an edit root, includes optional draft stub info. +pub struct CreateRootResult { + /// The root record URI + pub root_uri: AtUri<'static>, + /// The root record CID + pub root_cid: Cid<'static>, + /// Draft stub StrongRef if this was a new draft (not editing published entry) + pub draft_ref: Option>, +} + /// Create the edit root record for an entry. /// /// Uploads the current Loro snapshot as a blob and creates an `sh.weaver.edit.root` /// record referencing the entry (or draft key if unpublished). /// /// For drafts, also creates the `sh.weaver.edit.draft` stub record first. +/// Returns the draft stub info so caller can set entry_ref. pub async fn create_edit_root( fetcher: &Fetcher, doc: &EditorDocument, draft_key: &str, entry_uri: Option<&AtUri<'_>>, entry_cid: Option<&Cid<'_>>, -) -> Result<(AtUri<'static>, Cid<'static>), WeaverError> { +) -> Result { let client = fetcher.get_client(); let did = fetcher .current_did() @@ -649,22 +696,56 @@ pub async fn create_edit_root( .ok_or_else(|| WeaverError::InvalidNotebook("Not authenticated".into()))?; // For drafts, create the stub record first (makes it discoverable via listRecords) - if entry_uri.is_none() { + let draft_ref: Option> = if entry_uri.is_none() { let rkey = extract_draft_rkey(draft_key); - // Try to create draft stub, ignore if it already exists + // Try to create draft stub, or get existing one match create_draft_stub(fetcher, &did, &rkey).await { - Ok((uri, _cid)) => { + Ok((uri, cid)) => { tracing::debug!("Created draft stub: {}", uri); + Some(StrongRef::new().uri(uri).cid(cid).build()) } Err(e) => { - // Check if it's a "record already exists" error - that's fine + // Check if it's a "record already exists" error let err_str = e.to_string(); - if !err_str.contains("RecordAlreadyExists") && !err_str.contains("already exists") { + if err_str.contains("RecordAlreadyExists") || err_str.contains("already exists") { + // Draft exists - fetch it to get the CID + let draft_uri_str = format!("at://{}/{}/{}", did, DRAFT_NSID, rkey); + if let Ok(draft_uri) = AtUri::new(&draft_uri_str) { + if let Ok(response) = + fetcher.get_client().get_record::(&draft_uri).await + { + if let Ok(output) = response.into_output() { + if let Some(cid) = output.cid { + Some( + StrongRef::new() + .uri(draft_uri.into_static()) + .cid(cid.into_static()) + .build(), + ) + } else { + tracing::warn!("Draft exists but has no CID"); + None + } + } else { + tracing::warn!("Draft exists but couldn't parse output"); + None + } + } else { + tracing::warn!("Draft exists but couldn't fetch record"); + None + } + } else { + None + } + } else { tracing::warn!("Failed to create draft stub (continuing anyway): {}", e); + None } } } - } + } else { + None // Published entry, not a draft + }; // Export full snapshot let snapshot = doc.export_snapshot(); @@ -708,7 +789,11 @@ pub async fn create_edit_root( .into_output() .map_err(|e| WeaverError::InvalidNotebook(e.to_string()))?; - Ok((output.uri.into_static(), output.cid.into_static())) + Ok(CreateRootResult { + root_uri: output.uri.into_static(), + root_cid: output.cid.into_static(), + draft_ref, + }) } /// Create a diff record with updates since the last sync. @@ -827,7 +912,7 @@ pub async fn sync_to_pds( if doc.edit_root().is_none() { // First sync - create root let create_start = crate::perf::now(); - let (root_uri, root_cid) = create_edit_root( + let result = create_edit_root( fetcher, doc, draft_key, @@ -839,8 +924,8 @@ pub async fn sync_to_pds( // Build StrongRef for the root let root_ref = StrongRef::new() - .uri(root_uri.clone()) - .cid(root_cid.clone()) + .uri(result.root_uri.clone()) + .cid(result.root_cid.clone()) .build(); // Update document state @@ -848,12 +933,20 @@ pub async fn sync_to_pds( doc.set_last_diff(None); doc.mark_synced(); + // For drafts: set entry_ref to the draft record (enables draft discovery/recovery) + if let Some(draft_ref) = result.draft_ref { + if doc.entry_ref().is_none() { + tracing::debug!("Setting entry_ref to draft: {}", draft_ref.uri); + doc.set_entry_ref(Some(draft_ref)); + } + } + let total_ms = crate::perf::now() - fn_start; tracing::debug!(total_ms, create_ms, "sync_to_pds: created root"); Ok(SyncResult::CreatedRoot { - uri: root_uri, - cid: root_cid, + uri: result.root_uri, + cid: result.root_cid, }) } else { // Subsequent sync - create diff @@ -916,6 +1009,8 @@ pub struct PdsEditState { /// Last seen diff URI per collaborator root (for incremental sync). /// Maps root URI -> last diff URI we've imported from that root. pub last_seen_diffs: std::collections::HashMap, AtUri<'static>>, + /// The DocRef from the root record (tells us what's being edited) + pub doc_ref: DocRef<'static>, } /// Fetch a blob from the PDS. @@ -1006,6 +1101,7 @@ pub async fn load_all_edit_states_from_pds( let merged_doc = LoroDoc::new(); let mut our_root_ref: Option> = None; let mut our_last_diff_ref: Option> = None; + let mut merged_doc_ref: Option> = None; let mut updated_last_seen = last_seen_diffs.clone(); // Get current user's DID to identify "our" root for sync state tracking @@ -1054,6 +1150,11 @@ pub async fn load_all_edit_states_from_pds( updated_last_seen.insert(uri.clone(), last_diff.uri.clone().into_static()); } + // Track doc_ref from the first root we process (they should all match) + if merged_doc_ref.is_none() { + merged_doc_ref = Some(pds_state.doc_ref.clone()); + } + // Track "our" root/diff refs for sync state (used when syncing back) // We want to track our own edit.root so subsequent diffs go to the right place let is_our_root = current_did.as_ref().is_some_and(|did| root_did == *did); @@ -1089,6 +1190,7 @@ pub async fn load_all_edit_states_from_pds( root_snapshot: merged_snapshot.into(), diff_updates: vec![], // Already merged into snapshot last_seen_diffs: updated_last_seen, + doc_ref: merged_doc_ref.expect("Should have at least one doc_ref if we have a root"), })) } @@ -1131,6 +1233,9 @@ async fn load_edit_state_from_root_id( .cid(root_cid.into_static()) .build(); + // Extract the DocRef from the root record + let doc_ref = root_output.value.doc.into_static(); + // Fetch the root snapshot blob let root_snapshot = fetch_blob( fetcher, @@ -1149,6 +1254,7 @@ async fn load_edit_state_from_root_id( root_snapshot, diff_updates: vec![], last_seen_diffs: std::collections::HashMap::new(), + doc_ref, })); } @@ -1170,9 +1276,14 @@ async fn load_edit_state_from_root_id( } } - let diff_uri = AtUri::new(&format_smolstr!("at://{}/{}/{}", diff_id.did, DIFF_NSID, rkey_str)) - .map_err(|e| WeaverError::InvalidNotebook(format!("Invalid diff URI: {}", e)))? - .into_static(); + let diff_uri = AtUri::new(&format_smolstr!( + "at://{}/{}/{}", + diff_id.did, + DIFF_NSID, + rkey_str + )) + .map_err(|e| WeaverError::InvalidNotebook(format!("Invalid diff URI: {}", e)))? + .into_static(); let diff_response = fetcher .client @@ -1226,6 +1337,7 @@ async fn load_edit_state_from_root_id( root_snapshot, diff_updates, last_seen_diffs: std::collections::HashMap::new(), + doc_ref, })) } @@ -1291,6 +1403,7 @@ pub async fn load_and_merge_document( synced_version: None, // Local-only, never synced to PDS last_seen_diffs: std::collections::HashMap::new(), resolved_content, + notebook_uri: local.notebook_uri, // Restored from localStorage })) } @@ -1314,17 +1427,24 @@ pub async fn load_and_merge_document( // Capture the version after loading all PDS state - this is our sync baseline let synced_version = Some(doc.oplog_vv()); + // Reconstruct entry_ref from the DocRef stored in edit.root + let entry_ref = doc_ref_to_entry_ref(fetcher, &pds.doc_ref).await; + if entry_ref.is_some() { + tracing::debug!("Reconstructed entry_ref from PDS DocRef"); + } + let resolved_content = prefetch_embeds_from_doc(&doc, fetcher, owner_ident.as_deref()).await; Ok(Some(LoadedDocState { doc, - entry_ref: None, // Entry ref comes from the entry itself, not edit state + entry_ref, edit_root: Some(pds.root_ref), last_diff: pds.last_diff_ref, synced_version, // Just loaded from PDS, fully synced last_seen_diffs: pds.last_seen_diffs, resolved_content, + notebook_uri: None, // PDS-only, notebook context comes from target_notebook })) } @@ -1377,6 +1497,7 @@ pub async fn load_and_merge_document( synced_version: Some(pds_version), last_seen_diffs: pds.last_seen_diffs, resolved_content, + notebook_uri: local.notebook_uri, // Restored from localStorage })) } } @@ -1413,8 +1534,8 @@ pub struct SyncStatusProps { pub document: EditorDocument, /// Draft key for this document pub draft_key: String, - /// Auto-sync interval in milliseconds (0 to disable) - #[props(default = 30_000)] + /// Auto-sync interval in milliseconds (0 to disable, default disabled) + #[props(default = 0)] pub auto_sync_interval_ms: u32, /// Callback to refresh/reload document from collaborators #[props(default)] @@ -1427,11 +1548,22 @@ pub struct SyncStatusProps { /// Sync status indicator with auto-sync functionality. /// /// Displays the current sync state and automatically syncs to PDS periodically. +/// Initially shows "Start syncing" until user activates sync, then auto-syncs. #[component] pub fn SyncStatus(props: SyncStatusProps) -> Element { let fetcher = use_context::(); let auth_state = use_context::>(); + let doc = props.document.clone(); + let draft_key = props.draft_key.clone(); + + // Sync activated - true if sync has been started (either manually or doc already has edit_root) + // Once activated, auto-sync is enabled + let mut sync_activated = use_signal(|| { + // If document already has an edit_root, syncing is already active + props.document.edit_root().is_some() + }); + // Sync state management let mut sync_state = use_signal(|| { if props.document.has_unsynced_changes() { @@ -1442,32 +1574,27 @@ pub fn SyncStatus(props: SyncStatusProps) -> Element { }); let mut last_error: Signal> = use_signal(|| None); - let doc = props.document.clone(); - let draft_key = props.draft_key.clone(); - // Check if we're authenticated (drafts can sync via DraftRef even without entry) let is_authenticated = auth_state.read().is_authenticated(); // Auto-sync trigger signal - set to true to trigger a sync let mut trigger_sync = use_signal(|| false); - // Auto-sync timer - triggers sync when there are unsynced changes + // Auto-sync timer - only triggers after sync has been activated { - let auto_sync_interval_ms = props.auto_sync_interval_ms; let doc_for_check = doc.clone(); - dioxus_sdk::time::use_interval( - std::time::Duration::from_millis(auto_sync_interval_ms as u64), - move |_| { - if auto_sync_interval_ms == 0 { - return; - } - // Only trigger if there are unsynced changes - if doc_for_check.has_unsynced_changes() { - trigger_sync.set(true); - } - }, - ); + // Use 30s interval for auto-sync once activated + dioxus_sdk::time::use_interval(std::time::Duration::from_secs(30), move |_| { + // Only auto-sync if activated + if !*sync_activated.peek() { + return; + } + // Only trigger if there are unsynced changes + if doc_for_check.has_unsynced_changes() { + trigger_sync.set(true); + } + }); } // Collaborator poll timer - checks for collaborator updates periodically @@ -1572,6 +1699,11 @@ pub fn SyncStatus(props: SyncStatusProps) -> Element { Ok(_) => { sync_state.set(SyncState::Synced); last_error.set(None); + // Activate auto-sync after first successful sync + if !*sync_activated.peek() { + sync_activated.set(true); + tracing::debug!("Sync activated - auto-sync enabled"); + } tracing::debug!("Sync completed successfully"); } Err(e) => { @@ -1584,19 +1716,66 @@ pub fn SyncStatus(props: SyncStatusProps) -> Element { }); // Determine display state (drafts can sync too via DraftRef) + let is_activated = *sync_activated.read(); let display_state = if !is_authenticated { SyncState::Disabled } else { *sync_state.read() }; - let (icon, label, class) = match display_state { - SyncState::Synced => ("✓", "Synced", "sync-status synced"), - SyncState::Syncing => ("◌", "Syncing...", "sync-status syncing"), - SyncState::Unsynced => ("●", "Unsynced", "sync-status unsynced"), - SyncState::RemoteChanges => ("↓", "Updates", "sync-status remote-changes"), - SyncState::Error => ("✕", "Sync error", "sync-status error"), - SyncState::Disabled => ("○", "Sync disabled", "sync-status disabled"), + // Before activation: show "Start syncing" button + // After activation: show normal sync states + let (icon, label, class) = if !is_activated && is_authenticated { + ("▶", "Start syncing", "sync-status start-sync") + } else { + match display_state { + SyncState::Synced => ("✓", "Synced", "sync-status synced"), + SyncState::Syncing => ("◌", "Syncing...", "sync-status syncing"), + SyncState::Unsynced => ("●", "Unsynced", "sync-status unsynced"), + SyncState::RemoteChanges => ("↓", "Updates", "sync-status remote-changes"), + SyncState::Error => ("✕", "Sync error", "sync-status error"), + SyncState::Disabled => ("○", "Sync disabled", "sync-status disabled"), + } + }; + + // Long-press detection for deactivating sync + let mut long_press_active = use_signal(|| false); + #[cfg(target_arch = "wasm32")] + let mut long_press_timeout: Signal> = + use_signal(|| None); + + let on_pointer_down = move |_: dioxus::events::PointerEvent| { + // Only allow deactivation if sync is currently activated + if !*sync_activated.peek() { + return; + } + + long_press_active.set(true); + + // Start 1 second timer for long press + #[cfg(target_arch = "wasm32")] + let timeout = gloo_timers::callback::Timeout::new(1000, move || { + if *long_press_active.peek() { + sync_activated.set(false); + long_press_active.set(false); + tracing::debug!("Sync deactivated via long press"); + } + }); + #[cfg(target_arch = "wasm32")] + long_press_timeout.set(Some(timeout)); + }; + + let on_pointer_up = move |_: dioxus::events::PointerEvent| { + long_press_active.set(false); + // Cancel the timeout by dropping it + #[cfg(target_arch = "wasm32")] + long_press_timeout.set(None); + }; + + let on_pointer_leave = move |_: dioxus::events::PointerEvent| { + long_press_active.set(false); + #[cfg(target_arch = "wasm32")] + long_press_timeout.set(None); }; // Combined sync handler - pulls remote changes first if needed, then pushes local @@ -1605,6 +1784,11 @@ pub fn SyncStatus(props: SyncStatusProps) -> Element { let on_refresh = props.on_refresh.clone(); let current_state = display_state; move |_: dioxus::events::MouseEvent| { + // Don't trigger click if long press just fired + if !*sync_activated.peek() && *long_press_active.peek() { + return; + } + if *sync_state.peek() == SyncState::Syncing { return; // Already syncing } @@ -1623,11 +1807,25 @@ pub fn SyncStatus(props: SyncStatusProps) -> Element { } }; + // Show tooltip hint about long-press when sync is active + let title = if is_activated { + if let Some(ref err) = *last_error.read() { + err.clone() + } else { + format!("{} (hold to stop syncing)", label) + } + } else { + label.to_string() + }; + rsx! { div { class: "{class}", - title: if let Some(ref err) = *last_error.read() { err.clone() } else { label.to_string() }, + title: "{title}", onclick: on_sync_click, + onpointerdown: on_pointer_down, + onpointerup: on_pointer_up, + onpointerleave: on_pointer_leave, span { class: "sync-icon", "{icon}" } span { class: "sync-label", "{label}" } diff --git a/crates/weaver-app/src/components/editor/worker.rs b/crates/weaver-app/src/components/editor/worker.rs index e54dedc..b7beab9 100644 --- a/crates/weaver-app/src/components/editor/worker.rs +++ b/crates/weaver-app/src/components/editor/worker.rs @@ -43,6 +43,8 @@ pub enum WorkerInput { editing_uri: Option, /// Editing CID if editing existing entry editing_cid: Option, + /// Notebook URI for re-publishing + notebook_uri: Option, }, /// Start collab session (worker will spawn CollabNode) StartCollab { @@ -100,6 +102,8 @@ pub enum WorkerOutput { editing_uri: Option, /// Editing CID editing_cid: Option, + /// Notebook URI for re-publishing + notebook_uri: Option, /// Export timing in ms export_ms: f64, /// Encode timing in ms @@ -266,6 +270,7 @@ mod worker_impl { cursor_offset, editing_uri, editing_cid, + notebook_uri, } => { let Some(ref doc) = doc else { if let Err(e) = scope @@ -314,6 +319,7 @@ mod worker_impl { cursor_offset, editing_uri, editing_cid, + notebook_uri, export_ms, encode_ms, }) @@ -663,6 +669,7 @@ mod worker_impl { cursor_offset, editing_uri, editing_cid, + notebook_uri, } => { let Some(ref doc) = doc else { if let Err(e) = scope @@ -707,6 +714,7 @@ mod worker_impl { cursor_offset, editing_uri, editing_cid, + notebook_uri, export_ms, encode_ms, }) diff --git a/crates/weaver-app/src/components/entry.rs b/crates/weaver-app/src/components/entry.rs index 564471c..9b68975 100644 --- a/crates/weaver-app/src/components/entry.rs +++ b/crates/weaver-app/src/components/entry.rs @@ -605,14 +605,13 @@ pub fn EntryMetadata( div { class: "entry-meta-info", // Authors - if !entry_view.authors.is_empty() { div { class: "entry-authors", AuthorList { authors: entry_view.authors.clone(), owner_ident: Some(ident.clone()), } } - } + // Date div { class: "entry-date", diff --git a/crates/weaver-app/src/views/drafts.rs b/crates/weaver-app/src/views/drafts.rs index 8467c5a..b0d49b1 100644 --- a/crates/weaver-app/src/views/drafts.rs +++ b/crates/weaver-app/src/views/drafts.rs @@ -5,7 +5,7 @@ use crate::auth::AuthState; use crate::components::button::{Button, ButtonVariant}; use crate::components::dialog::{DialogContent, DialogDescription, DialogRoot, DialogTitle}; use crate::components::editor::{list_drafts_from_pds, RemoteDraft}; -use crate::components::editor::{delete_draft, list_drafts}; +use crate::components::editor::{delete_draft, delete_draft_from_pds, list_drafts}; use crate::fetch::Fetcher; use dioxus::prelude::*; use jacquard::smol_str::{SmolStr, format_smolstr}; @@ -39,9 +39,13 @@ pub fn DraftsList(ident: ReadSignal>) -> Element { let mut local_drafts = use_signal(list_drafts); let mut show_delete_confirm = use_signal(|| None::); + // Clone fetcher early for use in both resource and delete handler + let fetcher_for_resource = fetcher.clone(); + let fetcher_for_delete = fetcher.clone(); + // Fetch remote drafts from PDS (depends on auth state to re-run when logged in) let remote_drafts_resource = use_resource(move || { - let fetcher = fetcher.clone(); + let fetcher = fetcher_for_resource.clone(); let _did = auth_state.read().did.clone(); // Track auth state for reactivity async move { list_drafts_from_pds(&fetcher).await.ok().unwrap_or_default() } }); @@ -131,9 +135,20 @@ pub fn DraftsList(ident: ReadSignal>) -> Element { }); let mut handle_delete = move |key: String| { + let fetcher = fetcher_for_delete.clone(); + let key_clone = key.clone(); + + // Delete from localStorage immediately delete_draft(&key); local_drafts.set(list_drafts()); show_delete_confirm.set(None); + + // Also delete from PDS (async, fire-and-forget) + spawn(async move { + if let Err(e) = delete_draft_from_pds(&fetcher, &key_clone).await { + tracing::warn!("Failed to delete draft from PDS: {}", e); + } + }); }; rsx! { diff --git a/crates/weaver-app/src/views/home.rs b/crates/weaver-app/src/views/home.rs index bbafc01..9c48665 100644 --- a/crates/weaver-app/src/views/home.rs +++ b/crates/weaver-app/src/views/home.rs @@ -10,6 +10,7 @@ use jacquard::types::string::Did; /// Pinned content items - can be notebooks or entries #[derive(Clone, PartialEq)] pub enum PinnedItem { + #[allow(dead_code)] Notebook { ident: AtIdentifier<'static>, title: SmolStr, @@ -31,8 +32,12 @@ fn pinned_items() -> Vec { // }, PinnedItem::Entry { ident: AtIdentifier::Did(Did::new_static("did:plc:yfvwmnlztr4dwkb7hwz55r2g").unwrap()), - rkey: SmolStr::new_static("3m4rbphjzt62b"), + rkey: SmolStr::new_static("3m7ysqf2z5s22"), }, + // PinnedItem::Entry { + // ident: AtIdentifier::Did(Did::new_static("did:plc:yfvwmnlztr4dwkb7hwz55r2g").unwrap()), + // rkey: SmolStr::new_static("3m4rbphjzt62b"), + // }, ] } diff --git a/crates/weaver-cli/src/main.rs b/crates/weaver-cli/src/main.rs index bfc384e..acfe260 100644 --- a/crates/weaver-cli/src/main.rs +++ b/crates/weaver-cli/src/main.rs @@ -388,7 +388,7 @@ async fn publish_notebook(source: PathBuf, title: String, store_path: PathBuf) - // Use WeaverExt to upsert entry (handles notebook + entry creation/updates) use jacquard::http_client::HttpClient; use weaver_common::WeaverExt; - let (entry_ref, was_created) = agent + let (entry_ref, _, was_created) = agent .upsert_entry(&title, entry_title.as_ref(), entry, None) .await?; diff --git a/crates/weaver-common/src/agent.rs b/crates/weaver-common/src/agent.rs index 057b03c..6ef349c 100644 --- a/crates/weaver-common/src/agent.rs +++ b/crates/weaver-common/src/agent.rs @@ -123,6 +123,46 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { } } + /// Fetch a notebook by URI and return its entry list + /// + /// Returns Ok(Some((uri, entry_list))) if the notebook exists and can be parsed, + /// Ok(None) if the notebook doesn't exist, + /// Err if there's a network or parsing error. + fn get_notebook_by_uri( + &self, + uri: &str, + ) -> impl Future, Vec>)>, WeaverError>> + where + Self: Sized, + { + async move { + use weaver_api::sh_weaver::notebook::book::Book; + + let at_uri = AtUri::new(uri) + .map_err(|e| WeaverError::InvalidNotebook(format!("Invalid notebook URI: {}", e)))?; + + let response = match self.get_record::(&at_uri).await { + Ok(r) => r, + Err(_) => return Ok(None), // Notebook doesn't exist + }; + + let output = match response.into_output() { + Ok(o) => o, + Err(_) => return Ok(None), // Failed to parse + }; + + let entries = output + .value + .entry_list + .iter() + .cloned() + .map(IntoStatic::into_static) + .collect(); + + Ok(Some((at_uri.into_static(), entries))) + } + } + /// Find or create a notebook by title, returning its URI and entry list /// /// If the notebook doesn't exist, creates it with the given DID as author. @@ -208,37 +248,24 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { } } - /// Find or create an entry within a notebook - /// - /// Multi-step workflow: - /// 1. Find the notebook by title - /// 2. If existing_rkey is provided, match by rkey; otherwise match by title - /// 3. If found: update the entry with new content - /// 4. If not found: create new entry and append to notebook's entry_list + /// Find or create an entry within a notebook (with pre-fetched notebook data) /// - /// The `existing_rkey` parameter allows updating an entry even if its title changed, - /// and enables pre-generating rkeys for path rewriting before publish. + /// This variant accepts notebook URI and entry_refs directly to avoid redundant + /// notebook lookups when the caller has already fetched this data. /// - /// Returns (entry_ref, was_created) - fn upsert_entry( + /// Returns (entry_ref, notebook_uri, was_created) + fn upsert_entry_with_notebook( &self, - notebook_title: &str, + notebook_uri: AtUri<'static>, + entry_refs: Vec>, entry_title: &str, entry: entry::Entry<'_>, existing_rkey: Option<&str>, - ) -> impl Future, bool), WeaverError>> + ) -> impl Future, AtUri<'static>, bool), WeaverError>> where Self: Sized, { async move { - // Get our own DID - let (did, _) = self.session_info().await.ok_or_else(|| { - AgentError::from(ClientError::invalid_request("No session info available")) - })?; - - // Find or create notebook - let (notebook_uri, entry_refs) = self.upsert_notebook(notebook_title, &did).await?; - // If we have an existing rkey, try to find and update that specific entry if let Some(rkey) = existing_rkey { // Check if this entry exists in the notebook by comparing rkeys @@ -259,7 +286,7 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { .uri(output.uri.into_static()) .cid(output.cid.into_static()) .build(); - return Ok((updated_ref, false)); + return Ok((updated_ref, notebook_uri, false)); } } @@ -283,10 +310,10 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { }) .await?; - return Ok((new_ref, true)); + return Ok((new_ref, notebook_uri, true)); } - // No existing rkey - use title-based matching (original behavior) + // No existing rkey - use title-based matching // Fast path: if notebook is empty, skip search and create directly if entry_refs.is_empty() { @@ -307,7 +334,7 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { }) .await?; - return Ok((new_ref, true)); + return Ok((new_ref, notebook_uri, true)); } // Check if entry with this title exists in the notebook @@ -331,7 +358,7 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { .uri(output.uri.into_static()) .cid(output.cid.into_static()) .build(); - return Ok((updated_ref, false)); + return Ok((updated_ref, notebook_uri, false)); } } } @@ -355,7 +382,50 @@ pub trait WeaverExt: AgentSessionExt + XrpcExt + Send + Sync + Sized { }) .await?; - Ok((new_ref, true)) + Ok((new_ref, notebook_uri, true)) + } + } + + /// Find or create an entry within a notebook + /// + /// Multi-step workflow: + /// 1. Find the notebook by title + /// 2. If existing_rkey is provided, match by rkey; otherwise match by title + /// 3. If found: update the entry with new content + /// 4. If not found: create new entry and append to notebook's entry_list + /// + /// The `existing_rkey` parameter allows updating an entry even if its title changed, + /// and enables pre-generating rkeys for path rewriting before publish. + /// + /// Returns (entry_ref, notebook_uri, was_created) + fn upsert_entry( + &self, + notebook_title: &str, + entry_title: &str, + entry: entry::Entry<'_>, + existing_rkey: Option<&str>, + ) -> impl Future, AtUri<'static>, bool), WeaverError>> + where + Self: Sized, + { + async move { + // Get our own DID + let (did, _) = self.session_info().await.ok_or_else(|| { + AgentError::from(ClientError::invalid_request("No session info available")) + })?; + + // Find or create notebook + let (notebook_uri, entry_refs) = self.upsert_notebook(notebook_title, &did).await?; + + // Delegate to the variant with pre-fetched notebook data + self.upsert_entry_with_notebook( + notebook_uri, + entry_refs, + entry_title, + entry, + existing_rkey, + ) + .await } } diff --git a/crates/weaver-index/Cargo.toml b/crates/weaver-index/Cargo.toml index d52498b..33fb351 100644 --- a/crates/weaver-index/Cargo.toml +++ b/crates/weaver-index/Cargo.toml @@ -64,6 +64,9 @@ url = "2" chrono = { version = "0.4", features = ["serde"] } smol_str = "0.3" +# CRDT (for parsing draft snapshots) +loro = { workspace = true } + # CID handling (for CAR block lookups) cid = "0.11" @@ -73,6 +76,7 @@ base64 = "0.22" dashmap = "6" include_dir = "0.7.4" regex = "1" +mini-moka = { git = "https://github.com/moka-rs/mini-moka", rev = "da864e849f5d034f32e02197fee9bb5d5af36d3d" } # WebSocket (for tap consumer) tokio-tungstenite = { version = "0.26", features = ["native-tls"] } diff --git a/crates/weaver-index/migrations/clickhouse/040_draft_titles.sql b/crates/weaver-index/migrations/clickhouse/040_draft_titles.sql new file mode 100644 index 0000000..7ead1b7 --- /dev/null +++ b/crates/weaver-index/migrations/clickhouse/040_draft_titles.sql @@ -0,0 +1,22 @@ +-- Draft titles extracted from Loro snapshots +-- Updated by background task when edit_heads changes + +CREATE TABLE IF NOT EXISTS draft_titles ( + -- Draft identity (matches drafts table) + did String, + rkey String, + + -- Extracted title from Loro doc + title String DEFAULT '', + + -- Head used for extraction (stale if doesn't match edit_heads) + head_did String DEFAULT '', + head_rkey String DEFAULT '', + head_cid String DEFAULT '', + + -- Timestamps + updated_at DateTime64(3) DEFAULT now64(3), + indexed_at DateTime64(3) DEFAULT now64(3) +) +ENGINE = ReplacingMergeTree(indexed_at) +ORDER BY (did, rkey) diff --git a/crates/weaver-index/src/bin/weaver_indexer.rs b/crates/weaver-index/src/bin/weaver_indexer.rs index c17e200..9c46ffd 100644 --- a/crates/weaver-index/src/bin/weaver_indexer.rs +++ b/crates/weaver-index/src/bin/weaver_indexer.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use clap::{Parser, Subcommand}; use tracing::{error, info, warn}; +use jacquard::client::UnauthenticatedSession; use weaver_index::clickhouse::InserterConfig; use weaver_index::clickhouse::{Client, Migrator}; use weaver_index::config::{ @@ -9,7 +10,10 @@ use weaver_index::config::{ }; use weaver_index::firehose::FirehoseConsumer; use weaver_index::server::{AppState, ServerConfig, TelemetryConfig, telemetry}; -use weaver_index::{FirehoseIndexer, ServiceIdentity, TapIndexer, load_cursor}; +use weaver_index::{ + DraftTitleTaskConfig, FirehoseIndexer, ServiceIdentity, TapIndexer, load_cursor, + run_draft_title_task, +}; #[derive(Parser)] #[command(name = "indexer")] @@ -165,9 +169,10 @@ async fn run_full() -> miette::Result<()> { }); let did_doc = identity.did_document_with_service(&server_config.service_did, &service_endpoint); - // Create separate clients for indexer and server + // Create separate clients for indexer, server, and background tasks let indexer_client = Client::new(&ch_config)?; let server_client = Client::new(&ch_config)?; + let task_client = std::sync::Arc::new(Client::new(&ch_config)?); // Build AppState for server let state = AppState::new( @@ -209,6 +214,14 @@ async fn run_full() -> miette::Result<()> { } }; + // Spawn background tasks + let resolver = UnauthenticatedSession::new_public(); + tokio::spawn(run_draft_title_task( + task_client, + resolver, + DraftTitleTaskConfig::default(), + )); + // Run server, monitoring indexer health tokio::select! { result = weaver_index::server::run(state, server_config, did_doc) => { diff --git a/crates/weaver-index/src/clickhouse.rs b/crates/weaver-index/src/clickhouse.rs index b830480..4649417 100644 --- a/crates/weaver-index/src/clickhouse.rs +++ b/crates/weaver-index/src/clickhouse.rs @@ -7,8 +7,8 @@ mod schema; pub use client::{Client, TableSize}; pub use migrations::{DbObject, MigrationResult, Migrator, ObjectType}; pub use queries::{ - CollaboratorRow, EditHeadRow, EditNodeRow, EntryRow, HandleMappingRow, NotebookRow, - ProfileCountsRow, ProfileRow, ProfileWithCounts, + CollaboratorRow, EditChainNode, EditHeadRow, EditNodeRow, EntryRow, HandleMappingRow, + NotebookRow, ProfileCountsRow, ProfileRow, ProfileWithCounts, StaleDraftRow, }; pub use resilient_inserter::{InserterConfig, ResilientRecordInserter}; pub use schema::{ diff --git a/crates/weaver-index/src/clickhouse/queries.rs b/crates/weaver-index/src/clickhouse/queries.rs index 9fa8cfa..0fabb0e 100644 --- a/crates/weaver-index/src/clickhouse/queries.rs +++ b/crates/weaver-index/src/clickhouse/queries.rs @@ -12,7 +12,7 @@ mod profiles; pub use collab::PermissionRow; pub use collab_state::{CollaboratorRow, EditHeadRow}; -pub use edit::EditNodeRow; +pub use edit::{EditChainNode, EditNodeRow, StaleDraftRow}; pub use identity::HandleMappingRow; pub use notebooks::{EntryRow, NotebookRow}; pub use profiles::{ProfileCountsRow, ProfileRow, ProfileWithCounts}; diff --git a/crates/weaver-index/src/clickhouse/queries/edit.rs b/crates/weaver-index/src/clickhouse/queries/edit.rs index 95bc3d7..51f4305 100644 --- a/crates/weaver-index/src/clickhouse/queries/edit.rs +++ b/crates/weaver-index/src/clickhouse/queries/edit.rs @@ -40,6 +40,40 @@ pub struct DraftWithRootRow { pub root_cid: SmolStr, #[serde(with = "clickhouse::serde::chrono::datetime64::millis::option")] pub last_edit_at: Option>, + /// Title extracted from Loro doc (may be empty if not yet extracted) + pub title: SmolStr, +} + +/// Draft needing title extraction (stale or missing from draft_titles) +#[derive(Debug, Clone, Row, Deserialize)] +pub struct StaleDraftRow { + /// Draft DID + pub did: SmolStr, + /// Draft rkey + pub rkey: SmolStr, + /// Current head DID + pub head_did: SmolStr, + /// Current head rkey + pub head_rkey: SmolStr, + /// Current head CID + pub head_cid: SmolStr, + /// Root DID for this edit chain + pub root_did: SmolStr, + /// Root rkey + pub root_rkey: SmolStr, + /// Root CID + pub root_cid: SmolStr, +} + +/// Edit chain node for reconstructing Loro doc +#[derive(Debug, Clone, Row, Deserialize)] +pub struct EditChainNode { + pub did: SmolStr, + pub rkey: SmolStr, + pub cid: SmolStr, + pub node_type: SmolStr, + pub prev_did: SmolStr, + pub prev_rkey: SmolStr, } impl Client { @@ -108,15 +142,16 @@ impl Client { /// List drafts for an actor. /// - /// Returns draft records with associated edit root info if available. + /// Returns draft records with associated edit root info and title if available. pub async fn list_drafts( &self, actor_did: &str, cursor: Option, limit: i64, ) -> Result, IndexError> { - // Query drafts table with LEFT JOIN to get associated edit roots + // Query drafts table with LEFT JOINs to get edit roots and titles // Edit roots reference drafts via resource_type/did/rkey fields + // Titles are extracted from Loro snapshots by background task let query = r#" SELECT d.did, @@ -126,7 +161,8 @@ impl Client { COALESCE(e.did, '') AS root_did, COALESCE(e.rkey, '') AS root_rkey, COALESCE(e.cid, '') AS root_cid, - e.created_at AS last_edit_at + e.created_at AS last_edit_at, + COALESCE(t.title, '') AS title FROM drafts d FINAL LEFT JOIN ( SELECT @@ -141,6 +177,7 @@ impl Client { AND resource_type = 'draft' AND deleted_at = toDateTime64(0, 3) ) e ON e.resource_did = d.did AND e.resource_rkey = d.rkey + LEFT JOIN draft_titles t FINAL ON t.did = d.did AND t.rkey = d.rkey WHERE d.did = ? AND d.deleted_at = toDateTime64(0, 3) AND (? = 0 OR toUnixTimestamp64Milli(d.created_at) < ?) @@ -166,4 +203,152 @@ impl Client { Ok(rows) } + + /// Find drafts with stale or missing titles. + /// + /// Compares edit_heads to draft_titles to find drafts where the current + /// head doesn't match the head used for title extraction. + pub async fn get_stale_draft_titles(&self, limit: i64) -> Result, IndexError> { + // Join drafts -> edit_heads (for current head) -> draft_titles (to check staleness) + // edit_heads uses resource_type='draft' and resource_did/resource_rkey to link + let query = r#" + SELECT + d.did, + d.rkey, + h.head_did, + h.head_rkey, + h.head_cid, + h.root_did, + h.root_rkey, + h.root_cid + FROM drafts d FINAL + INNER JOIN edit_heads h FINAL + ON h.resource_did = d.did + AND h.resource_rkey = d.rkey + AND h.resource_collection = 'sh.weaver.edit.draft' + LEFT JOIN draft_titles t FINAL + ON t.did = d.did + AND t.rkey = d.rkey + WHERE d.deleted_at = toDateTime64(0, 3) + AND (t.head_cid IS NULL OR t.head_cid = '' OR t.head_cid != h.head_cid) + LIMIT ? + "#; + + let rows = self + .inner() + .query(query) + .bind(limit) + .fetch_all::() + .await + .map_err(|e| ClickHouseError::Query { + message: "failed to get stale draft titles".into(), + source: e, + })?; + + Ok(rows) + } + + /// Get the edit chain from head back to root for reconstructing a Loro doc. + /// + /// Returns nodes in order from root to head (for sequential application). + /// Validates that the chain terminates at the expected root. + pub async fn get_edit_chain( + &self, + root_did: &str, + root_rkey: &str, + head_did: &str, + head_rkey: &str, + ) -> Result, IndexError> { + // Walk backwards from head to root via prev links + // Use recursive CTE to traverse the chain, stopping when we hit the expected root + let query = r#" + WITH RECURSIVE chain AS ( + -- Start from head + SELECT did, rkey, cid, node_type, prev_did, prev_rkey, 0 as depth, + (did = ? AND rkey = ?) as is_root + FROM edit_nodes FINAL + WHERE did = ? AND rkey = ? + AND deleted_at = toDateTime64(0, 3) + + UNION ALL + + -- Follow prev links until we hit the root + SELECT e.did, e.rkey, e.cid, e.node_type, e.prev_did, e.prev_rkey, c.depth + 1, + (e.did = ? AND e.rkey = ?) as is_root + FROM edit_nodes e FINAL + INNER JOIN chain c ON e.did = c.prev_did AND e.rkey = c.prev_rkey + WHERE e.deleted_at = toDateTime64(0, 3) + AND c.is_root = 0 -- stop when we've reached the root + AND c.depth < 1000 -- safety limit + ) + SELECT did, rkey, cid, node_type, prev_did, prev_rkey + FROM chain + ORDER BY depth DESC -- root first, then diffs in order + "#; + + let rows = self + .inner() + .query(query) + .bind(root_did) + .bind(root_rkey) + .bind(head_did) + .bind(head_rkey) + .bind(root_did) + .bind(root_rkey) + .fetch_all::() + .await + .map_err(|e| ClickHouseError::Query { + message: "failed to get edit chain".into(), + source: e, + })?; + + // Validate chain terminates at expected root + if let Some(first) = rows.first() { + if first.did != root_did || first.rkey != root_rkey { + return Err(ClickHouseError::Query { + message: format!( + "edit chain did not terminate at expected root {}:{}, got {}:{}", + root_did, root_rkey, first.did, first.rkey + ), + source: clickhouse::error::Error::Custom("chain validation failed".into()), + } + .into()); + } + } + + Ok(rows) + } + + /// Upsert a draft title after extraction. + pub async fn upsert_draft_title( + &self, + did: &str, + rkey: &str, + title: &str, + head_did: &str, + head_rkey: &str, + head_cid: &str, + ) -> Result<(), IndexError> { + let query = r#" + INSERT INTO draft_titles (did, rkey, title, head_did, head_rkey, head_cid) + VALUES (?, ?, ?, ?, ?, ?) + "#; + + self.inner() + .query(query) + .bind(did) + .bind(rkey) + .bind(title) + .bind(head_did) + .bind(head_rkey) + .bind(head_cid) + .execute() + .await + .map_err(|e| ClickHouseError::Query { + message: "failed to upsert draft title".into(), + source: e, + })?; + + Ok(()) + } } diff --git a/crates/weaver-index/src/clickhouse/queries/notebooks.rs b/crates/weaver-index/src/clickhouse/queries/notebooks.rs index 8b34649..dbd65ac 100644 --- a/crates/weaver-index/src/clickhouse/queries/notebooks.rs +++ b/crates/weaver-index/src/clickhouse/queries/notebooks.rs @@ -163,7 +163,7 @@ impl Client { e.indexed_at AS indexed_at, e.record AS record FROM notebook_entries ne FINAL - INNER JOIN entries FINAL AS e ON + INNER JOIN entries e FINAL ON e.did = ne.entry_did AND e.rkey = ne.entry_rkey AND e.deleted_at = toDateTime64(0, 3) @@ -731,7 +731,7 @@ impl Client { e.indexed_at AS indexed_at, e.record AS record FROM notebook_entries ne FINAL - INNER JOIN entries FINAL AS e ON + INNER JOIN entries e FINAL ON e.did = ne.entry_did AND e.rkey = ne.entry_rkey AND e.deleted_at = toDateTime64(0, 3) diff --git a/crates/weaver-index/src/endpoints/edit.rs b/crates/weaver-index/src/endpoints/edit.rs index 7d56fc1..6be6503 100644 --- a/crates/weaver-index/src/endpoints/edit.rs +++ b/crates/weaver-index/src/endpoints/edit.rs @@ -369,6 +369,13 @@ pub async fn list_drafts( let last_edit_at = row.last_edit_at.map(|dt| Datetime::new(dt.fixed_offset())); + // Include title if available + let title = if row.title.is_empty() { + None + } else { + Some(row.title.to_cowstr().into_static()) + }; + drafts.push( DraftView::new() .uri(uri) @@ -376,6 +383,7 @@ pub async fn list_drafts( .created_at(created_at) .maybe_edit_root(edit_root) .maybe_last_edit_at(last_edit_at) + .maybe_title(title) .build(), ); } diff --git a/crates/weaver-index/src/endpoints/notebook.rs b/crates/weaver-index/src/endpoints/notebook.rs index 6fb4659..f8dae62 100644 --- a/crates/weaver-index/src/endpoints/notebook.rs +++ b/crates/weaver-index/src/endpoints/notebook.rs @@ -152,8 +152,25 @@ pub async fn resolve_notebook( XrpcErrorResponse::internal_error("Invalid CID stored") })?; + let entry_contributors = state + .clickhouse + .get_entry_contributors(did_str, &entry_row.rkey) + .await + .map_err(|e| { + tracing::error!("Failed to get entry contributors: {}", e); + XrpcErrorResponse::internal_error("Database query failed") + })?; + + let mut all_author_dids: HashSet = entry_contributors.iter().cloned().collect(); + // Also include author_dids from the record (explicit declarations) + for did in &entry_row.author_dids { + all_author_dids.insert(did.clone()); + } + + let author_dids_vec: Vec = all_author_dids.into_iter().collect(); + // Hydrate entry authors - let entry_authors = hydrate_authors(&entry_row.author_dids, &profile_map)?; + let entry_authors = hydrate_authors(&author_dids_vec, &profile_map)?; // Parse record JSON let entry_record = parse_record_json(&entry_row.record)?; diff --git a/crates/weaver-index/src/error.rs b/crates/weaver-index/src/error.rs index 587e5cc..c331136 100644 --- a/crates/weaver-index/src/error.rs +++ b/crates/weaver-index/src/error.rs @@ -29,6 +29,10 @@ pub enum IndexError { #[error(transparent)] #[diagnostic(transparent)] Sqlite(#[from] SqliteError), + + #[error("resource not found: {resource}")] + #[diagnostic(code(index::not_found))] + NotFound { resource: String }, } /// HTTP server errors diff --git a/crates/weaver-index/src/lib.rs b/crates/weaver-index/src/lib.rs index d07dee8..952079a 100644 --- a/crates/weaver-index/src/lib.rs +++ b/crates/weaver-index/src/lib.rs @@ -9,6 +9,7 @@ pub mod server; pub mod service_identity; pub mod sqlite; pub mod tap; +pub mod tasks; pub use config::Config; pub use error::{IndexError, Result}; @@ -17,3 +18,4 @@ pub use parallel_tap::TapIndexer; pub use server::{AppState, ServerConfig}; pub use service_identity::ServiceIdentity; pub use sqlite::{ShardKey, ShardRouter, SqliteShard}; +pub use tasks::{run_draft_title_task, DraftTitleTaskConfig}; diff --git a/crates/weaver-index/src/tasks/draft_titles.rs b/crates/weaver-index/src/tasks/draft_titles.rs new file mode 100644 index 0000000..e2b803f --- /dev/null +++ b/crates/weaver-index/src/tasks/draft_titles.rs @@ -0,0 +1,380 @@ +//! Background task for extracting draft titles from Loro snapshots. +//! +//! Periodically scans for drafts where the edit head has changed since +//! the last title extraction, fetches the edit chain from PDS, reconstructs +//! the Loro document, and extracts the title. + +use std::sync::Arc; +use std::time::Duration; + +use jacquard::client::UnauthenticatedSession; +use jacquard::identity::JacquardResolver; +use jacquard::prelude::{IdentityResolver, XrpcExt}; +use jacquard::types::ident::AtIdentifier; +use jacquard::types::string::{Cid, Did}; +use loro::LoroDoc; +use mini_moka::sync::Cache; +use tracing::{debug, error, info, warn}; + +use crate::clickhouse::{Client, StaleDraftRow}; +use crate::error::IndexError; + +use weaver_api::com_atproto::repo::get_record::GetRecord; +use weaver_api::com_atproto::sync::get_blob::GetBlob; +use weaver_api::sh_weaver::edit::diff::Diff; +use weaver_api::sh_weaver::edit::root::Root; + +/// Cache for PDS blob fetches. +/// +/// Blobs are content-addressed so safe to cache indefinitely. +/// Key is (did, cid) as a string. +#[derive(Clone)] +pub struct BlobCache { + cache: Cache>>, +} + +impl BlobCache { + pub fn new(max_capacity: u64) -> Self { + Self { + cache: Cache::new(max_capacity), + } + } + + fn key(did: &str, cid: &str) -> String { + format!("{}:{}", did, cid) + } + + pub fn get(&self, did: &str, cid: &str) -> Option>> { + self.cache.get(&Self::key(did, cid)) + } + + pub fn insert(&self, did: &str, cid: &str, data: Vec) { + self.cache.insert(Self::key(did, cid), Arc::new(data)); + } +} + +/// Configuration for the draft title extraction task +#[derive(Debug, Clone)] +pub struct DraftTitleTaskConfig { + /// How often to check for stale titles + pub interval: Duration, + /// Maximum drafts to process per run + pub batch_size: i64, +} + +impl Default for DraftTitleTaskConfig { + fn default() -> Self { + Self { + interval: Duration::from_secs(120), // 2 minutes + batch_size: 50, + } + } +} + +/// Run the draft title extraction task in a loop +pub async fn run_draft_title_task( + client: Arc, + resolver: UnauthenticatedSession, + config: DraftTitleTaskConfig, +) { + info!( + interval_secs = config.interval.as_secs(), + batch_size = config.batch_size, + "starting draft title extraction task" + ); + + // Cache for blob fetches - blobs are content-addressed, safe to cache indefinitely + // 1000 entries is plenty for typical edit chains + let blob_cache = BlobCache::new(1000); + + loop { + match process_stale_drafts(&client, &resolver, &blob_cache, config.batch_size).await { + Ok(count) => { + if count > 0 { + info!(processed = count, "draft title extraction complete"); + } else { + debug!("no stale draft titles to process"); + } + } + Err(e) => { + error!(error = ?e, "draft title extraction failed"); + } + } + + tokio::time::sleep(config.interval).await; + } +} + +/// Process a batch of stale drafts +async fn process_stale_drafts( + client: &Client, + resolver: &UnauthenticatedSession, + blob_cache: &BlobCache, + batch_size: i64, +) -> Result { + let stale = client.get_stale_draft_titles(batch_size).await?; + + if stale.is_empty() { + return Ok(0); + } + + debug!(count = stale.len(), "found stale draft titles"); + + let mut processed = 0; + for draft in stale { + match extract_and_save_title(client, resolver, blob_cache, &draft).await { + Ok(title) => { + debug!( + did = %draft.did, + rkey = %draft.rkey, + title = %title, + "extracted draft title" + ); + processed += 1; + } + Err(e) => { + warn!( + did = %draft.did, + rkey = %draft.rkey, + error = ?e, + "failed to extract draft title" + ); + } + } + } + + Ok(processed) +} + +/// Extract title from a single draft and save it +async fn extract_and_save_title( + client: &Client, + resolver: &UnauthenticatedSession, + blob_cache: &BlobCache, + draft: &StaleDraftRow, +) -> Result { + // Get the edit chain from ClickHouse + let chain = client + .get_edit_chain( + &draft.root_did, + &draft.root_rkey, + &draft.head_did, + &draft.head_rkey, + ) + .await?; + + if chain.is_empty() { + return Err(IndexError::NotFound { + resource: format!("edit chain for {}:{}", draft.did, draft.rkey), + }); + } + + // Resolve PDS for the root DID + let root_did = Did::new(&draft.root_did).map_err(|e| IndexError::NotFound { + resource: format!("invalid root DID: {}", e), + })?; + + let pds_url = resolver + .pds_for_did(&root_did) + .await + .map_err(|e| IndexError::NotFound { + resource: format!("PDS for {}: {}", root_did, e), + })?; + + // Initialize Loro doc + let doc = LoroDoc::new(); + + // Process chain: first node should be root, rest are diffs + for (i, node) in chain.iter().enumerate() { + let node_did = Did::new(&node.did).map_err(|e| IndexError::NotFound { + resource: format!("invalid node DID: {}", e), + })?; + + if node.node_type == "root" { + // Fetch root record + let root_record = + fetch_root_record(resolver, pds_url.clone(), &node_did, &node.rkey).await?; + + // Fetch snapshot blob + let snapshot_cid = root_record.snapshot.blob().cid(); + let snapshot_bytes = + fetch_blob(resolver, blob_cache, pds_url.clone(), &node_did, snapshot_cid).await?; + + // Import snapshot + doc.import(&snapshot_bytes) + .map_err(|e| IndexError::NotFound { + resource: format!("failed to import root snapshot: {}", e), + })?; + + debug!( + did = %node.did, + rkey = %node.rkey, + bytes = snapshot_bytes.len(), + "imported root snapshot" + ); + } else { + // Fetch diff record + let diff_record = + fetch_diff_record(resolver, pds_url.clone(), &node_did, &node.rkey).await?; + + // Diffs can have inline diff bytes or a snapshot blob reference + let diff_bytes = if let Some(ref inline) = diff_record.inline_diff { + // Use inline diff (base64 decoded by serde) + inline.to_vec() + } else if let Some(ref snapshot_blob) = diff_record.snapshot { + // Fetch snapshot blob + let snapshot_cid = snapshot_blob.blob().cid(); + fetch_blob(resolver, blob_cache, pds_url.clone(), &node_did, snapshot_cid).await? + } else { + warn!( + did = %node.did, + rkey = %node.rkey, + "diff has neither inline nor snapshot data, skipping" + ); + continue; + }; + + // Import diff + doc.import(&diff_bytes).map_err(|e| IndexError::NotFound { + resource: format!("failed to import diff {}: {}", i, e), + })?; + + debug!( + did = %node.did, + rkey = %node.rkey, + bytes = diff_bytes.len(), + "imported diff" + ); + } + } + + // Extract title from Loro doc + let title = doc.get_text("title").to_string(); + + // Save to ClickHouse + client + .upsert_draft_title( + &draft.did, + &draft.rkey, + &title, + &draft.head_did, + &draft.head_rkey, + &draft.head_cid, + ) + .await?; + + Ok(title) +} + +/// Fetch an edit.root record from PDS +async fn fetch_root_record( + resolver: &UnauthenticatedSession, + pds_url: jacquard::url::Url, + did: &Did<'_>, + rkey: &str, +) -> Result, IndexError> { + use jacquard::IntoStatic; + use jacquard::types::string::Nsid; + + let request = GetRecord::new() + .repo(AtIdentifier::Did(did.clone())) + .collection(Nsid::new_static("sh.weaver.edit.root").unwrap()) + .rkey( + jacquard::types::recordkey::RecordKey::any(rkey).map_err(|e| IndexError::NotFound { + resource: format!("invalid rkey: {}", e), + })?, + ) + .build(); + + let response = + resolver + .xrpc(pds_url) + .send(&request) + .await + .map_err(|e| IndexError::NotFound { + resource: format!("root record {}/{}: {}", did, rkey, e), + })?; + + let output = response.into_output().map_err(|e| IndexError::NotFound { + resource: format!("parse root record: {}", e), + })?; + + let root: Root = jacquard::from_data(&output.value).map_err(|e| IndexError::NotFound { + resource: format!("deserialize root: {}", e), + })?; + + Ok(root.into_static()) +} + +/// Fetch an edit.diff record from PDS +async fn fetch_diff_record( + resolver: &UnauthenticatedSession, + pds_url: jacquard::url::Url, + did: &Did<'_>, + rkey: &str, +) -> Result, IndexError> { + use jacquard::IntoStatic; + use jacquard::types::string::Nsid; + + let request = GetRecord::new() + .repo(AtIdentifier::Did(did.clone())) + .collection(Nsid::new_static("sh.weaver.edit.diff").unwrap()) + .rkey( + jacquard::types::recordkey::RecordKey::any(rkey).map_err(|e| IndexError::NotFound { + resource: format!("invalid rkey: {}", e), + })?, + ) + .build(); + + let response = + resolver + .xrpc(pds_url) + .send(&request) + .await + .map_err(|e| IndexError::NotFound { + resource: format!("diff record {}/{}: {}", did, rkey, e), + })?; + + let output = response.into_output().map_err(|e| IndexError::NotFound { + resource: format!("parse diff record: {}", e), + })?; + + let diff: Diff = jacquard::from_data(&output.value).map_err(|e| IndexError::NotFound { + resource: format!("deserialize diff: {}", e), + })?; + + Ok(diff.into_static()) +} + +/// Fetch a blob from PDS, using cache when available +async fn fetch_blob( + resolver: &UnauthenticatedSession, + cache: &BlobCache, + pds_url: jacquard::url::Url, + did: &Did<'_>, + cid: &Cid<'_>, +) -> Result, IndexError> { + // Check cache first - blobs are content-addressed + if let Some(cached) = cache.get(did.as_str(), cid.as_str()) { + debug!(cid = %cid, "blob cache hit"); + return Ok(cached.as_ref().clone()); + } + + let request = GetBlob::new().did(did.clone()).cid(cid.clone()).build(); + + let response = + resolver + .xrpc(pds_url) + .send(&request) + .await + .map_err(|e| IndexError::NotFound { + resource: format!("blob {}: {}", cid, e), + })?; + + let bytes = response.buffer().to_vec(); + + // Cache for future use + cache.insert(did.as_str(), cid.as_str(), bytes.clone()); + + Ok(bytes) +} diff --git a/crates/weaver-index/src/tasks/mod.rs b/crates/weaver-index/src/tasks/mod.rs new file mode 100644 index 0000000..b8c1b3b --- /dev/null +++ b/crates/weaver-index/src/tasks/mod.rs @@ -0,0 +1,5 @@ +//! Background tasks for the indexer + +mod draft_titles; + +pub use draft_titles::{run_draft_title_task, DraftTitleTaskConfig}; diff --git a/weaver_notes/.obsidian/workspace.json b/weaver_notes/.obsidian/workspace.json index d67eafa..d1bc997 100644 --- a/weaver_notes/.obsidian/workspace.json +++ b/weaver_notes/.obsidian/workspace.json @@ -41,12 +41,12 @@ "state": { "type": "markdown", "state": { - "file": "Writing the AppView Last.md", + "file": "Untitled.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "Writing the AppView Last" + "title": "Untitled" } } ], @@ -199,13 +199,15 @@ "bases:Create new base": false } }, - "active": "6029beecc3d03bce", + "active": "2f6bdb6d2dce13ed", "lastOpenFiles": [ + "cargo_bloated.png", + "Writing the AppView Last.md", + "Untitled.md", "weaver_index_html.png", "2025-12-14T23:32:19.png", "diff_record.png", "Why I rewrote pdsls in Rust (tm).md", - "Writing the AppView Last.md", "light_mode_excerpt.png", "notebook_entry_preview.png", "xkcd_345_excerpt.png", @@ -214,7 +216,6 @@ "Pasted image 20251114125028.png", "invalid_record.png", "Pasted image 20251114125031.png", - "Pasted image 20251114121431.png", "Arch.md", "Weaver - Long-form writing.md" ] diff --git a/weaver_notes/Untitled.md b/weaver_notes/Untitled.md new file mode 100644 index 0000000..66af37a --- /dev/null +++ b/weaver_notes/Untitled.md @@ -0,0 +1,256 @@ +I recently used Jacquard to write an ~AppView~ Index for Weaver. I alluded in my posts about my devlog about that experience how easy I had made the actual web server side of that. Lexicon as a specification language provides a lot of ways to specify data types and a few to specify API endpoints. XRPC is the canonical way to do that, and it's an opinionated subset of HTTP, which narrows down to a specific endpoint format and set of "verbs". Your path is `/xrpc/your.lexicon.nsidEndpoint?argument=value`, your bodies are mostly JSON. + +I'm going to lead off by tooting someone else's horn. Chad Miller's https://quickslice.slices.network/ provides an excellent example of the kind of thing you can do with atproto lexicons, and it doesn't use XRPC at all, but instead generates GraphQL's equivalents. This is more freeform, requires less of you upfront, and is in a lot of ways more granular than XRPC could possibly allow. Jacquard is for the moment built around the expectations of XRPC. If someone want's Jacquard support for GraphQL on atproto lexicons, I'm all ears, though. + +Here's to me one of the benefits of XRPC, and one of the challenges. XRPC only specifies your inputs and your output. everything else between you need to figure out. This means more work, but it also means you have internal flexibility. And Jacquard's server-side XRPC helpers follow that. Jacquard XRPC code generation itself provides the output type and the errors. For the server side it generates one additional marker type, generally labeled `YourXrpcQueryRequest`, and a trait implementation for `XrpcEndpoint`. You can also get these with `derive(XrpcRequest)` on existing Rust structs without writing out lexicon JSON. + +```rust +pub trait XrpcEndpoint { + /// Fully-qualified path ('/xrpc/\[nsid\]') where this endpoint should live on the server + const PATH: &'static str; + /// XRPC method (query/GET or procedure/POST) + const METHOD: XrpcMethod; + /// XRPC Request data type + type Request<'de>: XrpcRequest + Deserialize<'de> + IntoStatic; + /// XRPC Response data type + type Response: XrpcResp; +} + +/// Endpoint type for +///sh.weaver.actor.getActorNotebooks +pub struct GetActorNotebooksRequest; +impl XrpcEndpoint for GetActorNotebooksRequest { + const PATH: &'static str = "/xrpc/sh.weaver.actor.getActorNotebooks"; + const METHOD: XrpcMethod = XrpcMethod::Query; + type Request<'de> = GetActorNotebooks<'de>; + type Response = GetActorNotebooksResponse; +} +``` + +As with many Jacquard traits you see the associated types carrying the lifetime. You may ask, why a second struct and trait? This is very similar to the `XrpcRequest` trait, which is implemented on the request struct itself, after all. + +```rust +impl<'a> XrpcRequest for GetActorNotebooks<'a> { + const NSID: &'static str = "sh.weaver.actor.getActorNotebooks"; + const METHOD: XrpcMethod = XrpcMethod::Query; + type Response = GetActorNotebooksResponse; +} +``` + +## Time for magic +The reason is that lifetime when combined with the constraints Axum puts on extractors. Because the request type includes a lifetime, if we were to attempt to implement `FromRequest` directly for `XrpcRequest`, the trait would require that `XrpcRequest` be implemented for all lifetimes, and also apply an effective `DeserializeOwned` bound, even if we were to specify the `'static` lifetime as we do. And of course `XrpcRequest` is implemented for one specific lifetime, `'a`, the lifetime of whatever it's borrowed from. Meanwhile `XrpcEndpoint` has no lifetime itself, but instead carries the lifetime on the `Request` associated type. This allows us to do the following implementation, where `ExtractXrpc` has no lifetime itself and contains an owned version of the deserialized request. And we can then implement `FromRequest` for `ExtractXrpc`, and put the `for<'any>` bound on the `IntoStatic` trait requirement in a where clause, where it works perfectly. In combination with the code generation in `jacquard-lexicon`, this is the full implementation of Jacquard's Axum XRPC request extractor. Not so bad. + +```rust +pub struct ExtractXrpc(pub E::Request<'static>); + +impl FromRequest for ExtractXrpc +where + S: Send + Sync, + R: XrpcEndpoint, + for<'a> R::Request<'a>: IntoStatic>, +{ + type Rejection = Response; + + fn from_request( + req: Request, + state: &S, + ) -> impl Future> + Send { + async { + match R::METHOD { + XrpcMethod::Procedure(_) => { + let body = Bytes::from_request(req, state) + .await + .map_err(IntoResponse::into_response)?; + let decoded = R::Request::decode_body(&body); + match decoded { + Ok(value) => Ok(ExtractXrpc(*value.into_static())), + Err(err) => Err(( + StatusCode::BAD_REQUEST, + Json(json!({ + "error": "InvalidRequest", + "message": format!("failed to decode request: {}", err) + })), + ).into_response()), + } + } + XrpcMethod::Query => { + if let Some(path_query) = req.uri().path_and_query() { + let query = path_query.query().unwrap_or(""); + let value: R::Request<'_> = + serde_html_form::from_str::>(query).map_err(|e| { + ( + StatusCode::BAD_REQUEST, + Json(json!({ + "error": "InvalidRequest", + "message": format!("failed to decode request: {}", e) + })), + ).into_response() + })?; + Ok(ExtractXrpc(value.into_static())) + } else { + Err(( + StatusCode::BAD_REQUEST, + Json(json!({ + "error": "InvalidRequest", + "message": "wrong path" + })), + ).into_response()) + } + } + } + } + } +``` + +Jacquard then also provides an additional utility to round things out, using the associated `PATH` constant to put the handler for your XRPC request at the right spot in your router. +```rust +/// Conversion trait to turn an XrpcEndpoint and a handler into an axum Router +pub trait IntoRouter { + fn into_router(handler: U) -> Router + where + T: 'static, + S: Clone + Send + Sync + 'static, + U: axum::handler::Handler; +} + +impl IntoRouter for X +where + X: XrpcEndpoint, +{ + /// Creates an axum router that will invoke `handler` in response to xrpc + /// request `X`. + fn into_router(handler: U) -> Router + where + T: 'static, + S: Clone + Send + Sync + 'static, + U: axum::handler::Handler, + { + Router::new().route( + X::PATH, + (match X::METHOD { + XrpcMethod::Query => axum::routing::get, + XrpcMethod::Procedure(_) => axum::routing::post, + })(handler), + ) + } +} +``` + +Which then lets the Axum router for Weaver's Index look like this (truncated for length): + +```rust +pub fn router(state: AppState, did_doc: DidDocument<'static>) -> Router { + Router::new() + .route("/", get(landing)) + .route( + "/assets/IoskeleyMono-Regular.woff2", + get(font_ioskeley_regular), + ) + .route("/assets/IoskeleyMono-Bold.woff2", get(font_ioskeley_bold)) + .route( + "/assets/IoskeleyMono-Italic.woff2", + get(font_ioskeley_italic), + ) + .route("/xrpc/_health", get(health)) + .route("/metrics", get(metrics)) + // com.atproto.identity.* endpoints + .merge(ResolveHandleRequest::into_router(identity::resolve_handle)) + // com.atproto.repo.* endpoints (record cache) + .merge(GetRecordRequest::into_router(repo::get_record)) + .merge(ListRecordsRequest::into_router(repo::list_records)) + // app.bsky.* passthrough endpoints + .merge(BskyGetProfileRequest::into_router(bsky::get_profile)) + .merge(BskyGetPostsRequest::into_router(bsky::get_posts)) + // sh.weaver.actor.* endpoints + .merge(GetProfileRequest::into_router(actor::get_profile)) + .merge(GetActorNotebooksRequest::into_router( + actor::get_actor_notebooks, + )) + .merge(GetActorEntriesRequest::into_router( + actor::get_actor_entries, + )) + // sh.weaver.notebook.* endpoints + ... + // sh.weaver.collab.* endpoints + ... + // sh.weaver.edit.* endpoints + ... + .layer(TraceLayer::new_for_http()) + .layer(CorsLayer::permissive() + .max_age(std::time::Duration::from_secs(86400)) + ).with_state(state) + .merge(did_web_router(did_doc)) +} +``` + +Each of the handlers is a fairly straightforward async function that takes `AppState`, the XrpcExtractor, and an extractor and validator for service auth, which allows it to be accessed through via your PDS via the `atproto-proxy` header, and return user-specific data, or gate specific endpoints as requiring authentication. + +> And so yeah, the actual HTTP server part of the index was dead-easy to write. The handlers themselves are some of them fairly *long* functions, as they need to pull together the required data from the database over a couple of queries and then do some conversion, but they're straightforward. At some point I may end up either adding additional specialized view tables to the database or rewriting my queries to do more in SQL or both, but for now it made sense to keep the final decision-making and assembly in Rust, where it's easier to iterate on. +### Service Auth +Service Auth is, for those not familiar, the non-OAuth way to talk to an XRPC server other than your PDS with an authenticated identity. It's the method the Bluesky AppView uses. There are some downsides to proxying through the PDS, like delay in being able to read your own writes without some PDS-side or app-level handling, but it is conceptually very simple. The PDS, when it pipes through an XRPC request to another service, validates authentication, then generates a short-lived JWT, signs it with the user's private key, and puts it in a header. The service then extracts that, decodes it, and validates it using the public key in the user's DID document. Jacquard provides a middleware that can be used to gate routes based on service auth validation and it also provides an extractor. Initially I provided just one where authentication is required, but as part of building the index I added an additional one for optional authentication, where the endpoint is public, but returns user-specific information when there is an authenticated user. It returns this structure. + +```rust +#[derive(Debug, Clone, jacquard_derive::IntoStatic)] +pub struct VerifiedServiceAuth<'a> { + /// The authenticated user's DID (from `iss` claim) + did: Did<'a>, + /// The audience (should match your service DID) + aud: Did<'a>, + /// The lexicon method NSID, if present + lxm: Option>, + /// JWT ID (nonce), if present + jti: Option>, +} +``` + +Ultimately I want to provide a similar set of OAuth extractors as well, but those need to be built, still. If I move away from service proxying for the Weaver index, they will definitely get written at that point. + +> I mentioned some bug-fixing in Jacquard was required to make this work. There were a couple of oversights in the `DidDocument` struct and a spot I had incorrectly held a tracing span across an await point. Also, while using the `slingshot_resolver` set of options for `JacquardResolver` is great under normal circumstances (and normally I default to it), the mini-doc does NOT in fact include the signing keys, and cannot be used to validate service auth. +> +> I am not always a smart woman. + +## Why not go full magic? +One thing the Jacquard service auth validation extractor does **not** provide is validation of that jti nonce. That is left as an exercise for the server developer, to maintain a cache of recent nonces and compare against them. I leave a number of things this way, and this is deliberate. I think this is the correct approach. As powerful as "magic" all-in-one frameworks like Dioxus (or the various full-stack JS frameworks) are, the magic usually ends up constraining you in a number of ways. There are a number of awkward things in the front-end app implementation which are downstream of constraints Dioxus applies to your types and functions in order to work its magic. + +There are a lot of possible things you might want to do as an XRPC server. You might be a PDS, you might be an AppView or index, you might be some other sort of service that doesn't really fit into the boxes (like a Tangled knot server or Streamplace node) you might authenticate via service auth or OAuth, communicate via the PDS or directly with the client app. And as such, while my approach to everything in Jacquard is to provide a comprehensive box of tools rather than a complete end-to-end solution, this is especially true on the server side of things, because of that diversity in requirements, and my desire to not constrain developers using the library to work a certain way, so that they can build anything they want on atproto. + +> If you haven't read the Not An AppView entry, here it is. I might recommend reading it, and some other previous entries in that notebook, as it will help put the following in context. + +![[at://did:plc:yfvwmnlztr4dwkb7hwz55r2g/sh.weaver.notebook.entry/3m7ysqf2z5s22]] +## Dogfooding again +That being said, my experience writing the Weaver front-end and now the index server does leave me wanting a few things. One is a "BFF" session type, which forwards requests through a server to the PDS (or index), acting somewhat like [oatproxy](https://github.com/streamplace/oatproxy) (prototype jacquard version of that [here](https://github.com/espeon/istat/tree/main/jacquard-oatproxy) courtesy of Nat and Claude). This allows easier reading of your own writes via server-side caching, some caching and deduplication of common requests to reduce load on the PDS and roundtrip time. If the seession lives server-side it allows longer-lived confidential sessions for OAuth, and avoids putting OAuth tokens on the client device. + +Once implemented, I will likely refactor the Weaver app to use this session type in fullstack-server mode, which will then help dramatically simplify a bunch of client-side code. The refactored app will likely include an internal XRPC "server" of sorts that will elide differences between the index's XRPC APIs and the index-less flow. With the "fullstack-server" and "use-index" features, the client app running in the browser will forward authenticated requests through the app server to the index or PDS. With "fullstack-server" only, the app server itself acts like a discount version of the index, implemented via generic services like Constellation. Performance will be significantly improved over the original index-less implementation due to better caching, and unifying the cache. In client-only mode there are a couple of options, and I am not sure which is ultimately correct. The straightforward way as far as separation of concerns goes would be to essentially use a web worker as intermediary and local cache. That worker would be compiled to either use the index or to make Constellation and direct PDS requests, depending on the "use-index" feature. However that brings with it the obvious overhead of copying data from the worker to the app in the default mode, and I haven't yet investigated how feasible the available options which might allow zero-copy transfer via SharedArrayBuffer are. That being said, the real-time collaboration feature already works this way (sans SharedArrayBuffer) and lag is comparable to when the iroh connection was handled in the UI thread. + +A fair bit of this is somewhat new territory for me, when it comes to the browser, and I would be ***very*** interested in hearing from people with more domain experience on the likely correct approach. + +On that note, one of my main frustrations with Jacquard as a library is how heavy it is in terms of compiled binary size due to monomorphization. I made that choice, to do everything via static dispatch, but when you want to ship as small a binary as possible over the network, it works against you. On WASM I haven't gotten a great number of exactly the granular damage, but on x86_64 (albeit with less aggressive optimisation for size) we're talking kilobytes of pure duplicated functions for every jacquard type used in the application, plus whatever else. +```rust +0.0% 0.0% 9.3KiB weaver_app weaver_app::components::editor::sync::create_diff::{closure#0} +0.0% 0.0% 9.2KiB loro_internal ::_commit +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_renderer ::resolve_did_doc::{closure#0}::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB weaver_app ::get_record::::{closure#0} +0.0% 0.0% 9.2KiB resvg >>>::loop_filter +0.0% 0.0% 9.2KiB miette ::render_context:: +0.0% 0.0% 9.1KiB miette ::render_context:: +0.0% 0.0% 9.1KiB weaver_app weaver_app::components::record_editor::EditableRecordContent::{closure#7}::{closure#0} +``` + +I've taken a couple stabs at refactors to help with this, but haven't found a solution that satisfies me, in part because one of the problems in practice is of course overhead from `serde_json` monomorphization. Unfortunately, the alternatives trade off in frustrating ways. [`facet`](https://github.com/facet-rs/facet) has its own binary size impacts and `facet-json` is missing a couple of critical features to work with atproto JSON data (internally-tagged enums, most notably). Something like [`simd-json`](https://github.com/simd-lite/simd-json) or [`serde_json_borrow`](https://github.com/PSeitz/serde_json_borrow) is fast and can borrow from the buffer in a way that is very useful to us (and honestly I intend to swap to them for some uses at some point), but `serde_json_borrow` only provides a value type, and I would then be uncertain at the monomorphization overhead of transforming that type into jacquard types. The `serde` implementation for `simd-json` is heavily based on `serde_json` and thus likely has much the same overhead problem. And [`miniserde`](https://github.com/dtolnay/miniserde) similarly lacks support for parts of JSON that atproto data requires (enums again). And writing my own custom JSON parser that deserializes into Jacquard's `Data` or `RawData` types (from where it can then be deserialized more simply into concrete types, ideally with much less code duplication) is not a project I have time for, and is on the tedious side of the kind of thing I enjoy, particularly the process of ensuring it is sufficiently robust for real-world use, and doesn't perform terribly. + +`dyn` compatibility for some of the Jacquard traits is possible but comes with its own challenges, as currently `Serialize` is a supertrait of `XrpcRequest`, and rewriting around removing that bound that is both a nontrivial refactor (and a breaking API change, and it's not the only barrier to dyn compatibility) and may not actually reduce the number of copies of `get_record()` in the binary as much as one would hope. Now, if most of the code could be taken out of that and put into a function that could be totally shared between all implementations or at least most, that would be ideal but the solution I found prevented the compiler from inferring the output type from the request type, it decoupled those two things too much. Obviously if I were to do a bunch of cursed internal unsafe rust I could probably make this work, but while I'm comfortable writing unsafe Rust I'm also conscious that I'm writing Jacquard not just for myself. My code will run in situations I cannot anticipate, and it needs to be as reliable as possible and as usable as possible. Additional use of unsafe could help with the latter (laundering lifetimes would make a number of things in Jacquard's main code paths much easier, both for me and for users of the library) but at potential cost to the former if I'm not smart enough or comprehensive enough in my testing. + +So I leave you, dear reader, with some questions this time. + +What choices make sense here? For Jacquard as a library, for writing web applications in Rust, and so on. I'm pretty damn good at this (if I do say so myself, and enough other people agree that I must accept it), but I'm also one person, with a necessarily incomplete understanding of the totality of the field. \ No newline at end of file diff --git a/weaver_notes/cargo_bloated.png b/weaver_notes/cargo_bloated.png new file mode 100644 index 0000000000000000000000000000000000000000..359bb89e088d9fcfd60656740336e0a63abfffa8 GIT binary patch literal 182092 zcme7R@N?(olHy`uVBq!ia0y~yU_Z*hz<8U3je&t7Xoa^91JiL~PZ!6KiaBrQvd<7X zdhI)ZM@qmHg#}Dqah%gNJbT_uQqffKV(V;raaT2YN5_JO1qUW=E;`vU$@tala!~<+ z5FQoB0L~-@jgAA?{~vx^SXQ=c*Y32cvb1l1=B3Y{_wL@ibylIJuR^S*KK&*Xx;}v6 z=B8&0xa-&rOLr)q~^kj~$e z`1?++dPySsv%oS1C?mgt|FV`=O=)2KCUeVl8YfNx>M<}}^Zv$L+2|9d{a zKIreSuQr_O5u2XmO4-}B-mCv#n{{oCb|pF=GXnw^qp%p z_5AxCkNIZVRC4{_SMpNm?~lj*Ci(YbqPFMBaz13w{Pp9pf57T6-OG1Y$Nx`!b!DaS z^O>sN(*ka9%U$W-zdYmqzS=A6Vym;RtPp&9dU}7|zn|$E}W=BseP1 zS)Orj&i@OmLbZ41_|38C%)0LPXe}CBnI)zj-uCMc7S^oZ>N!6E(Yj5w@{nqW3 zFr1V6_gZv*;OA#&U!Iz(eeAcmME&QfnJjNUwM?~Hdx$gS<+@+a>hx=Cqop&yS?uTk$yj-$r`b=c^xYj#ZJn~GCpzZ^Oux{+E$Zi?zUKPm;d&g?f2^*|H?i!<>bEqm#^+FJMYT1`%jXn--0T? z)XPk(ZPrK2b2ce(fY5_^KD(M9fmjnRa$M^a>%UWezxK-d`1-0R6V+@uxl*R=;0DP; zRD;Qn?qWhLjsh%>Kjb6chHlTZy|ur7f4)PEK+6}^;AK8BX3PgyxvlXKW>Ku0@JSDB z3s~Ph1&BooKW3O_o6WVapXb&q=h~#;!mjE)?MrUrWBzTz9b0ZkItuJ@J+&LG1#ELg z^A3M;u!RC>{11j|a`d3sv^%YVP$FW>!c zS2vfaRtnEOJvIG~Wfo7)&Ypg(*L$IoYuAPO_5WU0TKwJlY*u!KLH*lXre99$@Bgtb zzWVLfkOdBmr)I0?ulm2g=BLrtoS!Wp*To&XDeC3tw=82x?Cxz>)<%bSiLH4esr}+q z@Fm4PF0*a+D$KgH!guzzHOB0`QXb{^D%G#9jh@cp$RWRR-DFl!l7pv!4%sbtmWQv3 znQ3k0-YexAzAk3rL}m92U8346la6|QIjMeo%e>QbERALD>*joVa?;uS-i-ND=6O!> zH6K~$S{AqMQJD28U~g4vvcs=*DWepQy}NUEK7aV7eqHwUbv5<3wq&lH_y6DT_nL|e z!!DH1w}_8s~k<12LAhFe(B-i z_LrCa?M+K!h5r8etSQ8zSaW2TpBPHoRV=$8C31eQwm2voum>~tS)8A3-v6!m$6VWL zzEjh5t^e!A?lL($^Rrl`tI1KucV|C}uPm7L=QPqFP>Q?Swmc`EkHm99^@#tu`N$#x>P!+TOOZ(}x zfL$e$eBxoB%=7Q*gvvaq`gUbyu*vha+cVDIGCgH+r|fp_wEN6!qqm#Qt8H1g|6f(# z{T|87nV+AXeIh!E@7@yTnAOTUx=}XMURlM6IH!Gmeck)%#6wzqT00pkW@KDlCHnNl z#O7@|H=R;XPh0wYp0vyFZ*PN_2|l{M=V$VN?#q8BT;|x4dATi>t^D~izo?x>tb0Em zlfG5NSaE%-_Vl0M7dqenwQlvL6^_kqGV`SK_e|VxUj8nIuRAs_DDkVG_0EXa%Qbw@ zPb?09y7$6#xv=}jnb+6VUU(n5bob>QyM2{IVI1Is(RA4gBLa5mla*Sc=F%3 zencSzDpo#rf1CG+;oZTF;ii2DUNU5Ec>e9p&r2^>~yG6ZWC} z{k^&S{(SPj$1a-p#JA|#8PA)WQWuNIRm|%W)jn6f*}C!^*HoK{a_0AIjK98{>vO;6 zbMM4-t52moe;&+SzyIGYiGqm3`*)YWpQgnpapvWvrN*^?J|6!P(l2LoU~yOTtLlP> z{NWd~!`445&ogJ^t@`rf<;^ARp6xHZ*jHObvV3~|CS~FcE39ASN?vxeRCL@f97FOu2ttK`%a%_ZE2wiaE@ zS{1g|>gtL@{{MBK&#o4p^XAfuU#w>#lR>ZqZeHmYljC_as(*m%}A9U8|>eJuOO0Hlf9Yzen?< z7a4or?RqU!`sbEy`&7H!+uI&HKK;wicj3;?&l&&zu_cHvNHSs{{C)V1&V@;JMBwfiTpb@!!ZBDt8IUNe*V07_4$}bj_iN_y<9%O zrtm<*krvL!FL%G+w|L5w!awg$P1V+Zyx`Zp_xu0*ojmDjlKoAmOnk0Up3HiOJx`}a zpWs%J-Q&%^W?AmlRjIRHU+`|!&VBnRLG&D#sMaOZV+?Y%W@#13lpLbW8 z9Ew+SZts!$DrL_;^9O&|>m`9JgO=XNW(`~Cl%^fN_KE1@2}{?-e3(~P`}@MW*xffu zt?Ksw`6RF}eY)F?ZA{Fi%WUH7Yqz}L8&-Le@2Yj_=Se?hE+ro6csS+e3HGO_!!P`s z7kjfrsN6hr$tmwuHE&(-l`i5os=CGI9In3l(8|^5rEvY0JIiM$Y*-Muxb2D9>gRbo z6y-}_Uz;1A_xA4Y>FicgHg_8CRlm3W)D_X`yyGw@p9J&wt=ZR?rJkNPG5Es49>zs) z+}bM+xAngG`8<9>pRDzgLTj0|PtMKlKKK8ld)D?($1K(OX8iefJAY1I!qKkPt6z9e z(MbF~hi}4i%Laz&TP%)0Xv+#E)eTd={M78Dwnw4 znPv@%3WIR*Hfc}x_m>W?(qX&ZeMgTk>>-bjJ;Qm41t}*doih2fzrCDYT7bJPG-AQ= zq9-T(zP`G;cx(3cAg8r03tm2R)7#N@b%VIxoagT!Z2xZ?DwU|+GtajAQ_703udheu z^*&?+2j>6cG^wX2CrAIT_B*vjHt!OL@ZL3_CTYc|pDt5#NYd`-yx4Z}04!{JqN8XU*GJKQr9Jw>>=6xjN^G_M67n=NCG+*X+N0 zgzjqYijc5=`EKQ}f$*07ye(&1wjadctE#YHKi=Q0=*8&`jc-CY)Z zf8SpXP0g9=D!sDSZF>&s^=|O`y7_$B=Oqi%=l>NCT@^F4ZT~N=)~4I#ZL_AA>=Vz8 zc=}q(G;4zRjh9T?D!FqPb9Gnh&#{VxSJxm$%MYj1wN^3ZNv@Or|C<`oS^VtG%?b0g zqnGKd&*@{?D8#=05a+o$mcG(vISZ!gMqAyQY$*}3ur+kn#zQwZKi8=4lUeyRX-@Lj z8RF+#Pc8MHelyip!lFRn{o}3;2~$|8-+7Zay)=8L6;kOrb^YEGyLUbHxMa4cVj+5#k>BE`ye{wuCRki@4szI{qHHN6|x|BdEKK838p#!|1qzK z*qC%>b-4bn_xpZ3NlDlhJ>ghymveug?cK85xnI6q-m4RS<>%?kyp#C8wJ6Ij;1<)3 zn)A$RkJSUYZ&yq&_A<}={rM1U+}f@JJ_&;fcg5yg6voVS`?0&|=cbP_^84D?svX)` z^!a*x{ZoS;N#kkH&dxsnae=^-MNU)Qb{0KNntmp++ ze%l|i{NVkmEIT1=x_<1U^Y;IJK>2K0?47$yCTi)g{IpfPGJLyC>XI$JEO9SVP@0PZ zkJ#5b7V@k+#Ht>q?K|IY?y0HT{*R9@XSw_R^2=ggX|pAV+xai+@BfojQkTCdD|Ge8 zdDCZ`=d0;=Y_r(&^_t8q`+COp&*W@=sM>wkn)B~L&B;keL-yCzo|>*Nf3tCF0>|?n zN#m7HKe?t%GVPI>4<{LBEO7e1Ghn7^w%Ynr z(xzD=e?J`l4(f)-N4(qf*>9FvuGQ5wk(WCji+tl-aJu|{t^4Y*wM){@&iZ`d5x@PP z1j~q*`u^q~RyTw@OFp=p^HtNaoUd?~rq2EiCWd*`Qr4m2n9&Gwq@_z4k$&b>> zN4^)FXnrK|<Nse}1xqx|>cWm$&6cdwF;);gh}fd)?VNmfdzg9@Hiu zc<(E8sj2Znr?7h9+gqI~N5m&>gO`dB#=(di%mYS*uIWzMEbC`~AMZYnRBvySvMS*C-cdX zd$KHwBEP=BKVP{vA5^n)!<*frs0ABhsDMHM8?CbL|$Zo_Bta zhqATND!l@&*99-dLn8xQRqv#p(agQQZRyL)=>cn!m(Aa=yS4D~vIW0X@7#Eq;>e*A z^@elxh9klkQ~ZzYUh{T>S?2_~1IHTqBy6ip=0ENz*3X?UrW(3V_C_2BmurOcNR#>}MRo1Fx z0jIFqhkTjByhT#~n`PpvUaIy*uMAr1@k4s8+ZhzeBOR} z+}?Q$bs{%q*q6WGYud}SYu>h;pEKU=|6jN3^}5|F0v0;8ImP*a#v4z}HrMZ!p6&$7 z4)*{5T>f0}tN5(x^$!XEZf(teF+INSW99-Fp3WpB4edUo*N+M4ZscUS4wJ^4%H7qPxLzkJ>+N6`4>>+_)O zc7C4!=Tc#bXi!hwDD~8Y>j!T9eAKPav-h{j-*30`OAhmzPq@CsT`DysR>1eE;;qwZ zdlGI1U6^{o*tY7+maVUy4*IEcH%+KJvfEbB+hewi2mh~cZ=HXCd%IXz-S5NCPGR-T z_xtwN+~o#!j&g5rTikE|@5T%LSDy8MK2~eR3S_^vcyVEps`s*;ec3z2Pp7c#wwZTX z|B{EbpTMK$oST~p|1%sbuKM=oq}id(=l^w!2e*06x7)i@{?MP>`|IspwXMD~=T!Er zX6;+Mvg7MMA@#@g;(9R&GoMWSr6?L@TQF5C{25pIl@)<^I^->jSk^{toTL@D#$|rZ zCg0aP&&)Kw{PFSeUY1j^afU;^-;C4GO%QhpmRS|PK99%a{~6=Rd$HT@d@lH9e|>9q z_&)pGTU$cT*Z=?feaeIY?H9W*CLF&1=xDd5rYGA`*5?Pf52eqqJyv<{$zPPZ^x#acQqbIx{ z^gjOedVRX=zu)ioUzX0_5#6!z`XRnCdye!6S!}G_J zRK0_ABRJGWwL%^|zW;X4^%wL1>$r>S$88l}v{d$ai2sr$J=q$ChheAMfley7=?+^V&A{K8!JxV{7==?E2P~ z(Zzg6de4tX-HzM~#UGlSoM|k6b!G5$G2JMS>3XrBV&&~>TF#fUu$^6dz-Mw#(1J;m zCP(W1>ya>=G-=YLALcQ^2bOZ)Id%A`=ijQYwg0d0D$OqQ+*bFOt!#4C>T99STOFI* zwv@cIUU~23`>Ob_=hS>>eb{<$Z}sNen?F82{&`is^_oLTyY5v!>QuMj`BD9-bK17= zbC~iu3NCYe&q+P~Z}R$sdneC4Z}8xS${dDyyld|4eQ0Ui({KOJV(+h41zK@?Tmr5; z{mVNWazFT_)rV?7!^>rFZjh6haVo9-;w)yd0lLE)V3VU z`1aN396s>uljevicsTFBR^|dPc2TXrNmHMn-ND}7JbB0NUimNTUuJWDQ9oCBcZtWu zMPDwu%WrJw7uLV@V)^_!Bde8qMd?#LkN3&G`0((s*G$G2Gk$&+FNixNm=`?vPVpqS z#>()iZvQ&;JbJyZttzhy3|n((Q5B!;>ItlA#!i-rSiKee$xOx^*V=&a156)A!}pSaW}sH)uHg z;heR1?e{~nerrU&=RapyGpXoNn#rWM7R#%c;`GnWvz`6SYTZM*J)eI(J~FLHn0-O; zzc+8>WOv^=vQCEe@VXqf)9Gc2g^MQNT{hn{VYZR-?TH6G<@0vDF&5tbQ%CIbiQY4B zzO;l&7H2g_-kcv+>6E(LGS~du%gfVa4Hw_LFqQkl#`SZ*eP%t}y-#TMpGU6s964(L zKgwLWxi)(HC*F?s=(LH`y2HO(pO~thzUrf{@ZEpep^+OeO)T5C&+j&;Fyn)#&)4nt zDwKNAQ5;ilmB+F3$%%<`TlQbP7c6tY)30X_2qV6>0kEdQonWXbGLpwq1>G-Bt4P%(@fvUR`X~HnO?A%r`n}bCv1++xwEOcRM;-<=olPxGDX--&C#8 zN8dx&#r&)nJ?Hk++Badwf~Lxctl?rpQO?`7}qSkB*VeuB+S z!AeZ<-0l4Rb616|bh;ilTUBLe#7V0g-{(y6HqL$Mx~Tg5yDv|t$A7w~vw~%Ai>ky7 zS@y`fzhAF!Njod`@9y4e^ZT_n^`Fn07hCNA`Api`ymU!Gz|v;fuwO^@&PN|TsP`_e z;au>NtA|>-U+j9lZt>$;cAQZILT+*Z^&a_-Xh%_WDDT$jA%OqjYpG_i2f^X8@Vz4Rgq8>(Ml zTf5kA?z|^9jiKSe%X|u_#Fuo4RUhJxT%FQp#q{32vL*IGiNHy{_|-YmqLNqn0*@Zl zyK8tb=|IE3Iny^hlK9{M_HoE}r>)9&nZ7?>%^Ugbe0;LDT2y#v)-q@Fs6&z`%jN{zUfl9>@s}r)Ge2{EVR*-?XR~qh(mTf-xBprB zMsDhwy(^Z_opLte=NUa6zmiS2zc3%XHAV7nU&*ER*J7c+UOw4e;h}83=E)+hWWOAP zsc~G>)I;tU-^yDPx0r4F$K!pnr{&s8E^b|4`updeH{m?f;+5{oKWaVWpz`!l=-)mA zo0$KJ&dX)|ierzfZPM1>^_K6!wzn+#jNM|oi}qH3F9PQR^NrHxHdS9Z{3W=S_AB1w z$PAF-Y-ZbYo3An9yzgwYsp*yp0hRw7nQeY^8|(ftZ~xvD(dle)IJ4;a-14HMA{=-2 z9-iyAA=11+aDUO&tf-wus_R?kTa{|1vV42@x9#qu!wR#lN~PpCiZhD{#>{il^qXOD zFJRyOK(A94(`0#!B>16&d~ux$Pw@pZR80@nOOGzK6fetaC9abbbu z9FD>t;HPBY;*RF-ZLD0+;`5&*;Y-_&e}2SbjlO|l6?>4QXVaF%u*NE zj(lCmpE>)i!5u;NGbiNj{)E~c|2?PWXxHl0Z$hV;_Ww=IT_%6XHtme`9Zu;RAGxj@ zuZ>z7JCm(e?8P*v<`+!aYf{!$Ee}t1 z4gJ1)o^`?v>#16?f6e+V_*G(_>c@R*$@ET4EBO6QmZ!Qp2>d}34hPn zsJ>^y*12a41bC|%${53<4)`_j1td;iA6n_@>bBK6q;P}x_u}X0KFLl0!8gyg_?gMm zU-R8nS6A;UTVkJgO5#qx{~U|P{kAzb4D24=xv#r(>b&ijPWs(;uqxwcD_qC-DaWVy z{OW>Ts+Znc$l4~hpPF{5vpMf)@J(B-$lE8+cv^BD$-K#vws^&%MXM*T4cMw_yi0cF zDSfqvTh5n!iqu&)F?wd?$)oFfR^}L}S7{pfuh{qe%DUI7Jm(SxOm1vjoA*ShtM=`Z z`T7F-zeU%6>Yn~r@$av%`>oiL|0G@tU12oaAhGFvZNKfcL*=%Yiasu?n{S$J7HaV6 z;~dN4pxEnMJKvo0(kPvCdz)~{pH}YYma=EQ9N784>}=WNwur_)PL~Oj3q;r29`)U@ zYAM>FtuJ~)r$A~SB0+rbY|ZAeZMB9+dMrtceb|Z~J!^EU@dJGl!<{WMIQ6u69o&Yo^VddfobzS_H}|D$ zYwp~*+jh}$zPf+jz4!NC&Et}=TCqi3XNJFg%?CzHS5dzctoOg&H|CO)bT~OlHTbS` zMQAUR`h8Ycal zZ00imxcRaC{~g_Re?GeZ>rGx6RDSW)RBa8BkdMuC;_H5qt^=}fj7~qA8#i5ad&08ymaFAn%AC2! z8UC|-x=rG(paqqmkKgE(IMtpvGi<7_wNhsL3IXfW_s$gs_az;j$MA4nzt5UW=e_hY z#1Afw*<1BB2bibB&YT4y|3Icg^*Sv~9 zb)sxZ@S)84FQ06V@KN4ww0Tl@Ui8`CxS*?yjrTWmbKhD%ft&4l(?d7Cha1-4eUMt4 zGBtkB8~&NL)neOukM+qO7k=$LRZ^`W(Mj*XfeoHjCspTs{Vkop=b-kkjZ^u<@@BvI z7Jqx{?SNN10&e+*-RPBe(rf3FP15H*6zU`;!hgiR`K3+x`nuY(?X5S(*!kri917U+ z!B@mHNtJJ5oA8>5jZIeN@6H(OhOdu%`a`xrlCkXFos+-gRn)GPeXY@H>f+*BopW>3 zm#^33KkjqAvH0Mc0~5}jw>8(eY8+r@@M#18>1k`Dd5#*-obQ;L#`OKy*H>4cM>?(G z2nq_S`M%~}<|8xy8?CB$64EYvJI5``i`^aAEA{0henx@p+oC}%ge$lv=lY~Qj= z!Wl(@FT(V z401KsM9ntOpO@b{H*e-O)k$R?TpKqZ^47U29Cy|I;G>n3xMM8e-P$_)*SELso}MRd z4pzRoVYn~-?WbJsz2j7Wc{QzU(d4^J=CAVAEKB)O7`^<~Q&6=p>!e$s_ch4EVYl46IU7&? zUcG+bq5u#6-?y}5^S~+P;Hrc_KZU!_?ECvoV0~3t)Ab_G^1poR*&^=VTN$k0#S&(9 zsWYzdcG<7*``LrPzPh?IX3^alXFKe-`8B-$`v2ee|CYblp0M#>Ke)#}C1lH;<%iF* zrltOTpW8H(JN?`o-j_Q!rJh9Uwhz<;+bEkIR50aSR@gZU_Vn}f9!nmY!N|U}$9~8E18)?|mZzSO>k^#I(I;ztZ1ZlP4f@A# zZcblW{9JF%p9euF-p$>0qm^~P?{|$k@jdTn|8y<&KRT`7{+X?VfpgA@35q5K4-{_e zGK4&AiTRLg9IxDe_^bOuC-aYI>i<`Btp6!yf`UUFSywWbb{4pOkkhL^W5iWW_IiSE1g(>Jb7Nj9;4J#KIV5z zCYR0RTbko`iEpFx8=dcE)BGY9ot#$?B+ts-oi}g0xAK~#il75>^=u;()0 z6Vs9~r+F4uce~DcsaIcQWAdGC_P9>;g}jV>?Ut2`pO+l!{PXC(e~TG^Sb|%$^|_Vv zJUk`yPw2jNxO-vY;UDwrXP-Ma?NX<+##;vM-N!i-bAR8O;%hCf8WAz)Rf_pb_K@6D zJvr`e()zDL1YTz@>7M-S{3*6#Dfa*6tM)xt(ea+9vn==aHYw(5{=pU;NB5Pjetl{2 zdX+nW6zO$Memg4y_s@1%&JuTh>)nu4*QLsHw>)06FMaB^87KCXy_G8a z-&dyGXK}oA`Qx^TJ-<)u@1OL+JZkT@0_TRCF?*}OKlHhgV^K?F6_E+a!8rd;TKmMG1@WS^uHqK4o{q?(Y(f+;xY))_;7YYOz0N zU&%@H&tawK`E0B+FS#hs|CUs)F=OvaX}=Rs&vT1cEYo<$3!3!lo?}rsM}2XOp_SmT zFE0;2bg4M|yieZ#*vry%!D zgAC5@4PI#xrCogM%yMrP>}|QKzC!py;o@D(ou-!0RoHdFa#eX%Y*@)WXWO##w%tUE_ASM-X}6$t zJ2W@crc8I+|L>Rf>psKeW0&?+ezuHfSpM^<@14ab=Y4RxrZscnzjj%MYKF4L%T~ct z>vnDHefRy*Qr{%e_JCZw)$dz>`I)EkRIYLFpErm7*VoslkIUIseJHhFQ?lu@*@W8# zhxTf)Rey+;5f&1*;a30ESs^X{_`hL5_3q=AfLAXa-ke%Cw^v9<$FFqL>@UU}vx3u7tS@ms+;+aCW2;G#vazV& zD|>BeA5nImx~r=~yMM3wbARQpd6zfz$XZY164#rQbw4w9XG>E2?yIFQuZ9bq`zP?l zdeNRl`@$u(kX&`<)6?Daj+dOV|Gc-XC0H-+%6qMn-|ItR`Q=dN)NM0j8kCPo zUCcXjanHMwKmH!w*A`f`aGNvE*l1e&t7P%#O*Xyq^OIh`a{n50xQ+MahlhvfY<&1{ zNz~P>H`D7uON~HtqucMza{l~W|G`a}X7>F@K)FMHPO@0U-(O#4P4`^L>@GMr!*H_J zl=TjpnvZR6FE_c>u<77EDG3?pIsLM`cd;v^aNJD#KP&U{vf#6`Oe_1@&RlE!-5{>=2H$w9@=4*HvdxY7SNE#l8_$zY-t)X*)sM6DY-gXEqUrqj zSnuM+?)$&rEuQ3daKrC;qG`7dh4Q489^B3HsG%lRTx_+)j5D)LC-=?@T$vi}U)yTbHUxrJ5&v7~m1ZFK{QP`>-&r{Z;Q) z20V>5@6g-e4z3+mJ>Qmh_s}=)zJ*N6^)D|i1r6Bz6Z;lD&!SN2e)n}7#@eQ1)AMGQ z1)DX$eByCav8-dR=&ZR<)pRZ<`CF{)Z)$bjZWnZt^{e)`RV{LkvRgAQD&0%sy1w^> z{>2pk757$ZXHPv7@Zb5^2KGA(yzFCdyM3?zcu8$zm-CLCeFxsR{+_E9SDia0uub0V z)K$(t;XaMaJM!A6`juT~D-!nGvpnJ8>bV;%?o}S2JFivvUAMabtS7tM_jJ@P&d5G) zc~$uAZ1ctE=3d_A_4d+IO^w!My+0q4$fU#FhCIPG1$%jry_e=Ox9E-?!GT^IpGiQo*+X}S+|Mf?F1O$H_xh?$ zNz0Yr-YPtJ;p}d|WB(_VP2hf_{Kt3MZSN4@$woK9YtpNNcXBle)Gi2`VNob_b=}+- zujc9XN}E695-?sPSMh)`F7Vo#na1XYc{esBDt~?Pamv#iTStLQ;ul#zGD>Zoaq`az zWnY`uclgXFsqbOWyJ*l|^XkgVDHl7p%vIgNUVTqhMJ1@6?``e%v~zP@PE@#5bg=99(eCLR*q@)AtUkG5 z>O9-(pt)A1FE<{a^;uz8!Bia;4yQW}H|-C`%t73xY~Ut9e5_jhmRfDer3?N3inUpzg&ZssG-mG>Wu@6gG+ zyNkE<&4r0(`?t>o&3oN@eEa_19Rl?ovPv9H;V!{Hg<}O|k5pTE*aSjZJaX<5|w1F%X-@Q>!&{IjPPW4^vHJ+KVr8z?d-D8&(B|8aq(N8U2Bi? z_Po0<4m37zNk1>wrM{%DKYl{^^YioFd8JI2gsqMK{I9*Bb9eT2J)uY98=YAkKXhei z)i2^+)Lh}uelp!jA@0WYi_^|8u~)HpsXD#y0wMtSB{MDtIY3Xt7)iZ{b;00agY*s327*=ll+^~}7o@$uv* zq8CNj-|YVJsQdpi-`QrtKR-Q{t@SsblW=m<_Lm3Q<)7U5oo%+X{C=$4&reT3-_?)U zpr9MEVFDYE%!}P+$7Rb4etg)TceiSmMWN8$qSLx691Ab(udhFs?b<6Ob?EAt`Jz~u_^6r)ckcHu4}6u{NlZWrMp_=%F5vOXXoY~|0R6<$dMx< z-(O!_d-~(**xhD39o2iK%yN!cNf*i5R2b++ZMpHe=9#P1&541fjlq%a-uqXa>eAAA zsF&=Qd3&3#?|i%FBw6FXe{xs$N}ID@)qi-*Po3LI;bM}1M62q&4z+5fd#SnYTRdC7 zFqrX`9C&eY@lP4|In|lJw`5#wx>r}Nxi)TZ+wTQ`N*!&sJiau$V5-C^UCRUC7d3#p=`~T_b=_{L_ z3Lo2DC7B+sVQD#&MN#CFL?ut<+gsV2Cke=J$-X{qSLy4l%KKjXUAQmI_qV;;Z1?v| z@U{87%ik}XXFLDdYc+$Ubul}gQcq1$T;C}8@u+ybMAi4qUvIbHf6*qLr?B3$-{jPa zT?(^KId7SMw&LKHr3nZHj`C`yY2f+IFK=(Re|vk|UEOca zgU`b1ewXgmR{z{3{^ZQe$+x!Uy35!9*?2Mj%H)4P9)AznrEu+5;e~~&-qU=FWv>_L zPdmv`R%x?5e#ykLISL$MDW|9P{+2OtyRtdmf1CBZ+HdiD9E?M>LOSN`ex2d6T+Vqm z^W*x)*)1h*s|^;#9NQKNB+`&^@V%Xz!sJ)h^C{CwWt-KnM!y1ZTI{pv5?CJcW&yDG1& zxc=kM*6VJY#06sRCzU8PCLQg%xX`)%#kJ_}iPH~AAOCti{=C(n@AvDMo8PNwJ~dVQ z{i%ve2Va{A#eOiq7$Uc5Z_Y8{wCCSnTwMI(V0&2L(^KV_udR(f!Tkobu=l0=p4-9) z9oS=b7Co)FlxU)uE2L<9;82V2Y_q`4X}&vi67-L%dQV#twL!p7D`driB*SpNBmiOP}jeG-OqK=oZKxA-#ox*v@tKfhj&uWVEg`(Bm0eTn$RwDu!a z^ZRChJX_#%t)Pj}d~*&LY`&QdJl|}*9p`+rYV&^S9tVSF-z}5Y-?MbKlyaxsk-gl};-MUxIwMX%QukxME zQ+0p)?_>M!mu7dkVe#YwrV6tSMc><=pE0oe_v7)GSE~=NtoSJ8cz4Z@-KBxG{;IDJ zyth2+b9-y{^^YGL+2uC$?$+X)QOIgJO)s|XaQFQJ#>f7<%id<*-1M|YcAXXTLE|5J z3IzvStN*QD(x@dHvf@j3=sfesayoo|kKUQpU6_9%U+uzti{qt}m0Y_nxy#ocVO!8C zp84gnzrEz#5b>RxPc2D4&R6o|L*mji3?*m&{N%RKf5@?YrQbVC&&3D!toBl_cM)IW zrkZ`#das6dSYn`Udtc}YCgc9I|BqHaFf+*e+TB%gTKIzgbYAH`>0`K5_kMw?_8bv1|XGS9x&Bc>N~&4U0&+`QpC;ay+{}d*9@9bp0pjls5*54PKmlWRDlo)Q-{M2~&28)X) z^DduNKU);Fv_xmT)#1f8qd>an`Io^@Lz9sMODRYH&v-u)qsy;k$%(}Cqa8|*QU2FDk za&}l3V;S6gc;<_5n^!+dUwZoOEfuR=8y}Vi^F_PgX&rsprF^dN?#mx)PbMk5mvyn+ zs_tUm!S0@RP|!kFf%n^;dA9LVU7Jj~|9(DiznD+<*5{tc^_^2KT8!tY^-7s8lCS@B zFyqhixVd&YHv-CD@2vlCclx_ir|qW`i%+B|Q;o12@1mwHY8Q9D&T{9pXj z4S_%Xgf%jGT=sl8^f_crMB%;Jj;&fc499Qh?Y#(e=tET2Ja4tR1XTpw4FHh_5e^MI~u+xIm+rZ#8&!#`WUa!wQ zJInOX?&|M(`+isadp0}&SwY?Bv*vAiuRInm@M5=K^QAj>-uABM*gJa}<}UYG-O4R~ zq4avJ`_IqMx7WIv_x}6wm|wa7?{sP2#_E4JPOd@UW0ALLSNe3fgP+!E@u@B1zLW&c zD_Y>ZvOW8{-n6|t*@b)ZNuPP1ak9UfPBYI9@R{euCiI+nc7FMMa!G-^#>%DzJLwbE8Tl4>Auj2$D z8-+k?f7i`mYTRAE<13HZY}U;UVe6uNwZqpvnff7kx!=;B1NZjc-w<6RkrKSz@3HbX z3kSyb)4>|EIfai&d%d;x@{~Njso9aub=I6YE}PTNKbk6``S{qz*G@YOj-1gsv%<)F z*}I*~)oOpgb=pZ{J*8?7lOo zb@fgkw|_3rx!*ae`AID)E7~||>$~q0w%z18EL(V>=g-5#@lT8QocD}avm+`^f1}Qa z+ws@ZektBbKDOuJ+zpMg>kD39S`~V0YUYLX#&>ZcFB96Q)@?lfd6mVAN9%;`t+OPK z32i&oFtO=<==!+B7mIggnv}cEdlfri0B!gwf_c+i7m+Dp3 zt4ubX+P%u6LvO<|@B*dKu#nRIuXZxG?M*goDmYT9@KCG%<0N&TXfETt+uPqK+~_W@ zJm9t6=k`=zZKd3HlnJ_3zfNfB_t-!Vh`kdGB&MO*~g69xy_lc$S#N_sVWs-QQpFveym&8BElkQgVRdWtZsb zN^6Z9k4$yKEIKvMwmRZq`0B9U zskK*Eg;pLr6CPiCQu}ey^K)lKyFAMlPv$f_)UyDz6>yo)%v05+4vDi@b2H2{Pd}b8 ziD?gS!=0VQk^3&Q6EY3F>QIN?hRDir>4F8>^}_t)2(cg4~SWo#-Mj3XZ&G7nS% zwPB?EHY6S8dUa=~@%b$=d#k2CEGt z{uWDo&%Ak)qm}*lI}1Y2FAz9<;_y6?=xuq1TwhY^*hKp|@4UXgI(&ZmeTRd~mMr=4 zE^_bXBe`-LxV?8A>{&RyZI8>{U1f`LKRJ9+>7s4y#aL_=JTex-K%=Nw#IjC z|Nq&$-*33{pwoIrc}v0Kr*^V=dIs93>+^-ra99*S%kelrRX5n|?3#%69aAkTMEw}T z)1dp?ZoRY4WKDaSa4p>J!Sng`W+Ew>Rsa8fk9_%1W{z!jUphxJo6Hp1e-FZ#Hy7QQ zJ58}#L&itRqU?>t`6EUL<)qTj&s%zK?(Pjijoym8%U>mli~TW+l`E_>+?W4M{Hm>J zwB5fM->)6;I;7~_cHux{bIx{#fL7t6gWJ#6^xE)Emuls@d&Kzv-}j1po)vRw*UkQ2 zxZsfS`E`;4I5xw)J0*2tGg1ZE{E_tA4v-L3{DPKdkSLgq`HElKubrVY__L z|9^jPB&_@zpvyP=)x03Ru07f9FPO?#+?yGfdi0g$nJY)Rj<0iL7rn#KzDsFu;NoK@ zMNhK#eV$`keDUahy-)8e&InHkU*AiE8IK5473j4iU zoRE0&?B}w~7SW9LdyIOjzP)kOjk0+b-T5rEvO}!e>8oEXXpQ9S3i~&VB~uRBHOx(D z*ZNzMD}DF<(d5}j?D-a6(e~Rf-dBFBaZj6{!wF&c<`j^Ji&?n4gXg|c``fSzV|F3(W`f`22mk%9exK8ldUDdyExEVDk`+E=audk0I z1tA*{jXp2)ovo>>i1KGbde?U{A4oQ{1?w zac^kByHJb2Gi@q`&Odl@Wu>tt>qD2A$&)5quF1Re@R*VP-;zrKZmk(VKRvB6vHn;7 z=Ek|rcYl4CKOpP3x9aPn-|u$!i(Z(h>|T+U{QKM6m)maV8H?nsH`$!9;kneE8}IM$ zKVR*=-$O?z$ewOlcn)o8OWE9SN{ei8q z`k&}FEm@|<)!M=HZ=O`0Vv{dkZvOH1&Sds-4^{Q=X@75S&yP2IS0KBKzbgOgs?eC9 z$>$|Q{_S|@Dy0~+CZG2&U!<~+LCyui1(PJh<}PYp?K|5nMnCZ@=PQ} z^a}c3&5fHbI`?@%|J~yAwlzF$vp(>)Y}TP`B%R?)h?fH)Mj&SvAh2{=D)wbK27eIv;DD4 zHq7c~ru2zii;cXznA6V}8+&On{j7y;`uR}qr+}wIUA3~FvEt`nHth46lK4iwuVB%h z8O@U)da(0+lC%D{IRAr{T*}*4*+ov<=f{fLPt_~GbZ-mS@=&S7jXZr4!mhu?+&}NS zTzJZ3k*P+XL-J4Cv>f00c6VRw(K$c+-0hv70k-XX+4{3=zd#26SDbp@^`xfIubjsMr_SeHO;;@hqpn#?uTOUq(&#l$RDn z>oFeJ2M8XJ6}_`_mfQMQ^MVu3EOI_{?rpc$oC(}9XO-gp=h?Kz`YG{0vOCoLcj>#m z-|ZgnG(1+#x4i5Ao12?g`ph&k$-84=RrRGBvJPYS`^dy)x-lCPwu$WVR^U4kUY5AA ztNG-bNaLFx5~f*GKE45ORQ<#hYQ@sTb>m8U*?S(<^XL}IXjsVk+`GG4--^pPvmX0=X{qL$z7dO#!qy?xE}6vrD*R0- zr)`r}*2P7xdya~%tDYFY=;XYBy}{v-l@~pU{5u>`R$Ua_tZBT@{oOir&$n9|i+SvG zrmPm0QI`o^R(O|Bd+wWMyie5TRQMlk3b!^d-1E85+Ll}Wj=29klauBAm4biWE?(;^ zUZpo(Y zayz6C_q<;jygbNF{ZiSq2lKY&-VSndQUaZoQl9(lOyrk??C+)Lvgc0{nBO6*#3H0^ z75R!!QOLutn@>fyCBpShwxb7k*hl@iJuSAr{_|{Rwygg0=H_J3F*yCU*XFQa+^fdT z*(6||5aw$jo<5^j^3|!M1wl)_LcjSLRDH?Fy0Sq~*0yR2^2}{V{M}t&GyebkYiO*X zSHXWrW}1F{UtHbKRLH#TgH1b1Uz=Uo=bT%Vp)d*N1UMV zQ~wgj5`p(G6@L0~9O3>Q^=YQS>x2X1+E1+|o-$#XUhX*2U@^h5(6K`qyy@7C>E*q> z(IIPn4%ErqP)j(}(pmE6hM|<*pHSq9@H4YaXP=vI@4wjX_U@k|Mtpa7l`dvx=esn| zwpwG}gkQFGj&XDC>*Y){E=-8pns@fM{2|qM>tc6%bi^Ow6J$B4y=dj1PQ4DkN4Bym z^En$U663w?gda76rk3w+wZ3@@I<;JOWnxT*^1I8d(CzXZ(gmDtk$-ON4ndhKUc`tv zY0K$G$CT6BQ1uX11zu@F;JIW77eP)5Sr;=C)B(S3$a$ow!A2`=jmO14o-Xt=#lTyk zZ(P`(A8%Fpi3PMKh9${zj@4J;udlDqUnEP$cIJfpN5$j+nCITwGV|1Q{r4}bcRghB zp0>xA-|mOP|GZmUH23~~XLM@1{`~iK2b)+k4>q-$Bs_4qzt#HPj^@Md`%7P6yL_Z` z@{4KFd5Vr4t^fZ1E{@ZV+M;pPO4=;vNztUZB}@Jho`dT~CAz89kU)C;8Pg3dh0;$4?}m$i5yoduq#%1<=#RPX7-3r5e@nujbWCxi2r5&)@X#&faSC zuWx>u#G5yMEPi@QR4aHHPbF_pzy7`-hc4{)zns7SubcUu0_IBJswIJLS6QKRncT~C zSz|eEFE4iYkKU~(f|$kJ*aDj3Uyy&_ZcF}sJFCJ+Ec54E6i)i~?)K^Hdlk~p&AGBI z*ZS9w$Njzk7R~!|Tz>!ZORO8O^)>V9Ms7-(^?FBJ^+q4J4W}Pl{#EMT@|b0P=hwP@ zJx)6=D_*J-M*hi-0pF)mQ-S3(F@ff$b-UgpfE<5!uoH`2Hq3mRqbwwlZ zNWY9_Q`njaPf_i#MgDd-mwkWcd-S0MY{+%y|5+bcdMd3jB8 zEIerWq3OfT^!bJJj4S{BeD2Yq?>EoJv;Obb=}&8?)^)Niun%0=FZESw{{C;bJiakC zy^ikTQwdlTVQ5wILf~#yw&5qCipM+t{dyhQ4mxJ-%Q5NvfSpB8VJW=*=HF+bR*;9~ZP%#r({>wX2l7vJo+FFY#eQ*t!_Yc_k9Q;639T3=%Id^S!br zGFVUgMswlGNvbQwbXq2JiE4fL_`d91_LuYZ(-wD&ufN54@e-(p4hlMUd1dhO6|UXK zgJR{aN+kHXuB?yOkNpy3ae||)a>YyWHNGpn^I$5STMnk+Wem}ecXXI1}i&-o7r1fN%aes-~rZ~x}>^Nu$+r9Qtuqt!6|Tr_yW z%MR%S@k-tu11QD?`q z|NUXD`G03;@s%Bg%C|wY$XE_>J7WCiW6bAg;o1f*>z!J=Oj1vYI47n*w79bF@Alc^ zJ0f3Re5`rDI1Ik8Sxi)WpLy*slV1;-`DK;{Zm^KCtCMN z=HA+}5HxW7{_I@q>0#?)606U9O0hnR4VC(-yi37UD{}R{m#&c;xw>CjmaN(L>D1~k zFBbO~NFMy3WIe5snSDuC$&Rb~T90)79rnkV{rf*F+erJe*osqYS7F`wJojAC)5_hO zGDDXM^4)3PZN9m;dBcsTj(3qJp$%E5pWVP1SO2%vs`i(OrR&r>E!mYy%C{9)Swt+g z{uI_N*8iC$NQdFT-#a^tKd+kJE?;-!(>?k6KZ2WNUoPu8__qS-#MC#_WL|!FyZwI9 z^K)}`o>tz@-L6@>X|mab=-3ST7p}(_+Q=nrB#x` zR`O}<-R~i#4Z^k(-Y*|4d#@Cg@^-2%W6{%7k|FC~f1AGgNs_32=x)LLmo>F#a0D!N z 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ðL2rLD?$nZ7^H=7|*3t>XRPrf&a- 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%0An*^$D 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ðM-%B{+CnZ7?>%@Y|{cy#I?>3M%XSl`jzId$IlODE%gIcOOa zbfjPNU3vQWeOJB>$GmNlZ-NHWd}rG^`P)p~y2gS3LEMu(9qZ^*nNoMI*-yQ@cv7jr z+bw=~d&17Wd9-NpMU2+?eN^~7aMrKCui1>) zUw6J0Hw4=n*8DI?R;pL5{rB_vn$ytP-}zM^9x&eP5lET&t!uewAqSUi#d2n*i-&yY zTD6ADCInTUzqfbKAr@`;NM|9rKR`xoj z#@*#D{oA(u`e#?+=o*3R zq32B+osM^Xe|vj-vE7@So84!!g~l+5+?b*{+0rkdL|pRy`iPB_-r8y_tS@|gY_pJz zx!znhez`NdLwq*O;&}RI`TV*|ed$ZRrfP0qxZ~kG=EBC?H=Eh{H!*UB7w_!(#K|o6 zT|rou`M956*ru}Ic29Eck|)Sz9_ac*cnX*mGzC1h=|cL%yd+#xz&+AfrhpCV&8^ny zxAkyY&pf!4m(BT1_4++VT3_t0+J?$Sg0h5*cto;l_EqENRobhz#f#>xPkbM;vgqO6 zA4lGM{8?3hGDyuQnoGO%_SWqD8{PRITBf3`lc=^n?P=>JQq8pDmh$QyFQ)v=__=(N z_svbIJ9B(yn@xScHezQ}m#Fr!#FA6pv$q`YwiRqy^ERdJB=-&G4cr?(GnO%xG;r-M zTC3%)RkTWWkymrGQQEoATsO^xkb|jTRRxS$n=ne?^6r>b1WX=2eIIrs_Ubxa;FOey?ntPUu<{g`Lw{K;Yyne zda3vHNqQ?3n16kLJlDM7>W4GN^~u+(JFEOA>+LRko7B9GaD_P6n??IXP*~l(V8M86KVuHH`pW53``)q0_YU zCXK)OE`PoG>FMbce7koj~W~|;E`tyO+*+l&=r5~UFuSC1{_uK7%Wap=zzLzCYj5^nQS{nac zuk}LcTrYFm(yPYJ>$Lq2o{BC$F7(NA*51eAf4!#}CEf~RkgI)=C-?Pm?%c4ezSc>Z zs4E)kw!5o^{Y+OC|I+>7(b~Gdzd(10daax+WTkvS*<9(Mk9d9r5`_dw-6@AKAD#UCz*VMa@ zSoxSIC|IPei>i494cha$gYxicWi_f*W^tSlrx2t!y$mZo3 z+}(Egd_~m5Wi`8GgG*PhZ|+#WF3%T|YMtlR-4ce*?0)3Gu`^fHe}Qpa-g5OlTUN&X z$_l%2cDa+@?{9CD*Jpq10?+Om-|^k?i&tvHX^}N`KR%t_8)M-;ZBJK{=bmMXmpZQY zF4wyA>FMc}!OQ*jTq?S{YU&d17>fpeyB`hbH?D}@K5y}dPL3(IA}4aU-*uZ`w`=x! z-9s}u)0|ss>VCaEIP=hgPm7Z;uaB?4wW+jwA%|Mw48!DOZU5NceSLjB|G?_91v4&R zzU=&c&AiMO^*7Fqlee^{fwPa||q-+PBO`BX33vfVG&K)Y50*pM)#BuWJXt1q-=(R=Og}i78_=@ z$F@nNvUG8E{rm9n@Ef%wEYHs#SX;Z?Z*HXI!QN?ijqfIji`SUPW*6=>yO%#r{Hm>J zv|0W$W6KSbL5stdJ2tc3mJ3|~E}Uk6fdT>jYRHHH5?1Xg@Hc-naHluG-{b(bVK+RyQ@ zXC5gn_;H$+zvB|JX{NW_dDI`?@RWY=NEYF2}t}(|IdkedJtGUi!{XZ=0LKR&N8h?AW3E zxva89HUo4xOx3qHj=E7cQnxzALTWq2tDV03Y~K`ilIQxznj6d!9Srzqber3Wo6(Jp zL!Qx%{bQ7JB4D5Rz2%!XFTJzmptt>jQ|~SwyuNR}@7*n%0~Z~24Oh25X|GW0mHv{s z?)D6=q`T{0oVc}W-HwUfeo}w7EWMMo{Cvr6KKn6d@aJl8hgja0Jt^J3ZIM>(Dznh#_cOlQY1=loUWQKh zE{9C_+KJ9??U(;Q@sVWs;%x=o4Wd1@zrQ{9ku*qPSikJYgXXsj|2ICmzadfi>&wgT z+A?9Ml8*}AiT<0$Qx_MxXNRFdK-al>U+b5qC@p1VzneVOpiK7;XtwvE|6`^M_-yZ| z#gN%vRVVOl?@f2V7;#m|Z0`{-yE=S*+G57* z%_o$PbbnpD@V;X95l%KiHl`4x!%7-F1zYp(&bqDo#?J9J>*J78|JGG+UMcKykOx=Z zd50Ft?tZ;)_YY$!zgPF~?X6zn)XMec#bVtbu@&$3t>5>k`ecmSg~Gz<<#Vr`&9JiM zuvVFso7H|JGRqp$Hg#HV+a)Hx{q4k9xx@<`yOO`WFf=KDXQLulOvWMTzxkEtKItZB|*mnj#YhmaWXaGK*PU3Hy69} z@5^hS>UZrevt_S(-@=0pM)$hUPuZo}cvSdtbg^Lt!iONPs zUllbky6D*~9(}5F&D%#SFQu>AcKCh8t(#kR{S>$-w^KIw>!S6BAH$dX$;!!Jd7FLv zWbpHuekr}1=WmRBxW0aG!t2A&>`Pt<$S)P_-*jze^e5+ejyHt0z+)TBvw};1^@e@i za!FXj{l}3zYQI+gFj@cl%yKoZb!tZ^M&#T+zG_0|bctG~}sj1p4 zA~!#?Inc(Y8D!}wxRCM2zQ6pei>j7Izj@U3{>Oa7W0vV@(-^-+Jdf-iHGf|EZr^V?@2?j;QOi+w&c8fdn&IiIeFNm`djv!u8ePQB4hGGzlW?iwNBVY z{Kn#{w6d$Bp*z+_Z~yQrpr(q|?fWEE?-i-1r~T2Jv)v}~5X-*lYf_nhvCLK6UV6*( zZqKsG$*Ju(6w9WWTc7rfd31E%h0Oj%M~~)KFPX%BD|}8nyKR$M*26=sHAh6&u?NOI zIyo=E_VU^_p+?TX`6Um_6*QqNcleR|*>&If_vx!Dem=R@e(3OpX-|67mBcXxT*Tmk4=UEAyCtecl{Z;xc@t0`AY zGoPQE`y=khkLi87Pfd8^eldoX_4CzjyIfX!`J7v)n0V~mH!A;?CfY5n{nc>aY*|;t zc5SOo+g54kJy|rF%|2Ii`;C{zp|=HHY(mY}=GnDuch5T>viFLij9`0b?1|v*>vP5Z z@1|QN&bc@*`QYs(o|A9f{ko&@vD5cQp4WC2d)J*X=BPgOXum{dlO> zSo-d-+bNe12Ye;V+-Z{WSw+8rWC2|vawpEzG(8~r$YXj(?3aCyIeciF!xutm~LAC#fT1tm<8$Q z=au~b%YR{Uzu9xoFBPhg>0(nq&~$O*8SRMy4c+sGbenrt#pi>Yo4!XnscKgYFpOP=iP_j-tUvO_V9>{=*BE7XDk{GJ^$b1Gnu^TJ%YqjrKa1a? zyS46b+lpUD^FjM#U4-<%wS>k@>a5tLJ}LHEna6yW6MPAe9oT!MOuOnI3-52YW^8mz-X03S|^V9a%|NHaP=Kmx1k5fSLxY%1?*A29k z^zH5MQ^RJsNY2l=zt6T;-hSTx$HM!4LyEg594j!%BIOW{_2<5*!Zd}||D*bLZubfRs-p;3DaSt?9%zl1)`q<9$Syk1y zHz(hI0024RJdNxB zS@Zh=US4xooOf&It?w4oU1V3A5)^Y`dD79YFAv-0i)I>EKAXAR(?f6H?|0U}-fq9I z^M3UgZyN?%{^YbLC#L`Wep~kwEUvw zjq=U6of!!Y8=YFY_Pj8dH^HuRf^0(ap*CLW6_J~l#h4fTd-CXLw{dR7hsj=3wLU(V z|2t>;%k%Z?9MaFnZ4iF66f}i*>GJW581f;NBx~@K?%@4~ZtA zYhxofsmNNDbnGd3bw%^nx7+!dkB)SPtiHClI~ z+n_b*Tw*#d-|zot(2d#AaBi-(_%6Gg8y_Y}bNkGSZVc78Tw3%UJWI`NZfVfKa3|%! zUamW*4&U`^{>AI`|N5#>?K0146F2tVU2<(@@xg%H8yf_5BPZ=thl=p@vgbES8VwV!Tv75;deN?Au5!;MxaO$6c;R!@%hcg>)Ky!i zuHk6@?#3h@vqOBJx73PHj;&t%Bc@gwzrA>A!Q`^#FPrDJ*BLk39nX(3-}&oR(3ki1 z|8M`;V^yNtVe{+X@Atiqt2Wz2q+VL+T>Y+PWeV{#)U07)vniXPo+YLmbw-cRw{}G^ z+udcdN1AN~r>t3<(&lLSA_FwRFlWIj`>A~)6DNaqVXpbKH*#~@mV|>&eBbXBpZ`2D zi~I>{>un`5^Nx`-K`nH1`I(btDil^voBtKnVQEO88=bFvy6BjC z-Q!*F<|sW0THsJ8^ho%6dKdE!cK7CH{{_~LQo486#m<-T+AS)6rQhz?gj?J5k87tj ztKGW{I;SzTKl6y)EGRxw$t)BfrbpM%?3IzHYTTeC?y^ z+uQP!r|X<(|N8oR;J-gVS9(p=`eXCv-Tn&))`r_%W@(e-*|Y!eTghGjeqE}W*UA+d z!(f-c=cC%~ef>gh%l>DxvMuJ#ul+W2;cTl3 z;_&&ou*?nK)dC40L@Ku?9IV;(WRmxbF70(2cpsbf*Zq7dzPRUax3$53$ep`w_3W@CxA+1T%cfOZpZ1Me)3Mc-am}f%@ZFhB zclmd0FMAvH>c&S-J>eUrrj?&k)_!`_Ro2C3q zn3|VWq}`petL9JalLtk6ju?Zt`(NDhQu)gf;mFfmUl`uGzH`~UdFh;G2Xp@h-?Y_= zWWVV5`pVgir)Tu^tgLQk{%g#r1?|y%)$`Cw(K;t8)MD(%$JsjFAG^;*9;XZ`-s`_1Y5k9`kGbt*l7I-*H5FE>g*IXmoU`ttZYUzkH4HXYEP znYfN)o87IS5vNsGn2A<`w=&1>2F+%dbwuxc@nFk-rhGL0`D^$ zs(sfqyu7t_^}-L=K4dM(SY39fD=@@Ju!nhu))$AJ#`~Y$`wG59$jt&9DC_ zx%+5)p+)Mckbg&Z3Iuc&RC2M}*8QeI9$tcgQ^UC$xF&VrGN97%N^m zn;GRRXC$bKo|?IHRgn6;sam1S?!38_W^tS~FP&e`=0qD~%-N?#r_|Zv3;2yL8mhg0 zlBAlw{k`+OXU10H9?W4=`J}%I{Xh1( zdd@W6&Fdbu+I5#qTnt;>5x&%MYMJ@V&5!*SA8uW2lyKu=ZTX$T0b5Iseuc>uY9-i(}`7YR$WrqbwElxa`imPkl4Vj ze^(V$^HwkSn#%qDn0lT2BiNK*#L_$KVyh3w)ikTGxpNJ)_}q03c+%$P?MS~@fs1Yk zJ6x5EJnFjqjQeT(sWGRf>7GtazO`j#P5#kSq9JP{4EYuZ$K6?C8~3JDAthJc*vAEQ zN=8Gh{089#%po_XW`{-w!FK%ry|Qw0%{{ey%^TGH{k(RM`0;Yzm+Chc^yb7yHm#|a7LJ~w!c!jm zrrch1lf;Gn^8P!L9gX+x|8VGYjen?G$oG=cj>#2O8#7CfU0akmwZ81vQD6H5dUK>_ zwi+im+-j}lf6U)4vgeeJ>DNQ=o~(oIYQCvBUE$Uh@Qk?2pbGK;tC9oEmY zES{B`drD^i;eL;8vxATDmFTQ#n$z;*@YR`*)G}D4%=29QHgc8UOg$RmnUKJ6jC)Fj z^Wg`T&*zqFXb36tX2_&V9^;6~k+Cd_I9=Q6XCc7?pY0Cd%lNS|NhnWiUgfj=H&yz9 z=QA&*f{w_YxGr!p+x)-bd#g(0QnyrpSKsq!L&3vCcM@hjF=v~ppuA?$u0NknXMTF3 zStG{D!(3d>=ulDYDk!FT;5LIs%#I09jeQ^NI#N=*t>&lEro(!AC!#kSre2pn#!x#^ zskOJUSzhP;%7%-q%t`((E;&hunMSEbcb#iO=Q7RxKGS9L zS6=fw4_?(y%rtn~*%qR%m&bA5Uc%`|ub(fTP0eZo*f%xrmd8KQt-TaF(cOHv9JZ<1 z%B75pYPP=gexvhM z|g$Qr>lkjp&Xxy=l$(%ErKO9 z>?9spTu(cFO}9;G&XL3m8)hBYTle=DZ^cm|sio*Uoj1XEI!9$--|76})g(<0CDWbU9cXy)A@x+-H(TUaRXv?Vbe@CtTh`S-Zk^5jLqE;B=v~Y;#}5?~)yp&!h4_WduCBiR;Qm9sE5eHRn>YEs z{lcO_nuV8-3?io?~BnVz|M-Lm!nkFLv+WV9u@1qAI@sc1T59gZU)LB)Ql8 zzI~;);(s2upSRTd-Hzr`?q69yd%isGVAv7&|N8!a^RHPem{&~Y`<|e9gn!+g<%gfL zg>|&fS8oaxQTLnkMdaPKPuo)?MWsbLC$X)`irMw0KeRQ#Ny1`l_i{gr9Y)dH^JH!R z{dm0XxnES{)r5$~X~Libfq0Zx%k2IAZugyl2j&))pG4LR9LusX5%|%pEwm;jDcr(M zvT@QerKZE}{GYvyh37CcZT~jcsx-|t$85@~^j$~w^nT>+y7s6jch7wt{Z-Xk+j8!m zc|I|r_1Wk1_WsRmyxVM7Ie<$xv)A&kl%(=ID za<}NJ?<+fenl=5`L$QyURNC3ff-Hycrt4=1}5C*)oeVL$(zU*lxV;`*N&TevzPZ9N%BPhz$aB z|Gw3!_djODSo!o+sbFmB)zEdXw@lw9>1}<4Z{y}UwZ)SfNcyWW3(JhN=U zn#2bppN=elH?cCz%Cf^wAmi2+$^O+2{~vsw=sqi}wOH7H%JdH(b-q+?kT$vZt8iD9 zl5OoA#qDc^SwXAqs~^@{S%cR1?D@jH=8c?e&D-C%uJ~F@t476?lx~{*hj|tFsH?wf zyK`@SFj^5jO-w)T$a9nLudZreF8=dw`Ko}aoLBcM9205~pM9;j?qY8LoM&sr|2|^r z`5IYyh07^z<1}x9+*O<+58nJFk!1yLpYwp_$nQ z$@|mKi!aQErfk0J|Cf5-Kk)R|p1`^@Q@5|GE>n1Oq*J)3=+^Am%?Yh*Z&x_!?Vo3P zcy(A|{(}RKZ(gxzzuzG7v^-g0Vf_9&8UG{KdDY*s!|v^|d>CiQhSI>6PpX0Z$0>E#m$XXbU>Q zL`~+_#@+9Bb=xm~xa8m4ySv-9LRL8R+udrq-}?OB?^oLUrk_b=nrjA|5^Ub!yy)h# zvW~e+w{4ztWwFaf^%C$o0Jr6CKE2p)|L+8irpE95PI|=o{%E?_-6;nib$-;bJ9hL( zz0a(KJsjH{_k4X^@|ERW$N!hh=WmKR&Z73Nb+_U+4(#O zTe`b%S0n|k^qXsC^G_&_tu*(jVe+wnlas=W<&9+Qf3MU6^#%RI+bw#RgEL9sR`;-9 zM|15R!uMOqG9NK6m}hZH;ng0?hmtL4l60pl+zQ$KWv8TZnYQ@ceUn3N)_xM>&ic^# z)Aq~N51Db-X9kNrk`JH!=tRa2odd5nWc33Mf|JV}qAnTgW^#aRIqmJJl{O6|c`u(oDr(kjK^Ecj?fi zzwWOqlxY9K8{V$aVRPuzX>YxqS0pvQb zZ%#k|sA%=BrYRb3`@n4d>i%8c{KKwz+HVTu^&-=gbSr8H>gA zVotdGJggFUsygot=T+mor$9^i|4%S{!>GRE)A_ZzkH6KPyrjO-t2y2*{>kq@b<4V@ z$IE42dC1;dRXXKzi~p6(nC*wX)U97KTXw3?3*Pgil`*mAUeRaW#-qY|%hY}6NbG9g z)KRzyydI?U+1c5F%l#tnZYz6tr}KSle?4e(%jE~qV=&UKbCGxd>ra9leE3)GnwjoD z`E_RvS8`RhO?SMzspbD+!G*d}TR3FBS^vC2)CzlUZOQx6y~=jeCH=hLjE*;IBwjn) zXosyiQPfz@F~xIXp^1huT?OeQH(Qbwd6G z>nFiSlVi;92H1LQo}9j&VeU`U8OK5eT7EF|UV$D=-S=+i(}nGFkB&*K-uvK{y!EMB zrk9I_pSP60z82gscUL#Om8)sO-VQwvz6%GBt$NiipC@$V!M6JzGdbjTWE@`gRWo>5 zfS>xcvS|-w=2*_06g2(T_I&YOX358VZf$uPn|vVEjPqpY4MiuF&P9`#z7EdNViS%8I}%I|7rfDn2M6&oWQg$1bn! zEdQmxOTq5M$)6KsGtaO5dqp89D)b+xrL@nFq~}%Di}xIy`s2aDX3gcFA9d>o?JRl< zI&I!aQSSJOB|8*U1XE8Qb) zikTD1klEQ0Gid;(zz@bRy1L(mu`22AuCGtp3ieLPshHT^1)9db6Sh9i8M@)Q<9_O- zdGq}I?S6{y41e9v{XCQJnxiE$>K=%!*65s&!5TLwEK*=U5eU? zGFMT(j&HU%CwxrXe`qA0BHyJ(WC_aylvtIs@&OlQ65m!58? zlJ#5QeeLhGv$Hm?Ee9Q=@_zUGeY5Ky?}}dHKdI(NTE=A-m^_r@rC@~>E?WXDf zqE)rJD&p(Zj(&c2)_ZT&*JX39%L|&nTy&QYe1FeBXr4`FO#ANN?`FBVo?f6EZ5F&X zYU_(@(fLb-RJ|m6?0=%cXs_ z)xUl`?&qC*ryF$l^QF}3v2L9wZfs0m;XC^pXpH97jg7+7|0~?sRmyF@+hd~AAL;qw zF@>z#r>fOm-@o9N??qv!3k%+C%es1M#+w=Lz0&5-Iw!n2;yW|{_BP(qx3`X7Pp<-< zc^$IMXQs~eLf-F>=BAuv-_NlrR?t@eh_dC%Gr!laFK*KN5S%7)9CT4yFX!%iCI9YJ z%H0>*#GcvDq8_q3tk*t&|KGAt%r7T%gozkFVci*9Qy3@Iv^akMKAUB!|K8r*+|HtS zvGA$gUazaG)@*R^lko(d65cCeIA@xEyyH~u@P*&+*PjpF(XF?uqff^2(A-t9xn=$@ z^P!uV1Flxc^E6ys?Ed*Z=tjz{>-)}|&D-^GSINsmFK1YlW-a@0zveUV(j`|qf4u>1 z&PHrwo)fsUDD~I*`hSyOM*aNs^d)HfHfZ{rrSz{bi|`wZeV}P$*QD1^vsnWFH)WW+ z`g3f2aAM-&FVALozg)F?-6D4R8iNf-S6S`Q{CvOmyDaEZ<$ZRSyJB`0Nm}h-dV6PQ z@Xz{}FCV|Su`&6|BG+e(dljAAJ}vNTX8Za$s{eSk-H}-ew?!|0cCwkolWMp7OR3)l z*0|KLjY+QaIF37U=NP=awAA>!Fz86Dcwulv#BR9xYT0Z(ot~X9m(4cH-S?^Ki)Yi} zE4yb%dzgSXZ#O;v@ln3gMtk=`x6n61_qS$;FMB;zD|C^pb=d?3i-T8}R6KNQ1(mOg zt)2dV^rE(Oye{}L&%U1V)O3CO`r}%cEQ{k7gO=7TINXLYaeQ`;Wix!@_`<1utlZBo zPtgoswD0kmnZ|$S&2gB2B}6N54-2o%46$8iH9t74YJNn|>uAdUr9SV)3BCLaoO3?k z%HC3SHtV1Lnv9%FPZHRnj`(5HdQ$26~w2$GCIQg)Mx7Zyw74=a&PzDZJ%pfeNCf2 zIOFZDtxwGILFdiYotytZrm_5O1E1`z&*~{G;;{>2c7B>8SK=|-eE)-8XS8?^XubWQ zv#4rW{8x_aMbFPAOZ!(?X5QSSDyk9Cur24N)AqY%(zbuUB*Y~ohfdp2@!)nof8fnc zsXr~Z-;0{YCu=40D?aq^X0Q2P>1!i4Dh0=Ecx{0B&gikWqqlxeD-@wQ~5(5gPzTOur0Q5@$GgTH>UJCD zhfXVbU83~b!#uZrraNfd-ze@uSX$h?mu7E4tIRvIuiO_>U3bc4;udcVUp@g@a#bK4N%xurSQjuJIj9=9YrpWwl8f zYr|rGEbO?wJwH9<3a^OC$9Zjccb5k*bYgYp_2?GUy|G~vX#8QvT*vmZo10Q+9^#X= zdQ$(+U=90)L|I4AmM$MMf^-@UxBF4lUMO=VYN#Gka1IrAQHegqvnly+uDrjJKl z+LEp6{vF+GzP_3-yh-_f=tHZL7Y^_D|F*T#%C?X1J>Kp3>}SVcb!LWP&Xa_r z@;tGhdot$ex8E<@Z5?vpM^j9~lSMvC_YE%?ReqA6+n4e4UFbZC6$kg8xBGo2L)mJA zxIw|!_x1m~#b;kx6KQ;1ssG>QUh_|B&sg_Z`Ls}j-p+5gUjO)AdOg;iov%jrSN-Z=%RyPFRrqG!t*6bP zBe#RY^(Izyc5|&R$UDvb&i>nt$9Liu$Sq_}ITrYQ=EZ=MXKK{gVs^u}hVN`=Ra#ZC ztGByL;nF*nilT;djlY9;fB!l6{ee|yUcWHb4te{Q-~Nw45r2E*-DY-v(6-&BKFqgN zUbtNO`towR#N40zU265&`sY_DWy?10DbTMG@0?z9e&x>zvO6-v8>DU5Uly~z8mYCU zcEUGBsm{1(%@?k(`j@umX%aiz?~OBr|FY}&w96F!nLoX*PkQD|hkqy7PjCz0mhe8j zF5t`A3F|BLW0oIYJSA2tq+(*E(hD8yH=oXD-d?ZudDW&TTm4^7i8y`B`*xyf+ymFN zym>EsV~#!f#UHaT;rPsD3*>*XddzpZFBW#Q=Hiz;0VD2J{}Po%jU;z6%dLFlVV_&O{)iE;hWmxpGu#iZ7~wZxFthC0@CyQPjPlo8LM8)RdKfIyZaU{r^*Z_{aXa zQ@{WC__&9$X+2~&^;{;OLra!@TR3xb+NV=bzHCgcysB_VF8R2ETKn0PYj(d|l__B! z|EctxO~Y|GbiAac8a+?g{8SS13_q z9`18%=|QcxtJ161y02VW+v|5PYw_7vZr;;$9;UB&6uB}e^;E9J7-y-nV@Q!w^dAkY=gYN(J*d6>= zh?8wW;I+5+o|o@qUTw5~@%c3ys?;8}B~L2gw~{hDb7Nz2g~EM3UWo;gHWdcF($mlF z7CF`{EpJtRoj2~2t@`uk&g(|&yJwfN?zs5b;i}l~i_rWK73$JZ z`?hPTSF>zhs#w3(>CC-bPunlg-M{M0?WwBzwO-3J`?s#yyG3@@N2cvY#h-n)XR_}s zjz98pUHpE=`bGLYf3E0XQvZ3jRO-!_Jf$1y4EHJ(r)SKI3&|{q{_ivA3j6xxl(4cT z;nDYf|AS__#E)(1+3zl&JMm@Qn#&D8na?OE*fD<{^A%PU zJhi*a@|?*$?@q^@B++-9PIrB5un4=9Xdrnk#MwD;|L1eoA|jm=*i7U$_q2(=OWBxI z_VJN*M;tQ?esWd()}4TntBq}97yFBF=FFOxG{6L4CA5M_t-XgFy3Qx zczCE)XMQHr^^`RA>YtxdQ`%!=CTw43c;qV3wYSA zt>QMFN<(|DI&@jQ8nbIYl6`zM`^7JuB_HUI4OyOgl+ zajSS-!u0Uk-`}2geyscZYZBLPqlI3(MdMyye7%1Exz!aLFLP+`fBUHOyW_Tp&*KhS zds~F1W}cpQ_R3;+{%hI>Q$F7I{zn`({b)8uD@k(d+R%b+IH(FyD}LH{)g=<$-F1o$Qmy3rl@&(&GecN zanDK%FRx?RzUyk2NA0rJn?pQ)E4+0!nY8lE@1^S-L+dZ|?SJ}+?aJ!~zc);DxcB1v z!MW>Rnq6f{<=(AxeSLg>kK~$%$^R8Dr=Fe`^{$HhQ^x*^E0KP$*T(*{%$08lomrkO z&NA^^#A}c3x$IBY8^;N}tzBiZqjK5pEzL1I^^*MSS8TTM6Uj9DfB&t8m&zse!uf0! zURuFt+Y#oyMA?p8}S4{d(ecb2ey^%Bjt9(XZ*0QK&fAYlsSes@)<^wfM^IBbt zFMQr`-{5_~t}kEroR-k#qZMfA27E|E#x7qV3F+lanpZ%rH!L{?GjVSA=6{Xuyk}<;$11zl*fJ zd?Yh+Yqz&Rg3{-w)7?KrDt^Bc+#PP3~I9yS40_C&vs0)Lvgp4VZ0~`=f4U(9$2<#UJ$L zAMcxcW&eMMSC`eNiW~Jx8e3id?{d0)|I?S(AMoCZeJG=`CdbvCT|02?1<&T$+b($9 z9=e%)qwuZCJgqgSgsdzg0{^^N+`nn#T5E-bsdm-qM3S1Z#5B90ngE9CrD`yyn~=ATE!=YM`xcJ+7C%=eDl3OlS1RHi<>v6QiU%7bgV z`S)tR-1&Sy_2Tgl5{(BtPK5rJFlp&cdc`fPBA*m}e{I3-bw`5E&;BuY`z5=G$!Fd= zZ<g#&3FV9)Oe>68^8Fzfny$0RhxhW6U?S5xe zH~*<|OXv>S^P82`U+aj5rQY+7^fHsarET??H7xVMj?UZZyI8Fc|9}7gU%zcZ!lkXQ z-Q356f23-A&40pwXNlv|az~>VR`$0;)|VdbKC|bX|Id@*@oyi;-u1gL9wN0WboIG) z*9*^CJQG()pTo^>{wCr?LnVj)@mwxT=J0oyABwT8o_py0zXM*=h1LD4xXPn{-mYLT z(*bSbPJeT0Y5kh6#*Lk_nPnfZaI%XBzrMD1@p1Wjo69$O{SKVce6#Hg=L4PTn^I3p z3QN8(O6T9t7^ZPZA_Y$;X^_!Fa}ozt8ReduC=b->_Iy96#45T~0r4Pe=Ioui)8IgBxw1 z7w?idW*w&Sif`Y_16()7<7K>TtG^joW%s+qYD#2zJwL}%`-`;OtCyG4BaVSL ze;>`iu;5^h%B`gb`!de=l-eHptq{Fg*KIdXe z?YW}7o9~7!^*w!Er!e6pKs{jW1!phfSwnLBPfFPGULXcm%rp!CI&=kx35iN`+l zxvD#R^SgOI{_^QtY$Msz+IS>SeNWgtd&k-DbLQ~hY`t7|bZwsL#;oqUFLXpN*?Ig< za8;WAQ{jv0hFAHkIGptIJ}#dBI3#U?^^^XC*-zLnHT-d`J)E#TSU~>B$p?`)o3=k$2%ZlBajS{UEt=PXd+$^4AhpuVB91vH#sv%cXc>lx#OeTay}7+zUVAnpGuwx(3;cFB}^(v_3+nW9F-U{jR&h9d zW!C{$De$GbqLa?K1Q~t!`~ALTTMlQ`wj9a&S(A%E)6KEVGBcSgC#`}_H#@a&Y!hvM zdu!|Cnip4BmroP*zhT)s^+NTx8o^!5p!-b={>Jb6!W*;hy!6xKF@`I4EoXkkb1voJ zPI=Y!S|`)bGlGwd4L>#CUVis&qmw5$U1R)O`|yx!bt#_^bW-0Fb4zE}lyqxd# zi7k!HFVDQZyeEQ#yZC|Ms?yij)@VOWlS(~YGM#rrbM=`Sh9R4tT#Y=p%JE;~2j2N{ zPnj-El(Hz85NLM%!kwL;E#ANFSia|I`L&~M@7!Y^1aZ%8_#irQg1TDf#_FBmt=l)g zN0qv|Zb?73OYULqeyO=RYmObWeWDx_oOryl!e;HMOaK1HOFzt8w0{4+^`hdtzeK*& znVj)E#!9wc<@$c{cs{FnG zKljnDo?qP-i+zKaGFmTrz$Mn)Agu1Uq38I=$Hy1Xt8YJ;a);aA|3dD*=TB-lj{D9m z@5W5qq2WS1&#*6C@4M)$c_@pb`1T(c&lf5i+lc>c_~Woop?+K5-BXj*Eu3ZE?|!vv z(h}xAUYFU8cN;mb?<{_<^Ye8`+Q(I!=Uj_k|2IT?(bP)W-3Pqq8_g?C5y)+qH!A)F zYNdu*XYYL_{&Mn;&8=%YmX!-1wF^$1&bGd6Q*i8q@?S}F*(N6))qDCW{a@tO&kHU$ zR5G8DPmp8$%*pVXdq(;JP_ONeotn2tT+B}Y<0=Ms0!v9Cbv|$Ejt7p+O;?=@*2=JSPGb99J7Y`R!p=5<{iTmH#rgJcoV^Y-6p?Y! zsc$#elJt9fe%d_ji=F-QWpeG!jWanE_Ad2`J)G%2i(mfVwDrzy8q5>EURinhMZ0`o z;k;x)rkKlXpr>lr&oJmT>A9c)>iw6P^+1o&IQ~CDi1YsQb91F%^{jqp$(VQLmn3hf zg~em$)q4(|Jh|!H?1<;qpxMX7H-~b0?`S*uE9e_K80FuyvHQ%Lz9+%_?VFpMH`xgP zZ+|QswI$=AyYYbuEEfKpzj)@yH5zhVj}}(zSq>V4c)>Bv{FH;c-G8ptUZF3$r|)5% z@KAMnOxBc#ANL4Vzq?~@G`H2F_!&>t%cY4XMNd5JKIa~6VvTwB_Ei_x>hxoq`nx7ZjDdsP`(_-S*+dFQw z76-=(h&`WwkT?G9tn$mV^YzY!sNJy%R^9v|{n4LOi{1ZUNH078Ww#y6#4_uEvilW} zdEf0=AG7n)3mg3_kY?!ex8?^KINv;G<9($)zjO1+bvvI){W~n^{BXskzGNvjo)`XQ zCi(YtUVS|`ce7r=*GZs}yG`87-*!dc%}Tm1W)1CX9qe+^`|S84dveBKUs1;sM|JDI z6yG^QkzpOoq+A!(h9HoxIm zHD7A&nb$X1H@*r!7In>Wi>#XCi8KDPiwMZdOb5&pYpQcult&ZoL{u`^HW`i^g7-=fp~$I zA57`_J~Iq18r*tqwM*SXecqFq89)0v-!L*?>Nz-3ZCZ;K0b^Ct3@>zGB)zO1*Nv;UXLB6T;7#2BC63&!wNpU%~6=eH~6}L8K zU0oI2%*LC6G||i1q+sC+-+AmiYs%Hw{o>cvoJxu|t=X*n);Rqf=%&ldCjymE@`H|` zMxH*cDcn)o^t;4SU=nwxG^^tV=cVcmFD5*3xy7z1Ik8BWqe(%sL#HhcA|?YrhD7efL-8>B-5*OLy#kzb?~9BIN?w#@L>dt6W7` zIw!R`wQ>c1e|Pug-tXH!{oa;;f0?NN-GZIP&!3(AW11ay`Qzi`&e!$#{YcvN>6G@$ zjERe8{`>#u=jSg!9`{S0f0g^^*W94sV9yQ5z2<#%Tk}}6=;Xg0&_k7-Hde(4sCe11 zf?bL8h}tY_SM^2X zzi!l)j$dCUdKSuteCkn8lJ%e1UHqr-bou>S_x*prNgsVx>o`&4qX?2aK>^I7_-)56 z&A1%_ilAfH{g*Le-sxJsv*>9BBlizWwmQcF-wKqB;>BprsX2I|>-*T9@mU z)ZWed%{ES2{?cz}?(MQIB`=NmJ|AG_ z|F9Qyi^!_b)xDdh=zrn+vo2!eqTCuOvmBSt&(1FI=_&nlb4B3e75}e;cE+ua-;m&# zYU8*%Y;8}`rjGePk4YD=^ppL4(tYA-Rsp{M!kjU+kL4~2%=~s-uDYi`@xcA$ep|m? zMw_|X7Yjc-Q#rTa_P|6g-dE-dvUi?LeD{0KiK9ne)vmJ1y1UC%H)_j_YwPa|{F-l9 z`%7bfrn>+B%K~S&TGc(aDwtglEyWOdHRb?h8p5!$1!=F`Wbw5XpWZC)w_EbLbkyD~(b9K!I=AKCceAbfvPHL9NxWR)Pp|pCBboPim1=LzxoNb8 z*?q0h3^o;kxM}^;AndqK4a&mI+{FSHL6)q*X27E$Kf-RtX!wPv$#TT7a?PGtwxOKKwDc9D_%hS}S zet&cK^lrV4vxTjnG^yOX%wYcXM(eGu+2P4ag}m=~*;am9qW=)I!+V}i7t6bQf_6pA zEgQOjh-!yl>=K3E4e7Dq|H;=f{w$Awf4^TZziasI$!^t3cO{uVudTJNeMt*X#E$`|@Sums8q{jqV-Tk8MUdC6N()7$$cXxFz}*}0FEiWIXhEZAqba8~Jb!!IoF9L+9X z`uB8t{G|^M55HJ+XXnbKqh6rJmm8-ko`QS&gW4!YnJfcUT4Ad1C+nC%-9TkW+H< zj=L4lpFf{pe{QlQuY8Wj`#qoeSY4HtBtAJYJ#(7j$7$B(a#lr83e?1}#2rdB+6ccp zaIzuuukVZh{QSK6_sjHoKc!1wT(CR+q<;bXlT1I6$2sYLjvdijm-xNd%Zuy&?w8AE zZ#fOBV!pn)Iaxe8C1uUi`wtIPGn`UYyIFFe^78cfI!oVqHmzRMbWTogo^j{%uIw;Z zqmxHh+^MMtPy)WyepI_XbdATjtVY*hR*YBDtpI6|W5?ekX?UdlUd;If*Ns_xW z9;V&ro3pmJc-!`zyGcF_{@@1148vs2YaKCnm~Fp&x!i0VdwYAn{LkK*?i=goixy1X zmweps)%|*guP-N`>YjS6NAm5CDt;JNlqzckLU0PZ{=O?fD`BpB`O})$gb_lE28L5d}zxcS{{@lCD zyA5;uZggr2%(X6`clUI&V>8?3$^Le(aR-{(^7sFp2C8x0`(y&&b(lKZ#g*SJ{Q_Dl za_JJ=2bZY1zh7NQogORpYyQ!0@r%{(bC<^Lt(wR;RXhAyQ6FcDPK!m&j{=RkRvj^O z8q-^Le>fMvpdq(5yw$q$lZgE1hS%3;0*46$J}Tn>Xj)HY?-5y*=N4UWSxG0>kc!e;o&D2ECfAl*p zoxkVej0@h6J1cirylP>dtmgaV?nYVbGNsr8K0WXtLYJ8CD%+D&-Xw%;or~Q6cAI_h zQm?5mZW#VP8D+ii$0P1tpkvB{*>!3ftlwW-8(kc^CT_3Q^y~3;KUITmKxMD0cbsp( z-K|64X0r3iJczvr8fDmYWp#MIyPRmzu9yIGalM!+Pp$UoJdhJx)6$&&fTQiw()@(K zMfX-5m#_Vzcr#Hn@AtRdJz1_w=R6S;~TW`kye}7B<{3uK|N@TveBCh%Tzq>mM zm02AZEm8baJ6X+l*{0OfAM@2ehO{2ee={NX!U9H7?XWXub~pA~pxy?R_4dkH&NFi? zo6Dzwu5bX2rGZY?io3b1l)GB#z;ZtOzh5R#wEw)qb8>s;w>Q^sZ2kM@=4K<`f3yCx zfB&f)wdDb4vS@rgV1G^5Li@GC-R*p`MJAehMTKnh8q<@T{++XaZ=_cAJgsy2SEGpu4-aIoKdY#3Q5k