diff --git a/Cargo.toml b/Cargo.toml index 9ad24599a..d37ec4bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "xmtp_user_preferences", "xmtp_v2", "xmtp_mls", - "xmtp_id", + "xmtp_id" ] exclude = [ @@ -18,6 +18,7 @@ exclude = [ "bindings_wasm", "xmtp_api_grpc_gateway", "bindings_node", + "diesel-wasm-sqlite" ] # Make the feature resolver explicit. diff --git a/diesel-wasm-sqlite/.gitignore b/diesel-wasm-sqlite/.gitignore new file mode 100644 index 000000000..f93dc9153 --- /dev/null +++ b/diesel-wasm-sqlite/.gitignore @@ -0,0 +1,8 @@ +# yarn +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions diff --git a/diesel-wasm-sqlite/.vscode/settings.json b/diesel-wasm-sqlite/.vscode/settings.json new file mode 100644 index 000000000..cb6099b62 --- /dev/null +++ b/diesel-wasm-sqlite/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "rust-analyzer": { + "cargo": { + "sysroot": "discover", + "allTargets": false, + "target": "wasm32-unknown-unknown" + }, + "procMacro": { + "enable": true, + "attributes.enable": true, + "ignored": { + "async-trait": ["async_trait"], + "napi-derive": ["napi"], + "async-recursion": ["async_recursion"], + "ctor": ["ctor"], + "tokio": ["test"] + } + } + }, + "[toml]": { + "editor.defaultFormatter": "tamasfe.even-better-toml" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/diesel-wasm-sqlite/.yarnrc.yml b/diesel-wasm-sqlite/.yarnrc.yml new file mode 100644 index 000000000..77ffe90a8 --- /dev/null +++ b/diesel-wasm-sqlite/.yarnrc.yml @@ -0,0 +1,7 @@ +compressionLevel: mixed + +enableGlobalCache: false + +enableTelemetry: false + +nodeLinker: node-modules diff --git a/diesel-wasm-sqlite/Cargo.lock b/diesel-wasm-sqlite/Cargo.lock new file mode 100644 index 000000000..aadc60362 --- /dev/null +++ b/diesel-wasm-sqlite/Cargo.lock @@ -0,0 +1,698 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "diesel" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" +dependencies = [ + "diesel_derives", +] + +[[package]] +name = "diesel-async" +version = "0.5.0" +source = "git+https://github.com/insipx/diesel_async?branch=insipx/make-stmt-cache-public#86a24a38d9d841ef9e92022cd983bbd700286397" +dependencies = [ + "async-trait", + "diesel", + "futures-util", + "scoped-futures", +] + +[[package]] +name = "diesel-wasm-sqlite" +version = "0.1.1" +dependencies = [ + "async-trait", + "bitflags", + "console_error_panic_hook", + "diesel", + "diesel-async", + "futures", + "futures-util", + "getrandom", + "log", + "rand", + "serde", + "serde-wasm-bindgen", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", +] + +[[package]] +name = "diesel_derives" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn", +] + +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "scoped-futures" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" +dependencies = [ + "cfg-if", + "pin-utils", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "bytes", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/diesel-wasm-sqlite/Cargo.toml b/diesel-wasm-sqlite/Cargo.toml new file mode 100644 index 000000000..737e6d77d --- /dev/null +++ b/diesel-wasm-sqlite/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "diesel-wasm-sqlite" +version = "0.1.1" +edition = "2021" + +[dependencies] +# diesel = { git = "https://github.com/xmtp/diesel", branch = "insipx/wasm-backend", default-features = false, features = ["i-implement-a-third-party-backend-and-opt-into-breaking-changes"] } +diesel = "2.2" +diesel-async = { git = "https://github.com/insipx/diesel_async", branch = "insipx/make-stmt-cache-public", features = ["stmt-cache"] } +wasm-bindgen = "0.2.92" +wasm-bindgen-futures = "0.4.42" +log = "0.4" +rand = "0.8" +getrandom = { version = "0.2", features = ["js"] } +web-sys = { version = "0.3", features = ["console"] } +# wee_alloc = { version = "0.4.2", optional = true } +console_error_panic_hook = { version = "0.1", optional = true } +tokio = { version = "1.38", default-features = false, features = ["rt", "macros", "sync", "io-util", "time"] } +futures = "0.3" +futures-util = "0.3" +async-trait = "0.1" +bitflags = "2.6" +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.6" + +[dev-dependencies] +rand = "0.8" +getrandom = { version = "0.2", features = ["js"] } +wasm-bindgen-test = "0.3.42" +web-sys = { version = "0.3", features = ["console"] } + + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[build] +target = "wasm32-unknown-unknown" + diff --git a/diesel-wasm-sqlite/README.md b/diesel-wasm-sqlite/README.md new file mode 100644 index 000000000..77b146779 --- /dev/null +++ b/diesel-wasm-sqlite/README.md @@ -0,0 +1,25 @@ +# Custom Diesel Backend for Wasm wa-sqlite + +#### Bundle the javascript in `package.js` to rust + +`yarn run build` + +#### Build the JS WASM interface + +`wasm-pack build` + +#### Run the Wasm Tests + +wasm-pack test --chrome --headless + +# TODO + +- [ ] wa-sqlite should be included in `pkg` build w/o manual copy (wasm-pack + issue?) +- [ ] OPFS + +# Notes + +- rust-analyzer doesn't like crates with different targets in the same + workspace. If you want this to work well with your LSP, open + `diesel-wasm-sqlite` as it's own project. diff --git a/diesel-wasm-sqlite/build.rs b/diesel-wasm-sqlite/build.rs new file mode 100644 index 000000000..01bfc1044 --- /dev/null +++ b/diesel-wasm-sqlite/build.rs @@ -0,0 +1,17 @@ +use std::env; +use std::process::Command; + +fn main() { + println!("cargo::rerun-if-changed=package.js"); + + Command::new("yarn") + .args(["run", "build"]) + .status() + .unwrap(); + + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + if target_arch != "wasm32" { + // Emit a compile error if the target is not wasm32-unknown-unknown + panic!("This crate only supports the wasm32 architecture"); + } +} diff --git a/diesel-wasm-sqlite/package-lock.json b/diesel-wasm-sqlite/package-lock.json new file mode 100644 index 000000000..e92a88e09 --- /dev/null +++ b/diesel-wasm-sqlite/package-lock.json @@ -0,0 +1,756 @@ +{ + "name": "diesel-wasm-sqlite", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "diesel-wasm-sqlite", + "version": "1.0.0", + "dependencies": { + "@xmtp/wa-sqlite": "^1.0.1" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "rollup": "^4.19.0", + "rollup-plugin-base64": "^1.0.1", + "rollup-plugin-copy": "^3.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.19.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.14.11", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@xmtp/wa-sqlite": { + "version": "1.0.1" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/colorette": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.19.0", + "@rollup/rollup-android-arm64": "4.19.0", + "@rollup/rollup-darwin-arm64": "4.19.0", + "@rollup/rollup-darwin-x64": "4.19.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.19.0", + "@rollup/rollup-linux-arm-musleabihf": "4.19.0", + "@rollup/rollup-linux-arm64-gnu": "4.19.0", + "@rollup/rollup-linux-arm64-musl": "4.19.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0", + "@rollup/rollup-linux-riscv64-gnu": "4.19.0", + "@rollup/rollup-linux-s390x-gnu": "4.19.0", + "@rollup/rollup-linux-x64-gnu": "4.19.0", + "@rollup/rollup-linux-x64-musl": "4.19.0", + "@rollup/rollup-win32-arm64-msvc": "4.19.0", + "@rollup/rollup-win32-ia32-msvc": "4.19.0", + "@rollup/rollup-win32-x64-msvc": "4.19.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-base64/-/rollup-plugin-base64-1.0.1.tgz", + "integrity": "sha512-IbdX8fjuXO/Op3hYmRPjVo0VwcSenwsQDaDTFdoe+70B5ZGoLMtr96L2yhHXCfxv7HwZVvxZqLsuWj6VwzRt3g==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0" + } + }, + "node_modules/rollup-plugin-base64/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/rollup-plugin-base64/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/rollup-plugin-base64/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/rollup-plugin-base64/node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "peer": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-copy": { + "version": "3.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + } + } +} diff --git a/diesel-wasm-sqlite/package.js b/diesel-wasm-sqlite/package.js new file mode 100644 index 000000000..9b4459237 --- /dev/null +++ b/diesel-wasm-sqlite/package.js @@ -0,0 +1,355 @@ +import * as WasmSQLiteLibrary from "@xmtp/wa-sqlite"; +import { OPFSCoopSyncVFS } from "@xmtp/wa-sqlite/vfs/OPFSCoopSync"; +import SQLiteESMFactory from "./node_modules/@xmtp/wa-sqlite/dist/wa-sqlite.mjs"; +import base64Wasm from "./node_modules/@xmtp/wa-sqlite/dist/wa-sqlite.wasm"; + +function base64Decode(str) { + const binaryString = typeof atob === "function" + ? atob(str) + : Buffer.from(str, "base64").toString("binary"); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} + +export class SQLite { + #module; + #sqlite3; + constructor(module) { + if (typeof module === "undefined") { + throw new Error("Cannot be called directly"); + } + this.module = module; + this.sqlite3 = WasmSQLiteLibrary.Factory(module); + } + + static async wasm_module() { + return await SQLiteESMFactory({ + "wasmBinary": base64Decode(base64Wasm), + }); + } + + static async build() { + const module = await SQLiteESMFactory({ + "wasmBinary": base64Decode(base64Wasm), + }); + return new WasmSQLiteLibrary(module); + } + + result_text(context, value) { + this.sqlite3.result_text(context, value); + } + + result_int(context, value) { + this.sqlite3.result_int(context, value); + } + + result_int64(context, value) { + this.sqlite3.result_int64(context, value); + } + + result_double(context, value) { + this.sqlite3.result_double(context, value); + } + + result_blob(context, value) { + this.sqlite3.result_blob(context, value); + } + + result_null(context) { + this.sqlite3.result_null(context); + } + + bind(stmt, i, value) { + try { + return this.sqlite3.bind(stmt, i, value); + } catch (error) { + console.log("bind err"); + throw error; + } + } + + bind_blob(stmt, i, value) { + try { + return this.sqlite3.bind_blob(stmt, i, value); + } catch (error) { + console.log("bind blob error"); + throw error; + } + } + + bind_collection(stmt, bindings) { + try { + return this.sqlite3.bind_collection(stmt, bindings); + } catch (error) { + console.log("bind collection error"); + throw error; + } + } + + bind_double(stmt, i, value) { + try { + return this.sqlite3.bind_double(stmt, i, value); + } catch (error) { + console.log("bind double error"); + throw error; + } + } + + bind_int(stmt, i, value) { + try { + return this.sqlite3.bind_int(stmt, i, value); + } catch (error) { + console.log("bind int error"); + throw error; + } + } + + bind_int64(stmt, i, value) { + try { + return this.sqlite3.bind_int64(stmt, i, value); + } catch (error) { + console.log("bind int644 error"); + throw error; + } + } + + bind_null(stmt, i) { + try { + return this.sqlite3.bind_null(stmt, i); + } catch (error) { + console.log("bind null error"); + throw error; + } + } + + bind_parameter_count(stmt) { + return this.sqlite3.bind_parameter_count(stmt); + } + + bind_parameter_name(stmt, i) { + return this.sqlite3.bind_paramater_name(stmt, it); + } + + bind_text(stmt, i, value) { + try { + this.sqlite3.bind_text(stmt, i, value); + } catch (error) { + console.log("bind text error"); + throw error; + } + } + + async reset(stmt) { + try { + return await this.sqlite3.reset(stmt); + } catch (error) { + console.log("reset err"); + throw error; + } + } + + value(pValue) { + this.sqlite3.value(pValue); + } + + value_dup(pValue) { + return this.module._sqlite3_value_dup(pValue); + } + + value_blob(pValue) { + this.sqlite3.value_blob(pValue); + } + + value_bytes(pValue) { + this.sqlite3.value_bytes(pValue); + } + + value_double(pValue) { + this.sqlite3.value_double(pValue); + } + + value_int(pValue) { + this.sqlite3.value_int(pValue); + } + + value_int64(pValue) { + this.sqlite3.value_int64(pValue); + } + + value_text(pValue) { + this.sqlite3.value_text(pValue); + } + + value_type(pValue) { + this.sqlite3.value_type(pValue); + } + + async open_v2(database_url, iflags) { + try { + console.log("Opening database!", database_url); + const vfs = await OPFSCoopSyncVFS.create(database_url, this.module); + this.sqlite3.vfs_register(vfs, true); + let db = await this.sqlite3.open_v2(database_url, iflags); + return db; + } catch (error) { + console.log("openv2 error", error); + throw error; + } + } + + async exec(db, query) { + try { + return await this.sqlite3.exec(db, query, (row, columns) => { + console.log(row); + }); + } catch (error) { + console.log("exec err"); + throw error; + } + } + + finalize(stmt) { + try { + return this.sqlite3.finalize(stmt); + } catch (error) { + console.log("stmt error"); + throw error; + } + } + + changes(db) { + return this.sqlite3.changes(db); + } + + clear_bindings(stmt) { + return this.sqlite3.clear_bindings(stmt); + } + + async close(db) { + try { + return this.sqlite3.close(db); + } catch (error) { + console.log("sqlite3.close error"); + throw error; + } + } + + column(stmt, i) { + return this.sqlite3.column(stmt, i); + } + + async prepare(database, sql, options) { + try { + return await this.sqlite3.statements(database, sql, options); + } catch (error) { + console.log("sqlite prepare error"); + throw error; + } + } + + async step(stmt) { + try { + return await this.sqlite3.step(stmt); + } catch (error) { + console.log("sqlite step error"); + throw error; + } + } + + column_name(stmt, idx) { + return this.sqlite3.column_name(stmt, idx); + } + + column_count(stmt) { + return this.sqlite3.column_count(stmt); + } + + batch_execute(database, query) { + try { + return this.sqlite3.exec(database, query); + console.log("Batch exec'ed"); + } catch (error) { + console.log("exec err"); + throw error; + } + } + + create_function( + database, + functionName, + nArg, + textRep, + pApp, + xFunc, + xStep, + xFinal, + ) { + try { + this.sqlite3.create_function( + database, + functionName, + nArg, + textRep, + pApp, // pApp is ignored + xFunc, + xStep, + xFinal, + ); + console.log("create function"); + } catch (error) { + console.log("create function err"); + throw error; + } + } + //TODO: At some point need a way to register functions from rust + //but for just libxmtp this is fine. + register_diesel_sql_functions(database) { + try { + this.sqlite3.create_function( + database, + "diesel_manage_updated_at", + 1, + WasmSQLiteLibrary.SQLITE_UTF8, + 0, + (context, values) => { + const table_name = this.sqlite3.value_text(values[0]); + + await this.sqlite3.exec( + context, + `CREATE TRIGGER __diesel_manage_updated_at_${table_name} + AFTER UPDATE ON ${table_name} + FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at + BEGIN + UPDATE ${table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; + END`, + (row, columns) => { + console.log(`------------------------------------`); + console.log(`Created trigger for ${table_name}`); + console.log(row); + console.log(columns); + console.log(`------------------------------------`); + }, + ); + }, + ); + } catch (error) { + console.log("error creating diesel trigger"); + throw error; + } + } + + /* + serialize(database, zSchema, size, flags) { + return this.module._sqlite3_serialize(database, zSchema, size, flags); + } + */ +} diff --git a/diesel-wasm-sqlite/package.json b/diesel-wasm-sqlite/package.json new file mode 100644 index 000000000..6f529c83e --- /dev/null +++ b/diesel-wasm-sqlite/package.json @@ -0,0 +1,23 @@ +{ + "name": "diesel-wasm-sqlite", + "type": "module", + "version": "1.0.0", + "description": "", + "main": "package.js", + "scripts": { + "build": "rollup -c" + }, + "dependencies": { + "@xmtp/wa-sqlite": "^1.0.1" + }, + "packageManager": "yarn@4.3.1", + "engines": { + "node": ">=20" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "rollup": "^4.19.0", + "rollup-plugin-base64": "^1.0.1", + "rollup-plugin-copy": "^3.5.0" + } +} diff --git a/diesel-wasm-sqlite/rollup.config.js b/diesel-wasm-sqlite/rollup.config.js new file mode 100644 index 000000000..881eeadf5 --- /dev/null +++ b/diesel-wasm-sqlite/rollup.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from "rollup"; +import resolve from "@rollup/plugin-node-resolve"; +import { base64 } from "rollup-plugin-base64"; + +export default defineConfig([ + { + input: "package.js", + output: { + file: "src/wa-sqlite-diesel-bundle.js", + format: "es", + }, + plugins: [ + resolve(), + base64({ include: "**/*.wasm" }), + ], + // external: ["@xmtp/wa-sqlite", "@xmtp/wa-sqlite/build"], + }, +]); diff --git a/diesel-wasm-sqlite/src/backend.rs b/diesel-wasm-sqlite/src/backend.rs new file mode 100644 index 000000000..4891c37e0 --- /dev/null +++ b/diesel-wasm-sqlite/src/backend.rs @@ -0,0 +1,88 @@ +//! The SQLite backend + +use super::connection::SqliteBindCollector; +use super::connection::SqliteValue; +use super::query_builder::SqliteQueryBuilder; +use diesel::backend::*; +use diesel::sql_types::TypeMetadata; + +/// The SQLite backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] +pub struct WasmSqlite; + +/// Determines how a bind parameter is given to SQLite +/// +/// Diesel deals with bind parameters after serialization as opaque blobs of +/// bytes. However, SQLite instead has several functions where it expects the +/// relevant C types. +/// +/// The variants of this struct determine what bytes are expected from +/// `ToSql` impls. +#[allow(missing_debug_implementations)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum SqliteType { + /// Bind using `sqlite3_bind_blob` + Binary, + /// Bind using `sqlite3_bind_text` + Text, + /// `bytes` should contain an `f32` + Float, + /// `bytes` should contain an `f64` + Double, + /// `bytes` should contain an `i16` + SmallInt, + /// `bytes` should contain an `i32` + Integer, + /// `bytes` should contain an `i64` + Long, +} + +impl Backend for WasmSqlite { + type QueryBuilder = SqliteQueryBuilder; + type RawValue<'a> = SqliteValue<'a, 'a, 'a>; + type BindCollector<'a> = SqliteBindCollector<'a>; +} + +impl TypeMetadata for WasmSqlite { + type TypeMetadata = SqliteType; + type MetadataLookup = (); +} + +impl SqlDialect for WasmSqlite { + #[cfg(not(feature = "returning_clauses_for_sqlite_3_35"))] + type ReturningClause = sql_dialect::returning_clause::DoesNotSupportReturningClause; + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + type ReturningClause = SqliteReturningClause; + + type OnConflictClause = SqliteOnConflictClause; + + type InsertWithDefaultKeyword = + sql_dialect::default_keyword_for_insert::DoesNotSupportDefaultKeyword; + type BatchInsertSupport = SqliteBatchInsert; + type ConcatClause = sql_dialect::concat_clause::ConcatWithPipesClause; + type DefaultValueClauseForInsert = sql_dialect::default_value_clause::AnsiDefaultValueClause; + + type EmptyFromClauseSyntax = sql_dialect::from_clause_syntax::AnsiSqlFromClauseSyntax; + type SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement; + + type ExistsSyntax = sql_dialect::exists_syntax::AnsiSqlExistsSyntax; + type ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison; + type AliasSyntax = sql_dialect::alias_syntax::AsAliasSyntax; +} + +impl DieselReserveSpecialization for WasmSqlite {} +impl TrustedBackend for WasmSqlite {} + +#[derive(Debug, Copy, Clone)] +pub struct SqliteOnConflictClause; + +impl sql_dialect::on_conflict_clause::SupportsOnConflictClause for SqliteOnConflictClause {} +impl sql_dialect::on_conflict_clause::PgLikeOnConflictClause for SqliteOnConflictClause {} + +#[derive(Debug, Copy, Clone)] +pub struct SqliteBatchInsert; + +#[derive(Debug, Copy, Clone)] +pub struct SqliteReturningClause; + +impl sql_dialect::returning_clause::SupportsReturningClause for SqliteReturningClause {} diff --git a/diesel-wasm-sqlite/src/connection/bind_collector.rs b/diesel-wasm-sqlite/src/connection/bind_collector.rs new file mode 100644 index 000000000..6eef85140 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/bind_collector.rs @@ -0,0 +1,258 @@ +use crate::{SqliteType, WasmSqlite}; +use diesel::{ + query_builder::{BindCollector, MoveableBindCollector}, + result::QueryResult, + serialize::{IsNull, Output}, + sql_types::HasSqlType, +}; +use serde::{Deserialize, Serialize}; +use wasm_bindgen::JsValue; + +// TODO:insipx this file remains largely unchanged other than the ffi::. Candidate for shared code? + +pub type BindValue = JsValue; + +#[derive(Debug, Default)] +pub struct SqliteBindCollector<'a> { + pub(crate) binds: Vec<(InternalSqliteBindValue<'a>, SqliteType)>, +} + +impl SqliteBindCollector<'_> { + pub(crate) fn new() -> Self { + Self { binds: Vec::new() } + } +} + +/// This type represents a value bound to +/// a sqlite prepared statement +/// +/// It can be constructed via the various `From` implementations +#[derive(Debug)] +pub struct SqliteBindValue<'a> { + pub(crate) inner: InternalSqliteBindValue<'a>, +} + +impl<'a> From for SqliteBindValue<'a> { + fn from(i: i32) -> Self { + Self { + inner: InternalSqliteBindValue::I32(i), + } + } +} + +impl<'a> From for SqliteBindValue<'a> { + fn from(i: i64) -> Self { + Self { + inner: InternalSqliteBindValue::I64(i), + } + } +} + +impl<'a> From for SqliteBindValue<'a> { + fn from(f: f64) -> Self { + Self { + inner: InternalSqliteBindValue::F64(f), + } + } +} + +impl<'a, T> From> for SqliteBindValue<'a> +where + T: Into>, +{ + fn from(o: Option) -> Self { + match o { + Some(v) => v.into(), + None => Self { + inner: InternalSqliteBindValue::Null, + }, + } + } +} + +impl<'a> From<&'a str> for SqliteBindValue<'a> { + fn from(s: &'a str) -> Self { + Self { + inner: InternalSqliteBindValue::BorrowedString(s), + } + } +} + +impl<'a> From for SqliteBindValue<'a> { + fn from(s: String) -> Self { + Self { + inner: InternalSqliteBindValue::String(s.into_boxed_str()), + } + } +} + +impl<'a> From> for SqliteBindValue<'a> { + fn from(b: Vec) -> Self { + Self { + inner: InternalSqliteBindValue::Binary(b.into_boxed_slice()), + } + } +} + +impl<'a> From<&'a [u8]> for SqliteBindValue<'a> { + fn from(b: &'a [u8]) -> Self { + Self { + inner: InternalSqliteBindValue::BorrowedBinary(b), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub(crate) enum InternalSqliteBindValue<'a> { + BorrowedString(&'a str), + String(Box), + BorrowedBinary(&'a [u8]), + Binary(Box<[u8]>), + I32(i32), + I64(i64), + F64(f64), + Null, +} + +impl std::fmt::Display for InternalSqliteBindValue<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let n = match self { + InternalSqliteBindValue::BorrowedString(_) | InternalSqliteBindValue::String(_) => { + "Text" + } + InternalSqliteBindValue::BorrowedBinary(_) | InternalSqliteBindValue::Binary(_) => { + "Binary" + } + InternalSqliteBindValue::I32(_) | InternalSqliteBindValue::I64(_) => "Integer", + InternalSqliteBindValue::F64(_) => "Float", + InternalSqliteBindValue::Null => "Null", + }; + f.write_str(n) + } +} + +impl InternalSqliteBindValue<'_> { + #[allow(unsafe_code)] // ffi function calls + pub(crate) fn result_of(self, ctx: &mut i32) { + let sqlite3 = crate::get_sqlite_unchecked(); + match self { + InternalSqliteBindValue::BorrowedString(s) => sqlite3.result_text(*ctx, s.to_string()), + InternalSqliteBindValue::String(s) => sqlite3.result_text(*ctx, s.to_string()), + InternalSqliteBindValue::Binary(b) => sqlite3.result_blob(*ctx, b.to_vec()), + InternalSqliteBindValue::BorrowedBinary(b) => sqlite3.result_blob(*ctx, b.to_vec()), + InternalSqliteBindValue::I32(i) => sqlite3.result_int(*ctx, i), + InternalSqliteBindValue::I64(l) => sqlite3.result_int64(*ctx, l), + InternalSqliteBindValue::F64(d) => sqlite3.result_double(*ctx, d), + InternalSqliteBindValue::Null => sqlite3.result_null(*ctx), + } + } +} + +impl<'a> BindCollector<'a, WasmSqlite> for SqliteBindCollector<'a> { + type Buffer = SqliteBindValue<'a>; + + fn push_bound_value(&mut self, bind: &'a U, metadata_lookup: &mut ()) -> QueryResult<()> + where + WasmSqlite: diesel::sql_types::HasSqlType, + U: diesel::serialize::ToSql + ?Sized, + { + let value = SqliteBindValue { + inner: InternalSqliteBindValue::Null, + }; + let mut to_sql_output = Output::new(value, metadata_lookup); + let is_null = bind + .to_sql(&mut to_sql_output) + .map_err(diesel::result::Error::SerializationError)?; + let bind = to_sql_output.into_inner(); + let metadata = WasmSqlite::metadata(metadata_lookup); + + self.binds.push(( + match is_null { + IsNull::No => bind.inner, + IsNull::Yes => InternalSqliteBindValue::Null, + }, + metadata, + )); + Ok(()) + } + + fn push_null_value(&mut self, metadata: SqliteType) -> QueryResult<()> { + self.binds.push((InternalSqliteBindValue::Null, metadata)); + Ok(()) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +enum OwnedSqliteBindValue { + String(Box), + Binary(Box<[u8]>), + I32(i32), + I64(i64), + F64(f64), + Null, +} + +impl<'a> std::convert::From<&InternalSqliteBindValue<'a>> for OwnedSqliteBindValue { + fn from(value: &InternalSqliteBindValue<'a>) -> Self { + match value { + InternalSqliteBindValue::String(s) => Self::String(s.clone()), + InternalSqliteBindValue::BorrowedString(s) => { + Self::String(String::from(*s).into_boxed_str()) + } + InternalSqliteBindValue::Binary(b) => Self::Binary(b.clone()), + InternalSqliteBindValue::BorrowedBinary(s) => { + Self::Binary(Vec::from(*s).into_boxed_slice()) + } + InternalSqliteBindValue::I32(val) => Self::I32(*val), + InternalSqliteBindValue::I64(val) => Self::I64(*val), + InternalSqliteBindValue::F64(val) => Self::F64(*val), + InternalSqliteBindValue::Null => Self::Null, + } + } +} + +impl<'a> std::convert::From<&OwnedSqliteBindValue> for InternalSqliteBindValue<'a> { + fn from(value: &OwnedSqliteBindValue) -> Self { + match value { + OwnedSqliteBindValue::String(s) => Self::String(s.clone()), + OwnedSqliteBindValue::Binary(b) => Self::Binary(b.clone()), + OwnedSqliteBindValue::I32(val) => Self::I32(*val), + OwnedSqliteBindValue::I64(val) => Self::I64(*val), + OwnedSqliteBindValue::F64(val) => Self::F64(*val), + OwnedSqliteBindValue::Null => Self::Null, + } + } +} + +#[derive(Debug)] +/// Sqlite bind collector data that is movable across threads +pub struct SqliteBindCollectorData { + binds: Vec<(OwnedSqliteBindValue, SqliteType)>, +} + +impl MoveableBindCollector for SqliteBindCollector<'_> { + type BindData = SqliteBindCollectorData; + + fn moveable(&self) -> Self::BindData { + let mut binds = Vec::with_capacity(self.binds.len()); + for b in self + .binds + .iter() + .map(|(bind, tpe)| (OwnedSqliteBindValue::from(bind), *tpe)) + { + binds.push(b); + } + SqliteBindCollectorData { binds } + } + + fn append_bind_data(&mut self, from: &Self::BindData) { + self.binds.reserve_exact(from.binds.len()); + self.binds.extend( + from.binds + .iter() + .map(|(bind, tpe)| (InternalSqliteBindValue::from(bind), *tpe)), + ); + } +} diff --git a/diesel-wasm-sqlite/src/connection/diesel_manage_updated_at.sql b/diesel-wasm-sqlite/src/connection/diesel_manage_updated_at.sql new file mode 100644 index 000000000..83c3d33d4 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/diesel_manage_updated_at.sql @@ -0,0 +1,11 @@ +CREATE TRIGGER __diesel_manage_updated_at_{table_name} +AFTER UPDATE ON {table_name} +FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at +BEGIN + UPDATE {table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; +END diff --git a/diesel-wasm-sqlite/src/connection/functions.rs b/diesel-wasm-sqlite/src/connection/functions.rs new file mode 100644 index 000000000..b8d8f7538 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/functions.rs @@ -0,0 +1,239 @@ +use super::raw::RawConnection; +use super::row::PrivateSqliteRow; +use super::{/*SqliteAggregateFunction,*/ SqliteBindValue, WasmSqlite}; +use crate::connection::bind_collector::InternalSqliteBindValue; +use crate::connection::sqlite_value::OwnedSqliteValue; +use crate::connection::SqliteValue; +use diesel::backend::Backend; +use diesel::deserialize::{FromSqlRow, StaticallySizedRow}; +use diesel::result::{DatabaseErrorKind, Error, QueryResult}; +use diesel::row::{Field, PartialRow, Row, RowIndex, RowSealed}; +use diesel::serialize::{IsNull, Output, ToSql}; +use diesel::sql_types::HasSqlType; +use futures::future::BoxFuture; +use futures::FutureExt; +use std::cell::{Ref, RefCell}; +use std::marker::PhantomData; +use std::mem::ManuallyDrop; +use std::ops::DerefMut; +use std::rc::Rc; +use wasm_bindgen::JsValue; + +pub(super) fn register( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut(&RawConnection, Args) -> BoxFuture<'static, Ret>, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + WasmSqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_sql_function(fn_name, fields_needed, deterministic, move |conn, args| { + async { + let args = build_sql_function_args::(args)?; + let conn = RawConnection { + internal_connection: conn, + }; + Ok(f(&conn, args).await) + } + .boxed() + })?; + Ok(()) +} + +/* +pub(super) fn register_noargs( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static, + Ret: ToSql, + WasmSqlite: HasSqlType, +{ + conn.register_sql_function(fn_name, 0, deterministic, move |_, _| Ok(f()))?; + Ok(()) +} + +pub(super) fn register_aggregate( + conn: &RawConnection, + fn_name: &str, +) -> QueryResult<()> +where + A: SqliteAggregateFunction + 'static + Send + std::panic::UnwindSafe, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + WasmSqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_aggregate_function::( + fn_name, + fields_needed, + )?; + + Ok(()) +} +*/ + +pub(super) fn build_sql_function_args(args: Vec) -> Result +where + Args: FromSqlRow, +{ + let row = FunctionRow::new(args); + Args::build_from_row(&row).map_err(Error::DeserializationError) +} + +// clippy is wrong here, the let binding is required +// for lifetime reasons +#[allow(clippy::let_unit_value)] +pub(super) fn process_sql_function_result( + result: &'_ Ret, +) -> QueryResult> +where + Ret: ToSql, + WasmSqlite: HasSqlType, +{ + let mut metadata_lookup = (); + let value = SqliteBindValue { + inner: InternalSqliteBindValue::Null, + }; + let mut buf = Output::new(value, &mut metadata_lookup); + let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?; + + if let IsNull::Yes = is_null { + Ok(InternalSqliteBindValue::Null) + } else { + Ok(buf.into_inner().inner) + } +} + +struct FunctionRow<'a> { + // we use `ManuallyDrop` to prevent dropping the content of the internal vector + // as this buffer is owned by sqlite not by diesel + args: Rc>>>, + field_count: usize, + marker: PhantomData<&'a JsValue>, +} + +impl<'a> Drop for FunctionRow<'a> { + #[allow(unsafe_code)] // manual drop calls + fn drop(&mut self) { + if let Some(args) = Rc::get_mut(&mut self.args) { + if let PrivateSqliteRow::Duplicated { column_names, .. } = + DerefMut::deref_mut(RefCell::get_mut(args)) + { + if Rc::strong_count(column_names) == 1 { + // According the https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html#method.drop + // it's fine to just drop the values here + unsafe { std::ptr::drop_in_place(column_names as *mut _) } + } + } + } + } +} + +impl<'a> FunctionRow<'a> { + #[allow(unsafe_code)] // complicated ptr cast + fn new(args: Vec) -> Self { + let lengths = args.len(); + + Self { + field_count: lengths, + args: Rc::new(RefCell::new(ManuallyDrop::new( + PrivateSqliteRow::Duplicated { + values: args + .into_iter() + .map(|a| Some(OwnedSqliteValue { value: a.into() })) + .collect(), + column_names: Rc::from(vec![None; lengths]), + }, + ))), + marker: PhantomData, + } + } +} + +impl RowSealed for FunctionRow<'_> {} + +impl<'a> Row<'a, WasmSqlite> for FunctionRow<'a> { + type Field<'f> = FunctionArgument<'f> where 'a: 'f, Self: 'f; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.field_count + } + + fn get<'b, I>(&'b self, idx: I) -> Option> + where + 'a: 'b, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(FunctionArgument { + args: self.args.borrow(), + col_idx: idx as i32, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for FunctionRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for FunctionRow<'b> { + fn idx(&self, _idx: &'a str) -> Option { + None + } +} + +struct FunctionArgument<'a> { + args: Ref<'a, ManuallyDrop>>, + col_idx: i32, +} + +impl<'a> Field<'a, WasmSqlite> for FunctionArgument<'a> { + fn field_name(&self) -> Option<&str> { + None + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'_>> { + SqliteValue::new( + Ref::map(Ref::clone(&self.args), |drop| std::ops::Deref::deref(drop)), + self.col_idx, + ) + } +} diff --git a/diesel-wasm-sqlite/src/connection/mod.rs b/diesel-wasm-sqlite/src/connection/mod.rs new file mode 100644 index 000000000..fac2bbe8b --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/mod.rs @@ -0,0 +1,292 @@ +mod bind_collector; +// mod functions; +mod owned_row; +mod raw; +mod row; +// mod serialized_database; +mod sqlite_value; +// mod statement_iterator; +mod stmt; + +pub(crate) use self::bind_collector::SqliteBindCollector; +pub use self::bind_collector::SqliteBindValue; +pub use self::sqlite_value::SqliteValue; + // pub use self::serialized_database::SerializedDatabase; + +use self::raw::RawConnection; +// use self::statement_iterator::*; +use self::stmt::{Statement, StatementUse}; +use crate::query_builder::*; +use diesel::{connection::{statement_cache::StatementCacheKey, DefaultLoadingMode, LoadConnection}, deserialize::{FromSqlRow, StaticallySizedRow}, expression::QueryMetadata, query_builder::QueryBuilder as _, result::*, serialize::ToSql, sql_types::HasSqlType}; +use futures::{FutureExt, TryFutureExt}; +use std::sync::{Arc, Mutex}; + + +use diesel::{connection::{ConnectionSealed, Instrumentation, WithMetadataLookup}, query_builder::{AsQuery, QueryFragment, QueryId}, sql_types::TypeMetadata, QueryResult}; +pub use diesel_async::{AnsiTransactionManager, AsyncConnection, SimpleAsyncConnection, TransactionManager, stmt_cache::StmtCache}; +use futures::{future::BoxFuture, stream::BoxStream}; +use row::SqliteRow; + +use crate::{get_sqlite_unchecked, WasmSqlite, WasmSqliteError}; + +pub struct WasmSqliteConnection { + // statement_cache needs to be before raw_connection + // otherwise we will get errors about open statements before closing the + // connection itself + statement_cache: StmtCache, + pub raw_connection: RawConnection, + transaction_state: AnsiTransactionManager, + // this exists for the sole purpose of implementing `WithMetadataLookup` trait + // and avoiding static mut which will be deprecated in 2024 edition + metadata_lookup: (), + instrumentation: Arc>>>, +} + + +// This relies on the invariant that RawConnection or Statement are never +// leaked. If a reference to one of those was held on a different thread, this +// would not be thread safe. +// Web is in one thread. Web workers can be used to hold a WasmSqliteConnection +// separately. + +#[allow(unsafe_code)] +unsafe impl Send for WasmSqliteConnection {} + + +impl ConnectionSealed for WasmSqliteConnection {} + + #[async_trait::async_trait(?Send)] +impl SimpleAsyncConnection for WasmSqliteConnection { + async fn batch_execute(&mut self, query: &str) -> diesel::prelude::QueryResult<()> { + get_sqlite_unchecked() + .batch_execute(&self.raw_connection.internal_connection, query) + .map_err(WasmSqliteError::from) + .map_err(Into::into) + } +} + +#[async_trait::async_trait(?Send)] +impl AsyncConnection for WasmSqliteConnection { + type Backend = WasmSqlite; + type TransactionManager = AnsiTransactionManager; + type ExecuteFuture<'conn, 'query> = BoxFuture<'query, QueryResult>; + type LoadFuture<'conn, 'query> = BoxFuture<'query, QueryResult>>; + type Stream<'conn, 'query> = BoxStream<'static, QueryResult>>; + type Row<'conn, 'query> = SqliteRow<'conn, 'query>; + + async fn establish(database_url: &str) -> diesel::prelude::ConnectionResult { + WasmSqliteConnection::establish_inner(database_url).await + } + + fn load<'conn, 'query, T>(&'conn mut self, _source: T) -> Self::LoadFuture<'conn, 'query> + where + T: AsQuery + 'query, + T::Query: QueryFragment + QueryId + 'query, + { + todo!() + } + + fn execute_returning_count<'conn, 'query, T>( + &'conn mut self, + _source: T, + ) -> Self::ExecuteFuture<'conn, 'query> + where + T: QueryFragment + QueryId + 'query, + { + todo!() + } + + fn transaction_state( + &mut self, + ) -> &mut >::TransactionStateData{ + todo!() + } + + fn instrumentation(&mut self) -> &mut dyn Instrumentation { + todo!() + } + + fn set_instrumentation(&mut self, _instrumentation: impl Instrumentation) { + todo!() + } +} + +/* +impl LoadConnection for WasmSqliteConnection { + type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>; + type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>; + + fn load<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: Query + QueryFragment + QueryId + 'query, + Self::Backend: QueryMetadata, + { + let statement = self.prepared_query(source)?; + + Ok(StatementIterator::new(statement)) + } +} +*/ +/* +impl WithMetadataLookup for WasmSqliteConnection { + fn metadata_lookup(&mut self) -> &mut ::MetadataLookup { + &mut self.metadata_lookup + } +} + */ + +#[cfg(feature = "r2d2")] +impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection { + fn ping(&mut self) -> QueryResult<()> { + use crate::RunQueryDsl; + + crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ()) + } + + fn is_broken(&mut self) -> bool { + AnsiTransactionManager::is_broken_transaction_manager(self) + } +} + /* +impl MultiConnectionHelper for SqliteConnection { + fn to_any<'a>( + lookup: &mut ::MetadataLookup, + ) -> &mut (dyn std::any::Any + 'a) { + lookup + } + + fn from_any( + lookup: &mut dyn std::any::Any, + ) -> Option<&mut ::MetadataLookup> { + lookup.downcast_mut() + } +} +*/ + +impl WasmSqliteConnection { + /// Run a transaction with `BEGIN IMMEDIATE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let mut conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.immediate_transaction(|conn| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub async fn immediate_transaction(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN IMMEDIATE").await + } + + /// Run a transaction with `BEGIN EXCLUSIVE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let mut conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.exclusive_transaction(|conn| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub async fn exclusive_transaction(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN EXCLUSIVE").await + } + + async fn transaction_sql(&mut self, f: F, sql: &str) -> Result + where + F: FnOnce(&mut Self) -> Result, + E: From, + { + AnsiTransactionManager::begin_transaction_sql(&mut *self, sql).await?; + match f(&mut *self) { + Ok(value) => { + AnsiTransactionManager::commit_transaction(&mut *self).await?; + Ok(value) + } + Err(e) => { + AnsiTransactionManager::rollback_transaction(&mut *self).await?; + Err(e) + } + } + } + + async fn prepared_query<'conn, 'query, T>( + &'conn mut self, + source: T, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + let raw_connection = &self.raw_connection; + let cache = &mut self.statement_cache; + let maybe_type_id = T::query_id(); + let cache_key = StatementCacheKey::for_source(maybe_type_id, &source, &[], &WasmSqlite)?; + + + let is_safe_to_cache_prepared = source.is_safe_to_cache_prepared(&WasmSqlite)?; + let mut qb = SqliteQueryBuilder::new(); + let sql = source.to_sql(&mut qb, &WasmSqlite).map(|()| qb.finish())?; + + let statement = cache.cached_prepared_statement( + cache_key, + sql, + is_safe_to_cache_prepared, + &[], + raw_connection.clone(), + &self.instrumentation, + ).await?.0; // Cloned RawConnection is dropped here + + + Ok(StatementUse::bind(statement, source, self.instrumentation.as_ref())?) + + } + + async fn establish_inner(database_url: &str) -> Result { + use diesel::result::ConnectionError::CouldntSetupConfiguration; + let raw_connection = RawConnection::establish(database_url).await.unwrap(); + let sqlite3 = crate::get_sqlite().await; + + sqlite3.register_diesel_sql_functions(&raw_connection.internal_connection).map_err(WasmSqliteError::from)?; + + Ok(Self { + statement_cache: StmtCache::new(), + raw_connection, + transaction_state: AnsiTransactionManager::default(), + metadata_lookup: (), + instrumentation: Arc::new(Mutex::new(None)), + }) + } +} diff --git a/diesel-wasm-sqlite/src/connection/owned_row.rs b/diesel-wasm-sqlite/src/connection/owned_row.rs new file mode 100644 index 000000000..f29dd8370 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/owned_row.rs @@ -0,0 +1,94 @@ +use std::sync::Arc; + +use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; +use crate::WasmSqlite; +use diesel::{ + backend::Backend, + row::{Field, PartialRow, Row, RowIndex, RowSealed}, +}; + +#[derive(Debug)] +pub struct OwnedSqliteRow { + pub(super) values: Vec>, + column_names: Arc<[Option]>, +} + +impl OwnedSqliteRow { + pub(super) fn new( + values: Vec>, + column_names: Arc<[Option]>, + ) -> Self { + OwnedSqliteRow { + values, + column_names, + } + } +} + +impl RowSealed for OwnedSqliteRow {} + +impl<'a> Row<'a, WasmSqlite> for OwnedSqliteRow { + type Field<'field> = OwnedSqliteField<'field> where 'a: 'field, Self: 'field; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.values.len() + } + + fn get<'field, I>(&'field self, idx: I) -> Option> + where + 'a: 'field, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(OwnedSqliteField { + row: self, + col_idx: i32::try_from(idx).ok()?, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for OwnedSqliteRow { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'idx> RowIndex<&'idx str> for OwnedSqliteRow { + fn idx(&self, field_name: &'idx str) -> Option { + self.column_names + .iter() + .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)) + } +} + +#[allow(missing_debug_implementations)] +pub struct OwnedSqliteField<'row> { + pub(super) row: &'row OwnedSqliteRow, + pub(super) col_idx: i32, +} + +impl<'row> Field<'row, WasmSqlite> for OwnedSqliteField<'row> { + fn field_name(&self) -> Option<&str> { + self.row + .column_names + .get(self.col_idx as usize) + .and_then(|o| o.as_ref().map(|s| s.as_ref())) + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'row>> { + SqliteValue::from_owned_row(self.row, self.col_idx) + } +} diff --git a/diesel-wasm-sqlite/src/connection/raw.rs b/diesel-wasm-sqlite/src/connection/raw.rs new file mode 100644 index 000000000..dd11abf25 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/raw.rs @@ -0,0 +1,203 @@ +use crate::{ + sqlite_types::{SqliteFlags, SqliteOpenFlags}, + SqliteType, WasmSqlite, WasmSqliteError, +}; +use diesel::{ + connection::statement_cache::PrepareForCache, result::*, serialize::ToSql, + sql_types::HasSqlType, +}; +use tokio::sync::oneshot; +use wasm_bindgen::{closure::Closure, JsValue}; + +use super::stmt::Statement; + +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub(super) struct RawConnection { + pub(super) internal_connection: JsValue, + drop_signal: Option>, +} + +impl Clone for RawConnection { + fn clone(&self) -> Self { + Self { + internal_connection: self.internal_connection.clone(), + drop_signal: None, + } + } +} + +impl RawConnection { + pub(super) async fn establish(database_url: &str) -> ConnectionResult { + let sqlite3 = crate::get_sqlite().await; + let database_url = if database_url.starts_with("sqlite://") { + database_url.replacen("sqlite://", "file:", 1) + } else { + database_url.to_string() + }; + let flags = SqliteOpenFlags::SQLITE_OPEN_READWRITE + | SqliteOpenFlags::SQLITE_OPEN_CREATE + | SqliteOpenFlags::SQLITE_OPEN_URI; + + let (tx, rx) = oneshot::channel::(); + // TODO: can make this into function/macro + wasm_bindgen_futures::spawn_local(async move { + match rx.await { + Ok(conn) => { + let sqlite3 = crate::get_sqlite_unchecked(); + match sqlite3.close(&conn).await { + Ok(_) => log::debug!("db closed"), + Err(e) => { + log::error!("error during db close"); + web_sys::console::log_1(&e); + } + } + } + Err(_) => { + log::error!("RawConnection never dropped."); + } + } + }); + + Ok(RawConnection { + internal_connection: sqlite3 + .open_v2(&database_url, Some(flags.bits() as i32)) + .await + .map_err(WasmSqliteError::from) + .map_err(ConnectionError::from)?, + drop_signal: Some(tx), + }) + } + + pub(super) async fn exec(&self, query: &str) -> QueryResult<()> { + let sqlite3 = crate::get_sqlite().await; + let result = sqlite3 + .exec(&self.internal_connection, query) + .await + .unwrap(); + + Ok(result) + } + + pub(super) fn rows_affected_by_last_query(&self) -> usize { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.changes(&self.internal_connection) + } + + pub(super) fn register_sql_function( + &self, + fn_name: &str, + num_args: usize, + deterministic: bool, + f: F, + ) -> QueryResult<()> + where + F: FnMut(JsValue, Vec) -> JsValue + 'static, + Ret: ToSql, + WasmSqlite: HasSqlType, + { + let sqlite3 = crate::get_sqlite_unchecked(); + let flags = Self::get_flags(deterministic); + + let cb = Closure::new(f); + sqlite3 + .create_function( + &self.internal_connection, + fn_name, + num_args + .try_into() + .expect("usize to i32 panicked in register_sql_function"), + flags, + 0, + Some(&cb), + None, + None, + ) + .unwrap(); + Ok(()) + } + + fn get_flags(deterministic: bool) -> i32 { + let mut flags = SqliteFlags::SQLITE_UTF8; + if deterministic { + flags |= SqliteFlags::SQLITE_DETERMINISTIC; + } + flags.bits() as i32 + } + + /* possible to implement this, but would need to fill in the missing wa-sqlite functions + pub(super) fn serialize(&mut self) -> SerializedDatabase { + unsafe { + let mut size: ffi::sqlite3_int64 = 0; + let data_ptr = ffi::sqlite3_serialize( + self.internal_connection.as_ptr(), + std::ptr::null(), + &mut size as *mut _, + 0, + ); + SerializedDatabase::new(data_ptr, size as usize) + } + } + + pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> { + // the cast for `ffi::SQLITE_DESERIALIZE_READONLY` is required for old libsqlite3-sys versions + #[allow(clippy::unnecessary_cast)] + unsafe { + let result = ffi::sqlite3_deserialize( + self.internal_connection.as_ptr(), + std::ptr::null(), + data.as_ptr() as *mut u8, + data.len() as i64, + data.len() as i64, + ffi::SQLITE_DESERIALIZE_READONLY as u32, + ); + + ensure_sqlite_ok(result, self.internal_connection.as_ptr()) + } + } + */ +} + +#[async_trait::async_trait(?Send)] +impl diesel_async::stmt_cache::PrepareCallback for RawConnection { + async fn prepare( + self, + sql: &str, + _metadata: &[SqliteType], + is_for_cache: PrepareForCache, + ) -> QueryResult<(Statement, Self)> { + let stmt = Statement::prepare(&self, sql, is_for_cache).await; + Ok((stmt?, self)) + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + if let Some(s) = self.drop_signal.take() { + let _ = s.send(self.internal_connection.clone()); + } else { + log::warn!("RawConnection not dropped because drop_signal is empty"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::connection::{AsyncConnection, WasmSqliteConnection}; + use diesel::connection::Connection; + use wasm_bindgen_test::*; + use web_sys::console; + wasm_bindgen_test_configure!(run_in_dedicated_worker); + + #[wasm_bindgen_test] + async fn test_fn_registration() { + let mut result = WasmSqliteConnection::establish("test").await; + let mut conn = result.unwrap(); + console::log_1(&"CONNECTED".into()); + conn.raw + .register_sql_function("test", 0, true, |ctx, values| { + console::log_1(&"Inside Fn".into()); + }); + } +} diff --git a/diesel-wasm-sqlite/src/connection/row.rs b/diesel-wasm-sqlite/src/connection/row.rs new file mode 100644 index 000000000..41c2c132d --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/row.rs @@ -0,0 +1,412 @@ +use std::cell::{Ref, RefCell}; +use std::rc::Rc; +use std::sync::Arc; + +use super::owned_row::OwnedSqliteRow; +use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; +use super::stmt::StatementUse; +use crate::WasmSqlite; +use diesel::{ + backend::Backend, + row::{Field, IntoOwnedRow, PartialRow, Row, RowIndex, RowSealed}, +}; + +#[allow(missing_debug_implementations)] +pub struct SqliteRow<'stmt, 'query> { + pub(super) inner: Rc>>, + pub(super) field_count: usize, +} + +pub(super) enum PrivateSqliteRow<'stmt, 'query> { + Direct(StatementUse<'stmt, 'query>), + Duplicated { + values: Vec>, + column_names: Rc<[Option]>, + }, +} + +impl<'stmt> IntoOwnedRow<'stmt, WasmSqlite> for SqliteRow<'stmt, '_> { + type OwnedRow = OwnedSqliteRow; + + type Cache = Option]>>; + + fn into_owned(self, column_name_cache: &mut Self::Cache) -> Self::OwnedRow { + self.inner.borrow().moveable(column_name_cache) + } +} + +impl<'stmt, 'query> PrivateSqliteRow<'stmt, 'query> { + pub(super) fn duplicate( + &mut self, + column_names: &mut Option]>>, + ) -> PrivateSqliteRow<'stmt, 'query> { + match self { + PrivateSqliteRow::Direct(stmt) => { + let column_names = if let Some(column_names) = column_names { + column_names.clone() + } else { + let c: Rc<[Option]> = Rc::from( + (0..stmt.column_count()) + .map(|idx| stmt.field_name(idx).map(|s| s.to_owned())) + .collect::>(), + ); + *column_names = Some(c.clone()); + c + }; + PrivateSqliteRow::Duplicated { + values: (0..stmt.column_count()) + .map(|idx| stmt.copy_value(idx)) + .collect(), + column_names, + } + } + PrivateSqliteRow::Duplicated { + values, + column_names, + } => PrivateSqliteRow::Duplicated { + values: values + .iter() + .map(|v| v.as_ref().map(|v| v.duplicate())) + .collect(), + column_names: column_names.clone(), + }, + } + } + + pub(super) fn moveable( + &self, + column_name_cache: &mut Option]>>, + ) -> OwnedSqliteRow { + match self { + PrivateSqliteRow::Direct(stmt) => { + if column_name_cache.is_none() { + *column_name_cache = Some( + (0..stmt.column_count()) + .map(|idx| stmt.field_name(idx).map(|s| s.to_owned())) + .collect::>() + .into(), + ); + } + let column_names = Arc::clone( + column_name_cache + .as_ref() + .expect("This is initialized above"), + ); + OwnedSqliteRow::new( + (0..stmt.column_count()) + .map(|idx| stmt.copy_value(idx)) + .collect(), + column_names, + ) + } + PrivateSqliteRow::Duplicated { + values, + column_names, + } => { + if column_name_cache.is_none() { + *column_name_cache = Some( + (*column_names) + .iter() + .map(|s| s.to_owned()) + .collect::>() + .into(), + ); + } + let column_names = Arc::clone( + column_name_cache + .as_ref() + .expect("This is initialized above"), + ); + OwnedSqliteRow::new( + values + .iter() + .map(|v| v.as_ref().map(|v| v.duplicate())) + .collect(), + column_names, + ) + } + } + } +} + +impl<'stmt, 'query> RowSealed for SqliteRow<'stmt, 'query> {} + +impl<'stmt, 'query> Row<'stmt, WasmSqlite> for SqliteRow<'stmt, 'query> { + type Field<'field> = SqliteField<'field, 'field> where 'stmt: 'field, Self: 'field; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.field_count + } + + fn get<'field, I>(&'field self, idx: I) -> Option> + where + 'stmt: 'field, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(SqliteField { + row: self.inner.borrow(), + col_idx: i32::try_from(idx).ok()?, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl<'stmt, 'query> RowIndex for SqliteRow<'stmt, 'query> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count { + Some(idx) + } else { + None + } + } +} + +impl<'stmt, 'idx, 'query> RowIndex<&'idx str> for SqliteRow<'stmt, 'query> { + fn idx(&self, field_name: &'idx str) -> Option { + match &mut *self.inner.borrow_mut() { + PrivateSqliteRow::Direct(stmt) => stmt.index_for_column_name(field_name), + PrivateSqliteRow::Duplicated { column_names, .. } => column_names + .iter() + .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)), + } + } +} + +#[allow(missing_debug_implementations)] +pub struct SqliteField<'stmt, 'query> { + pub(super) row: Ref<'stmt, PrivateSqliteRow<'stmt, 'query>>, + pub(super) col_idx: i32, +} + +impl<'stmt, 'query> Field<'stmt, WasmSqlite> for SqliteField<'stmt, 'query> { + fn field_name(&self) -> Option<&str> { + match &*self.row { + PrivateSqliteRow::Direct(stmt) => stmt.field_name(self.col_idx), + PrivateSqliteRow::Duplicated { column_names, .. } => column_names + .get(self.col_idx as usize) + .and_then(|t| t.as_ref().map(|n| n as &str)), + } + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'_>> { + SqliteValue::new(Ref::clone(&self.row), self.col_idx) + } +} + +/* +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fun_with_row_iters() { + crate::table! { + #[allow(unused_parens)] + users(id) { + id -> Integer, + name -> Text, + } + } + + use crate::connection::LoadConnection; + use crate::deserialize::{FromSql, FromSqlRow}; + use crate::prelude::*; + use crate::row::{Field, Row}; + use crate::sql_types; + + let conn = &mut crate::test_helpers::connection(); + + crate::sql_query("CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);") + .execute(conn) + .unwrap(); + + crate::insert_into(users::table) + .values(vec![ + (users::id.eq(1), users::name.eq("Sean")), + (users::id.eq(2), users::name.eq("Tess")), + ]) + .execute(conn) + .unwrap(); + + let query = users::table.select((users::id, users::name)); + + let expected = vec![(1, String::from("Sean")), (2, String::from("Tess"))]; + + let row_iter = conn.load(query).unwrap(); + for (row, expected) in row_iter.zip(&expected) { + let row = row.unwrap(); + + let deserialized = <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(&row) + .unwrap(); + + assert_eq!(&deserialized, expected); + } + + { + let collected_rows = conn.load(query).unwrap().collect::>(); + + for (row, expected) in collected_rows.iter().zip(&expected) { + let deserialized = row + .as_ref() + .map(|row| { + <(i32, String) as FromSqlRow< + (sql_types::Integer, sql_types::Text), + _, + >>::build_from_row(row).unwrap() + }) + .unwrap(); + + assert_eq!(&deserialized, expected); + } + } + + let mut row_iter = conn.load(query).unwrap(); + + let first_row = row_iter.next().unwrap().unwrap(); + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(first_fields); + + let second_row = row_iter.next().unwrap().unwrap(); + let second_fields = (second_row.get(0).unwrap(), second_row.get(1).unwrap()); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_values); + assert!(row_iter.next().unwrap().is_err()); + std::mem::drop(second_fields); + + assert!(row_iter.next().is_none()); + + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let second_fields = (second_row.get(0).unwrap(), second_row.get(1).unwrap()); + + let first_values = (first_fields.0.value(), first_fields.1.value()); + let second_values = (second_fields.0.value(), second_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0) + .unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1) + .unwrap(), + expected[0].1 + ); + + assert_eq!( + >::from_nullable_sql(second_values.0) + .unwrap(), + expected[1].0 + ); + assert_eq!( + >::from_nullable_sql(second_values.1) + .unwrap(), + expected[1].1 + ); + + let first_fields = (first_row.get(0).unwrap(), first_row.get(1).unwrap()); + let first_values = (first_fields.0.value(), first_fields.1.value()); + + assert_eq!( + >::from_nullable_sql(first_values.0) + .unwrap(), + expected[0].0 + ); + assert_eq!( + >::from_nullable_sql(first_values.1) + .unwrap(), + expected[0].1 + ); + } + + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + crate::define_sql_function! {fn sleep(a: diesel::sql_types::Integer) -> diesel::sql_types::Integer} + + #[test] + #[cfg(feature = "returning_clauses_for_sqlite_3_35")] + fn parallel_iter_with_error() { + use crate::connection::Connection; + use crate::connection::LoadConnection; + use crate::connection::SimpleConnection; + use crate::expression_methods::ExpressionMethods; + use crate::SqliteConnection; + use std::sync::{Arc, Barrier}; + use std::time::Duration; + + let temp_dir = tempfile::tempdir().unwrap(); + let db_path = format!("{}/test.db", temp_dir.path().display()); + let mut conn1 = SqliteConnection::establish(&db_path).unwrap(); + let mut conn2 = SqliteConnection::establish(&db_path).unwrap(); + + crate::table! { + users { + id -> Integer, + name -> Text, + } + } + + conn1 + .batch_execute("CREATE TABLE users(id INTEGER NOT NULL PRIMARY KEY, name TEXT)") + .unwrap(); + + let barrier = Arc::new(Barrier::new(2)); + let barrier2 = barrier.clone(); + + // we unblock the main thread from the sleep function + sleep_utils::register_impl(&mut conn2, move |a: i32| { + barrier.wait(); + std::thread::sleep(Duration::from_secs(a as u64)); + a + }) + .unwrap(); + + // spawn a background thread that locks the database file + let handle = std::thread::spawn(move || { + use crate::query_dsl::RunQueryDsl; + + conn2 + .immediate_transaction(|conn| diesel::select(sleep(1)).execute(conn)) + .unwrap(); + }); + barrier2.wait(); + + // execute some action that also requires a lock + let mut iter = conn1 + .load( + diesel::insert_into(users::table) + .values((users::id.eq(1), users::name.eq("John"))) + .returning(users::id), + ) + .unwrap(); + + // get the first iterator result, that should return the lock error + let n = iter.next().unwrap(); + assert!(n.is_err()); + + // check that the iterator is now empty + let n = iter.next(); + assert!(n.is_none()); + + // join the background thread + handle.join().unwrap(); + } +} +*/ diff --git a/diesel-wasm-sqlite/src/connection/serialized_database.rs b/diesel-wasm-sqlite/src/connection/serialized_database.rs new file mode 100644 index 000000000..e302a097c --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/serialized_database.rs @@ -0,0 +1,42 @@ +use std::ops::Deref; + +/// `SerializedDatabase` is a wrapper for a serialized database that is dynamically allocated by calling `sqlite3_serialize`. +/// This RAII wrapper is necessary to deallocate the memory when it goes out of scope with `sqlite3_free`. +#[derive(Debug)] +pub struct SerializedDatabase { + data: JsValue, + len: usize, +} + +impl SerializedDatabase { + /// Creates a new `SerializedDatabase` with the given data pointer and length. + /// + /// SAFETY: The data pointer needs to be returned by sqlite + /// and the length must match the underlying buffer pointer + pub(crate) unsafe fn new(data: *mut u8, len: usize) -> Self { + Self { data, len } + } + + /// Returns a slice of the serialized database. + pub fn as_slice(&self) -> &[u8] { + // The pointer is never null because we don't pass the NO_COPY flag + unsafe { std::slice::from_raw_parts(self.data, self.len) } + } +} + +impl Deref for SerializedDatabase { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl Drop for SerializedDatabase { + /// Deallocates the memory of the serialized database when it goes out of scope. + fn drop(&mut self) { + unsafe { + ffi::sqlite3_free(self.data as _); + } + } +} diff --git a/diesel-wasm-sqlite/src/connection/sqlite_value.rs b/diesel-wasm-sqlite/src/connection/sqlite_value.rs new file mode 100644 index 000000000..e94e77217 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/sqlite_value.rs @@ -0,0 +1,159 @@ +#![allow(unsafe_code)] // ffi calls + +use std::cell::Ref; + +use crate::ffi::{self, SQLiteCompatibleType}; +use crate::{backend::SqliteType, sqlite_types}; +use wasm_bindgen::JsValue; + +use super::owned_row::OwnedSqliteRow; +use super::row::PrivateSqliteRow; + +/// Raw sqlite value as received from the database +/// +/// Use existing `FromSql` implementations to convert this into +/// rust values +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct SqliteValue<'row, 'stmt, 'query> { + // This field exists to ensure that nobody + // can modify the underlying row while we are + // holding a reference to some row value here + _row: Option>>, + // we extract the raw value pointer as part of the constructor + // to safe the match statements for each method + // According to benchmarks this leads to a ~20-30% speedup + // + // + // This is sound as long as nobody calls `stmt.step()` + // while holding this value. We ensure this by including + // a reference to the row above. + value: SQLiteCompatibleType, +} + +#[derive(Debug, Clone)] +pub(super) struct OwnedSqliteValue { + pub(super) value: SQLiteCompatibleType, +} + +// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup +// see https://www.sqlite.org/c3ref/value.html +unsafe impl Send for OwnedSqliteValue {} + +impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> { + pub(super) fn new( + row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>, + col_idx: i32, + ) -> Option> { + let value = match &*row { + PrivateSqliteRow::Direct(stmt) => stmt.column_value(col_idx)?, + PrivateSqliteRow::Duplicated { values, .. } => values + .get(col_idx as usize) + .and_then(|v| v.as_ref())? + .value + .clone(), + }; + + let ret = Self { + _row: Some(row), + value, + }; + if ret.value_type().is_none() { + None + } else { + Some(ret) + } + } + + pub(super) fn from_owned_row( + row: &'row OwnedSqliteRow, + col_idx: i32, + ) -> Option> { + let value = row + .values + .get(col_idx as usize) + .and_then(|v| v.as_ref())? + .value + .clone(); + let ret = Self { _row: None, value }; + if ret.value_type().is_none() { + None + } else { + Some(ret) + } + } + + pub(crate) fn parse_string(&self, f: impl FnOnce(String) -> R) -> R { + let sqlite3 = crate::get_sqlite_unchecked(); + let s = sqlite3.value_text(&self.value); + f(s) + } + + // TODO: Wasm bindgen can't work with references yet + // not sure if this will effect perf + pub(crate) fn read_text(&self) -> String { + self.parse_string(|s| s) + } + + pub(crate) fn read_blob(&self) -> Vec { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.value_blob(&self.value) + } + + pub(crate) fn read_integer(&self) -> i32 { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.value_int(&self.value) + } + + pub(crate) fn read_long(&self) -> i64 { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.value_int64(&self.value) + } + + pub(crate) fn read_double(&self) -> f64 { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.value_double(&self.value) + } + + /// Get the type of the value as returned by sqlite + pub fn value_type(&self) -> Option { + let sqlite3 = crate::get_sqlite_unchecked(); + let tpe = sqlite3.value_type(&self.value); + match tpe { + sqlite_types::SQLITE_TEXT => Some(SqliteType::Text), + sqlite_types::SQLITE_INTEGER => Some(SqliteType::Long), + sqlite_types::SQLITE_FLOAT => Some(SqliteType::Double), + sqlite_types::SQLITE_BLOB => Some(SqliteType::Binary), + sqlite_types::SQLITE_NULL => None, + _ => unreachable!( + "Sqlite's documentation state that this case ({}) is not reachable. \ + If you ever see this error message please open an issue at \ + https://github.com/diesel-rs/diesel.", + tpe + ), + } + } +} + +impl OwnedSqliteValue { + pub(super) fn copy_from_ptr(ptr: &JsValue) -> Option { + let sqlite3 = crate::get_sqlite_unchecked(); + let tpe = sqlite3.value_type(&ptr); + if sqlite_types::SQLITE_NULL == tpe { + return None; + } + + let value = sqlite3.value_dup(ptr); + + Some(Self { + value: value.into(), + }) + } + + pub(super) fn duplicate(&self) -> OwnedSqliteValue { + let sqlite3 = crate::get_sqlite_unchecked(); + let value = sqlite3.value_dup(&self.value); + OwnedSqliteValue { + value: value.into(), + } + } +} diff --git a/diesel-wasm-sqlite/src/connection/statement_iterator.rs b/diesel-wasm-sqlite/src/connection/statement_iterator.rs new file mode 100644 index 000000000..393ec9e47 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/statement_iterator.rs @@ -0,0 +1,172 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use super::row::{PrivateSqliteRow, SqliteRow}; +use super::stmt::StatementUse; +use crate::result::QueryResult; + +#[allow(missing_debug_implementations)] +pub struct StatementIterator<'stmt, 'query> { + inner: PrivateStatementIterator<'stmt, 'query>, + column_names: Option]>>, + field_count: usize, +} + +impl<'stmt, 'query> StatementIterator<'stmt, 'query> { + #[cold] + #[allow(unsafe_code)] // call to unsafe function + fn handle_duplicated_row_case( + outer_last_row: &mut Rc>>, + column_names: &mut Option]>>, + field_count: usize, + ) -> Option>> { + // We don't own the statement. There is another existing reference, likely because + // a user stored the row in some long time container before calling next another time + // In this case we copy out the current values into a temporary store and advance + // the statement iterator internally afterwards + let last_row = { + let mut last_row = match outer_last_row.try_borrow_mut() { + Ok(o) => o, + Err(_e) => { + return Some(Err(crate::result::Error::DeserializationError( + "Failed to reborrow row. Try to release any `SqliteField` or `SqliteValue` \ + that exists at this point" + .into(), + ))); + } + }; + let last_row = &mut *last_row; + let duplicated = last_row.duplicate(column_names); + std::mem::replace(last_row, duplicated) + }; + if let PrivateSqliteRow::Direct(mut stmt) = last_row { + let res = unsafe { + // This is actually safe here as we've already + // performed one step. For the first step we would have + // used `PrivateStatementIterator::NotStarted` where we don't + // have access to `PrivateSqliteRow` at all + stmt.step(false) + }; + *outer_last_row = Rc::new(RefCell::new(PrivateSqliteRow::Direct(stmt))); + match res { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => Some(Ok(SqliteRow { + inner: Rc::clone(outer_last_row), + field_count, + })), + } + } else { + // any other state than `PrivateSqliteRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + } + } +} + +enum PrivateStatementIterator<'stmt, 'query> { + NotStarted(Option>), + Started(Rc>>), +} + +impl<'stmt, 'query> StatementIterator<'stmt, 'query> { + pub fn new(stmt: StatementUse<'stmt, 'query>) -> StatementIterator<'stmt, 'query> { + Self { + inner: PrivateStatementIterator::NotStarted(Some(stmt)), + column_names: None, + field_count: 0, + } + } +} + +impl<'stmt, 'query> Iterator for StatementIterator<'stmt, 'query> { + type Item = QueryResult>; + + #[allow(unsafe_code)] // call to unsafe function + fn next(&mut self) -> Option { + use PrivateStatementIterator::{NotStarted, Started}; + match &mut self.inner { + NotStarted(ref mut stmt @ Some(_)) => { + let mut stmt = stmt + .take() + .expect("It must be there because we checked that above"); + let step = unsafe { + // This is safe as we pass `first_step = true` to reset the cached column names + stmt.step(true) + }; + match step { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => { + let field_count = stmt.column_count() as usize; + self.field_count = field_count; + let inner = Rc::new(RefCell::new(PrivateSqliteRow::Direct(stmt))); + self.inner = Started(inner.clone()); + Some(Ok(SqliteRow { inner, field_count })) + } + } + } + Started(ref mut last_row) => { + // There was already at least one iteration step + // We check here if the caller already released the row value or not + // by checking if our Rc owns the data or not + if let Some(last_row_ref) = Rc::get_mut(last_row) { + // We own the statement, there is no other reference here. + // This means we don't need to copy out values from the sqlite provided + // datastructures for now + // We don't need to use the runtime borrowing system of the RefCell here + // as we have a mutable reference, so all of this below is checked at compile time + if let PrivateSqliteRow::Direct(ref mut stmt) = last_row_ref.get_mut() { + let step = unsafe { + // This is actually safe here as we've already + // performed one step. For the first step we would have + // used `PrivateStatementIterator::NotStarted` where we don't + // have access to `PrivateSqliteRow` at all + + stmt.step(false) + }; + match step { + Err(e) => Some(Err(e)), + Ok(false) => None, + Ok(true) => { + let field_count = self.field_count; + Some(Ok(SqliteRow { + inner: Rc::clone(last_row), + field_count, + })) + } + } + } else { + // any other state than `PrivateSqliteRow::Direct` is invalid here + // and should not happen. If this ever happens this is a logic error + // in the code above + unreachable!( + "You've reached an impossible internal state. \ + If you ever see this error message please open \ + an issue at https://github.com/diesel-rs/diesel \ + providing example code how to trigger this error." + ) + } + } else { + Self::handle_duplicated_row_case( + last_row, + &mut self.column_names, + self.field_count, + ) + } + } + NotStarted(_s) => { + // we likely got an error while executing the other + // `NotStarted` branch above. In this case we just want to stop + // iterating here + None + } + } + } +} diff --git a/diesel-wasm-sqlite/src/connection/stmt.rs b/diesel-wasm-sqlite/src/connection/stmt.rs new file mode 100644 index 000000000..53fb5ba78 --- /dev/null +++ b/diesel-wasm-sqlite/src/connection/stmt.rs @@ -0,0 +1,422 @@ +#![allow(unsafe_code)] //TODO: can probably remove for wa-sqlite +use super::bind_collector::{InternalSqliteBindValue, SqliteBindCollector}; +use super::raw::RawConnection; +use super::sqlite_value::OwnedSqliteValue; +use crate::ffi::SQLiteCompatibleType; +use crate::{ + sqlite_types::{self, PrepareOptions, SqlitePrepareFlags}, + SqliteType, WasmSqlite, +}; +use diesel::{ + connection::{ + statement_cache::{MaybeCached, PrepareForCache}, + Instrumentation, + }, + query_builder::{QueryFragment, QueryId}, + result::{Error::DatabaseError, *}, +}; +use std::cell::OnceCell; +use std::sync::Mutex; + +use wasm_bindgen::JsValue; + +// this is OK b/c web runs in one thread +unsafe impl Send for Statement {} +pub(super) struct Statement { + inner_statement: JsValue, + drop_signal: Option>, +} + +impl Statement { + pub(super) async fn prepare( + raw_connection: &RawConnection, + sql: &str, + is_cached: PrepareForCache, + ) -> QueryResult { + let sqlite3 = crate::get_sqlite_unchecked(); + let flags = if matches!(is_cached, PrepareForCache::Yes) { + Some(SqlitePrepareFlags::SQLITE_PREPARE_PERSISTENT.bits()) + } else { + None + }; + + let options = PrepareOptions { + flags, + unscoped: None, + }; + + let stmt = sqlite3 + .prepare( + &raw_connection.internal_connection, + sql, + serde_wasm_bindgen::to_value(&options).unwrap(), + ) + .await + .unwrap(); + + let (tx, rx) = tokio::sync::oneshot::channel::(); + + // We don't have `AsyncDrop` in rust yet. + // instead, we `send` a signal on Drop for the destructor to run in an + // asynchronously-spawned task. + wasm_bindgen_futures::spawn_local(async move { + match rx.await { + Ok(inner_statement) => { + let this = Statement { + inner_statement, + drop_signal: None, + }; + + this.reset().await; + this.clear_bindings(); + } + Err(_) => { + log::error!("Statement never dropped"); + } + } + }); + + Ok(Statement { + inner_statement: stmt, + drop_signal: Some(tx), + }) + } + + // The caller of this function has to ensure that: + // * Any buffer provided as `SqliteBindValue::BorrowedBinary`, `SqliteBindValue::Binary` + // `SqliteBindValue::String` or `SqliteBindValue::BorrowedString` is valid + // till either a new value is bound to the same parameter or the underlying + // prepared statement is dropped. + fn bind( + &self, + _tpe: SqliteType, + value: InternalSqliteBindValue<'_>, + bind_index: i32, + ) -> QueryResult { + let sqlite3 = crate::get_sqlite_unchecked(); + let value = + serde_wasm_bindgen::to_value(&value).expect("Bind value failed to convert to JsValue"); + let result = sqlite3 + .bind(&self.inner_statement, bind_index, value.into()) + .unwrap(); + + // TODO:insipx Pretty sure we can have a simpler implementation here + // making use of `wa-sqlite` `bind` which abstracts over the individual bind functions in + // sqlite3. However, not sure how this will work further up the stack. + // This might not work because of differences in how serde_json recognizes js types + // and how wa-sqlite recogizes js types. In that case, need to resort to matching on + // individual types with bind_$type fns . + + Ok(result) + } + + async fn reset(&self) { + let sqlite3 = crate::get_sqlite_unchecked(); + let _ = sqlite3.reset(&self.inner_statement).await.unwrap(); + } + + fn clear_bindings(&self) { + let sqlite3 = crate::get_sqlite_unchecked(); + let _ = sqlite3.clear_bindings(&self.inner_statement).unwrap(); + } + + /* not sure if there is equivalent method or just cloning the stmt is enough + fn raw_connection(&self) -> *mut ffi::sqlite3 { + unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) } + } + */ +} + +/* TODO: Useful for converting JS Error messages to Rust +fn last_error(raw_connection: *mut ffi::sqlite3) -> Error { + let error_message = last_error_message(raw_connection); + let error_information = Box::new(error_message); + let error_kind = match last_error_code(raw_connection) { + ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => { + DatabaseErrorKind::UniqueViolation + } + ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation, + ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation, + ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::Unknown, + }; + DatabaseError(error_kind, error_information) +} + +fn last_error_message(conn: *mut ffi::sqlite3) -> String { + let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) }; + c_str.to_string_lossy().into_owned() +} + + +fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int { + unsafe { ffi::sqlite3_extended_errcode(conn) } +} +*/ + +impl Drop for Statement { + fn drop(&mut self) { + let sqlite3 = crate::get_sqlite_unchecked(); + // TODO:insipx potential problems here. + // wa-sqlite does not throw an error if finalize fails: -- it might just crash + // doc: https://rhashimoto.github.io/wa-sqlite/docs/interfaces/SQLiteAPI.html#finalize.finalize-1 + // in that case we might not know if this errored or not + // maybe depends how wasm panic/errors work + // Worth unit testing the Drop implementation. + let _ = sqlite3 + .finalize(&self.inner_statement) + .expect("Error finalized SQLite prepared statement"); + } +} + +// A warning for future editors: +// Changing this code to something "simpler" may +// introduce undefined behaviour. Make sure you read +// the following discussions for details about +// the current version: +// +// * https://github.com/weiznich/diesel/pull/7 +// * https://users.rust-lang.org/t/code-review-for-unsafe-code-in-diesel/66798/ +// * https://github.com/rust-lang/unsafe-code-guidelines/issues/194 +struct BoundStatement<'stmt, 'query> { + statement: MaybeCached<'stmt, Statement>, + // we need to store the query here to ensure no one does + // drop it till the end of the statement + // We use a boxed queryfragment here just to erase the + // generic type, we use NonNull to communicate + // that this is a shared buffer + query: Option + 'query>>, + instrumentation: &'stmt Mutex, + has_error: bool, +} + +impl<'stmt, 'query> BoundStatement<'stmt, 'query> { + fn bind( + statement: MaybeCached<'stmt, Statement>, + query: T, + instrumentation: &'stmt Mutex, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + // Don't use a trait object here to prevent using a virtual function call + // For sqlite this can introduce a measurable overhead + // Query is boxed here to make sure it won't move in memory anymore, so any bind + // it could output would stay valid. + let query = Box::new(query); + + let mut bind_collector = SqliteBindCollector::new(); + query.collect_binds(&mut bind_collector, &mut (), &WasmSqlite)?; + let SqliteBindCollector { binds } = bind_collector; + + let mut ret = BoundStatement { + statement, + query: None, + instrumentation, + has_error: false, + }; + + ret.bind_buffers(binds)?; + + let query = query as Box + 'query>; + ret.query = Some(query); + + Ok(ret) + } + + // This is a separated function so that + // not the whole constructor is generic over the query type T. + // This hopefully prevents binary bloat. + fn bind_buffers( + &mut self, + binds: Vec<(InternalSqliteBindValue<'_>, SqliteType)>, + ) -> QueryResult<()> { + for (bind_idx, (bind, tpe)) in (1..).zip(binds) { + // It's safe to call bind here as: + // * The type and value matches + // * We ensure that corresponding buffers lives long enough below + // * The statement is not used yet by `step` or anything else + let _ = self.statement.bind(tpe, bind, bind_idx)?; + + // we don't track binds to free like sqlite3 C bindings + // The assumption is that wa-sqlite, being WASM run in web browser that + // lies in the middle of rust -> sqlite, takes care of this for us. + // if we run into memory issues, especially memory leaks + // this should be the first place to pay attention to. + // + // The bindings shuold be collected/freed with JS once `clear_bindings` is + // run on `Drop` for `BoundStatement` + } + Ok(()) + } + + fn finish_query_with_error(mut self, _e: &Error) { + self.has_error = true; + /* + if let Some(q) = self.query { + // it's safe to get a reference from this ptr as it's guaranteed to not be null + let q = unsafe { q.as_ref() }; + self.instrumentation.on_connection_event( + diesel::connection::InstrumentationEvent::FinishQuery { + query: &crate::debug_query(&q), + error: Some(e), + }, + ); + } + */ + } + + // FIXME: [`AsyncDrop`](https://github.com/rust-lang/rust/issues/126482) is a missing feature in rust. + // Until then we need to manually reset the statement object. + pub async fn reset(&mut self) { + self.statement.reset().await; + self.statement.clear_bindings() + } +} + +// Eventually replace with `AsyncDrop`: https://github.com/rust-lang/rust/issues/126482 +impl<'stmt, 'query> Drop for BoundStatement<'stmt, 'query> { + fn drop(&mut self) { + let sender = self + .statement + .drop_signal + .take() + .expect("Drop may only be ran once"); + let _ = sender.send(self.statement.inner_statement.clone()); + self.query.take(); + } +} + +#[allow(missing_debug_implementations)] +pub struct StatementUse<'stmt, 'query> { + statement: BoundStatement<'stmt, 'query>, + column_names: OnceCell>, +} + +impl<'stmt, 'query> StatementUse<'stmt, 'query> { + pub(super) fn bind( + statement: MaybeCached<'stmt, Statement>, + query: T, + instrumentation: &'stmt Mutex, + ) -> QueryResult> + where + T: QueryFragment + QueryId + 'query, + { + Ok(Self { + statement: BoundStatement::bind(statement, query, instrumentation)?, + column_names: OnceCell::new(), + }) + } + + pub(super) async fn run(mut self) -> QueryResult<()> { + // This is safe as we pass `first_step = true` + // and we consume the statement so nobody could + // access the columns later on anyway. + let r = self.step(true).await.map(|_| ()); + + if let Err(ref e) = r { + self.statement.finish_query_with_error(e); + } + r + } + + // This function is marked as unsafe incorrectly passing `false` to `first_step` + // for a first call to this function could cause access to freed memory via + // the cached column names. + // + // It's always safe to call this function with `first_step = true` as this removes + // the cached column names + pub(super) async fn step(&mut self, first_step: bool) -> QueryResult { + let sqlite3 = crate::get_sqlite_unchecked(); + let res = match serde_wasm_bindgen::from_value::( + sqlite3 + .step(&self.statement.statement.inner_statement) + .await + .unwrap(), + ) + .unwrap() + { + sqlite_types::SQLITE_DONE => Ok(false), + sqlite_types::SQLITE_ROW => Ok(true), + _ => panic!("SQLite Step returned Unhandled Result Code. Turn into err message"), + }; + + if first_step { + self.column_names = OnceCell::new(); + } + res + } + + // The returned string pointer is valid until either the prepared statement is + // destroyed by sqlite3_finalize() or until the statement is automatically + // reprepared by the first call to sqlite3_step() for a particular run or + // until the next call to sqlite3_column_name() or sqlite3_column_name16() + // on the same column. + // + // https://sqlite.org/c3ref/column_name.html + // + // Note: This function is marked as unsafe, as calling it can invalidate + // other existing column name pointers on the same column. To prevent that, + // it should maximally be called once per column at all. + fn column_name(&self, idx: i32) -> String { + let sqlite3 = crate::get_sqlite_unchecked(); + + sqlite3.column_name(&self.statement.statement.inner_statement, idx) + } + + pub(super) fn column_count(&self) -> i32 { + let sqlite3 = crate::get_sqlite_unchecked(); + sqlite3.column_count(&self.statement.statement.inner_statement) + } + + pub(super) fn index_for_column_name(&mut self, field_name: &str) -> Option { + (0..self.column_count()) + .find(|idx| self.field_name(*idx) == Some(field_name)) + .map(|v| v as usize) + } + + pub(super) fn field_name(&self, idx: i32) -> Option<&str> { + let column_names = self.column_names.get_or_init(|| { + let count = self.column_count(); + (0..count).map(|idx| self.column_name(idx)).collect() + }); + + column_names.get(idx as usize).map(AsRef::as_ref) + } + + pub(super) fn copy_value(&self, idx: i32) -> Option { + OwnedSqliteValue::copy_from_ptr(&self.column_value(idx)?.into()) + } + + pub(super) fn column_value(&self, idx: i32) -> Option { + let sqlite3 = crate::get_sqlite_unchecked(); + Some(sqlite3.column(&self.statement.statement.inner_statement, idx)) + } +} +/* +#[cfg(test)] +mod tests { + use crate::prelude::*; + use crate::sql_types::Text; + + // this is a regression test for + // https://github.com/diesel-rs/diesel/issues/3558 + #[test] + fn check_out_of_bounds_bind_does_not_panic_on_drop() { + let mut conn = SqliteConnection::establish(":memory:").unwrap(); + + let e = crate::sql_query("SELECT '?'") + .bind::("foo") + .execute(&mut conn); + + assert!(e.is_err()); + let e = e.unwrap_err(); + if let crate::result::Error::DatabaseError(crate::result::DatabaseErrorKind::Unknown, m) = e + { + assert_eq!(m.message(), "column index out of range"); + } else { + panic!("Wrong error returned"); + } + } +} +*/ diff --git a/diesel-wasm-sqlite/src/ffi.rs b/diesel-wasm-sqlite/src/ffi.rs new file mode 100644 index 000000000..9b11e0c95 --- /dev/null +++ b/diesel-wasm-sqlite/src/ffi.rs @@ -0,0 +1,220 @@ +use wasm_bindgen::{prelude::*, JsValue}; + +/* +/// Simple Connection +#[wasm_bindgen(module = "/src/package.js")] +extern "C" { + #[wasm_bindgen(catch)] + pub fn batch_execute(database: &JsValue, query: &str) -> Result<(), JsValue>; + + #[wasm_bindgen(catch)] + pub async fn establish(database_url: &str) -> Result; +} +*/ + +/* once https://github.com/rust-lang/rust/issues/128183 is merged this would work +pub mod consts { + pub const SQLITE_INTEGER: i32 = super::SQLITE_INTEGER; +} +*/ + +// Constants +#[wasm_bindgen(module = "/src/wa-sqlite-diesel-bundle.js")] +extern "C" { + pub static SQLITE_DONE: i32; + pub static SQLITE_ROW: i32; + + // Fundamental datatypes. + // https://www.sqlite.org/c3ref/c_blob.html + pub static SQLITE_INTEGER: i32; + pub static SQLITE_FLOAT: i32; + pub static SQLITE_TEXT: i32; + pub static SQLITE_BLOB: i32; + pub static SQLITE_NULL: i32; +} + +// WASM is ran in the browser `main thread`. Tokio is only a single-threaded runtime. +// We need SQLite available globally, so this should be ok until we get threads with WASI or +// something. At which point we can (hopefully) use multi-threaded async runtime to block the +// thread and get SQLite. +unsafe impl Send for SQLite {} +unsafe impl Sync for SQLite {} + +/// Direct Shim for wa-sqlite +#[wasm_bindgen(module = "/src/wa-sqlite-diesel-bundle.js")] +extern "C" { + pub type SQLite; + #[derive(Debug, Clone)] + pub type SQLiteCompatibleType; + // pub type SqlitePrepareOptions; + + #[wasm_bindgen(constructor)] + pub fn new(module: JsValue) -> SQLite; + + #[wasm_bindgen(static_method_of = SQLite)] + pub async fn wasm_module() -> JsValue; + + #[wasm_bindgen(method)] + pub fn result_text(this: &SQLite, context: i32, value: String); + + #[wasm_bindgen(method)] + pub fn result_int(this: &SQLite, context: i32, value: i32); + + #[wasm_bindgen(method)] + pub fn result_int64(this: &SQLite, context: i32, value: i64); + + #[wasm_bindgen(method)] + pub fn result_double(this: &SQLite, context: i32, value: f64); + + #[wasm_bindgen(method)] + pub fn result_blob(this: &SQLite, context: i32, value: Vec); + + #[wasm_bindgen(method)] + pub fn result_null(this: &SQLite, context: i32); + + #[wasm_bindgen(method, catch)] + pub fn bind( + this: &SQLite, + stmt: &JsValue, + idx: i32, + value: SQLiteCompatibleType, + ) -> Result; + /* + #[wasm_bindgen(method, catch)] + pub fn bind_blob( + this: &SQLite, + stmt: &JsValue, + idx: i32, + value: Vec, + ) -> Result; + + // JsValue here is an interesting type that needs to be ported in order to make use of this + // but not currently using it. + + #[wasm_bindgen(method, catch)] + pub fn bind_collection( + this: &SQLite, + stmt: &JsValue, + bindings: JsValue, + ) -> Result; + + #[wasm_bindgen(method, catch)] + pub fn bind_double(this: &SQLite, stmt: &JsValue, idx: i32, value: f64) + -> Result; + + #[wasm_bindgen(method, catch)] + pub fn bind_int(this: &SQLite, stmt: &JsValue, idx: i32, value: i32) -> Result; + + #[wasm_bindgen(method, catch)] + pub fn bind_int64(this: &SQLite, stmt: &JsValue, idx: i32, value: i64) -> Result; + + #[wasm_bindgen(method, catch)] + pub fn bind_null(this: &SQLite, stmt: &JsValue, idx: i32) -> Result; + */ + #[wasm_bindgen(method)] + pub fn bind_parameter_count(this: &SQLite, stmt: &JsValue) -> i32; + + #[wasm_bindgen(method)] + pub fn bind_parameter_name(this: &SQLite, stmt: &JsValue, idx: i32) -> String; + + #[wasm_bindgen(method, catch)] + pub fn bind_text(this: &SQLite, stmt: &JsValue, idx: i32, value: &str) -> Result; + + #[wasm_bindgen(method, catch)] + pub async fn reset(this: &SQLite, stmt: &JsValue) -> Result; + + #[wasm_bindgen(method)] + pub fn value(this: &SQLite, pValue: &JsValue) -> SQLiteCompatibleType; + + #[wasm_bindgen(method)] + pub fn value_dup(this: &SQLite, pValue: &JsValue) -> SQLiteCompatibleType; + + #[wasm_bindgen(method)] + pub fn value_blob(this: &SQLite, pValue: &JsValue) -> Vec; + + #[wasm_bindgen(method)] + pub fn value_bytes(this: &SQLite, pValue: &JsValue) -> i32; + + #[wasm_bindgen(method)] + pub fn value_double(this: &SQLite, pValue: &JsValue) -> f64; + + #[wasm_bindgen(method)] + pub fn value_int(this: &SQLite, pValue: &JsValue) -> i32; + + #[wasm_bindgen(method)] + pub fn value_int64(this: &SQLite, pValue: &JsValue) -> i64; + + // TODO: If wasm-bindgen allows returning references, could return &str + #[wasm_bindgen(method)] + pub fn value_text(this: &SQLite, pValue: &JsValue) -> String; + + #[wasm_bindgen(method)] + pub fn value_type(this: &SQLite, pValue: &JsValue) -> i32; + + #[wasm_bindgen(method, catch)] + pub async fn open_v2( + this: &SQLite, + database_url: &str, + iflags: Option, + ) -> Result; + + #[wasm_bindgen(method, catch)] + pub async fn exec(this: &SQLite, database: &JsValue, query: &str) -> Result<(), JsValue>; + + #[wasm_bindgen(method, catch)] + pub fn finalize(this: &SQLite, stmt: &JsValue) -> Result<(), JsValue>; + + #[wasm_bindgen(method)] + pub fn changes(this: &SQLite, database: &JsValue) -> usize; + + #[wasm_bindgen(method, catch)] + pub async fn step(this: &SQLite, stmt: &JsValue) -> Result; + + #[wasm_bindgen(method, catch)] + pub fn clear_bindings(this: &SQLite, stmt: &JsValue) -> Result; + + #[wasm_bindgen(method, catch)] + pub async fn close(this: &SQLite, database: &JsValue) -> Result<(), JsValue>; + + #[wasm_bindgen(method)] + pub fn column(this: &SQLite, stmt: &JsValue, idx: i32) -> SQLiteCompatibleType; + + #[wasm_bindgen(method, catch)] + pub async fn prepare( + this: &SQLite, + database: &JsValue, + sql: &str, + options: JsValue, + ) -> Result; + + #[wasm_bindgen(method)] + pub fn column_name(this: &SQLite, stmt: &JsValue, idx: i32) -> String; + + #[wasm_bindgen(method)] + pub fn column_count(this: &SQLite, stmt: &JsValue) -> i32; + + #[wasm_bindgen(method, catch)] + pub fn batch_execute(this: &SQLite, database: &JsValue, query: &str) -> Result<(), JsValue>; + + #[wasm_bindgen(method, catch)] + pub fn create_function( + this: &SQLite, + database: &JsValue, + functionName: &str, + n_arg: i32, + textRep: i32, + pApp: i32, //ignored + x_func: Option<&Closure) -> JsValue>>, + x_step: Option<&Closure) -> JsValue>>, + x_final: Option<&Closure>, + ) -> Result<(), JsValue>; + + #[wasm_bindgen(method, catch)] + pub fn register_diesel_sql_functions(this: &SQLite, database: &JsValue) -> Result<(), JsValue>; +} + +impl std::fmt::Debug for SQLite { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "SQLite WASM bridge") + } +} diff --git a/diesel-wasm-sqlite/src/lib.rs b/diesel-wasm-sqlite/src/lib.rs new file mode 100755 index 000000000..ffd6ad0f1 --- /dev/null +++ b/diesel-wasm-sqlite/src/lib.rs @@ -0,0 +1,59 @@ +//! Module for an SQLite backend accesible from the web. +pub mod backend; +pub mod connection; +pub mod ffi; +pub mod query_builder; +pub mod sqlite_types; +pub mod utils; + +#[cfg(not(target_arch = "wasm32"))] +compile_error!("This crate only suports the `wasm32-unknown-unknown` target"); + +use self::ffi::SQLite; +use tokio::sync::OnceCell; +use wasm_bindgen::JsValue; + +pub use backend::{SqliteType, WasmSqlite}; + +/// The SQLite Library +/// this global constant references the loaded SQLite WASM. +static SQLITE: OnceCell = OnceCell::const_new(); + +pub type SQLiteWasm = &'static JsValue; + +pub(crate) async fn get_sqlite() -> &'static SQLite { + SQLITE + .get_or_init(|| async { + let module = SQLite::wasm_module().await; + SQLite::new(module) + }) + .await +} + +pub(crate) fn get_sqlite_unchecked() -> &'static SQLite { + SQLITE.get().expect("SQLite is not initialized") +} + +#[derive(Debug)] +pub struct WasmSqliteError(JsValue); + +impl From for diesel::result::Error { + fn from(value: WasmSqliteError) -> diesel::result::Error { + log::error!("NOT IMPLEMENTED, {:?}", value); + diesel::result::Error::NotFound + } +} + +impl From for diesel::result::ConnectionError { + fn from(value: WasmSqliteError) -> diesel::result::ConnectionError { + log::error!("NOT IMPLEMENTED, {:?}", value); + web_sys::console::log_1(&value.0); + diesel::result::ConnectionError::BadConnection("Not implemented".to_string()) + } +} + +impl From for WasmSqliteError { + fn from(err: JsValue) -> WasmSqliteError { + WasmSqliteError(err) + } +} diff --git a/diesel-wasm-sqlite/src/query_builder/limit_offset.rs b/diesel-wasm-sqlite/src/query_builder/limit_offset.rs new file mode 100644 index 000000000..65740b9fd --- /dev/null +++ b/diesel-wasm-sqlite/src/query_builder/limit_offset.rs @@ -0,0 +1,127 @@ +use crate::WasmSqlite; +use diesel::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use diesel::query_builder::{BoxedLimitOffsetClause, LimitOffsetClause}; +use diesel::query_builder::{LimitClause, NoLimitClause}; +use diesel::query_builder::{NoOffsetClause, OffsetClause}; +use diesel::result::QueryResult; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause> +where + OffsetClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + // Sqlite requires a limit clause in front of any offset clause + // using `LIMIT -1` is the same as not having any limit clause + // https://sqlite.org/lang_select.html + out.push_sql(" LIMIT -1 "); + self.offset_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, WasmSqlite> { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // See the `QueryFragment` implementation for `LimitOffsetClause` for details. + out.push_sql(" LIMIT -1 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +// Have explicit impls here because we need to set `Some`/`None` for the clauses +// correspondingly, otherwise we cannot match on it in the `QueryFragment` impl +// above +impl<'a> IntoBoxedClause<'a, WasmSqlite> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, WasmSqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, WasmSqlite> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, WasmSqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, O> IntoBoxedClause<'a, WasmSqlite> for LimitOffsetClause> +where + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, WasmSqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: Some(Box::new(self.offset_clause)), + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, WasmSqlite> + for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, WasmSqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/diesel-wasm-sqlite/src/query_builder/mod.rs b/diesel-wasm-sqlite/src/query_builder/mod.rs new file mode 100644 index 000000000..8bf48119b --- /dev/null +++ b/diesel-wasm-sqlite/src/query_builder/mod.rs @@ -0,0 +1,44 @@ +//! The SQLite query builder + +use super::backend::WasmSqlite; +use diesel::query_builder::QueryBuilder; +use diesel::result::QueryResult; + +mod limit_offset; +mod query_fragment_impls; +mod returning; + +/// Constructs SQL queries for use with the SQLite backend +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct SqliteQueryBuilder { + sql: String, +} + +impl SqliteQueryBuilder { + /// Construct a new query builder with an empty query + pub fn new() -> Self { + SqliteQueryBuilder::default() + } +} + +impl QueryBuilder for SqliteQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace('`', "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/diesel-wasm-sqlite/src/query_builder/query_fragment_impls.rs b/diesel-wasm-sqlite/src/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..4d840529e --- /dev/null +++ b/diesel-wasm-sqlite/src/query_builder/query_fragment_impls.rs @@ -0,0 +1,42 @@ +/* +// use diesel::query_builder::into_conflict_clause::OnConflictSelectWrapper; +// use diesel::query_builder::where_clause::BoxedWhereClause; +use diesel::query_builder::AstPass; +use diesel::query_builder::BoxedSelectStatement; +use diesel::query_builder::QueryFragment; +use diesel::query_builder::SelectStatement; +// use diesel::query_builder::WhereClause; +use crate::storage::wasm_sqlite::WasmSqlite; +use diesel::result::QueryResult; + +// The corresponding impl for`NoWhereClause` is missing because of +// https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) +impl QueryFragment + for OnConflictSelectWrapper, O, LOf, G, H, LC>> +where + SelectStatement, O, LOf, G, H, LC>: QueryFragment, +{ + fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +// #[cfg(feature = "sqlite")] +impl<'a, ST, QS, GB> QueryFragment + for OnConflictSelectWrapper> +where + BoxedSelectStatement<'a, ST, QS, WasmSqlite, GB>: QueryFragment, + QS: QueryFragment, +{ + fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + // https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) + self.0.build_query(pass, |where_clause, mut pass| { + match where_clause { + BoxedWhereClause::None => pass.push_sql(" WHERE 1=1 "), + w => w.walk_ast(pass.reborrow())?, + } + Ok(()) + }) + } +} +*/ diff --git a/diesel-wasm-sqlite/src/query_builder/returning.rs b/diesel-wasm-sqlite/src/query_builder/returning.rs new file mode 100644 index 000000000..055cab509 --- /dev/null +++ b/diesel-wasm-sqlite/src/query_builder/returning.rs @@ -0,0 +1,16 @@ +use crate::backend::{SqliteReturningClause, WasmSqlite}; +use diesel::query_builder::ReturningClause; +use diesel::query_builder::{AstPass, QueryFragment}; +use diesel::result::QueryResult; +/* +impl QueryFragment for ReturningClause +where + Expr: QueryFragment, +{ + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, WasmSqlite>) -> QueryResult<()> { + out.skip_from(true); + out.push_sql(" RETURNING "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +}*/ diff --git a/diesel-wasm-sqlite/src/sqlite_types.rs b/diesel-wasm-sqlite/src/sqlite_types.rs new file mode 100644 index 000000000..1d2066976 --- /dev/null +++ b/diesel-wasm-sqlite/src/sqlite_types.rs @@ -0,0 +1,105 @@ +use super::backend::{SqliteType, WasmSqlite}; +use bitflags::bitflags; +use diesel::sql_types::*; +use serde::{Deserialize, Serialize}; + +//TODO These Database Types are defined in the wasm file and should be imported. +// this is easier for now because of quirks with converting from JsValue to integer within extern +// "C" declaration. + +// result codes +pub const SQLITE_DONE: i32 = 101; +pub const SQLITE_ROW: i32 = 100; + +// Fundamental datatypes. +// https://www.sqlite.org/c3ref/c_blob.html +pub const SQLITE_INTEGER: i32 = 1; +pub const SQLITE_FLOAT: i32 = 2; +pub const SQLITE_TEXT: i32 = 3; +pub const SQLITE_BLOB: i32 = 4; +pub const SQLITE_NULL: i32 = 5; + +/// `SqlitePrepareOptions` imported type +#[derive(Serialize, Deserialize, Default, Clone, Debug, Copy)] +pub struct PrepareOptions { + pub flags: Option, + pub unscoped: Option, +} + +macro_rules! impl_has_sql_type { + ($type:ty, $sql_type:expr) => { + impl HasSqlType<$type> for WasmSqlite { + fn metadata(_: &mut ()) -> SqliteType { + $sql_type + } + } + }; +} + +impl_has_sql_type!(Bool, SqliteType::Integer); +impl_has_sql_type!(SmallInt, SqliteType::SmallInt); +impl_has_sql_type!(Integer, SqliteType::Integer); +impl_has_sql_type!(BigInt, SqliteType::Long); +impl_has_sql_type!(Float, SqliteType::Float); +impl_has_sql_type!(Double, SqliteType::Double); +impl_has_sql_type!(Numeric, SqliteType::Double); +impl_has_sql_type!(Text, SqliteType::Text); +impl_has_sql_type!(Binary, SqliteType::Binary); +impl_has_sql_type!(Date, SqliteType::Text); +impl_has_sql_type!(Time, SqliteType::Text); +impl_has_sql_type!(Timestamp, SqliteType::Text); + +bitflags! { + pub struct SqliteOpenFlags: u32 { + const SQLITE_OPEN_READONLY = 0x00000001; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_READWRITE = 0x00000002; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_CREATE = 0x00000004; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_DELETEONCLOSE = 0x00000008; /* VFS only */ + const SQLITE_OPEN_EXCLUSIVE = 0x00000010; /* VFS only */ + const SQLITE_OPEN_AUTOPROXY = 0x00000020; /* VFS only */ + const SQLITE_OPEN_URI = 0x00000040; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_MEMORY = 0x00000080; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_MAIN_DB = 0x00000100; /* VFS only */ + const SQLITE_OPEN_TEMP_DB = 0x00000200; /* VFS only */ + const SQLITE_OPEN_TRANSIENT_DB = 0x00000400; /* VFS only */ + const SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; /* VFS only */ + const SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; /* VFS only */ + const SQLITE_OPEN_SUBJOURNAL = 0x00002000; /* VFS only */ + const SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; /* VFS only */ + const SQLITE_OPEN_NOMUTEX = 0x00008000; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_FULLMUTEX = 0x00010000; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_SHAREDCACHE = 0x00020000; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_PRIVATECACHE = 0x00040000; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_WAL = 0x00080000; /* VFS only */ + const SQLITE_OPEN_NOFOLLOW = 0x01000000; /* Ok for sqlite3_open_v2() */ + const SQLITE_OPEN_EXRESCODE = 0x02000000; /* Extended result codes */ + } +} + +// SQLite Text Encodings https://www.sqlite.org/capi3ref.html#SQLITE_ANY +bitflags! { + pub struct SqliteFlags: u32 { + const SQLITE_UTF8 = 1; /* IMP: R-37514-35566 */ + const SQLITE_UTF16LE = 2; /* IMP: R-03371-37637 */ + const SQLITE_UTF16BE = 3; /* IMP: R-51971-34154 */ + const SQLITE_UTF16 = 4; /* Use native byte order */ + const SQLITE_ANY = 5; /* Deprecated */ + const SQLITE_UTF16_ALIGNED = 8; /* sqlite3_create_collation only */ + + /// SQLite Function Flags https://www.sqlite.org/capi3ref.html#sqlitedeterministic + const SQLITE_DETERMINISTIC = 0x000000800; + const SQLITE_DIRECTONLY = 0x000080000; + const SQLITE_SUBTYPE = 0x000100000; + const SQLITE_INNOCUOUS = 0x000200000; + const SQLITE_RESULT_SUBTYPE = 0x001000000; + } +} + +// SQLite Prepare Flags https://www.sqlite.org/c3ref/c_prepare_normalize.html#sqlitepreparepersistent +bitflags! { + pub struct SqlitePrepareFlags: i32 { + const SQLITE_PREPARE_PERSISTENT = 0x01; + const SQLITE_PREPARE_NORMALIZE = 0x02; + const SQLITE_PREPARE_NO_VTAB = 0x04; + } +} diff --git a/diesel-wasm-sqlite/src/utils.rs b/diesel-wasm-sqlite/src/utils.rs new file mode 100644 index 000000000..b1d7929dc --- /dev/null +++ b/diesel-wasm-sqlite/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/diesel-wasm-sqlite/src/wa-sqlite-diesel-bundle.js b/diesel-wasm-sqlite/src/wa-sqlite-diesel-bundle.js new file mode 100644 index 000000000..2e16221b9 --- /dev/null +++ b/diesel-wasm-sqlite/src/wa-sqlite-diesel-bundle.js @@ -0,0 +1,3050 @@ +// Primary result codes. +// https://www.sqlite.org/rescode.html +const SQLITE_OK = 0; +const SQLITE_ERROR = 1; +const SQLITE_INTERNAL = 2; +const SQLITE_PERM = 3; +const SQLITE_ABORT = 4; +const SQLITE_BUSY = 5; +const SQLITE_LOCKED = 6; +const SQLITE_NOMEM = 7; +const SQLITE_READONLY = 8; +const SQLITE_INTERRUPT = 9; +const SQLITE_IOERR = 10; +const SQLITE_CORRUPT = 11; +const SQLITE_NOTFOUND = 12; +const SQLITE_FULL = 13; +const SQLITE_CANTOPEN = 14; +const SQLITE_PROTOCOL = 15; +const SQLITE_EMPTY = 16; +const SQLITE_SCHEMA = 17; +const SQLITE_TOOBIG = 18; +const SQLITE_CONSTRAINT = 19; +const SQLITE_MISMATCH = 20; +const SQLITE_MISUSE = 21; +const SQLITE_NOLFS = 22; +const SQLITE_AUTH = 23; +const SQLITE_FORMAT = 24; +const SQLITE_RANGE = 25; +const SQLITE_NOTADB = 26; +const SQLITE_NOTICE = 27; +const SQLITE_WARNING = 28; +const SQLITE_ROW = 100; +const SQLITE_DONE = 101; + +// Extended error codes. +const SQLITE_IOERR_ACCESS = 3338; +const SQLITE_IOERR_CHECKRESERVEDLOCK = 3594; +const SQLITE_IOERR_CLOSE = 4106; +const SQLITE_IOERR_DATA = 8202; +const SQLITE_IOERR_DELETE = 2570; +const SQLITE_IOERR_DELETE_NOENT = 5898; +const SQLITE_IOERR_DIR_FSYNC = 1290; +const SQLITE_IOERR_FSTAT = 1802; +const SQLITE_IOERR_FSYNC = 1034; +const SQLITE_IOERR_GETTEMPPATH = 6410; +const SQLITE_IOERR_LOCK = 3850; +const SQLITE_IOERR_NOMEM = 3082; +const SQLITE_IOERR_READ = 266; +const SQLITE_IOERR_RDLOCK = 2314; +const SQLITE_IOERR_SEEK = 5642; +const SQLITE_IOERR_SHORT_READ = 522; +const SQLITE_IOERR_TRUNCATE = 1546; +const SQLITE_IOERR_UNLOCK = 2058; +const SQLITE_IOERR_VNODE = 6922; +const SQLITE_IOERR_WRITE = 778; +const SQLITE_IOERR_BEGIN_ATOMIC = 7434; +const SQLITE_IOERR_COMMIT_ATOMIC = 7690; +const SQLITE_IOERR_ROLLBACK_ATOMIC = 7946; + +// Other extended result codes. +const SQLITE_CONSTRAINT_CHECK = 275; +const SQLITE_CONSTRAINT_COMMITHOOK = 531; +const SQLITE_CONSTRAINT_FOREIGNKEY = 787; +const SQLITE_CONSTRAINT_FUNCTION = 1043; +const SQLITE_CONSTRAINT_NOTNULL = 1299; +const SQLITE_CONSTRAINT_PINNED = 2835; +const SQLITE_CONSTRAINT_PRIMARYKEY = 1555; +const SQLITE_CONSTRAINT_ROWID = 2579; +const SQLITE_CONSTRAINT_TRIGGER = 1811; +const SQLITE_CONSTRAINT_UNIQUE = 2067; +const SQLITE_CONSTRAINT_VTAB = 2323; + +// Open flags. +// https://www.sqlite.org/c3ref/c_open_autoproxy.html +const SQLITE_OPEN_READONLY = 0x00000001; +const SQLITE_OPEN_READWRITE = 0x00000002; +const SQLITE_OPEN_CREATE = 0x00000004; +const SQLITE_OPEN_DELETEONCLOSE = 0x00000008; +const SQLITE_OPEN_EXCLUSIVE = 0x00000010; +const SQLITE_OPEN_AUTOPROXY = 0x00000020; +const SQLITE_OPEN_URI = 0x00000040; +const SQLITE_OPEN_MEMORY = 0x00000080; +const SQLITE_OPEN_MAIN_DB = 0x00000100; +const SQLITE_OPEN_TEMP_DB = 0x00000200; +const SQLITE_OPEN_TRANSIENT_DB = 0x00000400; +const SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; +const SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; +const SQLITE_OPEN_SUBJOURNAL = 0x00002000; +const SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; +const SQLITE_OPEN_NOMUTEX = 0x00008000; +const SQLITE_OPEN_FULLMUTEX = 0x00010000; +const SQLITE_OPEN_SHAREDCACHE = 0x00020000; +const SQLITE_OPEN_PRIVATECACHE = 0x00040000; +const SQLITE_OPEN_WAL = 0x00080000; +const SQLITE_OPEN_NOFOLLOW = 0x01000000; + +// Locking levels. +// https://www.sqlite.org/c3ref/c_lock_exclusive.html +const SQLITE_LOCK_NONE = 0; +const SQLITE_LOCK_SHARED = 1; +const SQLITE_LOCK_RESERVED = 2; +const SQLITE_LOCK_PENDING = 3; +const SQLITE_LOCK_EXCLUSIVE = 4; + +// Device characteristics. +// https://www.sqlite.org/c3ref/c_iocap_atomic.html +const SQLITE_IOCAP_ATOMIC = 0x00000001; +const SQLITE_IOCAP_ATOMIC512 = 0x00000002; +const SQLITE_IOCAP_ATOMIC1K = 0x00000004; +const SQLITE_IOCAP_ATOMIC2K = 0x00000008; +const SQLITE_IOCAP_ATOMIC4K = 0x00000010; +const SQLITE_IOCAP_ATOMIC8K = 0x00000020; +const SQLITE_IOCAP_ATOMIC16K = 0x00000040; +const SQLITE_IOCAP_ATOMIC32K = 0x00000080; +const SQLITE_IOCAP_ATOMIC64K = 0x00000100; +const SQLITE_IOCAP_SAFE_APPEND = 0x00000200; +const SQLITE_IOCAP_SEQUENTIAL = 0x00000400; +const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800; +const SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000; +const SQLITE_IOCAP_IMMUTABLE = 0x00002000; +const SQLITE_IOCAP_BATCH_ATOMIC = 0x00004000; + +// xAccess flags. +// https://www.sqlite.org/c3ref/c_access_exists.html +const SQLITE_ACCESS_EXISTS = 0; +const SQLITE_ACCESS_READWRITE = 1; +const SQLITE_ACCESS_READ = 2; + +// File control opcodes +// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbeginatomicwrite +const SQLITE_FCNTL_LOCKSTATE = 1; +const SQLITE_FCNTL_GET_LOCKPROXYFILE = 2; +const SQLITE_FCNTL_SET_LOCKPROXYFILE = 3; +const SQLITE_FCNTL_LAST_ERRNO = 4; +const SQLITE_FCNTL_SIZE_HINT = 5; +const SQLITE_FCNTL_CHUNK_SIZE = 6; +const SQLITE_FCNTL_FILE_POINTER = 7; +const SQLITE_FCNTL_SYNC_OMITTED = 8; +const SQLITE_FCNTL_WIN32_AV_RETRY = 9; +const SQLITE_FCNTL_PERSIST_WAL = 10; +const SQLITE_FCNTL_OVERWRITE = 11; +const SQLITE_FCNTL_VFSNAME = 12; +const SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13; +const SQLITE_FCNTL_PRAGMA = 14; +const SQLITE_FCNTL_BUSYHANDLER = 15; +const SQLITE_FCNTL_TEMPFILENAME = 16; +const SQLITE_FCNTL_MMAP_SIZE = 18; +const SQLITE_FCNTL_TRACE = 19; +const SQLITE_FCNTL_HAS_MOVED = 20; +const SQLITE_FCNTL_SYNC = 21; +const SQLITE_FCNTL_COMMIT_PHASETWO = 22; +const SQLITE_FCNTL_WIN32_SET_HANDLE = 23; +const SQLITE_FCNTL_WAL_BLOCK = 24; +const SQLITE_FCNTL_ZIPVFS = 25; +const SQLITE_FCNTL_RBU = 26; +const SQLITE_FCNTL_VFS_POINTER = 27; +const SQLITE_FCNTL_JOURNAL_POINTER = 28; +const SQLITE_FCNTL_WIN32_GET_HANDLE = 29; +const SQLITE_FCNTL_PDB = 30; +const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = 31; +const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = 32; +const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = 33; +const SQLITE_FCNTL_LOCK_TIMEOUT = 34; +const SQLITE_FCNTL_DATA_VERSION = 35; +const SQLITE_FCNTL_SIZE_LIMIT = 36; +const SQLITE_FCNTL_CKPT_DONE = 37; +const SQLITE_FCNTL_RESERVE_BYTES = 38; +const SQLITE_FCNTL_CKPT_START = 39; + +// Fundamental datatypes. +// https://www.sqlite.org/c3ref/c_blob.html +const SQLITE_INTEGER = 1; +const SQLITE_FLOAT = 2; +const SQLITE_TEXT = 3; +const SQLITE_BLOB = 4; +const SQLITE_NULL = 5; + +// Special destructor behavior. +// https://www.sqlite.org/c3ref/c_static.html +const SQLITE_STATIC = 0; +const SQLITE_TRANSIENT = -1; + +// Text encodings. +// https://sqlite.org/c3ref/c_any.html +const SQLITE_UTF8 = 1; /* IMP: R-37514-35566 */ +const SQLITE_UTF16LE = 2; /* IMP: R-03371-37637 */ +const SQLITE_UTF16BE = 3; /* IMP: R-51971-34154 */ +const SQLITE_UTF16 = 4; /* Use native byte order */ + +// Module constraint ops. +const SQLITE_INDEX_CONSTRAINT_EQ = 2; +const SQLITE_INDEX_CONSTRAINT_GT = 4; +const SQLITE_INDEX_CONSTRAINT_LE = 8; +const SQLITE_INDEX_CONSTRAINT_LT = 16; +const SQLITE_INDEX_CONSTRAINT_GE = 32; +const SQLITE_INDEX_CONSTRAINT_MATCH = 64; +const SQLITE_INDEX_CONSTRAINT_LIKE = 65; +const SQLITE_INDEX_CONSTRAINT_GLOB = 66; +const SQLITE_INDEX_CONSTRAINT_REGEXP = 67; +const SQLITE_INDEX_CONSTRAINT_NE = 68; +const SQLITE_INDEX_CONSTRAINT_ISNOT = 69; +const SQLITE_INDEX_CONSTRAINT_ISNOTNULL = 70; +const SQLITE_INDEX_CONSTRAINT_ISNULL = 71; +const SQLITE_INDEX_CONSTRAINT_IS = 72; +const SQLITE_INDEX_CONSTRAINT_FUNCTION = 150; +const SQLITE_INDEX_SCAN_UNIQUE = 1; /* Scan visits at most = 1 row */ + +// Function flags +const SQLITE_DETERMINISTIC = 0x000000800; +const SQLITE_DIRECTONLY = 0x000080000; +const SQLITE_SUBTYPE = 0x000100000; +const SQLITE_INNOCUOUS = 0x000200000; + +// Sync flags +const SQLITE_SYNC_NORMAL = 0x00002; +const SQLITE_SYNC_FULL = 0x00003; +const SQLITE_SYNC_DATAONLY = 0x00010; + +// Authorizer action codes +const SQLITE_CREATE_INDEX = 1; +const SQLITE_CREATE_TABLE = 2; +const SQLITE_CREATE_TEMP_INDEX = 3; +const SQLITE_CREATE_TEMP_TABLE = 4; +const SQLITE_CREATE_TEMP_TRIGGER = 5; +const SQLITE_CREATE_TEMP_VIEW = 6; +const SQLITE_CREATE_TRIGGER = 7; +const SQLITE_CREATE_VIEW = 8; +const SQLITE_DELETE = 9; +const SQLITE_DROP_INDEX = 10; +const SQLITE_DROP_TABLE = 11; +const SQLITE_DROP_TEMP_INDEX = 12; +const SQLITE_DROP_TEMP_TABLE = 13; +const SQLITE_DROP_TEMP_TRIGGER = 14; +const SQLITE_DROP_TEMP_VIEW = 15; +const SQLITE_DROP_TRIGGER = 16; +const SQLITE_DROP_VIEW = 17; +const SQLITE_INSERT = 18; +const SQLITE_PRAGMA = 19; +const SQLITE_READ = 20; +const SQLITE_SELECT = 21; +const SQLITE_TRANSACTION = 22; +const SQLITE_UPDATE = 23; +const SQLITE_ATTACH = 24; +const SQLITE_DETACH = 25; +const SQLITE_ALTER_TABLE = 26; +const SQLITE_REINDEX = 27; +const SQLITE_ANALYZE = 28; +const SQLITE_CREATE_VTABLE = 29; +const SQLITE_DROP_VTABLE = 30; +const SQLITE_FUNCTION = 31; +const SQLITE_SAVEPOINT = 32; +const SQLITE_COPY = 0; +const SQLITE_RECURSIVE = 33; + +// Authorizer return codes +const SQLITE_DENY = 1; +const SQLITE_IGNORE = 2; + +// Limit categories +const SQLITE_LIMIT_LENGTH = 0; +const SQLITE_LIMIT_SQL_LENGTH = 1; +const SQLITE_LIMIT_COLUMN = 2; +const SQLITE_LIMIT_EXPR_DEPTH = 3; +const SQLITE_LIMIT_COMPOUND_SELECT = 4; +const SQLITE_LIMIT_VDBE_OP = 5; +const SQLITE_LIMIT_FUNCTION_ARG = 6; +const SQLITE_LIMIT_ATTACHED = 7; +const SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8; +const SQLITE_LIMIT_VARIABLE_NUMBER = 9; +const SQLITE_LIMIT_TRIGGER_DEPTH = 10; +const SQLITE_LIMIT_WORKER_THREADS = 11; + +const SQLITE_PREPARE_PERSISTENT = 0x01; +const SQLITE_PREPARE_NORMALIZED = 0x02; +const SQLITE_PREPARE_NO_VTAB = 0x04; + +// Copyright 2021 Roy T. Hashimoto. All Rights Reserved. + + +const MAX_INT64 = 0x7fffffffffffffffn; +const MIN_INT64 = -0x8000000000000000n; + +const AsyncFunction$1 = Object.getPrototypeOf(async function(){}).constructor; + +class SQLiteError extends Error { + constructor(message, code) { + super(message); + this.code = code; + } +} + +const async = true; + +/** + * Builds a Javascript API from the Emscripten module. This API is still + * low-level and closely corresponds to the C API exported by the module, + * but differs in some specifics like throwing exceptions on errors. + * @param {*} Module SQLite Emscripten module + * @returns {SQLiteAPI} + */ +function Factory(Module) { + /** @type {SQLiteAPI} */ const sqlite3 = {}; + + Module.retryOps = []; + const sqliteFreeAddress = Module._getSqliteFree(); + + // Allocate some space for 32-bit returned values. + const tmp = Module._malloc(8); + const tmpPtr = [tmp, tmp + 4]; + + // Convert a JS string to a C string. sqlite3_malloc is used to allocate + // memory (use sqlite3_free to deallocate). + function createUTF8(s) { + if (typeof s !== 'string') return 0; + const utf8 = new TextEncoder().encode(s); + const zts = Module._sqlite3_malloc(utf8.byteLength + 1); + Module.HEAPU8.set(utf8, zts); + Module.HEAPU8[zts + utf8.byteLength] = 0; + return zts; + } + + /** + * Concatenate 32-bit numbers into a 64-bit (signed) BigInt. + * @param {number} lo32 + * @param {number} hi32 + * @returns {bigint} + */ + function cvt32x2ToBigInt(lo32, hi32) { + return (BigInt(hi32) << 32n) | (BigInt(lo32) & 0xffffffffn); + } + + /** + * Concatenate 32-bit numbers and return as number or BigInt, depending + * on the value. + * @param {number} lo32 + * @param {number} hi32 + * @returns {number|bigint} + */ + const cvt32x2AsSafe = (function() { + const hiMax = BigInt(Number.MAX_SAFE_INTEGER) >> 32n; + const hiMin = BigInt(Number.MIN_SAFE_INTEGER) >> 32n; + + return function(lo32, hi32) { + if (hi32 > hiMax || hi32 < hiMin) { + // Can't be expressed as a Number so use BigInt. + return cvt32x2ToBigInt(lo32, hi32); + } else { + // Combine the upper and lower 32-bit numbers. The complication is + // that lo32 is a signed integer which makes manipulating its bits + // a little tricky - the sign bit gets handled separately. + return (hi32 * 0x100000000) + (lo32 & 0x7fffffff) - (lo32 & 0x80000000); + } + } + })(); + + const databases = new Set(); + function verifyDatabase(db) { + if (!databases.has(db)) { + throw new SQLiteError('not a database', SQLITE_MISUSE); + } + } + + const mapStmtToDB = new Map(); + function verifyStatement(stmt) { + if (!mapStmtToDB.has(stmt)) { + throw new SQLiteError('not a statement', SQLITE_MISUSE); + } + } + + sqlite3.bind_collection = function(stmt, bindings) { + verifyStatement(stmt); + const isArray = Array.isArray(bindings); + const nBindings = sqlite3.bind_parameter_count(stmt); + for (let i = 1; i <= nBindings; ++i) { + const key = isArray ? i - 1 : sqlite3.bind_parameter_name(stmt, i); + const value = bindings[key]; + if (value !== undefined) { + sqlite3.bind(stmt, i, value); + } + } + return SQLITE_OK; + }; + + sqlite3.bind = function(stmt, i, value) { + verifyStatement(stmt); + switch (typeof value) { + case 'number': + if (value === (value | 0)) { + return sqlite3.bind_int(stmt, i, value); + } else { + return sqlite3.bind_double(stmt, i, value); + } + case 'string': + return sqlite3.bind_text(stmt, i, value); + default: + if (value instanceof Uint8Array || Array.isArray(value)) { + return sqlite3.bind_blob(stmt, i, value); + } else if (value === null) { + return sqlite3.bind_null(stmt, i); + } else if (typeof value === 'bigint') { + return sqlite3.bind_int64(stmt, i, value); + } else if (value === undefined) { + // Existing binding (or NULL) will be used. + return SQLITE_NOTICE; + } else { + console.warn('unknown binding converted to null', value); + return sqlite3.bind_null(stmt, i); + } + } + }; + + sqlite3.bind_blob = (function() { + const fname = 'sqlite3_bind_blob'; + const f = Module.cwrap(fname, ...decl('nnnnn:n')); + return function(stmt, i, value) { + verifyStatement(stmt); + // @ts-ignore + const byteLength = value.byteLength ?? value.length; + const ptr = Module._sqlite3_malloc(byteLength); + Module.HEAPU8.subarray(ptr).set(value); + const result = f(stmt, i, ptr, byteLength, sqliteFreeAddress); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_parameter_count = (function() { + const fname = 'sqlite3_bind_parameter_count'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.bind_double = (function() { + const fname = 'sqlite3_bind_double'; + const f = Module.cwrap(fname, ...decl('nnn:n')); + return function(stmt, i, value) { + verifyStatement(stmt); + const result = f(stmt, i, value); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_int = (function() { + const fname = 'sqlite3_bind_int'; + const f = Module.cwrap(fname, ...decl('nnn:n')); + return function(stmt, i, value) { + verifyStatement(stmt); + if (value > 0x7fffffff || value < -0x80000000) return SQLITE_RANGE; + + const result = f(stmt, i, value); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_int64 = (function() { + const fname = 'sqlite3_bind_int64'; + const f = Module.cwrap(fname, ...decl('nnnn:n')); + return function(stmt, i, value) { + verifyStatement(stmt); + if (value > MAX_INT64 || value < MIN_INT64) return SQLITE_RANGE; + + const lo32 = value & 0xffffffffn; + const hi32 = value >> 32n; + const result = f(stmt, i, Number(lo32), Number(hi32)); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_null = (function() { + const fname = 'sqlite3_bind_null'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, i) { + verifyStatement(stmt); + const result = f(stmt, i); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_parameter_name = (function() { + const fname = 'sqlite3_bind_parameter_name'; + const f = Module.cwrap(fname, ...decl('n:s')); + return function(stmt, i) { + verifyStatement(stmt); + const result = f(stmt, i); + return result; + }; + })(); + + sqlite3.bind_text = (function() { + const fname = 'sqlite3_bind_text'; + const f = Module.cwrap(fname, ...decl('nnnnn:n')); + return function(stmt, i, value) { + verifyStatement(stmt); + const ptr = createUTF8(value); + const result = f(stmt, i, ptr, -1, sqliteFreeAddress); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.changes = (function() { + const fname = 'sqlite3_changes'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(db) { + verifyDatabase(db); + const result = f(db); + return result; + }; + })(); + + sqlite3.close = (function() { + const fname = 'sqlite3_close'; + const f = Module.cwrap(fname, ...decl('n:n'), { async }); + return async function(db) { + verifyDatabase(db); + const result = await f(db); + databases.delete(db); + return check(fname, result, db); + }; + })(); + + sqlite3.column = function(stmt, iCol) { + verifyStatement(stmt); + const type = sqlite3.column_type(stmt, iCol); + switch (type) { + case SQLITE_BLOB: + return sqlite3.column_blob(stmt, iCol); + case SQLITE_FLOAT: + return sqlite3.column_double(stmt, iCol); + case SQLITE_INTEGER: + const lo32 = sqlite3.column_int(stmt, iCol); + const hi32 = Module.getTempRet0(); + return cvt32x2AsSafe(lo32, hi32); + case SQLITE_NULL: + return null; + case SQLITE_TEXT: + return sqlite3.column_text(stmt, iCol); + default: + throw new SQLiteError('unknown type', type); + } + }; + + sqlite3.column_blob = (function() { + const fname = 'sqlite3_column_blob'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const nBytes = sqlite3.column_bytes(stmt, iCol); + const address = f(stmt, iCol); + const result = Module.HEAPU8.subarray(address, address + nBytes); + return result; + }; + })(); + + sqlite3.column_bytes = (function() { + const fname = 'sqlite3_column_bytes'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_count = (function() { + const fname = 'sqlite3_column_count'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.column_double = (function() { + const fname = 'sqlite3_column_double'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_int = (function() { + // Retrieve int64 but use only the lower 32 bits. The upper 32-bits are + // accessible with Module.getTempRet0(). + const fname = 'sqlite3_column_int64'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_int64 = (function() { + const fname = 'sqlite3_column_int64'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const lo32 = f(stmt, iCol); + const hi32 = Module.getTempRet0(); + const result = cvt32x2ToBigInt(lo32, hi32); + return result; + }; + })(); + + sqlite3.column_name = (function() { + const fname = 'sqlite3_column_name'; + const f = Module.cwrap(fname, ...decl('nn:s')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_names = function(stmt) { + const columns = []; + const nColumns = sqlite3.column_count(stmt); + for (let i = 0; i < nColumns; ++i) { + columns.push(sqlite3.column_name(stmt, i)); + } + return columns; + }; + + sqlite3.column_text = (function() { + const fname = 'sqlite3_column_text'; + const f = Module.cwrap(fname, ...decl('nn:s')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_type = (function() { + const fname = 'sqlite3_column_type'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.create_function = function(db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { + verifyDatabase(db); + + // Convert SQLite callback arguments to JavaScript-friendly arguments. + function adapt(f) { + return f instanceof AsyncFunction$1 ? + (async (ctx, n, values) => f(ctx, Module.HEAP32.subarray(values / 4, values / 4 + n))) : + ((ctx, n, values) => f(ctx, Module.HEAP32.subarray(values / 4, values / 4 + n))); + } + + const result = Module.create_function( + db, + zFunctionName, + nArg, + eTextRep, + pApp, + xFunc && adapt(xFunc), + xStep && adapt(xStep), + xFinal); + return check('sqlite3_create_function', result, db); + }; + + sqlite3.data_count = (function() { + const fname = 'sqlite3_data_count'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.exec = async function(db, sql, callback) { + for await (const stmt of sqlite3.statements(db, sql)) { + let columns; + while (await sqlite3.step(stmt) === SQLITE_ROW) { + if (callback) { + columns = columns ?? sqlite3.column_names(stmt); + const row = sqlite3.row(stmt); + await callback(row, columns); + } + } + } + return SQLITE_OK; + }; + + sqlite3.finalize = (function() { + const fname = 'sqlite3_finalize'; + const f = Module.cwrap(fname, ...decl('n:n'), { async }); + return async function(stmt) { + const result = await f(stmt); + mapStmtToDB.delete(stmt); + + // Don't throw on error here. Typically the error has already been + // thrown and finalize() is part of the cleanup. + return result; + }; + })(); + + sqlite3.get_autocommit = (function() { + const fname = 'sqlite3_get_autocommit'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(db) { + const result = f(db); + return result; + }; + })(); + + sqlite3.libversion = (function() { + const fname = 'sqlite3_libversion'; + const f = Module.cwrap(fname, ...decl(':s')); + return function() { + const result = f(); + return result; + }; + })(); + + sqlite3.libversion_number = (function() { + const fname = 'sqlite3_libversion_number'; + const f = Module.cwrap(fname, ...decl(':n')); + return function() { + const result = f(); + return result; + }; + })(); + + sqlite3.limit = (function() { + const fname = 'sqlite3_limit'; + const f = Module.cwrap(fname, ...decl('nnn:n')); + return function(db, id, newVal) { + const result = f(db, id, newVal); + return result; + }; + })(); + + sqlite3.open_v2 = (function() { + const fname = 'sqlite3_open_v2'; + const f = Module.cwrap(fname, ...decl('snnn:n'), { async }); + return async function(zFilename, flags, zVfs) { + flags = flags || SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; + zVfs = createUTF8(zVfs); + try { + // Allow retry operations. + const rc = await retry(() => f(zFilename, tmpPtr[0], flags, zVfs)); + + const db = Module.getValue(tmpPtr[0], '*'); + databases.add(db); + + Module.ccall('RegisterExtensionFunctions', 'void', ['number'], [db]); + check(fname, rc); + return db; + } finally { + Module._sqlite3_free(zVfs); + } + }; + })(); + + sqlite3.progress_handler = function(db, nProgressOps, handler, userData) { + verifyDatabase(db); + Module.progress_handler(db, nProgressOps, handler, userData); + }; + sqlite3.reset = (function() { + const fname = 'sqlite3_reset'; + const f = Module.cwrap(fname, ...decl('n:n'), { async }); + return async function(stmt) { + verifyStatement(stmt); + const result = await f(stmt); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.result = function(context, value) { + switch (typeof value) { + case 'number': + if (value === (value | 0)) { + sqlite3.result_int(context, value); + } else { + sqlite3.result_double(context, value); + } + break; + case 'string': + sqlite3.result_text(context, value); + break; + default: + if (value instanceof Uint8Array || Array.isArray(value)) { + sqlite3.result_blob(context, value); + } else if (value === null) { + sqlite3.result_null(context); + } else if (typeof value === 'bigint') { + return sqlite3.result_int64(context, value); + } else { + console.warn('unknown result converted to null', value); + sqlite3.result_null(context); + } + break; + } + + }; + + sqlite3.result_blob = (function() { + const fname = 'sqlite3_result_blob'; + const f = Module.cwrap(fname, ...decl('nnnn:n')); + return function(context, value) { + // @ts-ignore + const byteLength = value.byteLength ?? value.length; + const ptr = Module._sqlite3_malloc(byteLength); + Module.HEAPU8.subarray(ptr).set(value); + f(context, ptr, byteLength, sqliteFreeAddress); // void return + }; + })(); + + sqlite3.result_double = (function() { + const fname = 'sqlite3_result_double'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(context, value) { + f(context, value); // void return + }; + })(); + + sqlite3.result_int = (function() { + const fname = 'sqlite3_result_int'; + const f = Module.cwrap(fname, ...decl('nn:n')); + return function(context, value) { + f(context, value); // void return + }; + })(); + + sqlite3.result_int64 = (function() { + const fname = 'sqlite3_result_int64'; + const f = Module.cwrap(fname, ...decl('nnn:n')); + return function(context, value) { + if (value > MAX_INT64 || value < MIN_INT64) return SQLITE_RANGE; + + const lo32 = value & 0xffffffffn; + const hi32 = value >> 32n; + f(context, Number(lo32), Number(hi32)); // void return + }; + })(); + + sqlite3.result_null = (function() { + const fname = 'sqlite3_result_null'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(context) { + f(context); // void return + }; + })(); + + sqlite3.result_text = (function() { + const fname = 'sqlite3_result_text'; + const f = Module.cwrap(fname, ...decl('nnnn:n')); + return function(context, value) { + const ptr = createUTF8(value); + f(context, ptr, -1, sqliteFreeAddress); // void return + }; + })(); + + sqlite3.row = function(stmt) { + const row = []; + const nColumns = sqlite3.data_count(stmt); + for (let i = 0; i < nColumns; ++i) { + const value = sqlite3.column(stmt, i); + + // Copy blob if aliasing volatile WebAssembly memory. This avoids an + // unnecessary copy if users monkey patch column_blob to copy. + // @ts-ignore + row.push(value?.buffer === Module.HEAPU8.buffer ? value.slice() : value); + } + return row; + }; + + sqlite3.set_authorizer = function(db, xAuth, pApp) { + verifyDatabase(db); + + // Convert SQLite callback arguments to JavaScript-friendly arguments. + function cvtArgs(_, iAction, p3, p4, p5, p6) { + return [ + _, + iAction, + Module.UTF8ToString(p3), + Module.UTF8ToString(p4), + Module.UTF8ToString(p5), + Module.UTF8ToString(p6) + ]; + } function adapt(f) { + return f instanceof AsyncFunction$1 ? + (async (_, iAction, p3, p4, p5, p6) => f(cvtArgs(_, iAction, p3, p4, p5, p6))) : + ((_, iAction, p3, p4, p5, p6) => f(cvtArgs(_, iAction, p3, p4, p5, p6))); + } + + const result = Module.set_authorizer(db, adapt(xAuth), pApp); + return check('sqlite3_set_authorizer', result, db); + }; + sqlite3.sql = (function() { + const fname = 'sqlite3_sql'; + const f = Module.cwrap(fname, ...decl('n:s')); + return function(stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.statements = function(db, sql, options = {}) { + const prepare = Module.cwrap( + 'sqlite3_prepare_v3', + 'number', + ['number', 'number', 'number', 'number', 'number', 'number'], + { async: true }); + + return (async function*() { + const onFinally = []; + try { + // Encode SQL string to UTF-8. + const utf8 = new TextEncoder().encode(sql); + + // Copy encoded string to WebAssembly memory. The SQLite docs say + // zero-termination is a minor optimization so add room for that. + // Also add space for the statement handle and SQL tail pointer. + const allocSize = utf8.byteLength - (utf8.byteLength % 4) + 12; + const pzHead = Module._sqlite3_malloc(allocSize); + const pzEnd = pzHead + utf8.byteLength + 1; + onFinally.push(() => Module._sqlite3_free(pzHead)); + Module.HEAPU8.set(utf8, pzHead); + Module.HEAPU8[pzEnd - 1] = 0; + + // Use extra space for the statement handle and SQL tail pointer. + const pStmt = pzHead + allocSize - 8; + const pzTail = pzHead + allocSize - 4; + + // Ensure that statement handles are not leaked. + let stmt; + function maybeFinalize() { + if (stmt && !options.unscoped) { + sqlite3.finalize(stmt); + } + stmt = 0; + } + onFinally.push(maybeFinalize); + + // Loop over statements. + Module.setValue(pzTail, pzHead, '*'); + do { + // Reclaim resources for the previous iteration. + maybeFinalize(); + + // Call sqlite3_prepare_v3() for the next statement. + // Allow retry operations. + const zTail = Module.getValue(pzTail, '*'); + const rc = await retry(() => { + return prepare( + db, + zTail, + pzEnd - pzTail, + options.flags || 0, + pStmt, + pzTail); + }); + + if (rc !== SQLITE_OK) { + check('sqlite3_prepare_v3', rc, db); + } + + stmt = Module.getValue(pStmt, '*'); + if (stmt) { + mapStmtToDB.set(stmt, db); + yield stmt; + } + } while (stmt); + } finally { + while (onFinally.length) { + onFinally.pop()(); + } + } + })(); + }; + + sqlite3.step = (function() { + const fname = 'sqlite3_step'; + const f = Module.cwrap(fname, ...decl('n:n'), { async }); + return async function(stmt) { + verifyStatement(stmt); + + // Allow retry operations. + const rc = await retry(() => f(stmt)); + + return check(fname, rc, mapStmtToDB.get(stmt), [SQLITE_ROW, SQLITE_DONE]); + }; + })(); + + sqlite3.value = function(pValue) { + const type = sqlite3.value_type(pValue); + switch (type) { + case SQLITE_BLOB: + return sqlite3.value_blob(pValue); + case SQLITE_FLOAT: + return sqlite3.value_double(pValue); + case SQLITE_INTEGER: + const lo32 = sqlite3.value_int(pValue); + const hi32 = Module.getTempRet0(); + return cvt32x2AsSafe(lo32, hi32); + case SQLITE_NULL: + return null; + case SQLITE_TEXT: + return sqlite3.value_text(pValue); + default: + throw new SQLiteError('unknown type', type); + } + }; + + sqlite3.value_blob = (function() { + const fname = 'sqlite3_value_blob'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const nBytes = sqlite3.value_bytes(pValue); + const address = f(pValue); + const result = Module.HEAPU8.subarray(address, address + nBytes); + return result; + }; + })(); + + sqlite3.value_bytes = (function() { + const fname = 'sqlite3_value_bytes'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_double = (function() { + const fname = 'sqlite3_value_double'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_int = (function() { + const fname = 'sqlite3_value_int64'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_int64 = (function() { + const fname = 'sqlite3_value_int64'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const lo32 = f(pValue); + const hi32 = Module.getTempRet0(); + const result = cvt32x2ToBigInt(lo32, hi32); + return result; + }; + })(); + + sqlite3.value_text = (function() { + const fname = 'sqlite3_value_text'; + const f = Module.cwrap(fname, ...decl('n:s')); + return function(pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_type = (function() { + const fname = 'sqlite3_value_type'; + const f = Module.cwrap(fname, ...decl('n:n')); + return function(pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.vfs_register = function(vfs, makeDefault) { + const result = Module.vfs_register(vfs, makeDefault); + return check('sqlite3_vfs_register', result); + }; + + function check(fname, result, db = null, allowed = [SQLITE_OK]) { + if (allowed.includes(result)) return result; + const message = db ? + Module.ccall('sqlite3_errmsg', 'string', ['number'], [db]) : + fname; + throw new SQLiteError(message, result); + } + + // This function is used to automatically retry failed calls that + // have pending retry operations that should allow the retry to + // succeed. + async function retry(f) { + let rc; + do { + // Wait for all pending retry operations to complete. This is + // normally empty on the first loop iteration. + if (Module.retryOps.length) { + await Promise.all(Module.retryOps); + Module.retryOps = []; + } + + rc = await f(); + + // Retry on failure with new pending retry operations. + } while (rc && Module.retryOps.length); + return rc; + } + + return sqlite3; +} + +// Helper function to use a more compact signature specification. +function decl(s) { + const result = []; + const m = s.match(/([ns@]*):([nsv@])/); + switch (m[2]) { + case 'n': result.push('number'); break; + case 's': result.push('string'); break; + case 'v': result.push(null); break; + } + + const args = []; + for (let c of m[1]) { + switch (c) { + case 'n': args.push('number'); break; + case 's': args.push('string'); break; + } + } + result.push(args); + return result; +} + +var WasmSQLiteLibrary = /*#__PURE__*/Object.freeze({ + __proto__: null, + Factory: Factory, + SQLITE_ABORT: SQLITE_ABORT, + SQLITE_ACCESS_EXISTS: SQLITE_ACCESS_EXISTS, + SQLITE_ACCESS_READ: SQLITE_ACCESS_READ, + SQLITE_ACCESS_READWRITE: SQLITE_ACCESS_READWRITE, + SQLITE_ALTER_TABLE: SQLITE_ALTER_TABLE, + SQLITE_ANALYZE: SQLITE_ANALYZE, + SQLITE_ATTACH: SQLITE_ATTACH, + SQLITE_AUTH: SQLITE_AUTH, + SQLITE_BLOB: SQLITE_BLOB, + SQLITE_BUSY: SQLITE_BUSY, + SQLITE_CANTOPEN: SQLITE_CANTOPEN, + SQLITE_CONSTRAINT: SQLITE_CONSTRAINT, + SQLITE_CONSTRAINT_CHECK: SQLITE_CONSTRAINT_CHECK, + SQLITE_CONSTRAINT_COMMITHOOK: SQLITE_CONSTRAINT_COMMITHOOK, + SQLITE_CONSTRAINT_FOREIGNKEY: SQLITE_CONSTRAINT_FOREIGNKEY, + SQLITE_CONSTRAINT_FUNCTION: SQLITE_CONSTRAINT_FUNCTION, + SQLITE_CONSTRAINT_NOTNULL: SQLITE_CONSTRAINT_NOTNULL, + SQLITE_CONSTRAINT_PINNED: SQLITE_CONSTRAINT_PINNED, + SQLITE_CONSTRAINT_PRIMARYKEY: SQLITE_CONSTRAINT_PRIMARYKEY, + SQLITE_CONSTRAINT_ROWID: SQLITE_CONSTRAINT_ROWID, + SQLITE_CONSTRAINT_TRIGGER: SQLITE_CONSTRAINT_TRIGGER, + SQLITE_CONSTRAINT_UNIQUE: SQLITE_CONSTRAINT_UNIQUE, + SQLITE_CONSTRAINT_VTAB: SQLITE_CONSTRAINT_VTAB, + SQLITE_COPY: SQLITE_COPY, + SQLITE_CORRUPT: SQLITE_CORRUPT, + SQLITE_CREATE_INDEX: SQLITE_CREATE_INDEX, + SQLITE_CREATE_TABLE: SQLITE_CREATE_TABLE, + SQLITE_CREATE_TEMP_INDEX: SQLITE_CREATE_TEMP_INDEX, + SQLITE_CREATE_TEMP_TABLE: SQLITE_CREATE_TEMP_TABLE, + SQLITE_CREATE_TEMP_TRIGGER: SQLITE_CREATE_TEMP_TRIGGER, + SQLITE_CREATE_TEMP_VIEW: SQLITE_CREATE_TEMP_VIEW, + SQLITE_CREATE_TRIGGER: SQLITE_CREATE_TRIGGER, + SQLITE_CREATE_VIEW: SQLITE_CREATE_VIEW, + SQLITE_CREATE_VTABLE: SQLITE_CREATE_VTABLE, + SQLITE_DELETE: SQLITE_DELETE, + SQLITE_DENY: SQLITE_DENY, + SQLITE_DETACH: SQLITE_DETACH, + SQLITE_DETERMINISTIC: SQLITE_DETERMINISTIC, + SQLITE_DIRECTONLY: SQLITE_DIRECTONLY, + SQLITE_DONE: SQLITE_DONE, + SQLITE_DROP_INDEX: SQLITE_DROP_INDEX, + SQLITE_DROP_TABLE: SQLITE_DROP_TABLE, + SQLITE_DROP_TEMP_INDEX: SQLITE_DROP_TEMP_INDEX, + SQLITE_DROP_TEMP_TABLE: SQLITE_DROP_TEMP_TABLE, + SQLITE_DROP_TEMP_TRIGGER: SQLITE_DROP_TEMP_TRIGGER, + SQLITE_DROP_TEMP_VIEW: SQLITE_DROP_TEMP_VIEW, + SQLITE_DROP_TRIGGER: SQLITE_DROP_TRIGGER, + SQLITE_DROP_VIEW: SQLITE_DROP_VIEW, + SQLITE_DROP_VTABLE: SQLITE_DROP_VTABLE, + SQLITE_EMPTY: SQLITE_EMPTY, + SQLITE_ERROR: SQLITE_ERROR, + SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, + SQLITE_FCNTL_BUSYHANDLER: SQLITE_FCNTL_BUSYHANDLER, + SQLITE_FCNTL_CHUNK_SIZE: SQLITE_FCNTL_CHUNK_SIZE, + SQLITE_FCNTL_CKPT_DONE: SQLITE_FCNTL_CKPT_DONE, + SQLITE_FCNTL_CKPT_START: SQLITE_FCNTL_CKPT_START, + SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, + SQLITE_FCNTL_COMMIT_PHASETWO: SQLITE_FCNTL_COMMIT_PHASETWO, + SQLITE_FCNTL_DATA_VERSION: SQLITE_FCNTL_DATA_VERSION, + SQLITE_FCNTL_FILE_POINTER: SQLITE_FCNTL_FILE_POINTER, + SQLITE_FCNTL_GET_LOCKPROXYFILE: SQLITE_FCNTL_GET_LOCKPROXYFILE, + SQLITE_FCNTL_HAS_MOVED: SQLITE_FCNTL_HAS_MOVED, + SQLITE_FCNTL_JOURNAL_POINTER: SQLITE_FCNTL_JOURNAL_POINTER, + SQLITE_FCNTL_LAST_ERRNO: SQLITE_FCNTL_LAST_ERRNO, + SQLITE_FCNTL_LOCKSTATE: SQLITE_FCNTL_LOCKSTATE, + SQLITE_FCNTL_LOCK_TIMEOUT: SQLITE_FCNTL_LOCK_TIMEOUT, + SQLITE_FCNTL_MMAP_SIZE: SQLITE_FCNTL_MMAP_SIZE, + SQLITE_FCNTL_OVERWRITE: SQLITE_FCNTL_OVERWRITE, + SQLITE_FCNTL_PDB: SQLITE_FCNTL_PDB, + SQLITE_FCNTL_PERSIST_WAL: SQLITE_FCNTL_PERSIST_WAL, + SQLITE_FCNTL_POWERSAFE_OVERWRITE: SQLITE_FCNTL_POWERSAFE_OVERWRITE, + SQLITE_FCNTL_PRAGMA: SQLITE_FCNTL_PRAGMA, + SQLITE_FCNTL_RBU: SQLITE_FCNTL_RBU, + SQLITE_FCNTL_RESERVE_BYTES: SQLITE_FCNTL_RESERVE_BYTES, + SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, + SQLITE_FCNTL_SET_LOCKPROXYFILE: SQLITE_FCNTL_SET_LOCKPROXYFILE, + SQLITE_FCNTL_SIZE_HINT: SQLITE_FCNTL_SIZE_HINT, + SQLITE_FCNTL_SIZE_LIMIT: SQLITE_FCNTL_SIZE_LIMIT, + SQLITE_FCNTL_SYNC: SQLITE_FCNTL_SYNC, + SQLITE_FCNTL_SYNC_OMITTED: SQLITE_FCNTL_SYNC_OMITTED, + SQLITE_FCNTL_TEMPFILENAME: SQLITE_FCNTL_TEMPFILENAME, + SQLITE_FCNTL_TRACE: SQLITE_FCNTL_TRACE, + SQLITE_FCNTL_VFSNAME: SQLITE_FCNTL_VFSNAME, + SQLITE_FCNTL_VFS_POINTER: SQLITE_FCNTL_VFS_POINTER, + SQLITE_FCNTL_WAL_BLOCK: SQLITE_FCNTL_WAL_BLOCK, + SQLITE_FCNTL_WIN32_AV_RETRY: SQLITE_FCNTL_WIN32_AV_RETRY, + SQLITE_FCNTL_WIN32_GET_HANDLE: SQLITE_FCNTL_WIN32_GET_HANDLE, + SQLITE_FCNTL_WIN32_SET_HANDLE: SQLITE_FCNTL_WIN32_SET_HANDLE, + SQLITE_FCNTL_ZIPVFS: SQLITE_FCNTL_ZIPVFS, + SQLITE_FLOAT: SQLITE_FLOAT, + SQLITE_FORMAT: SQLITE_FORMAT, + SQLITE_FULL: SQLITE_FULL, + SQLITE_FUNCTION: SQLITE_FUNCTION, + SQLITE_IGNORE: SQLITE_IGNORE, + SQLITE_INDEX_CONSTRAINT_EQ: SQLITE_INDEX_CONSTRAINT_EQ, + SQLITE_INDEX_CONSTRAINT_FUNCTION: SQLITE_INDEX_CONSTRAINT_FUNCTION, + SQLITE_INDEX_CONSTRAINT_GE: SQLITE_INDEX_CONSTRAINT_GE, + SQLITE_INDEX_CONSTRAINT_GLOB: SQLITE_INDEX_CONSTRAINT_GLOB, + SQLITE_INDEX_CONSTRAINT_GT: SQLITE_INDEX_CONSTRAINT_GT, + SQLITE_INDEX_CONSTRAINT_IS: SQLITE_INDEX_CONSTRAINT_IS, + SQLITE_INDEX_CONSTRAINT_ISNOT: SQLITE_INDEX_CONSTRAINT_ISNOT, + SQLITE_INDEX_CONSTRAINT_ISNOTNULL: SQLITE_INDEX_CONSTRAINT_ISNOTNULL, + SQLITE_INDEX_CONSTRAINT_ISNULL: SQLITE_INDEX_CONSTRAINT_ISNULL, + SQLITE_INDEX_CONSTRAINT_LE: SQLITE_INDEX_CONSTRAINT_LE, + SQLITE_INDEX_CONSTRAINT_LIKE: SQLITE_INDEX_CONSTRAINT_LIKE, + SQLITE_INDEX_CONSTRAINT_LT: SQLITE_INDEX_CONSTRAINT_LT, + SQLITE_INDEX_CONSTRAINT_MATCH: SQLITE_INDEX_CONSTRAINT_MATCH, + SQLITE_INDEX_CONSTRAINT_NE: SQLITE_INDEX_CONSTRAINT_NE, + SQLITE_INDEX_CONSTRAINT_REGEXP: SQLITE_INDEX_CONSTRAINT_REGEXP, + SQLITE_INDEX_SCAN_UNIQUE: SQLITE_INDEX_SCAN_UNIQUE, + SQLITE_INNOCUOUS: SQLITE_INNOCUOUS, + SQLITE_INSERT: SQLITE_INSERT, + SQLITE_INTEGER: SQLITE_INTEGER, + SQLITE_INTERNAL: SQLITE_INTERNAL, + SQLITE_INTERRUPT: SQLITE_INTERRUPT, + SQLITE_IOCAP_ATOMIC: SQLITE_IOCAP_ATOMIC, + SQLITE_IOCAP_ATOMIC16K: SQLITE_IOCAP_ATOMIC16K, + SQLITE_IOCAP_ATOMIC1K: SQLITE_IOCAP_ATOMIC1K, + SQLITE_IOCAP_ATOMIC2K: SQLITE_IOCAP_ATOMIC2K, + SQLITE_IOCAP_ATOMIC32K: SQLITE_IOCAP_ATOMIC32K, + SQLITE_IOCAP_ATOMIC4K: SQLITE_IOCAP_ATOMIC4K, + SQLITE_IOCAP_ATOMIC512: SQLITE_IOCAP_ATOMIC512, + SQLITE_IOCAP_ATOMIC64K: SQLITE_IOCAP_ATOMIC64K, + SQLITE_IOCAP_ATOMIC8K: SQLITE_IOCAP_ATOMIC8K, + SQLITE_IOCAP_BATCH_ATOMIC: SQLITE_IOCAP_BATCH_ATOMIC, + SQLITE_IOCAP_IMMUTABLE: SQLITE_IOCAP_IMMUTABLE, + SQLITE_IOCAP_POWERSAFE_OVERWRITE: SQLITE_IOCAP_POWERSAFE_OVERWRITE, + SQLITE_IOCAP_SAFE_APPEND: SQLITE_IOCAP_SAFE_APPEND, + SQLITE_IOCAP_SEQUENTIAL: SQLITE_IOCAP_SEQUENTIAL, + SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN: SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN, + SQLITE_IOERR: SQLITE_IOERR, + SQLITE_IOERR_ACCESS: SQLITE_IOERR_ACCESS, + SQLITE_IOERR_BEGIN_ATOMIC: SQLITE_IOERR_BEGIN_ATOMIC, + SQLITE_IOERR_CHECKRESERVEDLOCK: SQLITE_IOERR_CHECKRESERVEDLOCK, + SQLITE_IOERR_CLOSE: SQLITE_IOERR_CLOSE, + SQLITE_IOERR_COMMIT_ATOMIC: SQLITE_IOERR_COMMIT_ATOMIC, + SQLITE_IOERR_DATA: SQLITE_IOERR_DATA, + SQLITE_IOERR_DELETE: SQLITE_IOERR_DELETE, + SQLITE_IOERR_DELETE_NOENT: SQLITE_IOERR_DELETE_NOENT, + SQLITE_IOERR_DIR_FSYNC: SQLITE_IOERR_DIR_FSYNC, + SQLITE_IOERR_FSTAT: SQLITE_IOERR_FSTAT, + SQLITE_IOERR_FSYNC: SQLITE_IOERR_FSYNC, + SQLITE_IOERR_GETTEMPPATH: SQLITE_IOERR_GETTEMPPATH, + SQLITE_IOERR_LOCK: SQLITE_IOERR_LOCK, + SQLITE_IOERR_NOMEM: SQLITE_IOERR_NOMEM, + SQLITE_IOERR_RDLOCK: SQLITE_IOERR_RDLOCK, + SQLITE_IOERR_READ: SQLITE_IOERR_READ, + SQLITE_IOERR_ROLLBACK_ATOMIC: SQLITE_IOERR_ROLLBACK_ATOMIC, + SQLITE_IOERR_SEEK: SQLITE_IOERR_SEEK, + SQLITE_IOERR_SHORT_READ: SQLITE_IOERR_SHORT_READ, + SQLITE_IOERR_TRUNCATE: SQLITE_IOERR_TRUNCATE, + SQLITE_IOERR_UNLOCK: SQLITE_IOERR_UNLOCK, + SQLITE_IOERR_VNODE: SQLITE_IOERR_VNODE, + SQLITE_IOERR_WRITE: SQLITE_IOERR_WRITE, + SQLITE_LIMIT_ATTACHED: SQLITE_LIMIT_ATTACHED, + SQLITE_LIMIT_COLUMN: SQLITE_LIMIT_COLUMN, + SQLITE_LIMIT_COMPOUND_SELECT: SQLITE_LIMIT_COMPOUND_SELECT, + SQLITE_LIMIT_EXPR_DEPTH: SQLITE_LIMIT_EXPR_DEPTH, + SQLITE_LIMIT_FUNCTION_ARG: SQLITE_LIMIT_FUNCTION_ARG, + SQLITE_LIMIT_LENGTH: SQLITE_LIMIT_LENGTH, + SQLITE_LIMIT_LIKE_PATTERN_LENGTH: SQLITE_LIMIT_LIKE_PATTERN_LENGTH, + SQLITE_LIMIT_SQL_LENGTH: SQLITE_LIMIT_SQL_LENGTH, + SQLITE_LIMIT_TRIGGER_DEPTH: SQLITE_LIMIT_TRIGGER_DEPTH, + SQLITE_LIMIT_VARIABLE_NUMBER: SQLITE_LIMIT_VARIABLE_NUMBER, + SQLITE_LIMIT_VDBE_OP: SQLITE_LIMIT_VDBE_OP, + SQLITE_LIMIT_WORKER_THREADS: SQLITE_LIMIT_WORKER_THREADS, + SQLITE_LOCKED: SQLITE_LOCKED, + SQLITE_LOCK_EXCLUSIVE: SQLITE_LOCK_EXCLUSIVE, + SQLITE_LOCK_NONE: SQLITE_LOCK_NONE, + SQLITE_LOCK_PENDING: SQLITE_LOCK_PENDING, + SQLITE_LOCK_RESERVED: SQLITE_LOCK_RESERVED, + SQLITE_LOCK_SHARED: SQLITE_LOCK_SHARED, + SQLITE_MISMATCH: SQLITE_MISMATCH, + SQLITE_MISUSE: SQLITE_MISUSE, + SQLITE_NOLFS: SQLITE_NOLFS, + SQLITE_NOMEM: SQLITE_NOMEM, + SQLITE_NOTADB: SQLITE_NOTADB, + SQLITE_NOTFOUND: SQLITE_NOTFOUND, + SQLITE_NOTICE: SQLITE_NOTICE, + SQLITE_NULL: SQLITE_NULL, + SQLITE_OK: SQLITE_OK, + SQLITE_OPEN_AUTOPROXY: SQLITE_OPEN_AUTOPROXY, + SQLITE_OPEN_CREATE: SQLITE_OPEN_CREATE, + SQLITE_OPEN_DELETEONCLOSE: SQLITE_OPEN_DELETEONCLOSE, + SQLITE_OPEN_EXCLUSIVE: SQLITE_OPEN_EXCLUSIVE, + SQLITE_OPEN_FULLMUTEX: SQLITE_OPEN_FULLMUTEX, + SQLITE_OPEN_MAIN_DB: SQLITE_OPEN_MAIN_DB, + SQLITE_OPEN_MAIN_JOURNAL: SQLITE_OPEN_MAIN_JOURNAL, + SQLITE_OPEN_MEMORY: SQLITE_OPEN_MEMORY, + SQLITE_OPEN_NOFOLLOW: SQLITE_OPEN_NOFOLLOW, + SQLITE_OPEN_NOMUTEX: SQLITE_OPEN_NOMUTEX, + SQLITE_OPEN_PRIVATECACHE: SQLITE_OPEN_PRIVATECACHE, + SQLITE_OPEN_READONLY: SQLITE_OPEN_READONLY, + SQLITE_OPEN_READWRITE: SQLITE_OPEN_READWRITE, + SQLITE_OPEN_SHAREDCACHE: SQLITE_OPEN_SHAREDCACHE, + SQLITE_OPEN_SUBJOURNAL: SQLITE_OPEN_SUBJOURNAL, + SQLITE_OPEN_SUPER_JOURNAL: SQLITE_OPEN_SUPER_JOURNAL, + SQLITE_OPEN_TEMP_DB: SQLITE_OPEN_TEMP_DB, + SQLITE_OPEN_TEMP_JOURNAL: SQLITE_OPEN_TEMP_JOURNAL, + SQLITE_OPEN_TRANSIENT_DB: SQLITE_OPEN_TRANSIENT_DB, + SQLITE_OPEN_URI: SQLITE_OPEN_URI, + SQLITE_OPEN_WAL: SQLITE_OPEN_WAL, + SQLITE_PERM: SQLITE_PERM, + SQLITE_PRAGMA: SQLITE_PRAGMA, + SQLITE_PREPARE_NORMALIZED: SQLITE_PREPARE_NORMALIZED, + SQLITE_PREPARE_NO_VTAB: SQLITE_PREPARE_NO_VTAB, + SQLITE_PREPARE_PERSISTENT: SQLITE_PREPARE_PERSISTENT, + SQLITE_PROTOCOL: SQLITE_PROTOCOL, + SQLITE_RANGE: SQLITE_RANGE, + SQLITE_READ: SQLITE_READ, + SQLITE_READONLY: SQLITE_READONLY, + SQLITE_RECURSIVE: SQLITE_RECURSIVE, + SQLITE_REINDEX: SQLITE_REINDEX, + SQLITE_ROW: SQLITE_ROW, + SQLITE_SAVEPOINT: SQLITE_SAVEPOINT, + SQLITE_SCHEMA: SQLITE_SCHEMA, + SQLITE_SELECT: SQLITE_SELECT, + SQLITE_STATIC: SQLITE_STATIC, + SQLITE_SUBTYPE: SQLITE_SUBTYPE, + SQLITE_SYNC_DATAONLY: SQLITE_SYNC_DATAONLY, + SQLITE_SYNC_FULL: SQLITE_SYNC_FULL, + SQLITE_SYNC_NORMAL: SQLITE_SYNC_NORMAL, + SQLITE_TEXT: SQLITE_TEXT, + SQLITE_TOOBIG: SQLITE_TOOBIG, + SQLITE_TRANSACTION: SQLITE_TRANSACTION, + SQLITE_TRANSIENT: SQLITE_TRANSIENT, + SQLITE_UPDATE: SQLITE_UPDATE, + SQLITE_UTF16: SQLITE_UTF16, + SQLITE_UTF16BE: SQLITE_UTF16BE, + SQLITE_UTF16LE: SQLITE_UTF16LE, + SQLITE_UTF8: SQLITE_UTF8, + SQLITE_WARNING: SQLITE_WARNING, + SQLiteError: SQLiteError +}); + +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. + +const DEFAULT_SECTOR_SIZE = 512; + +// Base class for a VFS. +class Base { + name; + mxPathname = 64; + _module; + + /** + * @param {string} name + * @param {object} module + */ + constructor(name, module) { + this.name = name; + this._module = module; + } + + /** + * @returns {void|Promise} + */ + close() { + } + + /** + * @returns {boolean|Promise} + */ + isReady() { + return true; + } + + /** + * Overload in subclasses to indicate which methods are asynchronous. + * @param {string} methodName + * @returns {boolean} + */ + hasAsyncMethod(methodName) { + return false; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} pFile + * @param {number} flags + * @param {number} pOutFlags + * @returns {number|Promise} + */ + xOpen(pVfs, zName, pFile, flags, pOutFlags) { + return SQLITE_CANTOPEN; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} syncDir + * @returns {number|Promise} + */ + xDelete(pVfs, zName, syncDir) { + return SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} flags + * @param {number} pResOut + * @returns {number|Promise} + */ + xAccess(pVfs, zName, flags, pResOut) { + return SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} nOut + * @param {number} zOut + * @returns {number|Promise} + */ + xFullPathname(pVfs, zName, nOut, zOut) { + return SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} nBuf + * @param {number} zBuf + * @returns {number|Promise} + */ + xGetLastError(pVfs, nBuf, zBuf) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xClose(pFile) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} sizeLo + * @param {number} sizeHi + * @returns {number|Promise} + */ + xTruncate(pFile, sizeLo, sizeHi) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + xSync(pFile, flags) { + return SQLITE_OK; + } + + /** + * + * @param {number} pFile + * @param {number} pSize + * @returns {number|Promise} + */ + xFileSize(pFile, pSize) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xLock(pFile, lockType) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xUnlock(pFile, lockType) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pResOut + * @returns {number|Promise} + */ + xCheckReservedLock(pFile, pResOut) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} op + * @param {number} pArg + * @returns {number|Promise} + */ + xFileControl(pFile, op, pArg) { + return SQLITE_NOTFOUND; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xSectorSize(pFile) { + return DEFAULT_SECTOR_SIZE; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xDeviceCharacteristics(pFile) { + return 0; + } +} + +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. + +const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; + +// Convenience base class for a JavaScript VFS. +// The raw xOpen, xRead, etc. function signatures receive only C primitives +// which aren't easy to work with. This class provides corresponding calls +// like jOpen, jRead, etc., which receive JavaScript-friendlier arguments +// such as string, Uint8Array, and DataView. +class FacadeVFS extends Base { + /** + * @param {string} name + * @param {object} module + */ + constructor(name, module) { + super(name, module); + } + + /** + * Override to indicate which methods are asynchronous. + * @param {string} methodName + * @returns {boolean} + */ + hasAsyncMethod(methodName) { + // The input argument is a string like "xOpen", so convert to "jOpen". + // Then check if the method exists and is async. + const jMethodName = `j${methodName.slice(1)}`; + return this[jMethodName] instanceof AsyncFunction; + } + + /** + * Return the filename for a file id for use by mixins. + * @param {number} pFile + * @returns {string} + */ + getFilename(pFile) { + throw new Error('unimplemented'); + } + + /** + * @param {string?} filename + * @param {number} pFile + * @param {number} flags + * @param {DataView} pOutFlags + * @returns {number|Promise} + */ + jOpen(filename, pFile, flags, pOutFlags) { + return SQLITE_CANTOPEN; + } + + /** + * @param {string} filename + * @param {number} syncDir + * @returns {number|Promise} + */ + jDelete(filename, syncDir) { + return SQLITE_OK; + } + + /** + * @param {string} filename + * @param {number} flags + * @param {DataView} pResOut + * @returns {number|Promise} + */ + jAccess(filename, flags, pResOut) { + return SQLITE_OK; + } + + /** + * @param {string} filename + * @param {Uint8Array} zOut + * @returns {number|Promise} + */ + jFullPathname(filename, zOut) { + // Copy the filename to the output buffer. + const { read, written } = new TextEncoder().encodeInto(filename, zOut); + if (read < filename.length) return SQLITE_IOERR; + if (written >= zOut.length) return SQLITE_IOERR; + zOut[written] = 0; + return SQLITE_OK; + } + + /** + * @param {Uint8Array} zBuf + * @returns {number|Promise} + */ + jGetLastError(zBuf) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jClose(pFile) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number|Promise} + */ + jRead(pFile, pData, iOffset) { + pData.fill(0); + return SQLITE_IOERR_SHORT_READ; + } + + /** + * @param {number} pFile + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number|Promise} + */ + jWrite(pFile, pData, iOffset) { + return SQLITE_IOERR_WRITE; + } + + /** + * @param {number} pFile + * @param {number} size + * @returns {number|Promise} + */ + jTruncate(pFile, size) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + jSync(pFile, flags) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {DataView} pSize + * @returns {number|Promise} + */ + jFileSize(pFile, pSize) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + jLock(pFile, lockType) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + jUnlock(pFile, lockType) { + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {DataView} pResOut + * @returns {number|Promise} + */ + jCheckReservedLock(pFile, pResOut) { + pResOut.setInt32(0, 0, true); + return SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} op + * @param {DataView} pArg + * @returns {number|Promise} + */ + jFileControl(pFile, op, pArg) { + return SQLITE_NOTFOUND; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jSectorSize(pFile) { + return super.xSectorSize(pFile); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jDeviceCharacteristics(pFile) { + return 0; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} pFile + * @param {number} flags + * @param {number} pOutFlags + * @returns {number|Promise} + */ + xOpen(pVfs, zName, pFile, flags, pOutFlags) { + const filename = this.#decodeFilename(zName, flags); + const pOutFlagsView = this.#makeTypedDataView('Int32', pOutFlags); + this['log']?.('jOpen', filename, pFile, '0x' + flags.toString(16)); + return this.jOpen(filename, pFile, flags, pOutFlagsView); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} syncDir + * @returns {number|Promise} + */ + xDelete(pVfs, zName, syncDir) { + const filename = this._module.UTF8ToString(zName); + this['log']?.('jDelete', filename, syncDir); + return this.jDelete(filename, syncDir); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} flags + * @param {number} pResOut + * @returns {number|Promise} + */ + xAccess(pVfs, zName, flags, pResOut) { + const filename = this._module.UTF8ToString(zName); + const pResOutView = this.#makeTypedDataView('Int32', pResOut); + this['log']?.('jAccess', filename, flags); + return this.jAccess(filename, flags, pResOutView); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} nOut + * @param {number} zOut + * @returns {number|Promise} + */ + xFullPathname(pVfs, zName, nOut, zOut) { + const filename = this._module.UTF8ToString(zName); + const zOutArray = this._module.HEAPU8.subarray(zOut, zOut + nOut); + this['log']?.('jFullPathname', filename, nOut); + return this.jFullPathname(filename, zOutArray); + } + + /** + * @param {number} pVfs + * @param {number} nBuf + * @param {number} zBuf + * @returns {number|Promise} + */ + xGetLastError(pVfs, nBuf, zBuf) { + const zBufArray = this._module.HEAPU8.subarray(zBuf, zBuf + nBuf); + this['log']?.('jGetLastError', nBuf); + return this.jGetLastError(zBufArray); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xClose(pFile) { + this['log']?.('jClose', pFile); + return this.jClose(pFile); + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + const pDataArray = this.#makeDataArray(pData, iAmt); + const iOffset = delegalize(iOffsetLo, iOffsetHi); + this['log']?.('jRead', pFile, iAmt, iOffset); + return this.jRead(pFile, pDataArray, iOffset); + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + const pDataArray = this.#makeDataArray(pData, iAmt); + const iOffset = delegalize(iOffsetLo, iOffsetHi); + this['log']?.('jWrite', pFile, pDataArray, iOffset); + return this.jWrite(pFile, pDataArray, iOffset); + } + + /** + * @param {number} pFile + * @param {number} sizeLo + * @param {number} sizeHi + * @returns {number|Promise} + */ + xTruncate(pFile, sizeLo, sizeHi) { + const size = delegalize(sizeLo, sizeHi); + this['log']?.('jTruncate', pFile, size); + return this.jTruncate(pFile, size); + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + xSync(pFile, flags) { + this['log']?.('jSync', pFile, flags); + return this.jSync(pFile, flags); + } + + /** + * + * @param {number} pFile + * @param {number} pSize + * @returns {number|Promise} + */ + xFileSize(pFile, pSize) { + const pSizeView = this.#makeTypedDataView('BigInt64', pSize); + this['log']?.('jFileSize', pFile); + return this.jFileSize(pFile, pSizeView); + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xLock(pFile, lockType) { + this['log']?.('jLock', pFile, lockType); + return this.jLock(pFile, lockType); + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xUnlock(pFile, lockType) { + this['log']?.('jUnlock', pFile, lockType); + return this.jUnlock(pFile, lockType); + } + + /** + * @param {number} pFile + * @param {number} pResOut + * @returns {number|Promise} + */ + xCheckReservedLock(pFile, pResOut) { + const pResOutView = this.#makeTypedDataView('Int32', pResOut); + this['log']?.('jCheckReservedLock', pFile); + return this.jCheckReservedLock(pFile, pResOutView); + } + + /** + * @param {number} pFile + * @param {number} op + * @param {number} pArg + * @returns {number|Promise} + */ + xFileControl(pFile, op, pArg) { + const pArgView = new DataView( + this._module.HEAPU8.buffer, + this._module.HEAPU8.byteOffset + pArg); + this['log']?.('jFileControl', pFile, op, pArgView); + return this.jFileControl(pFile, op, pArgView); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xSectorSize(pFile) { + this['log']?.('jSectorSize', pFile); + return this.jSectorSize(pFile); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xDeviceCharacteristics(pFile) { + this['log']?.('jDeviceCharacteristics', pFile); + return this.jDeviceCharacteristics(pFile); + } + + /** + * Wrapped DataView for pointer arguments. + * Pointers to a single value are passed using DataView. A Proxy + * wrapper prevents use of incorrect type or endianness. + * @param {'Int32'|'BigInt64'} type + * @param {number} byteOffset + * @returns {DataView} + */ + #makeTypedDataView(type, byteOffset) { + const byteLength = type === 'Int32' ? 4 : 8; + const getter = `get${type}`; + const setter = `set${type}`; + const makeDataView = () => new DataView( + this._module.HEAPU8.buffer, + this._module.HEAPU8.byteOffset + byteOffset, + byteLength); + let dataView = makeDataView(); + return new Proxy(dataView, { + get(_, prop) { + if (dataView.buffer.byteLength === 0) { + // WebAssembly memory resize detached the buffer. + dataView = makeDataView(); + } + if (prop === getter) { + return function(byteOffset, littleEndian) { + if (!littleEndian) throw new Error('must be little endian'); + return dataView[prop](byteOffset, littleEndian); + } + } + if (prop === setter) { + return function(byteOffset, value, littleEndian) { + if (!littleEndian) throw new Error('must be little endian'); + return dataView[prop](byteOffset, value, littleEndian); + } + } + if (typeof prop === 'string' && (prop.match(/^(get)|(set)/))) { + throw new Error('invalid type'); + } + const result = dataView[prop]; + return typeof result === 'function' ? result.bind(dataView) : result; + } + }); + } + + /** + * @param {number} byteOffset + * @param {number} byteLength + */ + #makeDataArray(byteOffset, byteLength) { + let target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength); + return new Proxy(target, { + get: (_, prop, receiver) => { + if (target.buffer.byteLength === 0) { + // WebAssembly memory resize detached the buffer. + target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength); + } + const result = target[prop]; + return typeof result === 'function' ? result.bind(target) : result; + } + }); + } + + #decodeFilename(zName, flags) { + if (flags & SQLITE_OPEN_URI) { + // The first null-terminated string is the URI path. Subsequent + // strings are query parameter keys and values. + // https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open + let pName = zName; + let state = 1; + const charCodes = []; + while (state) { + const charCode = this._module.HEAPU8[pName++]; + if (charCode) { + charCodes.push(charCode); + } else { + if (!this._module.HEAPU8[pName]) state = null; + switch (state) { + case 1: // path + charCodes.push('?'.charCodeAt(0)); + state = 2; + break; + case 2: // key + charCodes.push('='.charCodeAt(0)); + state = 3; + break; + case 3: // value + charCodes.push('&'.charCodeAt(0)); + state = 2; + break; + } + } + } + return new TextDecoder().decode(new Uint8Array(charCodes)); + } + return zName ? this._module.UTF8ToString(zName) : null; + } +} + +// Emscripten "legalizes" 64-bit integer arguments by passing them as +// two 32-bit signed integers. +function delegalize(lo32, hi32) { + return (hi32 * 0x100000000) + lo32 + (lo32 < 0 ? 2**32 : 0); +} + +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. + +const DEFAULT_TEMPORARY_FILES = 10; +const LOCK_NOTIFY_INTERVAL = 1000; + +const DB_RELATED_FILE_SUFFIXES = ['', '-journal', '-wal']; + +const finalizationRegistry = new FinalizationRegistry(releaser => releaser()); + +class File { + /** @type {string} */ path + /** @type {number} */ flags; + /** @type {FileSystemSyncAccessHandle} */ accessHandle; + + /** @type {PersistentFile?} */ persistentFile; + + constructor(path, flags) { + this.path = path; + this.flags = flags; + } +} + +class PersistentFile { + /** @type {FileSystemFileHandle} */ fileHandle + /** @type {FileSystemSyncAccessHandle} */ accessHandle = null + + // The following properties are for main database files. + + /** @type {boolean} */ isLockBusy = false; + /** @type {boolean} */ isFileLocked = false; + /** @type {boolean} */ isRequestInProgress = false; + /** @type {function} */ handleLockReleaser = null; + + /** @type {BroadcastChannel} */ handleRequestChannel; + /** @type {boolean} */ isHandleRequested = false; + + constructor(fileHandle) { + this.fileHandle = fileHandle; + } +} + +class OPFSCoopSyncVFS extends FacadeVFS { + /** @type {Map} */ mapIdToFile = new Map(); + + lastError = null; + log = null; //function(...args) { console.log(`[${contextName}]`, ...args) }; + + /** @type {Map} */ persistentFiles = new Map(); + /** @type {Map} */ boundAccessHandles = new Map(); + /** @type {Set} */ unboundAccessHandles = new Set(); + /** @type {Set} */ accessiblePaths = new Set(); + releaser = null; + + static async create(name, module) { + const vfs = new OPFSCoopSyncVFS(name, module); + await Promise.all([ + vfs.isReady(), + vfs.#initialize(DEFAULT_TEMPORARY_FILES), + ]); + return vfs; + } + + constructor(name, module) { + super(name, module); + } + + async #initialize(nTemporaryFiles) { + // Delete temporary directories no longer in use. + const root = await navigator.storage.getDirectory(); + // @ts-ignore + for await (const entry of root.values()) { + if (entry.kind === 'directory' && entry.name.startsWith('.ahp-')) { + // A lock with the same name as the directory protects it from + // being deleted. + await navigator.locks.request(entry.name, { ifAvailable: true }, async lock => { + if (lock) { + this.log?.(`Deleting temporary directory ${entry.name}`); + await root.removeEntry(entry.name, { recursive: true }); + } else { + this.log?.(`Temporary directory ${entry.name} is in use`); + } + }); + } + } + + // Create our temporary directory. + const tmpDirName = `.ahp-${Math.random().toString(36).slice(2)}`; + this.releaser = await new Promise(resolve => { + navigator.locks.request(tmpDirName, () => { + return new Promise(release => { + resolve(release); + }); + }); + }); + finalizationRegistry.register(this, this.releaser); + const tmpDir = await root.getDirectoryHandle(tmpDirName, { create: true }); + + // Populate temporary directory. + for (let i = 0; i < nTemporaryFiles; i++) { + const tmpFile = await tmpDir.getFileHandle(`${i}.tmp`, { create: true }); + const tmpAccessHandle = await tmpFile.createSyncAccessHandle(); + this.unboundAccessHandles.add(tmpAccessHandle); + } + } + + /** + * @param {string?} zName + * @param {number} fileId + * @param {number} flags + * @param {DataView} pOutFlags + * @returns {number} + */ + jOpen(zName, fileId, flags, pOutFlags) { + try { + const url = new URL(zName || Math.random().toString(36).slice(2), 'file://'); + const path = url.pathname; + + if (flags & SQLITE_OPEN_MAIN_DB) { + const persistentFile = this.persistentFiles.get(path); + if (persistentFile?.isRequestInProgress) { + // Should not reach here unless SQLite itself retries an open. + // Otherwise, asynchronous operations started on a previous + // open try should have completed. + return SQLITE_BUSY; + } else if (!persistentFile) { + // This is the usual starting point for opening a database. + // Register a Promise that resolves when the database and related + // files are ready to be used. + this.log?.(`creating persistent file for ${path}`); + const create = !!(flags & SQLITE_OPEN_CREATE); + this._module.retryOps.push((async () => { + try { + // Get the path directory handle. + let dirHandle = await navigator.storage.getDirectory(); + const directories = path.split('/').filter(d => d); + const filename = directories.pop(); + for (const directory of directories) { + dirHandle = await dirHandle.getDirectoryHandle(directory, { create }); + } + + // Get file handles for the database and related files, + // and create persistent file instances. + for (const suffix of DB_RELATED_FILE_SUFFIXES) { + const fileHandle = await dirHandle.getFileHandle(filename + suffix, { create }); + await this.#createPersistentFile(fileHandle); + } + + // Get access handles for the files. + const file = new File(path, flags); + file.persistentFile = this.persistentFiles.get(path); + await this.#requestAccessHandle(file); + } catch (e) { + // Use an invalid persistent file to signal this error + // for the retried open. + const persistentFile = new PersistentFile(null); + this.persistentFiles.set(path, persistentFile); + console.error(e); + } + })()); + return SQLITE_BUSY; + } else if (!persistentFile.fileHandle) { + // The asynchronous open operation failed. + this.persistentFiles.delete(path); + return SQLITE_CANTOPEN; + } else if (!persistentFile.accessHandle) { + // This branch is reached if the database was previously opened + // and closed. + this._module.retryOps.push((async () => { + const file = new File(path, flags); + file.persistentFile = this.persistentFiles.get(path); + await this.#requestAccessHandle(file); + })()); + return SQLITE_BUSY; + } + } + + if (!this.accessiblePaths.has(path) && + !(flags & SQLITE_OPEN_CREATE)) { + throw new Error(`File ${path} not found`); + } + + const file = new File(path, flags); + this.mapIdToFile.set(fileId, file); + + if (this.persistentFiles.has(path)) { + file.persistentFile = this.persistentFiles.get(path); + } else if (this.boundAccessHandles.has(path)) { + // This temporary file was previously created and closed. Reopen + // the same access handle. + file.accessHandle = this.boundAccessHandles.get(path); + } else if (this.unboundAccessHandles.size) { + // Associate an unbound access handle to this file. + file.accessHandle = this.unboundAccessHandles.values().next().value; + file.accessHandle.truncate(0); + this.unboundAccessHandles.delete(file.accessHandle); + this.boundAccessHandles.set(path, file.accessHandle); + } + this.accessiblePaths.add(path); + + pOutFlags.setInt32(0, flags, true); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_CANTOPEN; + } + } + + /** + * @param {string} zName + * @param {number} syncDir + * @returns {number} + */ + jDelete(zName, syncDir) { + try { + const url = new URL(zName, 'file://'); + const path = url.pathname; + if (this.persistentFiles.has(path)) { + const persistentFile = this.persistentFiles.get(path); + persistentFile.accessHandle.truncate(0); + } else { + this.boundAccessHandles.get(path)?.truncate(0); + } + this.accessiblePaths.delete(path); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_DELETE; + } + } + + /** + * @param {string} zName + * @param {number} flags + * @param {DataView} pResOut + * @returns {number} + */ + jAccess(zName, flags, pResOut) { + try { + const url = new URL(zName, 'file://'); + const path = url.pathname; + pResOut.setInt32(0, this.accessiblePaths.has(path) ? 1 : 0, true); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_ACCESS; + } + } + + /** + * @param {number} fileId + * @returns {number} + */ + jClose(fileId) { + try { + const file = this.mapIdToFile.get(fileId); + this.mapIdToFile.delete(fileId); + + if (file?.flags & SQLITE_OPEN_MAIN_DB) { + if (file.persistentFile?.handleLockReleaser) { + this.#releaseAccessHandle(file); + } + } else if (file?.flags & SQLITE_OPEN_DELETEONCLOSE) { + file.accessHandle.truncate(0); + this.accessiblePaths.delete(file.path); + if (!this.persistentFiles.has(file.path)) { + this.boundAccessHandles.delete(file.path); + this.unboundAccessHandles.add(file.accessHandle); + } + } + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_CLOSE; + } + } + + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + jRead(fileId, pData, iOffset) { + try { + const file = this.mapIdToFile.get(fileId); + + // On Chrome (at least), passing pData to accessHandle.read() is + // an error because pData is a Proxy of a Uint8Array. Calling + // subarray() produces a real Uint8Array and that works. + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const bytesRead = accessHandle.read(pData.subarray(), { at: iOffset }); + + // Opening a database file performs one read without a xLock call. + if ((file.flags & SQLITE_OPEN_MAIN_DB) && !file.persistentFile.isFileLocked) { + this.#releaseAccessHandle(file); + } + + if (bytesRead < pData.byteLength) { + pData.fill(0, bytesRead); + return SQLITE_IOERR_SHORT_READ; + } + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_READ; + } + } + + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + jWrite(fileId, pData, iOffset) { + try { + const file = this.mapIdToFile.get(fileId); + + // On Chrome (at least), passing pData to accessHandle.write() is + // an error because pData is a Proxy of a Uint8Array. Calling + // subarray() produces a real Uint8Array and that works. + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const nBytes = accessHandle.write(pData.subarray(), { at: iOffset }); + if (nBytes !== pData.byteLength) throw new Error('short write'); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_WRITE; + } + } + + /** + * @param {number} fileId + * @param {number} iSize + * @returns {number} + */ + jTruncate(fileId, iSize) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + accessHandle.truncate(iSize); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_TRUNCATE; + } + } + + /** + * @param {number} fileId + * @param {number} flags + * @returns {number} + */ + jSync(fileId, flags) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + accessHandle.flush(); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_FSYNC; + } + } + + /** + * @param {number} fileId + * @param {DataView} pSize64 + * @returns {number} + */ + jFileSize(fileId, pSize64) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const size = accessHandle.getSize(); + pSize64.setBigInt64(0, BigInt(size), true); + return SQLITE_OK; + } catch (e) { + this.lastError = e; + return SQLITE_IOERR_FSTAT; + } + } + + /** + * @param {number} fileId + * @param {number} lockType + * @returns {number} + */ + jLock(fileId, lockType) { + const file = this.mapIdToFile.get(fileId); + if (file.persistentFile.isRequestInProgress) { + file.persistentFile.isLockBusy = true; + return SQLITE_BUSY; + } + + file.persistentFile.isFileLocked = true; + if (!file.persistentFile.handleLockReleaser) { + // Start listening for notifications from other connections. + // This is before we actually get access handles, but waiting to + // listen until then allows a race condition where notifications + // are missed. + file.persistentFile.handleRequestChannel.onmessage = () => { + this.log?.(`received notification for ${file.path}`); + if (file.persistentFile.isFileLocked) { + // We're still using the access handle, so mark it to be + // released when we're done. + file.persistentFile.isHandleRequested = true; + } else { + // Release the access handles immediately. + this.#releaseAccessHandle(file); + } + file.persistentFile.handleRequestChannel.onmessage = null; + }; + + this.#requestAccessHandle(file); + this.log?.('returning SQLITE_BUSY'); + file.persistentFile.isLockBusy = true; + return SQLITE_BUSY; + } + file.persistentFile.isLockBusy = false; + return SQLITE_OK; + } + + /** + * @param {number} fileId + * @param {number} lockType + * @returns {number} + */ + jUnlock(fileId, lockType) { + const file = this.mapIdToFile.get(fileId); + if (lockType === SQLITE_LOCK_NONE) { + // Don't change any state if this unlock is because xLock returned + // SQLITE_BUSY. + if (!file.persistentFile.isLockBusy) { + if (file.persistentFile.isHandleRequested) { + // Another connection wants the access handle. + this.#releaseAccessHandle(file); + this.isHandleRequested = false; + } + file.persistentFile.isFileLocked = false; + } + } + return SQLITE_OK; + } + + /** + * @param {number} fileId + * @param {number} op + * @param {DataView} pArg + * @returns {number|Promise} + */ + jFileControl(fileId, op, pArg) { + try { + const file = this.mapIdToFile.get(fileId); + switch (op) { + case SQLITE_FCNTL_PRAGMA: + const key = extractString(pArg, 4); + const value = extractString(pArg, 8); + this.log?.('xFileControl', file.path, 'PRAGMA', key, value); + switch (key.toLowerCase()) { + case 'journal_mode': + if (value && + !['off', 'memory', 'delete', 'wal'].includes(value.toLowerCase())) { + throw new Error('journal_mode must be "off", "memory", "delete", or "wal"'); + } + break; + } + break; + } + } catch (e) { + this.lastError = e; + return SQLITE_IOERR; + } + return SQLITE_NOTFOUND; + } + + /** + * @param {Uint8Array} zBuf + * @returns + */ + jGetLastError(zBuf) { + if (this.lastError) { + console.error(this.lastError); + const outputArray = zBuf.subarray(0, zBuf.byteLength - 1); + const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray); + zBuf[written] = 0; + } + return SQLITE_OK + } + + /** + * @param {FileSystemFileHandle} fileHandle + * @returns {Promise} + */ + async #createPersistentFile(fileHandle) { + const persistentFile = new PersistentFile(fileHandle); + const root = await navigator.storage.getDirectory(); + const relativePath = await root.resolve(fileHandle); + const path = `/${relativePath.join('/')}`; + persistentFile.handleRequestChannel = new BroadcastChannel(`ahp:${path}`); + this.persistentFiles.set(path, persistentFile); + + const f = await fileHandle.getFile(); + if (f.size) { + this.accessiblePaths.add(path); + } + return persistentFile; + } + + /** + * @param {File} file + */ + #requestAccessHandle(file) { + console.assert(!file.persistentFile.handleLockReleaser); + if (!file.persistentFile.isRequestInProgress) { + file.persistentFile.isRequestInProgress = true; + this._module.retryOps.push((async () => { + // Acquire the Web Lock. + file.persistentFile.handleLockReleaser = await this.#acquireLock(file.persistentFile); + + // Get access handles for the database and releated files in parallel. + this.log?.(`creating access handles for ${file.path}`); + await Promise.all(DB_RELATED_FILE_SUFFIXES.map(async suffix => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle = + await persistentFile.fileHandle.createSyncAccessHandle(); + } + })); + file.persistentFile.isRequestInProgress = false; + })()); + return this._module.retryOps.at(-1); + } + return Promise.resolve(); + } + + /** + * @param {File} file + */ + async #releaseAccessHandle(file) { + DB_RELATED_FILE_SUFFIXES.forEach(async suffix => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle?.close(); + persistentFile.accessHandle = null; + } + }); + this.log?.(`access handles closed for ${file.path}`); + + file.persistentFile.handleLockReleaser?.(); + file.persistentFile.handleLockReleaser = null; + this.log?.(`lock released for ${file.path}`); + } + + /** + * @param {PersistentFile} persistentFile + * @returns {Promise} lock releaser + */ + #acquireLock(persistentFile) { + return new Promise(resolve => { + // Tell other connections we want the access handle. + const lockName = persistentFile.handleRequestChannel.name; + const notify = () => { + this.log?.(`notifying for ${lockName}`); + persistentFile.handleRequestChannel.postMessage(null); + }; + const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL); + setTimeout(notify); + + this.log?.(`lock requested: ${lockName}`); + navigator.locks.request(lockName, lock => { + // We have the lock. Stop asking other connections for it. + this.log?.(`lock acquired: ${lockName}`, lock); + clearInterval(notifyId); + return new Promise(resolve); + }); + }); + } +} + +function extractString(dataView, offset) { + const p = dataView.getUint32(offset, true); + if (p) { + const chars = new Uint8Array(dataView.buffer, p); + return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0))); + } + return null; +} + +var Module = (() => { + var _scriptName = import.meta.url; + + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject;});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var moduleOverrides=Object.assign({},Module);var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href;}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src;}if(_scriptName){scriptDirectory=_scriptName;}if(scriptDirectory.startsWith("blob:")){scriptDirectory="";}else {scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1);}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)};}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))});}}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift());}}callRuntimeCallbacks(__ATPRERUN__);}function initRuntime(){if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;callRuntimeCallbacks(__ATINIT__);}function preMain(){callRuntimeCallbacks(__ATMAIN__);}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift());}}callRuntimeCallbacks(__ATPOSTRUN__);}function addOnPreRun(cb){__ATPRERUN__.unshift(cb);}function addOnInit(cb){__ATINIT__.unshift(cb);}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb);}var runDependencies=0;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies);}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback();}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="wa-sqlite.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("wa-sqlite.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw "both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason);})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return {a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["ja"];updateMemoryViews();wasmTable=wasmExports["af"];addOnInit(wasmExports["ka"]);removeRunDependency();return wasmExports}addRunDependency();function receiveInstantiationResult(result){receiveInstance(result["instance"]);}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e);}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return {}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status;}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module);}};function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`);}}var noExitRuntime=Module["noExitRuntime"]||true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`);}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023);}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ___assert_fail=(condition,filename,line,func)=>{abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"]);};var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1);}else if(last===".."){parts.splice(i,1);up++;}else if(up){parts.splice(i,1);up--;}}if(allowAboveRoot){for(;up;up--){parts.unshift("..");}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path=".";}if(path&&trailingSlash){path+="/";}return (isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return "."}if(dir){dir=dir.substr(0,dir.length-1);}return root+dir},basename:path=>{if(path==="/")return "/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else abort("initRandomDevice");};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return ""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path);}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return (resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return [];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i;}else {len+=3;}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023;}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u;}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63;}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;}else {if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n";}}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true);}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops);},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false;},close(stream){stream.tty.ops.fsync(stream.tty);},fsync(stream){stream.tty.ops.fsync(stream.tty);},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[];}},ioctl_tcgets(tty){return {c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return [24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[];}else {if(val!=0)tty.output.push(val);}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[];}}}};var zeroMemory=(address,size)=>{HEAPU8.fill(0,address,address+size);return address};var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(!ptr)return 0;return zeroMemory(ptr,size)};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={};}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null;}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream;}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream;}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp;}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;}else {var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));}node.usedBytes=newSize;}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096;}else if(FS.isFile(node.mode)){attr.size=node.usedBytes;}else if(FS.isLink(node.mode)){attr.size=node.link.length;}else {attr.size=0;}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode;}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp;}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size);}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name);}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now();},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now();},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key);}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset);}else {for(var i=0;i0||position+length{var dep=getUniqueRunDependency(`al ${url}`);readAsync(url).then(arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency();},err=>{if(onerror){onerror();}else {throw `Loading data file "${url}" failed.`}});if(dep)addRunDependency();};var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn);};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true;}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn);}onload?.();removeRunDependency();}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency();})){return}finish(byteArray);}addRunDependency();if(typeof url=="string"){asyncLoad(url,processData,onerror);}else {processData(url);}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno;}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={};}get object(){return this.node}set object(val){this.node=val;}get isRead(){return (this.flags&2097155)!==1}get isWrite(){return (this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val;}get position(){return this.shared.position}set position(val){this.shared.position=val;}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this;}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146;}get read(){return (this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode;}get write(){return (this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode;}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return {path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return {path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent;}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node;},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next;}else {var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next;}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node);},isRoot(node){return node===node.parent},isMountpoint(node){return !!node.mounted},isFile(mode){return (mode&61440)===32768},isDir(mode){return (mode&61440)===16384},isLink(mode){return (mode&61440)===40960},isChrdev(mode){return (mode&61440)===8192},isBlkdev(mode){return (mode&61440)===24576},isFIFO(mode){return (mode&61440)===4096},isSocket(mode){return (mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w";}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name);}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else {if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd();}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null;},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream);},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops};},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts);}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false;}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`);}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null);}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done);});},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot;}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount);}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current);}current=next;}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1);},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path;},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user");},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength;}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp");},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd");},createStandardStreams(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"]);}else {FS.symlink("/dev/tty","/dev/stdin");}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"]);}else {FS.symlink("/dev/tty","/dev/stdout");}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"]);}else {FS.symlink("/dev/tty1","/dev/stderr");}FS.open("/dev/stdin",0);FS.open("/dev/stdout",1);FS.open("/dev/stderr",1);},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack="";});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS:MEMFS};},init(input,output,error){FS.init.initialized=true;Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams();},quit(){FS.init.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter;}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined");}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end);}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed");}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true;}get length(){if(!this.lengthKnown){this.cacheLength();}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength();}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray};}else {var properties={isDevice:false,url:url};}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents;}else if(properties.url){node.contents=null;node.url=properties.url;}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)};});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return {ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd();}else {var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path;}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+24>>2]=tempI64[0],HEAP32[buf+28>>2]=tempI64[1];HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAPU32[buf+48>>2]=atime%1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+56>>2]=tempI64[0],HEAP32[buf+60>>2]=tempI64[1];HEAPU32[buf+64>>2]=mtime%1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+72>>2]=tempI64[0],HEAP32[buf+76>>2]=tempI64[1];HEAPU32[buf+80>>2]=ctime%1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+88>>2]=tempI64[0],HEAP32[buf+92>>2]=tempI64[1];return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags);},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_chmod(path,mode){try{path=SYSCALLS.getStr(path);FS.chmod(path,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return -28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return -44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return -2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function ___syscall_fchmod(fd,mode){try{FS.fchmod(fd,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function ___syscall_fchown32(fd,owner,group){try{FS.fchown(fd,owner,group);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function syscallGetVarargI(){var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret}var syscallGetVarargP=syscallGetVarargI;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return -28}while(FS.streams[arg]){arg++;}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return -28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function ___syscall_ftruncate64(fd,length_low,length_high){var length=convertI32PairToI53Checked(length_low,length_high);try{if(isNaN(length))return 61;FS.ftruncate(fd,length);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return -28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(sizeHEAPU32[ptr>>2]+HEAP32[ptr+4>>2]*4294967296;function ___syscall_utimensat(dirfd,path,times,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path,true);if(!times){var atime=Date.now();var mtime=atime;}else {var seconds=readI53FromI64(times);var nanoseconds=HEAP32[times+8>>2];atime=seconds*1e3+nanoseconds/(1e3*1e3);times+=16;seconds=readI53FromI64(times);nanoseconds=HEAP32[times+8>>2];mtime=seconds*1e3+nanoseconds/(1e3*1e3);}FS.utime(path,atime,mtime);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time_low,time_high,tmPtr){var time=convertI32PairToI53Checked(time_low,time_high);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst;}function __mmap_js(len,prot,flags,fd,offset_low,offset_high,allocated,addr){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset_low,offset_high){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset);}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return -e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=date=>date.toLocaleTimeString(undefined,{hour12:false,timeZoneName:"short"}).split(" ")[1];var winterName=extractZone(winter);var summerName=extractZone(summer);if(summerOffsetDate.now();var _emscripten_get_now;_emscripten_get_now=()=>performance.now();var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x];}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`);}getEnvStrings.strings=strings;}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1;});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4;}HEAP8[pbuf]=type;HEAP16[pbuf+2>>1]=flags;tempI64=[rightsBase>>>0,(tempDouble=rightsBase,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[pbuf+8>>2]=tempI64[0],HEAP32[pbuf+12>>2]=tempI64[1];tempI64=[rightsInheriting>>>0,(tempDouble=rightsInheriting,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[pbuf+16>>2]=tempI64[0],HEAP32[pbuf+20>>2]=tempI64[1];return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return -1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_sync(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);if(stream.stream_ops?.fsync){return stream.stream_ops.fsync(stream)}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return -1;ret+=curr;}return ret};function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var adapters_support=function(){const handleAsync=typeof Asyncify==="object"?Asyncify.handleAsync.bind(Asyncify):null;Module["handleAsync"]=handleAsync;const targets=new Map;Module["setCallback"]=(key,target)=>targets.set(key,target);Module["getCallback"]=key=>targets.get(key);Module["deleteCallback"]=key=>targets.delete(key);adapters_support=function(isAsync,key,...args){const receiver=targets.get(key);let methodName=null;const f=typeof receiver==="function"?receiver:receiver[methodName=UTF8ToString(args.shift())];if(isAsync){if(handleAsync){return handleAsync(()=>f.apply(receiver,args))}throw new Error("Synchronous WebAssembly cannot call async function")}const result=f.apply(receiver,args);if(typeof result?.then=="function"){console.error("unexpected Promise",f);throw new Error(`${methodName} unexpectedly returned a Promise`)}return result};};function _ipp(...args){return adapters_support(false,...args)}function _ipp_async(...args){return adapters_support(true,...args)}function _ippipppp(...args){return adapters_support(false,...args)}function _ippipppp_async(...args){return adapters_support(true,...args)}function _ippp(...args){return adapters_support(false,...args)}function _ippp_async(...args){return adapters_support(true,...args)}function _ipppi(...args){return adapters_support(false,...args)}function _ipppi_async(...args){return adapters_support(true,...args)}function _ipppiii(...args){return adapters_support(false,...args)}function _ipppiii_async(...args){return adapters_support(true,...args)}function _ipppiiip(...args){return adapters_support(false,...args)}function _ipppiiip_async(...args){return adapters_support(true,...args)}function _ipppip(...args){return adapters_support(false,...args)}function _ipppip_async(...args){return adapters_support(true,...args)}function _ipppj(...args){return adapters_support(false,...args)}function _ipppj_async(...args){return adapters_support(true,...args)}function _ipppp(...args){return adapters_support(false,...args)}function _ipppp_async(...args){return adapters_support(true,...args)}function _ippppi(...args){return adapters_support(false,...args)}function _ippppi_async(...args){return adapters_support(true,...args)}function _ippppij(...args){return adapters_support(false,...args)}function _ippppij_async(...args){return adapters_support(true,...args)}function _ippppip(...args){return adapters_support(false,...args)}function _ippppip_async(...args){return adapters_support(true,...args)}function _ipppppip(...args){return adapters_support(false,...args)}function _ipppppip_async(...args){return adapters_support(true,...args)}function _vppp(...args){return adapters_support(false,...args)}function _vppp_async(...args){return adapters_support(true,...args)}function _vpppip(...args){return adapters_support(false,...args)}function _vpppip_async(...args){return adapters_support(true,...args)}var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true;}quit_(code,new ExitStatus(code));};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status);};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e);};var uleb128Encode=(n,target)=>{if(n<128){target.push(n);}else {target.push(n%128|128,n>>7);}};var sigToWasmTypes=sig=>{var typeNames={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i{var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={i:127,p:127,j:126,f:125,d:124,e:111};target.push(96);uleb128Encode(sigParam.length,target);for(var i=0;i{if(typeof WebAssembly.Function=="function"){return new WebAssembly.Function(sigToWasmTypes(sig),func)}var typeSectionBody=[1];generateFuncType(sig,typeSectionBody);var bytes=[0,97,115,109,1,0,0,0,1];uleb128Encode(typeSectionBody.length,bytes);bytes.push(...typeSectionBody);bytes.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(new Uint8Array(bytes));var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var wasmTable;var getWasmTableEntry=funcPtr=>wasmTable.get(funcPtr);var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length);}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1);}catch(err){if(!(err instanceof RangeError)){throw err}throw "Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1};var setWasmTableEntry=(idx,func)=>wasmTable.set(idx,func);var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func);}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped);}functionsInTableMap.set(func,ret);return ret};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer);};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str);}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return (...args)=>ccall(ident,returnType,argTypes,args)};var getTempRet0=val=>__emscripten_tempret_get();var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2;}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023;}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch);}};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit);}return str};var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023);}else {str+=String.fromCharCode(utf32);}}return str};function intArrayToString(array){var ret=[];for(var i=0;i255){chr&=255;}ret.push(String.fromCharCode(chr));}return ret.join("")}FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();adapters_support();var wasmImports={a:___assert_fail,Y:___syscall_chmod,$:___syscall_faccessat,Z:___syscall_fchmod,X:___syscall_fchown32,b:___syscall_fcntl64,W:___syscall_fstat64,y:___syscall_ftruncate64,Q:___syscall_getcwd,U:___syscall_lstat64,N:___syscall_mkdirat,T:___syscall_newfstatat,L:___syscall_openat,J:___syscall_readlinkat,I:___syscall_rmdir,V:___syscall_stat64,G:___syscall_unlinkat,F:___syscall_utimensat,w:__localtime_js,u:__mmap_js,v:__munmap_js,M:__tzset_js,n:_emscripten_date_now,m:_emscripten_get_now,D:_emscripten_resize_heap,O:_environ_get,P:_environ_sizes_get,o:_fd_close,E:_fd_fdstat_get,K:_fd_read,x:_fd_seek,S:_fd_sync,H:_fd_write,s:_ipp,t:_ipp_async,fa:_ippipppp,ia:_ippipppp_async,i:_ippp,j:_ippp_async,c:_ipppi,d:_ipppi_async,ca:_ipppiii,da:_ipppiii_async,ea:_ipppiiip,ga:_ipppiiip_async,g:_ipppip,h:_ipppip_async,z:_ipppj,A:_ipppj_async,e:_ipppp,f:_ipppp_async,aa:_ippppi,ba:_ippppi_async,B:_ippppij,C:_ippppij_async,p:_ippppip,q:_ippppip_async,ha:_ipppppip,r:_ipppppip_async,k:_vppp,l:_vppp_async,R:_vpppip,_:_vpppip_async};var wasmExports=createWasm();Module["_sqlite3_status64"]=(a0,a1,a2,a3)=>(Module["_sqlite3_status64"]=wasmExports["la"])(a0,a1,a2,a3);Module["_sqlite3_status"]=(a0,a1,a2,a3)=>(Module["_sqlite3_status"]=wasmExports["ma"])(a0,a1,a2,a3);Module["_sqlite3_db_status"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_db_status"]=wasmExports["na"])(a0,a1,a2,a3,a4);Module["_sqlite3_msize"]=a0=>(Module["_sqlite3_msize"]=wasmExports["oa"])(a0);Module["_sqlite3_vfs_find"]=a0=>(Module["_sqlite3_vfs_find"]=wasmExports["pa"])(a0);Module["_sqlite3_vfs_register"]=(a0,a1)=>(Module["_sqlite3_vfs_register"]=wasmExports["qa"])(a0,a1);Module["_sqlite3_vfs_unregister"]=a0=>(Module["_sqlite3_vfs_unregister"]=wasmExports["ra"])(a0);Module["_sqlite3_release_memory"]=a0=>(Module["_sqlite3_release_memory"]=wasmExports["sa"])(a0);Module["_sqlite3_soft_heap_limit64"]=(a0,a1)=>(Module["_sqlite3_soft_heap_limit64"]=wasmExports["ta"])(a0,a1);Module["_sqlite3_memory_used"]=()=>(Module["_sqlite3_memory_used"]=wasmExports["ua"])();Module["_sqlite3_hard_heap_limit64"]=(a0,a1)=>(Module["_sqlite3_hard_heap_limit64"]=wasmExports["va"])(a0,a1);Module["_sqlite3_memory_highwater"]=a0=>(Module["_sqlite3_memory_highwater"]=wasmExports["wa"])(a0);Module["_sqlite3_malloc"]=a0=>(Module["_sqlite3_malloc"]=wasmExports["xa"])(a0);Module["_sqlite3_malloc64"]=(a0,a1)=>(Module["_sqlite3_malloc64"]=wasmExports["ya"])(a0,a1);Module["_sqlite3_free"]=a0=>(Module["_sqlite3_free"]=wasmExports["za"])(a0);Module["_sqlite3_realloc"]=(a0,a1)=>(Module["_sqlite3_realloc"]=wasmExports["Aa"])(a0,a1);Module["_sqlite3_realloc64"]=(a0,a1,a2)=>(Module["_sqlite3_realloc64"]=wasmExports["Ba"])(a0,a1,a2);Module["_sqlite3_str_vappendf"]=(a0,a1,a2)=>(Module["_sqlite3_str_vappendf"]=wasmExports["Ca"])(a0,a1,a2);Module["_sqlite3_str_append"]=(a0,a1,a2)=>(Module["_sqlite3_str_append"]=wasmExports["Da"])(a0,a1,a2);Module["_sqlite3_str_appendchar"]=(a0,a1,a2)=>(Module["_sqlite3_str_appendchar"]=wasmExports["Ea"])(a0,a1,a2);Module["_sqlite3_str_appendall"]=(a0,a1)=>(Module["_sqlite3_str_appendall"]=wasmExports["Fa"])(a0,a1);Module["_sqlite3_str_appendf"]=(a0,a1,a2)=>(Module["_sqlite3_str_appendf"]=wasmExports["Ga"])(a0,a1,a2);Module["_sqlite3_str_finish"]=a0=>(Module["_sqlite3_str_finish"]=wasmExports["Ha"])(a0);Module["_sqlite3_str_errcode"]=a0=>(Module["_sqlite3_str_errcode"]=wasmExports["Ia"])(a0);Module["_sqlite3_str_length"]=a0=>(Module["_sqlite3_str_length"]=wasmExports["Ja"])(a0);Module["_sqlite3_str_value"]=a0=>(Module["_sqlite3_str_value"]=wasmExports["Ka"])(a0);Module["_sqlite3_str_reset"]=a0=>(Module["_sqlite3_str_reset"]=wasmExports["La"])(a0);Module["_sqlite3_str_new"]=a0=>(Module["_sqlite3_str_new"]=wasmExports["Ma"])(a0);Module["_sqlite3_vmprintf"]=(a0,a1)=>(Module["_sqlite3_vmprintf"]=wasmExports["Na"])(a0,a1);Module["_sqlite3_mprintf"]=(a0,a1)=>(Module["_sqlite3_mprintf"]=wasmExports["Oa"])(a0,a1);Module["_sqlite3_vsnprintf"]=(a0,a1,a2,a3)=>(Module["_sqlite3_vsnprintf"]=wasmExports["Pa"])(a0,a1,a2,a3);Module["_sqlite3_snprintf"]=(a0,a1,a2,a3)=>(Module["_sqlite3_snprintf"]=wasmExports["Qa"])(a0,a1,a2,a3);Module["_sqlite3_log"]=(a0,a1,a2)=>(Module["_sqlite3_log"]=wasmExports["Ra"])(a0,a1,a2);Module["_sqlite3_randomness"]=(a0,a1)=>(Module["_sqlite3_randomness"]=wasmExports["Sa"])(a0,a1);Module["_sqlite3_stricmp"]=(a0,a1)=>(Module["_sqlite3_stricmp"]=wasmExports["Ta"])(a0,a1);Module["_sqlite3_strnicmp"]=(a0,a1,a2)=>(Module["_sqlite3_strnicmp"]=wasmExports["Ua"])(a0,a1,a2);Module["_sqlite3_os_init"]=()=>(Module["_sqlite3_os_init"]=wasmExports["Va"])();Module["_sqlite3_os_end"]=()=>(Module["_sqlite3_os_end"]=wasmExports["Wa"])();Module["_sqlite3_serialize"]=(a0,a1,a2,a3)=>(Module["_sqlite3_serialize"]=wasmExports["Xa"])(a0,a1,a2,a3);Module["_sqlite3_prepare_v2"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_prepare_v2"]=wasmExports["Ya"])(a0,a1,a2,a3,a4);Module["_sqlite3_step"]=a0=>(Module["_sqlite3_step"]=wasmExports["Za"])(a0);Module["_sqlite3_column_int64"]=(a0,a1)=>(Module["_sqlite3_column_int64"]=wasmExports["_a"])(a0,a1);Module["_sqlite3_reset"]=a0=>(Module["_sqlite3_reset"]=wasmExports["$a"])(a0);Module["_sqlite3_exec"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_exec"]=wasmExports["ab"])(a0,a1,a2,a3,a4);Module["_sqlite3_column_int"]=(a0,a1)=>(Module["_sqlite3_column_int"]=wasmExports["bb"])(a0,a1);Module["_sqlite3_finalize"]=a0=>(Module["_sqlite3_finalize"]=wasmExports["cb"])(a0);Module["_sqlite3_deserialize"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(Module["_sqlite3_deserialize"]=wasmExports["db"])(a0,a1,a2,a3,a4,a5,a6,a7);Module["_sqlite3_database_file_object"]=a0=>(Module["_sqlite3_database_file_object"]=wasmExports["eb"])(a0);Module["_sqlite3_backup_init"]=(a0,a1,a2,a3)=>(Module["_sqlite3_backup_init"]=wasmExports["fb"])(a0,a1,a2,a3);Module["_sqlite3_backup_step"]=(a0,a1)=>(Module["_sqlite3_backup_step"]=wasmExports["gb"])(a0,a1);Module["_sqlite3_backup_finish"]=a0=>(Module["_sqlite3_backup_finish"]=wasmExports["hb"])(a0);Module["_sqlite3_backup_remaining"]=a0=>(Module["_sqlite3_backup_remaining"]=wasmExports["ib"])(a0);Module["_sqlite3_backup_pagecount"]=a0=>(Module["_sqlite3_backup_pagecount"]=wasmExports["jb"])(a0);Module["_sqlite3_clear_bindings"]=a0=>(Module["_sqlite3_clear_bindings"]=wasmExports["kb"])(a0);Module["_sqlite3_value_blob"]=a0=>(Module["_sqlite3_value_blob"]=wasmExports["lb"])(a0);Module["_sqlite3_value_text"]=a0=>(Module["_sqlite3_value_text"]=wasmExports["mb"])(a0);Module["_sqlite3_value_bytes"]=a0=>(Module["_sqlite3_value_bytes"]=wasmExports["nb"])(a0);Module["_sqlite3_value_bytes16"]=a0=>(Module["_sqlite3_value_bytes16"]=wasmExports["ob"])(a0);Module["_sqlite3_value_double"]=a0=>(Module["_sqlite3_value_double"]=wasmExports["pb"])(a0);Module["_sqlite3_value_int"]=a0=>(Module["_sqlite3_value_int"]=wasmExports["qb"])(a0);Module["_sqlite3_value_int64"]=a0=>(Module["_sqlite3_value_int64"]=wasmExports["rb"])(a0);Module["_sqlite3_value_subtype"]=a0=>(Module["_sqlite3_value_subtype"]=wasmExports["sb"])(a0);Module["_sqlite3_value_pointer"]=(a0,a1)=>(Module["_sqlite3_value_pointer"]=wasmExports["tb"])(a0,a1);Module["_sqlite3_value_text16"]=a0=>(Module["_sqlite3_value_text16"]=wasmExports["ub"])(a0);Module["_sqlite3_value_text16be"]=a0=>(Module["_sqlite3_value_text16be"]=wasmExports["vb"])(a0);Module["_sqlite3_value_text16le"]=a0=>(Module["_sqlite3_value_text16le"]=wasmExports["wb"])(a0);Module["_sqlite3_value_type"]=a0=>(Module["_sqlite3_value_type"]=wasmExports["xb"])(a0);Module["_sqlite3_value_encoding"]=a0=>(Module["_sqlite3_value_encoding"]=wasmExports["yb"])(a0);Module["_sqlite3_value_nochange"]=a0=>(Module["_sqlite3_value_nochange"]=wasmExports["zb"])(a0);Module["_sqlite3_value_frombind"]=a0=>(Module["_sqlite3_value_frombind"]=wasmExports["Ab"])(a0);Module["_sqlite3_value_dup"]=a0=>(Module["_sqlite3_value_dup"]=wasmExports["Bb"])(a0);Module["_sqlite3_value_free"]=a0=>(Module["_sqlite3_value_free"]=wasmExports["Cb"])(a0);Module["_sqlite3_result_blob"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_blob"]=wasmExports["Db"])(a0,a1,a2,a3);Module["_sqlite3_result_blob64"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_result_blob64"]=wasmExports["Eb"])(a0,a1,a2,a3,a4);Module["_sqlite3_result_double"]=(a0,a1)=>(Module["_sqlite3_result_double"]=wasmExports["Fb"])(a0,a1);Module["_sqlite3_result_error"]=(a0,a1,a2)=>(Module["_sqlite3_result_error"]=wasmExports["Gb"])(a0,a1,a2);Module["_sqlite3_result_error16"]=(a0,a1,a2)=>(Module["_sqlite3_result_error16"]=wasmExports["Hb"])(a0,a1,a2);Module["_sqlite3_result_int"]=(a0,a1)=>(Module["_sqlite3_result_int"]=wasmExports["Ib"])(a0,a1);Module["_sqlite3_result_int64"]=(a0,a1,a2)=>(Module["_sqlite3_result_int64"]=wasmExports["Jb"])(a0,a1,a2);Module["_sqlite3_result_null"]=a0=>(Module["_sqlite3_result_null"]=wasmExports["Kb"])(a0);Module["_sqlite3_result_pointer"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_pointer"]=wasmExports["Lb"])(a0,a1,a2,a3);Module["_sqlite3_result_subtype"]=(a0,a1)=>(Module["_sqlite3_result_subtype"]=wasmExports["Mb"])(a0,a1);Module["_sqlite3_result_text"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_text"]=wasmExports["Nb"])(a0,a1,a2,a3);Module["_sqlite3_result_text64"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_result_text64"]=wasmExports["Ob"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_result_text16"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_text16"]=wasmExports["Pb"])(a0,a1,a2,a3);Module["_sqlite3_result_text16be"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_text16be"]=wasmExports["Qb"])(a0,a1,a2,a3);Module["_sqlite3_result_text16le"]=(a0,a1,a2,a3)=>(Module["_sqlite3_result_text16le"]=wasmExports["Rb"])(a0,a1,a2,a3);Module["_sqlite3_result_value"]=(a0,a1)=>(Module["_sqlite3_result_value"]=wasmExports["Sb"])(a0,a1);Module["_sqlite3_result_error_toobig"]=a0=>(Module["_sqlite3_result_error_toobig"]=wasmExports["Tb"])(a0);Module["_sqlite3_result_zeroblob"]=(a0,a1)=>(Module["_sqlite3_result_zeroblob"]=wasmExports["Ub"])(a0,a1);Module["_sqlite3_result_zeroblob64"]=(a0,a1,a2)=>(Module["_sqlite3_result_zeroblob64"]=wasmExports["Vb"])(a0,a1,a2);Module["_sqlite3_result_error_code"]=(a0,a1)=>(Module["_sqlite3_result_error_code"]=wasmExports["Wb"])(a0,a1);Module["_sqlite3_result_error_nomem"]=a0=>(Module["_sqlite3_result_error_nomem"]=wasmExports["Xb"])(a0);Module["_sqlite3_user_data"]=a0=>(Module["_sqlite3_user_data"]=wasmExports["Yb"])(a0);Module["_sqlite3_context_db_handle"]=a0=>(Module["_sqlite3_context_db_handle"]=wasmExports["Zb"])(a0);Module["_sqlite3_vtab_nochange"]=a0=>(Module["_sqlite3_vtab_nochange"]=wasmExports["_b"])(a0);Module["_sqlite3_vtab_in_first"]=(a0,a1)=>(Module["_sqlite3_vtab_in_first"]=wasmExports["$b"])(a0,a1);Module["_sqlite3_vtab_in_next"]=(a0,a1)=>(Module["_sqlite3_vtab_in_next"]=wasmExports["ac"])(a0,a1);Module["_sqlite3_aggregate_context"]=(a0,a1)=>(Module["_sqlite3_aggregate_context"]=wasmExports["bc"])(a0,a1);Module["_sqlite3_get_auxdata"]=(a0,a1)=>(Module["_sqlite3_get_auxdata"]=wasmExports["cc"])(a0,a1);Module["_sqlite3_set_auxdata"]=(a0,a1,a2,a3)=>(Module["_sqlite3_set_auxdata"]=wasmExports["dc"])(a0,a1,a2,a3);Module["_sqlite3_column_count"]=a0=>(Module["_sqlite3_column_count"]=wasmExports["ec"])(a0);Module["_sqlite3_data_count"]=a0=>(Module["_sqlite3_data_count"]=wasmExports["fc"])(a0);Module["_sqlite3_column_blob"]=(a0,a1)=>(Module["_sqlite3_column_blob"]=wasmExports["gc"])(a0,a1);Module["_sqlite3_column_bytes"]=(a0,a1)=>(Module["_sqlite3_column_bytes"]=wasmExports["hc"])(a0,a1);Module["_sqlite3_column_bytes16"]=(a0,a1)=>(Module["_sqlite3_column_bytes16"]=wasmExports["ic"])(a0,a1);Module["_sqlite3_column_double"]=(a0,a1)=>(Module["_sqlite3_column_double"]=wasmExports["jc"])(a0,a1);Module["_sqlite3_column_text"]=(a0,a1)=>(Module["_sqlite3_column_text"]=wasmExports["kc"])(a0,a1);Module["_sqlite3_column_value"]=(a0,a1)=>(Module["_sqlite3_column_value"]=wasmExports["lc"])(a0,a1);Module["_sqlite3_column_text16"]=(a0,a1)=>(Module["_sqlite3_column_text16"]=wasmExports["mc"])(a0,a1);Module["_sqlite3_column_type"]=(a0,a1)=>(Module["_sqlite3_column_type"]=wasmExports["nc"])(a0,a1);Module["_sqlite3_column_name"]=(a0,a1)=>(Module["_sqlite3_column_name"]=wasmExports["oc"])(a0,a1);Module["_sqlite3_column_name16"]=(a0,a1)=>(Module["_sqlite3_column_name16"]=wasmExports["pc"])(a0,a1);Module["_sqlite3_bind_blob"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_bind_blob"]=wasmExports["qc"])(a0,a1,a2,a3,a4);Module["_sqlite3_bind_blob64"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_bind_blob64"]=wasmExports["rc"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_bind_double"]=(a0,a1,a2)=>(Module["_sqlite3_bind_double"]=wasmExports["sc"])(a0,a1,a2);Module["_sqlite3_bind_int"]=(a0,a1,a2)=>(Module["_sqlite3_bind_int"]=wasmExports["tc"])(a0,a1,a2);Module["_sqlite3_bind_int64"]=(a0,a1,a2,a3)=>(Module["_sqlite3_bind_int64"]=wasmExports["uc"])(a0,a1,a2,a3);Module["_sqlite3_bind_null"]=(a0,a1)=>(Module["_sqlite3_bind_null"]=wasmExports["vc"])(a0,a1);Module["_sqlite3_bind_pointer"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_bind_pointer"]=wasmExports["wc"])(a0,a1,a2,a3,a4);Module["_sqlite3_bind_text"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_bind_text"]=wasmExports["xc"])(a0,a1,a2,a3,a4);Module["_sqlite3_bind_text64"]=(a0,a1,a2,a3,a4,a5,a6)=>(Module["_sqlite3_bind_text64"]=wasmExports["yc"])(a0,a1,a2,a3,a4,a5,a6);Module["_sqlite3_bind_text16"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_bind_text16"]=wasmExports["zc"])(a0,a1,a2,a3,a4);Module["_sqlite3_bind_value"]=(a0,a1,a2)=>(Module["_sqlite3_bind_value"]=wasmExports["Ac"])(a0,a1,a2);Module["_sqlite3_bind_zeroblob"]=(a0,a1,a2)=>(Module["_sqlite3_bind_zeroblob"]=wasmExports["Bc"])(a0,a1,a2);Module["_sqlite3_bind_zeroblob64"]=(a0,a1,a2,a3)=>(Module["_sqlite3_bind_zeroblob64"]=wasmExports["Cc"])(a0,a1,a2,a3);Module["_sqlite3_bind_parameter_count"]=a0=>(Module["_sqlite3_bind_parameter_count"]=wasmExports["Dc"])(a0);Module["_sqlite3_bind_parameter_name"]=(a0,a1)=>(Module["_sqlite3_bind_parameter_name"]=wasmExports["Ec"])(a0,a1);Module["_sqlite3_bind_parameter_index"]=(a0,a1)=>(Module["_sqlite3_bind_parameter_index"]=wasmExports["Fc"])(a0,a1);Module["_sqlite3_db_handle"]=a0=>(Module["_sqlite3_db_handle"]=wasmExports["Gc"])(a0);Module["_sqlite3_stmt_readonly"]=a0=>(Module["_sqlite3_stmt_readonly"]=wasmExports["Hc"])(a0);Module["_sqlite3_stmt_isexplain"]=a0=>(Module["_sqlite3_stmt_isexplain"]=wasmExports["Ic"])(a0);Module["_sqlite3_stmt_explain"]=(a0,a1)=>(Module["_sqlite3_stmt_explain"]=wasmExports["Jc"])(a0,a1);Module["_sqlite3_stmt_busy"]=a0=>(Module["_sqlite3_stmt_busy"]=wasmExports["Kc"])(a0);Module["_sqlite3_next_stmt"]=(a0,a1)=>(Module["_sqlite3_next_stmt"]=wasmExports["Lc"])(a0,a1);Module["_sqlite3_stmt_status"]=(a0,a1,a2)=>(Module["_sqlite3_stmt_status"]=wasmExports["Mc"])(a0,a1,a2);Module["_sqlite3_sql"]=a0=>(Module["_sqlite3_sql"]=wasmExports["Nc"])(a0);Module["_sqlite3_expanded_sql"]=a0=>(Module["_sqlite3_expanded_sql"]=wasmExports["Oc"])(a0);Module["_sqlite3_value_numeric_type"]=a0=>(Module["_sqlite3_value_numeric_type"]=wasmExports["Pc"])(a0);Module["_sqlite3_blob_open"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(Module["_sqlite3_blob_open"]=wasmExports["Qc"])(a0,a1,a2,a3,a4,a5,a6,a7);Module["_sqlite3_blob_close"]=a0=>(Module["_sqlite3_blob_close"]=wasmExports["Rc"])(a0);Module["_sqlite3_blob_read"]=(a0,a1,a2,a3)=>(Module["_sqlite3_blob_read"]=wasmExports["Sc"])(a0,a1,a2,a3);Module["_sqlite3_blob_write"]=(a0,a1,a2,a3)=>(Module["_sqlite3_blob_write"]=wasmExports["Tc"])(a0,a1,a2,a3);Module["_sqlite3_blob_bytes"]=a0=>(Module["_sqlite3_blob_bytes"]=wasmExports["Uc"])(a0);Module["_sqlite3_blob_reopen"]=(a0,a1,a2)=>(Module["_sqlite3_blob_reopen"]=wasmExports["Vc"])(a0,a1,a2);Module["_sqlite3_set_authorizer"]=(a0,a1,a2)=>(Module["_sqlite3_set_authorizer"]=wasmExports["Wc"])(a0,a1,a2);Module["_sqlite3_strglob"]=(a0,a1)=>(Module["_sqlite3_strglob"]=wasmExports["Xc"])(a0,a1);Module["_sqlite3_strlike"]=(a0,a1,a2)=>(Module["_sqlite3_strlike"]=wasmExports["Yc"])(a0,a1,a2);Module["_sqlite3_errmsg"]=a0=>(Module["_sqlite3_errmsg"]=wasmExports["Zc"])(a0);Module["_sqlite3_auto_extension"]=a0=>(Module["_sqlite3_auto_extension"]=wasmExports["_c"])(a0);Module["_sqlite3_cancel_auto_extension"]=a0=>(Module["_sqlite3_cancel_auto_extension"]=wasmExports["$c"])(a0);Module["_sqlite3_reset_auto_extension"]=()=>(Module["_sqlite3_reset_auto_extension"]=wasmExports["ad"])();Module["_sqlite3_prepare"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_prepare"]=wasmExports["bd"])(a0,a1,a2,a3,a4);Module["_sqlite3_prepare_v3"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_prepare_v3"]=wasmExports["cd"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_prepare16"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_prepare16"]=wasmExports["dd"])(a0,a1,a2,a3,a4);Module["_sqlite3_prepare16_v2"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_prepare16_v2"]=wasmExports["ed"])(a0,a1,a2,a3,a4);Module["_sqlite3_prepare16_v3"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_prepare16_v3"]=wasmExports["fd"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_get_table"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_get_table"]=wasmExports["gd"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_free_table"]=a0=>(Module["_sqlite3_free_table"]=wasmExports["hd"])(a0);Module["_sqlite3_create_module"]=(a0,a1,a2,a3)=>(Module["_sqlite3_create_module"]=wasmExports["id"])(a0,a1,a2,a3);Module["_sqlite3_create_module_v2"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_create_module_v2"]=wasmExports["jd"])(a0,a1,a2,a3,a4);Module["_sqlite3_drop_modules"]=(a0,a1)=>(Module["_sqlite3_drop_modules"]=wasmExports["kd"])(a0,a1);Module["_sqlite3_declare_vtab"]=(a0,a1)=>(Module["_sqlite3_declare_vtab"]=wasmExports["ld"])(a0,a1);Module["_sqlite3_vtab_on_conflict"]=a0=>(Module["_sqlite3_vtab_on_conflict"]=wasmExports["md"])(a0);Module["_sqlite3_vtab_config"]=(a0,a1,a2)=>(Module["_sqlite3_vtab_config"]=wasmExports["nd"])(a0,a1,a2);Module["_sqlite3_vtab_collation"]=(a0,a1)=>(Module["_sqlite3_vtab_collation"]=wasmExports["od"])(a0,a1);Module["_sqlite3_vtab_in"]=(a0,a1,a2)=>(Module["_sqlite3_vtab_in"]=wasmExports["pd"])(a0,a1,a2);Module["_sqlite3_vtab_rhs_value"]=(a0,a1,a2)=>(Module["_sqlite3_vtab_rhs_value"]=wasmExports["qd"])(a0,a1,a2);Module["_sqlite3_vtab_distinct"]=a0=>(Module["_sqlite3_vtab_distinct"]=wasmExports["rd"])(a0);Module["_sqlite3_keyword_name"]=(a0,a1,a2)=>(Module["_sqlite3_keyword_name"]=wasmExports["sd"])(a0,a1,a2);Module["_sqlite3_keyword_count"]=()=>(Module["_sqlite3_keyword_count"]=wasmExports["td"])();Module["_sqlite3_keyword_check"]=(a0,a1)=>(Module["_sqlite3_keyword_check"]=wasmExports["ud"])(a0,a1);Module["_sqlite3_complete"]=a0=>(Module["_sqlite3_complete"]=wasmExports["vd"])(a0);Module["_sqlite3_complete16"]=a0=>(Module["_sqlite3_complete16"]=wasmExports["wd"])(a0);Module["_sqlite3_libversion"]=()=>(Module["_sqlite3_libversion"]=wasmExports["xd"])();Module["_sqlite3_libversion_number"]=()=>(Module["_sqlite3_libversion_number"]=wasmExports["yd"])();Module["_sqlite3_threadsafe"]=()=>(Module["_sqlite3_threadsafe"]=wasmExports["zd"])();Module["_sqlite3_initialize"]=()=>(Module["_sqlite3_initialize"]=wasmExports["Ad"])();Module["_sqlite3_shutdown"]=()=>(Module["_sqlite3_shutdown"]=wasmExports["Bd"])();Module["_sqlite3_config"]=(a0,a1)=>(Module["_sqlite3_config"]=wasmExports["Cd"])(a0,a1);Module["_sqlite3_db_mutex"]=a0=>(Module["_sqlite3_db_mutex"]=wasmExports["Dd"])(a0);Module["_sqlite3_db_release_memory"]=a0=>(Module["_sqlite3_db_release_memory"]=wasmExports["Ed"])(a0);Module["_sqlite3_db_cacheflush"]=a0=>(Module["_sqlite3_db_cacheflush"]=wasmExports["Fd"])(a0);Module["_sqlite3_db_config"]=(a0,a1,a2)=>(Module["_sqlite3_db_config"]=wasmExports["Gd"])(a0,a1,a2);Module["_sqlite3_last_insert_rowid"]=a0=>(Module["_sqlite3_last_insert_rowid"]=wasmExports["Hd"])(a0);Module["_sqlite3_set_last_insert_rowid"]=(a0,a1,a2)=>(Module["_sqlite3_set_last_insert_rowid"]=wasmExports["Id"])(a0,a1,a2);Module["_sqlite3_changes64"]=a0=>(Module["_sqlite3_changes64"]=wasmExports["Jd"])(a0);Module["_sqlite3_changes"]=a0=>(Module["_sqlite3_changes"]=wasmExports["Kd"])(a0);Module["_sqlite3_total_changes64"]=a0=>(Module["_sqlite3_total_changes64"]=wasmExports["Ld"])(a0);Module["_sqlite3_total_changes"]=a0=>(Module["_sqlite3_total_changes"]=wasmExports["Md"])(a0);Module["_sqlite3_txn_state"]=(a0,a1)=>(Module["_sqlite3_txn_state"]=wasmExports["Nd"])(a0,a1);Module["_sqlite3_close"]=a0=>(Module["_sqlite3_close"]=wasmExports["Od"])(a0);Module["_sqlite3_close_v2"]=a0=>(Module["_sqlite3_close_v2"]=wasmExports["Pd"])(a0);Module["_sqlite3_busy_handler"]=(a0,a1,a2)=>(Module["_sqlite3_busy_handler"]=wasmExports["Qd"])(a0,a1,a2);Module["_sqlite3_progress_handler"]=(a0,a1,a2,a3)=>(Module["_sqlite3_progress_handler"]=wasmExports["Rd"])(a0,a1,a2,a3);Module["_sqlite3_busy_timeout"]=(a0,a1)=>(Module["_sqlite3_busy_timeout"]=wasmExports["Sd"])(a0,a1);Module["_sqlite3_interrupt"]=a0=>(Module["_sqlite3_interrupt"]=wasmExports["Td"])(a0);Module["_sqlite3_is_interrupted"]=a0=>(Module["_sqlite3_is_interrupted"]=wasmExports["Ud"])(a0);Module["_sqlite3_create_function"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(Module["_sqlite3_create_function"]=wasmExports["Vd"])(a0,a1,a2,a3,a4,a5,a6,a7);Module["_sqlite3_create_function_v2"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(Module["_sqlite3_create_function_v2"]=wasmExports["Wd"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);Module["_sqlite3_create_window_function"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)=>(Module["_sqlite3_create_window_function"]=wasmExports["Xd"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);Module["_sqlite3_create_function16"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(Module["_sqlite3_create_function16"]=wasmExports["Yd"])(a0,a1,a2,a3,a4,a5,a6,a7);Module["_sqlite3_overload_function"]=(a0,a1,a2)=>(Module["_sqlite3_overload_function"]=wasmExports["Zd"])(a0,a1,a2);Module["_sqlite3_trace_v2"]=(a0,a1,a2,a3)=>(Module["_sqlite3_trace_v2"]=wasmExports["_d"])(a0,a1,a2,a3);Module["_sqlite3_commit_hook"]=(a0,a1,a2)=>(Module["_sqlite3_commit_hook"]=wasmExports["$d"])(a0,a1,a2);Module["_sqlite3_update_hook"]=(a0,a1,a2)=>(Module["_sqlite3_update_hook"]=wasmExports["ae"])(a0,a1,a2);Module["_sqlite3_rollback_hook"]=(a0,a1,a2)=>(Module["_sqlite3_rollback_hook"]=wasmExports["be"])(a0,a1,a2);Module["_sqlite3_autovacuum_pages"]=(a0,a1,a2,a3)=>(Module["_sqlite3_autovacuum_pages"]=wasmExports["ce"])(a0,a1,a2,a3);Module["_sqlite3_wal_autocheckpoint"]=(a0,a1)=>(Module["_sqlite3_wal_autocheckpoint"]=wasmExports["de"])(a0,a1);Module["_sqlite3_wal_hook"]=(a0,a1,a2)=>(Module["_sqlite3_wal_hook"]=wasmExports["ee"])(a0,a1,a2);Module["_sqlite3_wal_checkpoint_v2"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_wal_checkpoint_v2"]=wasmExports["fe"])(a0,a1,a2,a3,a4);Module["_sqlite3_wal_checkpoint"]=(a0,a1)=>(Module["_sqlite3_wal_checkpoint"]=wasmExports["ge"])(a0,a1);Module["_sqlite3_error_offset"]=a0=>(Module["_sqlite3_error_offset"]=wasmExports["he"])(a0);Module["_sqlite3_errmsg16"]=a0=>(Module["_sqlite3_errmsg16"]=wasmExports["ie"])(a0);Module["_sqlite3_errcode"]=a0=>(Module["_sqlite3_errcode"]=wasmExports["je"])(a0);Module["_sqlite3_extended_errcode"]=a0=>(Module["_sqlite3_extended_errcode"]=wasmExports["ke"])(a0);Module["_sqlite3_system_errno"]=a0=>(Module["_sqlite3_system_errno"]=wasmExports["le"])(a0);Module["_sqlite3_errstr"]=a0=>(Module["_sqlite3_errstr"]=wasmExports["me"])(a0);Module["_sqlite3_limit"]=(a0,a1,a2)=>(Module["_sqlite3_limit"]=wasmExports["ne"])(a0,a1,a2);Module["_sqlite3_open"]=(a0,a1)=>(Module["_sqlite3_open"]=wasmExports["oe"])(a0,a1);Module["_sqlite3_open_v2"]=(a0,a1,a2,a3)=>(Module["_sqlite3_open_v2"]=wasmExports["pe"])(a0,a1,a2,a3);Module["_sqlite3_open16"]=(a0,a1)=>(Module["_sqlite3_open16"]=wasmExports["qe"])(a0,a1);Module["_sqlite3_create_collation"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_create_collation"]=wasmExports["re"])(a0,a1,a2,a3,a4);Module["_sqlite3_create_collation_v2"]=(a0,a1,a2,a3,a4,a5)=>(Module["_sqlite3_create_collation_v2"]=wasmExports["se"])(a0,a1,a2,a3,a4,a5);Module["_sqlite3_create_collation16"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_create_collation16"]=wasmExports["te"])(a0,a1,a2,a3,a4);Module["_sqlite3_collation_needed"]=(a0,a1,a2)=>(Module["_sqlite3_collation_needed"]=wasmExports["ue"])(a0,a1,a2);Module["_sqlite3_collation_needed16"]=(a0,a1,a2)=>(Module["_sqlite3_collation_needed16"]=wasmExports["ve"])(a0,a1,a2);Module["_sqlite3_get_clientdata"]=(a0,a1)=>(Module["_sqlite3_get_clientdata"]=wasmExports["we"])(a0,a1);Module["_sqlite3_set_clientdata"]=(a0,a1,a2,a3)=>(Module["_sqlite3_set_clientdata"]=wasmExports["xe"])(a0,a1,a2,a3);Module["_sqlite3_get_autocommit"]=a0=>(Module["_sqlite3_get_autocommit"]=wasmExports["ye"])(a0);Module["_sqlite3_table_column_metadata"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(Module["_sqlite3_table_column_metadata"]=wasmExports["ze"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);Module["_sqlite3_sleep"]=a0=>(Module["_sqlite3_sleep"]=wasmExports["Ae"])(a0);Module["_sqlite3_extended_result_codes"]=(a0,a1)=>(Module["_sqlite3_extended_result_codes"]=wasmExports["Be"])(a0,a1);Module["_sqlite3_file_control"]=(a0,a1,a2,a3)=>(Module["_sqlite3_file_control"]=wasmExports["Ce"])(a0,a1,a2,a3);Module["_sqlite3_test_control"]=(a0,a1)=>(Module["_sqlite3_test_control"]=wasmExports["De"])(a0,a1);Module["_sqlite3_create_filename"]=(a0,a1,a2,a3,a4)=>(Module["_sqlite3_create_filename"]=wasmExports["Ee"])(a0,a1,a2,a3,a4);Module["_sqlite3_free_filename"]=a0=>(Module["_sqlite3_free_filename"]=wasmExports["Fe"])(a0);Module["_sqlite3_uri_parameter"]=(a0,a1)=>(Module["_sqlite3_uri_parameter"]=wasmExports["Ge"])(a0,a1);Module["_sqlite3_uri_key"]=(a0,a1)=>(Module["_sqlite3_uri_key"]=wasmExports["He"])(a0,a1);Module["_sqlite3_uri_boolean"]=(a0,a1,a2)=>(Module["_sqlite3_uri_boolean"]=wasmExports["Ie"])(a0,a1,a2);Module["_sqlite3_uri_int64"]=(a0,a1,a2,a3)=>(Module["_sqlite3_uri_int64"]=wasmExports["Je"])(a0,a1,a2,a3);Module["_sqlite3_filename_database"]=a0=>(Module["_sqlite3_filename_database"]=wasmExports["Ke"])(a0);Module["_sqlite3_filename_journal"]=a0=>(Module["_sqlite3_filename_journal"]=wasmExports["Le"])(a0);Module["_sqlite3_filename_wal"]=a0=>(Module["_sqlite3_filename_wal"]=wasmExports["Me"])(a0);Module["_sqlite3_db_name"]=(a0,a1)=>(Module["_sqlite3_db_name"]=wasmExports["Ne"])(a0,a1);Module["_sqlite3_db_filename"]=(a0,a1)=>(Module["_sqlite3_db_filename"]=wasmExports["Oe"])(a0,a1);Module["_sqlite3_db_readonly"]=(a0,a1)=>(Module["_sqlite3_db_readonly"]=wasmExports["Pe"])(a0,a1);Module["_sqlite3_compileoption_used"]=a0=>(Module["_sqlite3_compileoption_used"]=wasmExports["Qe"])(a0);Module["_sqlite3_compileoption_get"]=a0=>(Module["_sqlite3_compileoption_get"]=wasmExports["Re"])(a0);Module["_sqlite3_sourceid"]=()=>(Module["_sqlite3_sourceid"]=wasmExports["Se"])();Module["_malloc"]=a0=>(Module["_malloc"]=wasmExports["Te"])(a0);Module["_free"]=a0=>(Module["_free"]=wasmExports["Ue"])(a0);Module["_RegisterExtensionFunctions"]=a0=>(Module["_RegisterExtensionFunctions"]=wasmExports["Ve"])(a0);Module["_getSqliteFree"]=()=>(Module["_getSqliteFree"]=wasmExports["We"])();var _main=Module["_main"]=(a0,a1)=>(_main=Module["_main"]=wasmExports["Xe"])(a0,a1);Module["_libauthorizer_set_authorizer"]=(a0,a1,a2)=>(Module["_libauthorizer_set_authorizer"]=wasmExports["Ye"])(a0,a1,a2);Module["_libfunction_create_function"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(Module["_libfunction_create_function"]=wasmExports["Ze"])(a0,a1,a2,a3,a4,a5,a6,a7);Module["_libprogress_progress_handler"]=(a0,a1,a2,a3)=>(Module["_libprogress_progress_handler"]=wasmExports["_e"])(a0,a1,a2,a3);Module["_libvfs_vfs_register"]=(a0,a1,a2,a3,a4,a5)=>(Module["_libvfs_vfs_register"]=wasmExports["$e"])(a0,a1,a2,a3,a4,a5);var _emscripten_builtin_memalign=(a0,a1)=>(_emscripten_builtin_memalign=wasmExports["bf"])(a0,a1);var __emscripten_tempret_get=()=>(__emscripten_tempret_get=wasmExports["cf"])();var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["df"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["ef"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["ff"])();Module["_sqlite3_version"]=5472;Module["getTempRet0"]=getTempRet0;Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;Module["intArrayFromString"]=intArrayFromString;Module["intArrayToString"]=intArrayToString;Module["AsciiToString"]=AsciiToString;Module["UTF16ToString"]=UTF16ToString;Module["stringToUTF16"]=stringToUTF16;Module["UTF32ToString"]=UTF32ToString;Module["stringToUTF32"]=stringToUTF32;Module["writeArrayToMemory"]=writeArrayToMemory;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller;};function callMain(){var entryFunction=_main;var argc=0;var argv=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();if(shouldRunNow)callMain();postRun();}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("");},1);doRun();},1);}else {doRun();}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()();}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;run();(function(){const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;let pAsyncFlags=0;Module["set_authorizer"]=function(db,xAuthorizer,pApp){if(pAsyncFlags){Module["deleteCallback"](pAsyncFlags);Module["_sqlite3_free"](pAsyncFlags);pAsyncFlags=0;}pAsyncFlags=Module["_sqlite3_malloc"](4);setValue(pAsyncFlags,xAuthorizer instanceof AsyncFunction?1:0,"i32");const result=ccall("libauthorizer_set_authorizer","number",["number","number","number"],[db,xAuthorizer?1:0,pAsyncFlags]);if(!result&&xAuthorizer){Module["setCallback"](pAsyncFlags,(_,iAction,p3,p4,p5,p6)=>xAuthorizer(pApp,iAction,p3,p4,p5,p6));}return result};})();(function(){const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;const FUNC_METHODS=["xFunc","xStep","xFinal"];const mapFunctionNameToKey=new Map;Module["create_function"]=function(db,zFunctionName,nArg,eTextRep,pApp,xFunc,xStep,xFinal){const pAsyncFlags=Module["_sqlite3_malloc"](4);const target={xFunc:xFunc,xStep:xStep,xFinal:xFinal};setValue(pAsyncFlags,FUNC_METHODS.reduce((mask,method,i)=>{if(target[method]instanceof AsyncFunction){return mask|1<xProgress(pApp));}};})();(function(){const VFS_METHODS=["xOpen","xDelete","xAccess","xFullPathname","xRandomness","xSleep","xCurrentTime","xGetLastError","xCurrentTimeInt64","xClose","xRead","xWrite","xTruncate","xSync","xFileSize","xLock","xUnlock","xCheckReservedLock","xFileControl","xSectorSize","xDeviceCharacteristics","xShmMap","xShmLock","xShmBarrier","xShmUnmap"];const mapVFSNameToKey=new Map;Module["vfs_register"]=function(vfs,makeDefault){let methodMask=0;let asyncMask=0;VFS_METHODS.forEach((method,i)=>{if(vfs[method]){methodMask|=1< { + console.log(row); + }); + } catch (error) { + console.log("exec err"); + throw error; + } + } + + finalize(stmt) { + try { + return this.sqlite3.finalize(stmt); + } catch (error) { + console.log("stmt error"); + } + } + + changes(db) { + return this.sqlite3.changes(db); + } + + clear_bindings(stmt) { + return this.sqlite3.clear_bindings(stmt); + } + + async close(db) { + try { + return this.sqlite3.close(db); + } catch (error) { + console.log("sqlite3.close error"); + throw error; + } + } + + column(stmt, i) { + return this.sqlite3.column(stmt, i); + } + + async prepare(database, sql, options) { + try { + return await this.sqlite3.statements(database, sql, options); + } catch (error) { + console.log("sqlite prepare error"); + throw error; + } + } + + async step(stmt) { + try { + return await this.sqlite3.step(stmt); + } catch (error) { + console.log("sqlite step error"); + throw error; + } + } + + column_name(stmt, idx) { + return this.sqlite3.column_name(stmt, idx); + } + + column_count(stmt) { + return this.sqlite3.column_count(stmt); + } + + batch_execute(database, query) { + try { + return this.sqlite3.exec(database, query); + console.log("Batch exec'ed"); + } catch { + console.log("exec err"); + } + } + + create_function( + database, + functionName, + nArg, + textRep, + pApp, + xFunc, + xStep, + xFinal, + ) { + try { + this.sqlite3.create_function( + database, + functionName, + nArg, + textRep, + pApp, // pApp is ignored + xFunc, + xStep, + xFinal, + ); + console.log("create function"); + } catch { + console.log("create function err"); + } + } + + register_diesel_sql_functions(database) { + try { + this.sqlite3.create_function( + database, + "diesel_manage_updated_at", + 1, + SQLITE_UTF8, + 0, + (context, values) => { + const table_name = this.sqlite3.value_text(values[0]); + + this.sqlite3.exec( + context, + `CREATE TRIGGER __diesel_manage_updated_at_${table_name} + AFTER UPDATE ON ${table_name} + FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at + BEGIN + UPDATE ${table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; + END`, + (row, columns) => { + console.log(`------------------------------------`); + console.log(`Created trigger for ${table_name}`); + console.log(row); + console.log(columns); + console.log(`------------------------------------`); + }, + ); + }, + ); + } catch (error) { + } + } + + /* + serialize(database, zSchema, size, flags) { + return this.module._sqlite3_serialize(database, zSchema, size, flags); + } + */ +} + +export { SQLite }; diff --git a/diesel-wasm-sqlite/tests/web.rs b/diesel-wasm-sqlite/tests/web.rs new file mode 100755 index 000000000..b8a3a28a5 --- /dev/null +++ b/diesel-wasm-sqlite/tests/web.rs @@ -0,0 +1,61 @@ +#![cfg(target_arch = "wasm32")] + +use diesel::connection::Connection; +use diesel_wasm_sqlite::connection::{AsyncConnection, WasmSqliteConnection}; +use wasm_bindgen_test::*; +use web_sys::console; +wasm_bindgen_test_configure!(run_in_dedicated_worker); +/* +#[wasm_bindgen_test] +async fn test_establish_and_exec() { + let rng: u16 = rand::random(); + let result = WasmSqliteConnection::establish("test-15873").await; + let mut conn = result.unwrap(); + console::log_1(&"CONNECTED".into()); + + let raw = conn.raw_connection; + + console::log_1(&"CREATE".into()); + raw.exec( + " + CREATE TABLE books ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + author TEXT NOT NULL, + published_year INTEGER, + genre TEXT + );", + ) + .await; + + console::log_1(&"INSERT".into()); + raw.exec( + " + INSERT INTO books (title, author, published_year, genre) VALUES + ('To Kill a Mockingbird', 'Harper Lee', 1960, 'Fiction'), + ('1984', 'George Orwell', 1949, 'Dystopian'), + ('The Great Gatsby', 'F. Scott Fitzgerald', 1925, 'Classics'), + ('Pride and Prejudice', 'Jane Austen', 1813, 'Romance'); + ", + ) + .await; + + console::log_1(&"SELECT ALL".into()); + raw.exec("SELECT * FROM books").await; + + console::log_1(&"SELECT title, author FROM books WHERE published_year > 1950;".into()); + raw.exec( + " + + SELECT title, published_year FROM books WHERE author = 'George Orwell'; + ", + ) + .await; + + console::log_1( + &"SELECT title, published_year FROM books WHERE author = 'George Orwell';".into(), + ); + raw.exec("SELECT title, author FROM books WHERE published_year > 1950;".into()) + .await; +} +*/ diff --git a/diesel-wasm-sqlite/yarn.lock b/diesel-wasm-sqlite/yarn.lock new file mode 100644 index 000000000..59311254a --- /dev/null +++ b/diesel-wasm-sqlite/yarn.lock @@ -0,0 +1,1640 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10 + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10/e9ed5fd27c3aec1095e3a16e0c0cf148d1fee55a38665c35f7b3f86a9b5d00d042ddaabc98e8a1cb7463b9378c15f22a94eb35e99469c201453eb8375191f243 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10/6ab2a9b8a1d67b067922c36f259e3b3dfd6b97b219c540877a4944549a4d49ea5ceba5663905ab5289682f1f3c15ff441d02f0447f620a42e1cb5e1937174d4b + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10/012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10/40033e33e96e97d77fba5a238e4bba4487b8284678906a9f616b5579ddaf868a18874c0054a75402c9fbaaa033a25ceae093af58c9c30278e35c23c9479e79b0 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^2.0.0": + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10/96fc0036b101bae5032dc2a4cd832efb815ce9b33f9ee2f29909ee49d96a0026b3565f73c507a69eb8603f5cb32e0ae45a70cab1e2655990a4e06ae99f7f572a + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0": + version: 3.1.1 + resolution: "@npmcli/fs@npm:3.1.1" + dependencies: + semver: "npm:^7.3.5" + checksum: 10/1e0e04087049b24b38bc0b30d87a9388ee3ca1d3fdfc347c2f77d84fcfe6a51f250bc57ba2c1f614d7e4285c6c62bf8c769bc19aa0949ea39e5b043ee023b0bd + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff + languageName: node + linkType: hard + +"@rollup/plugin-node-resolve@npm:^15.2.3": + version: 15.2.3 + resolution: "@rollup/plugin-node-resolve@npm:15.2.3" + dependencies: + "@rollup/pluginutils": "npm:^5.0.1" + "@types/resolve": "npm:1.20.2" + deepmerge: "npm:^4.2.2" + is-builtin-module: "npm:^3.2.1" + is-module: "npm:^1.0.0" + resolve: "npm:^1.22.1" + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10/d36a6792fbe9d8673d3a7c7dc88920be669ac54fba02ac0093d3c00fc9463fce2e87da1906a2651016742709c3d202b367fb49a62acd0d98f18409343f27b8b4 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^3.1.0": + version: 3.1.0 + resolution: "@rollup/pluginutils@npm:3.1.0" + dependencies: + "@types/estree": "npm:0.0.39" + estree-walker: "npm:^1.0.1" + picomatch: "npm:^2.2.2" + peerDependencies: + rollup: ^1.20.0||^2.0.0 + checksum: 10/3b69f02893eea42455fb97b81f612ac6bfadf94ac73bebd481ea13e90a693eef52c163210a095b12e574a25603af5e55f86a020889019167f331aa8dd3ff30e0 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.0.1": + version: 5.1.0 + resolution: "@rollup/pluginutils@npm:5.1.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^2.3.1" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10/abb15eaec5b36f159ec351b48578401bedcefdfa371d24a914cfdbb1e27d0ebfbf895299ec18ccc343d247e71f2502cba21202bc1362d7ef27d5ded699e5c2b2 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.19.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-android-arm64@npm:4.19.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.19.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.19.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.19.0" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.19.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.19.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.19.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.19.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.19.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.19.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.19.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.19.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.19.0": + version: 4.19.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.19.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@types/estree@npm:0.0.39": + version: 0.0.39 + resolution: "@types/estree@npm:0.0.39" + checksum: 10/9f0f20990dbf725470564d4d815d3758ac688b790f601ea98654b6e0b9797dc3c80306fb525abdacd9e75e014e3d09ad326098eaa2ed1851e4823a8e278538aa + languageName: node + linkType: hard + +"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: 10/7de6d928dd4010b0e20c6919e1a6c27b61f8d4567befa89252055fad503d587ecb9a1e3eab1b1901f923964d7019796db810b7fd6430acb26c32866d126fd408 + languageName: node + linkType: hard + +"@types/fs-extra@npm:^8.0.1": + version: 8.1.5 + resolution: "@types/fs-extra@npm:8.1.5" + dependencies: + "@types/node": "npm:*" + checksum: 10/565d9e55cd05064b3ab272b8748ed512b8fa5cddc23fd32b0d5f147f9ea3a45981577c4478b5060cae7b3d914c508bd2ea97eb84d9c1fa6f967982c892e4ab26 + languageName: node + linkType: hard + +"@types/glob@npm:^7.1.1": + version: 7.2.0 + resolution: "@types/glob@npm:7.2.0" + dependencies: + "@types/minimatch": "npm:*" + "@types/node": "npm:*" + checksum: 10/6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 + languageName: node + linkType: hard + +"@types/minimatch@npm:*": + version: 5.1.2 + resolution: "@types/minimatch@npm:5.1.2" + checksum: 10/94db5060d20df2b80d77b74dd384df3115f01889b5b6c40fa2dfa27cfc03a68fb0ff7c1f2a0366070263eb2e9d6bfd8c87111d4bc3ae93c3f291297c1bf56c85 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 20.14.11 + resolution: "@types/node@npm:20.14.11" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10/344e1ce1ed16c86ed1c4209ab4d1de67db83dd6b694a6fabe295c47144dde2c58dabddae9f39a0a2bdd246e95f8d141ccfe848e464884b48b8918df4f7788025 + languageName: node + linkType: hard + +"@types/resolve@npm:1.20.2": + version: 1.20.2 + resolution: "@types/resolve@npm:1.20.2" + checksum: 10/1bff0d3875e7e1557b6c030c465beca9bf3b1173ebc6937cac547654b0af3bb3ff0f16470e9c4d7c5dc308ad9ac8627c38dbff24ef698b66673ff5bd4ead7f7e + languageName: node + linkType: hard + +"@xmtp/wa-sqlite@npm:^1.0.1": + version: 1.0.1 + resolution: "@xmtp/wa-sqlite@npm:1.0.1" + dependenciesMeta: + monaco-editor@0.34.1: + unplugged: true + web-test-runner-jasmine@0.0.6: + unplugged: true + checksum: 10/bd08dd04fb98fef2f791274a502f6227c2050c92fdce288576d68c5e864efea2653217cfef73f134ed27999e90ad7300a64a1ab8344eeb9e9c6c2f802b7a7c64 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10/ca0a54e35bea4ece0ecb68a47b312e1a9a6f772408d5bcb9051230aaa94b0460671c5b5c9cb3240eb5b7bc94c52476550eb221f65a0bbd0145bdc9f3113a6707 + languageName: node + linkType: hard + +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: 10/c478fec8f79953f118704d007a38f2a185458853f5c45579b9669372bd0e12602e88dc2ad0233077831504f7cd6fcc8251c383375bba5eaaf563b102938bda26 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: "npm:^2.0.0" + indent-string: "npm:^4.0.0" + checksum: 10/1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10/2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 10/1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10/b4494dfbfc7e4591b4711a396bd27e540f8153914123dccb4cdbbcb514015ada63a3809f362b9d8d4f6b17a706f1d7bea3c6f974b15fa5ae76b5b502070889ff + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10/70fdf883b704d17a5dfc9cde206e698c16bcd74e7f196ab821511651aee4f9f76c9514bdfa6ca3a27b5e49138b89cb222a28caf3afe4567570139577f991df32 + languageName: node + linkType: hard + +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10/5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10/9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10/faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10/a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6 + languageName: node + linkType: hard + +"builtin-modules@npm:^3.3.0": + version: 3.3.0 + resolution: "builtin-modules@npm:3.3.0" + checksum: 10/62e063ab40c0c1efccbfa9ffa31873e4f9d57408cb396a2649981a0ecbce56aabc93c28feaccbc5658c95aab2703ad1d11980e62ec2e5e72637404e1eb60f39e + languageName: node + linkType: hard + +"cacache@npm:^18.0.0": + version: 18.0.4 + resolution: "cacache@npm:18.0.4" + dependencies: + "@npmcli/fs": "npm:^3.1.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^4.0.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + unique-filename: "npm:^3.0.0" + checksum: 10/ca2f7b2d3003f84d362da9580b5561058ccaecd46cba661cbcff0375c90734b610520d46b472a339fd032d91597ad6ed12dde8af81571197f3c9772b5d35b104 + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10/c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 10/2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10/fa00c91b4332b294de06b443923246bccebe9fab1b253f7fe1772d37b06a2269b4039a85e309abe1fe11b267b11c08d1d0473fda3badd6167f57313af2887a64 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"colorette@npm:^1.1.0": + version: 1.4.0 + resolution: "colorette@npm:1.4.0" + checksum: 10/c8d6c8c3ef5a99acfc3dd9a68f48019f1479ec347551387e4a1762e40f69e98ce19d4dc321ffb4919d1f28a7bdc90c39d4e9a901f4c474fd2124ad93a00c0454 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10/9680699c8e2b3af0ae22592cb764acaf973f292a7b71b8a06720233011853a58e256c89216a10cbe889727532fd77f8bcd49a760cedfde271b8e006c20e079f2 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10/e1a13869d2f57d974de0d9ef7acbf69dc6937db20b918525a01dacb5032129bd552d290d886d981e99f1b624cb03657084cc87bd40f115c07ecf376821c729ce + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.3.4": + version: 4.3.5 + resolution: "debug@npm:4.3.5" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/cb6eab424c410e07813ca1392888589972ce9a32b8829c6508f5e1f25f3c3e70a76731610ae55b4bbe58d1a2fffa1424b30e97fa8d394e49cd2656a9643aedd2 + languageName: node + linkType: hard + +"deepmerge@npm:^4.2.2": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 10/058d9e1b0ff1a154468bf3837aea436abcfea1ba1d165ddaaf48ca93765fdd01a30d33c36173da8fbbed951dd0a267602bc782fe288b0fc4b7e1e7091afc4529 + languageName: node + linkType: hard + +"diesel-wasm-sqlite@workspace:.": + version: 0.0.0-use.local + resolution: "diesel-wasm-sqlite@workspace:." + dependencies: + "@rollup/plugin-node-resolve": "npm:^15.2.3" + "@xmtp/wa-sqlite": "npm:^1.0.1" + rollup: "npm:^4.19.0" + rollup-plugin-base64: "npm:^1.0.1" + rollup-plugin-copy: "npm:^3.5.0" + languageName: unknown + linkType: soft + +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10/fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10/9b1d3e1baefeaf7d70799db8774149cef33b97183a6addceeba0cf6b85ba23ee2686f302f14482006df32df75d32b17c509c143a3689627929e4a8efaf483952 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10/c72d67a6821be15ec11997877c437491c313d924306b8da5d87d2a2bcc2cec9903cb5b04ee1a088460501d8e5b44f10df82fdc93c444101a7610b80c8b6938e1 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10/915acf859cea7131dac1b2b5c9c8e35c4849e325a1d114c30adb8cd615970f6dca0e27f64f3a4949d7d6ed86ecd79a1c5c63f02e697513cddd7b5835c90948b8 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10/bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10/1d20d825cdcce8d811bfbe86340f4755c02655a7feb2f13f8c880566d9d72a3f6c92c192a6867632e490d6da67b678271f46e01044996a6443e870331100dfdd + languageName: node + linkType: hard + +"estree-walker@npm:^1.0.1": + version: 1.0.1 + resolution: "estree-walker@npm:1.0.1" + checksum: 10/1cf11a0aff7613aa765dc535ed1d83e2a1986207d2353f4795df309a2c55726de3ca4948df635c09969a739dc59e8e2d69f88d3b3d2c6dfc5701257aafd1d11b + languageName: node + linkType: hard + +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10/b02109c5d46bc2ed47de4990eef770f7457b1159a229f0999a09224d2b85ffeed2d7679cffcff90aeb4448e94b0168feb5265b209cdec29aad50a3d6e93d21e2 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 10/2d9bbb6473de7051f96790d5f9a678f32e60ed0aa70741dc7fdc96fec8d631124ec3374ac144387604f05afff9500f31a1d45bd9eee4cdc2e4f9ad2d9b9d5dbd + languageName: node + linkType: hard + +"fast-glob@npm:^3.0.3": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.4" + checksum: 10/222512e9315a0efca1276af9adb2127f02105d7288fa746145bf45e2716383fb79eb983c89601a72a399a56b7c18d38ce70457c5466218c5f13fad957cee16df + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.17.1 + resolution: "fastq@npm:1.17.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10/a443180068b527dd7b3a63dc7f2a47ceca2f3e97b9c00a1efe5538757e6cc4056a3526df94308075d7727561baf09ebaa5b67da8dcbddb913a021c5ae69d1f69 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.2.1 + resolution: "foreground-child@npm:3.2.1" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10/77b33b3c438a499201727ca84de39a66350ccd54a8805df712773e963cefb5c4632dbc4386109e97a0df8fb1585aee95fa35acb07587e3e04cfacabfc0ae15dc + languageName: node + linkType: hard + +"fs-extra@npm:^8.1.0": + version: 8.1.0 + resolution: "fs-extra@npm:8.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^4.0.0" + universalify: "npm:^0.1.0" + checksum: 10/6fb12449f5349be724a138b4a7b45fe6a317d2972054517f5971959c26fbd17c0e145731a11c7324460262baa33e0a799b183ceace98f7a372c95fbb6f20f5de + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/03191781e94bc9a54bd376d3146f90fe8e082627c502185dbf7b9b3032f66b0b142c1115f3b2cc5936575fc1b44845ce903dd4c21bec2a8d69f3bd56f9cee9ec + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 10/e703107c28e362d8d7b910bbcbfd371e640a3bb45ae157a362b5952c0030c0b6d4981140ec319b347bce7adc025dd7813da1ff908a945ac214d64f5402a51b96 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10/4c1ade961ded57cdbfbb5cac5106ec17bc8bccd62e16343c569a0ceeca83b9dfef87550b4dc5cbb89642da412b20c5071f304c8c464b80415446e8e155a038c0 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10/185e20d20f10c8d661d59aac0f3b63b31132d492e1b11fcc2a93cb2c47257ebaee7407c38513efd2b35cafdf972d9beb2ea4593c1e0f3bf8f2744836928d7454 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10/32cd106ce8c0d83731966d31517adb766d02c3812de49c30cfe0675c7c0ae6630c11214c54a5ae67aca882cf738d27fd7768f21aa19118b9245950554be07247 + languageName: node + linkType: hard + +"glob@npm:^10.2.2, glob@npm:^10.3.10": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10/698dfe11828b7efd0514cd11e573eaed26b2dff611f0400907281ce3eab0c1e56143ef9b35adc7c77ecc71fba74717b510c7c223d34ca8a98ec81777b293d4ac + languageName: node + linkType: hard + +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10/59452a9202c81d4508a43b8af7082ca5c76452b9fcc4a9ab17655822e6ce9b21d4f8fbadabe4fe3faef448294cec249af305e2cd824b7e9aaf689240e5e96a7b + languageName: node + linkType: hard + +"globby@npm:10.0.1": + version: 10.0.1 + resolution: "globby@npm:10.0.1" + dependencies: + "@types/glob": "npm:^7.1.1" + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.0.3" + glob: "npm:^7.1.3" + ignore: "npm:^5.1.1" + merge2: "npm:^1.2.3" + slash: "npm:^3.0.0" + checksum: 10/ea724a820d7d4eafc5aa3882d667a7ef3505b3018652b2658185d58752f122b75d3370086e4d4a7b1bbcdf8c2e700e5709a48db189a930df37005e1b3c86c256 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10/362d5ed66b12ceb9c0a328fb31200b590ab1b02f4a254a697dc796850cc4385603e75f53ec59f768b2dad3bfa1464bd229f7de278d2899a0e3beffc634b6683f + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" + dependencies: + agent-base: "npm:^7.0.2" + debug: "npm:4" + checksum: 10/6679d46159ab3f9a5509ee80c3a3fc83fba3a920a5e18d32176c3327852c3c00ad640c0c4210a8fd70ea3c4a6d3a1b375bf01942516e7df80e2646bdc77658ab + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10/24e3292dd3dadaa81d065c6f8c41b274a47098150d444b96e5f53b4638a9a71482921ea6a91a1f59bb71d9796de25e04afd05919fa64c360347ba65d3766f10f + languageName: node + linkType: hard + +"ignore@npm:^5.1.1": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 10/0a884c2fbc8c316f0b9f92beaf84464253b73230a4d4d286697be45fca081199191ca33e1c2e82d9e5f851f5e9a48a78e25a35c951e7eb41e59f150db3530065 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10/cd3f5cbc9ca2d624c6a1f53f12e6b341659aba0e2d3254ae2b4464aaea8b4294cdb09616abbc59458f980531f2429784ed6a420d48d245bcad0811980c9efae9 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: "npm:^1.3.0" + wrappy: "npm:1" + checksum: 10/d2ebd65441a38c8336c223d1b80b921b9fa737e37ea466fd7e253cb000c64ae1f17fa59e68130ef5bda92cfd8d36b83d37dab0eb0a4558bcfec8e8cdfd2dcb67 + languageName: node + linkType: hard + +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10/1ed81e06721af012306329b31f532b5e24e00cb537be18ddc905a84f19fe8f83a09a1699862bf3a1ec4b9dea93c55a3fa5faf8b5ea380431469df540f38b092c + languageName: node + linkType: hard + +"is-builtin-module@npm:^3.2.1": + version: 3.2.1 + resolution: "is-builtin-module@npm:3.2.1" + dependencies: + builtin-modules: "npm:^3.3.0" + checksum: 10/e8f0ffc19a98240bda9c7ada84d846486365af88d14616e737d280d378695c8c448a621dcafc8332dbf0fcd0a17b0763b845400709963fa9151ddffece90ae88 + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0": + version: 2.15.0 + resolution: "is-core-module@npm:2.15.0" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10/70e962543e5d3a97c07cb29144a86792d545a21f28e67da5401d85878a0193d46fbab8d97bc3ca680e2778705dca66e7b6ca840c493497a27ca0e8c5f3ac3d1d + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10/df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10/44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10/3ed74f2b0cdf4f401f38edb0442ddfde3092d79d7d35c9919c86641efdbcbb32e45aa3c0f70ce5eecc946896cd5a0f26e4188b9f2b881876f7cb6c505b82da11 + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 10/93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 + languageName: node + linkType: hard + +"is-module@npm:^1.0.0": + version: 1.0.0 + resolution: "is-module@npm:1.0.0" + checksum: 10/8cd5390730c7976fb4e8546dd0b38865ee6f7bacfa08dfbb2cc07219606755f0b01709d9361e01f13009bbbd8099fa2927a8ed665118a6105d66e40f1b838c3f + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10/6a6c3383f68afa1e05b286af866017c78f1226d43ac8cb064e115ff9ed85eb33f5c4f7216c96a71e4dfea289ef52c5da3aef5bbfade8ffe47a0465d70c0c8e86 + languageName: node + linkType: hard + +"is-plain-object@npm:^3.0.0": + version: 3.0.1 + resolution: "is-plain-object@npm:3.0.1" + checksum: 10/d13fe75db350d4ac669595cdfe0242ae87fcecddf2bca858d2dd443a6ed6eb1f69951fac8c2fa85b16106c6b0d7738fea86c2aca2ecee7fd61de15c1574f2cc5 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10/7c9f715c03aff08f35e98b1fadae1b9267b38f0615d501824f9743f3aab99ef10e303ce7db3f186763a0b70a19de5791ebfc854ff884d5a8c4d92211f642ec92 + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10/7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10/96f8786eaab98e4bf5b2a5d6d9588ea46c4d06bbc4f2eb861fdd7b6b182b16f71d8a70e79820f335d52653b16d4843b29dd9cdcf38ae80406756db9199497cf3 + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10/bebe7ae829bbd586ce8cbe83501dd8cb8c282c8902a8aeeed0a073a89dc37e8103b1244f3c6acd60278bcbfe12d93a3f83c9ac396868a3b3bbc3c5e5e3b648ef + languageName: node + linkType: hard + +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.6" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10/17796f0ab1be8479827d3683433f97ebe0a1c6932c3360fa40348eac36904d69269aab26f8b16da311882d94b42e9208e8b28e490bf926364f3ac9bff134c226 + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a + languageName: node + linkType: hard + +"make-fetch-happen@npm:^13.0.0": + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" + dependencies: + "@npmcli/agent": "npm:^2.0.0" + cacache: "npm:^18.0.0" + http-cache-semantics: "npm:^4.1.1" + is-lambda: "npm:^1.0.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^0.6.3" + proc-log: "npm:^4.2.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^10.0.0" + checksum: 10/11bae5ad6ac59b654dbd854f30782f9de052186c429dfce308eda42374528185a100ee40ac9ffdc36a2b6c821ecaba43913e4730a12f06f15e895ea9cb23fa59 + languageName: node + linkType: hard + +"merge2@npm:^1.2.3, merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10/7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.4": + version: 4.0.7 + resolution: "micromatch@npm:4.0.7" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10/a11ed1cb67dcbbe9a5fc02c4062cf8bb0157d73bf86956003af8dcfdf9b287f9e15ec0f6d6925ff6b8b5b496202335e497b01de4d95ef6cf06411bc5e5c474a0 + languageName: node + linkType: hard + +"minimatch@npm:^3.1.1": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 + languageName: node + linkType: hard + +"minipass-fetch@npm:^3.0.0": + version: 3.0.5 + resolution: "minipass-fetch@npm:3.0.5" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^2.1.2" + dependenciesMeta: + encoding: + optional: true + checksum: 10/c669948bec1373313aaa8f104b962a3ced9f45c49b26366a4b0ae27ccdfa9c5740d72c8a84d3f8623d7a61c5fc7afdfda44789008c078f61a62441142efc4a97 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/40982d8d836a52b0f37049a0a7e5d0f089637298e6d9b45df9c115d4f0520682a78258905e5c8b180fb41b593b0a82cc1361d2c74b45f7ada66334f84d1ecfdd + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10/61682162d29f45d3152b78b08bab7fb32ca10899bc5991ffe98afc18c9e9543bd1e3be94f8b8373ba6262497db63607079dc242ea62e43e7b2270837b7347c93 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10/ae0f45436fb51344dcb87938446a32fbebb540d0e191d63b35e1c773d47512e17307bf54aa88326cc6d176594d00e4423563a091f7266c2f9a6872cdc1e234d1 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: 10/d71b8dcd4b5af2fe13ecf3bd24070263489404fe216488c5ba7e38ece1f54daf219e72a833a3a2dc404331e870e9f44963a33399589490956bff003a3404d3b2 + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 10/673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f + languageName: node + linkType: hard + +"negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10/2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 10.2.0 + resolution: "node-gyp@npm:10.2.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + glob: "npm:^10.3.10" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^13.0.0" + nopt: "npm:^7.0.0" + proc-log: "npm:^4.1.0" + semver: "npm:^7.3.5" + tar: "npm:^6.2.1" + which: "npm:^4.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10/41773093b1275751dec942b985982fd4e7a69b88cae719b868babcef3880ee6168aaec8dcaa8cd0b9fa7c84873e36cc549c6cac6a124ee65ba4ce1f1cc108cfe + languageName: node + linkType: hard + +"nopt@npm:^7.0.0": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10/95a1f6dec8a81cd18cdc2fed93e6f0b4e02cf6bdb4501c848752c6e34f9883d9942f036a5e3b21a699047d8a448562d891e67492df68ec9c373e6198133337ae + languageName: node + linkType: hard + +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: "npm:^3.0.0" + checksum: 10/7ba4a2b1e24c05e1fc14bbaea0fc6d85cf005ae7e9c9425d4575550f37e2e584b1af97bcde78eacd7559208f20995988d52881334db16cf77bc1bcf68e48ed7c + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: 10/ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10/060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10/49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10/5e8845c159261adda6f09814d7725683257fcc85a18f329880ab4d7cc1d12830967eae5d5894e453f341710d5484b8fdbbd4d75181b4d6e1eb2f4dc7aeadc434 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10/5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 + languageName: node + linkType: hard + +"picomatch@npm:^2.2.2, picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc + languageName: node + linkType: hard + +"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 10/4e1394491b717f6c1ade15c570ecd4c2b681698474d3ae2d303c1e4b6ab9455bd5a81566211e82890d5a5ae9859718cc6954d5150bb18b09b72ecb297beae90a + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10/96e1a82453c6c96eef53a37a1d6134c9f2482f94068f98a59145d0986ca4e497bf110a410adf73857e588165eab3899f0ebcf7b3890c1b3ce802abc0d65967d4 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10/72900df0616e473e824202113c3df6abae59150dfb73ed13273503127235320e9c8ca4aaaaccfd58cf417c6ca92a6e68ee9a5c3182886ae949a768639b388a7b + languageName: node + linkType: hard + +"resolve@npm:^1.22.1": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/c473506ee01eb45cbcfefb68652ae5759e092e6b0fb64547feadf9736a6394f258fbc6f88e00c5ca36d5477fbb65388b272432a3600fa223062e54333c156753 + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.22.1#optional!builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/f345cd37f56a2c0275e3fe062517c650bb673815d885e7507566df589375d165bbbf4bdb6aa95600a9bc55f4744b81f452b5a63f95b9f10a72787dba3c90890a + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10/1f914879f97e7ee931ad05fe3afa629bd55270fc6cf1c1e589b6a99fab96d15daad0fa1a52a00c729ec0078045fe3e399bd4fd0c93bcc906957bdc17f89cb8e6 + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: 10/14222c9e1d3f9ae01480c50d96057228a8524706db79cdeb5a2ce5bb7070dd9f409a6f84a02cbef8cdc80d39aef86f2dd03d155188a1300c599b05437dcd2ffb + languageName: node + linkType: hard + +"rollup-plugin-base64@npm:^1.0.1": + version: 1.0.1 + resolution: "rollup-plugin-base64@npm:1.0.1" + dependencies: + "@rollup/pluginutils": "npm:^3.1.0" + checksum: 10/1f4ebf46a9ec5220ba9b596820e56d6a7a1b77d567097b8713a63ef669b02b1d296d6e77f48c01e43de7c98c751b5ee5683b4b1f7e4cb4b76a4fe55109654862 + languageName: node + linkType: hard + +"rollup-plugin-copy@npm:^3.5.0": + version: 3.5.0 + resolution: "rollup-plugin-copy@npm:3.5.0" + dependencies: + "@types/fs-extra": "npm:^8.0.1" + colorette: "npm:^1.1.0" + fs-extra: "npm:^8.1.0" + globby: "npm:10.0.1" + is-plain-object: "npm:^3.0.0" + checksum: 10/706ba6bd2052b95d1037f12963ff4b50749730f18aefad10544f9574aff7c035c88c5dd9ae1f0c0408cf09862e595a0ea4d68e13c2717addaea2bda3ade0d0e0 + languageName: node + linkType: hard + +"rollup@npm:^4.19.0": + version: 4.19.0 + resolution: "rollup@npm:4.19.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.19.0" + "@rollup/rollup-android-arm64": "npm:4.19.0" + "@rollup/rollup-darwin-arm64": "npm:4.19.0" + "@rollup/rollup-darwin-x64": "npm:4.19.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.19.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.19.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.19.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.19.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.19.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.19.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.19.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.19.0" + "@rollup/rollup-linux-x64-musl": "npm:4.19.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.19.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.19.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.19.0" + "@types/estree": "npm:1.0.5" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10/a5f56e60d160e727f372fb0b0adbab03c1e5b858df7af62e626459687e6510d5b9685e4badef50bb6ffd916eaf53c1684a8e12ae959dacb8e6930c77a00a0f19 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10/cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 + languageName: node + linkType: hard + +"semver@npm:^7.3.5": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10/6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10/1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f + languageName: node + linkType: hard + +"slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10/94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.4 + resolution: "socks-proxy-agent@npm:8.0.4" + dependencies: + agent-base: "npm:^7.1.1" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10/c8e7c2b398338b49a0a0f4d2bae5c0602aeeca6b478b99415927b6c5db349ca258448f2c87c6958ebf83eea17d42cbc5d1af0bfecb276cac10b9658b0f07f7d7 + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10/ffcb622c22481dfcd7589aae71fbfd71ca34334064d181df64bf8b7feaeee19706aba4cffd1de35cc7bbaeeaa0af96be2d7f40fcbc7bc0ab69533a7ae9ffc4fb + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10/e7587128c423f7e43cc625fe2f87e6affdf5ca51c1cc468e910d8aaca46bb44a7fbcfa552f787b1d3987f7043aeb4527d1b99559e6621e01b42b3f45e5a24cbb + languageName: node + linkType: hard + +"ssri@npm:^10.0.0": + version: 10.0.6 + resolution: "ssri@npm:10.0.6" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/f92c1b3cc9bfd0a925417412d07d999935917bc87049f43ebec41074661d64cf720315661844106a77da9f8204b6d55ae29f9514e673083cae39464343af2a8b + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10/e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10/7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10/ae3b5436d34fadeb6096367626ce987057713c566e1e7768818797e00ac5d62023d0f198c4e681eae9e20701721980b26a64a8f5b91238869592a9c6800719a2 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10/475f53e9c44375d6e72807284024ac5d668ee1d06010740dec0b9744f2ddf47de8d7151f80e5f6190fc8f384e802fdf9504b76a7e9020c9faee7103623338be2 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10/a9dc19ae2220c952bd2231d08ddeecb1b0328b61e72071ff4000c8384e145cc07c1c0bdb3b5a1cb06e186a7b2790f1dee793418b332f6ddf320de25d9125be7e + languageName: node + linkType: hard + +"tar@npm:^6.1.11, tar@npm:^6.2.1": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10/bfbfbb2861888077fc1130b84029cdc2721efb93d1d1fb80f22a7ac3a98ec6f8972f29e564103bbebf5e97be67ebc356d37fa48dbc4960600a1eb7230fbd1ea0 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10/10dda13571e1f5ad37546827e9b6d4252d2e0bc176c24a101252153ef435d83696e2557fe128c4678e4e78f5f01e83711c703eef9814eb12dab028580d45980a + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10/0097779d94bc0fd26f0418b3a05472410408877279141ded2bd449167be1aed7ea5b76f756562cb3586a07f251b90799bab22d9019ceba49c037c76445f7cddd + languageName: node + linkType: hard + +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" + dependencies: + unique-slug: "npm:^4.0.0" + checksum: 10/8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df + languageName: node + linkType: hard + +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10/40912a8963fc02fb8b600cf50197df4a275c602c60de4cac4f75879d3c48558cfac48de08a25cc10df8112161f7180b3bbb4d662aadb711568602f9eddee54f0 + languageName: node + linkType: hard + +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 10/40cdc60f6e61070fe658ca36016a8f4ec216b29bf04a55dce14e3710cc84c7448538ef4dad3728d0bfe29975ccd7bfb5f414c45e7b78883567fb31b246f02dff + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10/4782f8a1d6b8fc12c65e968fea49f59752bf6302dc43036c3bf87da718a80710f61a062516e9764c70008b487929a73546125570acea95c5b5dcc8ac3052c70f + languageName: node + linkType: hard + +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10/f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10/cebdaeca3a6880da410f75209e68cd05428580de5ad24535f22696d7d9cab134d1f8498599f344c3cf0fb37c1715807a183778d8c648d6cc0cb5ff2bb4236540 + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10/7b1e4b35e9bb2312d2ee9ee7dc95b8cb5f8b4b5a89f7dde5543fe66c1e3715663094defa50d75454ac900bd210f702d575f15f3f17fa9ec0291806d2578d1ddf + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd + languageName: node + linkType: hard