diff --git a/Cargo.toml b/Cargo.toml index ed0f05ca..a0048d88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ parking_lot = { version = "0.12.4", features = ["send_guard"] } fxhash = "0.2.1" static_assertions = "1.1.0" rayon = "1.10.0" +serde = { version = "1", features = ["derive"] } [dev-dependencies] criterion = "0.6.0" diff --git a/explorer/.gitignore b/explorer/.gitignore new file mode 100644 index 00000000..1b0fa1e3 --- /dev/null +++ b/explorer/.gitignore @@ -0,0 +1,3 @@ + +# Built frontend assets +explorer/frontend/dist/ diff --git a/explorer/Cargo.lock b/explorer/Cargo.lock new file mode 100644 index 00000000..20d04e16 --- /dev/null +++ b/explorer/Cargo.lock @@ -0,0 +1,2607 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" +dependencies = [ + "alloy-rlp", + "arbitrary", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "getrandom 0.3.4", + "hashbrown", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "proptest-derive 0.6.0", + "rand 0.9.2", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "alloy-trie" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "arrayvec", + "derive_arbitrary", + "derive_more", + "nybbles", + "proptest", + "proptest-derive 0.5.1", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.111", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "const-hex" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +dependencies = [ + "cfg-if", + "cpufeatures", + "proptest", + "serde_core", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "derive_more" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.111", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", + "serde", + "serde_core", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "arbitrary", + "equivalent", + "hashbrown", + "serde", + "serde_core", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memmap2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +dependencies = [ + "libc", +] + +[[package]] +name = "metrics" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3dbdd96ed57d565ec744cba02862d707acf373c5772d152abae6ec5c4e24f6c" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.111", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "nybbles" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "alloy-rlp", + "arbitrary", + "const-hex", + "proptest", + "serde", + "smallvec", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "proptest-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "proptest-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", + "serde", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" +dependencies = [ + "alloy-rlp", + "arbitrary", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.27", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "arbitrary", + "serde", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "triedb" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "arrayvec", + "fxhash", + "memmap2", + "metrics", + "metrics-derive", + "parking_lot", + "proptest", + "proptest-derive 0.6.0", + "rayon", + "sealed", + "serde", + "static_assertions", + "zerocopy", +] + +[[package]] +name = "triedb-explorer" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-trie", + "axum", + "clap", + "include_dir", + "mime_guess", + "parking_lot", + "serde", + "serde_json", + "tokio", + "tower", + "tower-http", + "triedb", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] diff --git a/explorer/Cargo.toml b/explorer/Cargo.toml new file mode 100644 index 00000000..8e54b350 --- /dev/null +++ b/explorer/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "triedb-explorer" +version = "0.1.0" +edition = "2021" + +[dependencies] +triedb = { path = ".." } +axum = { version = "0.7", features = ["macros"] } +tokio = { version = "1", features = ["full"] } +tower = "0.5" +tower-http = { version = "0.6", features = ["fs", "cors"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +clap = { version = "4", features = ["derive"] } +alloy-primitives = { version = "1.2.1", features = ["serde"] } +alloy-trie = { version = "0.8.1", features = ["serde"] } +parking_lot = "0.12" +include_dir = "0.7" +mime_guess = "2" + +[[bin]] +name = "triedb-explorer" +path = "src/main.rs" diff --git a/explorer/frontend/index.html b/explorer/frontend/index.html new file mode 100644 index 00000000..1117fd5e --- /dev/null +++ b/explorer/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + TrieDB Explorer + + +
+ + + diff --git a/explorer/frontend/package-lock.json b/explorer/frontend/package-lock.json new file mode 100644 index 00000000..e8fdf7da --- /dev/null +++ b/explorer/frontend/package-lock.json @@ -0,0 +1,1681 @@ +{ + "name": "triedb-explorer-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "triedb-explorer-frontend", + "version": "0.1.0", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "typescript": "^5.2.0", + "vite": "^5.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.0.tgz", + "integrity": "sha512-Mh++g+2LPfzZToywfE1BUzvZbfOY52Nil0rn9H1CPC5DJ7fX+Vir7nToBeoiSbB1zTNeGYbELEvJESujgGrzXw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.0.tgz", + "integrity": "sha512-Dn+NlSF/7+0lVSEZ57SYQg6/E44arLzsVOGgrElBn/BlG1B8WKdbLppOocFrXwRNTkNlgdGNaBgH1o0lggDPiw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/explorer/frontend/package.json b/explorer/frontend/package.json new file mode 100644 index 00000000..aed851a5 --- /dev/null +++ b/explorer/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "triedb-explorer-frontend", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "typescript": "^5.2.0", + "vite": "^5.0.0" + } +} diff --git a/explorer/frontend/src/App.tsx b/explorer/frontend/src/App.tsx new file mode 100644 index 00000000..c3ca9a9d --- /dev/null +++ b/explorer/frontend/src/App.tsx @@ -0,0 +1,131 @@ +import { useEffect, useState } from 'react'; +import { useTrieData } from './hooks/useTrieData'; +import { PageContainer } from './components/PageContainer'; +import { SearchBar } from './components/SearchBar'; +import { MetadataModal } from './components/MetadataModal'; +import type { PathSearchResult } from './types'; +import './styles/index.css'; + +export function App() { + const { + state, + loadRootPage, + loadPage, + toggleNode, + isPageLoaded, + getPageBasePath, + loadSearchPath, + toggleAutoExpand, + toggleShowEmptySlots, + toggleShowHashes, + expandAllOnPage, + collapseAllOnPage, + } = useTrieData(); + + const [isMetadataOpen, setIsMetadataOpen] = useState(false); + const [searchResult, setSearchResult] = useState(null); + + useEffect(() => { + loadRootPage(); + }, [loadRootPage]); + + const handleNavigate = (pageId: number, basePath?: string, isChildPage: boolean = true) => { + if (!isPageLoaded(pageId)) { + loadPage(pageId, basePath, isChildPage); + } + }; + + const handleSearchResult = async (result: PathSearchResult | null) => { + setSearchResult(result); + if (result) { + // Load pages along the search path with proper hierarchy + await loadSearchPath(result); + } + }; + + // Sort pages by ID for consistent display, filtering out child pages + const sortedPages = Array.from(state.loadedPages.entries()) + .filter(([pageId]) => !state.childPages.has(pageId)) + .sort(([a], [b]) => a - b); + + return ( +
+
+

TrieDB Explorer

+ +
+ + + + +
+
+ +
+ {state.isLoading &&
Loading...
} + {state.error &&
{state.error}
} + + {searchResult && ( +
+ Found: {searchResult.node.nodeType} at Page {searchResult.node.pageId}, + Cell {searchResult.node.cellIndex} +
+ )} + +
+ {sortedPages.map(([pageId, pageData]) => ( + + ))} +
+ + {!state.isLoading && sortedPages.length === 0 && !state.error && ( +
+ No data loaded. The database may be empty. +
+ )} +
+ + setIsMetadataOpen(false)} + /> +
+ ); +} diff --git a/explorer/frontend/src/api/client.ts b/explorer/frontend/src/api/client.ts new file mode 100644 index 00000000..660c383f --- /dev/null +++ b/explorer/frontend/src/api/client.ts @@ -0,0 +1,58 @@ +import type { + ApiResponse, + DatabaseInfo, + DescendantsResponse, + ExplorerNode, + PageInfo, + PageResponse, + PathSearchResult, + RootResponse, +} from '../types'; + +const API_BASE = '/api'; + +async function fetchApi(url: string): Promise> { + const response = await fetch(`${API_BASE}${url}`); + return response.json(); +} + +export async function getRoot(): Promise> { + return fetchApi('/root'); +} + +export async function getPage(pageId: number): Promise> { + return fetchApi(`/pages/${pageId}`); +} + +export async function getPageInfo(pageId: number): Promise> { + return fetchApi(`/pages/${pageId}/info`); +} + +export async function getDescendants(pageId: number): Promise> { + return fetchApi(`/pages/${pageId}/descendants`); +} + +export async function getNodeDescendants( + pageId: number, + cellIndex: number +): Promise> { + return fetchApi(`/pages/${pageId}/nodes/${cellIndex}/descendants`); +} + +export async function getNode( + pageId: number, + cellIndex: number +): Promise> { + return fetchApi(`/nodes/${pageId}/${cellIndex}`); +} + +export async function search( + query: string, + type: 'raw' | 'address' | 'storage' = 'raw' +): Promise> { + return fetchApi(`/search?q=${encodeURIComponent(query)}&type=${type}`); +} + +export async function getMetadata(): Promise> { + return fetchApi('/metadata'); +} diff --git a/explorer/frontend/src/components/MetadataModal.tsx b/explorer/frontend/src/components/MetadataModal.tsx new file mode 100644 index 00000000..350dec92 --- /dev/null +++ b/explorer/frontend/src/components/MetadataModal.tsx @@ -0,0 +1,89 @@ +import { useState, useEffect } from 'react'; +import type { DatabaseInfo } from '../types'; +import * as api from '../api/client'; + +interface MetadataModalProps { + isOpen: boolean; + onClose: () => void; +} + +export function MetadataModal({ isOpen, onClose }: MetadataModalProps) { + const [metadata, setMetadata] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (isOpen && !metadata) { + loadMetadata(); + } + }, [isOpen]); + + const loadMetadata = async () => { + setIsLoading(true); + setError(null); + try { + const response = await api.getMetadata(); + if (response.success && response.data) { + setMetadata(response.data); + } else { + setError(response.error || 'Failed to load metadata'); + } + } catch (e) { + setError(e instanceof Error ? e.message : 'Unknown error'); + } finally { + setIsLoading(false); + } + }; + + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()}> +
+

Database Metadata

