diff --git a/Cargo.lock b/Cargo.lock index a33ff928e..86ec24ab2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,12 +23,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aead" version = "0.5.1" @@ -270,9 +264,9 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.17.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" +checksum = "8e6acf7e4a267eecbb127ed696bb2d50572c22ba7f586a646321e1798d8336a1" dependencies = [ "futures-io", "futures-util", @@ -365,9 +359,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.16" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc" dependencies = [ "async-trait", "axum-core", @@ -383,8 +377,10 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", + "rustversion", "serde", "serde_json", + "serde_path_to_error", "serde_urlencoded", "sync_wrapper", "tokio", @@ -396,9 +392,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34" dependencies = [ "async-trait", "bytes", @@ -406,6 +402,7 @@ dependencies = [ "http", "http-body", "mime", + "rustversion", "tower-layer", "tower-service", ] @@ -448,6 +445,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64ct" version = "1.5.2" @@ -626,7 +629,7 @@ dependencies = [ [[package]] name = "builtin-psl-connectors" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "connection-string", "either", @@ -644,6 +647,12 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + [[package]] name = "bytemuck" version = "1.12.1" @@ -692,6 +701,37 @@ dependencies = [ "system-deps 6.0.2", ] +[[package]] +name = "camino" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.14", + "serde", + "serde_json", +] + [[package]] name = "cargo_toml" version = "0.11.8" @@ -787,9 +827,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", @@ -1313,8 +1353,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core 0.14.2", + "darling_macro 0.14.2", ] [[package]] @@ -1331,13 +1381,38 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core 0.14.2", "quote", "syn", ] @@ -1359,7 +1434,7 @@ dependencies = [ [[package]] name = "datamodel-renderer" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "base64 0.13.1", "once_cell", @@ -1378,16 +1453,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "deflate" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "derivative" version = "2.2.0" @@ -1415,7 +1480,7 @@ dependencies = [ [[package]] name = "diagnostics" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "colored", "indoc", @@ -1492,7 +1557,7 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "dml" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "chrono", "cuid", @@ -1510,7 +1575,7 @@ dependencies = [ [[package]] name = "dmmf" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "bigdecimal", "indexmap", @@ -1522,6 +1587,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "document-features" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e493c573fce17f00dcab13b6ac057994f3ce17d1af4dc39bfd482b83c6eb6157" +dependencies = [ + "litrs", +] + [[package]] name = "dtoa" version = "0.4.8" @@ -1537,6 +1611,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + [[package]] name = "either" version = "1.8.0" @@ -1626,6 +1706,15 @@ dependencies = [ "libc", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -2440,11 +2529,11 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "httpz" version = "0.0.3" -source = "git+https://github.com/oscartbeaumont/httpz.git?rev=1ddbd9ad594ac7ee3e5b167afe689d1ce1228519#1ddbd9ad594ac7ee3e5b167afe689d1ce1228519" +source = "git+https://github.com/oscartbeaumont/httpz.git?rev=a5020adecb15b55d84a8330b4680eba82fa6b820#a5020adecb15b55d84a8330b4680eba82fa6b820" dependencies = [ "async-tungstenite", "axum", - "base64 0.13.1", + "base64 0.20.0", "cookie", "form_urlencoded", "futures", @@ -2455,6 +2544,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "httpz" +version = "0.0.3" +source = "git+https://github.com/oscartbeaumont/httpz?rev=a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6#a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6" +dependencies = [ + "axum", + "form_urlencoded", + "futures", + "http", + "hyper", + "percent-encoding", + "tauri", + "thiserror", + "tokio", +] + [[package]] name = "humantime" version = "2.1.0" @@ -2463,9 +2568,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -2524,12 +2629,12 @@ dependencies = [ [[package]] name = "ico" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4b3331534254a9b64095ae60d3dc2a8225a7a70229cd5888be127cdc1f6804" +checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" dependencies = [ "byteorder", - "png 0.11.0", + "png", ] [[package]] @@ -2609,7 +2714,7 @@ dependencies = [ "jpeg-decoder", "num-rational", "num-traits", - "png 0.17.6", + "png", "scoped_threadpool", "tiff", ] @@ -2660,15 +2765,6 @@ dependencies = [ "cfb", ] -[[package]] -name = "inflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f9f47468e9a76a6452271efadc88fe865a82be91fe75e6c0c57b87ccea59d4" -dependencies = [ - "adler32", -] - [[package]] name = "inotify" version = "0.9.6" @@ -2731,7 +2827,7 @@ dependencies = [ [[package]] name = "introspection-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "anyhow", "async-trait", @@ -2829,6 +2925,20 @@ dependencies = [ "walkdir", ] +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine 4.6.6", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -2876,7 +2986,7 @@ dependencies = [ [[package]] name = "json-rpc-api-build" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "backtrace", "heck 0.3.3", @@ -3018,6 +3128,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "litrs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" + [[package]] name = "lock_api" version = "0.4.9" @@ -3148,9 +3264,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "matchit" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "maybe-uninit" @@ -3276,7 +3392,7 @@ dependencies = [ [[package]] name = "migration-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "chrono", "enumflags2 0.7.5", @@ -3291,7 +3407,7 @@ dependencies = [ [[package]] name = "migration-core" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "async-trait", "chrono", @@ -3317,6 +3433,21 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mini-moka" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cafc5ec7807288595f9c20c86e6ce6d262b722f61e0547fe7e6e6e6451b58d5" +dependencies = [ + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "skeptic", + "smallvec 1.10.0", + "tagptr", + "triomphe", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3569,28 +3700,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "normi" -version = "0.0.1" -source = "git+https://github.com/oscartbeaumont/rspc?rev=6243b5b6a1376940a40318340e5eaef22e4a2c22#6243b5b6a1376940a40318340e5eaef22e4a2c22" -dependencies = [ - "normi-macros", - "rspc", - "serde", - "serde_json", - "specta 0.0.4", -] - -[[package]] -name = "normi-macros" -version = "0.0.1" -source = "git+https://github.com/oscartbeaumont/rspc?rev=6243b5b6a1376940a40318340e5eaef22e4a2c22#6243b5b6a1376940a40318340e5eaef22e4a2c22" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "notify" version = "5.0.0" @@ -4028,7 +4137,7 @@ dependencies = [ [[package]] name = "parser-database" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "diagnostics", "either", @@ -4285,18 +4394,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "png" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925" -dependencies = [ - "bitflags", - "deflate", - "inflate", - "num-iter", -] - [[package]] name = "png" version = "0.17.6" @@ -4369,7 +4466,7 @@ dependencies = [ [[package]] name = "prisma-client-rust" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.4#74b984dd4eb155b093f70988d1c046cf6e7e51b2" +source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "base64 0.13.1", "bigdecimal", @@ -4403,7 +4500,7 @@ dependencies = [ [[package]] name = "prisma-client-rust-cli" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.4#74b984dd4eb155b093f70988d1c046cf6e7e51b2" +source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "directories", "flate2", @@ -4423,7 +4520,7 @@ dependencies = [ [[package]] name = "prisma-client-rust-sdk" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.4#74b984dd4eb155b093f70988d1c046cf6e7e51b2" +source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "convert_case 0.5.0", "dml", @@ -4444,7 +4541,7 @@ dependencies = [ [[package]] name = "prisma-models" version = "0.0.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "bigdecimal", "chrono", @@ -4460,7 +4557,7 @@ dependencies = [ [[package]] name = "prisma-value" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "base64 0.12.3", "bigdecimal", @@ -4524,9 +4621,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -4534,7 +4631,7 @@ dependencies = [ [[package]] name = "psl" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "builtin-psl-connectors", "dml", @@ -4544,7 +4641,7 @@ dependencies = [ [[package]] name = "psl-core" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "bigdecimal", "chrono", @@ -4563,6 +4660,17 @@ dependencies = [ "url", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + [[package]] name = "quaint" version = "0.2.0-alpha.13" @@ -4610,7 +4718,7 @@ dependencies = [ [[package]] name = "query-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "anyhow", "async-trait", @@ -4630,7 +4738,7 @@ dependencies = [ [[package]] name = "query-core" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "async-trait", "base64 0.12.3", @@ -4674,7 +4782,7 @@ dependencies = [ [[package]] name = "query-engine-metrics" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "metrics 0.18.1", "metrics-exporter-prometheus", @@ -4752,9 +4860,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -5054,9 +5162,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -5090,7 +5198,7 @@ dependencies = [ [[package]] name = "request-handlers" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "bigdecimal", "connection-string", @@ -5210,14 +5318,14 @@ dependencies = [ [[package]] name = "rspc" version = "0.1.2" -source = "git+https://github.com/oscartbeaumont/rspc?rev=6243b5b6a1376940a40318340e5eaef22e4a2c22#6243b5b6a1376940a40318340e5eaef22e4a2c22" +source = "git+https://github.com/oscartbeaumont/rspc?rev=c03872c0ba29d2429e9c059dfb235cdd03e15e8c#c03872c0ba29d2429e9c059dfb235cdd03e15e8c" dependencies = [ "async-stream", "futures", - "httpz", + "httpz 0.0.3 (git+https://github.com/oscartbeaumont/httpz.git?rev=a5020adecb15b55d84a8330b4680eba82fa6b820)", "serde", "serde_json", - "specta 0.0.4", + "specta 0.0.6", "tauri", "thiserror", "tokio", @@ -5381,7 +5489,7 @@ dependencies = [ [[package]] name = "schema" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "once_cell", "prisma-models", @@ -5391,7 +5499,7 @@ dependencies = [ [[package]] name = "schema-ast" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "diagnostics", "pest", @@ -5401,7 +5509,7 @@ dependencies = [ [[package]] name = "schema-builder" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "itertools", "lazy_static", @@ -5461,11 +5569,13 @@ dependencies = [ "futures", "globset", "hostname", + "http-range", + "httpz 0.0.3 (git+https://github.com/oscartbeaumont/httpz?rev=a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6)", "image", "include_dir", "int-enum", "itertools", - "normi", + "mini-moka", "notify", "once_cell", "prisma-client-rust", @@ -5478,7 +5588,8 @@ dependencies = [ "sd-sync", "serde", "serde_json", - "specta 0.0.4", + "serde_with 2.2.0", + "specta 0.0.6", "sysinfo", "tempfile", "thiserror", @@ -5496,7 +5607,7 @@ dependencies = [ name = "sd-core-android" version = "0.1.0" dependencies = [ - "jni", + "jni 0.19.0", "sd-core-mobile", "tracing", ] @@ -5546,7 +5657,6 @@ dependencies = [ "serde", "serde-big-array", "serde_json", - "specta 0.0.4", "thiserror", "tokio", "uuid 1.2.1", @@ -5734,9 +5844,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -5762,9 +5872,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -5773,9 +5883,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "indexmap", "itoa 1.0.4", @@ -5822,7 +5932,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d904179146de381af4c93d3af6ca4984b3152db687dacb9c3c35e86f39809c" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros 2.2.0", + "time 0.3.15", ] [[package]] @@ -5831,7 +5957,19 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_with_macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" +dependencies = [ + "darling 0.14.2", "proc-macro2", "quote", "syn", @@ -5865,6 +6003,9 @@ version = "0.1.0" dependencies = [ "axum", "ctrlc", + "http", + "httpz 0.0.3 (git+https://github.com/oscartbeaumont/httpz?rev=a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6)", + "hyper", "rspc", "sd-core", "tokio", @@ -5881,17 +6022,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.5", -] - [[package]] name = "sha1" version = "0.10.5" @@ -5967,6 +6097,21 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "sketches-ddsketch" version = "0.1.3" @@ -6039,14 +6184,21 @@ dependencies = [ name = "spacedrive" version = "0.1.0" dependencies = [ + "axum", + "http", + "httpz 0.0.3 (git+https://github.com/oscartbeaumont/httpz?rev=a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6)", + "percent-encoding", + "rand 0.8.5", "rspc", "sd-core", "serde", + "server", "swift-rs", "tauri", "tauri-build", "tokio", "tracing", + "url", "window-shadows", ] @@ -6076,13 +6228,19 @@ dependencies = [ [[package]] name = "specta" -version = "0.0.4" -source = "git+https://github.com/oscartbeaumont/rspc?rev=6243b5b6a1376940a40318340e5eaef22e4a2c22#6243b5b6a1376940a40318340e5eaef22e4a2c22" +version = "0.0.6" +source = "git+https://github.com/oscartbeaumont/rspc?rev=c03872c0ba29d2429e9c059dfb235cdd03e15e8c#c03872c0ba29d2429e9c059dfb235cdd03e15e8c" dependencies = [ "chrono", + "document-features", "indexmap", + "indoc", + "once_cell", + "paste", + "serde", "serde_json", - "specta-macros 0.0.4", + "specta-macros 0.0.6", + "thiserror", "uhlc", "uuid 1.2.1", ] @@ -6102,10 +6260,11 @@ dependencies = [ [[package]] name = "specta-macros" -version = "0.0.4" -source = "git+https://github.com/oscartbeaumont/rspc?rev=6243b5b6a1376940a40318340e5eaef22e4a2c22#6243b5b6a1376940a40318340e5eaef22e4a2c22" +version = "0.0.6" +source = "git+https://github.com/oscartbeaumont/rspc?rev=c03872c0ba29d2429e9c059dfb235cdd03e15e8c#c03872c0ba29d2429e9c059dfb235cdd03e15e8c" dependencies = [ "Inflector", + "itertools", "proc-macro2", "quote", "syn", @@ -6130,12 +6289,12 @@ dependencies = [ [[package]] name = "sql-ddl" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" [[package]] name = "sql-introspection-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "anyhow", "async-trait", @@ -6159,7 +6318,7 @@ dependencies = [ [[package]] name = "sql-migration-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "chrono", "connection-string", @@ -6186,7 +6345,7 @@ dependencies = [ [[package]] name = "sql-query-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "anyhow", "async-trait", @@ -6217,7 +6376,7 @@ dependencies = [ [[package]] name = "sql-schema-describer" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "async-trait", "bigdecimal", @@ -6359,9 +6518,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -6437,10 +6596,16 @@ dependencies = [ ] [[package]] -name = "tao" -version = "0.14.0" +name = "tagptr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43336f5d1793543ba96e2a1e75f3a5c7dcd592743be06a0ab3a190f4fcb4b934" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tao" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8e6399427c8494f9849b58694754d7cc741293348a6836b6c8d2c5aa82d8e6" dependencies = [ "bitflags", "cairo-rs", @@ -6460,7 +6625,7 @@ dependencies = [ "gtk", "image", "instant", - "jni", + "jni 0.20.0", "lazy_static", "libc", "log", @@ -6471,7 +6636,7 @@ dependencies = [ "once_cell", "parking_lot 0.12.1", "paste", - "png 0.17.6", + "png", "raw-window-handle", "scopeguard", "serde", @@ -6495,9 +6660,9 @@ dependencies = [ [[package]] name = "tauri" -version = "1.1.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbf22abd61d95ca9b2becd77f9db4c093892f73e8a07d21d8b0b2bf71a7bcea" +checksum = "fe7e0f1d535e7cbbbab43c82be4fc992b84f9156c16c160955617e0260ebc449" dependencies = [ "anyhow", "attohttpc", @@ -6564,16 +6729,16 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356fa253e40ae4d6ff02075011f2f2bb4066f5c9d8c1e16ca6912d7b75903ba6" +checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" dependencies = [ "base64 0.13.1", "brotli", "ico", "json-patch", "plist", - "png 0.17.6", + "png", "proc-macro2", "quote", "regex", @@ -6590,9 +6755,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6051fd6940ddb22af452340d03c66a3e2f5d72e0788d4081d91e31528ccdc4d" +checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -6604,14 +6769,13 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49439a5ea47f474572b854972f42eda2e02a470be5ca9609cc83bb66945abe2" +checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b" dependencies = [ "gtk", "http", "http-range", - "infer", "rand 0.8.5", "raw-window-handle", "serde", @@ -6625,9 +6789,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dce920995fd49907aa9bea7249ed1771454f11f7611924c920a1f75fb614d4" +checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30" dependencies = [ "cocoa", "gtk", @@ -6645,15 +6809,16 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8fdae6f29cef959809a3c3afef510c5b715a446a597ab8b791497585363f39" +checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" dependencies = [ "brotli", "ctor", "glob", "heck 0.4.0", "html5ever", + "infer", "json-patch", "kuchiki", "memchr", @@ -6663,7 +6828,7 @@ dependencies = [ "semver 1.0.14", "serde", "serde_json", - "serde_with", + "serde_with 1.14.0", "thiserror", "url", "walkdir", @@ -6733,18 +6898,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -6800,6 +6965,7 @@ dependencies = [ "itoa 1.0.4", "libc", "num_threads", + "serde", "time-macros", ] @@ -7075,6 +7241,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "triomphe" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" + [[package]] name = "try-lock" version = "0.2.3" @@ -7106,9 +7278,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", @@ -7117,7 +7289,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1", + "sha1", "thiserror", "url", "utf-8", @@ -7149,6 +7321,15 @@ dependencies = [ "uuid 1.2.1", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -7228,7 +7409,7 @@ dependencies = [ [[package]] name = "user-facing-error-macros" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "proc-macro2", "quote", @@ -7238,7 +7419,7 @@ dependencies = [ [[package]] name = "user-facing-errors" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=6bad339fc5b8bbc77e028eeae2038cf2ade2e6be#6bad339fc5b8bbc77e028eeae2038cf2ade2e6be" dependencies = [ "backtrace", "indoc", @@ -7430,9 +7611,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29952969fb5e10fe834a52eb29ad0814ccdfd8387159b0933edf1344a1c9cdcc" +checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" dependencies = [ "bitflags", "cairo-rs", @@ -7916,15 +8097,16 @@ dependencies = [ [[package]] name = "wry" -version = "0.21.1" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff5c1352b4266fdf92c63479d2f58ab4cd29dc4e78fbc1b62011ed1227926945" +checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" dependencies = [ "base64 0.13.1", "block", "cocoa", "core-graphics", "crossbeam-channel", + "dunce", "gdk", "gio", "glib", @@ -7940,6 +8122,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.6", + "soup2", "tao", "thiserror", "url", diff --git a/Cargo.toml b/Cargo.toml index dea20cf4a..768288e20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,25 +12,25 @@ members = [ ] [workspace.dependencies] -prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.4", features = [ +prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } -prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.4", features = [ +prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } -prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.4", features = [ +prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ "sqlite", ], default-features = false } rspc = { version = "0.1.2" } -normi = { version = "0.0.1" } -specta = { version = "0.0.4" } +specta = { version = "0.0.6" } +httpz = { version = "0.0.3" } swift-rs = { git = "https://github.com/Brendonovich/swift-rs.git", rev = "833e29ba333f1dfe303eaa21de78c4f8c5a3f2ff" } @@ -40,6 +40,6 @@ tokio = { version = "1.25.0" } # We use this patch so we can compile for the IOS simulator on M1 openssl-sys = { git = "https://github.com/spacedriveapp/rust-openssl", rev = "92c3dec225a9e984884d5b30a517e5d44a24d03b" } -rspc = { git = "https://github.com/oscartbeaumont/rspc", rev = "6243b5b6a1376940a40318340e5eaef22e4a2c22" } # TODO: Move back to crates.io when new jsonrpc executor + `tokio::spawn` in the Tauri IPC plugin is released -normi = { git = "https://github.com/oscartbeaumont/rspc", rev = "6243b5b6a1376940a40318340e5eaef22e4a2c22" } # TODO: When normi is released on crates.io -specta = { git = "https://github.com/oscartbeaumont/rspc", rev = "6243b5b6a1376940a40318340e5eaef22e4a2c22" } # TODO: When normi is released on crates.io +rspc = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" } # TODO: Move back to crates.io when new jsonrpc executor + `tokio::spawn` in the Tauri IPC plugin + upgraded Tauri version is released +specta = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" } +httpz = { git = "https://github.com/oscartbeaumont/httpz", rev = "a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6" } \ No newline at end of file diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 8e5c574fc..0a384195e 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -18,13 +18,13 @@ "@sd/client": "workspace:*", "@sd/interface": "workspace:*", "@sd/ui": "workspace:*", - "@tauri-apps/api": "1.1.0", + "@tauri-apps/api": "1.2.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@sd/config": "workspace:*", - "@tauri-apps/cli": "1.1.1", + "@tauri-apps/cli": "1.2.3", "@types/babel-core": "^6.25.7", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index ee2fea74c..585f6fc55 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -10,13 +10,22 @@ edition = "2021" build = "build.rs" [dependencies] -tauri = { version = "1.1.1", features = ["api-all", "macos-private-api"] } +tauri = { version = "1.2.4", features = ["api-all", "linux-protocol-headers", "macos-private-api"] } rspc = { workspace = true, features = ["tauri"] } +httpz = { workspace = true, features = ["axum", "tauri"] } # TODO: The `axum` feature should be only enabled on Linux but this currently can't be done: https://github.com/rust-lang/cargo/issues/1197 sd-core = { path = "../../../core", features = ["ffmpeg", "location-watcher"] } tokio = { workspace = true, features = ["sync"] } window-shadows = "0.2.0" tracing = "0.1.36" serde = "1.0.145" +percent-encoding = "2.2.0" +http = "0.2.8" + +[target.'cfg(target_os = "linux")'.dependencies] +server = { path = "../../server" } +axum = "0.6.4" +rand = "0.8.5" +url = "2.1.1" [target.'cfg(target_os = "macos")'.dependencies] swift-rs.workspace = true diff --git a/apps/desktop/src-tauri/src/app_linux.rs b/apps/desktop/src-tauri/src/app_linux.rs new file mode 100644 index 000000000..7d8865b67 --- /dev/null +++ b/apps/desktop/src-tauri/src/app_linux.rs @@ -0,0 +1,95 @@ +use std::{ + net::{SocketAddr, TcpListener}, + sync::Arc, +}; + +use sd_core::Node; + +use axum::{ + extract::State, + http::{Request, StatusCode}, + middleware::{self, Next}, + response::{IntoResponse, Response}, + routing::get, +}; +use httpz::{Endpoint, HttpEndpoint}; +use rand::{distributions::Alphanumeric, Rng}; +use tauri::{plugin::TauriPlugin, Builder, Runtime}; +use tracing::debug; +use url::Url; + +pub(super) async fn setup( + app: Builder, + node: Arc, + endpoint: Endpoint, +) -> Builder { + let signal = server::utils::axum_shutdown_signal(node); + + let auth_token: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect(); + + let axum_app = axum::Router::new() + .route("/", get(|| async { "Spacedrive Server!" })) + .nest("/spacedrive", endpoint.axum()) + .route_layer(middleware::from_fn_with_state( + auth_token.clone(), + auth_middleware, + )) + .fallback(|| async { "404 Not Found: We're past the event horizon..." }); + + // Only allow current device to access it and randomise port + let listener = TcpListener::bind("127.0.0.1:0").expect("Error creating localhost server!"); + let listen_addr = listener + .local_addr() + .expect("Error getting localhost server listen addr!"); + + debug!("Localhost server listening on: http://{:?}", listen_addr); + + tokio::spawn(async move { + axum::Server::from_tcp(listener) + .expect("error creating HTTP server!") + .serve(axum_app.into_make_service()) + .with_graceful_shutdown(signal) + .await + .expect("Error with HTTP server!"); + }); + + app.plugin(spacedrive_plugin_init(&auth_token, listen_addr)) +} + +async fn auth_middleware( + State(auth_token): State, + request: Request, + next: Next, +) -> Response { + let url = Url::parse(&request.uri().to_string()).unwrap(); + if let Some((_, v)) = url.query_pairs().find(|(k, _)| k == "token") { + if v == auth_token { + return next.run(request).await; + } + } else if let Some(v) = request + .headers() + .get("Authorization") + .and_then(|v| v.to_str().ok()) + { + if v == auth_token { + return next.run(request).await; + } + } + + (StatusCode::UNAUTHORIZED, "Unauthorized!").into_response() +} + +pub fn spacedrive_plugin_init( + auth_token: &str, + listen_addr: SocketAddr, +) -> TauriPlugin { + tauri::plugin::Builder::new("spacedrive") + .js_init_script(format!( + r#"window.__SD_CUSTOM_SERVER_AUTH_TOKEN__ = "{auth_token}"; window.__SD_CUSTOM_URI_SERVER__ = "http://{listen_addr}";"# + )) + .build() +} diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index 0c1674f7d..b5d4f146f 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -3,24 +3,20 @@ windows_subsystem = "windows" )] -use std::error::Error; -use std::path::PathBuf; -use std::time::Duration; +use std::{error::Error, path::PathBuf, sync::Arc, time::Duration}; -use sd_core::Node; -use tauri::async_runtime::block_on; -use tauri::{ - api::path, - http::{ResponseBuilder, Uri}, - Manager, RunEvent, -}; -use tokio::task::block_in_place; -use tokio::time::sleep; +use sd_core::{custom_uri::create_custom_uri_endpoint, Node}; + +use tauri::{api::path, async_runtime::block_on, Manager, RunEvent}; +use tokio::{task::block_in_place, time::sleep}; use tracing::{debug, error}; #[cfg(target_os = "macos")] mod macos; +#[cfg(target_os = "linux")] +mod app_linux; + mod menu; #[tauri::command(async)] @@ -41,26 +37,21 @@ async fn main() -> Result<(), Box> { let (node, router) = Node::new(data_dir).await?; - let app = tauri::Builder::default() - .plugin(rspc::integrations::tauri::plugin(router, { - let node = node.clone(); - move || node.get_request_context() - })) - .register_uri_scheme_protocol("spacedrive", { - let node = node.clone(); - move |_, req| { - let url = req.uri().parse::().unwrap(); - let mut path = url.path().split('/').collect::>(); - path[0] = url.host().unwrap(); // The first forward slash causes an empty item and we replace it with the URL's host which you expect to be at the start + let app = tauri::Builder::default().plugin(rspc::integrations::tauri::plugin(router, { + let node = Arc::clone(&node); + move || node.get_request_context() + })); - let (status_code, content_type, body) = - block_in_place(|| block_on(node.handle_custom_uri(path))); - ResponseBuilder::new() - .status(status_code) - .mimetype(content_type) - .body(body) - } - }) + // This is a super cringe workaround for: https://github.com/tauri-apps/tauri/issues/3725 & https://bugs.webkit.org/show_bug.cgi?id=146351#c5 + let endpoint = create_custom_uri_endpoint(Arc::clone(&node)); + + #[cfg(target_os = "linux")] + let app = app_linux::setup(app, Arc::clone(&node), endpoint).await; + + #[cfg(not(target_os = "linux"))] + let app = app.register_uri_scheme_protocol("spacedrive", endpoint.tauri_uri_scheme("spacedrive")); + + let app = app .setup(|app| { let app = app.handle(); app.windows().iter().for_each(|(_, window)| { @@ -71,7 +62,9 @@ async fn main() -> Result<(), Box> { async move { sleep(Duration::from_secs(3)).await; if !window.is_visible().unwrap_or(true) { - println!("Window did not emit `app_ready` event fast enough. Showing window..."); + println!( + "Window did not emit `app_ready` event fast enough. Showing window..." + ); let _ = window.show(); } } diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 879c976ec..c6233cd1a 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -2,6 +2,7 @@ import { loggerLink } from '@rspc/client'; import { tauriLink } from '@rspc/tauri'; import { dialog, invoke, os, shell } from '@tauri-apps/api'; import { listen } from '@tauri-apps/api/event'; +import { convertFileSrc } from '@tauri-apps/api/tauri'; import { useEffect } from 'react'; import { getDebugState, hooks, queryClient } from '@sd/client'; import SpacedriveInterface, { OperatingSystem, Platform, PlatformProvider } from '@sd/interface'; @@ -30,9 +31,29 @@ async function getOs(): Promise { } } +let customUriServerUrl = (window as any).__SD_CUSTOM_URI_SERVER__ as string | undefined; +const customUriAuthToken = (window as any).__SD_CUSTOM_URI_TOKEN__ as string | undefined; + +if (customUriServerUrl && !customUriServerUrl?.endsWith('/')) { + customUriServerUrl += '/'; +} + +function getCustomUriURL(path: string): string { + if (customUriServerUrl) { + const queryParams = customUriAuthToken + ? `?token=${encodeURIComponent(customUriAuthToken)}` + : ''; + return `${customUriServerUrl}spacedrive/${path}${queryParams}`; + } else { + return convertFileSrc(path, 'spacedrive'); + } +} + const platform: Platform = { platform: 'tauri', - getThumbnailUrlById: (casId) => `spacedrive://thumbnail/${encodeURIComponent(casId)}`, + getThumbnailUrlById: (casId) => getCustomUriURL(`thumbnail/${casId}`), + getFileUrl: (libraryId, locationLocalId, filePathId) => + getCustomUriURL(`file/${libraryId}/${locationLocalId}/${filePathId}`), openLink: shell.open, getOs, openDirectoryPickerDialog: () => dialog.open({ directory: true }), diff --git a/apps/desktop/src/index.html b/apps/desktop/src/index.html index 819681673..91388ff0b 100644 --- a/apps/desktop/src/index.html +++ b/apps/desktop/src/index.html @@ -8,8 +8,6 @@
- - diff --git a/apps/desktop/src/index.tsx b/apps/desktop/src/index.tsx index d965b4ff7..e7337a723 100644 --- a/apps/desktop/src/index.tsx +++ b/apps/desktop/src/index.tsx @@ -6,6 +6,13 @@ import '@sd/ui/style'; import '~/patches'; import App from './App'; +// React dev tools extension +if (import.meta.env.DEV) { + var script = document.createElement('script'); + script.src = 'http://localhost:8097'; + document.head.appendChild(script); +} + const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/apps/mobile/rust/android/src/lib.rs b/apps/mobile/rust/android/src/lib.rs index b92d6b80a..f337a2bb8 100644 --- a/apps/mobile/rust/android/src/lib.rs +++ b/apps/mobile/rust/android/src/lib.rs @@ -36,10 +36,7 @@ pub extern "system" fn Java_com_spacedrive_app_SDCore_registerCoreEventListener( if let Err(err) = result { // TODO: Send rspc error or something here so we can show this in the UI. // TODO: Maybe reinitialise the core cause it could be in an invalid state? - println!( - "Error in Java_com_spacedrive_app_SDCore_registerCoreEventListener: {:?}", - err - ); + println!("Error in Java_com_spacedrive_app_SDCore_registerCoreEventListener: {err:?}"); } } @@ -86,9 +83,7 @@ pub extern "system" fn Java_com_spacedrive_app_SDCore_handleCoreMsg( ) .unwrap(); } - Err(_) => { - // TODO: handle error - } + Err(err) => error!(err), }); }); diff --git a/apps/mobile/rust/mobile/src/lib.rs b/apps/mobile/rust/mobile/src/lib.rs index 362475845..e84a14c1f 100644 --- a/apps/mobile/rust/mobile/src/lib.rs +++ b/apps/mobile/rust/mobile/src/lib.rs @@ -95,7 +95,7 @@ pub fn spawn_core_event_listener(callback: impl Fn(String) + Send + 'static) { let data = match to_string(&event) { Ok(json) => json, Err(err) => { - println!("Failed to serialize event: {}", err); + println!("Failed to serialize event: {err}"); continue; } }; diff --git a/apps/mobile/src/containers/OverviewStats.tsx b/apps/mobile/src/containers/OverviewStats.tsx index 0362837af..6aeb03d42 100644 --- a/apps/mobile/src/containers/OverviewStats.tsx +++ b/apps/mobile/src/containers/OverviewStats.tsx @@ -13,8 +13,8 @@ const StatItemNames: Partial> = { total_bytes_free: 'Free space' }; -const StatItem: FC<{ title: string; bytes: number }> = ({ title, bytes }) => { - const { value, unit } = byteSize(+bytes); +const StatItem: FC<{ title: string; bytes: bigint }> = ({ title, bytes }) => { + const { value, unit } = byteSize(Number(bytes)); // TODO: This BigInt to Number conversion will truncate the number if the number is too large. `byteSize` doesn't support BigInt so we are gonna need to come up with a longer term solution at some point. const count = useCounter({ name: title, end: Number(value) }); @@ -51,12 +51,13 @@ const OverviewStats = () => { return libraryStatistics ? ( - {Object.entries(libraryStatistics).map(([key, bytes]) => { + {Object.entries(libraryStatistics).map(([key, bytesRaw]) => { if (!displayableStatItems.includes(key)) return null; + let bytes = BigInt(bytesRaw); if (key === 'total_bytes_free') { - bytes = sizeInfo.freeSpace; + bytes = BigInt(sizeInfo.freeSpace); } else if (key === 'total_bytes_capacity') { - bytes = sizeInfo.totalSpace; + bytes = BigInt(sizeInfo.totalSpace); } return ; })} diff --git a/apps/mobile/src/stores/explorerStore.ts b/apps/mobile/src/stores/explorerStore.ts index d2edb639c..032760f97 100644 --- a/apps/mobile/src/stores/explorerStore.ts +++ b/apps/mobile/src/stores/explorerStore.ts @@ -4,11 +4,7 @@ import { resetStore } from '@sd/client'; // TODO: Add "media" export type ExplorerLayoutMode = 'list' | 'grid'; -export enum ExplorerKind { - Location, - Tag, - Space -} +export type ExplorerKind = 'Location' | 'Tag' | 'Space'; const state = { locationId: null as number | null, diff --git a/apps/server/Cargo.toml b/apps/server/Cargo.toml index b7e1fd3d2..a02a59d3a 100644 --- a/apps/server/Cargo.toml +++ b/apps/server/Cargo.toml @@ -6,7 +6,10 @@ edition = "2021" [dependencies] sd-core = { path = "../../core", features = ["ffmpeg"] } rspc = { workspace = true, features = ["axum"] } -axum = "0.5.16" +httpz = { workspace = true, features = ["axum"] } +axum = "0.6.4" tokio = { workspace = true, features = ["sync", "rt-multi-thread", "signal"] } tracing = "0.1.36" ctrlc = "3.2.3" +http = "0.2.8" +hyper = "0.14.23" diff --git a/apps/server/src/lib.rs b/apps/server/src/lib.rs new file mode 100644 index 000000000..b5614dd82 --- /dev/null +++ b/apps/server/src/lib.rs @@ -0,0 +1 @@ +pub mod utils; diff --git a/apps/server/src/main.rs b/apps/server/src/main.rs index a46e9c3cd..acfbad1ef 100644 --- a/apps/server/src/main.rs +++ b/apps/server/src/main.rs @@ -1,12 +1,7 @@ use std::{env, net::SocketAddr, path::Path}; -use axum::{ - extract, - handler::Handler, - http::{header::CONTENT_TYPE, HeaderMap, StatusCode}, - routing::get, -}; -use sd_core::Node; +use axum::routing::get; +use sd_core::{custom_uri::create_custom_uri_endpoint, Node}; use tracing::info; mod utils; @@ -39,29 +34,15 @@ async fn main() { let app = axum::Router::new() .route("/", get(|| async { "Spacedrive Server!" })) .route("/health", get(|| async { "OK" })) - .route("/spacedrive/*id", { - let node = node.clone(); - get(|extract::Path(path): extract::Path| async move { - let (status_code, content_type, body) = node - .handle_custom_uri(path.split('/').skip(1).collect()) - .await; - - ( - StatusCode::from_u16(status_code).unwrap(), - { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, content_type.parse().unwrap()); - headers - }, - body, - ) - }) - }) - .route( - "/rspc/:id", + .nest( + "/spacedrive", + create_custom_uri_endpoint(node.clone()).axum(), + ) + .nest( + "/rspc", router.endpoint(move || node.get_request_context()).axum(), ) - .fallback((|| async { "404 Not Found: We're past the event horizon..." }).into_service()); + .fallback(|| async { "404 Not Found: We're past the event horizon..." }); let mut addr = "[::]:8080".parse::().unwrap(); // This listens on IPv6 and IPv4 addr.set_port(port); diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index ca30c9482..6919cf8eb 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -23,11 +23,16 @@ const client = hooks.createClient({ }); const http = isDev ? 'http' : 'https'; +const spacedriveProtocol = `${http}://${serverOrigin}/spacedrive`; const platform: Platform = { platform: 'web', getThumbnailUrlById: (casId) => - `${http}://${serverOrigin}/spacedrive/thumbnail/${encodeURIComponent(casId)}.webp`, + `${spacedriveProtocol}/thumbnail/${encodeURIComponent(casId)}.webp`, + getFileUrl: (libraryId, locationLocalId, filePathId) => + `${spacedriveProtocol}/file/${encodeURIComponent(libraryId)}/${encodeURIComponent( + locationLocalId + )}/${encodeURIComponent(filePathId)}`, openLink: (url) => window.open(url, '_blank')?.focus(), demoMode: true }; diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index 907828be7..d965b4ff7 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -3,8 +3,8 @@ import React, { Suspense } from 'react'; import ReactDOM from 'react-dom/client'; import '@sd/ui/style'; // THIS MUST GO BEFORE importing the App +import '~/patches'; import App from './App'; -import './patches'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/core/Cargo.toml b/core/Cargo.toml index d63bb232b..1a6a1e6bf 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,15 +10,10 @@ rust-version = "1.67.0" [features] default = ["p2p"] -p2p = [ -] # This feature controls whether the Spacedrive Core contains the Peer to Peer syncing engine (It isn't required for the hosted core so we can disable it). -mobile = [ -] # This feature allows features to be disabled when the Core is running on mobile. +p2p = [] # This feature controls whether the Spacedrive Core contains the Peer to Peer syncing engine (It isn't required for the hosted core so we can disable it). +mobile = [] # This feature allows features to be disabled when the Core is running on mobile. android = ["dep:tracing-android"] -ffmpeg = [ - "dep:ffmpeg-next", - "dep:sd-ffmpeg", -] # This feature controls whether the Spacedrive Core contains functionality which requires FFmpeg. +ffmpeg = ["dep:ffmpeg-next", "dep:sd-ffmpeg"] # This feature controls whether the Spacedrive Core contains functionality which requires FFmpeg. location-watcher = ["dep:notify"] [dependencies] @@ -37,8 +32,8 @@ blake3 = "1.3.1" # Project dependencies rspc = { workspace = true, features = ["uuid", "chrono", "tracing"] } +httpz = { workspace = true } prisma-client-rust = { workspace = true } -normi = { workspace = true } specta = { workspace = true } uuid = { version = "1.1.2", features = ["v4", "serde"] } sysinfo = "0.26.4" @@ -73,6 +68,9 @@ notify = { version = "5.0.0", default-features = false, features = [ "macos_fsevent", ], optional = true } uhlc = "0.5.1" +http-range = "0.1.5" +mini-moka = "0.10.0" +serde_with = "2.2.0" dashmap = { version = "5.4.0", features = ["serde"] } [dev-dependencies] diff --git a/core/build.rs b/core/build.rs index cf49cbcb2..af19d274e 100644 --- a/core/build.rs +++ b/core/build.rs @@ -7,5 +7,5 @@ fn main() { .expect("error getting git hash. Does `git rev-parse --short HEAD` work for you?"); let git_hash = String::from_utf8(output.stdout) .expect("Error passing output of `git rev-parse --short HEAD`"); - println!("cargo:rustc-env=GIT_HASH={}", git_hash); + println!("cargo:rustc-env=GIT_HASH={git_hash}"); } diff --git a/core/src/api/keys.rs b/core/src/api/keys.rs index d15a4043e..b3bf83dee 100644 --- a/core/src/api/keys.rs +++ b/core/src/api/keys.rs @@ -342,7 +342,7 @@ pub(crate) fn mount() -> RouterBuilder { invalidate_query!(library, "keys.list"); invalidate_query!(library, "keys.listMounted"); - Ok(updated_keys.len()) + Ok(TryInto::::try_into(updated_keys.len()).unwrap()) // We convert from `usize` (bigint type) to `u32` (number type) because rspc doesn't support bigints. }) }) .library_mutation("changeMasterPassword", |t| { diff --git a/core/src/api/locations.rs b/core/src/api/locations.rs index f3a80a59e..164e4aa54 100644 --- a/core/src/api/locations.rs +++ b/core/src/api/locations.rs @@ -9,7 +9,7 @@ use crate::{ use std::path::PathBuf; -use rspc::{self, internal::MiddlewareBuilderLike, ErrorCode, Type}; +use rspc::{self, ErrorCode, RouterBuilderLike, Type}; use serde::{Deserialize, Serialize}; use super::{utils::LibraryRequest, Ctx, RouterBuilder}; @@ -27,11 +27,11 @@ pub enum ExplorerContext { pub enum ExplorerItem { Path { has_thumbnail: bool, - item: Box, + item: file_path_with_object::Data, }, Object { has_thumbnail: bool, - item: Box, + item: object_with_file_paths::Data, }, } @@ -45,12 +45,7 @@ file_path::include!(file_path_with_object { object }); object::include!(object_with_file_paths { file_paths }); indexer_rules_in_location::include!(indexer_rules_in_location_with_rules { indexer_rule }); -// TODO(@Oscar): This return type sucks. Add an upstream rspc solution. -pub(crate) fn mount() -> rspc::RouterBuilder< - Ctx, - (), - impl MiddlewareBuilderLike + Send + 'static, -> { +pub(crate) fn mount() -> impl RouterBuilderLike { ::new() .library_query("list", |t| { t(|_, _: (), library| async move { @@ -149,7 +144,7 @@ pub(crate) fn mount() -> rspc::RouterBuilder< items.push(ExplorerItem::Path { has_thumbnail, - item: Box::new(file_path), + item: file_path, }); } diff --git a/core/src/api/mod.rs b/core/src/api/mod.rs index 7e2774cd7..7e711a13b 100644 --- a/core/src/api/mod.rs +++ b/core/src/api/mod.rs @@ -42,7 +42,6 @@ mod keys; mod libraries; mod locations; mod nodes; -mod normi; mod tags; pub mod utils; pub mod volumes; @@ -90,15 +89,14 @@ pub(crate) fn mount() -> Arc { }) }) }) - .merge("normi.", normi::mount()) - .merge("library.", libraries::mount()) - .merge("volumes.", volumes::mount()) - .merge("tags.", tags::mount()) - .merge("nodes.", nodes::mount()) - .merge("keys.", keys::mount()) - .merge("locations.", locations::mount()) - .merge("files.", files::mount()) - .merge("jobs.", jobs::mount()) + .yolo_merge("library.", libraries::mount()) + .yolo_merge("volumes.", volumes::mount()) + .yolo_merge("tags.", tags::mount()) + .yolo_merge("nodes.", nodes::mount()) + .yolo_merge("keys.", keys::mount()) + .yolo_merge("locations.", locations::mount()) + .yolo_merge("files.", files::mount()) + .yolo_merge("jobs.", jobs::mount()) // TODO: Scope the invalidate queries to a specific library (filtered server side) .subscription("invalidateQuery", |t| { t(|ctx, _: ()| { diff --git a/core/src/api/normi.rs b/core/src/api/normi.rs deleted file mode 100644 index f345ca285..000000000 --- a/core/src/api/normi.rs +++ /dev/null @@ -1,91 +0,0 @@ -use normi::{typed, Object}; -use rspc::Type; -use serde::Serialize; - -use super::RouterBuilder; - -#[derive(Serialize, Type, Object)] -#[normi(rename = "org")] -pub struct Organisation { - #[normi(id)] - pub id: String, - pub name: String, - #[normi(refr)] - pub users: Vec, - #[normi(refr)] - pub owner: User, - pub non_normalised_data: Vec<()>, -} - -#[derive(Serialize, Type, Object)] -pub struct User { - #[normi(id)] - pub id: String, - pub name: String, -} - -#[derive(Serialize, Type, Object)] -pub struct CompositeId { - #[normi(id)] - pub org_id: String, - #[normi(id)] - pub user_id: String, -} - -pub fn mount() -> RouterBuilder { - RouterBuilder::new() - .query("version", |t| t(|_, _: ()| "0.1.0")) - .query("userSync", |t| { - t.resolver(|_, _: ()| User { - id: "1".to_string(), - name: "Monty Beaumont".to_string(), - }) - .map(typed) - }) - .query("user", |t| { - t.resolver(|_, _: ()| async move { - Ok(User { - id: "1".to_string(), - name: "Monty Beaumont".to_string(), - }) - }) - .map(typed) - }) - .query("org", |t| { - t.resolver(|_, _: ()| async move { - Ok(Organisation { - id: "org-1".into(), - name: "Org 1".into(), - users: vec![ - User { - id: "user-1".into(), - name: "Monty Beaumont".into(), - }, - User { - id: "user-2".into(), - name: "Millie Beaumont".into(), - }, - User { - id: "user-3".into(), - name: "Oscar Beaumont".into(), - }, - ], - owner: User { - id: "user-1".into(), - name: "Monty Beaumont".into(), - }, - non_normalised_data: vec![(), ()], - }) - }) - .map(typed) - }) - .query("composite", |t| { - t.resolver(|_, _: ()| async move { - Ok(CompositeId { - org_id: "org-1".into(), - user_id: "user-1".into(), - }) - }) - .map(typed) - }) -} diff --git a/core/src/api/tags.rs b/core/src/api/tags.rs index e9ac010fd..0d842ebcf 100644 --- a/core/src/api/tags.rs +++ b/core/src/api/tags.rs @@ -79,7 +79,7 @@ pub(crate) fn mount() -> RouterBuilder { items.push(ExplorerItem::Object { has_thumbnail, - item: Box::new(object), + item: object, }); } diff --git a/core/src/api/utils/invalidate.rs b/core/src/api/utils/invalidate.rs index 14639145a..93eceb757 100644 --- a/core/src/api/utils/invalidate.rs +++ b/core/src/api/utils/invalidate.rs @@ -32,7 +32,7 @@ impl InvalidateOperationEvent { #[allow(dead_code)] pub(crate) struct InvalidationRequest { pub key: &'static str, - pub arg_ty: Option, + pub input_ty: Option, pub macro_src: &'static str, } @@ -60,8 +60,8 @@ impl InvalidRequests { let queries = r.queries(); for req in &invalidate_requests.queries { if let Some(query_ty) = queries.get(req.key) { - if let Some(arg) = &req.arg_ty { - if &query_ty.ty.arg_ty != arg { + if let Some(input) = &req.input_ty { + if &query_ty.ty.input != input { panic!( "Error at '{}': Attempted to invalid query '{}' but the argument type does not match the type defined on the router.", req.macro_src, req.key @@ -104,8 +104,8 @@ macro_rules! invalidate_query { .queries .push(crate::api::utils::InvalidationRequest { key: $key, - arg_ty: None, - macro_src: concat!(file!(), ":", line!()), + input_ty: None, + macro_src: concat!(file!(), ":", line!()), }) } } @@ -115,8 +115,8 @@ macro_rules! invalidate_query { crate::api::utils::InvalidateOperationEvent::dangerously_create($key, serde_json::Value::Null) )) }}; - ($ctx:expr, $key:literal: $arg_ty:ty, $arg:expr $(,)?) => {{ - let _: $arg_ty = $arg; // Assert the type the user provided is correct + ($ctx:expr, $key:literal: $input_ty:ty, $input:expr $(,)?) => {{ + let _: $input_ty = $input; // Assert the type the user provided is correct let ctx: &crate::library::LibraryContext = &$ctx; // Assert the context is the correct type #[cfg(debug_assertions)] @@ -129,7 +129,7 @@ macro_rules! invalidate_query { .queries .push(crate::api::utils::InvalidationRequest { key: $key, - arg_ty: Some(<$arg_ty as rspc::internal::specta::Type>::reference(rspc::internal::specta::DefOpts { + input_ty: Some(<$input_ty as rspc::internal::specta::Type>::reference(rspc::internal::specta::DefOpts { parent_inline: false, type_map: &mut rspc::internal::specta::TypeDefs::new(), }, &[])), @@ -139,7 +139,7 @@ macro_rules! invalidate_query { } // The error are ignored here because they aren't mission critical. If they fail the UI might be outdated for a bit. - let _ = serde_json::to_value($arg) + let _ = serde_json::to_value($input) .map(|v| ctx.emit(crate::api::CoreEvent::InvalidateOperation( crate::api::utils::InvalidateOperationEvent::dangerously_create($key, v), diff --git a/core/src/api/utils/library.rs b/core/src/api/utils/library.rs index 8f537d3e5..893ddc87d 100644 --- a/core/src/api/utils/library.rs +++ b/core/src/api/utils/library.rs @@ -67,7 +67,7 @@ pub trait LibraryRequest { } // Note: This will break with middleware context switching but that's fine for now -impl LibraryRequest for rspc::RouterBuilder +impl LibraryRequest for rspc::RouterBuilder where TMiddleware: MiddlewareBuilderLike + Send + 'static, { diff --git a/core/src/custom_uri.rs b/core/src/custom_uri.rs new file mode 100644 index 000000000..246e7ff84 --- /dev/null +++ b/core/src/custom_uri.rs @@ -0,0 +1,300 @@ +use crate::{prisma::file_path, Node}; + +use std::{cmp::min, io, path::PathBuf, str::FromStr, sync::Arc}; + +use http_range::HttpRange; +use httpz::{ + http::{Method, Response, StatusCode}, + Endpoint, GenericEndpoint, HttpEndpoint, Request, +}; +use mini_moka::sync::Cache; +use once_cell::sync::Lazy; +use prisma_client_rust::QueryError; +use thiserror::Error; +use tokio::{ + fs::{self, File}, + io::{AsyncReadExt, AsyncSeekExt, SeekFrom}, +}; +use tracing::{error, warn}; +use uuid::Uuid; + +// This LRU cache allows us to avoid doing a DB lookup on every request. +// The main advantage of this LRU Cache is for video files. Video files are fetch in multiple chunks and the cache prevents a DB lookup on every chunk reducing the request time from 15-25ms to 1-10ms. +type MetadataCacheKey = (Uuid, i32, i32); +static FILE_METADATA_CACHE: Lazy)>> = + Lazy::new(|| Cache::new(100)); + +// TODO: We should listen to events when deleting or moving a location and evict the cache accordingly. +// TODO: Probs use this cache in rspc queries too! + +async fn handler(node: Arc, req: Request) -> Result>, HandleCustomUriError> { + let path = req + .uri() + .path() + .strip_prefix('/') + .unwrap_or_else(|| req.uri().path()) + .split('/') + .collect::>(); + + match path.first() { + Some(&"thumbnail") => handle_thumbnail(&node, &path).await, + Some(&"file") => handle_file(&node, &path, &req).await, + _ => Err(HandleCustomUriError::BadRequest("Invalid operation!")), + } +} + +async fn handle_thumbnail( + node: &Node, + path: &[&str], +) -> Result>, HandleCustomUriError> { + let file_cas_id = path + .get(1) + .ok_or_else(|| HandleCustomUriError::BadRequest("Invalid number of parameters!"))?; + let filename = node + .config + .data_directory() + .join("thumbnails") + .join(file_cas_id) + .with_extension("webp"); + + let buf = fs::read(&filename).await.map_err(|err| { + if err.kind() == io::ErrorKind::NotFound { + HandleCustomUriError::NotFound("file") + } else { + err.into() + } + })?; + + Ok(Response::builder() + .header("Content-Type", "image/webp") + .status(StatusCode::OK) + .body(buf)?) +} + +async fn handle_file( + node: &Node, + path: &[&str], + req: &Request, +) -> Result>, HandleCustomUriError> { + let library_id = path + .get(1) + .and_then(|id| Uuid::from_str(id).ok()) + .ok_or_else(|| { + HandleCustomUriError::BadRequest("Invalid number of parameters. Missing library_id!") + })?; + + let location_id = path + .get(2) + .and_then(|id| id.parse::().ok()) + .ok_or_else(|| { + HandleCustomUriError::BadRequest("Invalid number of parameters. Missing location_id!") + })?; + + let file_path_id = path + .get(3) + .and_then(|id| id.parse::().ok()) + .ok_or_else(|| { + HandleCustomUriError::BadRequest("Invalid number of parameters. Missing file_path_id!") + })?; + + let lru_cache_key = (library_id, location_id, file_path_id); + + let (file_path_materialized_path, extension) = + if let Some(entry) = FILE_METADATA_CACHE.get(&lru_cache_key) { + entry + } else { + let library = node + .library_manager + .get_ctx(library_id) + .await + .ok_or_else(|| HandleCustomUriError::NotFound("library"))?; + let file_path = library + .db + .file_path() + .find_unique(file_path::location_id_id(location_id, file_path_id)) + .include(file_path::include!({ location })) + .exec() + .await? + .ok_or_else(|| HandleCustomUriError::NotFound("object"))?; + + let lru_entry = ( + PathBuf::from(file_path.location.local_path.ok_or_else(|| { + warn!( + "Location '{}' doesn't have local path set", + file_path.location_id + ); + HandleCustomUriError::BadRequest("Location doesn't have `local_path` set!") + })?) + .join(&file_path.materialized_path), + file_path.extension, + ); + FILE_METADATA_CACHE.insert(lru_cache_key, lru_entry.clone()); + + lru_entry + }; + + let mut file = File::open(file_path_materialized_path) + .await + .map_err(|err| { + if err.kind() == io::ErrorKind::NotFound { + HandleCustomUriError::NotFound("file") + } else { + err.into() + } + })?; + + let metadata = file.metadata().await?; + + // TODO: This should be determined from magic bytes when the file is indexed and stored it in the DB on the file path + let (mime_type, is_video) = match extension.as_deref() { + Some("mp4") => ("video/mp4", true), + Some("webm") => ("video/webm", true), + Some("mkv") => ("video/x-matroska", true), + Some("avi") => ("video/x-msvideo", true), + Some("mov") => ("video/quicktime", true), + Some("png") => ("image/png", false), + Some("jpg") => ("image/jpeg", false), + Some("jpeg") => ("image/jpeg", false), + Some("gif") => ("image/gif", false), + Some("webp") => ("image/webp", false), + Some("svg") => ("image/svg+xml", false), + _ => { + return Err(HandleCustomUriError::BadRequest( + "TODO: This filetype is not supported because of the missing mime type!", + )); + } + }; + + if is_video { + let mut response = Response::builder(); + let mut status_code = 200; + + // if the webview sent a range header, we need to send a 206 in return + let buf = if let Some(range) = req.headers().get("range") { + let mut buf = Vec::new(); + let file_size = metadata.len(); + let range = HttpRange::parse( + range + .to_str() + .map_err(|_| HandleCustomUriError::BadRequest("Error passing range header!"))?, + file_size, + ) + .map_err(|_| HandleCustomUriError::BadRequest("Error passing range!"))?; + // let support only 1 range for now + let first_range = range.first(); + if let Some(range) = first_range { + let mut real_length = range.length; + + // prevent max_length; + // specially on webview2 + if range.length > file_size / 3 { + // max size sent (400kb / request) + // as it's local file system we can afford to read more often + real_length = min(file_size - range.start, 1024 * 400); + } + + // last byte we are reading, the length of the range include the last byte + // who should be skipped on the header + let last_byte = range.start + real_length - 1; + status_code = 206; + + // Only macOS and Windows are supported, if you set headers in linux they are ignored + response = response + .header("Connection", "Keep-Alive") + .header("Accept-Ranges", "bytes") + .header("Content-Length", real_length) + .header( + "Content-Range", + format!("bytes {}-{}/{}", range.start, last_byte, file_size), + ); + + // FIXME: Add ETag support (caching on the webview) + + file.seek(SeekFrom::Start(range.start)).await?; + file.take(real_length).read_to_end(&mut buf).await?; + } else { + file.read_to_end(&mut buf).await?; + } + + buf + } else { + // Linux is mega cringe and doesn't support streaming so we just load the whole file into memory and return it + let mut buf = Vec::with_capacity(metadata.len() as usize); + file.read_to_end(&mut buf).await?; + buf + }; + + Ok(response + .header("Content-type", mime_type) + .status(status_code) + .body(buf)?) + } else { + let mut buf = Vec::with_capacity(metadata.len() as usize); + file.read_to_end(&mut buf).await?; + Ok(Response::builder() + .header("Content-Type", mime_type) + .status(StatusCode::OK) + .body(buf)?) + } +} + +pub fn create_custom_uri_endpoint(node: Arc) -> Endpoint { + GenericEndpoint::new("/*any", [Method::GET, Method::POST], move |req: Request| { + let node = node.clone(); + async move { handler(node, req).await.unwrap_or_else(Into::into) } + }) +} + +#[derive(Error, Debug)] +pub enum HandleCustomUriError { + #[error("error creating http request/response: {0}")] + Http(#[from] httpz::http::Error), + #[error("io error: {0}")] + Io(#[from] io::Error), + #[error("query error: {0}")] + QueryError(#[from] QueryError), + #[error("{0}")] + BadRequest(&'static str), + #[error("resource '{0}' not found")] + NotFound(&'static str), +} + +impl From for Response> { + fn from(value: HandleCustomUriError) -> Self { + let builder = Response::builder().header("Content-Type", "text/plain"); + + (match value { + HandleCustomUriError::Http(err) => { + error!("Error creating http request/response: {}", err); + builder + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(b"Internal Server Error".to_vec()) + } + HandleCustomUriError::Io(err) => { + error!("IO error: {}", err); + builder + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(b"Internal Server Error".to_vec()) + } + HandleCustomUriError::QueryError(err) => { + error!("Query error: {}", err); + builder + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(b"Internal Server Error".to_vec()) + } + HandleCustomUriError::BadRequest(msg) => { + error!("Bad request: {}", msg); + builder + .status(StatusCode::BAD_REQUEST) + .body(msg.as_bytes().to_vec()) + } + HandleCustomUriError::NotFound(resource) => builder.status(StatusCode::NOT_FOUND).body( + format!("Resource '{resource}' not found") + .as_bytes() + .to_vec(), + ), + }) + // SAFETY: This unwrap is ok as we have an hardcoded the response builders. + .expect("internal error building hardcoded HTTP error response") + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index f88b95fcd..12417483a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,15 +7,12 @@ use util::secure_temp_keystore::SecureTempKeystore; use std::{path::Path, sync::Arc}; use thiserror::Error; -use tokio::{ - fs::{self, File}, - io::AsyncReadExt, - sync::broadcast, -}; +use tokio::{fs, sync::broadcast}; use tracing::{error, info}; use tracing_subscriber::{prelude::*, EnvFilter}; pub mod api; +pub mod custom_uri; pub(crate) mod job; pub(crate) mod library; pub(crate) mod location; @@ -187,50 +184,6 @@ impl Node { } } - // Note: this system doesn't use chunked encoding which could prove a problem with large files but I can't see an easy way to do chunked encoding with Tauri custom URIs. - pub async fn handle_custom_uri( - &self, - path: Vec<&str>, - ) -> ( - u16, /* Status Code */ - &str, /* Content-Type */ - Vec, /* Body */ - ) { - match path.first().copied() { - Some("thumbnail") => { - if path.len() != 2 { - return ( - 400, - "text/html", - b"Bad Request: Invalid number of parameters".to_vec(), - ); - } - - let filename = Path::new(&self.config.data_directory()) - .join("thumbnails") - .join(path[1] /* file_cas_id */) - .with_extension("webp"); - match File::open(&filename).await { - Ok(mut file) => { - let mut buf = match fs::metadata(&filename).await { - Ok(metadata) => Vec::with_capacity(metadata.len() as usize), - Err(_) => Vec::new(), - }; - - file.read_to_end(&mut buf).await.unwrap(); - (200, "image/webp", buf) - } - Err(_) => (404, "text/html", b"File Not Found".to_vec()), - } - } - _ => ( - 400, - "text/html", - b"Bad Request: Invalid operation!".to_vec(), - ), - } - } - pub async fn shutdown(&self) { info!("Spacedrive shutting down..."); self.jobs.pause().await; diff --git a/core/src/library/library_config.rs b/core/src/library/library_config.rs index 168660455..6c63f296b 100644 --- a/core/src/library/library_config.rs +++ b/core/src/library/library_config.rs @@ -1,6 +1,6 @@ use std::{ fs::File, - io::{BufReader, Seek, SeekFrom}, + io::{BufReader, Seek}, path::PathBuf, }; @@ -35,7 +35,7 @@ impl LibraryConfig { Self::migrate_config(base_config.version, file_dir)?; - file.seek(SeekFrom::Start(0))?; + file.rewind()?; Ok(serde_json::from_reader(BufReader::new(&mut file))?) } diff --git a/core/src/node/config.rs b/core/src/node/config.rs index 92f79cd57..8dd9906f0 100644 --- a/core/src/node/config.rs +++ b/core/src/node/config.rs @@ -2,7 +2,7 @@ use rspc::Type; use serde::{Deserialize, Serialize}; use std::{ fs::File, - io::{self, BufReader, Seek, SeekFrom, Write}, + io::{self, BufReader, Seek, Write}, path::{Path, PathBuf}, sync::Arc, }; @@ -66,7 +66,7 @@ impl NodeConfig { // SAFETY: This is just for display purposes so it doesn't matter if it's lossy Ok(hostname) => hostname.to_string_lossy().into_owned(), Err(err) => { - eprintln!("Falling back to default node name as an error occurred getting your systems hostname: '{}'", err); + eprintln!("Falling back to default node name as an error occurred getting your systems hostname: '{err}'"); "my-spacedrive".into() } }, @@ -123,7 +123,7 @@ impl NodeConfigManager { Self::migrate_config(base_config.version, path)?; - file.seek(SeekFrom::Start(0))?; + file.rewind()?; Ok(serde_json::from_reader(BufReader::new(&mut file))?) } false => { diff --git a/core/src/object/fs/erase.rs b/core/src/object/fs/erase.rs index af094a804..2b1c66d28 100644 --- a/core/src/object/fs/erase.rs +++ b/core/src/object/fs/erase.rs @@ -3,6 +3,7 @@ use crate::job::{JobError, JobReportUpdate, JobResult, JobState, StatefulJob, Wo use std::{hash::Hash, path::PathBuf}; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; use specta::Type; use tokio::{fs::OpenOptions, io::AsyncWriteExt}; use tracing::{trace, warn}; @@ -11,10 +12,13 @@ use super::{context_menu_fs_info, FsInfo}; pub struct FileEraserJob {} +#[serde_as] #[derive(Serialize, Deserialize, Hash, Type)] pub struct FileEraserJobInit { pub location_id: i32, pub path_id: i32, + #[specta(type = String)] + #[serde_as(as = "DisplayFromStr")] pub passes: usize, } diff --git a/core/src/volume.rs b/core/src/volume.rs index cd2ec8870..ec58fa3a1 100644 --- a/core/src/volume.rs +++ b/core/src/volume.rs @@ -2,15 +2,21 @@ use crate::{library::LibraryContext, prisma::volume::*}; use rspc::Type; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; use std::process::Command; use sysinfo::{DiskExt, System, SystemExt}; use thiserror::Error; +#[serde_as] #[derive(Serialize, Deserialize, Debug, Default, Clone, Type)] pub struct Volume { pub name: String, pub mount_point: String, + #[specta(type = String)] + #[serde_as(as = "DisplayFromStr")] pub total_capacity: u64, + #[specta(type = String)] + #[serde_as(as = "DisplayFromStr")] pub available_capacity: u64, pub is_removable: bool, pub disk_type: Option, diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index 498a80832..e6f518b76 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -7,6 +7,10 @@ description = "A library to handle cryptographic functions within Spacedrive" edition = "2021" rust-version = "1.67.0" +[features] +rspc = ["dep:rspc"] +serde = ["dep:serde", "dep:serde_json", "dep:serde-big-array", "uuid/serde"] + [dependencies] # rng rand = "0.8.5" @@ -41,7 +45,6 @@ dashmap = "5.4.0" # optional, for support with rspc rspc = { workspace = true, features = ["uuid"], optional = true } -specta = { workspace = true, optional = true } # for asynchronous crypto tokio = { workspace = true, features = ["io-util", "rt-multi-thread", "sync"] } @@ -62,14 +65,10 @@ tokio = { workspace = true, features = [ "macros", ] } # features needed for examples -[features] -rspc = ["dep:rspc", "dep:specta"] -serde = ["dep:serde", "dep:serde_json", "dep:serde-big-array", "uuid/serde"] - -# [[bench]] -# name = "aes-256-gcm" -# path = "benches/aes-256-gcm.rs" -# harness = false +[[bench]] +name = "aes-256-gcm" +path = "benches/aes-256-gcm.rs" +harness = false # [[bench]] # name = "xchacha20-poly1305" diff --git a/crates/crypto/src/crypto/stream.rs b/crates/crypto/src/crypto/stream.rs index 15a4f75be..bffe80a76 100644 --- a/crates/crypto/src/crypto/stream.rs +++ b/crates/crypto/src/crypto/stream.rs @@ -25,7 +25,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; derive(serde::Serialize), derive(serde::Deserialize) )] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum Algorithm { XChaCha20Poly1305, Aes256Gcm, diff --git a/crates/crypto/src/keys/hashing.rs b/crates/crypto/src/keys/hashing.rs index d1b6a66b8..3eab6c389 100644 --- a/crates/crypto/src/keys/hashing.rs +++ b/crates/crypto/src/keys/hashing.rs @@ -30,7 +30,7 @@ use balloon_hash::Balloon; derive(serde::Serialize), derive(serde::Deserialize) )] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum Params { Standard, Hardened, @@ -45,7 +45,7 @@ pub enum Params { derive(serde::Deserialize), serde(tag = "name", content = "params") )] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum HashingAlgorithm { Argon2id(Params), BalloonBlake3(Params), diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 38233042e..ebb965484 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -62,7 +62,7 @@ use super::{ /// This is a stored key, and can be freely written to Prisma/another database. #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub struct StoredKey { pub uuid: Uuid, // uuid for identification. shared with mounted keys pub version: StoredKeyVersion, @@ -81,7 +81,7 @@ pub struct StoredKey { #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum StoredKeyType { User, Root, @@ -89,7 +89,7 @@ pub enum StoredKeyType { #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum StoredKeyVersion { V1, } diff --git a/crates/crypto/src/keys/keyring/apple.rs b/crates/crypto/src/keys/keyring/apple.rs index bbe387180..881f76c1c 100644 --- a/crates/crypto/src/keys/keyring/apple.rs +++ b/crates/crypto/src/keys/keyring/apple.rs @@ -1,6 +1,6 @@ //! This is Spacedrive's Apple OS keyring integration. It has no strict dependencies. //! -//! This has been tested on MacOS, but should work just the same for iOS (according to the `security_framework` documentation) +//! This has been tested on macOS, but should work just the same for iOS (according to the `security_framework` documentation) use super::{Identifier, Keyring}; use crate::{primitives::types::SecretKeyString, Error, Protected, Result}; @@ -13,19 +13,19 @@ pub struct AppleKeyring; impl Keyring for AppleKeyring { fn insert(&self, identifier: Identifier, value: SecretKeyString) -> Result<()> { set_generic_password( - &identifier.application, + identifier.application, &identifier.to_apple_account(), value.expose().as_bytes(), ) .map_err(Error::AppleKeyringError) } fn retrieve(&self, identifier: Identifier) -> Result>> { - get_generic_password(&identifier.application, &identifier.to_apple_account()) + get_generic_password(identifier.application, &identifier.to_apple_account()) .map(Protected::new) .map_err(Error::AppleKeyringError) } fn delete(&self, identifier: Identifier) -> Result<()> { - delete_generic_password(&identifier.application, &identifier.to_apple_account()) + delete_generic_password(identifier.application, &identifier.to_apple_account()) .map_err(Error::AppleKeyringError) } } diff --git a/crates/crypto/src/primitives/types.rs b/crates/crypto/src/primitives/types.rs index f62310275..dca07d912 100644 --- a/crates/crypto/src/primitives/types.rs +++ b/crates/crypto/src/primitives/types.rs @@ -6,7 +6,7 @@ use crate::{crypto::stream::Algorithm, keys::hashing::HashingAlgorithm, Error, P #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub enum Nonce { XChaCha20Poly1305([u8; 20]), Aes256Gcm([u8; 8]), @@ -220,7 +220,7 @@ use serde_big_array::BigArray; use super::{to_array, ENCRYPTED_KEY_LEN, KEY_LEN, SALT_LEN, SECRET_KEY_LEN}; #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub struct EncryptedKey( #[cfg_attr(feature = "serde", serde(with = "BigArray"))] // salt used for file data pub [u8; ENCRYPTED_KEY_LEN], @@ -244,7 +244,7 @@ impl TryFrom> for EncryptedKey { #[derive(Clone, PartialEq, Eq, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub struct Salt(pub [u8; SALT_LEN]); impl Salt { @@ -274,7 +274,7 @@ impl TryFrom> for Salt { #[derive(Clone)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] -#[cfg_attr(feature = "rspc", derive(specta::Type))] +#[cfg_attr(feature = "rspc", derive(rspc::Type))] pub struct OnboardingConfig { pub password: Protected, pub algorithm: Algorithm, diff --git a/crates/crypto/src/protected.rs b/crates/crypto/src/protected.rs index bfd38b01c..882df6929 100644 --- a/crates/crypto/src/protected.rs +++ b/crates/crypto/src/protected.rs @@ -97,12 +97,16 @@ where } } +#[cfg(feature = "rspc")] +use rspc::internal::specta; #[cfg(feature = "rspc")] impl specta::Type for Protected where T: specta::Type + Zeroize, { const NAME: &'static str = T::NAME; + const SID: specta::TypeSid = specta::sid!(); + const IMPL_LOCATION: specta::ImplLocation = specta::impl_location!(); fn inline(opts: specta::DefOpts, generics: &[specta::DataType]) -> specta::DataType { T::inline(opts, generics) @@ -112,7 +116,7 @@ where T::reference(opts, generics) } - fn definition(opts: specta::DefOpts) -> specta::DataType { + fn definition(opts: specta::DefOpts) -> specta::DataTypeExt { T::definition(opts) } } diff --git a/crates/file-ext/src/magic.rs b/crates/file-ext/src/magic.rs index 667a944f2..17babd09b 100644 --- a/crates/file-ext/src/magic.rs +++ b/crates/file-ext/src/magic.rs @@ -4,7 +4,7 @@ use crate::extensions::{CodeExtension, Extension, VideoExtension}; use std::{ffi::OsStr, io::SeekFrom, path::Path}; use tokio::{ - fs::{self, File}, + fs::File, io::{AsyncReadExt, AsyncSeekExt}, }; diff --git a/crates/p2p/src/discovery/mdns.rs b/crates/p2p/src/discovery/mdns.rs index 17910f316..ce3638b33 100644 --- a/crates/p2p/src/discovery/mdns.rs +++ b/crates/p2p/src/discovery/mdns.rs @@ -104,7 +104,7 @@ impl Mdns { let service_info = ServiceInfo::new( &self.service_type, peer_id_str, - &format!("{}.", peer_id_str), + &format!("{peer_id_str}."), &(self .nm .lan_addrs diff --git a/crates/p2p/tunnel/utils/src/peer_id.rs b/crates/p2p/tunnel/utils/src/peer_id.rs index 9a736a607..c68039c54 100644 --- a/crates/p2p/tunnel/utils/src/peer_id.rs +++ b/crates/p2p/tunnel/utils/src/peer_id.rs @@ -27,7 +27,7 @@ impl PeerId { let peer_id = digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, &cert.0) .as_ref() .iter() - .map(|b| format!("{:02x}", b)) + .map(|b| format!("{b:02x}")) .collect(); Self(peer_id) diff --git a/crates/sync-generator/src/attribute/mod.rs b/crates/sync-generator/src/attribute/mod.rs index ae780f25a..48bba6189 100644 --- a/crates/sync-generator/src/attribute/mod.rs +++ b/crates/sync-generator/src/attribute/mod.rs @@ -8,6 +8,7 @@ pub enum AttributeFieldValue<'a> { List(Vec<&'a str>), } +#[allow(unused)] impl AttributeFieldValue<'_> { pub fn as_single(&self) -> Option<&str> { match self { @@ -44,10 +45,6 @@ pub fn model_attributes(model: &dml::Model) -> Vec { model .documentation .as_ref() - .map(|docs| { - docs.lines() - .flat_map(|line| Attribute::parse(line)) - .collect() - }) + .map(|docs| docs.lines().flat_map(Attribute::parse).collect()) .unwrap_or_default() } diff --git a/crates/sync-generator/src/lib.rs b/crates/sync-generator/src/lib.rs index 10987996a..d90fa1ea3 100644 --- a/crates/sync-generator/src/lib.rs +++ b/crates/sync-generator/src/lib.rs @@ -13,6 +13,7 @@ struct SDSyncGenerator {} type FieldVec<'a> = Vec<&'a dml::Field>; #[derive(Debug)] +#[allow(unused)] enum ModelSyncType<'a> { Local { id: FieldVec<'a>, @@ -79,7 +80,7 @@ impl PrismaGenerator for SDSyncGenerator { let model_modules = args.dml.models().map(|model| { let model_name_snake = snake_ident(&model.name); - let attributes = model_attributes(&model); + let attributes = model_attributes(model); let sync_id = attributes .iter() diff --git a/crates/sync/example/Cargo.toml b/crates/sync/example/Cargo.toml index a2d73f93b..1ffc7dcf3 100644 --- a/crates/sync/example/Cargo.toml +++ b/crates/sync/example/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] serde_json = "1.0.85" serde = { version = "1.0.145", features = ["derive"] } -axum = "0.5.16" +axum = "0.6.4" rspc = { workspace = true, features = ["axum"] } tokio = { workspace = true, features = ["full"] } prisma-client-rust = { workspace = true } diff --git a/crates/sync/src/crdt.rs b/crates/sync/src/crdt.rs index 7e1f9d6f0..9457cb9f8 100644 --- a/crates/sync/src/crdt.rs +++ b/crates/sync/src/crdt.rs @@ -75,7 +75,7 @@ pub enum CRDTOperationType { Owned(OwnedOperation), } -#[derive(Serialize, Deserialize, Clone, Type)] +#[derive(Serialize, Deserialize, Clone)] pub struct CRDTOperation { pub node: Uuid, pub timestamp: NTP64, diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index 978756513..4b4318419 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -4,36 +4,31 @@ export type Procedures = { queries: { key: "buildInfo", input: never, result: BuildInfo } | - { key: "files.get", input: LibraryArgs, result: { id: number, pub_id: Array, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: Array, media_data: MediaData | null } | null } | - { key: "jobs.getHistory", input: LibraryArgs, result: Array } | - { key: "jobs.getRunning", input: LibraryArgs, result: Array } | + { key: "files.get", input: LibraryArgs, result: { id: number, pub_id: number[], name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: FilePath[], media_data: MediaData | null } | null } | + { key: "jobs.getHistory", input: LibraryArgs, result: JobReport[] } | + { key: "jobs.getRunning", input: LibraryArgs, result: JobReport[] } | { key: "jobs.isRunning", input: LibraryArgs, result: boolean } | { key: "keys.getDefault", input: LibraryArgs, result: string | null } | { key: "keys.getKey", input: LibraryArgs, result: string } | { key: "keys.getSecretKey", input: LibraryArgs, result: string | null } | { key: "keys.isKeyManagerUnlocking", input: LibraryArgs, result: boolean | null } | { key: "keys.isUnlocked", input: LibraryArgs, result: boolean } | - { key: "keys.list", input: LibraryArgs, result: Array } | - { key: "keys.listMounted", input: LibraryArgs, result: Array } | + { key: "keys.list", input: LibraryArgs, result: StoredKey[] } | + { key: "keys.listMounted", input: LibraryArgs, result: string[] } | { key: "library.getStatistics", input: LibraryArgs, result: Statistics } | - { key: "library.list", input: never, result: Array } | - { key: "locations.getById", input: LibraryArgs, result: { id: number, pub_id: Array, node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string, indexer_rules: Array } | null } | + { key: "library.list", input: never, result: LibraryConfigWrapped[] } | + { key: "locations.getById", input: LibraryArgs, result: { id: number, pub_id: number[], node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string, indexer_rules: IndexerRulesInLocation[] } | null } | { key: "locations.getExplorerData", input: LibraryArgs, result: ExplorerData } | { key: "locations.indexer_rules.get", input: LibraryArgs, result: IndexerRule } | - { key: "locations.indexer_rules.list", input: LibraryArgs, result: Array } | - { key: "locations.indexer_rules.listForLocation", input: LibraryArgs, result: Array } | - { key: "locations.list", input: LibraryArgs, result: Array<{ id: number, pub_id: Array, node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string, node: Node }> } | + { key: "locations.indexer_rules.list", input: LibraryArgs, result: IndexerRule[] } | + { key: "locations.indexer_rules.listForLocation", input: LibraryArgs, result: IndexerRule[] } | + { key: "locations.list", input: LibraryArgs, result: { id: number, pub_id: number[], node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string, node: Node }[] } | { key: "nodeState", input: never, result: NodeState } | - { key: "normi.composite", input: never, result: NormalisedCompositeId } | - { key: "normi.org", input: never, result: NormalisedOrganisation } | - { key: "normi.user", input: never, result: NormalisedUser } | - { key: "normi.userSync", input: never, result: NormalisedUser } | - { key: "normi.version", input: never, result: string } | { key: "tags.get", input: LibraryArgs, result: Tag | null } | { key: "tags.getExplorerData", input: LibraryArgs, result: ExplorerData } | - { key: "tags.getForObject", input: LibraryArgs, result: Array } | - { key: "tags.list", input: LibraryArgs, result: Array } | - { key: "volumes.list", input: never, result: Array }, + { key: "tags.getForObject", input: LibraryArgs, result: Tag[] } | + { key: "tags.list", input: LibraryArgs, result: Tag[] } | + { key: "volumes.list", input: never, result: Volume[] }, mutations: { key: "files.copyFiles", input: LibraryArgs, result: null } | { key: "files.cutFiles", input: LibraryArgs, result: null } | @@ -82,141 +77,183 @@ export type Procedures = { subscriptions: { key: "invalidateQuery", input: never, result: InvalidateOperationEvent } | { key: "jobs.newThumbnail", input: LibraryArgs, result: string } | - { key: "locations.online", input: never, result: Array> } + { key: "locations.online", input: never, result: number[][] } }; +/** + * These are all possible algorithms that can be used for encryption and decryption + */ export type Algorithm = "XChaCha20Poly1305" | "Aes256Gcm" export type AuthOption = { type: "Password", value: string } | { type: "TokenizedPassword", value: string } -export interface AutomountUpdateArgs { uuid: string, status: boolean } +export type AutomountUpdateArgs = { uuid: string, status: boolean } -export interface BuildInfo { version: string, commit: string } +export type BuildInfo = { version: string, commit: string } -export interface ConfigMetadata { version: string | null } +/** + * ConfigMetadata is a part of node configuration that is loaded before the main configuration and contains information about the schema of the config. + * This allows us to migrate breaking changes to the config format between Spacedrive releases. + */ +export type ConfigMetadata = { version: string | null } -export interface CreateLibraryArgs { name: string, auth: AuthOption, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm } +export type CreateLibraryArgs = { name: string, auth: AuthOption, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm } -export interface EditLibraryArgs { id: string, name: string | null, description: string | null } +export type EditLibraryArgs = { id: string, name: string | null, description: string | null } -export type EncryptedKey = Array +export type EncryptedKey = number[] -export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } & Tag +export type ExplorerContext = ({ type: "Location" } & Location) | ({ type: "Tag" } & Tag) -export interface ExplorerData { context: ExplorerContext, items: Array } +export type ExplorerData = { context: ExplorerContext, items: ExplorerItem[] } -export type ExplorerItem = { type: "Path", has_thumbnail: boolean, item: FilePathWithObject } | { type: "Object", has_thumbnail: boolean, item: ObjectWithFilePaths } +export type ExplorerItem = { type: "Path", has_thumbnail: boolean, item: file_path_with_object } | { type: "Object", has_thumbnail: boolean, item: object_with_file_paths } -export interface FileCopierJobInit { source_location_id: number, source_path_id: number, target_location_id: number, target_path: string, target_file_name_suffix: string | null } +export type FileCopierJobInit = { source_location_id: number, source_path_id: number, target_location_id: number, target_path: string, target_file_name_suffix: string | null } -export interface FileCutterJobInit { source_location_id: number, source_path_id: number, target_location_id: number, target_path: string } +export type FileCutterJobInit = { source_location_id: number, source_path_id: number, target_location_id: number, target_path: string } -export interface FileDecryptorJobInit { location_id: number, path_id: number, mount_associated_key: boolean, output_path: string | null, password: string | null, save_to_library: boolean | null } +export type FileDecryptorJobInit = { location_id: number, path_id: number, mount_associated_key: boolean, output_path: string | null, password: string | null, save_to_library: boolean | null } -export interface FileDeleterJobInit { location_id: number, path_id: number } +export type FileDeleterJobInit = { location_id: number, path_id: number } -export interface FileEncryptorJobInit { location_id: number, path_id: number, key_uuid: string, algorithm: Algorithm, metadata: boolean, preview_media: boolean, output_path: string | null } +export type FileEncryptorJobInit = { location_id: number, path_id: number, key_uuid: string, algorithm: Algorithm, metadata: boolean, preview_media: boolean, output_path: string | null } -export interface FileEraserJobInit { location_id: number, path_id: number, passes: number } +export type FileEraserJobInit = { location_id: number, path_id: number, passes: string } -export interface FilePath { id: number, is_dir: boolean, cas_id: string | null, integrity_checksum: string | null, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string } +export type FilePath = { id: number, is_dir: boolean, cas_id: string | null, integrity_checksum: string | null, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string } -export interface GenerateThumbsForLocationArgs { id: number, path: string } +export type GenerateThumbsForLocationArgs = { id: number, path: string } -export interface GetArgs { id: number } +export type GetArgs = { id: number } +/** + * This defines all available password hashing algorithms. + */ export type HashingAlgorithm = { name: "Argon2id", params: Params } | { name: "BalloonBlake3", params: Params } -export interface IdentifyUniqueFilesArgs { id: number, path: string } +export type IdentifyUniqueFilesArgs = { id: number, path: string } -export interface IndexerRule { id: number, kind: number, name: string, parameters: Array, date_created: string, date_modified: string } +export type IndexerRule = { id: number, kind: number, name: string, parameters: number[], date_created: string, date_modified: string } -export interface IndexerRuleCreateArgs { kind: RuleKind, name: string, parameters: Array } +/** + * `IndexerRuleCreateArgs` is the argument received from the client using rspc to create a new indexer rule. + * Note that `parameters` field **MUST** be a JSON object serialized to bytes. + * + * In case of `RuleKind::AcceptFilesByGlob` or `RuleKind::RejectFilesByGlob`, it will be a + * single string containing a glob pattern. + * + * In case of `RuleKind::AcceptIfChildrenDirectoriesArePresent` or `RuleKind::RejectIfChildrenDirectoriesArePresent` the + * `parameters` field must be a vector of strings containing the names of the directories. + */ +export type IndexerRuleCreateArgs = { kind: RuleKind, name: string, parameters: number[] } -export interface IndexerRulesInLocation { date_created: string, location_id: number, indexer_rule_id: number } +export type IndexerRulesInLocation = { date_created: string, location_id: number, indexer_rule_id: number } -export interface InvalidateOperationEvent { key: string, arg: any } +export type InvalidateOperationEvent = { key: string, arg: any } -export interface JobReport { id: string, name: string, data: Array | null, metadata: any | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number } +export type JobReport = { id: string, name: string, data: number[] | null, metadata: any | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number } export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused" -export interface KeyAddArgs { algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, key: string, library_sync: boolean, automount: boolean } +export type KeyAddArgs = { algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, key: string, library_sync: boolean, automount: boolean } -export interface LibraryArgs { library_id: string, arg: T } +/** + * Can wrap a query argument to require it to contain a `library_id` and provide helpers for working with libraries. + */ +export type LibraryArgs = { library_id: string, arg: T } -export interface LibraryConfig { version: string | null, name: string, description: string } +/** + * LibraryConfig holds the configuration for a specific library. This is stored as a '{uuid}.sdlibrary' file. + */ +export type LibraryConfig = ({ version: string | null }) & { name: string, description: string } -export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig } +export type LibraryConfigWrapped = { uuid: string, config: LibraryConfig } -export interface Location { id: number, pub_id: Array, node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string } +export type Location = { id: number, pub_id: number[], node_id: number, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, is_archived: boolean, generate_preview_media: boolean, sync_preview_media: boolean, hidden: boolean, date_created: string } -export interface LocationCreateArgs { path: string, indexer_rules_ids: Array } +/** + * `LocationCreateArgs` is the argument received from the client using `rspc` to create a new location. + * It has the actual path and a vector of indexer rules ids, to create many-to-many relationships + * between the location and indexer rules. + */ +export type LocationCreateArgs = { path: string, indexer_rules_ids: number[] } -export interface LocationExplorerArgs { location_id: number, path: string, limit: number, cursor: string | null } +export type LocationExplorerArgs = { location_id: number, path: string, limit: number, cursor: string | null } -export interface LocationUpdateArgs { id: number, name: string | null, generate_preview_media: boolean | null, sync_preview_media: boolean | null, hidden: boolean | null, indexer_rules_ids: Array } +/** + * `LocationUpdateArgs` is the argument received from the client using `rspc` to update a location. + * It contains the id of the location to be updated, possible a name to change the current location's name + * and a vector of indexer rules ids to add or remove from the location. + * + * It is important to note that only the indexer rule ids in this vector will be used from now on. + * Old rules that aren't in this vector will be purged. + */ +export type LocationUpdateArgs = { id: number, name: string | null, generate_preview_media: boolean | null, sync_preview_media: boolean | null, hidden: boolean | null, indexer_rules_ids: number[] } -export interface MasterPasswordChangeArgs { password: string, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm } +export type MasterPasswordChangeArgs = { password: string, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm } -export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null } +export type MediaData = { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null } -export interface Node { id: number, pub_id: Array, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string } +export type Node = { id: number, pub_id: number[], name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string } -export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null } +/** + * NodeConfig is the configuration for a node. This is shared between all libraries and is stored in a JSON file on disk. + */ +export type NodeConfig = ({ version: string | null }) & { id: string, name: string, p2p_port: number | null } -export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string } +export type NodeState = (({ version: string | null }) & { id: string, name: string, p2p_port: number | null }) & { data_path: string } -export type Nonce = { XChaCha20Poly1305: Array } | { Aes256Gcm: Array } +export type Nonce = { XChaCha20Poly1305: number[] } | { Aes256Gcm: number[] } -export interface NormalisedCompositeId { $type: string, $id: any, org_id: string, user_id: string } +export type Object = { id: number, pub_id: number[], name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string } -export interface NormalisedOrganisation { $type: string, $id: any, id: string, name: string, users: NormalizedVec, owner: NormalisedUser, non_normalised_data: Array } - -export interface NormalisedUser { $type: string, $id: any, id: string, name: string } - -export interface NormalizedVec { $type: string, edges: Array } - -export interface Object { id: number, pub_id: Array, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string } - -export interface ObjectValidatorArgs { id: number, path: string } +export type ObjectValidatorArgs = { id: number, path: string } +/** + * These parameters define the password-hashing level. + * + * The harder the parameter, the longer the password will take to hash. + */ export type Params = "Standard" | "Hardened" | "Paranoid" -export interface RestoreBackupArgs { password: string, secret_key: string, path: string } +export type RestoreBackupArgs = { password: string, secret_key: string, path: string } export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent" -export type Salt = Array +export type Salt = number[] -export interface SetFavoriteArgs { id: number, favorite: boolean } +export type SetFavoriteArgs = { id: number, favorite: boolean } -export interface SetNoteArgs { id: number, note: string | null } +export type SetNoteArgs = { id: number, note: string | null } -export interface Statistics { id: number, date_captured: string, total_object_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } +export type Statistics = { id: number, date_captured: string, total_object_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } -export interface StoredKey { uuid: string, version: StoredKeyVersion, key_type: StoredKeyType, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, content_salt: Salt, master_key: EncryptedKey, master_key_nonce: Nonce, key_nonce: Nonce, key: Array, salt: Salt, memory_only: boolean, automount: boolean } +/** + * This is a stored key, and can be freely written to Prisma/another database. + */ +export type StoredKey = { uuid: string, version: StoredKeyVersion, key_type: StoredKeyType, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, content_salt: Salt, master_key: EncryptedKey, master_key_nonce: Nonce, key_nonce: Nonce, key: number[], salt: Salt, memory_only: boolean, automount: boolean } export type StoredKeyType = "User" | "Root" export type StoredKeyVersion = "V1" -export interface Tag { id: number, pub_id: Array, name: string | null, color: string | null, total_objects: number | null, redundancy_goal: number | null, date_created: string, date_modified: string } +export type Tag = { id: number, pub_id: number[], name: string | null, color: string | null, total_objects: number | null, redundancy_goal: number | null, date_created: string, date_modified: string } -export interface TagAssignArgs { object_id: number, tag_id: number, unassign: boolean } +export type TagAssignArgs = { object_id: number, tag_id: number, unassign: boolean } -export interface TagCreateArgs { name: string, color: string } +export type TagCreateArgs = { name: string, color: string } -export interface TagUpdateArgs { id: number, name: string | null, color: string | null } +export type TagUpdateArgs = { id: number, name: string | null, color: string | null } -export interface TokenizeKeyArgs { secret_key: string } +export type TokenizeKeyArgs = { secret_key: string } -export interface TokenizeResponse { token: string } +export type TokenizeResponse = { token: string } -export interface UnlockKeyManagerArgs { password: string, secret_key: string } +export type UnlockKeyManagerArgs = { password: string, secret_key: string } -export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } +export type Volume = { name: string, mount_point: string, total_capacity: string, available_capacity: string, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } -export interface FilePathWithObject { id: number, is_dir: boolean, cas_id: string | null, integrity_checksum: string | null, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, object: Object | null } +export type file_path_with_object = { id: number, is_dir: boolean, cas_id: string | null, integrity_checksum: string | null, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, object: Object | null } -export interface ObjectWithFilePaths { id: number, pub_id: Array, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: Array } +export type object_with_file_paths = { id: number, pub_id: number[], name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: FilePath[] } diff --git a/packages/interface/src/components/dialog/EraseFileDialog.tsx b/packages/interface/src/components/dialog/EraseFileDialog.tsx index da377fa6b..634d437f7 100644 --- a/packages/interface/src/components/dialog/EraseFileDialog.tsx +++ b/packages/interface/src/components/dialog/EraseFileDialog.tsx @@ -29,7 +29,7 @@ export const EraseFileDialog = (props: EraseDialogProps) => { eraseFile.mutateAsync({ location_id: props.location_id, path_id: props.path_id, - passes: data.passes + passes: data.passes.toString() }) ); diff --git a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx index f7278e5d3..069b39c2f 100644 --- a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx +++ b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx @@ -17,10 +17,16 @@ import { TrashSimple } from 'phosphor-react'; import { PropsWithChildren, useMemo } from 'react'; -import { ExplorerItem, useLibraryMutation, useLibraryQuery } from '@sd/client'; +import { + ExplorerItem, + getLibraryIdRaw, + useDebugState, + useLibraryMutation, + useLibraryQuery +} from '@sd/client'; import { ContextMenu as CM } from '@sd/ui'; import { dialogManager } from '@sd/ui'; -import { CutCopyType, getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore'; +import { getExplorerStore, useExplorerStore } from '~/hooks/useExplorerStore'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; import { useExplorerParams } from '~/screens/LocationExplorer'; import { usePlatform } from '~/util/Platform'; @@ -144,7 +150,7 @@ export function ExplorerContextMenu(props: PropsWithChildren) { keybind="⌘V" hidden={!store.cutCopyState.active} onClick={(e) => { - if (store.cutCopyState.actionType == CutCopyType.Copy) { + if (store.cutCopyState.actionType == 'Copy') { store.locationId && copyFiles.mutate({ source_location_id: store.cutCopyState.sourceLocationId, @@ -209,6 +215,7 @@ export interface FileItemContextMenuProps extends PropsWithChildren { export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps) { const store = useExplorerStore(); const params = useExplorerParams(); + const platform = usePlatform(); const objectData = data ? (isObject(data) ? data.item : data.item.object) : null; const isUnlockedQuery = useLibraryQuery(['keys.isUnlocked']); @@ -224,7 +231,19 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps return (
- + { + // TODO: Replace this with a proper UI + window.location.href = platform.getFileUrl( + getLibraryIdRaw()!, + store.locationId!, + data.item.id + ); + }} + icon={Copy} + /> @@ -256,7 +275,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps getExplorerStore().cutCopyState = { sourceLocationId: store.locationId!, sourcePathId: data.item.id, - actionType: CutCopyType.Cut, + actionType: 'Cut', active: true }; }} @@ -270,7 +289,7 @@ export function FileItemContextMenu({ data, ...props }: FileItemContextMenuProps getExplorerStore().cutCopyState = { sourceLocationId: store.locationId!, sourcePathId: data.item.id, - actionType: CutCopyType.Copy, + actionType: 'Copy', active: true }; }} diff --git a/packages/interface/src/components/layout/Sidebar.tsx b/packages/interface/src/components/layout/Sidebar.tsx index 084f7d977..eb475d5b3 100644 --- a/packages/interface/src/components/layout/Sidebar.tsx +++ b/packages/interface/src/components/layout/Sidebar.tsx @@ -39,7 +39,7 @@ import { tw } from '@sd/ui'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; -import { usePlatform } from '~/util/Platform'; +import { OperatingSystem, usePlatform } from '~/util/Platform'; import AddLocationDialog from '../dialog/AddLocationDialog'; import CreateLibraryDialog from '../dialog/CreateLibraryDialog'; import { Folder } from '../icons/Folder'; @@ -449,16 +449,17 @@ const Icon = ({ component: Icon, ...props }: any) => ( ); // cute little helper to decrease code clutter -const macOnly = (platform: string | undefined, classnames: string) => +const macOnly = (platform: OperatingSystem | undefined, classnames: string) => platform === 'macOS' ? classnames : ''; function WindowControls() { const { platform } = usePlatform(); + const os = useOperatingSystem(); const showControls = window.location.search.includes('showControls'); if (platform === 'tauri' || showControls) { return ( -
+
{/* We do not provide the onClick handlers for 'MacTrafficLights' because this is only used in demo mode */} {showControls && }
diff --git a/packages/interface/src/components/onboarding/OnboardingProgress.tsx b/packages/interface/src/components/onboarding/OnboardingProgress.tsx index df38dd460..bd8b027ab 100644 --- a/packages/interface/src/components/onboarding/OnboardingProgress.tsx +++ b/packages/interface/src/components/onboarding/OnboardingProgress.tsx @@ -1,8 +1,7 @@ -import { getOnboardingStore, unlockOnboardingScreen, useOnboardingStore } from '@sd/client'; import clsx from 'clsx'; import { useEffect } from 'react'; import { useNavigate } from 'react-router'; - +import { getOnboardingStore, unlockOnboardingScreen, useOnboardingStore } from '@sd/client'; import { ONBOARDING_SCREENS } from './OnboardingRoot'; import { useCurrentOnboardingScreenKey } from './helpers/screens'; diff --git a/packages/interface/src/components/onboarding/OnboardingRoot.tsx b/packages/interface/src/components/onboarding/OnboardingRoot.tsx index d14452e90..d19b33721 100644 --- a/packages/interface/src/components/onboarding/OnboardingRoot.tsx +++ b/packages/interface/src/components/onboarding/OnboardingRoot.tsx @@ -1,10 +1,9 @@ import BloomOne from '@sd/assets/images/bloom-one.png'; -import { getOnboardingStore } from '@sd/client'; -import { tw } from '@sd/ui'; import clsx from 'clsx'; import { ComponentType, useEffect } from 'react'; import { Outlet, useNavigate } from 'react-router'; - +import { getOnboardingStore } from '@sd/client'; +import { tw } from '@sd/ui'; import { useOperatingSystem } from '../../hooks/useOperatingSystem'; import OnboardingCreatingLibrary from './OnboardingCreatingLibrary'; import OnboardingMasterPassword from './OnboardingMasterPassword'; diff --git a/packages/interface/src/hooks/useExplorerStore.tsx b/packages/interface/src/hooks/useExplorerStore.tsx index 50adc596d..e93cee6f3 100644 --- a/packages/interface/src/hooks/useExplorerStore.tsx +++ b/packages/interface/src/hooks/useExplorerStore.tsx @@ -10,10 +10,7 @@ export enum ExplorerKind { Space } -export enum CutCopyType { - Cut, - Copy -} +export type CutCopyType = 'Cut' | 'Copy'; const state = { locationId: null as number | null, @@ -30,7 +27,7 @@ const state = { cutCopyState: { sourceLocationId: 0, sourcePathId: 0, - actionType: CutCopyType.Cut, + actionType: 'Cut', active: false } }; diff --git a/packages/interface/src/screens/Overview.tsx b/packages/interface/src/screens/Overview.tsx index c0e4644d0..6de0cbc5c 100644 --- a/packages/interface/src/screens/Overview.tsx +++ b/packages/interface/src/screens/Overview.tsx @@ -28,7 +28,7 @@ import { usePlatform } from '~/util/Platform'; interface StatItemProps { title: string; - bytes: string; + bytes: bigint; isLoading: boolean; } @@ -76,9 +76,9 @@ onLibraryChange((newLibraryId) => { const StatItem: React.FC = (props) => { const { library } = useCurrentLibrary(); - const { title, bytes = '0', isLoading } = props; + const { title, bytes = BigInt('0'), isLoading } = props; - const size = byteSize(+bytes); + const size = byteSize(Number(bytes)); // TODO: This BigInt to Number conversion will truncate the number if the number is too large. `byteSize` doesn't support BigInt so we are gonna need to come up with a longer term solution at some point. const count = useCounter({ name: title, end: +size.value, @@ -101,7 +101,7 @@ const StatItem: React.FC = (props) => {
{title} @@ -160,7 +160,7 @@ export default function OverviewScreen() { ); diff --git a/packages/interface/src/util/Platform.tsx b/packages/interface/src/util/Platform.tsx index 2f417a632..d49ef6256 100644 --- a/packages/interface/src/util/Platform.tsx +++ b/packages/interface/src/util/Platform.tsx @@ -7,6 +7,7 @@ export type OperatingSystem = 'browser' | 'linux' | 'macOS' | 'windows' | 'unkno export type Platform = { platform: 'web' | 'tauri'; // This represents the specific platform implementation getThumbnailUrlById: (casId: string) => string; + getFileUrl: (libraryId: string, locationLocalId: number, filePathId: number) => string; openLink: (url: string) => void; demoMode?: boolean; // TODO: Remove this in favour of demo mode being handled at the React Query level getOs?(): Promise; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30f1683db..97fda9064 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,8 +43,8 @@ importers: '@sd/config': workspace:* '@sd/interface': workspace:* '@sd/ui': workspace:* - '@tauri-apps/api': 1.1.0 - '@tauri-apps/cli': 1.1.1 + '@tauri-apps/api': 1.2.0 + '@tauri-apps/cli': 1.2.3 '@types/babel-core': ^6.25.7 '@types/react': ^18.0.21 '@types/react-dom': ^18.0.6 @@ -59,16 +59,16 @@ importers: vite-tsconfig-paths: ^4.0.3 dependencies: '@rspc/client': 0.0.0-main-7c0a67c1 - '@rspc/tauri': 0.0.0-main-7c0a67c1_@tauri-apps+api@1.1.0 + '@rspc/tauri': 0.0.0-main-7c0a67c1_@tauri-apps+api@1.2.0 '@sd/client': link:../../packages/client '@sd/interface': link:../../packages/interface '@sd/ui': link:../../packages/ui - '@tauri-apps/api': 1.1.0 + '@tauri-apps/api': 1.2.0 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: '@sd/config': link:../../packages/config - '@tauri-apps/cli': 1.1.1 + '@tauri-apps/cli': 1.2.3 '@types/babel-core': 6.25.7 '@types/react': 18.0.27 '@types/react-dom': 18.0.10 @@ -5707,13 +5707,13 @@ packages: '@tanstack/react-query': 4.22.0 dev: false - /@rspc/tauri/0.0.0-main-7c0a67c1_@tauri-apps+api@1.1.0: + /@rspc/tauri/0.0.0-main-7c0a67c1_@tauri-apps+api@1.2.0: resolution: {integrity: sha512-GnTAGcVV1FWp4Cs5n3wK0x/etrOTGbUHHq1M2sqLiG2Nfq2ej8bI5e5HTVhDgXD+PCGN38zYV3u8rEYlxNAMpA==} peerDependencies: '@tauri-apps/api': ^1.0.2 dependencies: '@rspc/client': 0.0.0-main-7c0a67c1 - '@tauri-apps/api': 1.1.0 + '@tauri-apps/api': 1.2.0 dev: false /@segment/loosely-validate-event/2.0.0: @@ -7616,13 +7616,13 @@ packages: engines: {node: '>=12'} dev: false - /@tauri-apps/api/1.1.0: - resolution: {integrity: sha512-n13pIqdPd3KtaMmmAcrU7BTfdMtIlGNnfZD0dNX8L4p8dgmuNyikm6JAA+yCpl9gqq6I8x5cV2Y0muqdgD0cWw==} - engines: {node: '>= 12.22.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + /@tauri-apps/api/1.2.0: + resolution: {integrity: sha512-lsI54KI6HGf7VImuf/T9pnoejfgkNoXveP14pVV7XarrQ46rOejIVJLFqHI9sRReJMGdh2YuCoI3cc/yCWCsrw==} + engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false - /@tauri-apps/cli-darwin-arm64/1.1.1: - resolution: {integrity: sha512-qBG11ig525/qf0f5OQxn0ON3hT8YdpTfpa4Y4kVqBJhdW50R5fadPv6tv5Dpl2TS2X7nWh/zg5mEXYoCK3HZ9w==} + /@tauri-apps/cli-darwin-arm64/1.2.3: + resolution: {integrity: sha512-phJN3fN8FtZZwqXg08bcxfq1+X1JSDglLvRxOxB7VWPq+O5SuB8uLyssjJsu+PIhyZZnIhTGdjhzLSFhSXfLsw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -7630,8 +7630,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-darwin-x64/1.1.1: - resolution: {integrity: sha512-M3dMsp78OdxisbTwAWGvy3jIb3uqThtQcUYVvqOu9LeEOHyldOBFDSht+6PTBpaJLAHFMQK2rmNxiWgigklJaA==} + /@tauri-apps/cli-darwin-x64/1.2.3: + resolution: {integrity: sha512-jFZ/y6z8z6v4yliIbXKBXA7BJgtZVMsITmEXSuD6s5+eCOpDhQxbRkr6CA+FFfr+/r96rWSDSgDenDQuSvPAKw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -7639,8 +7639,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-linux-arm-gnueabihf/1.1.1: - resolution: {integrity: sha512-LYlvdAd73cq+yTi6rw7j/DWIvDpeApwgQkIn+HYsNNeFhyFmABU7tmw+pekK3W3nHAkYAJ69Rl4ZdoxdNGKmHg==} + /@tauri-apps/cli-linux-arm-gnueabihf/1.2.3: + resolution: {integrity: sha512-C7h5vqAwXzY0kRGSU00Fj8PudiDWFCiQqqUNI1N+fhCILrzWZB9TPBwdx33ZfXKt/U4+emdIoo/N34v3TiAOmQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] @@ -7648,8 +7648,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-linux-arm64-gnu/1.1.1: - resolution: {integrity: sha512-o/hbMQIKuFI7cTNpeQBHD/OCNJOBIci78faKms/t6AstLXx0QJuRHDk477Rg6VVy/I3BBKbyATALbmcTq+ti0A==} + /@tauri-apps/cli-linux-arm64-gnu/1.2.3: + resolution: {integrity: sha512-buf1c8sdkuUzVDkGPQpyUdAIIdn5r0UgXU6+H5fGPq/Xzt5K69JzXaeo6fHsZEZghbV0hOK+taKV4J0m30UUMQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -7657,8 +7657,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-linux-arm64-musl/1.1.1: - resolution: {integrity: sha512-8Ci4qlDnXIp93XqUrtzFCBDatUzPHpZq7L3bociUbWpvy/bnlzxp1C/C+vwdc4uS1MiAp9v3BFgrU4i0f0Z3QQ==} + /@tauri-apps/cli-linux-arm64-musl/1.2.3: + resolution: {integrity: sha512-x88wPS9W5xAyk392vc4uNHcKBBvCp0wf4H9JFMF9OBwB7vfd59LbQCFcPSu8f0BI7bPrOsyHqspWHuFL8ojQEA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -7666,8 +7666,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-linux-x64-gnu/1.1.1: - resolution: {integrity: sha512-ES4Bkx2JAI8+dDNDJswhLS3yqt+yT/4C6UfGOPIHFxcXUh6fe36eUllrTt+HLRS9xTZbYnteJy7ebq2TqMkaxw==} + /@tauri-apps/cli-linux-x64-gnu/1.2.3: + resolution: {integrity: sha512-ZMz1jxEVe0B4/7NJnlPHmwmSIuwiD6ViXKs8F+OWWz2Y4jn5TGxWKFg7DLx5OwQTRvEIZxxT7lXHi5CuTNAxKg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -7675,8 +7675,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-linux-x64-musl/1.1.1: - resolution: {integrity: sha512-qrN1WOMAaDl+LE8P8iO0+DYlrWNTc9jIu/CsnVY/LImTn79ZPxEkcVBo0UGeKRI7f10TfvkVmLCBLxTz8QhEyA==} + /@tauri-apps/cli-linux-x64-musl/1.2.3: + resolution: {integrity: sha512-B/az59EjJhdbZDzawEVox0LQu2ZHCZlk8rJf85AMIktIUoAZPFbwyiUv7/zjzA/sY6Nb58OSJgaPL2/IBy7E0A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -7684,8 +7684,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-win32-ia32-msvc/1.1.1: - resolution: {integrity: sha512-vw7VOmrQlywHhFV3pf54udf2FRNj9dg9WP1gL0My55FnB+w+PWS9Ipm871kX5qepmChdnZHKq9fsqE2uTjX//Q==} + /@tauri-apps/cli-win32-ia32-msvc/1.2.3: + resolution: {integrity: sha512-ypdO1OdC5ugNJAKO2m3sb1nsd+0TSvMS9Tr5qN/ZSMvtSduaNwrcZ3D7G/iOIanrqu/Nl8t3LYlgPZGBKlw7Ng==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -7693,8 +7693,8 @@ packages: dev: true optional: true - /@tauri-apps/cli-win32-x64-msvc/1.1.1: - resolution: {integrity: sha512-OukxlLLi3AoCN4ABnqCDTiiC7xJGWukAjrKCIx7wFISrLjNfsrnH7/UOzuopfGpZChSe2c+AamVmcpBfVsEmJA==} + /@tauri-apps/cli-win32-x64-msvc/1.2.3: + resolution: {integrity: sha512-CsbHQ+XhnV/2csOBBDVfH16cdK00gNyNYUW68isedmqcn8j+s0e9cQ1xXIqi+Hue3awp8g3ImYN5KPepf3UExw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -7702,20 +7702,20 @@ packages: dev: true optional: true - /@tauri-apps/cli/1.1.1: - resolution: {integrity: sha512-80kjMEMPBwLYCp0tTKSquy90PHHGGBvZsneNr3B/mWxNsvjzA1C0vOyGJGFrJuT2OmkvrdvuJZ5mch5hL8O1Xg==} + /@tauri-apps/cli/1.2.3: + resolution: {integrity: sha512-erxtXuPhMEGJPBtnhPILD4AjuT81GZsraqpFvXAmEJZ2p8P6t7MVBifCL8LznRknznM3jn90D3M8RNBP3wcXTw==} engines: {node: '>= 10'} hasBin: true optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 1.1.1 - '@tauri-apps/cli-darwin-x64': 1.1.1 - '@tauri-apps/cli-linux-arm-gnueabihf': 1.1.1 - '@tauri-apps/cli-linux-arm64-gnu': 1.1.1 - '@tauri-apps/cli-linux-arm64-musl': 1.1.1 - '@tauri-apps/cli-linux-x64-gnu': 1.1.1 - '@tauri-apps/cli-linux-x64-musl': 1.1.1 - '@tauri-apps/cli-win32-ia32-msvc': 1.1.1 - '@tauri-apps/cli-win32-x64-msvc': 1.1.1 + '@tauri-apps/cli-darwin-arm64': 1.2.3 + '@tauri-apps/cli-darwin-x64': 1.2.3 + '@tauri-apps/cli-linux-arm-gnueabihf': 1.2.3 + '@tauri-apps/cli-linux-arm64-gnu': 1.2.3 + '@tauri-apps/cli-linux-arm64-musl': 1.2.3 + '@tauri-apps/cli-linux-x64-gnu': 1.2.3 + '@tauri-apps/cli-linux-x64-musl': 1.2.3 + '@tauri-apps/cli-win32-ia32-msvc': 1.2.3 + '@tauri-apps/cli-win32-x64-msvc': 1.2.3 dev: true /@testing-library/dom/8.20.0: