mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-16 13:38:12 -07:00
bitview: reorg part 9
This commit is contained in:
Generated
+37
-37
@@ -229,9 +229,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
|
checksum = "98e529aee37b5c8206bb4bf4c44797127566d72f76952c970bd3d1e85de8f4e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -248,8 +248,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"serde_core",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@@ -263,9 +262,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.5.2"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
|
checksum = "0ac7a6beb1182c7e30253ee75c3e918080bfb83f5a3023bcdf7209d85fd147e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -274,7 +273,6 @@ dependencies = [
|
|||||||
"http-body-util",
|
"http-body-util",
|
||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -500,6 +498,7 @@ dependencies = [
|
|||||||
name = "brk"
|
name = "brk"
|
||||||
version = "0.0.109"
|
version = "0.0.109"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"brk_binder",
|
||||||
"brk_bundler",
|
"brk_bundler",
|
||||||
"brk_cli",
|
"brk_cli",
|
||||||
"brk_computer",
|
"brk_computer",
|
||||||
@@ -562,7 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "91ff3e445e42475fba5e0cfaed51345f491e479b9f2069f29875f434a5327913"
|
checksum = "91ff3e445e42475fba5e0cfaed51345f491e479b9f2069f29875f434a5327913"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_bridge"
|
name = "brk_binder"
|
||||||
version = "0.0.109"
|
version = "0.0.109"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brk_interface",
|
"brk_interface",
|
||||||
@@ -585,7 +584,7 @@ name = "brk_cli"
|
|||||||
version = "0.0.109"
|
version = "0.0.109"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoincore-rpc",
|
"bitcoincore-rpc",
|
||||||
"brk_bridge",
|
"brk_binder",
|
||||||
"brk_bundler",
|
"brk_bundler",
|
||||||
"brk_computer",
|
"brk_computer",
|
||||||
"brk_error",
|
"brk_error",
|
||||||
@@ -639,7 +638,7 @@ dependencies = [
|
|||||||
"fjall",
|
"fjall",
|
||||||
"jiff",
|
"jiff",
|
||||||
"minreq",
|
"minreq",
|
||||||
"serde_json",
|
"sonic-rs 0.5.5",
|
||||||
"vecdb",
|
"vecdb",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
@@ -653,7 +652,7 @@ dependencies = [
|
|||||||
"brk_structs",
|
"brk_structs",
|
||||||
"log",
|
"log",
|
||||||
"minreq",
|
"minreq",
|
||||||
"serde_json",
|
"sonic-rs 0.5.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -688,8 +687,8 @@ dependencies = [
|
|||||||
"quick_cache",
|
"quick_cache",
|
||||||
"schemars 1.0.4",
|
"schemars 1.0.4",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
"sonic-rs 0.5.5",
|
||||||
"vecdb",
|
"vecdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -711,6 +710,8 @@ dependencies = [
|
|||||||
"brk_interface",
|
"brk_interface",
|
||||||
"brk_rmcp",
|
"brk_rmcp",
|
||||||
"log",
|
"log",
|
||||||
|
"schemars 1.0.4",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -749,7 +750,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sse-stream",
|
"sse-stream",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -1140,7 +1141,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"quick_cache",
|
"quick_cache",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"sonic-rs 0.5.5",
|
"sonic-rs 0.5.5",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -2659,9 +2659,9 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lsm-tree"
|
name = "lsm-tree"
|
||||||
version = "2.10.3"
|
version = "2.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab73c02eadb3dc12c0024e5b61d6284e6d59064e67e74fbad77856caa56f62c7"
|
checksum = "799399117a2bfb37660e08be33f470958babb98386b04185288d829df362ea15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"crossbeam-skiplist",
|
"crossbeam-skiplist",
|
||||||
@@ -2683,9 +2683,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lz4_flex"
|
name = "lz4_flex"
|
||||||
version = "0.11.3"
|
version = "0.11.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
|
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
@@ -2978,7 +2978,7 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2992,7 +2992,7 @@ dependencies = [
|
|||||||
"owo-colors",
|
"owo-colors",
|
||||||
"oxc-miette-derive",
|
"oxc-miette-derive",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
@@ -3283,7 +3283,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"simdutf8",
|
"simdutf8",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows",
|
||||||
@@ -3645,7 +3645,7 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3739,9 +3739,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -3894,23 +3894,23 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.16",
|
"getrandom 0.2.16",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-cast"
|
name = "ref-cast"
|
||||||
version = "1.0.24"
|
version = "1.0.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
|
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ref-cast-impl",
|
"ref-cast-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-cast-impl"
|
name = "ref-cast-impl"
|
||||||
version = "1.0.24"
|
version = "1.0.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
|
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4433,7 +4433,7 @@ dependencies = [
|
|||||||
"simdutf8",
|
"simdutf8",
|
||||||
"sonic-number",
|
"sonic-number",
|
||||||
"sonic-simd",
|
"sonic-simd",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4454,7 +4454,7 @@ dependencies = [
|
|||||||
"simdutf8",
|
"simdutf8",
|
||||||
"sonic-number",
|
"sonic-number",
|
||||||
"sonic-simd",
|
"sonic-simd",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4619,11 +4619,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.16"
|
version = "2.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.16",
|
"thiserror-impl 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4639,9 +4639,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.16"
|
version = "2.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4963,7 +4963,7 @@ version = "11.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be"
|
checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.17",
|
||||||
"ts-rs-macros",
|
"ts-rs-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -42,10 +42,10 @@ debug-assertions = false
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
allocative = { version = "0.3.4", features = ["parking_lot"] }
|
allocative = { version = "0.3.4", features = ["parking_lot"] }
|
||||||
axum = "0.8.4"
|
axum = "0.8.5"
|
||||||
bitcoin = { version = "0.32.7", features = ["serde"] }
|
bitcoin = { version = "0.32.7", features = ["serde"] }
|
||||||
bitcoincore-rpc = "0.19.0"
|
bitcoincore-rpc = "0.19.0"
|
||||||
brk_bridge = { version = "0.0.109", path = "crates/brk_bridge" }
|
brk_binder = { version = "0.0.109", path = "crates/brk_binder" }
|
||||||
brk_bundler = { version = "0.0.109", path = "crates/brk_bundler" }
|
brk_bundler = { version = "0.0.109", path = "crates/brk_bundler" }
|
||||||
brk_cli = { version = "0.0.109", path = "crates/brk_cli" }
|
brk_cli = { version = "0.0.109", path = "crates/brk_cli" }
|
||||||
brk_computer = { version = "0.0.109", path = "crates/brk_computer" }
|
brk_computer = { version = "0.0.109", path = "crates/brk_computer" }
|
||||||
@@ -68,10 +68,10 @@ minreq = { version = "2.14.1", features = ["https", "serde_json"] }
|
|||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.4"
|
||||||
quick_cache = "0.6.16"
|
quick_cache = "0.6.16"
|
||||||
rayon = "1.11.0"
|
rayon = "1.11.0"
|
||||||
|
schemars = "1.0.4"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_bytes = "0.11.19"
|
serde_bytes = "0.11.19"
|
||||||
serde_derive = "1.0.228"
|
serde_derive = "1.0.228"
|
||||||
serde_json = { version = "1.0.145", features = ["float_roundtrip"] }
|
|
||||||
sonic-rs = "0.5.5"
|
sonic-rs = "0.5.5"
|
||||||
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
|
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
|
||||||
# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]}
|
# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ build = "build.rs"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
full = [
|
full = [
|
||||||
|
"binder",
|
||||||
"bundler",
|
"bundler",
|
||||||
"computer",
|
"computer",
|
||||||
"error",
|
"error",
|
||||||
@@ -24,6 +25,7 @@ full = [
|
|||||||
"store",
|
"store",
|
||||||
"structs",
|
"structs",
|
||||||
]
|
]
|
||||||
|
binder = ["brk_binder"]
|
||||||
bundler = ["brk_bundler"]
|
bundler = ["brk_bundler"]
|
||||||
computer = ["brk_computer"]
|
computer = ["brk_computer"]
|
||||||
error = ["brk_error"]
|
error = ["brk_error"]
|
||||||
@@ -38,6 +40,7 @@ store = ["brk_store"]
|
|||||||
structs = ["brk_structs"]
|
structs = ["brk_structs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
brk_binder = { workspace = true, optional = true }
|
||||||
brk_bundler = { workspace = true, optional = true }
|
brk_bundler = { workspace = true, optional = true }
|
||||||
brk_cli = { workspace = true }
|
brk_cli = { workspace = true }
|
||||||
brk_computer = { workspace = true, optional = true }
|
brk_computer = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
#[cfg(feature = "binder")]
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use brk_binder as binder;
|
||||||
|
|
||||||
#[cfg(feature = "bundler")]
|
#[cfg(feature = "bundler")]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use brk_bundler as bundler;
|
pub use brk_bundler as bundler;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "brk_bridge"
|
name = "brk_binder"
|
||||||
description = "A generator of bridge files for other languages"
|
description = "A generator of binding files for other languages"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# brk_binder
|
||||||
@@ -15,12 +15,12 @@ const AUTO_GENERATED_DISCLAIMER: &str = "//
|
|||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub trait Bridge {
|
pub trait Bridge {
|
||||||
fn generate_js_files(&self, packages_path: &Path) -> io::Result<()>;
|
fn generate_js_files(&self, modules_path: &Path) -> io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bridge for Interface<'static> {
|
impl Bridge for Interface<'static> {
|
||||||
fn generate_js_files(&self, packages_path: &Path) -> io::Result<()> {
|
fn generate_js_files(&self, modules_path: &Path) -> io::Result<()> {
|
||||||
let path = packages_path.join("brk-client");
|
let path = modules_path.join("brk-client");
|
||||||
|
|
||||||
if !fs::exists(&path)? {
|
if !fs::exists(&path)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -36,7 +36,7 @@ impl Bridge for Interface<'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_version_file(parent: &Path) -> io::Result<()> {
|
fn generate_version_file(parent: &Path) -> io::Result<()> {
|
||||||
let path = parent.join(Path::new("metrics.js"));
|
let path = parent.join(Path::new("version.js"));
|
||||||
|
|
||||||
let contents = format!(
|
let contents = format!(
|
||||||
"{AUTO_GENERATED_DISCLAIMER}
|
"{AUTO_GENERATED_DISCLAIMER}
|
||||||
@@ -89,29 +89,39 @@ fn generate_metrics_file(interface: &Interface<'static>, parent: &Path) -> io::R
|
|||||||
let mut contents = format!(
|
let mut contents = format!(
|
||||||
"{AUTO_GENERATED_DISCLAIMER}
|
"{AUTO_GENERATED_DISCLAIMER}
|
||||||
|
|
||||||
|
export const INDEXES = /** @type {{const}} */ ([
|
||||||
|
{}
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
"
|
* @typedef {{typeof INDEXES[number]}} IndexName
|
||||||
);
|
|
||||||
|
|
||||||
contents += &indexes
|
|
||||||
.iter()
|
|
||||||
.map(|i| format!(" * @typedef {{\"{}\"}} {i}", i.serialize_long()))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
contents += &format!(
|
|
||||||
"
|
|
||||||
* @typedef {{{}}} Index
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
",
|
",
|
||||||
indexes
|
indexes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.to_string())
|
.map(|i| format!(" \"{}\"", i.serialize_long()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" | ")
|
.join(",\n")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// contents += &indexes
|
||||||
|
// .iter()
|
||||||
|
// .map(|i| format!(" * @typedef {{\"{}\"}} {i}", i.serialize_long()))
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// .join("\n");
|
||||||
|
|
||||||
|
// contents += &format!(
|
||||||
|
// "
|
||||||
|
// * @typedef {{{}}} Index
|
||||||
|
// */
|
||||||
|
// ",
|
||||||
|
// indexes
|
||||||
|
// .iter()
|
||||||
|
// .map(|i| i.to_string())
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// .join(" | ")
|
||||||
|
// );
|
||||||
|
|
||||||
let mut unique_index_groups = BTreeMap::new();
|
let mut unique_index_groups = BTreeMap::new();
|
||||||
|
|
||||||
let mut word_to_freq: BTreeMap<_, usize> = BTreeMap::new();
|
let mut word_to_freq: BTreeMap<_, usize> = BTreeMap::new();
|
||||||
@@ -131,16 +141,18 @@ fn generate_metrics_file(interface: &Interface<'static>, parent: &Path) -> io::R
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
contents += &format!(
|
contents += &format!(
|
||||||
"export const INDEX_TO_WORD = [
|
"
|
||||||
|
export const INDEX_TO_WORD = [
|
||||||
{}
|
{}
|
||||||
];
|
];
|
||||||
|
|
||||||
",
|
",
|
||||||
words
|
words
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| format!("\"{w}\""))
|
.enumerate()
|
||||||
|
.map(|(index, word)| format!("\"{word}\", // {}", index_to_letters(index)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(",\n ")
|
.join("\n ")
|
||||||
);
|
);
|
||||||
|
|
||||||
let word_to_base62 = words
|
let word_to_base62 = words
|
||||||
@@ -150,7 +162,7 @@ fn generate_metrics_file(interface: &Interface<'static>, parent: &Path) -> io::R
|
|||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let mut ser_metric_to_indexes = "
|
let mut ser_metric_to_indexes = "
|
||||||
/** @type {Record<string, Index[]>} */
|
/** @type {Record<string, IndexName[]>} */
|
||||||
export const COMPRESSED_METRIC_TO_INDEXES = {
|
export const COMPRESSED_METRIC_TO_INDEXES = {
|
||||||
"
|
"
|
||||||
.to_string();
|
.to_string();
|
||||||
@@ -186,7 +198,7 @@ export const COMPRESSED_METRIC_TO_INDEXES = {
|
|||||||
sorted_groups.sort_by_key(|(_, index)| *index);
|
sorted_groups.sort_by_key(|(_, index)| *index);
|
||||||
sorted_groups.into_iter().for_each(|(group, index)| {
|
sorted_groups.into_iter().for_each(|(group, index)| {
|
||||||
let index = index_to_letters(index);
|
let index = index_to_letters(index);
|
||||||
contents += &format!("/** @type {{Index[]}} */\nconst {index} = {group};\n");
|
contents += &format!("/** @type {{IndexName[]}} */\nconst {index} = {group};\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
contents += &ser_metric_to_indexes;
|
contents += &ser_metric_to_indexes;
|
||||||
@@ -1 +0,0 @@
|
|||||||
# brk_bridge
|
|
||||||
@@ -6,7 +6,10 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use brk_rolldown::{Bundler, BundlerOptions, RawMinifyOptions, SourceMapType};
|
use brk_rolldown::{
|
||||||
|
Bundler, BundlerOptions, InlineConstConfig, InlineConstMode, InlineConstOption,
|
||||||
|
OptimizationOption, RawMinifyOptions, SourceMapType,
|
||||||
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
use notify::{EventKind, RecursiveMode, Watcher};
|
use notify::{EventKind, RecursiveMode, Watcher};
|
||||||
use sugar_path::SugarPath;
|
use sugar_path::SugarPath;
|
||||||
@@ -15,17 +18,17 @@ use tokio::sync::Mutex;
|
|||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
pub async fn bundle(
|
pub async fn bundle(
|
||||||
packages_path: &Path,
|
modules_path: &Path,
|
||||||
websites_path: &Path,
|
websites_path: &Path,
|
||||||
source_folder: &str,
|
source_folder: &str,
|
||||||
watch: bool,
|
watch: bool,
|
||||||
) -> io::Result<PathBuf> {
|
) -> io::Result<PathBuf> {
|
||||||
let relative_packages_path = packages_path;
|
let relative_modules_path = modules_path;
|
||||||
let relative_source_path = websites_path.join(source_folder);
|
let relative_source_path = websites_path.join(source_folder);
|
||||||
let relative_dist_path = websites_path.join("dist");
|
let relative_dist_path = websites_path.join("dist");
|
||||||
|
|
||||||
let absolute_packages_path = relative_packages_path.absolutize();
|
let absolute_modules_path = relative_modules_path.absolutize();
|
||||||
let absolute_packages_path_clone = absolute_packages_path.clone();
|
let absolute_modules_path_clone = absolute_modules_path.clone();
|
||||||
let absolute_websites_path = websites_path.absolutize();
|
let absolute_websites_path = websites_path.absolutize();
|
||||||
let absolute_websites_path_clone = absolute_websites_path.clone();
|
let absolute_websites_path_clone = absolute_websites_path.clone();
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ pub async fn bundle(
|
|||||||
let absolute_source_index_path = absolute_source_path.join("index.html");
|
let absolute_source_index_path = absolute_source_path.join("index.html");
|
||||||
let absolute_source_index_path_clone = absolute_source_index_path.clone();
|
let absolute_source_index_path_clone = absolute_source_index_path.clone();
|
||||||
let absolute_source_scripts_path = absolute_source_path.join("scripts");
|
let absolute_source_scripts_path = absolute_source_path.join("scripts");
|
||||||
let absolute_source_scripts_packages_path = absolute_source_scripts_path.join("packages");
|
let absolute_source_scripts_modules_path = absolute_source_scripts_path.join("modules");
|
||||||
let absolute_source_sw_path = absolute_source_path.join("service-worker.js");
|
let absolute_source_sw_path = absolute_source_path.join("service-worker.js");
|
||||||
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
|
let absolute_source_sw_path_clone = absolute_source_sw_path.clone();
|
||||||
|
|
||||||
@@ -45,21 +48,45 @@ pub async fn bundle(
|
|||||||
let absolute_dist_sw_path = absolute_dist_path.join("service-worker.js");
|
let absolute_dist_sw_path = absolute_dist_path.join("service-worker.js");
|
||||||
|
|
||||||
let _ = fs::remove_dir_all(&absolute_dist_path);
|
let _ = fs::remove_dir_all(&absolute_dist_path);
|
||||||
let _ = fs::remove_dir_all(&absolute_source_scripts_packages_path);
|
let _ = fs::remove_dir_all(&absolute_source_scripts_modules_path);
|
||||||
copy_dir_all(
|
copy_dir_all(
|
||||||
&absolute_packages_path,
|
&absolute_modules_path,
|
||||||
&absolute_source_scripts_packages_path,
|
&absolute_source_scripts_modules_path,
|
||||||
)?;
|
)?;
|
||||||
copy_dir_all(&absolute_source_path, &absolute_dist_path)?;
|
copy_dir_all(&absolute_source_path, &absolute_dist_path)?;
|
||||||
fs::remove_dir_all(&absolute_dist_scripts_path)?;
|
fs::remove_dir_all(&absolute_dist_scripts_path)?;
|
||||||
fs::create_dir(&absolute_dist_scripts_path)?;
|
fs::create_dir(&absolute_dist_scripts_path)?;
|
||||||
|
|
||||||
|
// dbg!(BundlerOptions::default());
|
||||||
|
|
||||||
let mut bundler = Bundler::new(BundlerOptions {
|
let mut bundler = Bundler::new(BundlerOptions {
|
||||||
input: Some(vec![format!("./{source_folder}/scripts/entry.js").into()]),
|
input: Some(vec![format!("./{source_folder}/scripts/entry.js").into()]),
|
||||||
dir: Some("./dist/scripts".to_string()),
|
dir: Some("./dist/scripts".to_string()),
|
||||||
cwd: Some(absolute_websites_path),
|
cwd: Some(absolute_websites_path),
|
||||||
minify: Some(RawMinifyOptions::Bool(true)),
|
minify: Some(RawMinifyOptions::Bool(true)),
|
||||||
sourcemap: Some(SourceMapType::File),
|
sourcemap: Some(SourceMapType::File),
|
||||||
|
// advanced_chunks: Some(AdvancedChunksOptions {
|
||||||
|
// // min_size: Some(1000.0),
|
||||||
|
// min_share_count: Some(20),
|
||||||
|
// // min_module_size: S
|
||||||
|
// // include_dependencies_recursively: Some(true),
|
||||||
|
// ..Default::default()
|
||||||
|
// }),
|
||||||
|
//
|
||||||
|
// inline_dynamic_imports
|
||||||
|
// experimental: Some(ExperimentalOptions {
|
||||||
|
// strict_execution_order: Some(true),
|
||||||
|
// ..Default::default()
|
||||||
|
// }),
|
||||||
|
optimization: Some(OptimizationOption {
|
||||||
|
inline_const: Some(InlineConstOption::Config(InlineConstConfig {
|
||||||
|
mode: Some(InlineConstMode::All),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
// Needs benchmarks
|
||||||
|
// pife_for_module_wrappers: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -114,12 +141,11 @@ pub async fn bundle(
|
|||||||
update_dist_index();
|
update_dist_index();
|
||||||
} else if path == absolute_source_sw_path_clone {
|
} else if path == absolute_source_sw_path_clone {
|
||||||
update_source_sw();
|
update_source_sw();
|
||||||
} else if let Ok(suffix) = path.strip_prefix(&absolute_packages_path) {
|
} else if let Ok(suffix) = path.strip_prefix(&absolute_modules_path) {
|
||||||
let source_packages_path =
|
let source_modules_path = absolute_source_scripts_modules_path.join(suffix);
|
||||||
absolute_source_scripts_packages_path.join(suffix);
|
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
let _ = fs::create_dir_all(path.parent().unwrap());
|
let _ = fs::create_dir_all(path.parent().unwrap());
|
||||||
let _ = fs::copy(&path, &source_packages_path);
|
let _ = fs::copy(&path, &source_modules_path);
|
||||||
}
|
}
|
||||||
} else if let Ok(suffix) = path.strip_prefix(&absolute_source_path)
|
} else if let Ok(suffix) = path.strip_prefix(&absolute_source_path)
|
||||||
// scripts are handled by rolldown
|
// scripts are handled by rolldown
|
||||||
@@ -141,7 +167,7 @@ pub async fn bundle(
|
|||||||
.watch(&absolute_websites_path_clone, RecursiveMode::Recursive)
|
.watch(&absolute_websites_path_clone, RecursiveMode::Recursive)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
event_watcher
|
event_watcher
|
||||||
.watch(&absolute_packages_path_clone, RecursiveMode::Recursive)
|
.watch(&absolute_modules_path_clone, RecursiveMode::Recursive)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let watcher =
|
let watcher =
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ build = "build.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitcoincore-rpc = { workspace = true }
|
bitcoincore-rpc = { workspace = true }
|
||||||
brk_bridge = { workspace = true }
|
brk_binder = { workspace = true }
|
||||||
brk_bundler = { workspace = true }
|
brk_bundler = { workspace = true }
|
||||||
brk_computer = { workspace = true }
|
brk_computer = { workspace = true }
|
||||||
brk_error = { workspace = true }
|
brk_error = { workspace = true }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use bitcoincore_rpc::{self, RpcApi};
|
use bitcoincore_rpc::{self, RpcApi};
|
||||||
use brk_bridge::Bridge;
|
use brk_binder::Bridge;
|
||||||
use brk_bundler::bundle;
|
use brk_bundler::bundle;
|
||||||
use brk_computer::Computer;
|
use brk_computer::Computer;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
@@ -63,19 +63,19 @@ pub fn run() -> color_eyre::Result<()> {
|
|||||||
let future = async move {
|
let future = async move {
|
||||||
let bundle_path = if website.is_some() {
|
let bundle_path = if website.is_some() {
|
||||||
let websites_dev_path = Path::new("../../websites");
|
let websites_dev_path = Path::new("../../websites");
|
||||||
let packages_dev_path = Path::new("../../packages");
|
let modules_dev_path = Path::new("../../modules");
|
||||||
|
|
||||||
let websites_path;
|
let websites_path;
|
||||||
let packages_path;
|
let modules_path;
|
||||||
|
|
||||||
if fs::exists(websites_dev_path)? && fs::exists(packages_dev_path)? {
|
if fs::exists(websites_dev_path)? && fs::exists(modules_dev_path)? {
|
||||||
websites_path = websites_dev_path.to_path_buf();
|
websites_path = websites_dev_path.to_path_buf();
|
||||||
packages_path = packages_dev_path.to_path_buf();
|
modules_path = modules_dev_path.to_path_buf();
|
||||||
} else {
|
} else {
|
||||||
let downloaded_brk_path = downloads_path.join(format!("brk-{VERSION}"));
|
let downloaded_brk_path = downloads_path.join(format!("brk-{VERSION}"));
|
||||||
|
|
||||||
let downloaded_websites_path = downloaded_brk_path.join("websites");
|
let downloaded_websites_path = downloaded_brk_path.join("websites");
|
||||||
let downloaded_packages_path = downloaded_brk_path.join("packages");
|
let downloaded_modules_path = downloaded_brk_path.join("modules");
|
||||||
|
|
||||||
if !fs::exists(&downloaded_websites_path)? {
|
if !fs::exists(&downloaded_websites_path)? {
|
||||||
info!("Downloading source from Github...");
|
info!("Downloading source from Github...");
|
||||||
@@ -94,14 +94,14 @@ pub fn run() -> color_eyre::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
websites_path = downloaded_websites_path;
|
websites_path = downloaded_websites_path;
|
||||||
packages_path = downloaded_packages_path;
|
modules_path = downloaded_modules_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface.generate_js_files(&packages_path)?;
|
interface.generate_js_files(&modules_path)?;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
bundle(
|
bundle(
|
||||||
&packages_path,
|
&modules_path,
|
||||||
&websites_path,
|
&websites_path,
|
||||||
website.to_folder_name(),
|
website.to_folder_name(),
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ bitcoincore-rpc = { workspace = true }
|
|||||||
fjall = { workspace = true }
|
fjall = { workspace = true }
|
||||||
jiff = { workspace = true }
|
jiff = { workspace = true }
|
||||||
minreq = { workspace = true }
|
minreq = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
sonic-rs = { workspace = true }
|
||||||
vecdb = { workspace = true }
|
vecdb = { workspace = true }
|
||||||
zerocopy = { workspace = true }
|
zerocopy = { workspace = true }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub enum Error {
|
|||||||
SystemTimeError(time::SystemTimeError),
|
SystemTimeError(time::SystemTimeError),
|
||||||
BitcoinConsensusEncode(bitcoin::consensus::encode::Error),
|
BitcoinConsensusEncode(bitcoin::consensus::encode::Error),
|
||||||
BitcoinBip34Error(bitcoin::block::Bip34Error),
|
BitcoinBip34Error(bitcoin::block::Bip34Error),
|
||||||
SerdeJson(serde_json::Error),
|
SonicRS(sonic_rs::Error),
|
||||||
ZeroCopyError,
|
ZeroCopyError,
|
||||||
Vecs(vecdb::Error),
|
Vecs(vecdb::Error),
|
||||||
|
|
||||||
@@ -49,9 +49,9 @@ impl From<time::SystemTimeError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_json::Error> for Error {
|
impl From<sonic_rs::Error> for Error {
|
||||||
fn from(error: serde_json::Error) -> Self {
|
fn from(error: sonic_rs::Error) -> Self {
|
||||||
Self::SerdeJson(error)
|
Self::SonicRS(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ impl fmt::Display for Error {
|
|||||||
Error::Jiff(error) => Display::fmt(&error, f),
|
Error::Jiff(error) => Display::fmt(&error, f),
|
||||||
Error::Minreq(error) => Display::fmt(&error, f),
|
Error::Minreq(error) => Display::fmt(&error, f),
|
||||||
Error::SeqDB(error) => Display::fmt(&error, f),
|
Error::SeqDB(error) => Display::fmt(&error, f),
|
||||||
Error::SerdeJson(error) => Display::fmt(&error, f),
|
Error::SonicRS(error) => Display::fmt(&error, f),
|
||||||
Error::SystemTimeError(error) => Display::fmt(&error, f),
|
Error::SystemTimeError(error) => Display::fmt(&error, f),
|
||||||
Error::VecDB(error) => Display::fmt(&error, f),
|
Error::VecDB(error) => Display::fmt(&error, f),
|
||||||
Error::Vecs(error) => Display::fmt(&error, f),
|
Error::Vecs(error) => Display::fmt(&error, f),
|
||||||
|
|||||||
@@ -15,4 +15,4 @@ brk_logger = { workspace = true }
|
|||||||
brk_structs = { workspace = true }
|
brk_structs = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
minreq = { workspace = true }
|
minreq = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
sonic-rs = { workspace = true }
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ use std::{
|
|||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str::FromStr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_structs::{Cents, OHLCCents, Timestamp};
|
use brk_structs::{Cents, OHLCCents, Timestamp};
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde_json::Value;
|
use sonic_rs::{JsonContainerTrait, JsonValueTrait, Value};
|
||||||
|
|
||||||
use crate::{Close, Date, Dollars, Fetcher, High, Low, Open, default_retry};
|
use crate::{Close, Date, Dollars, Fetcher, High, Low, Open, default_retry};
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ use crate::{Close, Date, Dollars, Fetcher, High, Low, Open, default_retry};
|
|||||||
pub struct Binance {
|
pub struct Binance {
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
_1mn: Option<BTreeMap<Timestamp, OHLCCents>>,
|
_1mn: Option<BTreeMap<Timestamp, OHLCCents>>,
|
||||||
pub _1d: Option<BTreeMap<Date, OHLCCents>>,
|
_1d: Option<BTreeMap<Date, OHLCCents>>,
|
||||||
har: Option<BTreeMap<Timestamp, OHLCCents>>,
|
har: Option<BTreeMap<Timestamp, OHLCCents>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +68,11 @@ impl Binance {
|
|||||||
info!("Fetching 1mn prices from Binance...");
|
info!("Fetching 1mn prices from Binance...");
|
||||||
|
|
||||||
default_retry(|_| {
|
default_retry(|_| {
|
||||||
Self::json_to_timestamp_to_ohlc(
|
Self::json_to_timestamp_to_ohlc(&sonic_rs::from_str(
|
||||||
&minreq::get(Self::url("interval=1m&limit=1000"))
|
minreq::get(Self::url("interval=1m&limit=1000"))
|
||||||
.send()?
|
.send()?
|
||||||
.json()?,
|
.as_str()?,
|
||||||
)
|
)?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +93,9 @@ impl Binance {
|
|||||||
info!("Fetching daily prices from Binance...");
|
info!("Fetching daily prices from Binance...");
|
||||||
|
|
||||||
default_retry(|_| {
|
default_retry(|_| {
|
||||||
Self::json_to_date_to_ohlc(&minreq::get(Self::url("interval=1d")).send()?.json()?)
|
Self::json_to_date_to_ohlc(&sonic_rs::from_str(
|
||||||
|
minreq::get(Self::url("interval=1d")).send()?.as_str()?,
|
||||||
|
)?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ impl Binance {
|
|||||||
|
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
let json: BTreeMap<String, Value> = if let Ok(json) = serde_json::from_reader(reader) {
|
let json: BTreeMap<String, Value> = if let Ok(json) = sonic_rs::from_reader(reader) {
|
||||||
json
|
json
|
||||||
} else {
|
} else {
|
||||||
return Ok(Default::default());
|
return Ok(Default::default());
|
||||||
@@ -129,7 +130,7 @@ impl Binance {
|
|||||||
.ok_or(Error::Str("Expect object to have log attribute"))?
|
.ok_or(Error::Str("Expect object to have log attribute"))?
|
||||||
.as_object()
|
.as_object()
|
||||||
.ok_or(Error::Str("Expect to be an object"))?
|
.ok_or(Error::Str("Expect to be an object"))?
|
||||||
.get("entries")
|
.get(&"entries")
|
||||||
.ok_or(Error::Str("Expect object to have entries"))?
|
.ok_or(Error::Str("Expect object to have entries"))?
|
||||||
.as_array()
|
.as_array()
|
||||||
.ok_or(Error::Str("Expect to be an array"))?
|
.ok_or(Error::Str("Expect to be an array"))?
|
||||||
@@ -138,11 +139,11 @@ impl Binance {
|
|||||||
entry
|
entry
|
||||||
.as_object()
|
.as_object()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get("request")
|
.get(&"request")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_object()
|
.as_object()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get("url")
|
.get(&"url")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -152,14 +153,14 @@ impl Binance {
|
|||||||
let response = entry
|
let response = entry
|
||||||
.as_object()
|
.as_object()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get("response")
|
.get(&"response")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_object()
|
.as_object()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let content = response.get("content").unwrap().as_object().unwrap();
|
let content = response.get(&"content").unwrap().as_object().unwrap();
|
||||||
|
|
||||||
let text = content.get("text");
|
let text = content.get(&"text");
|
||||||
|
|
||||||
if text.is_none() {
|
if text.is_none() {
|
||||||
return Ok(BTreeMap::new());
|
return Ok(BTreeMap::new());
|
||||||
@@ -167,7 +168,7 @@ impl Binance {
|
|||||||
|
|
||||||
let text = text.unwrap().as_str().unwrap();
|
let text = text.unwrap().as_str().unwrap();
|
||||||
|
|
||||||
Self::json_to_timestamp_to_ohlc(&serde_json::Value::from_str(text).unwrap())
|
Self::json_to_timestamp_to_ohlc(&sonic_rs::from_str(text).unwrap())
|
||||||
})
|
})
|
||||||
.try_fold(BTreeMap::default(), |mut all, res| {
|
.try_fold(BTreeMap::default(), |mut all, res| {
|
||||||
all.append(&mut res?);
|
all.append(&mut res?);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
|
|||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_structs::{Cents, CheckedSub, Date, DateIndex, Height, OHLCCents};
|
use brk_structs::{Cents, CheckedSub, Date, DateIndex, Height, OHLCCents};
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde_json::Value;
|
use sonic_rs::{JsonContainerTrait, JsonValueTrait, Value};
|
||||||
|
|
||||||
use crate::{Close, Dollars, High, Low, Open, default_retry};
|
use crate::{Close, Dollars, High, Low, Open, default_retry};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ impl BRK {
|
|||||||
height + CHUNK_SIZE
|
height + CHUNK_SIZE
|
||||||
);
|
);
|
||||||
|
|
||||||
let body: Value = minreq::get(url).send()?.json()?;
|
let body: Value = sonic_rs::from_str(minreq::get(url).send()?.as_str()?)?;
|
||||||
|
|
||||||
body.as_array()
|
body.as_array()
|
||||||
.ok_or(Error::Str("Expect to be an array"))?
|
.ok_or(Error::Str("Expect to be an array"))?
|
||||||
@@ -96,7 +96,7 @@ impl BRK {
|
|||||||
dateindex + CHUNK_SIZE
|
dateindex + CHUNK_SIZE
|
||||||
);
|
);
|
||||||
|
|
||||||
let body: Value = minreq::get(url).send()?.json()?;
|
let body: Value = sonic_rs::from_str(minreq::get(url).send()?.json()?)?;
|
||||||
|
|
||||||
body.as_array()
|
body.as_array()
|
||||||
.ok_or(Error::Str("Expect to be an array"))?
|
.ok_or(Error::Str("Expect to be an array"))?
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
|
|||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_structs::{Cents, Close, Date, Dollars, High, Low, OHLCCents, Open, Timestamp};
|
use brk_structs::{Cents, Close, Date, Dollars, High, Low, OHLCCents, Open, Timestamp};
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde_json::Value;
|
use sonic_rs::{JsonContainerTrait, JsonValueTrait, Value};
|
||||||
|
|
||||||
use crate::{Fetcher, default_retry};
|
use crate::{Fetcher, default_retry};
|
||||||
|
|
||||||
@@ -36,7 +36,9 @@ impl Kraken {
|
|||||||
info!("Fetching 1mn prices from Kraken...");
|
info!("Fetching 1mn prices from Kraken...");
|
||||||
|
|
||||||
default_retry(|_| {
|
default_retry(|_| {
|
||||||
Self::json_to_timestamp_to_ohlc(&minreq::get(Self::url(1)).send()?.json()?)
|
Self::json_to_timestamp_to_ohlc(&sonic_rs::from_str(
|
||||||
|
minreq::get(Self::url(1)).send()?.as_str()?,
|
||||||
|
)?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +57,11 @@ impl Kraken {
|
|||||||
pub fn fetch_1d() -> Result<BTreeMap<Date, OHLCCents>> {
|
pub fn fetch_1d() -> Result<BTreeMap<Date, OHLCCents>> {
|
||||||
info!("Fetching daily prices from Kraken...");
|
info!("Fetching daily prices from Kraken...");
|
||||||
|
|
||||||
default_retry(|_| Self::json_to_date_to_ohlc(&minreq::get(Self::url(1440)).send()?.json()?))
|
default_retry(|_| {
|
||||||
|
Self::json_to_date_to_ohlc(&sonic_rs::from_str(
|
||||||
|
minreq::get(Self::url(1440)).send()?.as_str()?,
|
||||||
|
)?)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_to_timestamp_to_ohlc(json: &Value) -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
fn json_to_timestamp_to_ohlc(json: &Value) -> Result<BTreeMap<Timestamp, OHLCCents>> {
|
||||||
@@ -73,11 +79,11 @@ impl Kraken {
|
|||||||
{
|
{
|
||||||
json.as_object()
|
json.as_object()
|
||||||
.ok_or(Error::Str("Expect to be an object"))?
|
.ok_or(Error::Str("Expect to be an object"))?
|
||||||
.get("result")
|
.get(&"result")
|
||||||
.ok_or(Error::Str("Expect object to have result"))?
|
.ok_or(Error::Str("Expect object to have result"))?
|
||||||
.as_object()
|
.as_object()
|
||||||
.ok_or(Error::Str("Expect to be an object"))?
|
.ok_or(Error::Str("Expect to be an object"))?
|
||||||
.get("XXBTZUSD")
|
.get(&"XXBTZUSD")
|
||||||
.ok_or(Error::Str("Expect to have XXBTZUSD"))?
|
.ok_or(Error::Str("Expect to have XXBTZUSD"))?
|
||||||
.as_array()
|
.as_array()
|
||||||
.ok_or(Error::Str("Expect to be an array"))?
|
.ok_or(Error::Str("Expect to be an array"))?
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ where
|
|||||||
if i == retries || res.is_ok() {
|
if i == retries || res.is_ok() {
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
|
let _ = dbg!(res);
|
||||||
info!("Failed, waiting {sleep_in_s} seconds...");
|
info!("Failed, waiting {sleep_in_s} seconds...");
|
||||||
sleep(Duration::from_secs(sleep_in_s));
|
sleep(Duration::from_secs(sleep_in_s));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ brk_structs = { workspace = true }
|
|||||||
vecdb = { workspace = true }
|
vecdb = { workspace = true }
|
||||||
derive_deref = { workspace = true }
|
derive_deref = { workspace = true }
|
||||||
quick_cache = { workspace = true }
|
quick_cache = { workspace = true }
|
||||||
schemars = "1.0.4"
|
schemars = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
sonic-rs = { workspace = true }
|
||||||
serde_with = "3.14.1"
|
serde_with = "3.14.1"
|
||||||
nucleo-matcher = "0.3.1"
|
nucleo-matcher = "0.3.1"
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use sonic_rs::{JsonValueTrait, Value};
|
||||||
|
|
||||||
pub fn de_unquote_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
|
pub fn de_unquote_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
|
let value: Option<Value> = Option::deserialize(deserializer)?;
|
||||||
|
|
||||||
match value {
|
if value.is_none() {
|
||||||
None => Ok(None),
|
return Ok(None);
|
||||||
Some(serde_json::Value::String(mut s)) => {
|
}
|
||||||
if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
|
|
||||||
s = s[1..s.len() - 1].to_string();
|
let value = value.unwrap();
|
||||||
}
|
|
||||||
s.parse::<i64>().map(Some).map_err(serde::de::Error::custom)
|
if let Some(mut s) = value.as_str().map(|s| s.to_string()) {
|
||||||
|
if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
|
||||||
|
s = s[1..s.len() - 1].to_string();
|
||||||
}
|
}
|
||||||
Some(serde_json::Value::Number(n)) => {
|
s.parse::<i64>().map(Some).map_err(serde::de::Error::custom)
|
||||||
// If it's a number, convert it to i64
|
} else if let Some(n) = value.as_i64() {
|
||||||
n.as_i64()
|
Ok(Some(n))
|
||||||
.ok_or_else(|| serde::de::Error::custom("number out of range"))
|
} else {
|
||||||
.map(Some)
|
Err(serde::de::Error::custom("expected a string or number"))
|
||||||
}
|
|
||||||
_ => Err(serde::de::Error::custom("expected a string or number")),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,28 +29,24 @@ pub fn de_unquote_usize<'de, D>(deserializer: D) -> Result<Option<usize>, D::Err
|
|||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
|
let value: Option<Value> = Option::deserialize(deserializer)?;
|
||||||
|
|
||||||
match value {
|
if value.is_none() {
|
||||||
None => Ok(None),
|
return Ok(None);
|
||||||
Some(serde_json::Value::String(mut s)) => {
|
}
|
||||||
if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
|
|
||||||
s = s[1..s.len() - 1].to_string();
|
let value = value.unwrap();
|
||||||
}
|
|
||||||
s.parse::<usize>()
|
if let Some(mut s) = value.as_str().map(|s| s.to_string()) {
|
||||||
.map(Some)
|
if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
|
||||||
.map_err(serde::de::Error::custom)
|
s = s[1..s.len() - 1].to_string();
|
||||||
}
|
|
||||||
Some(serde_json::Value::Number(n)) => {
|
|
||||||
// If it's a number, convert it to usize
|
|
||||||
n.as_u64()
|
|
||||||
.ok_or_else(|| serde::de::Error::custom("number out of range"))
|
|
||||||
.map(|v| v as usize)
|
|
||||||
.map(Some)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
dbg!(value);
|
|
||||||
Err(serde::de::Error::custom("expected a string or number"))
|
|
||||||
}
|
}
|
||||||
|
s.parse::<usize>()
|
||||||
|
.map(Some)
|
||||||
|
.map_err(serde::de::Error::custom)
|
||||||
|
} else if let Some(n) = value.as_u64() {
|
||||||
|
Ok(Some(n as usize))
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom("expected a string or number"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::fmt;
|
|||||||
use derive_deref::Deref;
|
use derive_deref::Deref;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use sonic_rs::{JsonContainerTrait, JsonValueTrait, Value};
|
||||||
|
|
||||||
#[derive(Debug, Deref, JsonSchema)]
|
#[derive(Debug, Deref, JsonSchema)]
|
||||||
pub struct MaybeIds(Vec<String>);
|
pub struct MaybeIds(Vec<String>);
|
||||||
@@ -27,26 +28,26 @@ impl<'de> Deserialize<'de> for MaybeIds {
|
|||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
match serde_json::Value::deserialize(deserializer)? {
|
let value = Value::deserialize(deserializer)?;
|
||||||
serde_json::Value::String(str) => {
|
|
||||||
if str.len() <= MAX_STRING_SIZE {
|
if let Some(str) = value.as_str() {
|
||||||
Ok(MaybeIds(sanitize_ids(
|
if str.len() <= MAX_STRING_SIZE {
|
||||||
str.split(",").map(|s| s.to_string()),
|
Ok(MaybeIds(sanitize_ids(
|
||||||
)))
|
str.split(",").map(|s| s.to_string()),
|
||||||
} else {
|
)))
|
||||||
Err(serde::de::Error::custom("Given parameter is too long"))
|
} else {
|
||||||
}
|
Err(serde::de::Error::custom("Given parameter is too long"))
|
||||||
}
|
}
|
||||||
serde_json::Value::Array(vec) => {
|
} else if let Some(vec) = value.as_array() {
|
||||||
if vec.len() <= MAX_VECS {
|
if vec.len() <= MAX_VECS {
|
||||||
Ok(MaybeIds(sanitize_ids(
|
Ok(MaybeIds(sanitize_ids(
|
||||||
vec.into_iter().map(|s| s.as_str().unwrap().to_string()),
|
vec.into_iter().map(|s| s.as_str().unwrap().to_string()),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Err(serde::de::Error::custom("Given parameter is too long"))
|
Err(serde::de::Error::custom("Given parameter is too long"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Err(serde::de::Error::custom("Bad ids format")),
|
} else {
|
||||||
|
Err(serde::de::Error::custom("Bad ids format"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub use format::Format;
|
|||||||
pub use index::Index;
|
pub use index::Index;
|
||||||
pub use output::{Output, Value};
|
pub use output::{Output, Value};
|
||||||
pub use pagination::{PaginatedIndexParam, PaginationParam};
|
pub use pagination::{PaginatedIndexParam, PaginationParam};
|
||||||
pub use params::{IdParam, Params, ParamsOpt};
|
pub use params::{Params, ParamsOpt};
|
||||||
use vecs::Vecs;
|
use vecs::Vecs;
|
||||||
|
|
||||||
use crate::vecs::{IndexToVec, MetricToVec};
|
use crate::vecs::{IndexToVec, MetricToVec};
|
||||||
@@ -222,16 +222,16 @@ impl<'a> Interface<'a> {
|
|||||||
&self.vecs.index_to_metric_to_vec
|
&self.vecs.index_to_metric_to_vec
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_metric_count(&self) -> usize {
|
pub fn distinct_metric_count(&self) -> usize {
|
||||||
self.vecs.metric_count
|
self.vecs.metric_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_index_count(&self) -> usize {
|
pub fn total_metric_count(&self) -> usize {
|
||||||
self.vecs.index_count
|
self.vecs.vec_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vec_count(&self) -> usize {
|
pub fn index_count(&self) -> usize {
|
||||||
self.vecs.vec_count
|
self.vecs.index_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_indexes(&self) -> &[&'static str] {
|
pub fn get_indexes(&self) -> &[&'static str] {
|
||||||
@@ -250,8 +250,8 @@ impl<'a> Interface<'a> {
|
|||||||
self.vecs.index_to_ids(paginated_index)
|
self.vecs.index_to_ids(paginated_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vecid_to_indexes(&self, id: String) -> Option<&Vec<&'static str>> {
|
pub fn metric_to_indexes(&self, metric: String) -> Option<&Vec<&'static str>> {
|
||||||
self.vecs.id_to_indexes(id)
|
self.vecs.metric_to_indexes(metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parser(&self) -> &Parser {
|
pub fn parser(&self) -> &Parser {
|
||||||
|
|||||||
@@ -106,8 +106,3 @@ impl ParamsOpt {
|
|||||||
self.format
|
self.format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, JsonSchema)]
|
|
||||||
pub struct IdParam {
|
|
||||||
pub id: String,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -131,8 +131,9 @@ impl<'a> Vecs<'a> {
|
|||||||
&self.metrics[start..end]
|
&self.metrics[start..end]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id_to_indexes(&self, id: String) -> Option<&Vec<&'static str>> {
|
pub fn metric_to_indexes(&self, metric: String) -> Option<&Vec<&'static str>> {
|
||||||
self.metric_to_indexes.get(id.as_str())
|
self.metric_to_indexes
|
||||||
|
.get(metric.replace("-", "_").as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_to_ids(
|
pub fn index_to_ids(
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ brk_rmcp = { version = "0.7.1", features = [
|
|||||||
"transport-streamable-http-server",
|
"transport-streamable-http-server",
|
||||||
] }
|
] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
schemars = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { version = "1.0.145", features = ["float_roundtrip"] }
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
ignored = ["serde_json"]
|
ignored = ["serde_json"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
use brk_interface::{IdParam, Interface, PaginatedIndexParam, PaginationParam, Params};
|
use brk_interface::{Interface, PaginatedIndexParam, PaginationParam, Params};
|
||||||
use brk_rmcp::{
|
use brk_rmcp::{
|
||||||
ErrorData as McpError, RoleServer, ServerHandler,
|
ErrorData as McpError, RoleServer, ServerHandler,
|
||||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||||
@@ -9,6 +9,8 @@ use brk_rmcp::{
|
|||||||
tool, tool_handler, tool_router,
|
tool, tool_handler, tool_router,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
pub mod route;
|
pub mod route;
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ Get the count of all existing indexes.
|
|||||||
async fn get_index_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_index_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
info!("mcp: get_index_count");
|
info!("mcp: get_index_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_index_count()).unwrap(),
|
Content::json(self.interface.index_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ Get the count of all existing metrics.
|
|||||||
async fn get_metric_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_metric_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
info!("mcp: get_metric_count");
|
info!("mcp: get_metric_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_metric_count()).unwrap(),
|
Content::json(self.interface.distinct_metric_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ Equals to the sum of supported Indexes of each vec id.
|
|||||||
async fn get_vec_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_vec_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
info!("mcp: get_vec_count");
|
info!("mcp: get_vec_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vec_count()).unwrap(),
|
Content::json(self.interface.total_metric_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ The list will be empty if the vec id isn't correct.
|
|||||||
) -> Result<CallToolResult, McpError> {
|
) -> Result<CallToolResult, McpError> {
|
||||||
info!("mcp: get_vecid_to_indexes");
|
info!("mcp: get_vecid_to_indexes");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vecid_to_indexes(param.id)).unwrap(),
|
Content::json(self.interface.metric_to_indexes(param.id)).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,3 +188,8 @@ An 'Index' (or indexes) is the timeframe of a dataset.
|
|||||||
Ok(self.get_info())
|
Ok(self.get_info())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct IdParam {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ jiff = { workspace = true }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
quick_cache = { workspace = true }
|
quick_cache = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
|
||||||
sonic-rs = { workspace = true }
|
sonic-rs = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Cursor, Read, Seek, SeekFrom},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json, Router,
|
||||||
|
extract::{Path, State},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
routing::get,
|
||||||
|
};
|
||||||
|
use bitcoin::{Address, Network, Transaction, consensus::Decodable};
|
||||||
|
use brk_parser::XORIndex;
|
||||||
|
use brk_structs::{
|
||||||
|
AddressBytesHash, AnyAddressDataIndexEnum, Bitcoin, OutputType, TxIndex, Txid, TxidPrefix,
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use sonic_rs::{Number, Value};
|
||||||
|
use vecdb::{AnyIterableVec, VecIterator};
|
||||||
|
|
||||||
|
use super::AppState;
|
||||||
|
|
||||||
|
pub trait ApiExplorerRoutes {
|
||||||
|
fn add_api_explorer_routes(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct TxResponse {
|
||||||
|
txid: Txid,
|
||||||
|
index: TxIndex,
|
||||||
|
tx: Transaction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiExplorerRoutes for Router<AppState> {
|
||||||
|
fn add_api_explorer_routes(self) -> Self {
|
||||||
|
self.route(
|
||||||
|
"/api/address/{address}",
|
||||||
|
get(
|
||||||
|
async |Path(address): Path<String>, state: State<AppState>| -> Response {
|
||||||
|
let Ok(address) = Address::from_str(&address) else {
|
||||||
|
return "Invalid address".into_response();
|
||||||
|
};
|
||||||
|
if !address.is_valid_for_network(Network::Bitcoin) {
|
||||||
|
return "Invalid address".into_response();
|
||||||
|
}
|
||||||
|
let address = address.assume_checked();
|
||||||
|
let interface = state.interface;
|
||||||
|
let indexer = interface.indexer();
|
||||||
|
let computer = interface.computer();
|
||||||
|
let stores = &indexer.stores;
|
||||||
|
let hash = AddressBytesHash::from(&address);
|
||||||
|
|
||||||
|
let Ok(Some(addri)) = stores
|
||||||
|
.addressbyteshash_to_typeindex
|
||||||
|
.get(&hash)
|
||||||
|
.map(|opt| opt.map(|cow| cow.into_owned()))
|
||||||
|
else {
|
||||||
|
return "Unknown address".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_type = OutputType::from(&address);
|
||||||
|
let stateful = &computer.stateful;
|
||||||
|
let price = computer.price.as_ref().map(|v| {
|
||||||
|
*v.timeindexes_to_price_close
|
||||||
|
.dateindex
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.into_owned()
|
||||||
|
});
|
||||||
|
|
||||||
|
let anyaddri = match output_type {
|
||||||
|
OutputType::P2PK33 => stateful
|
||||||
|
.p2pk33addressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2PK65 => stateful
|
||||||
|
.p2pk65addressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2PKH => stateful
|
||||||
|
.p2pkhaddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2SH => stateful
|
||||||
|
.p2shaddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2TR => stateful
|
||||||
|
.p2traddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2WPKH => stateful
|
||||||
|
.p2wpkhaddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2WSH => stateful
|
||||||
|
.p2wshaddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
OutputType::P2A => stateful
|
||||||
|
.p2aaddressindex_to_anyaddressindex
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(addri.into()),
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let addr_data = match anyaddri.to_enum() {
|
||||||
|
AnyAddressDataIndexEnum::Loaded(loadedi) => stateful
|
||||||
|
.loadedaddressindex_to_loadedaddressdata
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(loadedi),
|
||||||
|
AnyAddressDataIndexEnum::Empty(emptyi) => stateful
|
||||||
|
.emptyaddressindex_to_emptyaddressdata
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(emptyi)
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let amount = addr_data.amount();
|
||||||
|
Json(sonic_rs::json!({
|
||||||
|
"address": address,
|
||||||
|
"type": output_type,
|
||||||
|
"index": addri,
|
||||||
|
"chain_stats": {
|
||||||
|
"funded_txo_count": null,
|
||||||
|
"funded_txo_sum": addr_data.received,
|
||||||
|
"spent_txo_count": null,
|
||||||
|
"spent_txo_sum": addr_data.sent,
|
||||||
|
"utxo_count": addr_data.utxos,
|
||||||
|
"balance": amount,
|
||||||
|
"balance_usd": price.map_or(Value::new(), |p| {
|
||||||
|
Value::from(Number::from_f64(*(p * Bitcoin::from(amount))).unwrap())
|
||||||
|
}),
|
||||||
|
"realized_value": addr_data.realized_cap,
|
||||||
|
"tx_count": null,
|
||||||
|
"avg_cost_basis": addr_data.realized_price()
|
||||||
|
},
|
||||||
|
"mempool_stats": null
|
||||||
|
}))
|
||||||
|
.into_response()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/tx/{txid}",
|
||||||
|
get(
|
||||||
|
async |Path(txid): Path<String>, state: State<AppState>| -> Response {
|
||||||
|
let Ok(txid) = bitcoin::Txid::from_str(&txid) else {
|
||||||
|
return "Invalid txid".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let txid = Txid::from(txid);
|
||||||
|
let prefix = TxidPrefix::from(&txid);
|
||||||
|
let interface = state.interface;
|
||||||
|
let indexer = interface.indexer();
|
||||||
|
let Ok(Some(txindex)) = indexer
|
||||||
|
.stores
|
||||||
|
.txidprefix_to_txindex
|
||||||
|
.get(&prefix)
|
||||||
|
.map(|opt| opt.map(|cow| cow.into_owned()))
|
||||||
|
else {
|
||||||
|
return "Unknown transaction".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let txid = indexer
|
||||||
|
.vecs
|
||||||
|
.txindex_to_txid
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(txindex);
|
||||||
|
|
||||||
|
let parser = interface.parser();
|
||||||
|
let computer = interface.computer();
|
||||||
|
|
||||||
|
let position = computer
|
||||||
|
.blks
|
||||||
|
.txindex_to_position
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(txindex);
|
||||||
|
let len = indexer
|
||||||
|
.vecs
|
||||||
|
.txindex_to_total_size
|
||||||
|
.iter()
|
||||||
|
.unwrap_get_inner(txindex);
|
||||||
|
|
||||||
|
let blk_index_to_blk_path = parser.blk_index_to_blk_path();
|
||||||
|
|
||||||
|
let Some(blk_path) = blk_index_to_blk_path.get(&position.blk_index()) else {
|
||||||
|
return "Unknown blk index".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut xori = XORIndex::default();
|
||||||
|
xori.add_assign(position.offset() as usize);
|
||||||
|
|
||||||
|
let Ok(mut file) = File::open(blk_path) else {
|
||||||
|
return "Error opening blk file".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
if file
|
||||||
|
.seek(SeekFrom::Start(position.offset() as u64))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return "Error seeking position in blk file".into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer = vec![0u8; *len as usize];
|
||||||
|
if file.read_exact(&mut buffer).is_err() {
|
||||||
|
return "File fail read exact".into_response();
|
||||||
|
}
|
||||||
|
xori.bytes(&mut buffer, parser.xor_bytes());
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(buffer);
|
||||||
|
let Ok(tx) = Transaction::consensus_decode(&mut reader) else {
|
||||||
|
return "Error decoding transaction".into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = TxResponse {
|
||||||
|
txid,
|
||||||
|
index: txindex,
|
||||||
|
tx,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = sonic_rs::to_vec(&response).unwrap();
|
||||||
|
|
||||||
|
Response::builder()
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.body(bytes.into())
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
use axum::{
|
||||||
|
Json, Router,
|
||||||
|
extract::{Path, Query, State},
|
||||||
|
http::{HeaderMap, Uri},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
routing::get,
|
||||||
|
};
|
||||||
|
use brk_interface::{Index, PaginatedIndexParam, PaginationParam, Params, ParamsOpt};
|
||||||
|
|
||||||
|
use super::AppState;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
|
||||||
|
pub trait ApiMetricsRoutes {
|
||||||
|
fn add_api_metrics_routes(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TO_SEPARATOR: &str = "_to_";
|
||||||
|
|
||||||
|
impl ApiMetricsRoutes for Router<AppState> {
|
||||||
|
fn add_api_metrics_routes(self) -> Self {
|
||||||
|
self.route(
|
||||||
|
"/api/metrics/count",
|
||||||
|
get(async |State(app_state): State<AppState>| -> Response {
|
||||||
|
Json(sonic_rs::json!({
|
||||||
|
"distinct": app_state.interface.distinct_metric_count(),
|
||||||
|
"total": app_state.interface.total_metric_count(),
|
||||||
|
}))
|
||||||
|
.into_response()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/metrics/indexes",
|
||||||
|
get(async |State(app_state): State<AppState>| -> Response {
|
||||||
|
Json(app_state.interface.get_accepted_indexes()).into_response()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/vecs/metrics",
|
||||||
|
get(
|
||||||
|
async |State(app_state): State<AppState>,
|
||||||
|
Query(pagination): Query<PaginationParam>|
|
||||||
|
-> Response {
|
||||||
|
Json(app_state.interface.get_metrics(pagination)).into_response()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/vecs/index-to-metrics",
|
||||||
|
get(
|
||||||
|
async |State(app_state): State<AppState>,
|
||||||
|
Query(paginated_index): Query<PaginatedIndexParam>|
|
||||||
|
-> Response {
|
||||||
|
Json(app_state.interface.get_index_to_vecids(paginated_index)).into_response()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/metrics/{metric}",
|
||||||
|
get(
|
||||||
|
async |State(app_state): State<AppState>, Path(metric): Path<String>| -> Response {
|
||||||
|
// If not found do fuzzy search but here or in interface ?
|
||||||
|
Json(app_state.interface.metric_to_indexes(metric)).into_response()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/metrics/{metric}/{index}",
|
||||||
|
get(
|
||||||
|
async |State(app_state): State<AppState>,
|
||||||
|
Path((metric, index)): Path<(String, Index)>|
|
||||||
|
-> Response {
|
||||||
|
// If not found do fuzzy search but here or in interface ?
|
||||||
|
Json(
|
||||||
|
format!("{metric}/{index}"), // app_state
|
||||||
|
// .interface
|
||||||
|
// .metric_to_indexes(metric.replace("-", "_")),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// DEPRECATED
|
||||||
|
.route("/api/vecs/query", get(data::handler))
|
||||||
|
// DEPRECATED
|
||||||
|
.route(
|
||||||
|
"/api/vecs/{variant}",
|
||||||
|
get(
|
||||||
|
async |uri: Uri,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Path(variant): Path<String>,
|
||||||
|
Query(params_opt): Query<ParamsOpt>,
|
||||||
|
state: State<AppState>|
|
||||||
|
-> Response {
|
||||||
|
let variant = variant.replace("-", "_");
|
||||||
|
let mut split = variant.split(TO_SEPARATOR);
|
||||||
|
|
||||||
|
if let Ok(index) = Index::try_from(split.next().unwrap()) {
|
||||||
|
let params = Params::from((
|
||||||
|
(index, split.collect::<Vec<_>>().join(TO_SEPARATOR)),
|
||||||
|
params_opt,
|
||||||
|
));
|
||||||
|
data::handler(uri, headers, Query(params), state).await
|
||||||
|
} else {
|
||||||
|
"Bad variant".into_response()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,6 @@
|
|||||||
use std::{
|
use axum::{Router, response::Redirect, routing::get};
|
||||||
fs::File,
|
|
||||||
io::{Cursor, Read, Seek, SeekFrom},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use axum::{
|
use crate::api::{explorer::ApiExplorerRoutes, metrics::ApiMetricsRoutes};
|
||||||
Json, Router,
|
|
||||||
extract::{Path, Query, State},
|
|
||||||
http::{HeaderMap, Uri},
|
|
||||||
response::{IntoResponse, Redirect, Response},
|
|
||||||
routing::get,
|
|
||||||
};
|
|
||||||
use bitcoin::{Address, Network, Transaction, consensus::Decodable};
|
|
||||||
use brk_interface::{IdParam, Index, PaginatedIndexParam, PaginationParam, Params, ParamsOpt};
|
|
||||||
use brk_parser::XORIndex;
|
|
||||||
use brk_structs::{
|
|
||||||
AddressBytesHash, AnyAddressDataIndexEnum, Bitcoin, OutputType, TxIndex, Txid, TxidPrefix,
|
|
||||||
};
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Number;
|
|
||||||
use vecdb::{AnyIterableVec, VecIterator};
|
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
@@ -30,292 +11,17 @@ pub trait ApiRoutes {
|
|||||||
fn add_api_routes(self) -> Self;
|
fn add_api_routes(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TO_SEPARATOR: &str = "_to_";
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct TxResponse {
|
|
||||||
txid: Txid,
|
|
||||||
index: TxIndex,
|
|
||||||
tx: Transaction,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApiRoutes for Router<AppState> {
|
impl ApiRoutes for Router<AppState> {
|
||||||
fn add_api_routes(self) -> Self {
|
fn add_api_routes(self) -> Self {
|
||||||
self.route(
|
self.add_api_explorer_routes()
|
||||||
"/api/address/{address}",
|
.add_api_metrics_routes()
|
||||||
get(
|
.route(
|
||||||
async |Path(address): Path<String>, state: State<AppState>| -> Response {
|
"/api",
|
||||||
let Ok(address) = Address::from_str(&address) else {
|
get(|| async {
|
||||||
return "Invalid address".into_response();
|
Redirect::temporary(
|
||||||
};
|
"https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#api",
|
||||||
if !address.is_valid_for_network(Network::Bitcoin) {
|
)
|
||||||
return "Invalid address".into_response();
|
}),
|
||||||
}
|
)
|
||||||
let address = address.assume_checked();
|
|
||||||
let interface = state.interface;
|
|
||||||
let indexer = interface.indexer();
|
|
||||||
let computer = interface.computer();
|
|
||||||
let stores = &indexer.stores;
|
|
||||||
let hash = AddressBytesHash::from(&address);
|
|
||||||
|
|
||||||
let Ok(Some(addri)) = stores
|
|
||||||
.addressbyteshash_to_typeindex
|
|
||||||
.get(&hash)
|
|
||||||
.map(|opt| opt.map(|cow| cow.into_owned())) else {
|
|
||||||
return "Unknown address".into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let output_type = OutputType::from(&address);
|
|
||||||
let stateful = &computer.stateful;
|
|
||||||
let price = computer.price.as_ref().map(|v| {
|
|
||||||
*v.timeindexes_to_price_close
|
|
||||||
.dateindex
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.1
|
|
||||||
.into_owned()
|
|
||||||
});
|
|
||||||
|
|
||||||
let anyaddri = match output_type {
|
|
||||||
OutputType::P2PK33 => stateful
|
|
||||||
.p2pk33addressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2PK65 => stateful
|
|
||||||
.p2pk65addressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2PKH => stateful
|
|
||||||
.p2pkhaddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2SH => stateful
|
|
||||||
.p2shaddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2TR => stateful
|
|
||||||
.p2traddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2WPKH => stateful
|
|
||||||
.p2wpkhaddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2WSH => stateful
|
|
||||||
.p2wshaddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
OutputType::P2A => stateful
|
|
||||||
.p2aaddressindex_to_anyaddressindex
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(addri.into()),
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let addr_data = match anyaddri.to_enum() {
|
|
||||||
AnyAddressDataIndexEnum::Loaded(loadedi) => stateful
|
|
||||||
.loadedaddressindex_to_loadedaddressdata
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(loadedi),
|
|
||||||
AnyAddressDataIndexEnum::Empty(emptyi) => stateful
|
|
||||||
.emptyaddressindex_to_emptyaddressdata
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(emptyi)
|
|
||||||
.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let amount = addr_data.amount();
|
|
||||||
Json(serde_json::json!({
|
|
||||||
"address": address,
|
|
||||||
"type": output_type,
|
|
||||||
"index": addri,
|
|
||||||
"chain_stats": {
|
|
||||||
"funded_txo_count": serde_json::Value::Null,
|
|
||||||
"funded_txo_sum": addr_data.received,
|
|
||||||
"spent_txo_count": serde_json::Value::Null,
|
|
||||||
"spent_txo_sum": addr_data.sent,
|
|
||||||
"utxo_count": addr_data.utxos,
|
|
||||||
"balance": amount,
|
|
||||||
"balance_usd": price.map_or(serde_json::Value::Null, |p| serde_json::Value::Number(Number::from_f64( *(p * Bitcoin::from(amount))).unwrap())),
|
|
||||||
"realized_value": addr_data.realized_cap,
|
|
||||||
"tx_count": serde_json::Value::Null,
|
|
||||||
"avg_cost_basis": addr_data.realized_price()
|
|
||||||
},
|
|
||||||
"mempool_stats": serde_json::Value::Null
|
|
||||||
}))
|
|
||||||
.into_response()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/tx/{txid}",
|
|
||||||
get(
|
|
||||||
async |Path(txid): Path<String>, state: State<AppState>| -> Response {
|
|
||||||
let Ok(txid) = bitcoin::Txid::from_str(&txid) else {
|
|
||||||
return "Invalid txid".into_response()
|
|
||||||
};
|
|
||||||
|
|
||||||
let txid = Txid::from(txid);
|
|
||||||
let prefix = TxidPrefix::from(&txid);
|
|
||||||
let interface = state.interface;
|
|
||||||
let indexer = interface.indexer();
|
|
||||||
let Ok(Some(txindex)) = indexer
|
|
||||||
.stores
|
|
||||||
.txidprefix_to_txindex
|
|
||||||
.get(&prefix)
|
|
||||||
.map(|opt| opt.map(|cow| cow.into_owned())) else {
|
|
||||||
return "Unknown transaction".into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let txid = indexer
|
|
||||||
.vecs
|
|
||||||
.txindex_to_txid
|
|
||||||
.iter()
|
|
||||||
.unwrap_get_inner(txindex);
|
|
||||||
|
|
||||||
let parser = interface.parser();
|
|
||||||
let computer = interface.computer();
|
|
||||||
|
|
||||||
let position = computer.blks.txindex_to_position.iter().unwrap_get_inner(txindex);
|
|
||||||
let len = indexer.vecs.txindex_to_total_size.iter().unwrap_get_inner(txindex);
|
|
||||||
|
|
||||||
let blk_index_to_blk_path = parser.blk_index_to_blk_path();
|
|
||||||
|
|
||||||
let Some(blk_path) = blk_index_to_blk_path.get(&position.blk_index()) else {
|
|
||||||
return "Unknown blk index".into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut xori = XORIndex::default();
|
|
||||||
xori.add_assign(position.offset() as usize);
|
|
||||||
|
|
||||||
let Ok(mut file) = File::open(blk_path) else {
|
|
||||||
return "Error opening blk file".into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
if file.seek(SeekFrom::Start(position.offset() as u64)).is_err() {
|
|
||||||
return "Error seeking position in blk file".into_response();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer = vec![0u8; *len as usize];
|
|
||||||
if file.read_exact(&mut buffer).is_err() {
|
|
||||||
return "File fail read exact".into_response();
|
|
||||||
}
|
|
||||||
xori.bytes(&mut buffer, parser.xor_bytes());
|
|
||||||
|
|
||||||
let mut reader = Cursor::new(buffer);
|
|
||||||
let Ok(tx) = Transaction::consensus_decode(&mut reader) else {
|
|
||||||
return "Error decoding transaction".into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = TxResponse { txid, index: txindex, tx };
|
|
||||||
|
|
||||||
let bytes = sonic_rs::to_vec(&response).unwrap();
|
|
||||||
|
|
||||||
Response::builder()
|
|
||||||
.header("content-type", "application/json")
|
|
||||||
.body(bytes.into())
|
|
||||||
.unwrap()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/index-count",
|
|
||||||
get(async |State(app_state): State<AppState>| -> Response {
|
|
||||||
Json(app_state.interface.get_index_count()).into_response()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/metric-count",
|
|
||||||
get(async |State(app_state): State<AppState>| -> Response {
|
|
||||||
Json(app_state.interface.get_metric_count()).into_response()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/vec-count",
|
|
||||||
get(async |State(app_state): State<AppState>| -> Response {
|
|
||||||
Json(app_state.interface.get_vec_count()).into_response()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/indexes",
|
|
||||||
get(async |State(app_state): State<AppState>| -> Response {
|
|
||||||
Json(app_state.interface.get_indexes()).into_response()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/accepted-indexes",
|
|
||||||
get(async |State(app_state): State<AppState>| -> Response {
|
|
||||||
Json(app_state.interface.get_accepted_indexes()).into_response()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/metrics",
|
|
||||||
get(
|
|
||||||
async |State(app_state): State<AppState>,
|
|
||||||
Query(pagination): Query<PaginationParam>|
|
|
||||||
-> Response {
|
|
||||||
Json(app_state.interface.get_metrics(pagination)).into_response()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/index-to-metrics",
|
|
||||||
get(
|
|
||||||
async |State(app_state): State<AppState>,
|
|
||||||
Query(paginated_index): Query<PaginatedIndexParam>|
|
|
||||||
-> Response {
|
|
||||||
Json(app_state.interface.get_index_to_vecids(paginated_index)).into_response()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/vecs/metric-to-indexes",
|
|
||||||
get(
|
|
||||||
async |State(app_state): State<AppState>,
|
|
||||||
Query(param): Query<IdParam>|
|
|
||||||
-> Response {
|
|
||||||
Json(app_state.interface.get_vecid_to_indexes(param.id)).into_response()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// DEPRECATED
|
|
||||||
.route("/api/vecs/query", get(metrics::handler))
|
|
||||||
// DEPRECATED
|
|
||||||
.route(
|
|
||||||
"/api/vecs/{variant}",
|
|
||||||
get(
|
|
||||||
async |uri: Uri,
|
|
||||||
headers: HeaderMap,
|
|
||||||
Path(variant): Path<String>,
|
|
||||||
Query(params_opt): Query<ParamsOpt>,
|
|
||||||
state: State<AppState>|
|
|
||||||
-> Response {
|
|
||||||
let variant = variant.replace("-", "_");
|
|
||||||
let mut split = variant.split(TO_SEPARATOR);
|
|
||||||
|
|
||||||
if let Ok(index) = Index::try_from(split.next().unwrap()) {
|
|
||||||
let params = Params::from((
|
|
||||||
(index, split.collect::<Vec<_>>().join(TO_SEPARATOR)),
|
|
||||||
params_opt,
|
|
||||||
));
|
|
||||||
metrics::handler(uri, headers, Query(params), state).await
|
|
||||||
} else {
|
|
||||||
"Bad variant".into_response()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api",
|
|
||||||
get(|| async {
|
|
||||||
Redirect::temporary(
|
|
||||||
"https://github.com/bitcoinresearchkit/brk/tree/main/crates/brk_server#api",
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ impl Server {
|
|||||||
.route("/version", get(Json(VERSION)))
|
.route("/version", get(Json(VERSION)))
|
||||||
.route(
|
.route(
|
||||||
"/health",
|
"/health",
|
||||||
get(Json(serde_json::json!({
|
get(Json(sonic_rs::json!({
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
"service": "brk-server",
|
"service": "brk-server",
|
||||||
"timestamp": jiff::Timestamp::now().to_string()
|
"timestamp": jiff::Timestamp::now().to_string()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
LICENSE
|
LICENSE
|
||||||
*/**/*.json
|
**/*.*.*/*.json
|
||||||
*webcomponent*
|
*webcomponent*
|
||||||
README*.md
|
README*.md
|
||||||
cli*
|
cli*
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @import { IndexName } from "./generated/metrics"
|
||||||
|
* @import { Metric } from './metrics'
|
||||||
|
*
|
||||||
|
* @typedef {ReturnType<createClient>} BRK
|
||||||
|
*/
|
||||||
|
|
||||||
|
// client.metrics.catalog.a.b.c() -> string (uncompress inside)
|
||||||
|
|
||||||
import { runWhenIdle } from "./idle";
|
import { runWhenIdle } from "./idle";
|
||||||
|
|
||||||
import { POOL_ID_TO_POOL_NAME } from "./generated/pools";
|
import { POOL_ID_TO_POOL_NAME } from "./generated/pools";
|
||||||
import { hasMetric, metricToIndexes } from "./metrics";
|
import { INDEXES } from "./generated/metrics";
|
||||||
|
import { hasMetric, getIndexesFromMetric } from "./metrics";
|
||||||
/**
|
import { VERSION } from "./generated/version";
|
||||||
* @typedef {ReturnType<createClient>} BRKClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
const CACHE_NAME = "__BRK_CLIENT__";
|
const CACHE_NAME = "__BRK_CLIENT__";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} [origin] - defaults to /
|
* @param {string} origin
|
||||||
*/
|
*/
|
||||||
export function createClient(origin = "/") {
|
export function createClient(origin) {
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {(value: T) => void} callback
|
* @param {(value: T) => void} callback
|
||||||
@@ -80,7 +87,7 @@ export function createClient(origin = "/") {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Metric} metric
|
* @param {Metric} metric
|
||||||
* @param {Index} index
|
* @param {IndexName} index
|
||||||
* @param {number} [from]
|
* @param {number} [from]
|
||||||
* @param {number} [to]
|
* @param {number} [to]
|
||||||
*/
|
*/
|
||||||
@@ -102,7 +109,7 @@ export function createClient(origin = "/") {
|
|||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {(v: T[]) => void} callback
|
* @param {(v: T[]) => void} callback
|
||||||
* @param {Index} index
|
* @param {IndexName} index
|
||||||
* @param {Metric} metric
|
* @param {Metric} metric
|
||||||
* @param {number} [from]
|
* @param {number} [from]
|
||||||
* @param {number} [to]
|
* @param {number} [to]
|
||||||
@@ -112,9 +119,12 @@ export function createClient(origin = "/") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
VERSION,
|
||||||
POOL_ID_TO_POOL_NAME,
|
POOL_ID_TO_POOL_NAME,
|
||||||
|
INDEXES,
|
||||||
|
|
||||||
hasMetric,
|
hasMetric,
|
||||||
metricToIndexes,
|
getIndexesFromMetric,
|
||||||
|
|
||||||
genMetricURL,
|
genMetricURL,
|
||||||
fetchMetric,
|
fetchMetric,
|
||||||
@@ -3,6 +3,11 @@ import {
|
|||||||
COMPRESSED_METRIC_TO_INDEXES,
|
COMPRESSED_METRIC_TO_INDEXES,
|
||||||
} from "./generated/metrics";
|
} from "./generated/metrics";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {typeof import("./generated/metrics")["COMPRESSED_METRIC_TO_INDEXES"]} MetricToIndexes
|
||||||
|
* @typedef {string} Metric
|
||||||
|
*/
|
||||||
|
|
||||||
/** @type {Record<string, number>} */
|
/** @type {Record<string, number>} */
|
||||||
const WORD_TO_INDEX = {};
|
const WORD_TO_INDEX = {};
|
||||||
|
|
||||||
@@ -13,7 +18,7 @@ INDEX_TO_WORD.forEach((word, index) => {
|
|||||||
/**
|
/**
|
||||||
* @param {Metric} metric
|
* @param {Metric} metric
|
||||||
*/
|
*/
|
||||||
export function metricToIndexes(metric) {
|
export function getIndexesFromMetric(metric) {
|
||||||
return COMPRESSED_METRIC_TO_INDEXES[compressMetric(metric)];
|
return COMPRESSED_METRIC_TO_INDEXES[compressMetric(metric)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* @import { Signal, Signals } from "../brk-signals/index";
|
* @import { Signal, Signals } from "../brk-signals/index";
|
||||||
|
* @import { BRK } from '../brk-client/index'
|
||||||
|
* @import { Metric } from '../brk-client/metrics'
|
||||||
|
* @import { IndexName } from '../brk-client/generated/metrics'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ReturnType<createResources>} BRKResources
|
* @typedef {ReturnType<typeof createResources>} Resources
|
||||||
* @typedef {ReturnType<BRKResources["metrics"]["getOrCreate"]>} BRKMetricResource
|
* @typedef {ReturnType<Resources["metrics"]["getOrCreate"]>} MetricResource
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BRKClient} client
|
* @param {BRK} brk
|
||||||
* @param {Signals} signals
|
* @param {Signals} signals
|
||||||
*/
|
*/
|
||||||
export function createResources(client, signals) {
|
export function createResources(brk, signals) {
|
||||||
const owner = signals.getOwner();
|
const owner = signals.getOwner();
|
||||||
|
|
||||||
const defaultFrom = -10_000;
|
const defaultFrom = -10_000;
|
||||||
@@ -29,10 +32,10 @@ export function createResources(client, signals) {
|
|||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {Metric} metric
|
* @param {Metric} metric
|
||||||
* @param {Index} index
|
* @param {IndexName} index
|
||||||
*/
|
*/
|
||||||
function createMetricResource(metric, index) {
|
function createMetricResource(metric, index) {
|
||||||
if (client.hasMetric(metric)) {
|
if (!brk.hasMetric(metric)) {
|
||||||
throw Error(`${metric} is invalid`);
|
throw Error(`${metric} is invalid`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ export function createResources(client, signals) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: client.genMetricURL(metric, index, defaultFrom),
|
url: brk.genMetricURL(metric, index, defaultFrom),
|
||||||
fetched: fetchedRecord,
|
fetched: fetchedRecord,
|
||||||
/**
|
/**
|
||||||
* Defaults
|
* Defaults
|
||||||
@@ -81,7 +84,7 @@ export function createResources(client, signals) {
|
|||||||
}
|
}
|
||||||
fetched.loading = true;
|
fetched.loading = true;
|
||||||
const res = /** @type {T[] | null} */ (
|
const res = /** @type {T[] | null} */ (
|
||||||
await client.fetchMetric(
|
await brk.fetchMetric(
|
||||||
(data) => {
|
(data) => {
|
||||||
if (data.length || !fetched.data()) {
|
if (data.length || !fetched.data()) {
|
||||||
fetched.data.set(data);
|
fetched.data.set(data);
|
||||||
@@ -108,7 +111,7 @@ export function createResources(client, signals) {
|
|||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {Metric} metric
|
* @param {Metric} metric
|
||||||
* @param {Index} index
|
* @param {IndexName} index
|
||||||
*/
|
*/
|
||||||
getOrCreate(metric, index) {
|
getOrCreate(metric, index) {
|
||||||
const key = `${metric}/${index}`;
|
const key = `${metric}/${index}`;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"outDir": "/tmp/brk",
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"outDir": "/tmp/brk",
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
Vendored
Vendored
Vendored
Vendored
@@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* @import { Index } from "./generated/metrics"
|
|
||||||
* @import { BRKClient } from "./index"
|
|
||||||
*
|
|
||||||
* @typedef {typeof import("./generated/metrics")["COMPRESSED_METRIC_TO_INDEXES"]} MetricToIndexes
|
|
||||||
* @typedef {string} Metric
|
|
||||||
* @typedef {[number, number, number, number]} OHLCTuple
|
|
||||||
*/
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/**
|
|
||||||
* @import { BRKResources, BRKMetricResource } from "./index"
|
|
||||||
*/
|
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
**/scripts/packages
|
**/scripts/modules
|
||||||
|
|||||||
@@ -991,7 +991,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chart > legend,
|
.chart > legend,
|
||||||
#charts > fieldset {
|
#chart > fieldset {
|
||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1161,7 +1161,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#charts {
|
#chart {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -1824,7 +1824,7 @@
|
|||||||
</main>
|
</main>
|
||||||
<aside id="aside">
|
<aside id="aside">
|
||||||
<div id="explorer" hidden></div>
|
<div id="explorer" hidden></div>
|
||||||
<div id="charts" hidden></div>
|
<div id="chart" hidden></div>
|
||||||
<div id="table" hidden></div>
|
<div id="table" hidden></div>
|
||||||
<div id="simulation" hidden></div>
|
<div id="simulation" hidden></div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -6,5 +6,5 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"exclude": ["assets", "scripts/packages", "scripts/bridge"]
|
"exclude": ["assets", "scripts/modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../packages/lightweight-charts/5.0.8/dist/typings' */
|
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../modules/lightweight-charts/5.0.8/dist/typings' */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createChart,
|
createChart,
|
||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
HistogramSeries,
|
HistogramSeries,
|
||||||
LineSeries,
|
LineSeries,
|
||||||
BaselineSeries,
|
BaselineSeries,
|
||||||
// } from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
// } from "../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
||||||
} from "../../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
} from "../../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createHorizontalChoiceField,
|
createHorizontalChoiceField,
|
||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
import { createOklchToRGBA } from "./oklch";
|
import { createOklchToRGBA } from "./oklch";
|
||||||
import { throttle } from "../timing";
|
import { throttle } from "../timing";
|
||||||
import { serdeBool } from "../serde";
|
import { serdeBool } from "../serde";
|
||||||
|
import { stringToId } from "../format";
|
||||||
|
import { style } from "../elements";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Valued
|
* @typedef {Object} Valued
|
||||||
@@ -50,7 +52,7 @@ import { serdeBool } from "../serde";
|
|||||||
* @typedef {_BaselineData<number>} BaselineData
|
* @typedef {_BaselineData<number>} BaselineData
|
||||||
* @typedef {_HistogramData<number>} HistogramData
|
* @typedef {_HistogramData<number>} HistogramData
|
||||||
*
|
*
|
||||||
* @typedef {function({ iseries: ISeries; unit: Unit; index: Index }): void} SetDataCallback
|
* @typedef {function({ iseries: ISeries; unit: Unit; index: IndexName }): void} SetDataCallback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const oklchToRGBA = createOklchToRGBA();
|
const oklchToRGBA = createOklchToRGBA();
|
||||||
@@ -63,10 +65,8 @@ const lineWidth = /** @type {any} */ (1.5);
|
|||||||
* @param {HTMLElement} args.parent
|
* @param {HTMLElement} args.parent
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Colors} args.colors
|
* @param {Colors} args.colors
|
||||||
* @param {Utilities} args.utils
|
* @param {Resources} args.resources
|
||||||
* @param {Elements} args.elements
|
* @param {Accessor<IndexName>} args.index
|
||||||
* @param {VecsResources} args.vecsResources
|
|
||||||
* @param {Accessor<Index>} args.index
|
|
||||||
* @param {((unknownTimeScaleCallback: VoidFunction) => void)} [args.timeScaleSetCallback]
|
* @param {((unknownTimeScaleCallback: VoidFunction) => void)} [args.timeScaleSetCallback]
|
||||||
* @param {true} [args.fitContent]
|
* @param {true} [args.fitContent]
|
||||||
* @param {{unit: Unit; blueprints: AnySeriesBlueprint[]}[]} [args.config]
|
* @param {{unit: Unit; blueprints: AnySeriesBlueprint[]}[]} [args.config]
|
||||||
@@ -75,11 +75,9 @@ function createChartElement({
|
|||||||
parent,
|
parent,
|
||||||
signals,
|
signals,
|
||||||
colors,
|
colors,
|
||||||
utils,
|
|
||||||
elements,
|
|
||||||
id: chartId,
|
id: chartId,
|
||||||
index,
|
index,
|
||||||
vecsResources,
|
resources,
|
||||||
timeScaleSetCallback,
|
timeScaleSetCallback,
|
||||||
fitContent,
|
fitContent,
|
||||||
config,
|
config,
|
||||||
@@ -88,20 +86,14 @@ function createChartElement({
|
|||||||
div.classList.add("chart");
|
div.classList.add("chart");
|
||||||
parent.append(div);
|
parent.append(div);
|
||||||
|
|
||||||
const legendTop = createLegend({
|
const legendTop = createLegend(signals);
|
||||||
utils,
|
|
||||||
signals,
|
|
||||||
});
|
|
||||||
div.append(legendTop.element);
|
div.append(legendTop.element);
|
||||||
|
|
||||||
const chartDiv = window.document.createElement("div");
|
const chartDiv = window.document.createElement("div");
|
||||||
chartDiv.classList.add("lightweight-chart");
|
chartDiv.classList.add("lightweight-chart");
|
||||||
div.append(chartDiv);
|
div.append(chartDiv);
|
||||||
|
|
||||||
const legendBottom = createLegend({
|
const legendBottom = createLegend(signals);
|
||||||
utils,
|
|
||||||
signals,
|
|
||||||
});
|
|
||||||
div.append(legendBottom.element);
|
div.append(legendBottom.element);
|
||||||
|
|
||||||
/** @type {IChartApi} */
|
/** @type {IChartApi} */
|
||||||
@@ -110,7 +102,7 @@ function createChartElement({
|
|||||||
/** @satisfies {DeepPartial<ChartOptions>} */ ({
|
/** @satisfies {DeepPartial<ChartOptions>} */ ({
|
||||||
autoSize: true,
|
autoSize: true,
|
||||||
layout: {
|
layout: {
|
||||||
fontFamily: elements.style.fontFamily,
|
fontFamily: style.fontFamily,
|
||||||
background: { color: "transparent" },
|
background: { color: "transparent" },
|
||||||
attributionLogo: false,
|
attributionLogo: false,
|
||||||
colorSpace: "display-p3",
|
colorSpace: "display-p3",
|
||||||
@@ -185,24 +177,24 @@ function createChartElement({
|
|||||||
|
|
||||||
signals.createEffect(index, (index) => {
|
signals.createEffect(index, (index) => {
|
||||||
const minBarSpacing =
|
const minBarSpacing =
|
||||||
index === /** @satisfies {MonthIndex} */ (7)
|
index === "monthindex"
|
||||||
? 1
|
? 1
|
||||||
: index === /** @satisfies {QuarterIndex} */ (19)
|
: index === "quarterindex"
|
||||||
? 2
|
? 2
|
||||||
: index === /** @satisfies {SemesterIndex} */ (20)
|
: index === "semesterindex"
|
||||||
? 3
|
? 3
|
||||||
: index === /** @satisfies {YearIndex} */ (24)
|
: index === "yearindex"
|
||||||
? 6
|
? 6
|
||||||
: index === /** @satisfies {DecadeIndex} */ (1)
|
: index === "decadeindex"
|
||||||
? 60
|
? 60
|
||||||
: 0.5;
|
: 0.5;
|
||||||
|
|
||||||
ichart.applyOptions({
|
ichart.applyOptions({
|
||||||
timeScale: {
|
timeScale: {
|
||||||
timeVisible:
|
timeVisible:
|
||||||
index === /** @satisfies {Height} */ (5) ||
|
index === "height" ||
|
||||||
index === /** @satisfies {DifficultyEpoch} */ (2) ||
|
index === "difficultyepoch" ||
|
||||||
index === /** @satisfies {HalvingEpoch} */ (4),
|
index === "halvingepoch",
|
||||||
...(!fitContent
|
...(!fitContent
|
||||||
? {
|
? {
|
||||||
minBarSpacing,
|
minBarSpacing,
|
||||||
@@ -212,7 +204,7 @@ function createChartElement({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeResources = /** @type {Set<VecResource>} */ (new Set());
|
const activeResources = /** @type {Set<MetricResource>} */ (new Set());
|
||||||
ichart.subscribeCrosshairMove(
|
ichart.subscribeCrosshairMove(
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
activeResources.forEach((v) => {
|
activeResources.forEach((v) => {
|
||||||
@@ -292,7 +284,7 @@ function createChartElement({
|
|||||||
createChild(pane) {
|
createChild(pane) {
|
||||||
const { field, selected } = createHorizontalChoiceField({
|
const { field, selected } = createHorizontalChoiceField({
|
||||||
choices: /** @type {const} */ (["lin", "log"]),
|
choices: /** @type {const} */ (["lin", "log"]),
|
||||||
id: utils.stringToId(`${id} ${paneIndex} ${unit}`),
|
id: stringToId(`${id} ${paneIndex} ${unit}`),
|
||||||
defaultValue:
|
defaultValue:
|
||||||
unit === "usd" && seriesType !== "Baseline" ? "log" : "lin",
|
unit === "usd" && seriesType !== "Baseline" ? "log" : "lin",
|
||||||
key: `${id}-price-scale-${paneIndex}`,
|
key: `${id}-price-scale-${paneIndex}`,
|
||||||
@@ -340,7 +332,7 @@ function createChartElement({
|
|||||||
data,
|
data,
|
||||||
}) {
|
}) {
|
||||||
return signals.createRoot((dispose) => {
|
return signals.createRoot((dispose) => {
|
||||||
const id = `${utils.stringToId(name)}-${paneIndex}`;
|
const id = `${stringToId(name)}-${paneIndex}`;
|
||||||
|
|
||||||
const active = signals.createSignal(defaultActive ?? true, {
|
const active = signals.createSignal(defaultActive ?? true, {
|
||||||
save: {
|
save: {
|
||||||
@@ -361,7 +353,7 @@ function createChartElement({
|
|||||||
|
|
||||||
iseries.setSeriesOrder(order);
|
iseries.setSeriesOrder(order);
|
||||||
|
|
||||||
/** @type {VecResource | undefined} */
|
/** @type {MetricResource | undefined} */
|
||||||
let _valuesResource;
|
let _valuesResource;
|
||||||
|
|
||||||
/** @type {Series} */
|
/** @type {Series} */
|
||||||
@@ -383,15 +375,13 @@ function createChartElement({
|
|||||||
|
|
||||||
if (metric) {
|
if (metric) {
|
||||||
signals.createEffect(index, (index) => {
|
signals.createEffect(index, (index) => {
|
||||||
const timeResource = vecsResources.getOrCreate(
|
const timeResource = resources.metrics.getOrCreate(
|
||||||
|
index === "height" ? "timestamp_fixed" : "timestamp",
|
||||||
index,
|
index,
|
||||||
index === /** @satisfies {Height} */ (5)
|
|
||||||
? "timestamp_fixed"
|
|
||||||
: "timestamp",
|
|
||||||
);
|
);
|
||||||
timeResource.fetch();
|
timeResource.fetch();
|
||||||
|
|
||||||
const valuesResource = vecsResources.getOrCreate(index, metric);
|
const valuesResource = resources.metrics.getOrCreate(metric, index);
|
||||||
_valuesResource = valuesResource;
|
_valuesResource = valuesResource;
|
||||||
|
|
||||||
series.url.set(() => valuesResource.url);
|
series.url.set(() => valuesResource.url);
|
||||||
@@ -401,11 +391,11 @@ function createChartElement({
|
|||||||
valuesResource.fetch();
|
valuesResource.fetch();
|
||||||
activeResources.add(valuesResource);
|
activeResources.add(valuesResource);
|
||||||
|
|
||||||
const fetchedKey = vecsResources.defaultFetchedKey;
|
const fetchedKey = resources.metrics.genKey();
|
||||||
signals.createEffect(
|
signals.createEffect(
|
||||||
() => ({
|
() => ({
|
||||||
_indexes: timeResource.fetched().get(fetchedKey)?.vec(),
|
_indexes: timeResource.fetched().get(fetchedKey)?.data(),
|
||||||
values: valuesResource.fetched().get(fetchedKey)?.vec(),
|
values: valuesResource.fetched().get(fetchedKey)?.data(),
|
||||||
}),
|
}),
|
||||||
({ _indexes, values }) => {
|
({ _indexes, values }) => {
|
||||||
if (!_indexes?.length || !values?.length) return;
|
if (!_indexes?.length || !values?.length) return;
|
||||||
@@ -477,10 +467,10 @@ function createChartElement({
|
|||||||
|
|
||||||
timeScaleSetCallback?.(() => {
|
timeScaleSetCallback?.(() => {
|
||||||
if (
|
if (
|
||||||
index === /** @satisfies {QuarterIndex} */ (19) ||
|
index === "quarterindex" ||
|
||||||
index === /** @satisfies {SemesterIndex} */ (20) ||
|
index === "semesterindex" ||
|
||||||
index === /** @satisfies {YearIndex} */ (24) ||
|
index === "yearindex" ||
|
||||||
index === /** @satisfies {DecadeIndex} */ (1)
|
index === "decadeindex"
|
||||||
) {
|
) {
|
||||||
ichart.timeScale().setVisibleLogicalRange({
|
ichart.timeScale().setVisibleLogicalRange({
|
||||||
from: -1,
|
from: -1,
|
||||||
@@ -836,11 +826,9 @@ function createChartElement({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Signals} signals
|
||||||
* @param {Signals} args.signals
|
|
||||||
* @param {Utilities} args.utils
|
|
||||||
*/
|
*/
|
||||||
function createLegend({ signals, utils }) {
|
function createLegend(signals) {
|
||||||
const element = window.document.createElement("legend");
|
const element = window.document.createElement("legend");
|
||||||
|
|
||||||
const hovered = signals.createSignal(/** @type {Series | null} */ (null));
|
const hovered = signals.createSignal(/** @type {Series | null} */ (null));
|
||||||
@@ -874,8 +862,8 @@ function createLegend({ signals, utils }) {
|
|||||||
legends[order] = div;
|
legends[order] = div;
|
||||||
|
|
||||||
const { input, label } = createLabeledInput({
|
const { input, label } = createLabeledInput({
|
||||||
inputId: utils.stringToId(`legend-${series.id}`),
|
inputId: stringToId(`legend-${series.id}`),
|
||||||
inputName: utils.stringToId(`selected-${series.id}`),
|
inputName: stringToId(`selected-${series.id}`),
|
||||||
inputValue: "value",
|
inputValue: "value",
|
||||||
title: "Click to toggle",
|
title: "Click to toggle",
|
||||||
inputChecked: series.active(),
|
inputChecked: series.active(),
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import { getElementById } from "./dom";
|
import { getElementById } from "./dom";
|
||||||
|
|
||||||
export default {
|
export const style = getComputedStyle(window.document.documentElement);
|
||||||
head: window.document.getElementsByTagName("head")[0],
|
|
||||||
body: window.document.body,
|
export const headElement = window.document.getElementsByTagName("head")[0];
|
||||||
main: getElementById("main"),
|
export const bodyElement = window.document.body;
|
||||||
aside: getElementById("aside"),
|
|
||||||
asideLabel: getElementById("aside-selector-label"),
|
export const mainElement = getElementById("main");
|
||||||
navLabel: getElementById(`nav-selector-label`),
|
export const asideElement = getElementById("aside");
|
||||||
searchLabel: getElementById(`search-selector-label`),
|
export const searchElement = getElementById("search");
|
||||||
search: getElementById("search"),
|
export const navElement = getElementById("nav");
|
||||||
nav: getElementById("nav"),
|
export const chartElement = getElementById("chart");
|
||||||
searchInput: /** @type {HTMLInputElement} */ (getElementById("search-input")),
|
export const tableElement = getElementById("table");
|
||||||
searchResults: getElementById("search-results"),
|
export const explorerElement = getElementById("explorer");
|
||||||
selectors: getElementById("frame-selectors"),
|
export const simulationElement = getElementById("simulation");
|
||||||
style: getComputedStyle(window.document.documentElement),
|
|
||||||
charts: getElementById("charts"),
|
export const asideLabelElement = getElementById("aside-selector-label");
|
||||||
table: getElementById("table"),
|
export const navLabelElement = getElementById(`nav-selector-label`);
|
||||||
explorer: getElementById("explorer"),
|
export const searchLabelElement = getElementById(`search-selector-label`);
|
||||||
simulation: getElementById("simulation"),
|
export const searchInput = /** @type {HTMLInputElement} */ (
|
||||||
};
|
getElementById("search-input")
|
||||||
|
);
|
||||||
|
export const searchResultsElement = getElementById("search-results");
|
||||||
|
export const frameSelectorsElement = getElementById("frame-selectors");
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
const localhost = window.location.hostname === "localhost";
|
export const localhost = window.location.hostname === "localhost";
|
||||||
const standalone =
|
export const standalone =
|
||||||
"standalone" in window.navigator && !!window.navigator.standalone;
|
"standalone" in window.navigator && !!window.navigator.standalone;
|
||||||
const userAgent = navigator.userAgent.toLowerCase();
|
export const userAgent = navigator.userAgent.toLowerCase();
|
||||||
const isChrome = userAgent.includes("chrome");
|
export const isChrome = userAgent.includes("chrome");
|
||||||
const safari = userAgent.includes("safari");
|
export const safari = userAgent.includes("safari");
|
||||||
const safariOnly = safari && !isChrome;
|
export const safariOnly = safari && !isChrome;
|
||||||
const macOS = userAgent.includes("mac os");
|
export const macOS = userAgent.includes("mac os");
|
||||||
const iphone = userAgent.includes("iphone");
|
export const iphone = userAgent.includes("iphone");
|
||||||
const ipad = userAgent.includes("ipad");
|
export const ipad = userAgent.includes("ipad");
|
||||||
const ios = iphone || ipad;
|
export const ios = iphone || ipad;
|
||||||
|
export const canShare = "canShare" in navigator;
|
||||||
export default {
|
|
||||||
standalone,
|
|
||||||
userAgent,
|
|
||||||
isChrome,
|
|
||||||
safari,
|
|
||||||
safariOnly,
|
|
||||||
macOS,
|
|
||||||
iphone,
|
|
||||||
ipad,
|
|
||||||
ios,
|
|
||||||
localhost,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @param {number} [digits]
|
* @param {number} [digits]
|
||||||
* @param {Intl.NumberFormatOptions} [options]
|
* @param {Intl.NumberFormatOptions} [options]
|
||||||
*/
|
*/
|
||||||
function numberToUSFormat(value, digits, options) {
|
export function numberToUSNumber(value, digits, options) {
|
||||||
return value.toLocaleString("en-us", {
|
return value.toLocaleString("en-us", {
|
||||||
...options,
|
...options,
|
||||||
minimumFractionDigits: digits,
|
minimumFractionDigits: digits,
|
||||||
@@ -11,23 +11,18 @@ function numberToUSFormat(value, digits, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const locale = {
|
export const numberToDollars = new Intl.NumberFormat("en-US", {
|
||||||
numberToUSFormat,
|
style: "currency",
|
||||||
};
|
currency: "USD",
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
});
|
||||||
|
|
||||||
export const formatters = {
|
export const numberToPercentage = new Intl.NumberFormat("en-US", {
|
||||||
dollars: new Intl.NumberFormat("en-US", {
|
style: "percent",
|
||||||
style: "currency",
|
minimumFractionDigits: 2,
|
||||||
currency: "USD",
|
maximumFractionDigits: 2,
|
||||||
minimumFractionDigits: 2,
|
});
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
percentage: new Intl.NumberFormat("en-US", {
|
|
||||||
style: "percent",
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} s
|
* @param {string} s
|
||||||
@@ -7,26 +7,16 @@ import {
|
|||||||
import { serdeUnit } from "../serde";
|
import { serdeUnit } from "../serde";
|
||||||
import { pushHistory, resetParams } from "../url";
|
import { pushHistory, resetParams } from "../url";
|
||||||
import { readStored, writeToStorage } from "../storage";
|
import { readStored, writeToStorage } from "../storage";
|
||||||
|
import { stringToId } from "../format";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Colors} args.colors
|
* @param {Colors} args.colors
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Env} args.env
|
* @param {BRK} args.brk
|
||||||
* @param {Utilities} args.utils
|
|
||||||
* @param {MetricToIndexes} args.metricToIndexes
|
|
||||||
* @param {Pools} args.pools
|
|
||||||
* @param {Signal<string | null>} args.qrcode
|
* @param {Signal<string | null>} args.qrcode
|
||||||
*/
|
*/
|
||||||
export function initOptions({
|
export function initOptions({ colors, signals, brk, qrcode }) {
|
||||||
colors,
|
|
||||||
signals,
|
|
||||||
env,
|
|
||||||
utils,
|
|
||||||
qrcode,
|
|
||||||
metricToIndexes,
|
|
||||||
pools,
|
|
||||||
}) {
|
|
||||||
const LS_SELECTED_KEY = `selected_path`;
|
const LS_SELECTED_KEY = `selected_path`;
|
||||||
|
|
||||||
const urlPath_ = window.document.location.pathname
|
const urlPath_ = window.document.location.pathname
|
||||||
@@ -42,10 +32,8 @@ export function initOptions({
|
|||||||
const selected = signals.createSignal(/** @type {any} */ (undefined));
|
const selected = signals.createSignal(/** @type {any} */ (undefined));
|
||||||
|
|
||||||
const partialOptions = createPartialOptions({
|
const partialOptions = createPartialOptions({
|
||||||
env,
|
|
||||||
colors,
|
colors,
|
||||||
metricToIndexes,
|
brk,
|
||||||
pools,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @type {Option[]} */
|
/** @type {Option[]} */
|
||||||
@@ -58,7 +46,8 @@ export function initOptions({
|
|||||||
*/
|
*/
|
||||||
function arrayToRecord(arr = []) {
|
function arrayToRecord(arr = []) {
|
||||||
return (arr || []).reduce((record, blueprint) => {
|
return (arr || []).reduce((record, blueprint) => {
|
||||||
if (env.localhost && !(blueprint.metric in metricToIndexes)) {
|
if (!brk.hasMetric(blueprint.metric)) {
|
||||||
|
// if (localhost && !brk.hasMetric(blueprint.metric)) {
|
||||||
throw Error(`${blueprint.metric} not recognized`);
|
throw Error(`${blueprint.metric} not recognized`);
|
||||||
}
|
}
|
||||||
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
|
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
|
||||||
@@ -186,7 +175,7 @@ export function initOptions({
|
|||||||
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
|
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
|
||||||
);
|
);
|
||||||
|
|
||||||
const serName = utils.stringToId(anyPartial.name);
|
const serName = stringToId(anyPartial.name);
|
||||||
const path = [...parentPath, serName];
|
const path = [...parentPath, serName];
|
||||||
const childOptionsCount = recursiveProcessPartialTree(
|
const childOptionsCount = recursiveProcessPartialTree(
|
||||||
anyPartial.tree,
|
anyPartial.tree,
|
||||||
@@ -250,7 +239,7 @@ export function initOptions({
|
|||||||
const option = /** @type {Option} */ (anyPartial);
|
const option = /** @type {Option} */ (anyPartial);
|
||||||
|
|
||||||
const name = option.name;
|
const name = option.name;
|
||||||
const path = [...parentPath, utils.stringToId(option.name)];
|
const path = [...parentPath, stringToId(option.name)];
|
||||||
|
|
||||||
if ("kind" in anyPartial && anyPartial.kind === "explorer") {
|
if ("kind" in anyPartial && anyPartial.kind === "explorer") {
|
||||||
Object.assign(
|
Object.assign(
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// @ts_check
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} BaseSeriesBlueprint
|
* @typedef {Object} BaseSeriesBlueprint
|
||||||
* @property {string} title
|
* @property {string} title
|
||||||
@@ -119,70 +117,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { localhost } from "../env";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Env} args.env
|
|
||||||
* @param {Colors} args.colors
|
* @param {Colors} args.colors
|
||||||
* @param {MetricToIndexes} args.metricToIndexes
|
* @param {BRK} args.brk
|
||||||
* @param {Pools} args.pools
|
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
export function createPartialOptions({ colors, brk }) {
|
||||||
/**
|
|
||||||
* @template {string} S
|
|
||||||
* @typedef {Extract<Metric, `${S}${string}`>} StartsWith
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @template {string} S
|
|
||||||
* @typedef {Extract<Metric, `${string}${S}`>} EndsWith
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @template {string} K
|
|
||||||
* @template {string} S
|
|
||||||
* @typedef {K extends `${S}${infer Rest}` ? Rest : never} WithoutPrefix
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @template {string} K
|
|
||||||
* @template {string} S
|
|
||||||
* @typedef {K extends `${infer Rest}${S}` ? Rest : never} WithoutSuffix
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @template {string} K
|
|
||||||
* @template {string} S
|
|
||||||
* @typedef {K extends `${infer _Prefix}${S}${infer _Suffix}` ? never : K} ExcludeSubstring
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {"cumulative_"} CumulativePrefix
|
|
||||||
* @typedef {"_30d_delta"} _30DChageSubString
|
|
||||||
* @typedef {StartsWith<CumulativePrefix>} CumulativeMetric
|
|
||||||
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeMetric, CumulativePrefix>, _30DChageSubString>} CumulativeMetricBase
|
|
||||||
* @typedef {"_avg"} AverageSuffix
|
|
||||||
* @typedef {EndsWith<AverageSuffix>} MetricAverage
|
|
||||||
* @typedef {WithoutSuffix<MetricAverage, AverageSuffix>} MetricAverageBase
|
|
||||||
* @typedef {"_median"} MedianSuffix
|
|
||||||
* @typedef {EndsWith<MedianSuffix>} MetricMedian
|
|
||||||
* @typedef {WithoutSuffix<MetricMedian, MedianSuffix>} MetricMedianBase
|
|
||||||
* @typedef {"_pct90"} _pct90Suffix
|
|
||||||
* @typedef {EndsWith<_pct90Suffix>} MetricPct90
|
|
||||||
* @typedef {WithoutSuffix<MetricPct90, _pct90Suffix>} MetricPct90Base
|
|
||||||
* @typedef {"_pct75"} _pct75Suffix
|
|
||||||
* @typedef {EndsWith<_pct75Suffix>} MetricPct75
|
|
||||||
* @typedef {WithoutSuffix<MetricPct75, _pct75Suffix>} MetricPct75Base
|
|
||||||
* @typedef {"_pct25"} _pct25Suffix
|
|
||||||
* @typedef {EndsWith<_pct25Suffix>} MetricPct25
|
|
||||||
* @typedef {WithoutSuffix<MetricPct25, _pct25Suffix>} MetricPct25Base
|
|
||||||
* @typedef {"_pct10"} _pct10Suffix
|
|
||||||
* @typedef {EndsWith<_pct10Suffix>} MetricPct10
|
|
||||||
* @typedef {WithoutSuffix<MetricPct10, _pct10Suffix>} MetricPct10Base
|
|
||||||
* @typedef {"_max"} MaxSuffix
|
|
||||||
* @typedef {EndsWith<MaxSuffix>} MetricMax
|
|
||||||
* @typedef {WithoutSuffix<MetricMax, MaxSuffix>} MetricMaxBase
|
|
||||||
* @typedef {"_min"} MinSuffix
|
|
||||||
* @typedef {EndsWith<MinSuffix>} MetricMin
|
|
||||||
* @typedef {WithoutSuffix<MetricMin, MinSuffix>} MetricMinBase
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @param {boolean} compoundAdjective
|
* @param {boolean} compoundAdjective
|
||||||
@@ -681,7 +624,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MetricAverageBase} metric
|
* @param {Metric} metric
|
||||||
*/
|
*/
|
||||||
function createAverageSeries(metric) {
|
function createAverageSeries(metric) {
|
||||||
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
||||||
@@ -692,7 +635,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {CumulativeMetricBase} args.metric
|
* @param {Metric} args.metric
|
||||||
* @param {Color} [args.sumColor]
|
* @param {Color} [args.sumColor]
|
||||||
* @param {Color} [args.cumulativeColor]
|
* @param {Color} [args.cumulativeColor]
|
||||||
* @param {string} [args.common]
|
* @param {string} [args.common]
|
||||||
@@ -719,14 +662,14 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {CumulativeMetricBase} args.metric
|
* @param {Metric} args.metric
|
||||||
* @param {string} [args.title]
|
* @param {string} [args.title]
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
*/
|
*/
|
||||||
function createSumSeries({ metric, title = "", color }) {
|
function createSumSeries({ metric, title = "", color }) {
|
||||||
const metric_sum = `${metric}_sum`;
|
const metric_sum = `${metric}_sum`;
|
||||||
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
||||||
metric: metric_sum in metricToIndexes ? metric_sum : metric,
|
metric: brk.hasMetric(metric_sum) ? metric_sum : metric,
|
||||||
title: `Sum ${title}`,
|
title: `Sum ${title}`,
|
||||||
color: color ?? colors.red,
|
color: color ?? colors.red,
|
||||||
});
|
});
|
||||||
@@ -734,7 +677,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {CumulativeMetricBase} args.metric
|
* @param {Metric} args.metric
|
||||||
* @param {string} [args.title]
|
* @param {string} [args.title]
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
*/
|
*/
|
||||||
@@ -748,7 +691,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
* @param {Metric} metric
|
||||||
*/
|
*/
|
||||||
function createMinMaxPercentilesSeries(metric) {
|
function createMinMaxPercentilesSeries(metric) {
|
||||||
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
|
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
|
||||||
@@ -798,7 +741,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
* @param {Metric} metric
|
||||||
*/
|
*/
|
||||||
function createSumCumulativeMinMaxPercentilesSeries(metric) {
|
function createSumCumulativeMinMaxPercentilesSeries(metric) {
|
||||||
return [
|
return [
|
||||||
@@ -808,7 +751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
* @param {Metric} metric
|
||||||
*/
|
*/
|
||||||
function createAverageSumCumulativeMinMaxPercentilesSeries(metric) {
|
function createAverageSumCumulativeMinMaxPercentilesSeries(metric) {
|
||||||
return [
|
return [
|
||||||
@@ -819,7 +762,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Metric & MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.metric
|
* @param {Metric} args.metric
|
||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
*/
|
*/
|
||||||
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
|
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
|
||||||
@@ -835,12 +778,6 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {"_ratio_zscore"} RatioZScoreCapSuffix
|
|
||||||
* @typedef {EndsWith<RatioZScoreCapSuffix>} MetricRatioZScoreCap
|
|
||||||
* @typedef {WithoutSuffix<MetricRatioZScoreCap, RatioZScoreCapSuffix>} MetricRatioZScoreCapBase
|
|
||||||
*/
|
|
||||||
|
|
||||||
const percentiles = [
|
const percentiles = [
|
||||||
{
|
{
|
||||||
name: "pct1",
|
name: "pct1",
|
||||||
@@ -906,7 +843,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
* @param {string} args.legend
|
* @param {string} args.legend
|
||||||
* @param {string} args.title
|
* @param {string} args.title
|
||||||
* @param {MetricRatioZScoreCapBase} args.metric
|
* @param {Metric} args.metric
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
*/
|
*/
|
||||||
function createPriceWithRatioOptions({ name, title, legend, metric, color }) {
|
function createPriceWithRatioOptions({ name, title, legend, metric, color }) {
|
||||||
@@ -931,7 +868,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name: legend,
|
name: legend,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
...(`${metric}_ratio_p1sd_usd` in metricToIndexes
|
...(brk.hasMetric(`${metric}_ratio_p1sd_usd`)
|
||||||
? percentiles.map(({ name, color }) =>
|
? percentiles.map(({ name, color }) =>
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${metric}_ratio_${name}_usd`,
|
metric: `${metric}_ratio_${name}_usd`,
|
||||||
@@ -954,7 +891,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
baseValue: { price: 1 },
|
baseValue: { price: 1 },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(`${metric}_ratio_p1sd` in metricToIndexes
|
...(brk.hasMetric(`${metric}_ratio_p1sd`)
|
||||||
? percentiles.map(({ name, color }) =>
|
? percentiles.map(({ name, color }) =>
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${metric}_ratio_${name}`,
|
metric: `${metric}_ratio_${name}`,
|
||||||
@@ -967,7 +904,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
: []),
|
: []),
|
||||||
...(`${metric}_ratio_sma` in metricToIndexes
|
...(brk.hasMetric(`${metric}_ratio_sma`)
|
||||||
? ratioAverages.map(({ name, metric: metricAddon, color }) =>
|
? ratioAverages.map(({ name, metric: metricAddon, color }) =>
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${metric}_ratio_${metricAddon}`,
|
metric: `${metric}_ratio_${metricAddon}`,
|
||||||
@@ -986,7 +923,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
...(`${metric}_ratio_zscore` in metricToIndexes
|
...(brk.hasMetric(`${metric}_ratio_zscore`)
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
name: "ZScores",
|
name: "ZScores",
|
||||||
@@ -1178,18 +1115,12 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {"_supply_in_profit"} SupplyInProfitSuffix
|
|
||||||
* @typedef {EndsWith<SupplyInProfitSuffix>} MetricSupplyInProfit
|
|
||||||
* @typedef {WithoutSuffix<MetricSupplyInProfit, SupplyInProfitSuffix>} CohortId
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} UTXOGroupObject
|
* @typedef {Object} UTXOGroupObject
|
||||||
* @property {string} args.name
|
* @property {string} args.name
|
||||||
* @property {string} args.title
|
* @property {string} args.title
|
||||||
* @property {Color} args.color
|
* @property {Color} args.color
|
||||||
* @property {"" | CohortId} args.id
|
* @property {string} args.id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1200,13 +1131,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {"" | CohortId} T
|
* @param {string} id
|
||||||
* @param {T} id
|
|
||||||
*/
|
*/
|
||||||
const fixId = (id) =>
|
function fixId(id) {
|
||||||
id !== ""
|
return id !== "" ? `${id}_` : "";
|
||||||
? /** @type {Exclude<"" | `${T}_`, "_">} */ (`${id}_`)
|
}
|
||||||
: /** @type {const} */ ("");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {UTXOGroupObject | UTXOGroupsObject} args
|
* @param {UTXOGroupObject | UTXOGroupsObject} args
|
||||||
@@ -1465,11 +1394,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
...(list.filter(({ id }) => `${fixId(id)}addr_count` in metricToIndexes)
|
...(list.filter(({ id }) => brk.hasMetric(`${fixId(id)}addr_count`))
|
||||||
.length > ("list" in args ? 1 : 0)
|
.length > ("list" in args ? 1 : 0)
|
||||||
? !("list" in args) ||
|
? !("list" in args) ||
|
||||||
list.filter(
|
list.filter(({ id }) =>
|
||||||
({ id }) => `${fixId(id)}empty_addr_count` in metricToIndexes,
|
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||||
).length <= 1
|
).length <= 1
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -1478,7 +1407,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
bottom: list.flatMap(({ name, color, id: _id }) => {
|
bottom: list.flatMap(({ name, color, id: _id }) => {
|
||||||
const id = fixId(_id);
|
const id = fixId(_id);
|
||||||
return [
|
return [
|
||||||
...(`${id}addr_count` in metricToIndexes
|
...(brk.hasMetric(`${id}addr_count`)
|
||||||
? /** @type {const} */ ([
|
? /** @type {const} */ ([
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${id}addr_count`,
|
metric: `${id}addr_count`,
|
||||||
@@ -1487,7 +1416,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
: []),
|
: []),
|
||||||
...(`${id}empty_addr_count` in metricToIndexes
|
...(brk.hasMetric(`${id}empty_addr_count`)
|
||||||
? /** @type {const} */ ([
|
? /** @type {const} */ ([
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${id}empty_addr_count`,
|
metric: `${id}empty_addr_count`,
|
||||||
@@ -1509,9 +1438,8 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name: "loaded",
|
name: "loaded",
|
||||||
title: `Loaded Address Count ${title}`,
|
title: `Loaded Address Count ${title}`,
|
||||||
bottom: list
|
bottom: list
|
||||||
.filter(
|
.filter(({ id }) =>
|
||||||
({ id }) =>
|
brk.hasMetric(`${fixId(id)}addr_count`),
|
||||||
`${fixId(id)}addr_count` in metricToIndexes,
|
|
||||||
)
|
)
|
||||||
.flatMap(({ name, color, id: _id }) => {
|
.flatMap(({ name, color, id: _id }) => {
|
||||||
const id = fixId(_id);
|
const id = fixId(_id);
|
||||||
@@ -1524,19 +1452,16 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
...(list.filter(
|
...(list.filter(({ id }) =>
|
||||||
({ id }) =>
|
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||||
`${fixId(id)}empty_addr_count` in metricToIndexes,
|
|
||||||
).length
|
).length
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
title: `Empty Address Count ${title}`,
|
title: `Empty Address Count ${title}`,
|
||||||
bottom: list
|
bottom: list
|
||||||
.filter(
|
.filter(({ id }) =>
|
||||||
({ id }) =>
|
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||||
`${fixId(id)}empty_addr_count` in
|
|
||||||
metricToIndexes,
|
|
||||||
)
|
)
|
||||||
.flatMap(({ name, color, id: _id }) => {
|
.flatMap(({ name, color, id: _id }) => {
|
||||||
const id = fixId(_id);
|
const id = fixId(_id);
|
||||||
@@ -1622,7 +1547,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...(!("list" in args) &&
|
...(!("list" in args) &&
|
||||||
`${id}realized_cap_rel_to_own_market_cap` in metricToIndexes
|
brk.hasMetric(`${id}realized_cap_rel_to_own_market_cap`)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -1658,8 +1583,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
color: colors.red,
|
color: colors.red,
|
||||||
defaultActive: false,
|
defaultActive: false,
|
||||||
}),
|
}),
|
||||||
...(`${fixId(args.id)}realized_profit_to_loss_ratio` in
|
...(brk.hasMetric(
|
||||||
metricToIndexes
|
`${fixId(args.id)}realized_profit_to_loss_ratio`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${fixId(
|
metric: `${fixId(
|
||||||
@@ -1797,7 +1723,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(asoprKey in metricToIndexes
|
...(brk.hasMetric(asoprKey)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -1825,7 +1751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(asoprKey in metricToIndexes
|
...(brk.hasMetric(asoprKey)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -1853,7 +1779,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(asoprKey in metricToIndexes
|
...(brk.hasMetric(asoprKey)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -1940,8 +1866,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name,
|
name,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
...(`${id}realized_profit_to_loss_ratio` in
|
...(brk.hasMetric(
|
||||||
metricToIndexes
|
`${id}realized_profit_to_loss_ratio`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${id}realized_profit_to_loss_ratio`,
|
metric: `${id}realized_profit_to_loss_ratio`,
|
||||||
@@ -2102,7 +2029,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name,
|
name,
|
||||||
metric: `${fixId(id)}adjusted_sopr`,
|
metric: `${fixId(id)}adjusted_sopr`,
|
||||||
}))
|
}))
|
||||||
.filter(({ metric }) => metric in metricToIndexes);
|
.filter(({ metric }) => brk.hasMetric(metric));
|
||||||
|
|
||||||
return reducedList.length
|
return reducedList.length
|
||||||
? [
|
? [
|
||||||
@@ -2180,7 +2107,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name: "normal",
|
name: "normal",
|
||||||
color: colors.emerald,
|
color: colors.emerald,
|
||||||
}),
|
}),
|
||||||
...(adjKey in metricToIndexes
|
...(brk.hasMetric(adjKey)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: adjKey,
|
metric: adjKey,
|
||||||
@@ -2204,7 +2131,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name: "normal",
|
name: "normal",
|
||||||
color: colors.red,
|
color: colors.red,
|
||||||
}),
|
}),
|
||||||
...(adjKey in metricToIndexes
|
...(brk.hasMetric(adjKey)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: adjKey,
|
metric: adjKey,
|
||||||
@@ -2239,9 +2166,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name,
|
name,
|
||||||
metric: `${fixId(id)}adjusted_value_created`,
|
metric: `${fixId(id)}adjusted_value_created`,
|
||||||
}))
|
}))
|
||||||
.filter(
|
.filter(({ metric }) => brk.hasMetric(metric));
|
||||||
({ metric }) => metric in metricToIndexes,
|
|
||||||
);
|
|
||||||
return reducedList.length
|
return reducedList.length
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -2282,9 +2207,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name,
|
name,
|
||||||
metric: `${fixId(id)}adjusted_value_destroyed`,
|
metric: `${fixId(id)}adjusted_value_destroyed`,
|
||||||
}))
|
}))
|
||||||
.filter(
|
.filter(({ metric }) => brk.hasMetric(metric));
|
||||||
({ metric }) => metric in metricToIndexes,
|
|
||||||
);
|
|
||||||
return reducedList.length
|
return reducedList.length
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -2361,10 +2284,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
name: "Negative Loss",
|
name: "Negative Loss",
|
||||||
color: colors.red,
|
color: colors.red,
|
||||||
}),
|
}),
|
||||||
...(`${fixId(
|
...(brk.hasMetric(
|
||||||
args.id,
|
`${fixId(
|
||||||
)}unrealized_profit_rel_to_own_market_cap` in
|
args.id,
|
||||||
metricToIndexes
|
)}unrealized_profit_rel_to_own_market_cap`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${fixId(
|
metric: `${fixId(
|
||||||
@@ -2397,10 +2321,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...(`${fixId(
|
...(brk.hasMetric(
|
||||||
args.id,
|
`${fixId(
|
||||||
)}unrealized_profit_rel_to_own_total_unrealized_pnl` in
|
args.id,
|
||||||
metricToIndexes
|
)}unrealized_profit_rel_to_own_total_unrealized_pnl`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
createBaseSeries({
|
createBaseSeries({
|
||||||
metric: `${fixId(
|
metric: `${fixId(
|
||||||
@@ -2505,8 +2430,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
title: useGroupName ? name : "Net",
|
title: useGroupName ? name : "Net",
|
||||||
color: useGroupName ? color : undefined,
|
color: useGroupName ? color : undefined,
|
||||||
}),
|
}),
|
||||||
...(`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap` in
|
...(brk.hasMetric(
|
||||||
metricToIndexes
|
`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -2521,10 +2447,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...(`${fixId(
|
...(brk.hasMetric(
|
||||||
id,
|
`${fixId(
|
||||||
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl` in
|
id,
|
||||||
metricToIndexes
|
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl`,
|
||||||
|
)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
type: "Baseline",
|
type: "Baseline",
|
||||||
@@ -2660,7 +2587,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...(env.localhost
|
...(localhost
|
||||||
? /** @type {const} */ ([
|
? /** @type {const} */ ([
|
||||||
{
|
{
|
||||||
name: "Explorer",
|
name: "Explorer",
|
||||||
@@ -2792,7 +2719,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
createPriceLine({
|
createPriceLine({
|
||||||
unit: "percentage",
|
unit: "percentage",
|
||||||
}),
|
}),
|
||||||
...(cagr in metricToIndexes
|
...(brk.hasMetric(cagr)
|
||||||
? [
|
? [
|
||||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||||
metric: cagr,
|
metric: cagr,
|
||||||
@@ -3652,133 +3579,135 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pools",
|
name: "Pools",
|
||||||
tree: Object.entries(pools).map(([_id, name]) => {
|
tree: Object.entries(brk.POOL_ID_TO_POOL_NAME).map(
|
||||||
const id = /** @type {Pool} */ (_id);
|
([_id, name]) => {
|
||||||
return {
|
const id = /** @type {PoolId} */ (_id);
|
||||||
name,
|
return {
|
||||||
tree: [
|
name,
|
||||||
{
|
tree: [
|
||||||
name: "Dominance",
|
{
|
||||||
title: `Mining Dominance of ${name}`,
|
name: "Dominance",
|
||||||
bottom: [
|
title: `Mining Dominance of ${name}`,
|
||||||
createBaseSeries({
|
bottom: [
|
||||||
metric: `${id}_1d_dominance`,
|
createBaseSeries({
|
||||||
name: "1d",
|
metric: `${id}_1d_dominance`,
|
||||||
color: colors.rose,
|
name: "1d",
|
||||||
defaultActive: false,
|
color: colors.rose,
|
||||||
}),
|
defaultActive: false,
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1w_dominance`,
|
|
||||||
name: "1w",
|
|
||||||
color: colors.red,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1m_dominance`,
|
|
||||||
name: "1m",
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1y_dominance`,
|
|
||||||
name: "1y",
|
|
||||||
color: colors.lime,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_dominance`,
|
|
||||||
name: "all time",
|
|
||||||
color: colors.teal,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Blocks mined",
|
|
||||||
title: `Blocks mined by ${name}`,
|
|
||||||
bottom: [
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_blocks_mined`,
|
|
||||||
name: "Sum",
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_blocks_mined_cumulative`,
|
|
||||||
name: "Cumulative",
|
|
||||||
color: colors.blue,
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1w_blocks_mined`,
|
|
||||||
name: "1w Sum",
|
|
||||||
color: colors.red,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1m_blocks_mined`,
|
|
||||||
name: "1m Sum",
|
|
||||||
color: colors.pink,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
createBaseSeries({
|
|
||||||
metric: `${id}_1y_blocks_mined`,
|
|
||||||
name: "1y Sum",
|
|
||||||
color: colors.purple,
|
|
||||||
defaultActive: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Rewards",
|
|
||||||
title: `Rewards collected by ${name}`,
|
|
||||||
bottom: [
|
|
||||||
{
|
|
||||||
metricAddon: "coinbase",
|
|
||||||
cumulativeColor: colors.red,
|
|
||||||
sumColor: colors.orange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metricAddon: "subsidy",
|
|
||||||
cumulativeColor: colors.emerald,
|
|
||||||
sumColor: colors.lime,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metricAddon: "fee",
|
|
||||||
cumulativeColor: colors.indigo,
|
|
||||||
sumColor: colors.cyan,
|
|
||||||
},
|
|
||||||
].flatMap(
|
|
||||||
({ metricAddon, sumColor, cumulativeColor }) => [
|
|
||||||
...createSumCumulativeSeries({
|
|
||||||
metric: `${id}_${metricAddon}`,
|
|
||||||
common: metricAddon,
|
|
||||||
sumColor,
|
|
||||||
cumulativeColor,
|
|
||||||
}),
|
}),
|
||||||
...createSumCumulativeSeries({
|
createBaseSeries({
|
||||||
metric: `${id}_${metricAddon}_btc`,
|
metric: `${id}_1w_dominance`,
|
||||||
common: metricAddon,
|
name: "1w",
|
||||||
sumColor,
|
color: colors.red,
|
||||||
cumulativeColor,
|
defaultActive: false,
|
||||||
}),
|
}),
|
||||||
...createSumCumulativeSeries({
|
createBaseSeries({
|
||||||
metric: `${id}_${metricAddon}_usd`,
|
metric: `${id}_1m_dominance`,
|
||||||
common: metricAddon,
|
name: "1m",
|
||||||
sumColor,
|
}),
|
||||||
cumulativeColor,
|
createBaseSeries({
|
||||||
|
metric: `${id}_1y_dominance`,
|
||||||
|
name: "1y",
|
||||||
|
color: colors.lime,
|
||||||
|
defaultActive: false,
|
||||||
|
}),
|
||||||
|
createBaseSeries({
|
||||||
|
metric: `${id}_dominance`,
|
||||||
|
name: "all time",
|
||||||
|
color: colors.teal,
|
||||||
|
defaultActive: false,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
},
|
||||||
},
|
{
|
||||||
{
|
name: "Blocks mined",
|
||||||
name: "Days since block",
|
title: `Blocks mined by ${name}`,
|
||||||
title: `Days since ${name} mined a block`,
|
bottom: [
|
||||||
bottom: [
|
createBaseSeries({
|
||||||
createBaseSeries({
|
metric: `${id}_blocks_mined`,
|
||||||
metric: `${id}_days_since_block`,
|
name: "Sum",
|
||||||
name: "Since block",
|
}),
|
||||||
}),
|
createBaseSeries({
|
||||||
],
|
metric: `${id}_blocks_mined_cumulative`,
|
||||||
},
|
name: "Cumulative",
|
||||||
],
|
color: colors.blue,
|
||||||
};
|
}),
|
||||||
}),
|
createBaseSeries({
|
||||||
|
metric: `${id}_1w_blocks_mined`,
|
||||||
|
name: "1w Sum",
|
||||||
|
color: colors.red,
|
||||||
|
defaultActive: false,
|
||||||
|
}),
|
||||||
|
createBaseSeries({
|
||||||
|
metric: `${id}_1m_blocks_mined`,
|
||||||
|
name: "1m Sum",
|
||||||
|
color: colors.pink,
|
||||||
|
defaultActive: false,
|
||||||
|
}),
|
||||||
|
createBaseSeries({
|
||||||
|
metric: `${id}_1y_blocks_mined`,
|
||||||
|
name: "1y Sum",
|
||||||
|
color: colors.purple,
|
||||||
|
defaultActive: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Rewards",
|
||||||
|
title: `Rewards collected by ${name}`,
|
||||||
|
bottom: [
|
||||||
|
{
|
||||||
|
metricAddon: "coinbase",
|
||||||
|
cumulativeColor: colors.red,
|
||||||
|
sumColor: colors.orange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metricAddon: "subsidy",
|
||||||
|
cumulativeColor: colors.emerald,
|
||||||
|
sumColor: colors.lime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metricAddon: "fee",
|
||||||
|
cumulativeColor: colors.indigo,
|
||||||
|
sumColor: colors.cyan,
|
||||||
|
},
|
||||||
|
].flatMap(
|
||||||
|
({ metricAddon, sumColor, cumulativeColor }) => [
|
||||||
|
...createSumCumulativeSeries({
|
||||||
|
metric: `${id}_${metricAddon}`,
|
||||||
|
common: metricAddon,
|
||||||
|
sumColor,
|
||||||
|
cumulativeColor,
|
||||||
|
}),
|
||||||
|
...createSumCumulativeSeries({
|
||||||
|
metric: `${id}_${metricAddon}_btc`,
|
||||||
|
common: metricAddon,
|
||||||
|
sumColor,
|
||||||
|
cumulativeColor,
|
||||||
|
}),
|
||||||
|
...createSumCumulativeSeries({
|
||||||
|
metric: `${id}_${metricAddon}_usd`,
|
||||||
|
common: metricAddon,
|
||||||
|
sumColor,
|
||||||
|
cumulativeColor,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Days since block",
|
||||||
|
title: `Days since ${name} mined a block`,
|
||||||
|
bottom: [
|
||||||
|
createBaseSeries({
|
||||||
|
metric: `${id}_days_since_block`,
|
||||||
|
name: "Since block",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,59 +113,57 @@ export const serdeBool = {
|
|||||||
|
|
||||||
export const serdeChartableIndex = {
|
export const serdeChartableIndex = {
|
||||||
/**
|
/**
|
||||||
* @param {number} v
|
* @param {IndexName} v
|
||||||
* @returns {SerializedChartableIndex | null}
|
* @returns {ChartableIndexName | null}
|
||||||
*/
|
*/
|
||||||
serialize(v) {
|
serialize(v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case /** @satisfies {DateIndex} */ (0):
|
case "dateindex":
|
||||||
return "date";
|
return "date";
|
||||||
case /** @satisfies {DecadeIndex} */ (1):
|
case "decadeindex":
|
||||||
return "decade";
|
return "decade";
|
||||||
case /** @satisfies {DifficultyEpoch} */ (2):
|
case "difficultyepoch":
|
||||||
return "epoch";
|
return "epoch";
|
||||||
// case /** @satisfies {HalvingEpoch} */ (4):
|
case "height":
|
||||||
// return "halving";
|
|
||||||
case /** @satisfies {Height} */ (5):
|
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
case /** @satisfies {MonthIndex} */ (7):
|
case "monthindex":
|
||||||
return "month";
|
return "month";
|
||||||
case /** @satisfies {QuarterIndex} */ (19):
|
case "quarterindex":
|
||||||
return "quarter";
|
return "quarter";
|
||||||
case /** @satisfies {SemesterIndex} */ (20):
|
case "semesterindex":
|
||||||
return "semester";
|
return "semester";
|
||||||
case /** @satisfies {WeekIndex} */ (23):
|
case "weekindex":
|
||||||
return "week";
|
return "week";
|
||||||
case /** @satisfies {YearIndex} */ (24):
|
case "yearindex":
|
||||||
return "year";
|
return "year";
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {SerializedChartableIndex} v
|
* @param {ChartableIndexName} v
|
||||||
* @returns {Index}
|
* @returns {IndexName}
|
||||||
*/
|
*/
|
||||||
deserialize(v) {
|
deserialize(v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case "timestamp":
|
case "timestamp":
|
||||||
return /** @satisfies {Height} */ (5);
|
return "height";
|
||||||
case "date":
|
case "date":
|
||||||
return /** @satisfies {DateIndex} */ (0);
|
return "dateindex";
|
||||||
case "week":
|
case "week":
|
||||||
return /** @satisfies {WeekIndex} */ (23);
|
return "weekindex";
|
||||||
case "epoch":
|
case "epoch":
|
||||||
return /** @satisfies {DifficultyEpoch} */ (2);
|
return "difficultyepoch";
|
||||||
case "month":
|
case "month":
|
||||||
return /** @satisfies {MonthIndex} */ (7);
|
return "monthindex";
|
||||||
case "quarter":
|
case "quarter":
|
||||||
return /** @satisfies {QuarterIndex} */ (19);
|
return "quarterindex";
|
||||||
case "semester":
|
case "semester":
|
||||||
return /** @satisfies {SemesterIndex} */ (20);
|
return "semesterindex";
|
||||||
case "year":
|
case "year":
|
||||||
return /** @satisfies {YearIndex} */ (24);
|
return "yearindex";
|
||||||
case "decade":
|
case "decade":
|
||||||
return /** @satisfies {DecadeIndex} */ (1);
|
return "decadeindex";
|
||||||
default:
|
default:
|
||||||
throw Error("todo");
|
throw Error("todo");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
|
* @import * as _ from "./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts"
|
||||||
|
*
|
||||||
|
* @import { Signal, Signals, Accessor } from "./modules/brk-signals/index";
|
||||||
|
*
|
||||||
|
* @import { BRK } from "./modules/brk-client/index.js"
|
||||||
|
* @import { Metric, MetricToIndexes } from "./modules/brk-client/metrics"
|
||||||
|
* @import { IndexName } from "./modules/brk-client/generated/metrics"
|
||||||
|
* @import { PoolId, PoolIdToPoolName } from "./modules/brk-client/generated/pools"
|
||||||
|
*
|
||||||
|
* @import { Resources, MetricResource } from './modules/brk-resources/index.js'
|
||||||
*
|
*
|
||||||
* @import { Valued, SingleValueData, CandlestickData, Series, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, CreateChartElement, Chart } from "./core/chart/index"
|
* @import { Valued, SingleValueData, CandlestickData, Series, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, CreateChartElement, Chart } from "./core/chart/index"
|
||||||
*
|
*
|
||||||
* @import * as _ from "./packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts"
|
|
||||||
*
|
|
||||||
* @import { SerializedChartableIndex } from "./panes/chart";
|
|
||||||
*
|
|
||||||
* @import { Signal, Signals, Accessor } from "./packages/solidjs-signals/wrapper";
|
|
||||||
*
|
|
||||||
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, WeekIndex, SemesterIndex, YearIndex, MetricToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./bridge/vecs"
|
|
||||||
*
|
|
||||||
* @import { Pools, Pool } from "./bridge/pools"
|
|
||||||
*
|
|
||||||
* @import { Color, ColorName, Colors } from "./core/colors"
|
* @import { Color, ColorName, Colors } from "./core/colors"
|
||||||
*
|
*
|
||||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree } from "./core/options/partial"
|
|
||||||
*
|
|
||||||
* @import { WebSockets } from "./core/ws"
|
* @import { WebSockets } from "./core/ws"
|
||||||
*
|
*
|
||||||
|
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree } from "./core/options/partial"
|
||||||
|
*
|
||||||
* @import { Unit } from "./core/serde"
|
* @import { Unit } from "./core/serde"
|
||||||
|
*
|
||||||
|
* @import { ChartableIndexName } from "./panes/chart/index.js";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// import uFuzzy = require("./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {typeof import("./lazy")["default"]} Packages
|
* @typedef {typeof import("./lazy")["default"]} Modules
|
||||||
* @typedef {typeof import("./core/utils")} Utilities
|
* @typedef {[number, number, number, number]} OHLCTuple
|
||||||
* @typedef {typeof import("./core/env")["default"]} Env
|
|
||||||
* @typedef {typeof import("./core/elements")["default"]} Elements
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
|
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
const imports = {
|
const imports = {
|
||||||
async signals() {
|
async signals() {
|
||||||
return import("./packages/solidjs-signals/index.js").then((d) => d.default);
|
return import("./modules/brk-signals/index.js").then((d) => d.default);
|
||||||
},
|
},
|
||||||
|
async leanQr() {
|
||||||
|
return import("./modules/lean-qr/2.6.0/index.mjs").then((d) => d);
|
||||||
|
},
|
||||||
|
async ufuzzy() {
|
||||||
|
return import("./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.mjs").then(
|
||||||
|
({ default: d }) => d,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async brkClient() {
|
||||||
|
return import("./modules/brk-client/index.js").then((d) => d);
|
||||||
|
},
|
||||||
|
async brkResources() {
|
||||||
|
return import("./modules/brk-resources/index.js").then((d) => d);
|
||||||
|
},
|
||||||
|
|
||||||
async chart() {
|
async chart() {
|
||||||
return window.document.fonts.ready.then(() =>
|
return window.document.fonts.ready.then(() =>
|
||||||
import("./core/chart/index.js").then((d) => d.default),
|
import("./core/chart/index.js").then((d) => d.default),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async leanQr() {
|
|
||||||
return import("./packages/lean-qr/2.5.0/index.mjs").then((d) => d);
|
|
||||||
},
|
|
||||||
async ufuzzy() {
|
|
||||||
return import("./packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.mjs").then(
|
|
||||||
({ default: d }) => d,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async modernScreenshot() {
|
|
||||||
return import("./packages/modern-screenshot/index.js").then((d) => d);
|
|
||||||
},
|
|
||||||
async brk() {
|
|
||||||
return import("./packages/brk/index.js").then((d) => d);
|
|
||||||
},
|
|
||||||
|
|
||||||
async options() {
|
async options() {
|
||||||
return import("./core/options/full.js").then((d) => d);
|
return import("./core/options/full.js").then((d) => d);
|
||||||
},
|
},
|
||||||
|
|||||||
+524
-537
File diff suppressed because it is too large
Load Diff
+40
-53
@@ -2,9 +2,11 @@ import {
|
|||||||
createShadow,
|
createShadow,
|
||||||
createHorizontalChoiceField,
|
createHorizontalChoiceField,
|
||||||
createHeader,
|
createHeader,
|
||||||
} from "../core/dom";
|
} from "../../core/dom";
|
||||||
import { serdeChartableIndex, serdeOptNumber } from "../core/serde";
|
import { chartElement } from "../../core/elements";
|
||||||
import { throttle } from "../core/timing";
|
import { ios, canShare } from "../../core/env";
|
||||||
|
import { serdeChartableIndex, serdeOptNumber } from "../../core/serde";
|
||||||
|
import { throttle } from "../../core/timing";
|
||||||
|
|
||||||
const keyPrefix = "chart";
|
const keyPrefix = "chart";
|
||||||
const ONE_BTC_IN_SATS = 100_000_000;
|
const ONE_BTC_IN_SATS = 100_000_000;
|
||||||
@@ -13,7 +15,7 @@ const LINE = "line";
|
|||||||
const CANDLE = "candle";
|
const CANDLE = "candle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {"timestamp" | "date" | "week" | "epoch" | "month" | "quarter" | "semester" | "year" | "decade" } SerializedChartableIndex
|
* @typedef {"timestamp" | "date" | "week" | "epoch" | "month" | "quarter" | "semester" | "year" | "decade" } ChartableIndexName
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,38 +24,29 @@ const CANDLE = "candle";
|
|||||||
* @param {CreateChartElement} args.createChartElement
|
* @param {CreateChartElement} args.createChartElement
|
||||||
* @param {Accessor<ChartOption>} args.option
|
* @param {Accessor<ChartOption>} args.option
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
|
||||||
* @param {WebSockets} args.webSockets
|
* @param {WebSockets} args.webSockets
|
||||||
* @param {Elements} args.elements
|
* @param {Resources} args.resources
|
||||||
* @param {Env} args.env
|
* @param {BRK} args.brk
|
||||||
* @param {VecsResources} args.vecsResources
|
|
||||||
* @param {MetricToIndexes} args.metricToIndexes
|
|
||||||
* @param {Packages} args.packages
|
|
||||||
*/
|
*/
|
||||||
export function init({
|
export function init({
|
||||||
colors,
|
colors,
|
||||||
elements,
|
|
||||||
createChartElement,
|
createChartElement,
|
||||||
option,
|
option,
|
||||||
signals,
|
signals,
|
||||||
utils,
|
|
||||||
env,
|
|
||||||
webSockets,
|
webSockets,
|
||||||
vecsResources,
|
resources,
|
||||||
metricToIndexes,
|
brk,
|
||||||
packages,
|
|
||||||
}) {
|
}) {
|
||||||
elements.charts.append(createShadow("left"));
|
chartElement.append(createShadow("left"));
|
||||||
elements.charts.append(createShadow("right"));
|
chartElement.append(createShadow("right"));
|
||||||
|
|
||||||
const { headerElement, headingElement } = createHeader();
|
const { headerElement, headingElement } = createHeader();
|
||||||
elements.charts.append(headerElement);
|
chartElement.append(headerElement);
|
||||||
|
|
||||||
const { index, fieldset } = createIndexSelector({
|
const { index, fieldset } = createIndexSelector({
|
||||||
option,
|
option,
|
||||||
metricToIndexes,
|
brk,
|
||||||
signals,
|
signals,
|
||||||
utils,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const TIMERANGE_LS_KEY = signals.createMemo(
|
const TIMERANGE_LS_KEY = signals.createMemo(
|
||||||
@@ -80,13 +73,11 @@ export function init({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const chart = createChartElement({
|
const chart = createChartElement({
|
||||||
parent: elements.charts,
|
parent: chartElement,
|
||||||
signals,
|
signals,
|
||||||
colors,
|
colors,
|
||||||
id: "charts",
|
id: "charts",
|
||||||
utils,
|
resources,
|
||||||
vecsResources,
|
|
||||||
elements,
|
|
||||||
index,
|
index,
|
||||||
timeScaleSetCallback: (unknownTimeScaleCallback) => {
|
timeScaleSetCallback: (unknownTimeScaleCallback) => {
|
||||||
// TODO: Although it mostly works in practice, need to make it more robust, there is no guarantee that this runs in order and wait for `from` and `to` to update when `index` and thus `TIMERANGE_LS_KEY` is updated
|
// TODO: Although it mostly works in practice, need to make it more robust, there is no guarantee that this runs in order and wait for `from` and `to` to update when `index` and thus `TIMERANGE_LS_KEY` is updated
|
||||||
@@ -105,12 +96,11 @@ export function init({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(env.ios && !("canShare" in navigator))) {
|
if (!(ios && !canShare)) {
|
||||||
const chartBottomRightCanvas = Array.from(
|
const chartBottomRightCanvas = Array.from(
|
||||||
chart.inner.chartElement().getElementsByTagName("tr"),
|
chart.inner.chartElement().getElementsByTagName("tr"),
|
||||||
).at(-1)?.lastChild?.firstChild?.firstChild;
|
).at(-1)?.lastChild?.firstChild?.firstChild;
|
||||||
if (chartBottomRightCanvas) {
|
if (chartBottomRightCanvas) {
|
||||||
const charts = elements.charts;
|
|
||||||
const domain = window.document.createElement("p");
|
const domain = window.document.createElement("p");
|
||||||
domain.innerText = `${window.location.host}`;
|
domain.innerText = `${window.location.host}`;
|
||||||
domain.id = "domain";
|
domain.id = "domain";
|
||||||
@@ -121,20 +111,20 @@ export function init({
|
|||||||
screenshotButton.title = "Screenshot";
|
screenshotButton.title = "Screenshot";
|
||||||
chartBottomRightCanvas.replaceWith(screenshotButton);
|
chartBottomRightCanvas.replaceWith(screenshotButton);
|
||||||
screenshotButton.addEventListener("click", () => {
|
screenshotButton.addEventListener("click", () => {
|
||||||
packages.modernScreenshot().then(async ({ screenshot }) => {
|
import("./screenshot").then(async ({ screenshot }) => {
|
||||||
charts.dataset.screenshot = "true";
|
chartElement.dataset.screenshot = "true";
|
||||||
charts.append(domain);
|
chartElement.append(domain);
|
||||||
seriesTypeField.hidden = true;
|
seriesTypeField.hidden = true;
|
||||||
try {
|
try {
|
||||||
await screenshot({
|
await screenshot({
|
||||||
element: charts,
|
element: chartElement,
|
||||||
name: option().path.join("-"),
|
name: option().path.join("-"),
|
||||||
title: option().title,
|
title: option().title,
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
charts.removeChild(domain);
|
chartElement.removeChild(domain);
|
||||||
seriesTypeField.hidden = false;
|
seriesTypeField.hidden = false;
|
||||||
charts.dataset.screenshot = "false";
|
chartElement.dataset.screenshot = "false";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -148,7 +138,7 @@ export function init({
|
|||||||
}, 250),
|
}, 250),
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.charts.append(fieldset);
|
chartElement.append(fieldset);
|
||||||
|
|
||||||
const { field: seriesTypeField, selected: topSeriesType_ } =
|
const { field: seriesTypeField, selected: topSeriesType_ } =
|
||||||
createHorizontalChoiceField({
|
createHorizontalChoiceField({
|
||||||
@@ -205,7 +195,7 @@ export function init({
|
|||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {ISeries} params.iseries
|
* @param {ISeries} params.iseries
|
||||||
* @param {Unit} params.unit
|
* @param {Unit} params.unit
|
||||||
* @param {Index} params.index
|
* @param {IndexName} params.index
|
||||||
*/
|
*/
|
||||||
function printLatest({ iseries, unit, index }) {
|
function printLatest({ iseries, unit, index }) {
|
||||||
const _latest = webSockets.kraken1dCandle.latest();
|
const _latest = webSockets.kraken1dCandle.latest();
|
||||||
@@ -234,9 +224,9 @@ export function init({
|
|||||||
const date = new Date(latest.time * 1000);
|
const date = new Date(latest.time * 1000);
|
||||||
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case /** @satisfies {Height} */ (5):
|
case "height":
|
||||||
case /** @satisfies {DifficultyEpoch} */ (2):
|
case "difficultyepoch":
|
||||||
case /** @satisfies {HalvingEpoch} */ (4): {
|
case "halvingepoch": {
|
||||||
if ("close" in last) {
|
if ("close" in last) {
|
||||||
last.low = Math.min(last.low, latest.close);
|
last.low = Math.min(last.low, latest.close);
|
||||||
last.high = Math.max(last.high, latest.close);
|
last.high = Math.max(last.high, latest.close);
|
||||||
@@ -244,24 +234,24 @@ export function init({
|
|||||||
iseries.update(last);
|
iseries.update(last);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case /** @satisfies {DateIndex} */ (0): {
|
case "dateindex": {
|
||||||
iseries.update(last);
|
iseries.update(last);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
if (index === /** @satisfies {WeekIndex} */ (23)) {
|
if (index === "weekindex") {
|
||||||
date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7));
|
date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7));
|
||||||
} else if (index === /** @satisfies {MonthIndex} */ (7)) {
|
} else if (index === "monthindex") {
|
||||||
date.setUTCDate(1);
|
date.setUTCDate(1);
|
||||||
} else if (index === /** @satisfies {QuarterIndex} */ (19)) {
|
} else if (index === "quarterindex") {
|
||||||
const month = date.getUTCMonth();
|
const month = date.getUTCMonth();
|
||||||
date.setUTCMonth(month - (month % 3), 1);
|
date.setUTCMonth(month - (month % 3), 1);
|
||||||
} else if (index === /** @satisfies {SemesterIndex} */ (20)) {
|
} else if (index === "semesterindex") {
|
||||||
const month = date.getUTCMonth();
|
const month = date.getUTCMonth();
|
||||||
date.setUTCMonth(month - (month % 6), 1);
|
date.setUTCMonth(month - (month % 6), 1);
|
||||||
} else if (index === /** @satisfies {YearIndex} */ (24)) {
|
} else if (index === "yearindex") {
|
||||||
date.setUTCMonth(0, 1);
|
date.setUTCMonth(0, 1);
|
||||||
} else if (index === /** @satisfies {DecadeIndex} */ (1)) {
|
} else if (index === "decadeindex") {
|
||||||
date.setUTCFullYear(
|
date.setUTCFullYear(
|
||||||
Math.floor(date.getUTCFullYear() / 10) * 10,
|
Math.floor(date.getUTCFullYear() / 10) * 10,
|
||||||
0,
|
0,
|
||||||
@@ -446,9 +436,7 @@ export function init({
|
|||||||
blueprints[unit]?.forEach((blueprint, order) => {
|
blueprints[unit]?.forEach((blueprint, order) => {
|
||||||
order += orderStart;
|
order += orderStart;
|
||||||
|
|
||||||
const indexes = /** @type {readonly number[]} */ (
|
const indexes = brk.getIndexesFromMetric(blueprint.metric);
|
||||||
metricToIndexes[blueprint.metric]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (indexes.includes(index)) {
|
if (indexes.includes(index)) {
|
||||||
switch (blueprint.type) {
|
switch (blueprint.type) {
|
||||||
@@ -519,12 +507,11 @@ export function init({
|
|||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Accessor<ChartOption>} args.option
|
* @param {Accessor<ChartOption>} args.option
|
||||||
* @param {MetricToIndexes} args.metricToIndexes
|
* @param {BRK} args.brk
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
|
||||||
*/
|
*/
|
||||||
function createIndexSelector({ option, metricToIndexes, signals, utils }) {
|
function createIndexSelector({ option, brk, signals }) {
|
||||||
const choices_ = /** @satisfies {SerializedChartableIndex[]} */ ([
|
const choices_ = /** @satisfies {ChartableIndexName[]} */ ([
|
||||||
"timestamp",
|
"timestamp",
|
||||||
"date",
|
"date",
|
||||||
"week",
|
"week",
|
||||||
@@ -548,7 +535,7 @@ function createIndexSelector({ option, metricToIndexes, signals, utils }) {
|
|||||||
[Object.values(o.top), Object.values(o.bottom)]
|
[Object.values(o.top), Object.values(o.bottom)]
|
||||||
.flat(2)
|
.flat(2)
|
||||||
.filter((blueprint) => !blueprint.metric.startsWith("constant_"))
|
.filter((blueprint) => !blueprint.metric.startsWith("constant_"))
|
||||||
.map((blueprint) => metricToIndexes[blueprint.metric])
|
.map((blueprint) => brk.getIndexesFromMetric(blueprint.metric))
|
||||||
.flat(),
|
.flat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
+2
-6
@@ -1,9 +1,5 @@
|
|||||||
import { domToBlob } from "./4.6.6/dist/index.mjs";
|
import { ios } from "../../core/env";
|
||||||
|
import { domToBlob } from "../../modules/modern-screenshot/4.6.6/dist/index.mjs";
|
||||||
const userAgent = navigator.userAgent.toLowerCase();
|
|
||||||
const iphone = userAgent.includes("iphone");
|
|
||||||
const ipad = userAgent.includes("ipad");
|
|
||||||
const ios = iphone || ipad;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { randomFromArray } from "../core/array";
|
import { randomFromArray } from "../core/array";
|
||||||
|
import { explorerElement } from "../core/elements";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
@@ -6,26 +7,22 @@ import { randomFromArray } from "../core/array";
|
|||||||
* @param {CreateChartElement} args.createChartElement
|
* @param {CreateChartElement} args.createChartElement
|
||||||
* @param {Accessor<ChartOption>} args.option
|
* @param {Accessor<ChartOption>} args.option
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
|
||||||
* @param {WebSockets} args.webSockets
|
* @param {WebSockets} args.webSockets
|
||||||
* @param {Elements} args.elements
|
* @param {Resources} args.resources
|
||||||
* @param {VecsResources} args.vecsResources
|
* @param {BRK} args.brk
|
||||||
* @param {MetricToIndexes} args.metricToIndexes
|
|
||||||
*/
|
*/
|
||||||
export function init({
|
export function init({
|
||||||
colors,
|
colors,
|
||||||
elements,
|
|
||||||
createChartElement,
|
createChartElement,
|
||||||
option,
|
option,
|
||||||
signals,
|
signals,
|
||||||
utils,
|
|
||||||
webSockets,
|
webSockets,
|
||||||
vecsResources,
|
resources,
|
||||||
metricToIndexes,
|
brk,
|
||||||
}) {
|
}) {
|
||||||
const chain = window.document.createElement("div");
|
const chain = window.document.createElement("div");
|
||||||
chain.id = "chain";
|
chain.id = "chain";
|
||||||
elements.explorer.append(chain);
|
explorerElement.append(chain);
|
||||||
|
|
||||||
// vecsResources.getOrCreate(/** @satisfies {Height}*/ (5), "height");
|
// vecsResources.getOrCreate(/** @satisfies {Height}*/ (5), "height");
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ import {
|
|||||||
createHeader,
|
createHeader,
|
||||||
createSelect,
|
createSelect,
|
||||||
} from "../core/dom";
|
} from "../core/dom";
|
||||||
|
import { simulationElement } from "../core/elements";
|
||||||
|
import {
|
||||||
|
numberToDollars,
|
||||||
|
numberToPercentage,
|
||||||
|
numberToUSNumber,
|
||||||
|
} from "../core/format";
|
||||||
import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,18 +22,9 @@ import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
|||||||
* @param {Colors} args.colors
|
* @param {Colors} args.colors
|
||||||
* @param {CreateChartElement} args.createChartElement
|
* @param {CreateChartElement} args.createChartElement
|
||||||
* @param {Signals} args.signals
|
* @param {Signals} args.signals
|
||||||
* @param {Utilities} args.utils
|
* @param {Resources} args.resources
|
||||||
* @param {Elements} args.elements
|
|
||||||
* @param {VecsResources} args.vecsResources
|
|
||||||
*/
|
*/
|
||||||
export function init({
|
export function init({ colors, createChartElement, signals, resources }) {
|
||||||
colors,
|
|
||||||
elements,
|
|
||||||
createChartElement,
|
|
||||||
signals,
|
|
||||||
utils,
|
|
||||||
vecsResources,
|
|
||||||
}) {
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Frequency
|
* @typedef {Object} Frequency
|
||||||
* @property {string} name
|
* @property {string} name
|
||||||
@@ -39,8 +36,6 @@ export function init({
|
|||||||
* @property {Frequency[]} list
|
* @property {Frequency[]} list
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const simulationElement = elements.simulation;
|
|
||||||
|
|
||||||
const domExtended = {
|
const domExtended = {
|
||||||
/**
|
/**
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
@@ -686,7 +681,8 @@ export function init({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const index = () => /** @type {DateIndex} */ (0);
|
/** @type {() => IndexName} */
|
||||||
|
const index = () => "dateindex";
|
||||||
|
|
||||||
createChartElement({
|
createChartElement({
|
||||||
index,
|
index,
|
||||||
@@ -695,9 +691,7 @@ export function init({
|
|||||||
colors,
|
colors,
|
||||||
id: `result`,
|
id: `result`,
|
||||||
fitContent: true,
|
fitContent: true,
|
||||||
vecsResources,
|
resources,
|
||||||
utils,
|
|
||||||
elements,
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
unit: "usd",
|
unit: "usd",
|
||||||
@@ -740,9 +734,7 @@ export function init({
|
|||||||
colors,
|
colors,
|
||||||
id: `bitcoin`,
|
id: `bitcoin`,
|
||||||
fitContent: true,
|
fitContent: true,
|
||||||
vecsResources,
|
resources,
|
||||||
elements,
|
|
||||||
utils,
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
unit: "btc",
|
unit: "btc",
|
||||||
@@ -765,9 +757,7 @@ export function init({
|
|||||||
colors,
|
colors,
|
||||||
id: `average-price`,
|
id: `average-price`,
|
||||||
fitContent: true,
|
fitContent: true,
|
||||||
vecsResources,
|
resources,
|
||||||
utils,
|
|
||||||
elements,
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
unit: "usd",
|
unit: "usd",
|
||||||
@@ -794,11 +784,9 @@ export function init({
|
|||||||
parent: resultsElement,
|
parent: resultsElement,
|
||||||
signals,
|
signals,
|
||||||
colors,
|
colors,
|
||||||
vecsResources,
|
resources,
|
||||||
id: `return-ratio`,
|
id: `return-ratio`,
|
||||||
fitContent: true,
|
fitContent: true,
|
||||||
utils,
|
|
||||||
elements,
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
unit: "usd",
|
unit: "usd",
|
||||||
@@ -820,9 +808,7 @@ export function init({
|
|||||||
colors,
|
colors,
|
||||||
id: `simulation-profitability-ratios`,
|
id: `simulation-profitability-ratios`,
|
||||||
fitContent: true,
|
fitContent: true,
|
||||||
vecsResources,
|
resources,
|
||||||
utils,
|
|
||||||
elements,
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
unit: "percentage",
|
unit: "percentage",
|
||||||
@@ -844,8 +830,8 @@ export function init({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
vecsResources
|
resources.metrics
|
||||||
.getOrCreate(/** @satisfies {DateIndex} */ (0), "price_close")
|
.getOrCreate("price_close", "dateindex")
|
||||||
.fetch()
|
.fetch()
|
||||||
.then((_closes) => {
|
.then((_closes) => {
|
||||||
if (!_closes) return;
|
if (!_closes) return;
|
||||||
@@ -1055,11 +1041,11 @@ export function init({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const f = utils.locale.numberToUSFormat;
|
const f = numberToUSNumber;
|
||||||
/** @param {number} v */
|
/** @param {number} v */
|
||||||
const fd = (v) => utils.formatters.dollars.format(v);
|
const fd = (v) => numberToDollars.format(v);
|
||||||
/** @param {number} v */
|
/** @param {number} v */
|
||||||
const fp = (v) => utils.formatters.percentage.format(v);
|
const fp = (v) => numberToPercentage.format(v);
|
||||||
/**
|
/**
|
||||||
* @param {ColorName} c
|
* @param {ColorName} c
|
||||||
* @param {string} t
|
* @param {string} t
|
||||||
@@ -1098,45 +1084,6 @@ export function init({
|
|||||||
|
|
||||||
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
||||||
|
|
||||||
signals.createEffect(
|
|
||||||
() => 0.073,
|
|
||||||
(lowestAnnual4YReturn) => {
|
|
||||||
const serLowestAnnual4YReturn = c(
|
|
||||||
"cyan",
|
|
||||||
`${fp(lowestAnnual4YReturn)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const lowestAnnual4YReturnPercentage = 1 + lowestAnnual4YReturn;
|
|
||||||
/**
|
|
||||||
* @param {number} power
|
|
||||||
*/
|
|
||||||
function bitcoinValueReturn(power) {
|
|
||||||
return (
|
|
||||||
bitcoinValue *
|
|
||||||
Math.pow(lowestAnnual4YReturnPercentage, power)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const bitcoinValueAfter4y = bitcoinValueReturn(4);
|
|
||||||
const serBitcoinValueAfter4y = c(
|
|
||||||
"purple",
|
|
||||||
fd(bitcoinValueAfter4y),
|
|
||||||
);
|
|
||||||
const bitcoinValueAfter10y = bitcoinValueReturn(10);
|
|
||||||
const serBitcoinValueAfter10y = c(
|
|
||||||
"fuchsia",
|
|
||||||
fd(bitcoinValueAfter10y),
|
|
||||||
);
|
|
||||||
const bitcoinValueAfter21y = bitcoinValueReturn(21);
|
|
||||||
const serBitcoinValueAfter21y = c(
|
|
||||||
"pink",
|
|
||||||
fd(bitcoinValueAfter21y),
|
|
||||||
);
|
|
||||||
|
|
||||||
/** @param {number} v */
|
|
||||||
p4.innerHTML = `The lowest annual return after 4 years has historically been ${serLowestAnnual4YReturn}.<br/>Using it as the baseline, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter21y} after 21 years.`;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
totalInvestedAmountData.set((a) => a);
|
totalInvestedAmountData.set((a) => a);
|
||||||
bitcoinValueData.set((a) => a);
|
bitcoinValueData.set((a) => a);
|
||||||
bitcoinData.set((a) => a);
|
bitcoinData.set((a) => a);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user