+ +
+
+ {isLoading &&
Loading metadata...
} + {error &&
{error}
} + {metadata && ( + <> +
+

Database Info

+
+
Snapshot ID
+
{metadata.snapshotId}
+
State Root
+
0x{metadata.rootNodeHash}
+
Root Page ID
+
{metadata.rootNodePageId ?? 'None'}
+
Total Pages
+
{metadata.totalPageCount}
+
+
+ +
+

Orphaned Pages ({metadata.orphanedPages.length})

+ {metadata.orphanedPages.length > 0 ? ( +
+ {metadata.orphanedPages.map((orphan) => ( +
+ Page {orphan.pageId} + Orphaned at snapshot {orphan.orphanedAtSnapshot} +
+ ))} +
+ ) : ( +
No orphaned pages
+ )} +
+ + )} +
+
+
+ ); +} diff --git a/explorer/frontend/src/components/NodeComponent.tsx b/explorer/frontend/src/components/NodeComponent.tsx new file mode 100644 index 00000000..9c0f6c91 --- /dev/null +++ b/explorer/frontend/src/components/NodeComponent.tsx @@ -0,0 +1,157 @@ +import type { ExplorerNode, ChildEntry } from '../types'; + +interface NodeComponentProps { + node: ExplorerNode; + basePath: string; + isExpanded: boolean; + onToggle: () => void; + onNavigate: (pageId: number, basePath?: string) => void; +} + +export function NodeComponent({ node, basePath, isExpanded, onToggle, onNavigate }: NodeComponentProps) { + const nodeTypeClass = node.nodeType.toLowerCase().replace('leaf', '-leaf'); + const fullPath = basePath + (node.prefix || ''); + + return ( +
+
+ + {node.nodeType} + + {fullPath ? ( + <> + 0x{basePath && {basePath}} + {node.prefix && {node.prefix}} + + ) : ( + '(empty path)' + )} + + + Cell {node.cellIndex} | {node.sizeBytes} bytes + +
+ + {isExpanded && ( + <> +
+
+ {node.nodeType === 'AccountLeaf' && ( + <> +
Nonce
+
{node.nonce}
+
Balance
+
{node.balance}
+
Code Hash
+
0x{node.codeHash}
+ {node.storageRoot && ( + <> +
Storage Root
+
+ +
+ + )} + + )} + {node.nodeType === 'StorageLeaf' && ( + <> +
Value
+
{node.value}
+ + )} +
+
+ + {node.nodeType === 'Branch' && node.children && node.children.length > 0 && ( +
+
+ {node.children.map((child) => ( + + ))} +
+
+ )} + + )} +
+ ); +} + +interface PointerLinkProps { + pointer: { locationType: string; pageId?: number; cellIndex?: number; rlpHash?: string }; + onNavigate: (pageId: number, basePath?: string) => void; +} + +function PointerLink({ pointer, onNavigate }: PointerLinkProps) { + if (pointer.locationType === 'SamePage' && pointer.cellIndex !== undefined) { + return ( + { + e.stopPropagation(); + // Scroll to cell on same page - for now just highlight + }} + > + Cell {pointer.cellIndex} + + ); + } else if (pointer.locationType === 'OtherPage' && pointer.pageId !== undefined) { + return ( + { + e.stopPropagation(); + // Storage root pointers start a new trie, so empty base path + onNavigate(pointer.pageId!, ''); + }} + > + Page {pointer.pageId} + + ); + } + return Unknown; +} + +interface ChildPointerProps { + child: ChildEntry; + fullPath: string; + onNavigate: (pageId: number, basePath?: string) => void; +} + +function ChildPointer({ child, fullPath, onNavigate }: ChildPointerProps) { + const nibble = child.index.toString(16).toUpperCase(); + const pointer = child.pointer; + // The child's base path is the parent's full path + this nibble + const childBasePath = fullPath + nibble; + + if (pointer.locationType === 'SamePage' && pointer.cellIndex !== undefined) { + return ( + { + // For same-page navigation, could scroll to node + }} + > + [{nibble}] Cell {pointer.cellIndex} + + ); + } else if (pointer.locationType === 'OtherPage' && pointer.pageId !== undefined) { + return ( + onNavigate(pointer.pageId!, childBasePath)} + > + [{nibble}] Page {pointer.pageId} + + ); + } + return ( + + [{nibble}] Unknown + + ); +} diff --git a/explorer/frontend/src/components/PageContainer.tsx b/explorer/frontend/src/components/PageContainer.tsx new file mode 100644 index 00000000..f1642d00 --- /dev/null +++ b/explorer/frontend/src/components/PageContainer.tsx @@ -0,0 +1,81 @@ +import { useMemo } from 'react'; +import type { ExplorerNode, PageInfo } from '../types'; +import { TreeNodeComponent } from './TreeNodeComponent'; +import { buildTree } from '../utils/treeBuilder'; + +interface PageContainerProps { + pageInfo: PageInfo; + nodes: ExplorerNode[]; + basePath: string; + expandedNodes: Set; + loadedPages: Map; + pagePaths: Map; + showEmptySlots: boolean; + showHashes: boolean; + onToggleNode: (pageId: number, cellIndex: number) => void; + onNavigate: (pageId: number, basePath?: string, isChildPage?: boolean) => void; + onExpandAllOnPage: (pageId: number) => void; + onCollapseAllOnPage: (pageId: number) => void; +} + +function getFullnessColor(percent: number): string { + // HSL: 120=green, 60=yellow, 0=red + const hue = Math.max(0, 120 - percent * 1.2); + return `hsl(${hue}, 60%, 92%)`; +} + +export function PageContainer({ + pageInfo, + nodes, + basePath, + expandedNodes, + loadedPages, + pagePaths, + showEmptySlots, + showHashes, + onToggleNode, + onNavigate, + onExpandAllOnPage, + onCollapseAllOnPage, +}: PageContainerProps) { + const bgColor = getFullnessColor(pageInfo.occupancyPercent); + + // Build tree structure from flat node list + const treeRoots = useMemo(() => buildTree(nodes, basePath), [nodes, basePath]); + + return ( +
+
+ Page {pageInfo.pageId} + {pageInfo.cellCount} nodes + + {pageInfo.usedBytes} / {pageInfo.totalBytes} bytes + + {pageInfo.occupancyPercent.toFixed(1)}% full + + + + +
+
+ {treeRoots.map((treeNode) => ( + + ))} +
+
+ ); +} diff --git a/explorer/frontend/src/components/SearchBar.tsx b/explorer/frontend/src/components/SearchBar.tsx new file mode 100644 index 00000000..95142039 --- /dev/null +++ b/explorer/frontend/src/components/SearchBar.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import * as api from '../api/client'; +import type { PathSearchResult } from '../types'; + +interface SearchBarProps { + onSearchResult: (result: PathSearchResult | null) => void; +} + +export function SearchBar({ onSearchResult }: SearchBarProps) { + const [query, setQuery] = useState(''); + const [searchType, setSearchType] = useState<'raw' | 'address' | 'storage'>('raw'); + const [isSearching, setIsSearching] = useState(false); + + const handleSearch = async () => { + if (!query.trim()) return; + + setIsSearching(true); + try { + const response = await api.search(query, searchType); + if (response.success && response.data) { + onSearchResult(response.data); + } else { + onSearchResult(null); + } + } catch (e) { + console.error('Search failed:', e); + onSearchResult(null); + } finally { + setIsSearching(false); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearch(); + } + }; + + return ( +
+ + setQuery(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={ + searchType === 'address' + ? '0x... (Ethereum address)' + : searchType === 'storage' + ? 'address:slot (e.g., 0x...:0x0)' + : '0x... (raw path, or 0xAccount:0xStorage)' + } + /> + +
+ ); +} diff --git a/explorer/frontend/src/components/TreeNodeComponent.tsx b/explorer/frontend/src/components/TreeNodeComponent.tsx new file mode 100644 index 00000000..0686c7e3 --- /dev/null +++ b/explorer/frontend/src/components/TreeNodeComponent.tsx @@ -0,0 +1,493 @@ +import { useMemo, useState } from 'react'; +import type { ExplorerNode, PageInfo, TreeNode, ChildEntry } from '../types'; +import { buildTree } from '../utils/treeBuilder'; + +function getFullnessColor(percent: number): string { + const hue = Math.max(0, 120 - percent * 1.2); + return `hsl(${hue}, 60%, 92%)`; +} + +function copyToClipboard(text: string): Promise { + return navigator.clipboard.writeText(text); +} + +interface TreeNodeComponentProps { + treeNode: TreeNode; + depth: number; + accountPath: string; // Path of parent account (for storage nodes) + expandedNodes: Set; + loadedPages: Map; + pagePaths: Map; + showEmptySlots: boolean; + showHashes: boolean; + onToggleNode: (pageId: number, cellIndex: number) => void; + onNavigate: (pageId: number, basePath?: string, isChildPage?: boolean) => void; + onExpandAllOnPage: (pageId: number) => void; + onCollapseAllOnPage: (pageId: number) => void; +} + +export function TreeNodeComponent({ + treeNode, + depth, + accountPath, + expandedNodes, + loadedPages, + pagePaths, + showEmptySlots, + showHashes, + onToggleNode, + onNavigate, + onExpandAllOnPage, + onCollapseAllOnPage, +}: TreeNodeComponentProps) { + const { node, localChildren, basePath } = treeNode; + const fullPath = basePath + (node.prefix || ''); + // For storage nodes, prepend the account path + const displayPath = accountPath ? `${accountPath}:${fullPath}` : fullPath; + const isExpanded = expandedNodes.has(`${node.pageId}:${node.cellIndex}`); + const nodeTypeClass = node.nodeType.toLowerCase().replace('leaf', '-leaf'); + const [copied, setCopied] = useState(false); + + const handleCopyPath = (e: React.MouseEvent) => { + e.stopPropagation(); + const pathToCopy = displayPath ? `0x${displayPath}` : ''; + if (pathToCopy) { + copyToClipboard(pathToCopy).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }); + } + }; + + // Check if this node has any expandable content + const hasLocalChildren = localChildren.length > 0; + const hasRemoteChildren = node.nodeType === 'Branch' && node.children?.some( + c => c.pointer.locationType === 'OtherPage' + ); + const hasStorageRoot = node.nodeType === 'AccountLeaf' && node.storageRoot?.locationType === 'OtherPage'; + const hasExpandableContent = hasLocalChildren || hasRemoteChildren || hasStorageRoot || + node.nodeType === 'AccountLeaf' || node.nodeType === 'StorageLeaf'; + + // Build sorted list of all children (local + remote) by index for branches + const sortedBranchChildren = useMemo(() => { + if (node.nodeType !== 'Branch' || !node.children) return []; + + // Create a map of index -> child info + const childMap = new Map(); + + // Initialize all 16 slots as empty if showing empty slots + if (showEmptySlots) { + for (let i = 0; i < 16; i++) { + childMap.set(i, { type: 'empty' }); + } + } + + // Add remote children + for (const child of node.children) { + if (child.pointer.locationType === 'OtherPage') { + childMap.set(child.index, { type: 'remote', child }); + } + } + + // Add local children (override if exists) + for (const treeChild of localChildren) { + if (!treeChild.isStorageChild) { + // Find the nibble for this child from the parent's children array + const childEntry = node.children.find(c => + c.pointer.locationType === 'SamePage' && + c.pointer.cellIndex === treeChild.node.cellIndex + ); + if (childEntry) { + childMap.set(childEntry.index, { type: 'local', child: childEntry, treeNode: treeChild }); + } + } + } + + // Sort by index and return + return Array.from(childMap.entries()) + .sort((a, b) => a[0] - b[0]) + .map(([index, data]) => ({ index, ...data })); + }, [node, localChildren, showEmptySlots]); + + // Get storage children (local) separately + const storageChildren = localChildren.filter(c => c.isStorageChild); + + return ( +
+
onToggleNode(node.pageId, node.cellIndex)}> + {hasExpandableContent ? ( + + ) : ( + + )} + {node.nodeType} + + {displayPath ? ( + <> + 0x{accountPath && {accountPath}:} + {basePath && {basePath}} + {node.prefix && {node.prefix}} + {copied && Copied!} + + ) : ( + '(root)' + )} + + + Cell {node.cellIndex} | {node.sizeBytes} bytes + +
+ + {isExpanded && ( +
+ {/* Node details (account/storage leaf values) */} + {(node.nodeType === 'AccountLeaf' || node.nodeType === 'StorageLeaf') && ( +
+
+ {node.nodeType === 'AccountLeaf' && ( + <> +
Nonce
+
{node.nonce}
+
Balance
+
{node.balance}
+
Code Hash
+
0x{node.codeHash}
+ + )} + {node.nodeType === 'StorageLeaf' && ( + <> +
Value
+
{node.value}
+ + )} +
+
+ )} + + {/* Branch children - sorted by index */} + {node.nodeType === 'Branch' && sortedBranchChildren.length > 0 && ( +
+ {sortedBranchChildren.map(({ index, type, child, treeNode: childTreeNode }) => { + const nibble = index.toString(16).toUpperCase(); + + if (type === 'empty') { + return ( +
+ [{nibble}] + +
+ ); + } + + if (type === 'local' && childTreeNode) { + return ( +
+
+ [{nibble}] + {showHashes && child?.pointer.rlpHash && ( + + {child.pointer.rlpHash} + + )} +
+ +
+ ); + } + + if (type === 'remote' && child) { + return ( + + ); + } + + return null; + })} +
+ )} + + {/* Local storage children */} + {storageChildren.map((child) => ( +
+
+ Storage +
+ +
+ ))} + + {/* Remote storage root for account leaves */} + {node.nodeType === 'AccountLeaf' && node.storageRoot && + node.storageRoot.locationType === 'OtherPage' && + node.storageRoot.pageId !== undefined && ( +
+ +
+ )} +
+ )} +
+ ); +} + +interface RemoteChildPointerProps { + child: ChildEntry; + parentPath: string; + accountPath: string; + loadedPages: Map; + pagePaths: Map; + expandedNodes: Set; + showEmptySlots: boolean; + showHashes: boolean; + onNavigate: (pageId: number, basePath?: string, isChildPage?: boolean) => void; + onToggleNode: (pageId: number, cellIndex: number) => void; + onExpandAllOnPage: (pageId: number) => void; + onCollapseAllOnPage: (pageId: number) => void; + depth: number; +} + +function RemoteChildPointer({ + child, + parentPath, + accountPath, + loadedPages, + pagePaths, + expandedNodes, + showEmptySlots, + showHashes, + onNavigate, + onToggleNode, + onExpandAllOnPage, + onCollapseAllOnPage, + depth, +}: RemoteChildPointerProps) { + const nibble = child.index.toString(16).toUpperCase(); + const childBasePath = parentPath + nibble; + const pageId = child.pointer.pageId!; + const pageData = loadedPages.get(pageId); + + const pageTree = useMemo(() => { + if (!pageData) return null; + return buildTree(pageData.nodes, childBasePath); + }, [pageData, childBasePath]); + + if (!pageData) { + return ( +
+ [{nibble}] + onNavigate(pageId, childBasePath, true)} + > + Page {pageId} (click to load) + + {showHashes && child.pointer.rlpHash && ( + + {child.pointer.rlpHash} + + )} +
+ ); + } + + const fullnessColor = getFullnessColor(pageData.info.occupancyPercent); + + return ( +
+
+ [{nibble}] + Page {pageId} + {pageData.info.cellCount} nodes + + {pageData.info.usedBytes} / {pageData.info.totalBytes} bytes + + {pageData.info.occupancyPercent.toFixed(1)}% full + {showHashes && child.pointer.rlpHash && ( + + {child.pointer.rlpHash} + + )} + + + + +
+ {pageTree?.map((treeNode) => ( + + ))} +
+ ); +} + +interface StorageRootPointerProps { + pageId: number; + rlpHash?: string; + accountPath: string; + loadedPages: Map; + pagePaths: Map; + expandedNodes: Set; + showEmptySlots: boolean; + showHashes: boolean; + onNavigate: (pageId: number, basePath?: string, isChildPage?: boolean) => void; + onToggleNode: (pageId: number, cellIndex: number) => void; + onExpandAllOnPage: (pageId: number) => void; + onCollapseAllOnPage: (pageId: number) => void; + depth: number; +} + +function StorageRootPointer({ + pageId, + rlpHash, + accountPath, + loadedPages, + pagePaths, + expandedNodes, + showEmptySlots, + showHashes, + onNavigate, + onToggleNode, + onExpandAllOnPage, + onCollapseAllOnPage, + depth, +}: StorageRootPointerProps) { + const pageData = loadedPages.get(pageId); + + const pageTree = useMemo(() => { + if (!pageData) return null; + return buildTree(pageData.nodes, ''); + }, [pageData]); + + if (!pageData) { + return ( +
+ Storage: + onNavigate(pageId, '', true)} + > + Page {pageId} (click to load) + + {showHashes && rlpHash && ( + + {rlpHash} + + )} +
+ ); + } + + const fullnessColor = getFullnessColor(pageData.info.occupancyPercent); + + return ( +
+
+ Storage + Page {pageId} + {pageData.info.cellCount} nodes + + {pageData.info.usedBytes} / {pageData.info.totalBytes} bytes + + {pageData.info.occupancyPercent.toFixed(1)}% full + {showHashes && rlpHash && ( + + {rlpHash} + + )} + + + + +
+ {pageTree?.map((treeNode) => ( + + ))} +
+ ); +} diff --git a/explorer/frontend/src/hooks/useTrieData.ts b/explorer/frontend/src/hooks/useTrieData.ts new file mode 100644 index 00000000..1c7371f7 --- /dev/null +++ b/explorer/frontend/src/hooks/useTrieData.ts @@ -0,0 +1,274 @@ +import { useState, useCallback } from 'react'; +import type { ExplorerNode, PageInfo, PathSearchResult } from '../types'; +import * as api from '../api/client'; + +export interface PageData { + info: PageInfo; + nodes: ExplorerNode[]; +} + +export interface TrieState { + loadedPages: Map; + pagePaths: Map; // Base path for each page's root + childPages: Set; // Pages loaded as children of other nodes (not shown at top level) + expandedNodes: Set; + selectedNode: { pageId: number; cellIndex: number } | null; + autoExpand: boolean; // Auto-expand nodes as pages are loaded + showEmptySlots: boolean; // Show empty child slots in branches + showHashes: boolean; // Show RLP hashes alongside pointers + isLoading: boolean; + error: string | null; +} + +export function useTrieData() { + const [state, setState] = useState({ + loadedPages: new Map(), + pagePaths: new Map(), + childPages: new Set(), + expandedNodes: new Set(), + selectedNode: null, + autoExpand: false, + showEmptySlots: false, + showHashes: false, + isLoading: false, + error: null, + }); + + const loadRootPage = useCallback(async () => { + setState(s => ({ ...s, isLoading: true, error: null })); + try { + const response = await api.getRoot(); + if (!response.success) { + throw new Error(response.error || 'Failed to load root page'); + } + if (response.data?.page) { + const pageId = response.data.rootPageId!; + setState(s => ({ + ...s, + loadedPages: new Map([[pageId, response.data!.page!]]), + pagePaths: new Map([[pageId, '']]), // Root page has empty base path + isLoading: false, + })); + } else { + setState(s => ({ ...s, isLoading: false })); + } + } catch (e) { + setState(s => ({ + ...s, + isLoading: false, + error: e instanceof Error ? e.message : 'Unknown error', + })); + } + }, []); + + const loadPage = useCallback(async (pageId: number, basePath?: string, isChildPage: boolean = false) => { + setState(s => ({ ...s, isLoading: true, error: null })); + try { + const response = await api.getPage(pageId); + if (!response.success) { + throw new Error(response.error || 'Failed to load page'); + } + if (response.data) { + setState(s => { + const newPages = new Map(s.loadedPages); + newPages.set(pageId, response.data!); + const newPaths = new Map(s.pagePaths); + if (basePath !== undefined) { + newPaths.set(pageId, basePath); + } + const newChildPages = new Set(s.childPages); + if (isChildPage) { + newChildPages.add(pageId); + } + // Auto-expand all nodes on this page if autoExpand is enabled + const newExpanded = new Set(s.expandedNodes); + if (s.autoExpand && response.data) { + for (const node of response.data.nodes) { + newExpanded.add(`${pageId}:${node.cellIndex}`); + } + } + return { ...s, loadedPages: newPages, pagePaths: newPaths, childPages: newChildPages, expandedNodes: newExpanded, isLoading: false }; + }); + } + } catch (e) { + setState(s => ({ + ...s, + isLoading: false, + error: e instanceof Error ? e.message : 'Unknown error', + })); + } + }, []); + + const toggleNode = useCallback((pageId: number, cellIndex: number) => { + const key = `${pageId}:${cellIndex}`; + setState(s => { + const newExpanded = new Set(s.expandedNodes); + if (newExpanded.has(key)) { + newExpanded.delete(key); + } else { + newExpanded.add(key); + } + return { ...s, expandedNodes: newExpanded }; + }); + }, []); + + const selectNode = useCallback((pageId: number, cellIndex: number) => { + setState(s => ({ + ...s, + selectedNode: { pageId, cellIndex }, + })); + }, []); + + const isNodeExpanded = useCallback( + (pageId: number, cellIndex: number) => { + return state.expandedNodes.has(`${pageId}:${cellIndex}`); + }, + [state.expandedNodes] + ); + + const isPageLoaded = useCallback( + (pageId: number) => { + return state.loadedPages.has(pageId); + }, + [state.loadedPages] + ); + + const getPageBasePath = useCallback( + (pageId: number) => { + return state.pagePaths.get(pageId) ?? ''; + }, + [state.pagePaths] + ); + + // Load pages along a search path, computing proper base paths and expanding nodes + const loadSearchPath = useCallback(async (result: PathSearchResult) => { + setState(s => ({ ...s, isLoading: true, error: null })); + + try { + const newPages = new Map(state.loadedPages); + const newPaths = new Map(state.pagePaths); + const newChildPages = new Set(state.childPages); + const newExpanded = new Set(state.expandedNodes); + + let currentPath = ''; + let lastPageId: number | null = null; + + // Process each node in the path + for (const node of result.pathToNode) { + const pageId = node.pageId; + + // Load page if not already loaded + if (!newPages.has(pageId)) { + const response = await api.getPage(pageId); + if (response.success && response.data) { + newPages.set(pageId, response.data); + } + } + + // Set base path for this page if it's a new page in the traversal + if (lastPageId !== null && pageId !== lastPageId) { + // This is a child page - mark it and set path + newPaths.set(pageId, currentPath); + newChildPages.add(pageId); + } else if (lastPageId === null) { + // First page (root) - set empty base path + newPaths.set(pageId, ''); + } + + // Expand this node + newExpanded.add(`${pageId}:${node.cellIndex}`); + + // Update current path with this node's prefix + currentPath += node.prefix || ''; + + // If this is a branch, add the nibble for the next node + if (node.nodeType === 'Branch' && node.children) { + // Find which child leads to the next node in the path + const nodeIndex = result.pathToNode.indexOf(node); + if (nodeIndex < result.pathToNode.length - 1) { + const nextNode = result.pathToNode[nodeIndex + 1]; + for (const child of node.children) { + if (child.pointer.locationType === 'SamePage' && child.pointer.cellIndex === nextNode.cellIndex) { + currentPath += child.index.toString(16).toUpperCase(); + break; + } else if (child.pointer.locationType === 'OtherPage' && child.pointer.pageId === nextNode.pageId) { + currentPath += child.index.toString(16).toUpperCase(); + break; + } + } + } + } + + lastPageId = pageId; + } + + setState(s => ({ + ...s, + loadedPages: newPages, + pagePaths: newPaths, + childPages: newChildPages, + expandedNodes: newExpanded, + isLoading: false, + })); + } catch (e) { + setState(s => ({ + ...s, + isLoading: false, + error: e instanceof Error ? e.message : 'Unknown error', + })); + } + }, [state.loadedPages, state.pagePaths, state.childPages, state.expandedNodes]); + + const toggleAutoExpand = useCallback(() => { + setState(s => ({ ...s, autoExpand: !s.autoExpand })); + }, []); + + const toggleShowEmptySlots = useCallback(() => { + setState(s => ({ ...s, showEmptySlots: !s.showEmptySlots })); + }, []); + + const toggleShowHashes = useCallback(() => { + setState(s => ({ ...s, showHashes: !s.showHashes })); + }, []); + + const expandAllOnPage = useCallback((pageId: number) => { + setState(s => { + const pageData = s.loadedPages.get(pageId); + if (!pageData) return s; + const newExpanded = new Set(s.expandedNodes); + for (const node of pageData.nodes) { + newExpanded.add(`${pageId}:${node.cellIndex}`); + } + return { ...s, expandedNodes: newExpanded }; + }); + }, []); + + const collapseAllOnPage = useCallback((pageId: number) => { + setState(s => { + const pageData = s.loadedPages.get(pageId); + if (!pageData) return s; + const newExpanded = new Set(s.expandedNodes); + for (const node of pageData.nodes) { + newExpanded.delete(`${pageId}:${node.cellIndex}`); + } + return { ...s, expandedNodes: newExpanded }; + }); + }, []); + + return { + state, + loadRootPage, + loadPage, + toggleNode, + selectNode, + isNodeExpanded, + isPageLoaded, + getPageBasePath, + loadSearchPath, + toggleAutoExpand, + toggleShowEmptySlots, + toggleShowHashes, + expandAllOnPage, + collapseAllOnPage, + }; +} diff --git a/explorer/frontend/src/main.tsx b/explorer/frontend/src/main.tsx new file mode 100644 index 00000000..6c14464c --- /dev/null +++ b/explorer/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { App } from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +); diff --git a/explorer/frontend/src/styles/index.css b/explorer/frontend/src/styles/index.css new file mode 100644 index 00000000..df0e0ac0 --- /dev/null +++ b/explorer/frontend/src/styles/index.css @@ -0,0 +1,578 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + line-height: 1.5; + color: #333; + background: #f5f5f5; +} + +.app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.header { + background: #1a1a2e; + color: white; + padding: 1rem 2rem; + display: flex; + align-items: center; + gap: 2rem; +} + +.header h1 { + font-size: 1.5rem; + font-weight: 600; +} + +.header-actions { + display: flex; + gap: 1rem; + margin-left: auto; +} + +.search-bar { + display: flex; + gap: 0.5rem; +} + +.search-bar input { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + width: 400px; + font-size: 14px; +} + +.search-bar select { + padding: 0.5rem; + border: none; + border-radius: 4px; +} + +.search-bar button { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + background: #4a90d9; + color: white; + cursor: pointer; + font-weight: 500; +} + +.search-bar button:hover { + background: #3a7bc0; +} + +.btn { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: 500; +} + +.btn-secondary { + background: #6c757d; + color: white; +} + +.btn-secondary:hover { + background: #5a6268; +} + +.btn-primary { + background: #4a90d9; + color: white; +} + +.btn-primary:hover { + background: #3a7bc0; +} + +.btn-small { + padding: 0.15rem 0.4rem; + border: 1px solid #ccc; + border-radius: 3px; + background: white; + cursor: pointer; + font-size: 0.85rem; + font-weight: 600; + line-height: 1; +} + +.btn-small:hover { + background: #f0f0f0; +} + +.main-content { + flex: 1; + padding: 1.5rem; + overflow: auto; +} + +.tree-view { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.page-container { + border: 1px solid #ddd; + border-radius: 8px; + overflow: hidden; + background: white; +} + +.page-header { + padding: 0.75rem 1rem; + font-weight: 600; + display: flex; + gap: 1.5rem; + align-items: center; + border-bottom: 1px solid #eee; +} + +.page-header .page-id { + font-size: 1rem; +} + +.page-header .page-stats { + font-weight: normal; + color: #666; + font-size: 0.85rem; +} + +.page-header .page-actions { + margin-left: auto; + display: flex; + gap: 0.25rem; +} + +.page-nodes { + padding: 0.5rem; +} + +.node-item { + border: 1px solid #e0e0e0; + border-radius: 6px; + margin: 0.5rem; + background: white; +} + +.node-header { + padding: 0.75rem 1rem; + display: flex; + align-items: center; + gap: 1rem; + cursor: pointer; + user-select: none; +} + +.node-header:hover { + background: #f8f9fa; +} + +.node-type { + font-weight: 600; + padding: 0.2rem 0.5rem; + border-radius: 4px; + font-size: 0.8rem; +} + +.node-type.branch { + background: #e3f2fd; + color: #1565c0; +} + +.node-type.account-leaf { + background: #e8f5e9; + color: #2e7d32; +} + +.node-type.storage-leaf { + background: #fff3e0; + color: #ef6c00; +} + +.node-path { + font-family: monospace; + font-size: 0.85rem; + color: #666; + position: relative; +} + +.node-path.clickable { + cursor: pointer; + padding: 0.1rem 0.3rem; + border-radius: 3px; + transition: background-color 0.15s; +} + +.node-path.clickable:hover { + background-color: #e3f2fd; +} + +.node-path.copied { + background-color: #c8e6c9; +} + +.node-path .path-base { + color: #999; +} + +.node-path .path-prefix { + font-weight: 600; + color: #333; +} + +.node-path .copy-feedback { + margin-left: 0.5rem; + font-size: 0.75rem; + color: #2e7d32; + font-weight: 500; +} + +.node-location { + color: #999; + font-size: 0.8rem; + margin-left: auto; +} + +.expand-icon { + transition: transform 0.2s; +} + +.expand-icon.expanded { + transform: rotate(90deg); +} + +.node-details { + padding: 0.75rem 1rem; + border-top: 1px solid #eee; + background: #fafafa; +} + +.node-details dl { + display: grid; + grid-template-columns: auto 1fr; + gap: 0.25rem 1rem; +} + +.node-details dt { + color: #666; + font-weight: 500; +} + +.node-details dd { + font-family: monospace; + word-break: break-all; +} + +.node-children { + padding: 0.5rem 1rem; + border-top: 1px solid #eee; +} + +.child-list { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.child-pointer { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-family: monospace; + font-size: 0.8rem; + cursor: pointer; +} + +.child-pointer.same-page { + background: #e8f5e9; + color: #2e7d32; +} + +.child-pointer.other-page { + background: #e3f2fd; + color: #1565c0; +} + +.child-pointer:hover { + filter: brightness(0.95); +} + +.loading { + text-align: center; + padding: 2rem; + color: #666; +} + +.error { + text-align: center; + padding: 2rem; + color: #d32f2f; + background: #ffebee; + border-radius: 8px; +} + +/* Metadata Modal */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal { + background: white; + border-radius: 8px; + max-width: 700px; + max-height: 80vh; + overflow: auto; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); +} + +.modal-header { + padding: 1rem 1.5rem; + border-bottom: 1px solid #eee; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h2 { + font-size: 1.25rem; +} + +.modal-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #666; +} + +.modal-body { + padding: 1.5rem; +} + +.metadata-section { + margin-bottom: 1.5rem; +} + +.metadata-section h3 { + font-size: 1rem; + margin-bottom: 0.75rem; + color: #333; +} + +.metadata-grid { + display: grid; + grid-template-columns: auto 1fr; + gap: 0.5rem 1rem; +} + +.metadata-grid dt { + color: #666; +} + +.metadata-grid dd { + font-family: monospace; + word-break: break-all; +} + +.orphan-list { + max-height: 200px; + overflow: auto; + border: 1px solid #eee; + border-radius: 4px; +} + +.orphan-item { + padding: 0.5rem 1rem; + border-bottom: 1px solid #eee; + display: flex; + justify-content: space-between; +} + +.orphan-item:last-child { + border-bottom: none; +} + +.empty-message { + text-align: center; + padding: 2rem; + color: #666; +} + +.search-result-banner { + padding: 0.75rem 1rem; + background: #e3f2fd; + border: 1px solid #90caf9; + border-radius: 4px; + margin-bottom: 1rem; + color: #1565c0; +} + +/* Tree view styles */ +.tree-node { + position: relative; +} + +.tree-node::before { + content: ''; + position: absolute; + left: -10px; + top: 0; + bottom: 0; + width: 1px; + background: #ddd; +} + +.tree-node:first-child::before { + top: 20px; +} + +.tree-node:last-child::before { + height: 20px; +} + +.tree-node::after { + content: ''; + position: absolute; + left: -10px; + top: 20px; + width: 10px; + height: 1px; + background: #ddd; +} + +.page-nodes > .tree-node::before, +.page-nodes > .tree-node::after { + display: none; +} + +.node-content { + padding-left: 0.5rem; +} + +.expand-icon-placeholder { + width: 1rem; + display: inline-block; +} + +.remote-pointer-box, +.storage-pointer-box { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: #f8f9fa; + border-radius: 4px; + margin: 0.25rem 0; +} + +.remote-nibble { + font-family: monospace; + font-weight: 600; + color: #666; +} + +.storage-label { + font-weight: 600; + color: #9c27b0; + font-size: 0.85rem; +} + +.loaded-subtree, +.loaded-storage-subtree { + margin: 0.25rem 0; + padding-left: 0.5rem; + border-left: 2px solid #e3f2fd; +} + +.loaded-storage-subtree { + border-left-color: #f3e5f5; +} + +.subtree-header { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + border-radius: 4px; + margin-bottom: 0.25rem; +} + +.subtree-page-id { + font-weight: 600; +} + +.subtree-page-stats { + color: #666; + font-size: 0.8rem; +} + +.storage-child-label { + padding: 0.25rem 0; +} + +.slot-nibble { + font-family: monospace; + font-weight: 600; + color: #666; + min-width: 1.5rem; + display: inline-block; +} + +.empty-slot { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.15rem 0; + opacity: 0.5; +} + +.slot-empty { + color: #ccc; +} + +.child-nibble-label { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.15rem 0; +} + +.pointer-hash { + font-family: monospace; + font-size: 0.7rem; + color: #999; + background: #f5f5f5; + padding: 0.1rem 0.3rem; + border-radius: 3px; + word-break: break-all; +} + +.path-account { + color: #9c27b0; + font-weight: 500; +} diff --git a/explorer/frontend/src/types/index.ts b/explorer/frontend/src/types/index.ts new file mode 100644 index 00000000..52ee31b3 --- /dev/null +++ b/explorer/frontend/src/types/index.ts @@ -0,0 +1,83 @@ +export interface ExplorerPointer { + locationType: 'SamePage' | 'OtherPage' | 'Unknown'; + cellIndex?: number; + pageId?: number; + rlpHash?: string; +} + +export interface ChildEntry { + index: number; + pointer: ExplorerPointer; +} + +export interface ExplorerNode { + pageId: number; + cellIndex: number; + prefix: string; + nodeType: 'AccountLeaf' | 'StorageLeaf' | 'Branch'; + sizeBytes: number; + nonce?: number; + balance?: string; + codeHash?: string; + storageRoot?: ExplorerPointer; + value?: string; + children?: ChildEntry[]; +} + +// Tree node that wraps ExplorerNode with resolved children +export interface TreeNode { + node: ExplorerNode; + localChildren: TreeNode[]; + basePath: string; + isStorageChild?: boolean; // True if this subtree is storage for an account +} + +export interface PageInfo { + pageId: number; + snapshotId: number; + totalBytes: number; + usedBytes: number; + freeBytes: number; + cellCount: number; + occupancyPercent: number; +} + +export interface OrphanPageInfo { + pageId: number; + orphanedAtSnapshot: number; +} + +export interface DatabaseInfo { + snapshotId: number; + rootNodeHash: string; + rootNodePageId: number | null; + totalPageCount: number; + orphanedPages: OrphanPageInfo[]; +} + +export interface PathSearchResult { + matchedPath: string; + node: ExplorerNode; + pathToNode: ExplorerNode[]; +} + +export interface ApiResponse { + success: boolean; + data?: T; + error?: string; +} + +export interface PageResponse { + info: PageInfo; + nodes: ExplorerNode[]; +} + +export interface RootResponse { + rootPageId: number | null; + page: PageResponse | null; +} + +export interface DescendantsResponse { + pageId: number; + nodes: ExplorerNode[]; +} diff --git a/explorer/frontend/src/utils/treeBuilder.ts b/explorer/frontend/src/utils/treeBuilder.ts new file mode 100644 index 00000000..26c09ffe --- /dev/null +++ b/explorer/frontend/src/utils/treeBuilder.ts @@ -0,0 +1,60 @@ +import type { ExplorerNode, TreeNode } from '../types'; + +/** + * Build a tree structure from a flat list of nodes. + * Cell 0 is always the canonical root of a page's subtrie. + * Also handles same-page storage root references for account leaves. + */ +export function buildTree(nodes: ExplorerNode[], basePath: string): TreeNode[] { + // Create a map from cellIndex -> node for quick lookup + const nodeMap = new Map(); + for (const node of nodes) { + nodeMap.set(node.cellIndex, node); + } + + // Cell 0 is always the root of a page's subtrie + const rootNode = nodeMap.get(0); + if (!rootNode) { + // No root node found - page may be empty or corrupted + return []; + } + + // Recursively build tree nodes + function buildTreeNode(node: ExplorerNode, currentPath: string, isStorageSubtree: boolean = false): TreeNode { + const nodePath = currentPath + (node.prefix || ''); + const localChildren: TreeNode[] = []; + + if (node.nodeType === 'Branch' && node.children) { + for (const child of node.children) { + if (child.pointer.locationType === 'SamePage' && child.pointer.cellIndex !== undefined) { + const childNode = nodeMap.get(child.pointer.cellIndex); + if (childNode) { + const nibble = child.index.toString(16).toUpperCase(); + localChildren.push(buildTreeNode(childNode, nodePath + nibble, isStorageSubtree)); + } + } + } + } + + // Handle same-page storage root for account leaves + if (node.nodeType === 'AccountLeaf' && node.storageRoot) { + if (node.storageRoot.locationType === 'SamePage' && node.storageRoot.cellIndex !== undefined) { + const storageNode = nodeMap.get(node.storageRoot.cellIndex); + if (storageNode) { + // Storage tries start fresh with empty path + const storageTreeNode = buildTreeNode(storageNode, '', true); + storageTreeNode.isStorageChild = true; + localChildren.push(storageTreeNode); + } + } + } + + return { + node, + localChildren, + basePath: currentPath, + }; + } + + return [buildTreeNode(rootNode, basePath)]; +} diff --git a/explorer/frontend/src/vite-env.d.ts b/explorer/frontend/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/explorer/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/explorer/frontend/tsconfig.json b/explorer/frontend/tsconfig.json new file mode 100644 index 00000000..3934b8f6 --- /dev/null +++ b/explorer/frontend/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/explorer/frontend/vite.config.ts b/explorer/frontend/vite.config.ts new file mode 100644 index 00000000..92fce032 --- /dev/null +++ b/explorer/frontend/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + build: { + outDir: 'dist', + emptyOutDir: true, + }, + server: { + proxy: { + '/api': 'http://localhost:3000', + }, + }, +}) diff --git a/explorer/src/handlers/metadata.rs b/explorer/src/handlers/metadata.rs new file mode 100644 index 00000000..a6d0aa51 --- /dev/null +++ b/explorer/src/handlers/metadata.rs @@ -0,0 +1,13 @@ +use axum::{extract::State, Json}; +use std::sync::Arc; +use triedb::storage::explorer::DatabaseInfo; + +use crate::{models::ApiResponse, state::AppState}; + +/// GET /api/metadata - Get database metadata including orphaned pages +pub async fn get_metadata( + State(state): State>, +) -> Json> { + let data = state.with_explorer(|explorer, _context| explorer.get_database_info()); + Json(ApiResponse::success(data)) +} diff --git a/explorer/src/handlers/mod.rs b/explorer/src/handlers/mod.rs new file mode 100644 index 00000000..776e97f9 --- /dev/null +++ b/explorer/src/handlers/mod.rs @@ -0,0 +1,4 @@ +pub mod metadata; +pub mod nodes; +pub mod pages; +pub mod search; diff --git a/explorer/src/handlers/nodes.rs b/explorer/src/handlers/nodes.rs new file mode 100644 index 00000000..688b5ef9 --- /dev/null +++ b/explorer/src/handlers/nodes.rs @@ -0,0 +1,26 @@ +use axum::{ + extract::{Path, State}, + Json, +}; +use std::sync::Arc; +use triedb::{page::PageId, storage::explorer::ExplorerNode}; + +use crate::{models::ApiResponse, state::AppState}; + +/// GET /api/nodes/:page_id/:cell_index - Get a single node +pub async fn get_node( + State(state): State>, + Path((page_id, cell_index)): Path<(u32, u8)>, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let page_id = PageId::new(page_id).ok_or_else(|| "Invalid page ID".to_string())?; + explorer + .get_node_at(context, page_id, cell_index) + .map_err(|e| format!("Failed to get node: {}", e)) + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} diff --git a/explorer/src/handlers/pages.rs b/explorer/src/handlers/pages.rs new file mode 100644 index 00000000..64967a7f --- /dev/null +++ b/explorer/src/handlers/pages.rs @@ -0,0 +1,129 @@ +use axum::{ + extract::{Path, State}, + Json, +}; +use std::sync::Arc; +use triedb::page::PageId; + +use crate::{ + models::{ApiResponse, DescendantsResponse, PageResponse, RootResponse}, + state::AppState, +}; + +/// GET /api/root - Get root page info and nodes +pub async fn get_root( + State(state): State>, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let root_page_id = explorer.get_root_page_id(); + + match root_page_id { + Some(page_id) => { + let info = explorer.get_page_info(context, page_id); + let nodes = explorer.get_page_nodes(context, page_id); + + match (info, nodes) { + (Ok(info), Ok(nodes)) => Ok(RootResponse { + root_page_id: Some(page_id.as_u32()), + page: Some(PageResponse { info, nodes }), + }), + (Err(e), _) => Err(format!("Failed to get page info: {}", e)), + (_, Err(e)) => Err(format!("Failed to get page nodes: {}", e)), + } + } + None => Ok(RootResponse { + root_page_id: None, + page: None, + }), + } + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// GET /api/pages/:page_id - Get all nodes on a page with page info +pub async fn get_page( + State(state): State>, + Path(page_id): Path, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let page_id = PageId::new(page_id).ok_or_else(|| "Invalid page ID".to_string())?; + let info = explorer + .get_page_info(context, page_id) + .map_err(|e| format!("Failed to get page info: {}", e))?; + let nodes = explorer + .get_page_nodes(context, page_id) + .map_err(|e| format!("Failed to get page nodes: {}", e))?; + Ok(PageResponse { info, nodes }) + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// GET /api/pages/:page_id/info - Get page metadata only +pub async fn get_page_info( + State(state): State>, + Path(page_id): Path, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let page_id = PageId::new(page_id).ok_or_else(|| "Invalid page ID".to_string())?; + explorer + .get_page_info(context, page_id) + .map_err(|e| format!("Failed to get page info: {}", e)) + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// GET /api/pages/:page_id/descendants - Get page-local descendants from cell 0 +pub async fn get_descendants( + State(state): State>, + Path(page_id): Path, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let page_id = PageId::new(page_id).ok_or_else(|| "Invalid page ID".to_string())?; + let nodes = explorer + .walk_page_local_descendants(context, page_id, 0) + .map_err(|e| format!("Failed to walk descendants: {}", e))?; + Ok(DescendantsResponse { + page_id: page_id.as_u32(), + nodes, + }) + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// GET /api/pages/:page_id/nodes/:cell_index/descendants - Get descendants from specific node +pub async fn get_node_descendants( + State(state): State>, + Path((page_id, cell_index)): Path<(u32, u8)>, +) -> Json> { + let result = state.with_explorer(|explorer, context| { + let page_id = PageId::new(page_id).ok_or_else(|| "Invalid page ID".to_string())?; + let nodes = explorer + .walk_page_local_descendants(context, page_id, cell_index) + .map_err(|e| format!("Failed to walk descendants: {}", e))?; + Ok(DescendantsResponse { + page_id: page_id.as_u32(), + nodes, + }) + }); + + match result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} diff --git a/explorer/src/handlers/search.rs b/explorer/src/handlers/search.rs new file mode 100644 index 00000000..a7901266 --- /dev/null +++ b/explorer/src/handlers/search.rs @@ -0,0 +1,124 @@ +use alloy_primitives::{hex, Address, StorageKey}; +use alloy_trie::Nibbles; +use axum::{ + extract::{Query, State}, + Json, +}; +use serde::Deserialize; +use std::sync::Arc; +use triedb::storage::explorer::PathSearchResult; + +use crate::{models::ApiResponse, state::AppState}; + +#[derive(Debug, Deserialize)] +pub struct SearchQuery { + /// The search query (path in hex, address, or storage slot) + pub q: String, + /// Type of query: "raw", "address", or "storage" + #[serde(rename = "type", default = "default_query_type")] + pub query_type: String, +} + +fn default_query_type() -> String { + "raw".to_string() +} + +/// GET /api/search?q=&type= +pub async fn search( + State(state): State>, + Query(params): Query, +) -> Json>> { + let result = parse_search_query(¶ms.q, ¶ms.query_type); + + let nibbles = match result { + Ok(n) => n, + Err(e) => return Json(ApiResponse::error(e)), + }; + + let search_result = state.with_explorer(|explorer, context| { + explorer + .search_by_path(context, &nibbles) + .map_err(|e| format!("Search failed: {}", e)) + }); + + match search_result { + Ok(data) => Json(ApiResponse::success(data)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +fn parse_search_query(query: &str, query_type: &str) -> Result { + match query_type { + "raw" => { + // Raw nibbles path (hex encoded) + // Supports colon-separated format: "0xAccountPath:0xStoragePath" for account+storage paths + let hex_str = query.strip_prefix("0x").unwrap_or(query); + + if let Some(colon_pos) = hex_str.find(':') { + // Account + storage path format + let account_hex = &hex_str[..colon_pos]; + let storage_hex = hex_str[colon_pos + 1..] + .strip_prefix("0x") + .unwrap_or(&hex_str[colon_pos + 1..]); + + // Combine account and storage nibbles + let combined_hex = format!("{}{}", account_hex, storage_hex); + let bytes = + hex::decode(&combined_hex).map_err(|e| format!("Invalid hex: {}", e))?; + Ok(Nibbles::unpack(&bytes)) + } else { + // Simple path + let bytes = hex::decode(hex_str).map_err(|e| format!("Invalid hex: {}", e))?; + Ok(Nibbles::unpack(&bytes)) + } + } + "address" => { + // Ethereum address -> keccak256 hash -> nibbles + let hex_str = query.strip_prefix("0x").unwrap_or(query); + if hex_str.len() != 40 { + return Err("Address must be 20 bytes (40 hex chars)".to_string()); + } + let bytes: [u8; 20] = hex::decode(hex_str) + .map_err(|e| format!("Invalid hex: {}", e))? + .try_into() + .map_err(|_| "Invalid address length".to_string())?; + let address = Address::from(bytes); + let hash = alloy_primitives::keccak256(address); + Ok(Nibbles::unpack(hash)) + } + "storage" => { + // Format: "address:slot" or "address slot" + let parts: Vec<&str> = query.split(|c| c == ':' || c == ' ').collect(); + if parts.len() != 2 { + return Err( + "Storage query must be 'address:slot' or 'address slot'".to_string() + ); + } + + let address_hex = parts[0].strip_prefix("0x").unwrap_or(parts[0]); + if address_hex.len() != 40 { + return Err("Address must be 20 bytes (40 hex chars)".to_string()); + } + let address_bytes: [u8; 20] = hex::decode(address_hex) + .map_err(|e| format!("Invalid address hex: {}", e))? + .try_into() + .map_err(|_| "Invalid address length".to_string())?; + let address = Address::from(address_bytes); + + let slot_hex = parts[1].strip_prefix("0x").unwrap_or(parts[1]); + let slot_bytes = hex::decode(slot_hex).map_err(|e| format!("Invalid slot hex: {}", e))?; + let slot = StorageKey::left_padding_from(&slot_bytes); + + // Hash address and slot + let address_hash = alloy_primitives::keccak256(address); + let slot_hash = alloy_primitives::keccak256(slot); + + // Combine into 128 nibbles (64 for address hash + 64 for slot hash) + let mut combined = Vec::with_capacity(64); + combined.extend_from_slice(address_hash.as_slice()); + combined.extend_from_slice(slot_hash.as_slice()); + Ok(Nibbles::unpack(&combined)) + } + _ => Err(format!("Unknown query type: {}", query_type)), + } +} diff --git a/explorer/src/main.rs b/explorer/src/main.rs new file mode 100644 index 00000000..98c426ab --- /dev/null +++ b/explorer/src/main.rs @@ -0,0 +1,115 @@ +mod handlers; +mod models; +mod state; + +use axum::{ + http::Method, + routing::get, + Router, +}; +use clap::Parser; +use include_dir::{include_dir, Dir}; +use std::{net::SocketAddr, sync::Arc}; +use tower_http::cors::{Any, CorsLayer}; + +use crate::state::AppState; + +static FRONTEND_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/frontend/dist"); + +#[derive(Parser)] +#[command(name = "triedb-explorer")] +#[command(about = "Interactive web UI for exploring triedb")] +struct Args { + /// Path to the database file + #[arg(short, long)] + database: String, + + /// Port to run the server on + #[arg(short, long, default_value = "3000")] + port: u16, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args = Args::parse(); + + println!("Opening database: {}", args.database); + let state = Arc::new( + AppState::new(&args.database).expect("Failed to open database"), + ); + + // CORS layer for development + let cors = CorsLayer::new() + .allow_methods([Method::GET, Method::POST]) + .allow_origin(Any) + .allow_headers(Any); + + // API routes + let api_routes = Router::new() + .route("/root", get(handlers::pages::get_root)) + .route("/pages/:page_id", get(handlers::pages::get_page)) + .route("/pages/:page_id/info", get(handlers::pages::get_page_info)) + .route( + "/pages/:page_id/descendants", + get(handlers::pages::get_descendants), + ) + .route( + "/pages/:page_id/nodes/:cell_index/descendants", + get(handlers::pages::get_node_descendants), + ) + .route( + "/nodes/:page_id/:cell_index", + get(handlers::nodes::get_node), + ) + .route("/search", get(handlers::search::search)) + .route("/metadata", get(handlers::metadata::get_metadata)); + + // Main router + let app = Router::new() + .nest("/api", api_routes) + .fallback(serve_frontend) + .layer(cors) + .with_state(state); + + let addr = SocketAddr::from(([127, 0, 0, 1], args.port)); + println!("Starting server at http://{}", addr); + + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app).await?; + + Ok(()) +} + +/// Serve static files from the embedded frontend directory. +async fn serve_frontend(uri: axum::http::Uri) -> impl axum::response::IntoResponse { + let path = uri.path().trim_start_matches('/'); + let path = if path.is_empty() { "index.html" } else { path }; + + match FRONTEND_DIR.get_file(path) { + Some(file) => { + let mime = mime_guess::from_path(path).first_or_octet_stream(); + ( + [(axum::http::header::CONTENT_TYPE, mime.as_ref())], + file.contents(), + ) + .into_response() + } + None => { + // Fallback to index.html for SPA routing + match FRONTEND_DIR.get_file("index.html") { + Some(file) => ( + [(axum::http::header::CONTENT_TYPE, "text/html")], + file.contents(), + ) + .into_response(), + None => ( + axum::http::StatusCode::NOT_FOUND, + "Frontend not found. Please build the frontend first.", + ) + .into_response(), + } + } + } +} + +use axum::response::IntoResponse; diff --git a/explorer/src/models.rs b/explorer/src/models.rs new file mode 100644 index 00000000..33eedb2c --- /dev/null +++ b/explorer/src/models.rs @@ -0,0 +1,55 @@ +use serde::Serialize; +use triedb::storage::explorer::{ExplorerNode, PageInfo}; + +/// API response wrapper. +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApiResponse { + pub success: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +impl ApiResponse { + pub fn success(data: T) -> Self { + Self { + success: true, + data: Some(data), + error: None, + } + } + + pub fn error(message: String) -> Self { + Self { + success: false, + data: None, + error: Some(message), + } + } +} + +/// Response for page endpoints. +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PageResponse { + pub info: PageInfo, + pub nodes: Vec, +} + +/// Response for root endpoint. +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RootResponse { + pub root_page_id: Option, + pub page: Option, +} + +/// Response for descendants endpoint. +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DescendantsResponse { + pub page_id: u32, + pub nodes: Vec, +} diff --git a/explorer/src/state.rs b/explorer/src/state.rs new file mode 100644 index 00000000..1b10210f --- /dev/null +++ b/explorer/src/state.rs @@ -0,0 +1,35 @@ +use parking_lot::Mutex; +use std::sync::Arc; +use triedb::{ + context::TransactionContext, + storage::explorer::ExplorerService, + Database, +}; + +/// Application state that holds the database and a read-only transaction context. +pub struct AppState { + pub database: Arc, + context: Mutex, +} + +impl AppState { + /// Creates a new AppState from a database path. + pub fn new(db_path: &str) -> Result { + let database = Arc::new(Database::open(db_path)?); + let context = database.read_context(); + Ok(Self { + database, + context: Mutex::new(context), + }) + } + + /// Executes a function with an ExplorerService and TransactionContext. + pub fn with_explorer(&self, f: F) -> T + where + F: FnOnce(&ExplorerService<'_>, &TransactionContext) -> T, + { + let context = self.context.lock(); + let explorer = self.database.explorer(); + f(&explorer, &context) + } +} diff --git a/src/database.rs b/src/database.rs index ff241742..007d5c45 100644 --- a/src/database.rs +++ b/src/database.rs @@ -231,6 +231,16 @@ impl Database { self.storage_engine.read_context().root_node_hash } + /// Returns a read context for exploration. + pub fn read_context(&self) -> TransactionContext { + self.storage_engine.read_context() + } + + /// Returns an ExplorerService for the interactive web UI. + pub fn explorer(&self) -> crate::storage::explorer::ExplorerService<'_> { + self.storage_engine.explorer() + } + pub fn size(&self) -> u32 { self.storage_engine.size() } diff --git a/src/storage.rs b/src/storage.rs index 2638784e..0640a234 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,5 +1,6 @@ pub mod debug; pub mod engine; +pub mod explorer; pub mod overlay_root; pub mod proofs; mod test_utils; diff --git a/src/storage/engine.rs b/src/storage/engine.rs index 1e814ff9..3448b2e2 100644 --- a/src/storage/engine.rs +++ b/src/storage/engine.rs @@ -1328,6 +1328,11 @@ impl StorageEngine { pub fn debugger(&self) -> crate::storage::debug::StorageDebugger<'_> { crate::storage::debug::StorageDebugger::new(&self.page_manager) } + + /// Returns an ExplorerService for the interactive web UI. + pub fn explorer(&self) -> crate::storage::explorer::ExplorerService<'_> { + crate::storage::explorer::ExplorerService::new(self) + } } fn node_location(page_id: PageId, page_index: u8) -> Location { diff --git a/src/storage/explorer.rs b/src/storage/explorer.rs new file mode 100644 index 00000000..3395c3b7 --- /dev/null +++ b/src/storage/explorer.rs @@ -0,0 +1,504 @@ +//! Explorer module for the interactive web UI. +//! +//! This module provides query methods specifically designed for the triedb explorer, +//! enabling interactive traversal of trie nodes and pages. + +use crate::{ + context::TransactionContext, + node::{Node, NodeKind}, + page::{Page, PageError, PageId, SlottedPage}, + pointer::Pointer, + storage::{ + engine::{Error, StorageEngine}, + value::Value, + }, +}; +use alloy_primitives::hex; +use alloy_trie::{nybbles, Nibbles}; +use serde::Serialize; +use std::collections::HashSet; + +/// Error type for explorer operations. +#[derive(Debug)] +pub enum ExplorerError { + PageError(PageError), + StorageError(Error), + InvalidCellIndex(u8), + NodeNotFound, + InvalidSearchQuery(String), +} + +impl From for ExplorerError { + fn from(e: PageError) -> Self { + Self::PageError(e) + } +} + +impl From for ExplorerError { + fn from(e: Error) -> Self { + Self::StorageError(e) + } +} + +impl std::fmt::Display for ExplorerError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExplorerError::PageError(e) => write!(f, "Page error: {:?}", e), + ExplorerError::StorageError(e) => write!(f, "Storage error: {:?}", e), + ExplorerError::InvalidCellIndex(i) => write!(f, "Invalid cell index: {}", i), + ExplorerError::NodeNotFound => write!(f, "Node not found"), + ExplorerError::InvalidSearchQuery(s) => write!(f, "Invalid search query: {}", s), + } + } +} + +impl std::error::Error for ExplorerError {} + +/// Represents a child pointer entry for branches. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChildEntry { + pub index: u8, + pub pointer: ExplorerPointer, +} + +/// Represents a pointer with location information for the UI. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ExplorerPointer { + pub location_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub cell_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rlp_hash: Option, +} + +/// Represents a trie node with its location information for the UI. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ExplorerNode { + pub page_id: u32, + pub cell_index: u8, + pub prefix: String, + pub node_type: String, + pub size_bytes: usize, + + // AccountLeaf fields + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub balance: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub code_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub storage_root: Option, + + // StorageLeaf fields + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + + // Branch fields + #[serde(skip_serializing_if = "Option::is_none")] + pub children: Option>, +} + +/// Page information for the UI. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PageInfo { + pub page_id: u32, + pub snapshot_id: u64, + pub total_bytes: usize, + pub used_bytes: usize, + pub free_bytes: usize, + pub cell_count: usize, + pub occupancy_percent: f32, +} + +/// Database metadata for the UI. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DatabaseInfo { + pub snapshot_id: u64, + pub root_node_hash: String, + pub root_node_page_id: Option, + pub total_page_count: u32, + pub orphaned_pages: Vec, +} + +/// Orphan page information. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrphanPageInfo { + pub page_id: u32, + pub orphaned_at_snapshot: u64, +} + +/// Result of a path search. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PathSearchResult { + pub matched_path: String, + pub node: ExplorerNode, + pub path_to_node: Vec, +} + +/// Explorer service for querying trie data. +#[derive(Debug)] +pub struct ExplorerService<'a> { + storage_engine: &'a StorageEngine, +} + +impl<'a> ExplorerService<'a> { + /// Creates a new ExplorerService. + pub fn new(storage_engine: &'a StorageEngine) -> Self { + Self { storage_engine } + } + + /// Gets a node at a specific page and cell index. + pub fn get_node_at( + &self, + context: &TransactionContext, + page_id: PageId, + cell_index: u8, + ) -> Result { + let slotted_page = self.storage_engine.get_slotted_page(context, page_id)?; + let node: Node = slotted_page + .get_value(cell_index) + .map_err(|_| ExplorerError::InvalidCellIndex(cell_index))?; + Ok(self.convert_node(page_id, cell_index, &node)) + } + + /// Gets all nodes on a page (including detecting orphaned cells). + pub fn get_page_nodes( + &self, + context: &TransactionContext, + page_id: PageId, + ) -> Result, ExplorerError> { + let slotted_page = self.storage_engine.get_slotted_page(context, page_id)?; + let num_cells = slotted_page.num_occupied_cells(); + let mut nodes = Vec::new(); + let mut found = 0; + + // Iterate through all cell indices, skipping deleted cells + for cell_idx in 0..255u8 { + if found >= num_cells { + break; + } + match slotted_page.get_value::(cell_idx) { + Ok(node) => { + nodes.push(self.convert_node(page_id, cell_idx, &node)); + found += 1; + } + Err(_) => { + // Deleted cell or invalid - continue to next + continue; + } + } + } + Ok(nodes) + } + + /// Gets page metadata. + pub fn get_page_info( + &self, + context: &TransactionContext, + page_id: PageId, + ) -> Result { + let page = self.get_page(context, page_id)?; + let snapshot_id = page.snapshot_id(); + let slotted_page = SlottedPage::try_from(page)?; + + let used_bytes = slotted_page.num_occupied_bytes(); + let free_bytes = slotted_page.num_free_bytes(); + let total_bytes = Page::DATA_SIZE; + let cell_count = slotted_page.num_occupied_cells(); + + Ok(PageInfo { + page_id: page_id.as_u32(), + snapshot_id, + total_bytes, + used_bytes, + free_bytes, + cell_count, + occupancy_percent: (used_bytes as f32 / total_bytes as f32) * 100.0, + }) + } + + /// Walks all page-local descendants recursively from a starting cell. + pub fn walk_page_local_descendants( + &self, + context: &TransactionContext, + page_id: PageId, + start_cell_index: u8, + ) -> Result, ExplorerError> { + let slotted_page = self.storage_engine.get_slotted_page(context, page_id)?; + let mut result = Vec::new(); + let mut stack = vec![start_cell_index]; + let mut visited = HashSet::new(); + + while let Some(cell_idx) = stack.pop() { + if visited.contains(&cell_idx) { + continue; + } + visited.insert(cell_idx); + + let node: Node = match slotted_page.get_value(cell_idx) { + Ok(n) => n, + Err(_) => continue, + }; + result.push(self.convert_node(page_id, cell_idx, &node)); + + // Add same-page children to stack + match node.kind() { + NodeKind::Branch { children } => { + for child in children.iter().flatten() { + if let Some(child_cell_idx) = child.location().cell_index() { + stack.push(child_cell_idx); + } + } + } + NodeKind::AccountLeaf { storage_root, .. } => { + if let Some(ptr) = storage_root { + if let Some(child_cell_idx) = ptr.location().cell_index() { + stack.push(child_cell_idx); + } + } + } + NodeKind::StorageLeaf { .. } => {} + } + } + Ok(result) + } + + /// Searches by path and returns the matched node along with ancestors. + pub fn search_by_path( + &self, + context: &TransactionContext, + path: &Nibbles, + ) -> Result, ExplorerError> { + let root_page_id = match context.root_node_page_id { + Some(id) => id, + None => return Ok(None), + }; + + let mut path_to_node = Vec::new(); + let mut current_page_id = root_page_id; + let mut current_cell_idx = 0u8; + let mut path_offset = 0usize; + + loop { + let slotted_page = self.storage_engine.get_slotted_page(context, current_page_id)?; + let node: Node = slotted_page + .get_value(current_cell_idx) + .map_err(|_| ExplorerError::NodeNotFound)?; + + let explorer_node = self.convert_node(current_page_id, current_cell_idx, &node); + path_to_node.push(explorer_node.clone()); + + let prefix = node.prefix(); + let remaining_path = &path[path_offset..]; + let common_len = nybbles::common_prefix_length(remaining_path, prefix); + + if common_len < prefix.len() { + // Path diverges - no match + return Ok(None); + } + + path_offset += common_len; + + if path_offset >= path.len() { + // Found the node + return Ok(Some(PathSearchResult { + matched_path: hex::encode(path.pack()), + node: explorer_node, + path_to_node, + })); + } + + // Navigate to child + let next_nibble = path[path_offset]; + path_offset += 1; + + match node.kind() { + NodeKind::Branch { children } => match &children[next_nibble as usize] { + Some(ptr) => { + if let Some(cell_idx) = ptr.location().cell_index() { + current_cell_idx = cell_idx; + } else if let Some(page_id) = ptr.location().page_id() { + current_page_id = page_id; + current_cell_idx = 0; + } else { + return Ok(None); + } + } + None => return Ok(None), + }, + NodeKind::AccountLeaf { storage_root, .. } => { + // AccountLeaf doesn't consume a nibble for child navigation to storage + path_offset -= 1; + match storage_root { + Some(ptr) => { + if let Some(cell_idx) = ptr.location().cell_index() { + current_cell_idx = cell_idx; + } else if let Some(page_id) = ptr.location().page_id() { + current_page_id = page_id; + current_cell_idx = 0; + } else { + return Ok(None); + } + } + None => return Ok(None), + } + } + NodeKind::StorageLeaf { .. } => return Ok(None), + } + } + } + + /// Gets database metadata including orphaned pages. + pub fn get_database_info(&self) -> DatabaseInfo { + let mut meta_manager = self.storage_engine.meta_manager.lock(); + + // Collect orphaned pages first + let orphaned_pages: Vec = meta_manager + .orphan_pages() + .iter() + .map(|op| OrphanPageInfo { + page_id: op.page_id().as_u32(), + orphaned_at_snapshot: op.orphaned_at(), + }) + .collect(); + + let active_slot = meta_manager.active_slot(); + DatabaseInfo { + snapshot_id: active_slot.snapshot_id(), + root_node_hash: hex::encode(active_slot.root_node_hash()), + root_node_page_id: active_slot.root_node_page_id().map(|id| id.as_u32()), + total_page_count: active_slot.page_count(), + orphaned_pages, + } + } + + /// Gets the root page ID from metadata. + pub fn get_root_page_id(&self) -> Option { + let meta_manager = self.storage_engine.meta_manager.lock(); + meta_manager.active_slot().root_node_page_id() + } + + /// Helper to get a page from the page manager. + fn get_page( + &self, + context: &TransactionContext, + page_id: PageId, + ) -> Result, ExplorerError> { + self.storage_engine + .page_manager + .get(context.snapshot_id, page_id) + .map_err(|e| ExplorerError::PageError(e)) + } + + /// Converts a Node to ExplorerNode. + fn convert_node(&self, page_id: PageId, cell_index: u8, node: &Node) -> ExplorerNode { + // Convert nibbles to hex string directly (avoiding pack() which adds trailing 0 for odd lengths) + let prefix: String = node.prefix().iter().map(|n| format!("{:X}", n)).collect(); + let size_bytes = node.size(); + + match node.kind() { + NodeKind::AccountLeaf { nonce_rlp, balance_rlp, code_hash, storage_root } => { + let nonce: u64 = alloy_rlp::decode_exact(nonce_rlp).unwrap_or(0); + let balance: alloy_primitives::U256 = + alloy_rlp::decode_exact(balance_rlp).unwrap_or_default(); + + ExplorerNode { + page_id: page_id.as_u32(), + cell_index, + prefix, + node_type: "AccountLeaf".to_string(), + size_bytes, + nonce: Some(nonce), + balance: Some(format!("{}", balance)), + code_hash: Some(hex::encode(code_hash)), + storage_root: storage_root.as_ref().map(|p| self.convert_pointer(p)), + value: None, + children: None, + } + } + NodeKind::StorageLeaf { value_rlp } => { + let value: alloy_primitives::U256 = + alloy_rlp::decode_exact(value_rlp).unwrap_or_default(); + + ExplorerNode { + page_id: page_id.as_u32(), + cell_index, + prefix, + node_type: "StorageLeaf".to_string(), + size_bytes, + nonce: None, + balance: None, + code_hash: None, + storage_root: None, + value: Some(format!("0x{:064x}", value)), + children: None, + } + } + NodeKind::Branch { children } => { + let child_entries: Vec = children + .iter() + .enumerate() + .filter_map(|(idx, opt)| { + opt.as_ref().map(|p| ChildEntry { + index: idx as u8, + pointer: self.convert_pointer(p), + }) + }) + .collect(); + + ExplorerNode { + page_id: page_id.as_u32(), + cell_index, + prefix, + node_type: "Branch".to_string(), + size_bytes, + nonce: None, + balance: None, + code_hash: None, + storage_root: None, + value: None, + children: Some(child_entries), + } + } + } + } + + /// Converts a Pointer to ExplorerPointer. + fn convert_pointer(&self, pointer: &Pointer) -> ExplorerPointer { + let location = pointer.location(); + let rlp_hash = pointer.rlp().as_hash().map(|h| hex::encode(h)); + + if let Some(cell_idx) = location.cell_index() { + ExplorerPointer { + location_type: "SamePage".to_string(), + cell_index: Some(cell_idx), + page_id: None, + rlp_hash, + } + } else if let Some(page_id) = location.page_id() { + ExplorerPointer { + location_type: "OtherPage".to_string(), + cell_index: None, + page_id: Some(page_id.as_u32()), + rlp_hash, + } + } else { + ExplorerPointer { + location_type: "Unknown".to_string(), + cell_index: None, + page_id: None, + rlp_hash, + } + } + } +}