Compare commits

..

6 Commits

Author SHA1 Message Date
nym21 0a789fe551 release: v0.0.30 2025-04-23 00:07:41 +02:00
nym21 caa8ff23ed kibo: fix charts data fetch 2025-04-23 00:04:09 +02:00
nym21 ee30d1d36d release: v0.0.29 2025-04-22 19:34:55 +02:00
nym21 0d9415db9d kibo: fix add ts-ignore 2025-04-22 19:33:57 +02:00
nym21 8020e1126f kibo: database: part 2 2025-04-22 19:31:30 +02:00
nym21 3439422057 kibo: database: part 1 2025-04-21 23:17:37 +02:00
18 changed files with 1560 additions and 455 deletions
Generated
+72 -72
View File
@@ -152,9 +152,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.22" version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07"
dependencies = [ dependencies = [
"brotli", "brotli",
"flate2", "flate2",
@@ -374,7 +374,7 @@ dependencies = [
[[package]] [[package]]
name = "brk" name = "brk"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_cli", "brk_cli",
"brk_computer", "brk_computer",
@@ -391,7 +391,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_cli" name = "brk_cli"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_computer", "brk_computer",
"brk_core", "brk_core",
@@ -412,7 +412,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_computer" name = "brk_computer"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_core", "brk_core",
"brk_exit", "brk_exit",
@@ -427,7 +427,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_core" name = "brk_core"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"bitcoin", "bitcoin",
"bitcoincore-rpc", "bitcoincore-rpc",
@@ -444,7 +444,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_exit" name = "brk_exit"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_logger", "brk_logger",
"ctrlc", "ctrlc",
@@ -453,7 +453,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_fetcher" name = "brk_fetcher"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_core", "brk_core",
"brk_logger", "brk_logger",
@@ -466,7 +466,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_indexer" name = "brk_indexer"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"bitcoin", "bitcoin",
"bitcoincore-rpc", "bitcoincore-rpc",
@@ -485,7 +485,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_logger" name = "brk_logger"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"env_logger", "env_logger",
@@ -495,7 +495,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_parser" name = "brk_parser"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"bitcoin", "bitcoin",
"bitcoincore-rpc", "bitcoincore-rpc",
@@ -510,7 +510,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_query" name = "brk_query"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"brk_computer", "brk_computer",
"brk_indexer", "brk_indexer",
@@ -526,7 +526,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_server" name = "brk_server"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"axum", "axum",
"brk_computer", "brk_computer",
@@ -552,7 +552,7 @@ dependencies = [
[[package]] [[package]]
name = "brk_vec" name = "brk_vec"
version = "0.0.28" version = "0.0.30"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"axum", "axum",
@@ -566,9 +566,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "7.0.0" version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" checksum = "cf19e729cdbd51af9a397fb9ef8ac8378007b797f8273cfbfdf45dcaa316167b"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@@ -577,9 +577,9 @@ dependencies = [
[[package]] [[package]]
name = "brotli-decompressor" name = "brotli-decompressor"
version = "4.0.3" version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
@@ -694,9 +694,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.36" version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -704,9 +704,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.36" version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -1107,9 +1107,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]] [[package]]
name = "fjall" name = "fjall"
version = "2.8.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b2ced3483989a62b3533c9f99054d73b527c6c0045cf22b00fe87956f1a46f" checksum = "958511f67d1f80e6bff9ffac05c626bb340d4602ca6ea5617d9901c218c894f0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"byteview", "byteview",
@@ -1478,9 +1478,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "jiff" name = "jiff"
version = "0.2.8" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
dependencies = [ dependencies = [
"jiff-static", "jiff-static",
"jiff-tzdb-platform", "jiff-tzdb-platform",
@@ -1493,9 +1493,9 @@ dependencies = [
[[package]] [[package]]
name = "jiff-static" name = "jiff-static"
version = "0.2.8" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1585,9 +1585,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]] [[package]]
name = "lsm-tree" name = "lsm-tree"
version = "2.8.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a63a5e98a38b51765274137d8aedfbd848da5f4d016867e186b673fcc06a8c" checksum = "87d58bdef2dcbf50fce9f343265bdbd7fb08a458d241eb837ce426be22d674b4"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"crossbeam-skiplist", "crossbeam-skiplist",
@@ -1791,9 +1791,9 @@ checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
[[package]] [[package]]
name = "oxc" name = "oxc"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "548086420c5c78546c7417384689af59ed11dbe9462e8bfef42636be0f545b96" checksum = "339134dd7e4bb36c2d0e97601fe4193b37649f729e94e7c26667feecd439b27e"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1834,9 +1834,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_allocator" name = "oxc_allocator"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acaf33eacde1fca8fdb26655d7486842a8106916bc3855265607db2b4a4eaec4" checksum = "56c3dd872f5f819775c28163977399a1dd4f2177f3745dcb1bfd4214bae27e43"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"bumpalo", "bumpalo",
@@ -1848,9 +1848,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_ast" name = "oxc_ast"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3044daf0f6b02f27330954141d799f9206e04b9175a3f4fa199f78c5ef46b2b4" checksum = "a1bebb401e36d73f35400ee9609ab6372b457543ce7bb1ef17c922235f24ba7b"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cow-utils", "cow-utils",
@@ -1865,9 +1865,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_ast_macros" name = "oxc_ast_macros"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee5d1546ecb309530b30b48f1aefefad7e6d53b8502e7b91b20d6a523af270e" checksum = "3b997c2a787321814d6e92f212486b4d590af4dbfff5c2265d3b0386e016eafe"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1876,9 +1876,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_ast_visit" name = "oxc_ast_visit"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b29d0e9e05d2638317c39470b5a528e268fc088ae1e3fda26b049661f2de25ad" checksum = "e1ee363bd1ff2000ae2a1052bb4d68e827e62f76e0139449ad1301ad9adc6ac4"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1888,9 +1888,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_cfg" name = "oxc_cfg"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b3740da43d597136474fa738bafb697c8469b8ecd9c256819ae443ec91a67aa" checksum = "77cb606168f768c747885ae48272287a028de5374db865c29d6d307d3b601a97"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"itertools", "itertools",
@@ -1903,9 +1903,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_codegen" name = "oxc_codegen"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee5e197f8bed070bcc6e9744a7a5f91bc4ff250543f07849567e6e602383c7db" checksum = "4afef88d94fe9eee073332e3f908bd429baa989300d83152dab10dad99bd2a66"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cow-utils", "cow-utils",
@@ -1924,15 +1924,15 @@ dependencies = [
[[package]] [[package]]
name = "oxc_data_structures" name = "oxc_data_structures"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ca20f4fc9af4f462bfc6bbca60e3f4c2ca9c4f09e99363c0c85d90bb936641c" checksum = "235fe6251a3f06813e77b45f3cd9c7ec5d0a23741c33f6c308b5af393d5d2409"
[[package]] [[package]]
name = "oxc_diagnostics" name = "oxc_diagnostics"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e5ef1106d01ade76eec600e5b0da38932f555d1cb661f0d5ec7f3d717da22d" checksum = "2cd0f62519773d942baf1a17474e53b535182182f2cb41319ad1c2782e34310d"
dependencies = [ dependencies = [
"cow-utils", "cow-utils",
"oxc-miette", "oxc-miette",
@@ -1940,9 +1940,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_ecmascript" name = "oxc_ecmascript"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76b1ea0ef90fda6b68127181b3b136b21041a595e8c2028c4e08fcf85f99a10c" checksum = "5d073fdd2a3e1adb63731faab3c3861d0f9244cc15e88453d78460a4b2a604ca"
dependencies = [ dependencies = [
"cow-utils", "cow-utils",
"num-bigint", "num-bigint",
@@ -1954,9 +1954,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_estree" name = "oxc_estree"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9ce9584683d43622ef4b66bd605ce9878f3bf7468b9b59ad041f33af13c34d" checksum = "59df015cfd58d084b27d049281835094556a4469db3e2ac45b0676336e0aeed8"
[[package]] [[package]]
name = "oxc_index" name = "oxc_index"
@@ -1966,9 +1966,9 @@ checksum = "2fa07b0cfa997730afed43705766ef27792873fdf5215b1391949fec678d2392"
[[package]] [[package]]
name = "oxc_mangler" name = "oxc_mangler"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c80a552dc097088d466679bcaba2576bbe88a430ca9c5b0cb67e23787aaa7a" checksum = "7dfc3363726f4ade77ba4d9e56cc38cc306436feee7b72e1c69436fb30c02b9b"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"itertools", "itertools",
@@ -1983,9 +1983,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_minifier" name = "oxc_minifier"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e504ac83f23ff755eae4ebd6291258b44e05cf58379e1fe1c00b1007e41f29" checksum = "f095580f12a6277d4cb532310ba95cc6d1f719b6c59341fb392b71e37d1207e6"
dependencies = [ dependencies = [
"cow-utils", "cow-utils",
"oxc_allocator", "oxc_allocator",
@@ -2005,9 +2005,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_parser" name = "oxc_parser"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688491b2dfe8049a6410bc6b92f71e75bebfe9a1f352a64084a62069cd644e7f" checksum = "8d54fd003e5347dcb5ccdcc96cb283bf32a648ff291912b5a6e5e718b3359935"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cow-utils", "cow-utils",
@@ -2028,9 +2028,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_regular_expression" name = "oxc_regular_expression"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be6231edf43b8ca0fa8edad93b0b8e9067a3991ea2d8aa9f22a4c8fa464f267" checksum = "5e6e8058355e43a2388112eddf73031d8749b40255ad6f3a824e35fe0c6addec"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast_macros", "oxc_ast_macros",
@@ -2044,9 +2044,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_semantic" name = "oxc_semantic"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5c80c996fafc27a8444732bbba06f1de3eb488c585d65293a4b40b089ff564" checksum = "3f9f297db174c99b43e1b268b0960dcc588767093575cc39bb5517196a7a21d3"
dependencies = [ dependencies = [
"itertools", "itertools",
"oxc_allocator", "oxc_allocator",
@@ -2080,9 +2080,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_span" name = "oxc_span"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bce0a945765d00dd9ee0bfdc38fa265008e66f919157894c0383c525d026511f" checksum = "69575b94d8fdac097784abf48a5e43ee38375aa377f8e9bb8bc80f2ef3d2269c"
dependencies = [ dependencies = [
"compact_str", "compact_str",
"oxc-miette", "oxc-miette",
@@ -2093,9 +2093,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_syntax" name = "oxc_syntax"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b876a00908cb1fc6648823d7d8579a69af1c5a5c6d4cf6135040e09f41d116" checksum = "32c4940ed3e38ce51fb6bc3e880edb815182bdcdc19c6dc110857625aac9c9a7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cow-utils", "cow-utils",
@@ -2114,9 +2114,9 @@ dependencies = [
[[package]] [[package]]
name = "oxc_traverse" name = "oxc_traverse"
version = "0.64.0" version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c25f3da54211abd4b6ff1390a0f28d42b2128cb33f4b38edbdc5cb71bc3d3c" checksum = "5f8868dcb4ac2cd1e08dadbf34cd4fd9bbf36b70ef64dfa3376e1042a9f705a3"
dependencies = [ dependencies = [
"compact_str", "compact_str",
"itoa", "itoa",
@@ -2718,9 +2718,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.2" version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -3153,9 +3153,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "value-log" name = "value-log"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd29b17c041f94e0885179637289815cd038f0c9fc19c4549d5a97017404fb7d" checksum = "62fc7c4ce161f049607ecea654dca3f2d727da5371ae85e2e4f14ce2b98ed67c"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"byteview", "byteview",
+4 -4
View File
@@ -4,7 +4,7 @@ members = ["crates/*"]
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node" package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
package.license = "MIT" package.license = "MIT"
package.edition = "2024" package.edition = "2024"
package.version = "0.0.28" package.version = "0.0.30"
package.repository = "https://github.com/bitcoinresearchkit/brk" package.repository = "https://github.com/bitcoinresearchkit/brk"
[profile.release] [profile.release]
@@ -31,11 +31,11 @@ brk_query = { version = "0", path = "crates/brk_query" }
brk_server = { version = "0", path = "crates/brk_server" } brk_server = { version = "0", path = "crates/brk_server" }
brk_vec = { version = "0", path = "crates/brk_vec" } brk_vec = { version = "0", path = "crates/brk_vec" }
byteview = "0.6.1" byteview = "0.6.1"
clap = { version = "4.5.36", features = ["derive", "string"] } clap = { version = "4.5.37", features = ["derive", "string"] }
color-eyre = "0.6.3" color-eyre = "0.6.3"
derive_deref = "1.1.1" derive_deref = "1.1.1"
fjall = "2.8.0" fjall = "2.9.0"
jiff = "0.2.8" jiff = "0.2.10"
log = { version = "0.4.27" } log = { version = "0.4.27" }
minreq = { version = "2.13.4", features = ["https", "serde_json"] } minreq = { version = "2.13.4", features = ["https", "serde_json"] }
rayon = "1.10.0" rayon = "1.10.0"
+1
View File
@@ -92,6 +92,7 @@ Heartfelt thanks go out to every donor on [Nostr](https://primal.net/p/npub1jagm
If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org). If you'd like to have your own instance hosted for you please contact [hosting@bitcoinresearchkit.org](mailto:hosting@bitcoinresearchkit.org).
- 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability - 2 separate dedicated servers (1 GB/s each) with different ISPs and Cloudflare integration for enhanced performance and optimal availability
- 99.9% SLA
- Direct communication for feature requests and support - Direct communication for feature requests and support
- Updates delivered at your convenience - Updates delivered at your convenience
- Optional subdomains: `*.bitcoinresearchkit.org`, `*.kibo.money` and `*.satonomics.xyz` - Optional subdomains: `*.bitcoinresearchkit.org`, `*.kibo.money` and `*.satonomics.xyz`
@@ -154,6 +154,30 @@ where
Ok(()) Ok(())
} }
pub fn compute_range<A, F>(
&mut self,
max_from: I,
other: &mut StoredVec<I, A>,
mut t: F,
exit: &Exit,
) -> Result<()>
where
A: StoredType,
F: FnMut(I) -> (I, T),
{
self.validate_computed_version_or_reset_file(
Version::ZERO + self.version() + other.version(),
)?;
let index = max_from.min(I::from(self.len()));
(index.to_usize()?..other.len()).try_for_each(|i| {
let (i, v) = t(I::from(i));
self.forced_push_at(i, v, exit)
})?;
self.safe_flush(exit)
}
pub fn compute_transform<A, B, F>( pub fn compute_transform<A, B, F>(
&mut self, &mut self,
max_from: A, max_from: A,
@@ -120,10 +120,10 @@ impl Vecs {
starting_indexes, starting_indexes,
exit, exit,
|v, indexer, _, starting_indexes, exit| { |v, indexer, _, starting_indexes, exit| {
v.compute_transform( v.compute_range(
starting_indexes.height, starting_indexes.height,
indexer.mut_vecs().height_to_weight.mut_vec(), indexer.mut_vecs().height_to_weight.mut_vec(),
|(h, ..)| (h, StoredU32::from(1_u32)), |h| (h, StoredU32::from(1_u32)),
exit, exit,
) )
}, },
+253 -40
View File
@@ -1,8 +1,10 @@
use std::{fs, ops::Deref, path::Path}; use std::{fs, ops::Deref, path::Path};
use brk_core::{ use brk_core::{
Date, Dateindex, Decadeindex, Difficultyepoch, Halvingepoch, Height, Monthindex, Quarterindex, Addressindex, Date, Dateindex, Decadeindex, Difficultyepoch, Emptyindex, Halvingepoch, Height,
Timestamp, Txindex, Txinindex, Txoutindex, Weekindex, Yearindex, Monthindex, Multisigindex, Opreturnindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex,
P2TRindex, P2WPKHindex, P2WSHindex, Pushonlyindex, Quarterindex, Timestamp, Txindex, Txinindex,
Txoutindex, Unknownindex, Weekindex, Yearindex,
}; };
use brk_exit::Exit; use brk_exit::Exit;
use brk_indexer::Indexer; use brk_indexer::Indexer;
@@ -12,6 +14,7 @@ use super::ComputedVec;
#[derive(Clone)] #[derive(Clone)]
pub struct Vecs { pub struct Vecs {
pub addressindex_to_addressindex: ComputedVec<Addressindex, Addressindex>,
pub dateindex_to_date: ComputedVec<Dateindex, Date>, pub dateindex_to_date: ComputedVec<Dateindex, Date>,
pub dateindex_to_dateindex: ComputedVec<Dateindex, Dateindex>, pub dateindex_to_dateindex: ComputedVec<Dateindex, Dateindex>,
pub dateindex_to_first_height: ComputedVec<Dateindex, Height>, pub dateindex_to_first_height: ComputedVec<Dateindex, Height>,
@@ -27,6 +30,7 @@ pub struct Vecs {
pub difficultyepoch_to_first_height: ComputedVec<Difficultyepoch, Height>, pub difficultyepoch_to_first_height: ComputedVec<Difficultyepoch, Height>,
pub difficultyepoch_to_last_height: ComputedVec<Difficultyepoch, Height>, pub difficultyepoch_to_last_height: ComputedVec<Difficultyepoch, Height>,
pub difficultyepoch_to_timestamp: ComputedVec<Difficultyepoch, Timestamp>, pub difficultyepoch_to_timestamp: ComputedVec<Difficultyepoch, Timestamp>,
pub emptyindex_to_emptyindex: ComputedVec<Emptyindex, Emptyindex>,
pub halvingepoch_to_first_height: ComputedVec<Halvingepoch, Height>, pub halvingepoch_to_first_height: ComputedVec<Halvingepoch, Height>,
pub halvingepoch_to_halvingepoch: ComputedVec<Halvingepoch, Halvingepoch>, pub halvingepoch_to_halvingepoch: ComputedVec<Halvingepoch, Halvingepoch>,
pub halvingepoch_to_last_height: ComputedVec<Halvingepoch, Height>, pub halvingepoch_to_last_height: ComputedVec<Halvingepoch, Height>,
@@ -45,12 +49,26 @@ pub struct Vecs {
pub monthindex_to_quarterindex: ComputedVec<Monthindex, Quarterindex>, pub monthindex_to_quarterindex: ComputedVec<Monthindex, Quarterindex>,
pub monthindex_to_timestamp: ComputedVec<Monthindex, Timestamp>, pub monthindex_to_timestamp: ComputedVec<Monthindex, Timestamp>,
pub monthindex_to_yearindex: ComputedVec<Monthindex, Yearindex>, pub monthindex_to_yearindex: ComputedVec<Monthindex, Yearindex>,
pub multisigindex_to_multisigindex: ComputedVec<Multisigindex, Multisigindex>,
pub opreturnindex_to_opreturnindex: ComputedVec<Opreturnindex, Opreturnindex>,
pub p2pk33index_to_p2pk33index: ComputedVec<P2PK33index, P2PK33index>,
pub p2pk65index_to_p2pk65index: ComputedVec<P2PK65index, P2PK65index>,
pub p2pkhindex_to_p2pkhindex: ComputedVec<P2PKHindex, P2PKHindex>,
pub p2shindex_to_p2shindex: ComputedVec<P2SHindex, P2SHindex>,
pub p2trindex_to_p2trindex: ComputedVec<P2TRindex, P2TRindex>,
pub p2wpkhindex_to_p2wpkhindex: ComputedVec<P2WPKHindex, P2WPKHindex>,
pub p2wshindex_to_p2wshindex: ComputedVec<P2WSHindex, P2WSHindex>,
pub pushonlyindex_to_pushonlyindex: ComputedVec<Pushonlyindex, Pushonlyindex>,
pub quarterindex_to_first_monthindex: ComputedVec<Quarterindex, Monthindex>, pub quarterindex_to_first_monthindex: ComputedVec<Quarterindex, Monthindex>,
pub quarterindex_to_last_monthindex: ComputedVec<Quarterindex, Monthindex>, pub quarterindex_to_last_monthindex: ComputedVec<Quarterindex, Monthindex>,
pub quarterindex_to_quarterindex: ComputedVec<Quarterindex, Quarterindex>, pub quarterindex_to_quarterindex: ComputedVec<Quarterindex, Quarterindex>,
pub quarterindex_to_timestamp: ComputedVec<Quarterindex, Timestamp>, pub quarterindex_to_timestamp: ComputedVec<Quarterindex, Timestamp>,
pub txindex_to_last_txinindex: ComputedVec<Txindex, Txinindex>, pub txindex_to_last_txinindex: ComputedVec<Txindex, Txinindex>,
pub txindex_to_last_txoutindex: ComputedVec<Txindex, Txoutindex>, pub txindex_to_last_txoutindex: ComputedVec<Txindex, Txoutindex>,
pub txindex_to_txindex: ComputedVec<Txindex, Txindex>,
pub txinindex_to_txinindex: ComputedVec<Txinindex, Txinindex>,
pub txoutindex_to_txoutindex: ComputedVec<Txoutindex, Txoutindex>,
pub unknownindex_to_unknownindex: ComputedVec<Unknownindex, Unknownindex>,
pub weekindex_to_first_dateindex: ComputedVec<Weekindex, Dateindex>, pub weekindex_to_first_dateindex: ComputedVec<Weekindex, Dateindex>,
pub weekindex_to_last_dateindex: ComputedVec<Weekindex, Dateindex>, pub weekindex_to_last_dateindex: ComputedVec<Weekindex, Dateindex>,
pub weekindex_to_timestamp: ComputedVec<Weekindex, Timestamp>, pub weekindex_to_timestamp: ComputedVec<Weekindex, Timestamp>,
@@ -307,6 +325,86 @@ impl Vecs {
Version::ZERO, Version::ZERO,
compressed, compressed,
)?, )?,
p2pk33index_to_p2pk33index: ComputedVec::forced_import(
&path.join("p2pk33index_to_p2pk33index"),
Version::ZERO,
compressed,
)?,
p2pk65index_to_p2pk65index: ComputedVec::forced_import(
&path.join("p2pk65index_to_p2pk65index"),
Version::ZERO,
compressed,
)?,
p2pkhindex_to_p2pkhindex: ComputedVec::forced_import(
&path.join("p2pkhindex_to_p2pkhindex"),
Version::ZERO,
compressed,
)?,
p2shindex_to_p2shindex: ComputedVec::forced_import(
&path.join("p2shindex_to_p2shindex"),
Version::ZERO,
compressed,
)?,
p2trindex_to_p2trindex: ComputedVec::forced_import(
&path.join("p2trindex_to_p2trindex"),
Version::ZERO,
compressed,
)?,
p2wpkhindex_to_p2wpkhindex: ComputedVec::forced_import(
&path.join("p2wpkhindex_to_p2wpkhindex"),
Version::ZERO,
compressed,
)?,
p2wshindex_to_p2wshindex: ComputedVec::forced_import(
&path.join("p2wshindex_to_p2wshindex"),
Version::ZERO,
compressed,
)?,
txindex_to_txindex: ComputedVec::forced_import(
&path.join("txindex_to_txindex"),
Version::ZERO,
compressed,
)?,
txinindex_to_txinindex: ComputedVec::forced_import(
&path.join("txinindex_to_txinindex"),
Version::ZERO,
compressed,
)?,
emptyindex_to_emptyindex: ComputedVec::forced_import(
&path.join("emptyindex_to_emptyindex"),
Version::ZERO,
compressed,
)?,
multisigindex_to_multisigindex: ComputedVec::forced_import(
&path.join("multisigindex_to_multisigindex"),
Version::ZERO,
compressed,
)?,
opreturnindex_to_opreturnindex: ComputedVec::forced_import(
&path.join("opreturnindex_to_opreturnindex"),
Version::ZERO,
compressed,
)?,
pushonlyindex_to_pushonlyindex: ComputedVec::forced_import(
&path.join("pushonlyindex_to_pushonlyindex"),
Version::ZERO,
compressed,
)?,
unknownindex_to_unknownindex: ComputedVec::forced_import(
&path.join("unknownindex_to_unknownindex"),
Version::ZERO,
compressed,
)?,
addressindex_to_addressindex: ComputedVec::forced_import(
&path.join("addressindex_to_addressindex"),
Version::ZERO,
compressed,
)?,
txoutindex_to_txoutindex: ComputedVec::forced_import(
&path.join("txoutindex_to_txoutindex"),
Version::ZERO,
compressed,
)?,
}) })
} }
@@ -323,10 +421,10 @@ impl Vecs {
let txinindexes_count = indexer_vecs.txinindex_to_txoutindex.len(); let txinindexes_count = indexer_vecs.txinindex_to_txoutindex.len();
let txoutindexes_count = indexer_vecs.txoutindex_to_addressindex.len(); let txoutindexes_count = indexer_vecs.txoutindex_to_addressindex.len();
self.height_to_height.compute_transform( self.height_to_height.compute_range(
starting_indexes.height, starting_indexes.height,
indexer_vecs.height_to_timestamp.mut_vec(), indexer_vecs.height_to_timestamp.mut_vec(),
|(h, ..)| (h, h), |h| (h, h),
exit, exit,
)?; )?;
@@ -340,12 +438,12 @@ impl Vecs {
self.height_to_fixed_timestamp.compute_transform( self.height_to_fixed_timestamp.compute_transform(
starting_indexes.height, starting_indexes.height,
indexer_vecs.height_to_timestamp.mut_vec(), indexer_vecs.height_to_timestamp.mut_vec(),
|(h, d, s, ..)| { |(h, timestamp, s, ..)| {
let d = h let timestamp = h
.decremented() .decremented()
.and_then(|h| s.unwrap_cached_get(h)) .and_then(|h| s.unwrap_cached_get(h))
.map_or(d, |prev_d| prev_d.max(d)); .map_or(timestamp, |prev_d| prev_d.max(timestamp));
(h, d) (h, timestamp)
}, },
exit, exit,
)?; )?;
@@ -397,17 +495,17 @@ impl Vecs {
exit, exit,
)?; )?;
self.dateindex_to_dateindex.compute_transform( self.dateindex_to_dateindex.compute_range(
starting_dateindex, starting_dateindex,
self.dateindex_to_first_height.mut_vec(), self.dateindex_to_first_height.mut_vec(),
|(di, ..)| (di, di), |di| (di, di),
exit, exit,
)?; )?;
self.dateindex_to_date.compute_transform( self.dateindex_to_date.compute_range(
starting_dateindex, starting_dateindex,
self.dateindex_to_dateindex.mut_vec(), self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Date::from(di)), |di| (di, Date::from(di)),
exit, exit,
)?; )?;
@@ -448,10 +546,10 @@ impl Vecs {
.unwrap_cached_get(starting_dateindex) .unwrap_cached_get(starting_dateindex)
.unwrap_or_default(); .unwrap_or_default();
self.dateindex_to_weekindex.compute_transform( self.dateindex_to_weekindex.compute_range(
starting_dateindex, starting_dateindex,
self.dateindex_to_dateindex.mut_vec(), self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Weekindex::from(di)), |di| (di, Weekindex::from(di)),
exit, exit,
)?; )?;
@@ -470,10 +568,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.weekindex_to_weekindex.compute_transform( self.weekindex_to_weekindex.compute_range(
starting_weekindex, starting_weekindex,
self.weekindex_to_first_dateindex.mut_vec(), self.weekindex_to_first_dateindex.mut_vec(),
|(wi, ..)| (wi, wi), |wi| (wi, wi),
exit, exit,
)?; )?;
@@ -491,10 +589,10 @@ impl Vecs {
.unwrap_cached_get(starting_dateindex) .unwrap_cached_get(starting_dateindex)
.unwrap_or_default(); .unwrap_or_default();
self.dateindex_to_monthindex.compute_transform( self.dateindex_to_monthindex.compute_range(
starting_dateindex, starting_dateindex,
self.dateindex_to_dateindex.mut_vec(), self.dateindex_to_dateindex.mut_vec(),
|(di, ..)| (di, Monthindex::from(di)), |di| (di, Monthindex::from(di)),
exit, exit,
)?; )?;
@@ -515,10 +613,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.monthindex_to_monthindex.compute_transform( self.monthindex_to_monthindex.compute_range(
starting_monthindex, starting_monthindex,
self.monthindex_to_first_dateindex.mut_vec(), self.monthindex_to_first_dateindex.mut_vec(),
|(mi, ..)| (mi, mi), |mi| (mi, mi),
exit, exit,
)?; )?;
@@ -536,10 +634,10 @@ impl Vecs {
.unwrap_cached_get(starting_monthindex) .unwrap_cached_get(starting_monthindex)
.unwrap_or_default(); .unwrap_or_default();
self.monthindex_to_quarterindex.compute_transform( self.monthindex_to_quarterindex.compute_range(
starting_monthindex, starting_monthindex,
self.monthindex_to_monthindex.mut_vec(), self.monthindex_to_monthindex.mut_vec(),
|(mi, ..)| (mi, Quarterindex::from(mi)), |mi| (mi, Quarterindex::from(mi)),
exit, exit,
)?; )?;
@@ -560,10 +658,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.quarterindex_to_quarterindex.compute_transform( self.quarterindex_to_quarterindex.compute_range(
starting_quarterindex, starting_quarterindex,
self.quarterindex_to_first_monthindex.mut_vec(), self.quarterindex_to_first_monthindex.mut_vec(),
|(yi, ..)| (yi, yi), |i| (i, i),
exit, exit,
)?; )?;
@@ -581,10 +679,10 @@ impl Vecs {
.unwrap_cached_get(starting_monthindex) .unwrap_cached_get(starting_monthindex)
.unwrap_or_default(); .unwrap_or_default();
self.monthindex_to_yearindex.compute_transform( self.monthindex_to_yearindex.compute_range(
starting_monthindex, starting_monthindex,
self.monthindex_to_monthindex.mut_vec(), self.monthindex_to_monthindex.mut_vec(),
|(mi, ..)| (mi, Yearindex::from(mi)), |i| (i, Yearindex::from(i)),
exit, exit,
)?; )?;
@@ -605,10 +703,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.yearindex_to_yearindex.compute_transform( self.yearindex_to_yearindex.compute_range(
starting_yearindex, starting_yearindex,
self.yearindex_to_first_monthindex.mut_vec(), self.yearindex_to_first_monthindex.mut_vec(),
|(yi, ..)| (yi, yi), |i| (i, i),
exit, exit,
)?; )?;
@@ -626,10 +724,10 @@ impl Vecs {
.unwrap_cached_get(starting_yearindex) .unwrap_cached_get(starting_yearindex)
.unwrap_or_default(); .unwrap_or_default();
self.yearindex_to_decadeindex.compute_transform( self.yearindex_to_decadeindex.compute_range(
starting_yearindex, starting_yearindex,
self.yearindex_to_yearindex.mut_vec(), self.yearindex_to_yearindex.mut_vec(),
|(yi, ..)| (yi, Decadeindex::from(yi)), |i| (i, Decadeindex::from(i)),
exit, exit,
)?; )?;
@@ -648,10 +746,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.decadeindex_to_decadeindex.compute_transform( self.decadeindex_to_decadeindex.compute_range(
starting_decadeindex, starting_decadeindex,
self.decadeindex_to_first_yearindex.mut_vec(), self.decadeindex_to_first_yearindex.mut_vec(),
|(di, ..)| (di, di), |i| (i, i),
exit, exit,
)?; )?;
@@ -669,10 +767,10 @@ impl Vecs {
.unwrap_cached_get(decremented_starting_height) .unwrap_cached_get(decremented_starting_height)
.unwrap_or_default(); .unwrap_or_default();
self.height_to_difficultyepoch.compute_transform( self.height_to_difficultyepoch.compute_range(
starting_indexes.height, starting_indexes.height,
self.height_to_height.mut_vec(), self.height_to_height.mut_vec(),
|(h, ..)| (h, Difficultyepoch::from(h)), |h| (h, Difficultyepoch::from(h)),
exit, exit,
)?; )?;
@@ -691,10 +789,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.difficultyepoch_to_difficultyepoch.compute_transform( self.difficultyepoch_to_difficultyepoch.compute_range(
starting_difficultyepoch, starting_difficultyepoch,
self.difficultyepoch_to_first_height.mut_vec(), self.difficultyepoch_to_first_height.mut_vec(),
|(de, ..)| (de, de), |i| (i, i),
exit, exit,
)?; )?;
@@ -717,10 +815,10 @@ impl Vecs {
.unwrap_cached_get(decremented_starting_height) .unwrap_cached_get(decremented_starting_height)
.unwrap_or_default(); .unwrap_or_default();
self.height_to_halvingepoch.compute_transform( self.height_to_halvingepoch.compute_range(
starting_indexes.height, starting_indexes.height,
self.height_to_height.mut_vec(), self.height_to_height.mut_vec(),
|(h, ..)| (h, Halvingepoch::from(h)), |h| (h, Halvingepoch::from(h)),
exit, exit,
)?; )?;
@@ -739,10 +837,10 @@ impl Vecs {
exit, exit,
)?; )?;
self.halvingepoch_to_halvingepoch.compute_transform( self.halvingepoch_to_halvingepoch.compute_range(
starting_halvingepoch, starting_halvingepoch,
self.halvingepoch_to_first_height.mut_vec(), self.halvingepoch_to_first_height.mut_vec(),
|(he, ..)| (he, he), |i| (i, i),
exit, exit,
)?; )?;
@@ -758,6 +856,105 @@ impl Vecs {
// exit, // exit,
// )?; // )?;
// ---
self.addressindex_to_addressindex.compute_range(
starting_indexes.addressindex,
indexer_vecs.addressindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txoutindex_to_txoutindex.compute_range(
starting_indexes.txoutindex,
indexer_vecs.txoutindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pk33index_to_p2pk33index.compute_range(
starting_indexes.p2pk33index,
indexer_vecs.p2pk33index_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pk65index_to_p2pk65index.compute_range(
starting_indexes.p2pk65index,
indexer_vecs.p2pk65index_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2pkhindex_to_p2pkhindex.compute_range(
starting_indexes.p2pkhindex,
indexer_vecs.p2pkhindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2shindex_to_p2shindex.compute_range(
starting_indexes.p2shindex,
indexer_vecs.p2shindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2trindex_to_p2trindex.compute_range(
starting_indexes.p2trindex,
indexer_vecs.p2trindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2wpkhindex_to_p2wpkhindex.compute_range(
starting_indexes.p2wpkhindex,
indexer_vecs.p2wpkhindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.p2wshindex_to_p2wshindex.compute_range(
starting_indexes.p2wshindex,
indexer_vecs.p2wshindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txindex_to_txindex.compute_range(
starting_indexes.txindex,
indexer_vecs.txindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.txinindex_to_txinindex.compute_range(
starting_indexes.txinindex,
indexer_vecs.txinindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.emptyindex_to_emptyindex.compute_range(
starting_indexes.emptyindex,
indexer_vecs.emptyindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.multisigindex_to_multisigindex.compute_range(
starting_indexes.multisigindex,
indexer_vecs.multisigindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.opreturnindex_to_opreturnindex.compute_range(
starting_indexes.opreturnindex,
indexer_vecs.opreturnindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.pushonlyindex_to_pushonlyindex.compute_range(
starting_indexes.pushonlyindex,
indexer_vecs.pushonlyindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
self.unknownindex_to_unknownindex.compute_range(
starting_indexes.unknownindex,
indexer_vecs.unknownindex_to_height.mut_vec(),
|i| (i, i),
exit,
)?;
Ok(Indexes { Ok(Indexes {
indexes: starting_indexes, indexes: starting_indexes,
dateindex: starting_dateindex, dateindex: starting_dateindex,
@@ -821,6 +1018,22 @@ impl Vecs {
self.quarterindex_to_last_monthindex.any_vec(), self.quarterindex_to_last_monthindex.any_vec(),
self.quarterindex_to_quarterindex.any_vec(), self.quarterindex_to_quarterindex.any_vec(),
self.quarterindex_to_timestamp.any_vec(), self.quarterindex_to_timestamp.any_vec(),
self.p2pk33index_to_p2pk33index.any_vec(),
self.p2pk65index_to_p2pk65index.any_vec(),
self.p2pkhindex_to_p2pkhindex.any_vec(),
self.p2shindex_to_p2shindex.any_vec(),
self.p2trindex_to_p2trindex.any_vec(),
self.p2wpkhindex_to_p2wpkhindex.any_vec(),
self.p2wshindex_to_p2wshindex.any_vec(),
self.txindex_to_txindex.any_vec(),
self.txinindex_to_txinindex.any_vec(),
self.emptyindex_to_emptyindex.any_vec(),
self.multisigindex_to_multisigindex.any_vec(),
self.opreturnindex_to_opreturnindex.any_vec(),
self.pushonlyindex_to_pushonlyindex.any_vec(),
self.unknownindex_to_unknownindex.any_vec(),
self.addressindex_to_addressindex.any_vec(),
self.txoutindex_to_txoutindex.any_vec(),
] ]
} }
} }
+1 -1
View File
@@ -21,7 +21,7 @@ color-eyre = { workspace = true }
jiff = { workspace = true } jiff = { workspace = true }
log = { workspace = true } log = { workspace = true }
minreq = { workspace = true } minreq = { workspace = true }
oxc = { version = "0.64.0", features = ["codegen", "minifier"] } oxc = { version = "0.65.0", features = ["codegen", "minifier"] }
serde = { workspace = true } serde = { workspace = true }
tokio = { version = "1.44.2", features = ["full"] } tokio = { version = "1.44.2", features = ["full"] }
tower-http = { version = "0.6.2", features = ["compression-full", "trace"] } tower-http = { version = "0.6.2", features = ["compression-full", "trace"] }
+7 -8
View File
@@ -37,9 +37,8 @@
line-height: 1.5; line-height: 1.5;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
tab-size: 4; tab-size: 4;
font-family: "Geist mono", ui-sans-serif, system-ui, sans-serif, font-family: "Geist mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", Consolas, "Liberation Mono", "Courier New", monospace;
"Noto Color Emoji";
font-feature-settings: "ss03"; font-feature-settings: "ss03";
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
@@ -134,6 +133,7 @@
letter-spacing: inherit; letter-spacing: inherit;
color: inherit; color: inherit;
background: transparent; background: transparent;
text-transform: inherit;
} }
button, button,
@@ -590,7 +590,7 @@
> fieldset { > fieldset {
width: 100%; width: 100%;
display: flex; display: flex;
gap: 1rem; gap: 1.25rem;
> label { > label {
pointer-events: auto; pointer-events: auto;
@@ -626,6 +626,7 @@
} }
select { select {
cursor: pointer;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
@@ -884,7 +885,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
z-index: 10; z-index: 20;
pointer-events: none; pointer-events: none;
} }
.shadow-left { .shadow-left {
@@ -979,7 +980,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 0; min-height: 0;
z-index: 20;
flex: 1; flex: 1;
margin-top: 2rem; margin-top: 2rem;
margin-bottom: 1rem; margin-bottom: 1rem;
@@ -1607,9 +1607,8 @@
</main> </main>
<aside id="aside"> <aside id="aside">
<div id="charts" hidden></div> <div id="charts" hidden></div>
<div id="table" hidden></div>
<div id="simulation" hidden></div> <div id="simulation" hidden></div>
<div id="live-price" hidden></div>
<div id="moscow-time" hidden></div>
</aside> </aside>
<div id="share-div" hidden> <div id="share-div" hidden>
<div id="share-content-div"> <div id="share-content-div">
@@ -32,6 +32,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Colors} args.colors * @param {Colors} args.colors
* @param {Index} args.index * @param {Index} args.index
* @param {Utilities} args.utils * @param {Utilities} args.utils
* @param {Elements} args.elements
* @param {DeepPartial<ChartOptions>} [args.options] * @param {DeepPartial<ChartOptions>} [args.options]
*/ */
function createLightweightChart({ function createLightweightChart({
@@ -40,13 +41,15 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
colors, colors,
index, index,
utils, utils,
elements,
options: _options = {}, options: _options = {},
}) { }) {
console.log(elements.style.fontFamily);
/** @satisfies {DeepPartial<ChartOptions>} */ /** @satisfies {DeepPartial<ChartOptions>} */
const options = { const options = {
autoSize: true, autoSize: true,
layout: { layout: {
fontFamily: "Geist mono", fontFamily: elements.style.fontFamily,
// fontSize: 13, // fontSize: 13,
background: { color: "transparent" }, background: { color: "transparent" },
attributionLogo: false, attributionLogo: false,
@@ -134,6 +137,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {Signals} args.signals * @param {Signals} args.signals
* @param {Colors} args.colors * @param {Colors} args.colors
* @param {Utilities} args.utils * @param {Utilities} args.utils
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources * @param {VecsResources} args.vecsResources
* @param {Owner | null} [args.owner] * @param {Owner | null} [args.owner]
* @param {true} [args.fitContentOnResize] * @param {true} [args.fitContentOnResize]
@@ -144,6 +148,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals, signals,
colors, colors,
utils, utils,
elements,
id, id,
vecsResources, vecsResources,
owner: _owner, owner: _owner,
@@ -190,9 +195,13 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {VecResource} valuesResource * @param {VecResource} valuesResource
*/ */
function createSetFetchedDataEffect(series, valuesResource) { function createSetFetchedDataEffect(series, valuesResource) {
const fetchedKey = vecsResources.defaultFetchedKey;
signals.runWithOwner(owner, () => signals.runWithOwner(owner, () =>
signals.createEffect( signals.createEffect(
() => [timeResource?.fetched(), valuesResource.fetched()], () => [
timeResource?.fetched[fetchedKey](),
valuesResource.fetched[fetchedKey](),
],
([indexes, _ohlcs]) => { ([indexes, _ohlcs]) => {
if (!ichart) throw Error("IChart should be initialized"); if (!ichart) throw Error("IChart should be initialized");
@@ -281,6 +290,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
signals, signals,
colors, colors,
utils, utils,
elements,
}); });
if (fitContentOnResize) { if (fitContentOnResize) {
@@ -21,11 +21,11 @@ const importSignals = import("./v0.2.4-treeshaked/script.js").then(
(compute, effect) => { (compute, effect) => {
let dispose = /** @type {VoidFunction | null} */ (null); let dispose = /** @type {VoidFunction | null} */ (null);
// @ts-ignore // @ts-ignore
_signals.createEffect(compute, (v) => { _signals.createEffect(compute, (v, oldV) => {
dispose?.(); dispose?.();
signals.createRoot((_dispose) => { signals.createRoot((_dispose) => {
dispose = _dispose; dispose = _dispose;
effect(v); return effect(v, oldV);
}); });
signals.onCleanup(() => dispose?.()); signals.onCleanup(() => dispose?.());
}); });
+1
View File
@@ -38,6 +38,7 @@ export function init({
id: "charts", id: "charts",
utils, utils,
vecsResources, vecsResources,
elements,
}); });
const index = createIndexSelector({ elements, signals, utils }); const index = createIndexSelector({ elements, signals, utils });
+244 -198
View File
@@ -1,7 +1,7 @@
// @ts-check // @ts-check
/** /**
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, Unit, AnySeriesBlueprint, ChartableIndex } from "./options" * @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex } from "./options"
* @import {Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple} from "../packages/lightweight-charts/wrapper" * @import {Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple} from "../packages/lightweight-charts/wrapper"
* @import * as _ from "../packages/ufuzzy/v1.0.14/types" * @import * as _ from "../packages/ufuzzy/v1.0.14/types"
* @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, SeriesType, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.5-treeshaked/types" * @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, SeriesType, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.5-treeshaked/types"
@@ -9,7 +9,38 @@
* @import {Signal, Signals} from "../packages/solid-signals/types"; * @import {Signal, Signals} from "../packages/solid-signals/types";
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/v0.2.4-treeshaked/types/core/owner" * @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/v0.2.4-treeshaked/types/core/owner"
* @import { createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo } from "../packages/solid-signals/v0.2.4-treeshaked/types/signals"; * @import { createEffect as CreateEffect, Accessor, Setter, createMemo as CreateMemo } from "../packages/solid-signals/v0.2.4-treeshaked/types/signals";
* @import {Addressindex, Dateindex, Decadeindex, Difficultyepoch, Index, Halvingepoch, Height, Monthindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex, P2TRindex, P2WPKHindex, P2WSHindex, Txindex, Txinindex, Txoutindex, VecId, Weekindex, Yearindex, VecIdToIndexes, Quarterindex} from "./vecid-to-indexes" * @import {Addressindex, Dateindex, Decadeindex, Difficultyepoch, Index, Halvingepoch, Height, Monthindex, P2PK33index, P2PK65index, P2PKHindex, P2SHindex, P2TRindex, P2WPKHindex, P2WSHindex, Txindex, Txinindex, Txoutindex, VecId, Weekindex, Yearindex, VecIdToIndexes, Quarterindex, Emptyindex, Multisigindex, Opreturnindex, Pushonlyindex, Unknownindex} from "./vecid-to-indexes"
*/
/**
* @typedef {"" |
* "BTC" |
* "Cents" |
* "Coinblocks" |
* "Count" |
* "Date" |
* "Difficulty" |
* "ExaHash / Second" |
* "Gigabytes" |
* "Hash" |
* "Index" |
* "mb" |
* "%" |
* "Ratio" |
* "Sats" |
* "Seconds" |
* "Timestamp" |
* "tx" |
* "Type" |
* "USD / (PetaHash / Second)" |
* "USD" |
* "Version" |
* "WU" |
* "Bool" |
* "Locktime" |
* "sat/vB" |
* "vB"
* } Unit
*/ */
function initPackages() { function initPackages() {
@@ -113,22 +144,6 @@ function createUtils() {
if (!element) throw `Element with id = "${id}" should exist`; if (!element) throw `Element with id = "${id}" should exist`;
return element; return element;
}, },
/**
* @param {string} name
* @param {Elements} elements
*/
queryOrCreateMetaElement(name, elements) {
let meta = /** @type {HTMLMetaElement | null} */ (
window.document.querySelector(`meta[name="${name}"]`)
);
if (!meta) {
meta = window.document.createElement("meta");
meta.name = name;
elements.head.appendChild(meta);
}
return meta;
},
/** /**
* @param {HTMLElement} element * @param {HTMLElement} element
*/ */
@@ -206,14 +221,14 @@ function createUtils() {
}, },
/** /**
* @param {Object} arg * @param {Object} arg
* @param {string} arg.text * @param {string | HTMLElement} arg.inside
* @param {string} arg.title * @param {string} arg.title
* @param {VoidFunction} arg.onClick * @param {(event: MouseEvent) => void} arg.onClick
*/ */
createButtonElement({ text, onClick, title }) { createButtonElement({ inside: text, onClick, title }) {
const button = window.document.createElement("button"); const button = window.document.createElement("button");
button.innerHTML = text; button.append(text);
button.title = title; button.title = title;
@@ -404,128 +419,6 @@ function createUtils() {
return { field, selected }; return { field, selected };
}, },
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {string} args.placeholder
* @param {Signal<number | null>} args.signal
* @param {number} args.min
* @param {number} args.step
* @param {number} [args.max]
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputNumberElement({
id,
title,
signal,
min,
max,
step,
placeholder,
signals,
}) {
const input = window.document.createElement("input");
if (!id || !title || !placeholder) throw Error("input attribute missing");
input.id = id;
input.title = title;
input.placeholder = placeholder;
input.type = "number";
input.min = String(min);
if (max) {
input.max = String(max);
}
input.step = String(step);
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const value = signal();
return value ? String(value) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("input", () => {
const valueSer = input.value;
stateValue = valueSer;
const value = Number(valueSer);
if (value >= min && (max ? value <= max : true)) {
signal.set(value);
}
});
return { input, signal };
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDollar({ id, title, signal, signals }) {
return this.createInputNumberElement({
id,
placeholder: "USD",
min: 0,
title,
signal,
signals,
step: 1,
});
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
const min = "2011-01-01";
const minDate = new Date(min);
const maxDate = new Date();
const max = date.toString(maxDate);
input.min = min;
input.max = max;
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const dateSignal = signal();
return dateSignal ? date.toString(dateSignal) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("change", () => {
const value = input.value;
const date = new Date(value);
if (date >= minDate && date <= maxDate) {
stateValue = value;
signal.set(value ? date : null);
}
});
return { input, signal };
},
/** /**
* @param {Object} args * @param {Object} args
* @param {1 | 2 | 3} [args.level] * @param {1 | 2 | 3} [args.level]
@@ -545,46 +438,64 @@ function createUtils() {
}; };
}, },
/** /**
* @param {Object} param0 * @template {string} Name
* @param {string} param0.name * @template {string} Value
* @param {string} param0.value * @template {Value | {name: Name; value: Value}} T
* @param {T} arg
*/ */
createOption({ name, value }) { createOption(arg) {
const option = window.document.createElement("option"); const option = window.document.createElement("option");
option.value = value; if (typeof arg === "object") {
option.innerText = name; option.value = arg.value;
option.innerText = arg.name;
} else {
option.value = arg;
option.innerText = arg;
}
return option; return option;
}, },
/** /**
* @template {{name: string; value: string}} T * @template {string} Name
* @param {Object} param0 * @template {string} Value
* @param {string} param0.id * @template {Value | {name: Name; value: Value}} T
* @param {(({name: string; value: string} & T) | {name: string; list: ({name: string; value: string} & T)[]})[]} param0.list * @param {Object} args
* @param {Signal<T>} param0.signal * @param {string} [args.id]
* @param {boolean} [args.deep]
* @param {readonly ((T) | {name: string; list: T[]})[]} args.list
* @param {Signal<T>} args.signal
*/ */
createSelect({ id, list, signal }) { createSelect({ id, list, signal, deep = false }) {
const select = window.document.createElement("select"); const select = window.document.createElement("select");
select.name = id;
select.id = id; if (id) {
select.name = id;
select.id = id;
}
/** @type {Record<string, VoidFunction>} */ /** @type {Record<string, VoidFunction>} */
const setters = {}; const setters = {};
list.forEach((anyOption, index) => { list.forEach((anyOption, index) => {
if ("list" in anyOption) { if (typeof anyOption === "object" && "list" in anyOption) {
const { name, list } = anyOption; const { name, list } = anyOption;
const optGroup = window.document.createElement("optgroup"); const optGroup = window.document.createElement("optgroup");
optGroup.label = name; optGroup.label = name;
select.append(optGroup); select.append(optGroup);
list.forEach((option) => { list.forEach((option) => {
optGroup.append(this.createOption(option)); optGroup.append(this.createOption(option));
setters[option.value] = () => signal.set(() => option); const key = /** @type {string} */ (
typeof option === "object" ? option.value : option
);
setters[key] = () => signal.set(() => option);
}); });
} else { } else {
select.append(this.createOption(anyOption)); select.append(this.createOption(anyOption));
setters[anyOption.value] = () => signal.set(() => anyOption); const key = /** @type {string} */ (
typeof anyOption === "object" ? anyOption.value : anyOption
);
setters[key] = () => signal.set(() => anyOption);
} }
if (index !== list.length - 1) { if (deep && index !== list.length - 1) {
select.append(window.document.createElement("hr")); select.append(window.document.createElement("hr"));
} }
}); });
@@ -597,34 +508,13 @@ function createUtils() {
} }
}); });
select.value = signal().value; const initialSignal = signal();
const initialValue =
typeof initialSignal === "object" ? initialSignal.value : initialSignal;
select.value = String(initialValue);
return { select, signal }; return { select, signal };
}, },
/**
* @param {Object} param0
* @param {Signal<any>} param0.signal
* @param {HTMLInputElement} [param0.input]
* @param {HTMLSelectElement} [param0.select]
*/
createResetableInput({ input, select, signal }) {
const div = window.document.createElement("div");
const element = input || select;
if (!element) throw "createResetableField element missing";
div.append(element);
const button = this.createButtonElement({
onClick: signal.reset,
text: "Reset",
title: "Reset field",
});
button.type = "reset";
div.append(button);
return div;
},
/** /**
* @param {Object} args * @param {Object} args
* @param {string} args.title * @param {string} args.title
@@ -786,6 +676,79 @@ function createUtils() {
}); });
} }
/**
* @param {VecId} id
*/
function vecidToUnit(id) {
/** @type {Unit} */
let unit;
if (id.includes("index") || id.includes("height") || id.includes("epoch")) {
unit = "Index";
} else if (id.includes("type")) {
unit = "Type";
} else if (id === "locktime") {
unit = "Locktime";
} else if (id.startsWith("is-")) {
unit = "Bool";
} else if (
id.includes("hash") ||
id.includes("address") ||
id.includes("txid")
) {
unit = "Hash";
} else if (id.includes("interval")) {
unit = "Seconds";
} else if (id.includes("feerate")) {
unit = "sat/vB";
} else if (id.includes("in-cents")) {
unit = "Cents";
} else if (id.includes("in-usd")) {
unit = "USD";
} else if (id.includes("in-btc")) {
unit = "BTC";
} else if (
id.includes("in-sats") ||
id.startsWith("sats-") ||
id.includes("input-value") ||
id.includes("output-value") ||
id.includes("fee") ||
id.includes("coinbase") ||
id.includes("subsidy")
) {
unit = "Sats";
} else if (
id.includes("open") ||
id.includes("high") ||
id.includes("low") ||
id.includes("close") ||
id.includes("ohlc")
) {
unit = "USD";
} else if (id.includes("count") || id.match(/v[1-3]/g)) {
unit = "Count";
} else if (id.includes("date")) {
unit = "Date";
} else if (id.includes("timestamp")) {
unit = "Timestamp";
} else if (id.includes("difficulty")) {
unit = "Difficulty";
} else if (id.includes("-size")) {
unit = "mb";
} else if (id.includes("weight")) {
unit = "WU";
} else if (id.includes("vbytes") || id.includes("vsize")) {
unit = "vB";
} else if (id.includes("version")) {
unit = "Version";
} else if (id === "value") {
unit = "Sats";
} else {
console.log();
throw Error(`Unit not set for "${id}"`);
}
return unit;
}
const locale = { const locale = {
numberToUSFormat, numberToUSFormat,
/** @param {number} value */ /** @param {number} value */
@@ -894,6 +857,20 @@ function createUtils() {
return v; return v;
}, },
}, },
vecIds: {
/**
* @param {VecId[]} v
*/
serialize(v) {
return v.join(",");
},
/**
* @param {string} v
*/
deserialize(v) {
return /** @type {VecId[]} */ (v.split(","));
},
},
number: { number: {
/** /**
* @param {number} v * @param {number} v
@@ -1298,6 +1275,7 @@ function createUtils() {
runWhenIdle, runWhenIdle,
getNumberOfDaysBetweenTwoDates, getNumberOfDaysBetweenTwoDates,
stringToId, stringToId,
vecidToUnit,
}; };
} }
/** @typedef {ReturnType<typeof createUtils>} Utilities */ /** @typedef {ReturnType<typeof createUtils>} Utilities */
@@ -1309,6 +1287,24 @@ function createUtils() {
function createVecsResources(signals, utils) { function createVecsResources(signals, utils) {
const owner = signals.getOwner(); const owner = signals.getOwner();
const defaultFrom = -10_000;
const defaultTo = undefined;
/**
* Defaults
* - from: -10_000
* - to: undefined
*
* @param {Object} [args]
* @param {number} [args.from]
* @param {number} [args.to]
*/
function genFetchedKey(args) {
return `${args?.from}-${args?.to}`;
}
const defaultFetchedKey = genFetchedKey({ from: defaultFrom, to: defaultTo });
/** /**
* @template {number | OHLCTuple} [T=number] * @template {number | OHLCTuple} [T=number]
* @param {Index} index * @param {Index} index
@@ -1318,16 +1314,29 @@ function createVecsResources(signals, utils) {
return signals.runWithOwner(owner, () => { return signals.runWithOwner(owner, () => {
/** @typedef {T extends number ? SingleValueData : CandlestickData} Value */ /** @typedef {T extends number ? SingleValueData : CandlestickData} Value */
const fetched = signals.createSignal(/** @type {T[] | null} */ (null));
let loading = false; let loading = false;
let at = /** @type {Date | null} */ (null); let at = /** @type {Date | null} */ (null);
const from = -10_000;
return { return {
url: utils.api.genUrl(index, id, from), url: utils.api.genUrl(index, id, defaultFrom),
fetched, fetched: /** @type {Record<string, Signal<T[] | null>>} */ ({}),
async fetch() { /**
* Defaults
* - from: -10_000
* - to: undefined
*
* @param {Object} [args]
* @param {number} [args.from]
* @param {number} [args.to]
*/
async fetch(args) {
const from = args?.from ?? defaultFrom;
const to = args?.to ?? defaultTo;
const fetchedKey = genFetchedKey({ from, to });
this.fetched[fetchedKey] ??= signals.createSignal(
/** @type {T[] | null} */ (null),
);
const fetched = this.fetched[fetchedKey];
if (loading) return fetched(); if (loading) return fetched();
if (at) { if (at) {
const diff = new Date().getTime() - at.getTime(); const diff = new Date().getTime() - at.getTime();
@@ -1343,6 +1352,7 @@ function createVecsResources(signals, utils) {
index, index,
id, id,
from, from,
to,
) )
); );
at = new Date(); at = new Date();
@@ -1374,6 +1384,8 @@ function createVecsResources(signals, utils) {
map.set(key, /** @type {any} */ (vec)); map.set(key, /** @type {any} */ (vec));
return vec; return vec;
}, },
genFetchedKey,
defaultFetchedKey,
}; };
return vecs; return vecs;
@@ -1435,6 +1447,7 @@ function getElements() {
selectors: getElementById("frame-selectors"), selectors: getElementById("frame-selectors"),
style: getComputedStyle(window.document.documentElement), style: getComputedStyle(window.document.documentElement),
charts: getElementById("charts"), charts: getElementById("charts"),
table: getElementById("table"),
simulation: getElementById("simulation"), simulation: getElementById("simulation"),
}; };
} }
@@ -1920,6 +1933,12 @@ function main() {
optionsPromise.then(async ({ initOptions }) => { optionsPromise.then(async ({ initOptions }) => {
const vecIdToIndexes = createVecIdToIndexes(); const vecIdToIndexes = createVecIdToIndexes();
if (env.localhost) {
Object.keys(vecIdToIndexes).forEach((id) => {
utils.vecidToUnit(/** @type {VecId} */ (id));
});
}
function initDark() { function initDark() {
const preferredColorSchemeMatchMedia = window.matchMedia( const preferredColorSchemeMatchMedia = window.matchMedia(
"(prefers-color-scheme: dark)", "(prefers-color-scheme: dark)",
@@ -2003,7 +2022,8 @@ function main() {
undefined undefined
); );
let firstTimeLoadingChart = true; let firstTimeLoadingChart = true;
let firstTimeLoadingSim = true; let firstTimeLoadingTable = true;
let firstTimeLoadingSimulation = true;
signals.createEffect(options.selected, (option) => { signals.createEffect(options.selected, (option) => {
if (previousElement) { if (previousElement) {
@@ -2036,7 +2056,9 @@ function main() {
colors, colors,
elements, elements,
lightweightCharts, lightweightCharts,
selected: /** @type {any} */ (lastChartOption), selected: /** @type {Accessor<ChartOption>} */ (
lastChartOption
),
signals, signals,
utils, utils,
webSockets, webSockets,
@@ -2052,15 +2074,39 @@ function main() {
break; break;
} }
case "table": {
element = elements.table;
if (firstTimeLoadingTable) {
const tableScript = import("./table.js");
utils.dom.importStyleAndThen("/styles/table.css", () =>
tableScript.then(({ init }) =>
signals.runWithOwner(owner, () =>
init({
colors,
elements,
signals,
utils,
vecsResources,
option,
vecIdToIndexes,
}),
),
),
);
}
firstTimeLoadingTable = false;
break;
}
case "simulation": { case "simulation": {
element = elements.simulation; element = elements.simulation;
lastSimulationOption.set(option); lastSimulationOption.set(option);
if (firstTimeLoadingSim) { if (firstTimeLoadingSimulation) {
const lightweightCharts = packages.lightweightCharts(); const lightweightCharts = packages.lightweightCharts();
const simulationScript = import("./simulation.js"); const simulationScript = import("./simulation.js");
utils.dom.importStyleAndThen( utils.dom.importStyleAndThen(
"/styles/simulation.css", "/styles/simulation.css",
() => () =>
@@ -2080,7 +2126,7 @@ function main() {
), ),
); );
} }
firstTimeLoadingSim = false; firstTimeLoadingSimulation = false;
break; break;
} }
+67 -107
View File
@@ -12,28 +12,6 @@
*/ */
/** /**
* @typedef {"" |
* "BTC" |
* "Coinblocks" |
* "Count" |
* "Date" |
* "USD / (PetaHash / Second)" |
* "ExaHash / Second" |
* "Height" |
* "Gigabytes" |
* "Megabytes" |
* "Percentage" |
* "Ratio" |
* "Sats" |
* "sat/vB" |
* "Seconds" |
* "Transactions" |
* "USD" |
* "Version" |
* "vB" |
* "WU"
* } Unit
*
* @typedef {Object} BaseSeriesBlueprint * @typedef {Object} BaseSeriesBlueprint
* @property {string} title * @property {string} title
* @property {boolean} [defaultActive] * @property {boolean} [defaultActive]
@@ -85,6 +63,14 @@
* *
* @typedef {Required<Omit<PartialChartOption, "top" | "bottom">> & ProcessedChartOptionAddons & ProcessedOptionAddons} ChartOption * @typedef {Required<Omit<PartialChartOption, "top" | "bottom">> & ProcessedChartOptionAddons & ProcessedOptionAddons} ChartOption
* *
* @typedef {Object} PartialTableOptionSpecific
* @property {"table"} kind
* @property {string} title
*
* @typedef {PartialOption & PartialTableOptionSpecific} PartialTableOption
*
* @typedef {Required<PartialTableOption> & ProcessedOptionAddons} TableOption
*
* @typedef {Object} PartialSimulationOptionSpecific * @typedef {Object} PartialSimulationOptionSpecific
* @property {"simulation"} kind * @property {"simulation"} kind
* @property {string} title * @property {string} title
@@ -102,9 +88,9 @@
* *
* @typedef {Required<PartialUrlOption> & ProcessedOptionAddons} UrlOption * @typedef {Required<PartialUrlOption> & ProcessedOptionAddons} UrlOption
* *
* @typedef {PartialChartOption | PartialSimulationOption | PartialUrlOption} AnyPartialOption * @typedef {PartialChartOption | PartialTableOption | PartialSimulationOption | PartialUrlOption} AnyPartialOption
* *
* @typedef {ChartOption | SimulationOption | UrlOption} Option * @typedef {ChartOption | TableOption | SimulationOption | UrlOption} Option
* *
* @typedef {Object} PartialOptionsGroup * @typedef {Object} PartialOptionsGroup
* @property {string} name * @property {string} name
@@ -201,17 +187,18 @@ function createPartialOptions(colors) {
/** /**
* @param {Object} args * @param {Object} args
* @param {VecIdSumBase & TotalVecIdBase} args.concat * @param {VecIdSumBase & TotalVecIdBase} args.concat
* @param {string} [args.name]
*/ */
function createSumTotalSeries({ concat }) { function createSumTotalSeries({ concat, name }) {
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([ return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
{ {
key: `${concat}-sum`, key: `${concat}-sum`,
title: "Sum", title: name ? `${name} Sum` : "Sum",
color: colors.bitcoin, color: colors.bitcoin,
}, },
{ {
key: `total-${concat}`, key: `total-${concat}`,
title: "Total", title: name ? `Total ${name}` : "Total",
color: colors.offBitcoin, color: colors.offBitcoin,
defaultActive: false, defaultActive: false,
}, },
@@ -453,41 +440,42 @@ function createPartialOptions(colors) {
], ],
}, },
{ {
name: "Version", name: "Versions",
tree: [ title: "Transaction Versions",
{ bottom: [
name: "1", // {
title: "Transaction V1 Count", // name: "1",
bottom: [ // title: "Transaction V1 Count",
createBaseSeries({ // bottom: [
key: "tx-v1", createBaseSeries({
name: "Count", key: "tx-v1",
}), name: "v1 Count",
...createSumTotalSeries({ concat: "tx-v1" }), }),
], ...createSumTotalSeries({ concat: "tx-v1", name: "v1" }),
}, // ],
{ // },
name: "2", // {
title: "Transaction V2 Count", // name: "2",
bottom: [ // title: "Transaction V2 Count",
createBaseSeries({ // bottom: [
key: "tx-v2", createBaseSeries({
name: "Count", key: "tx-v2",
}), name: "v2 Count",
...createSumTotalSeries({ concat: "tx-v2" }), }),
], ...createSumTotalSeries({ concat: "tx-v2", name: "v2" }),
}, // ],
{ // },
name: "3", // {
title: "Transaction V3 Count", // name: "3",
bottom: [ // title: "Transaction V3 Count",
createBaseSeries({ // bottom: [
key: "tx-v3", createBaseSeries({
name: "Count", key: "tx-v3",
}), name: "v3 Count",
...createSumTotalSeries({ concat: "tx-v3" }), }),
], ...createSumTotalSeries({ concat: "tx-v3", name: "v3" }),
}, // ],
// },
], ],
}, },
], ],
@@ -542,6 +530,11 @@ function createPartialOptions(colors) {
}, },
], ],
}, },
{
kind: "table",
title: "Table",
name: "Table",
},
{ {
name: "Simulations", name: "Simulations",
tree: [ tree: [
@@ -677,41 +670,7 @@ export function initOptions({
*/ */
function arrayToRecord(id, arr = []) { function arrayToRecord(id, arr = []) {
return (arr || []).reduce((record, blueprint) => { return (arr || []).reduce((record, blueprint) => {
const key = blueprint.key; const unit = utils.vecidToUnit(blueprint.key);
/** @type {Unit} */
let unit;
if (key.includes("interval")) {
unit = "Seconds";
} else if (key.includes("feerate")) {
unit = "sat/vB";
} else if (key.includes("in-usd")) {
unit = "USD";
} else if (key.includes("in-btc")) {
unit = "BTC";
} else if (
key.includes("-in-sats") ||
key.startsWith("sats-") ||
key.includes("input-value") ||
key.includes("output-value") ||
key.includes("fee") ||
key.includes("coinbase") ||
key.includes("subsidy")
) {
unit = "Sats";
} else if (key.includes("count")) {
unit = "Count";
} else if (key.includes("-size")) {
unit = "Megabytes";
} else if (key.includes("weight")) {
unit = "WU";
} else if (key.includes("vbytes") || key.includes("vsize")) {
unit = "vB";
} else if (key.match(/v[1-3]/g)) {
unit = "Version";
} else {
console.log([id, key]);
throw Error("Unit not set");
}
record[unit] ??= []; record[unit] ??= [];
record[unit].push(blueprint); record[unit].push(blueprint);
return record; return record;
@@ -733,7 +692,7 @@ export function initOptions({
if (option.qrcode) { if (option.qrcode) {
return utils.dom.createButtonElement({ return utils.dom.createButtonElement({
text: option.name, inside: option.name,
title: option.title, title: option.title,
onClick: () => { onClick: () => {
qrcode.set(option.url); qrcode.set(option.url);
@@ -918,16 +877,17 @@ export function initOptions({
/** @type {Option} */ /** @type {Option} */
let option; let option;
/** @type {string} */ if ("kind" in anyPartial && anyPartial.kind === "table") {
let id; option = /** @satisfies {TableOption} */ ({
/** @type {Option["kind"]} */ kind: anyPartial.kind,
let kind; id: anyPartial.kind,
/** @type {string} */ name: anyPartial.name,
let title; path: path || [],
title: anyPartial.title,
if ("kind" in anyPartial && anyPartial.kind === "simulation") { });
} else if ("kind" in anyPartial && anyPartial.kind === "simulation") {
option = /** @satisfies {SimulationOption} */ ({ option = /** @satisfies {SimulationOption} */ ({
kind: "simulation", kind: anyPartial.kind,
id: anyPartial.kind, id: anyPartial.kind,
name: anyPartial.name, name: anyPartial.name,
path: path || [], path: path || [],
+174 -17
View File
@@ -30,6 +30,155 @@ export function init({
const simulationElement = elements.simulation; const simulationElement = elements.simulation;
const dom = {
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {string} args.placeholder
* @param {Signal<number | null>} args.signal
* @param {number} args.min
* @param {number} args.step
* @param {number} [args.max]
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputNumberElement({
id,
title,
signal,
min,
max,
step,
placeholder,
signals,
}) {
const input = window.document.createElement("input");
if (!id || !title || !placeholder) throw Error("input attribute missing");
input.id = id;
input.title = title;
input.placeholder = placeholder;
input.type = "number";
input.min = String(min);
if (max) {
input.max = String(max);
}
input.step = String(step);
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const value = signal();
return value ? String(value) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("input", () => {
const valueSer = input.value;
stateValue = valueSer;
const value = Number(valueSer);
if (value >= min && (max ? value <= max : true)) {
signal.set(value);
}
});
return { input, signal };
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<number | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDollar({ id, title, signal, signals }) {
return this.createInputNumberElement({
id,
placeholder: "USD",
min: 0,
title,
signal,
signals,
step: 1,
});
},
/**
* @param {Object} args
* @param {string} args.id
* @param {string} args.title
* @param {Signal<Date | null>} args.signal
* @param {{createEffect: typeof CreateEffect}} args.signals
*/
createInputDate({ id, title, signal, signals }) {
const input = window.document.createElement("input");
input.id = id;
input.title = title;
input.type = "date";
const min = "2011-01-01";
const minDate = new Date(min);
const maxDate = new Date();
const max = utils.date.toString(maxDate);
input.min = min;
input.max = max;
let stateValue = /** @type {string | null} */ (null);
signals.createEffect(
() => {
const dateSignal = signal();
return dateSignal ? utils.date.toString(dateSignal) : "";
},
(value) => {
if (stateValue !== value) {
input.value = value;
stateValue = value;
}
},
);
input.addEventListener("change", () => {
const value = input.value;
const date = new Date(value);
if (date >= minDate && date <= maxDate) {
stateValue = value;
signal.set(value ? date : null);
}
});
return { input, signal };
},
/**
* @param {Object} param0
* @param {Signal<any>} param0.signal
* @param {HTMLInputElement} [param0.input]
* @param {HTMLSelectElement} [param0.select]
*/
createResetableInput({ input, select, signal }) {
const div = window.document.createElement("div");
const element = input || select;
if (!element) throw "createResetableField element missing";
div.append(element);
const button = utils.dom.createButtonElement({
onClick: signal.reset,
inside: "Reset",
title: "Reset field",
});
button.type = "reset";
div.append(button);
return div;
},
};
const parametersElement = window.document.createElement("div"); const parametersElement = window.document.createElement("div");
simulationElement.append(parametersElement); simulationElement.append(parametersElement);
const resultsElement = window.document.createElement("div"); const resultsElement = window.document.createElement("div");
@@ -276,8 +425,8 @@ export function init({
}), }),
description: description:
"The amount of dollars you have ready on the exchange on day one.", "The amount of dollars you have ready on the exchange on day one.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDollar({ dom.createInputDollar({
id: "simulation-dollars-initial", id: "simulation-dollars-initial",
title: "Initial Dollar Amount", title: "Initial Dollar Amount",
signal: settings.dollars.initial.amount, signal: settings.dollars.initial.amount,
@@ -296,11 +445,12 @@ export function init({
}), }),
description: description:
"The frequency at which you'll top up your account at the exchange.", "The frequency at which you'll top up your account at the exchange.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createSelect({ utils.dom.createSelect({
id: "top-up-frequency", id: "top-up-frequency",
list: frequencies.list, list: frequencies.list,
signal: settings.dollars.topUp.frenquency, signal: settings.dollars.topUp.frenquency,
deep: true,
}), }),
), ),
}), }),
@@ -315,8 +465,8 @@ export function init({
}), }),
description: description:
"The recurrent amount of dollars you'll be transfering to said exchange.", "The recurrent amount of dollars you'll be transfering to said exchange.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDollar({ dom.createInputDollar({
id: "simulation-dollars-top-up-amount", id: "simulation-dollars-top-up-amount",
title: "Top Up Dollar Amount", title: "Top Up Dollar Amount",
signal: settings.dollars.topUp.amount, signal: settings.dollars.topUp.amount,
@@ -335,8 +485,8 @@ export function init({
}), }),
description: description:
"The amount, if available, of dollars that will be used to buy Bitcoin on day one.", "The amount, if available, of dollars that will be used to buy Bitcoin on day one.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDollar({ dom.createInputDollar({
id: "simulation-bitcoin-initial-investment", id: "simulation-bitcoin-initial-investment",
title: "Initial Swap Amount", title: "Initial Swap Amount",
signal: settings.bitcoin.investment.initial, signal: settings.bitcoin.investment.initial,
@@ -354,11 +504,12 @@ export function init({
text: "Investment Frequency", text: "Investment Frequency",
}), }),
description: "The frequency at which you'll be buying Bitcoin.", description: "The frequency at which you'll be buying Bitcoin.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createSelect({ utils.dom.createSelect({
id: "investment-frequency", id: "investment-frequency",
list: frequencies.list, list: frequencies.list,
signal: settings.bitcoin.investment.frequency, signal: settings.bitcoin.investment.frequency,
deep: true,
}), }),
), ),
}), }),
@@ -373,8 +524,8 @@ export function init({
}), }),
description: description:
"The recurrent amount, if available, of dollars that will be used to buy Bitcoin.", "The recurrent amount, if available, of dollars that will be used to buy Bitcoin.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDollar({ dom.createInputDollar({
id: "simulation-bitcoin-recurrent-investment", id: "simulation-bitcoin-recurrent-investment",
title: "Bitcoin Recurrent Investment", title: "Bitcoin Recurrent Investment",
signal: settings.bitcoin.investment.recurrent, signal: settings.bitcoin.investment.recurrent,
@@ -392,8 +543,8 @@ export function init({
text: "Start", text: "Start",
}), }),
description: "The first day of the simulation.", description: "The first day of the simulation.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDate({ dom.createInputDate({
id: "simulation-inverval-start", id: "simulation-inverval-start",
title: "First Simulation Date", title: "First Simulation Date",
signal: settings.interval.start, signal: settings.interval.start,
@@ -411,8 +562,8 @@ export function init({
text: "End", text: "End",
}), }),
description: "The last day of the simulation.", description: "The last day of the simulation.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputDate({ dom.createInputDate({
id: "simulation-inverval-end", id: "simulation-inverval-end",
title: "Last Simulation Day", title: "Last Simulation Day",
signal: settings.interval.end, signal: settings.interval.end,
@@ -430,8 +581,8 @@ export function init({
text: "Exchange", text: "Exchange",
}), }),
description: "The amount of trading fees (in %) at the exchange.", description: "The amount of trading fees (in %) at the exchange.",
input: utils.dom.createResetableInput( input: dom.createResetableInput(
utils.dom.createInputNumberElement({ dom.createInputNumberElement({
id: "simulation-fees", id: "simulation-fees",
title: "Exchange Fees", title: "Exchange Fees",
signal: settings.fees.percentage, signal: settings.fees.percentage,
@@ -555,6 +706,7 @@ export function init({
fitContentOnResize: true, fitContentOnResize: true,
vecsResources, vecsResources,
utils, utils,
elements,
config: [ config: [
{ {
unit: "USD", unit: "USD",
@@ -597,6 +749,7 @@ export function init({
id: `bitcoin`, id: `bitcoin`,
fitContentOnResize: true, fitContentOnResize: true,
vecsResources, vecsResources,
elements,
utils, utils,
config: [ config: [
{ {
@@ -621,6 +774,7 @@ export function init({
fitContentOnResize: true, fitContentOnResize: true,
vecsResources, vecsResources,
utils, utils,
elements,
config: [ config: [
{ {
unit: "USD", unit: "USD",
@@ -650,6 +804,8 @@ export function init({
id: `return-ratio`, id: `return-ratio`,
fitContentOnResize: true, fitContentOnResize: true,
utils, utils,
elements,
config: [ config: [
{ {
unit: "USD", unit: "USD",
@@ -672,10 +828,11 @@ export function init({
fitContentOnResize: true, fitContentOnResize: true,
vecsResources, vecsResources,
utils, utils,
elements,
owner, owner,
config: [ config: [
{ {
unit: "Percentage", unit: "%",
blueprints: [ blueprints: [
{ {
title: "Profitable Days Ratio", title: "Profitable Days Ratio",
+534
View File
@@ -0,0 +1,534 @@
// @ts-check
/**
* @param {Object} args
* @param {VecIdToIndexes} args.vecIdToIndexes
* @param {Option} args.option
* @param {Utilities} args.utils
* @param {Signals} args.signals
* @param {VecsResources} args.vecsResources
*/
function createTable({
utils,
vecIdToIndexes,
signals,
option,
vecsResources,
}) {
const indexToVecIds = createIndexToVecIds(vecIdToIndexes);
const serializedIndexes = createSerializedIndexes();
/** @type {SerializedIndex} */
const defaultSerializedIndex = "height";
const serializedIndex = /** @type {Signal<SerializedIndex>} */ (
signals.createSignal(
/** @type {SerializedIndex} */ (defaultSerializedIndex),
{
save: {
...utils.serde.string,
keyPrefix: "table",
key: "index",
},
},
)
);
const index = signals.createMemo(() =>
serializedIndexToIndex(serializedIndex()),
);
const table = window.document.createElement("table");
const obj = {
element: table,
/** @type {VoidFunction | undefined} */
addRandomCol: undefined,
};
signals.createEffect(index, (index, prevIndex) => {
if (prevIndex !== undefined) {
utils.url.resetParams(option);
}
const possibleVecIds = indexToVecIds[index];
const columns = signals.createSignal(/** @type {VecId[]} */ ([]), {
equals: false,
save: {
...utils.serde.vecIds,
keyPrefix: `table-${serializedIndex()}`,
key: `columns`,
},
});
columns.set((l) => l.filter((id) => possibleVecIds.includes(id)));
signals.createEffect(columns, (columns) => {
console.log(columns);
});
table.innerHTML = "";
const thead = window.document.createElement("thead");
table.append(thead);
const trHead = window.document.createElement("tr");
thead.append(trHead);
const tbody = window.document.createElement("tbody");
table.append(tbody);
const rowElements = signals.createSignal(
/** @type {HTMLTableRowElement[]} */ ([]),
);
/**
* @param {Object} args
* @param {HTMLSelectElement} args.select
* @param {Unit} [args.unit]
* @param {(event: MouseEvent) => void} [args.onLeft]
* @param {(event: MouseEvent) => void} [args.onRight]
* @param {(event: MouseEvent) => void} [args.onRemove]
*/
function addThCol({ select, onLeft, onRight, onRemove, unit: _unit }) {
const th = window.document.createElement("th");
th.scope = "col";
trHead.append(th);
const div = window.document.createElement("div");
div.append(select);
// const top = window.document.createElement("div");
// div.append(top);
// top.append(select);
// top.append(
// utils.dom.createAnchorElement({
// href: "",
// blank: true,
// }),
// );
const bottom = window.document.createElement("div");
const unit = window.document.createElement("span");
if (_unit) {
unit.innerHTML = _unit;
}
const moveLeft = utils.dom.createButtonElement({
inside: "←",
title: "Move column to the left",
onClick: onLeft || (() => {}),
});
const moveRight = utils.dom.createButtonElement({
inside: "→",
title: "Move column to the right",
onClick: onRight || (() => {}),
});
const remove = utils.dom.createButtonElement({
inside: "×",
title: "Remove column",
onClick: onRemove || (() => {}),
});
bottom.append(unit);
bottom.append(moveLeft);
bottom.append(moveRight);
bottom.append(remove);
div.append(bottom);
th.append(div);
return {
element: th,
/**
* @param {Unit} _unit
*/
setUnit(_unit) {
unit.innerHTML = _unit;
},
};
}
addThCol({
...utils.dom.createSelect({
list: serializedIndexes,
signal: serializedIndex,
}),
unit: "Index",
});
let from = 0;
let to = 0;
vecsResources
.getOrCreate(index, serializedIndex())
.fetch()
.then((vec) => {
if (!vec) return;
from = /** @type {number} */ (vec[0]);
to = /** @type {number} */ (vec.at(-1)) + 1;
const trs = /** @type {HTMLTableRowElement[]} */ ([]);
for (let i = vec.length - 1; i >= 0; i--) {
const value = vec[i];
const tr = window.document.createElement("tr");
trs.push(tr);
tbody.append(tr);
const th = window.document.createElement("th");
th.innerHTML = serializeValue({
value,
unit: "Index",
});
th.scope = "row";
tr.append(th);
}
rowElements.set(() => trs);
});
const owner = signals.getOwner();
/**
* @param {VecId} vecId
* @param {number} [_colIndex]
*/
function addCol(vecId, _colIndex = columns().length) {
signals.runWithOwner(owner, () => {
/** @type {VoidFunction | undefined} */
let dispose;
signals.createRoot((_dispose) => {
dispose = _dispose;
const vecIdOption = signals.createSignal({
name: vecId,
value: vecId,
});
const { select } = utils.dom.createSelect({
list: possibleVecIds.map((vecId) => ({
name: vecId,
value: vecId,
})),
signal: vecIdOption,
});
if (_colIndex === columns().length) {
columns.set((l) => {
l.push(vecId);
return l;
});
}
const colIndex = signals.createSignal(_colIndex);
/**
* @param {boolean} right
* @returns {(event: MouseEvent) => void}
*/
function createMoveColumnFunction(right) {
return () => {
const oldColIndex = colIndex();
const newColIndex = oldColIndex + (right ? 1 : -1);
const currentTh = /** @type {HTMLTableCellElement} */ (
trHead.childNodes[oldColIndex + 1]
);
const oterTh = /** @type {HTMLTableCellElement} */ (
trHead.childNodes[newColIndex + 1]
);
if (right) {
oterTh.after(currentTh);
} else {
oterTh.before(currentTh);
}
columns.set((l) => {
[l[oldColIndex], l[newColIndex]] = [
l[newColIndex],
l[oldColIndex],
];
return l;
});
const rows = rowElements();
for (let i = 0; i < rows.length; i++) {
const element = rows[i].childNodes[oldColIndex + 1];
const sibling = rows[i].childNodes[newColIndex + 1];
const temp = element.textContent;
element.textContent = sibling.textContent;
sibling.textContent = temp;
}
};
}
const th = addThCol({
select,
unit: utils.vecidToUnit(vecId),
onLeft: createMoveColumnFunction(false),
onRight: createMoveColumnFunction(true),
onRemove: () => {
const ci = colIndex();
trHead.childNodes[ci + 1].remove();
columns.set((l) => {
l.splice(ci, 1);
return l;
});
const rows = rowElements();
for (let i = 0; i < rows.length; i++) {
rows[i].childNodes[ci + 1].remove();
}
dispose?.();
},
});
signals.createEffect(columns, () => {
colIndex.set(Array.from(trHead.children).indexOf(th.element) - 1);
});
console.log(colIndex());
signals.createEffect(rowElements, (rowElements) => {
if (!rowElements.length) return;
for (let i = 0; i < rowElements.length; i++) {
const td = window.document.createElement("td");
rowElements[i].append(td);
}
signals.createEffect(
() => vecIdOption().name,
(vecId, prevVecId) => {
const unit = utils.vecidToUnit(vecId);
th.setUnit(unit);
const vec = vecsResources.getOrCreate(index, vecId);
vec.fetch({ from, to });
const fetchedKey = vecsResources.genFetchedKey({ from, to });
columns.set((l) => {
const i = l.indexOf(prevVecId ?? vecId);
if (i === -1) {
l.push(vecId);
} else {
l[i] = vecId;
}
return l;
});
signals.createEffect(vec.fetched[fetchedKey], (vec) => {
if (!vec) return;
const thIndex = colIndex() + 1;
for (let i = 0; i < rowElements.length; i++) {
const iRev = vec.length - 1 - i;
const value = vec[iRev];
// @ts-ignore
rowElements[i].childNodes[thIndex].innerHTML =
serializeValue({
value,
unit,
});
}
});
return () => vecId;
},
);
});
});
signals.onCleanup(() => {
dispose?.();
});
});
}
columns().forEach((vecId, colIndex) => addCol(vecId, colIndex));
obj.addRandomCol = function () {
const vecId =
possibleVecIds[Math.floor(Math.random() * possibleVecIds.length)];
addCol(vecId);
};
return () => index;
});
return obj;
}
/**
* @param {Object} args
* @param {Colors} args.colors
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Option} args.option
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources
* @param {VecIdToIndexes} args.vecIdToIndexes
*/
export function init({
colors,
elements,
signals,
option,
utils,
vecsResources,
vecIdToIndexes,
}) {
const parent = elements.table;
const { headerElement } = utils.dom.createHeader({
title: "Table",
});
parent.append(headerElement);
const div = window.document.createElement("div");
parent.append(div);
const table = createTable({
signals,
utils,
vecIdToIndexes,
vecsResources,
option,
});
div.append(table.element);
const span = window.document.createElement("span");
span.innerHTML = "Add column";
div.append(
utils.dom.createButtonElement({
onClick: () => {
table.addRandomCol?.();
},
inside: span,
title: "Click or tap to add a column to the table",
}),
);
}
function createSerializedIndexes() {
return /** @type {const} */ ([
/** @satisfies {VecId} */ ("height"),
/** @satisfies {VecId} */ ("dateindex"),
/** @satisfies {VecId} */ ("weekindex"),
/** @satisfies {VecId} */ ("difficultyepoch"),
/** @satisfies {VecId} */ ("monthindex"),
/** @satisfies {VecId} */ ("quarterindex"),
/** @satisfies {VecId} */ ("yearindex"),
/** @satisfies {VecId} */ ("decadeindex"),
/** @satisfies {VecId} */ ("halvingepoch"),
/** @satisfies {VecId} */ ("addressindex"),
/** @satisfies {VecId} */ ("p2pk33index"),
/** @satisfies {VecId} */ ("p2pk65index"),
/** @satisfies {VecId} */ ("p2pkhindex"),
/** @satisfies {VecId} */ ("p2shindex"),
/** @satisfies {VecId} */ ("p2trindex"),
/** @satisfies {VecId} */ ("p2wpkhindex"),
/** @satisfies {VecId} */ ("p2wshindex"),
/** @satisfies {VecId} */ ("txindex"),
/** @satisfies {VecId} */ ("txinindex"),
/** @satisfies {VecId} */ ("txoutindex"),
/** @satisfies {VecId} */ ("emptyindex"),
/** @satisfies {VecId} */ ("multisigindex"),
/** @satisfies {VecId} */ ("opreturnindex"),
/** @satisfies {VecId} */ ("pushonlyindex"),
/** @satisfies {VecId} */ ("unknownindex"),
]);
}
/** @typedef {ReturnType<typeof createSerializedIndexes>} SerializedIndexes */
/** @typedef {SerializedIndexes[number]} SerializedIndex */
/**
* @param {SerializedIndex} serializedIndex
* @returns {Index}
*/
function serializedIndexToIndex(serializedIndex) {
switch (serializedIndex) {
case "height":
return /** @satisfies {Height} */ (0);
case "dateindex":
return /** @satisfies {Dateindex} */ (1);
case "weekindex":
return /** @satisfies {Weekindex} */ (2);
case "difficultyepoch":
return /** @satisfies {Difficultyepoch} */ (3);
case "monthindex":
return /** @satisfies {Monthindex} */ (4);
case "quarterindex":
return /** @satisfies {Quarterindex} */ (5);
case "yearindex":
return /** @satisfies {Yearindex} */ (6);
case "decadeindex":
return /** @satisfies {Decadeindex} */ (7);
case "halvingepoch":
return /** @satisfies {Halvingepoch} */ (8);
case "addressindex":
return /** @satisfies {Addressindex} */ (9);
case "p2pk33index":
return /** @satisfies {P2PK33index} */ (10);
case "p2pk65index":
return /** @satisfies {P2PK65index} */ (11);
case "p2pkhindex":
return /** @satisfies {P2PKHindex} */ (12);
case "p2shindex":
return /** @satisfies {P2SHindex} */ (13);
case "p2trindex":
return /** @satisfies {P2TRindex} */ (14);
case "p2wpkhindex":
return /** @satisfies {P2WPKHindex} */ (15);
case "p2wshindex":
return /** @satisfies {P2WSHindex} */ (16);
case "txindex":
return /** @satisfies {Txindex} */ (17);
case "txinindex":
return /** @satisfies {Txinindex} */ (18);
case "txoutindex":
return /** @satisfies {Txoutindex} */ (19);
case "emptyindex":
return /** @satisfies {Emptyindex} */ (20);
case "multisigindex":
return /** @satisfies {Multisigindex} */ (21);
case "opreturnindex":
return /** @satisfies {Opreturnindex} */ (22);
case "pushonlyindex":
return /** @satisfies {Pushonlyindex} */ (23);
case "unknownindex":
return /** @satisfies {Unknownindex} */ (24);
}
}
/**
* @param {VecIdToIndexes} vecIdToIndexes
*/
function createIndexToVecIds(vecIdToIndexes) {
const indexToVecIds = Object.entries(vecIdToIndexes).reduce(
(arr, [_id, indexes]) => {
const id = /** @type {VecId} */ (_id);
indexes.forEach((i) => {
arr[i] ??= [];
arr[i].push(id);
});
return arr;
},
/** @type {VecId[][]} */ (new Array(24)),
);
indexToVecIds.forEach((arr) => {
arr.sort();
});
return indexToVecIds;
}
/**
* @param {Object} args
* @param {number | OHLCTuple} args.value
* @param {Unit} args.unit
*/
function serializeValue({ value, unit }) {
if (typeof value !== "number") {
return String(value);
} else if (value !== 18446744073709552000) {
if (unit === "USD" || unit === "Difficulty" || unit === "sat/vB") {
return value.toLocaleString("en-us", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
} else if (unit === "BTC") {
return value.toLocaleString("en-us", {
minimumFractionDigits: 8,
maximumFractionDigits: 8,
});
} else {
return value.toLocaleString("en-us");
}
} else {
return "";
}
}
@@ -58,7 +58,7 @@ export function createVecIdToIndexes() {
const Unknownindex = /** @satisfies {Unknownindex} */ (24); const Unknownindex = /** @satisfies {Unknownindex} */ (24);
return /** @type {const} */ ({ return /** @type {const} */ ({
addressindex: [Txoutindex], addressindex: [Addressindex, Txoutindex],
addresstype: [Addressindex], addresstype: [Addressindex],
addresstypeindex: [Addressindex], addresstypeindex: [Addressindex],
"base-size": [Txindex], "base-size": [Txindex],
@@ -114,6 +114,7 @@ export function createVecIdToIndexes() {
decadeindex: [Yearindex, Decadeindex], decadeindex: [Yearindex, Decadeindex],
difficulty: [Height], difficulty: [Height],
difficultyepoch: [Height, Difficultyepoch], difficultyepoch: [Height, Difficultyepoch],
emptyindex: [Emptyindex],
fee: [Txindex], fee: [Txindex],
"fee-10p": [Height], "fee-10p": [Height],
"fee-25p": [Height], "fee-25p": [Height],
@@ -208,12 +209,14 @@ export function createVecIdToIndexes() {
"low-in-cents": [Dateindex, Height], "low-in-cents": [Dateindex, Height],
"low-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "low-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
monthindex: [Dateindex, Monthindex], monthindex: [Dateindex, Monthindex],
multisigindex: [Multisigindex],
ohlc: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], ohlc: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"ohlc-in-cents": [Dateindex, Height], "ohlc-in-cents": [Dateindex, Height],
"ohlc-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "ohlc-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
open: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], open: [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"open-in-cents": [Dateindex, Height], "open-in-cents": [Dateindex, Height],
"open-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "open-in-sats": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
opreturnindex: [Opreturnindex],
"output-count": [Txindex], "output-count": [Txindex],
"output-count-10p": [Height], "output-count-10p": [Height],
"output-count-25p": [Height], "output-count-25p": [Height],
@@ -228,12 +231,20 @@ export function createVecIdToIndexes() {
"output-value-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "output-value-average": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
"output-value-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "output-value-sum": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
p2pk33addressbytes: [P2PK33index], p2pk33addressbytes: [P2PK33index],
p2pk33index: [P2PK33index],
p2pk65addressbytes: [P2PK65index], p2pk65addressbytes: [P2PK65index],
p2pk65index: [P2PK65index],
p2pkhaddressbytes: [P2PKHindex], p2pkhaddressbytes: [P2PKHindex],
p2pkhindex: [P2PKHindex],
p2shaddressbytes: [P2SHindex], p2shaddressbytes: [P2SHindex],
p2shindex: [P2SHindex],
p2traddressbytes: [P2TRindex], p2traddressbytes: [P2TRindex],
p2trindex: [P2TRindex],
p2wpkhaddressbytes: [P2WPKHindex], p2wpkhaddressbytes: [P2WPKHindex],
p2wpkhindex: [P2WPKHindex],
p2wshaddressbytes: [P2WSHindex], p2wshaddressbytes: [P2WSHindex],
p2wshindex: [P2WSHindex],
pushonlyindex: [Pushonlyindex],
quarterindex: [Monthindex, Quarterindex], quarterindex: [Monthindex, Quarterindex],
"real-date": [Height], "real-date": [Height],
subsidy: [Height], subsidy: [Height],
@@ -322,8 +333,11 @@ export function createVecIdToIndexes() {
"tx-weight-median": [Height], "tx-weight-median": [Height],
"tx-weight-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch], "tx-weight-min": [Dateindex, Height, Weekindex, Monthindex, Quarterindex, Yearindex, Decadeindex, Difficultyepoch],
txid: [Txindex], txid: [Txindex],
txoutindex: [Txinindex], txindex: [Txindex],
txinindex: [Txinindex],
txoutindex: [Txinindex, Txoutindex],
txversion: [Txindex], txversion: [Txindex],
unknownindex: [Unknownindex],
value: [Txinindex, Txoutindex], value: [Txinindex, Txoutindex],
vbytes: [Height], vbytes: [Height],
vsize: [Txindex], vsize: [Txindex],
+4
View File
@@ -27,4 +27,8 @@
.chart { .chart {
flex: 1; flex: 1;
} }
> * {
z-index: 30;
}
} }
+142
View File
@@ -0,0 +1,142 @@
#table {
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
padding: var(--main-padding);
> div {
display: flex;
font-size: var(--font-size-sm);
margin-left: var(--negative-main-padding);
margin-right: var(--negative-main-padding);
table {
z-index: 10;
border-top-width: 1px;
border-style: dashed !important;
/* width: 100%; */
line-height: var(--line-height-sm);
text-transform: uppercase;
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
/* border: 3px solid purple; */
/* min-height: 100%; */
}
th {
font-weight: 600;
}
th,
td {
/* border-top: 1px; */
border-right: 1px;
border-bottom: 1px;
border-color: var(--off-color);
border-style: dashed !important;
padding: 0.25rem 1rem;
}
td {
text-transform: lowercase;
}
a {
margin: -0.2rem 0;
font-size: 1.2rem;
}
th:first-child {
padding-left: var(--main-padding);
}
th[scope="col"] {
position: sticky;
top: 0;
background-color: var(--background-color);
> div {
display: flex;
flex-direction: column;
padding-top: 0.275rem;
> div {
display: flex;
gap: 0.25rem;
text-transform: lowercase;
color: var(--off-color);
text-align: left;
&:first-child {
gap: 0.5rem;
}
&:last-child {
gap: 1rem;
}
> span {
width: 100%;
}
> button {
padding: 0 0.25rem;
margin: 0 -0.25rem;
font-size: 1rem;
line-height: 0;
}
}
}
&:first-child {
button {
display: none;
}
}
&:nth-child(2) {
button:nth-of-type(1) {
display: none;
}
}
&:last-child {
button:nth-of-type(2) {
display: none;
}
}
}
/* select {
width: 100%;
} */
tbody {
text-align: right;
}
> button {
padding: 1rem;
min-width: 10rem;
display: flex;
flex-direction: column;
flex: 1;
position: relative;
border-top-width: 1px;
width: 100%;
/* border-right-width: 1px; */
border-bottom-width: 1px;
border-style: dashed !important;
> span {
text-align: left;
position: sticky;
top: 2rem;
left: 0;
right: 0;
}
}
}
}