From 27f353d52e08b117de699ee86f2cd864685206f4 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 01:44:55 +0500 Subject: [PATCH 01/10] feat: interfold node dashboard --- Cargo.lock | 15 + agent/flow-trace/00_INDEX.md | 2 + agent/flow-trace/02_TOKENS_AND_ACTIVATION.md | 7 + .../flow-trace/03_E3_REQUEST_AND_COMMITTEE.md | 7 + agent/flow-trace/04_DKG_AND_COMPUTATION.md | 18 + .../flow-trace/05_FAILURE_REFUND_SLASHING.md | 4 + .../06_DEACTIVATION_AND_COMPLETION.md | 14 + crates/ciphernode-builder/Cargo.toml | 1 + crates/ciphernode-builder/src/ciphernode.rs | 25 +- .../src/ciphernode_builder.rs | 18 + .../src/global_eventstore_cache.rs | 2 +- crates/cli/Cargo.toml | 1 + crates/cli/src/helpers/telemetry.rs | 75 +- crates/cli/src/start.rs | 54 +- crates/dashboard/Cargo.toml | 10 + crates/dashboard/assets/app.css | 1 + crates/dashboard/assets/app.js | 40 + crates/dashboard/assets/index.html | 15 + crates/dashboard/assets/interfold.svg | 3 + crates/dashboard/src/dashboard.html | 707 ------- crates/dashboard/src/lib.rs | 547 +++-- crates/dashboard/src/projection.rs | 916 +++++++++ crates/dashboard/src/updates.rs | 201 ++ crates/entrypoint/src/start/start.rs | 2 + crates/events/src/eventbus.rs | 10 +- .../ciphernode_deregistration_requested.rs | 27 + .../committee_activation_changed.rs | 27 + .../committee_formation_failed.rs | 28 + .../committee_viability_updated.rs | 29 + .../src/interfold_event/evm_log_observed.rs | 44 + .../src/interfold_event/input_published.rs | 32 + .../interfold_event/license_bond_updated.rs | 30 + crates/events/src/interfold_event/mod.rs | 53 +- .../src/interfold_event/reward_claimed.rs | 29 + .../src/interfold_event/reward_credited.rs | 29 + .../interfold_event/rewards_distributed.rs | 29 + crates/evm/src/contracts.rs | 59 +- .../evm/src/domain/bonding_registry_events.rs | 75 +- .../src/domain/ciphernode_registry_events.rs | 186 +- crates/evm/src/domain/evm_event_catalog.rs | 486 +++++ crates/evm/src/domain/evm_log_observation.rs | 78 + crates/evm/src/domain/interfold_events.rs | 257 ++- crates/evm/src/domain/mod.rs | 2 + crates/evm/src/domain/slashing_events.rs | 46 +- crates/evm/src/lib.rs | 2 + crates/evm/src/operator_status.rs | 77 + crates/logger/Cargo.toml | 3 + crates/logger/src/lib.rs | 5 + crates/logger/src/log_collector.rs | 350 ++++ crates/logger/src/logger.rs | 269 ++- crates/logger/src/tracing_layer.rs | 158 ++ crates/multithread/src/effect_gate.rs | 232 +++ crates/multithread/src/lib.rs | 1 + crates/multithread/src/multithread.rs | 36 +- crates/net/src/domain/mod.rs | 2 + crates/net/src/domain/network_status.rs | 177 ++ crates/net/src/lib.rs | 1 + crates/net/src/net_interface.rs | 31 +- crates/net/src/net_interface_handle.rs | 25 +- crates/request/src/actors/router.rs | 99 +- crates/request/src/domain/routing.rs | 39 +- crates/wasm/package.json | 2 +- examples/CRISP/interfold.config.yaml | 12 +- .../packages/crisp-zk-inputs/package.json | 2 +- packages/interfold-node/.gitignore | 24 + packages/interfold-node/README.md | 20 + packages/interfold-node/eslint.config.js | 23 + packages/interfold-node/index.html | 14 + packages/interfold-node/package.json | 33 + packages/interfold-node/src/App.css | 1779 +++++++++++++++++ packages/interfold-node/src/App.tsx | 141 ++ packages/interfold-node/src/api.ts | 150 ++ packages/interfold-node/src/assets/hero.png | Bin 0 -> 13057 bytes .../src/components/EventTimeline.tsx | 154 ++ .../src/components/StageFlow.tsx | 41 + packages/interfold-node/src/format.ts | 41 + packages/interfold-node/src/index.css | 73 + packages/interfold-node/src/main.tsx | 12 + packages/interfold-node/src/types.ts | 208 ++ .../interfold-node/src/views/E3Inspector.tsx | 219 ++ .../interfold-node/src/views/EventsView.tsx | 90 + .../src/views/FlowGraphView.tsx | 254 +++ .../interfold-node/src/views/LogsView.tsx | 117 ++ .../interfold-node/src/views/Overview.tsx | 210 ++ .../interfold-node/src/views/UpdatesView.tsx | 101 + packages/interfold-node/tsconfig.app.json | 24 + packages/interfold-node/tsconfig.json | 4 + packages/interfold-node/tsconfig.node.json | 24 + packages/interfold-node/vite.config.ts | 35 + pnpm-lock.yaml | 1349 +++++++++++-- pnpm-workspace.yaml | 1 + templates/default/client/package.json | 2 +- 92 files changed, 9637 insertions(+), 1270 deletions(-) create mode 100644 crates/dashboard/assets/app.css create mode 100644 crates/dashboard/assets/app.js create mode 100644 crates/dashboard/assets/index.html create mode 100644 crates/dashboard/assets/interfold.svg delete mode 100644 crates/dashboard/src/dashboard.html create mode 100644 crates/dashboard/src/projection.rs create mode 100644 crates/dashboard/src/updates.rs create mode 100644 crates/events/src/interfold_event/ciphernode_deregistration_requested.rs create mode 100644 crates/events/src/interfold_event/committee_activation_changed.rs create mode 100644 crates/events/src/interfold_event/committee_formation_failed.rs create mode 100644 crates/events/src/interfold_event/committee_viability_updated.rs create mode 100644 crates/events/src/interfold_event/evm_log_observed.rs create mode 100644 crates/events/src/interfold_event/input_published.rs create mode 100644 crates/events/src/interfold_event/license_bond_updated.rs create mode 100644 crates/events/src/interfold_event/reward_claimed.rs create mode 100644 crates/events/src/interfold_event/reward_credited.rs create mode 100644 crates/events/src/interfold_event/rewards_distributed.rs create mode 100644 crates/evm/src/domain/evm_event_catalog.rs create mode 100644 crates/evm/src/domain/evm_log_observation.rs create mode 100644 crates/evm/src/operator_status.rs create mode 100644 crates/logger/src/log_collector.rs create mode 100644 crates/logger/src/tracing_layer.rs create mode 100644 crates/multithread/src/effect_gate.rs create mode 100644 crates/net/src/domain/network_status.rs create mode 100644 packages/interfold-node/.gitignore create mode 100644 packages/interfold-node/README.md create mode 100644 packages/interfold-node/eslint.config.js create mode 100644 packages/interfold-node/index.html create mode 100644 packages/interfold-node/package.json create mode 100644 packages/interfold-node/src/App.css create mode 100644 packages/interfold-node/src/App.tsx create mode 100644 packages/interfold-node/src/api.ts create mode 100644 packages/interfold-node/src/assets/hero.png create mode 100644 packages/interfold-node/src/components/EventTimeline.tsx create mode 100644 packages/interfold-node/src/components/StageFlow.tsx create mode 100644 packages/interfold-node/src/format.ts create mode 100644 packages/interfold-node/src/index.css create mode 100644 packages/interfold-node/src/main.tsx create mode 100644 packages/interfold-node/src/types.ts create mode 100644 packages/interfold-node/src/views/E3Inspector.tsx create mode 100644 packages/interfold-node/src/views/EventsView.tsx create mode 100644 packages/interfold-node/src/views/FlowGraphView.tsx create mode 100644 packages/interfold-node/src/views/LogsView.tsx create mode 100644 packages/interfold-node/src/views/Overview.tsx create mode 100644 packages/interfold-node/src/views/UpdatesView.tsx create mode 100644 packages/interfold-node/tsconfig.app.json create mode 100644 packages/interfold-node/tsconfig.json create mode 100644 packages/interfold-node/tsconfig.node.json create mode 100644 packages/interfold-node/vite.config.ts diff --git a/Cargo.lock b/Cargo.lock index 37af0cd6dc..0e19d3cda1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3118,6 +3118,7 @@ dependencies = [ "e3-fhe", "e3-fhe-params", "e3-keyshare", + "e3-logger", "e3-multithread", "e3-net", "e3-request", @@ -3159,6 +3160,7 @@ dependencies = [ "e3-evm", "e3-fhe-params", "e3-init", + "e3-logger", "e3-support-scripts", "e3-trbfv", "e3-utils", @@ -3275,7 +3277,17 @@ dependencies = [ name = "e3-dashboard" version = "0.2.2" dependencies = [ + "actix-web", + "alloy", "anyhow", + "e3-ciphernode-builder", + "e3-config", + "e3-events", + "e3-evm", + "e3-logger", + "e3-net", + "e3-utils", + "futures", "reqwest", "serde", "serde_json", @@ -3576,7 +3588,10 @@ dependencies = [ "actix", "base64", "e3-events", + "serde", + "serde_json", "tracing", + "tracing-subscriber", ] [[package]] diff --git a/agent/flow-trace/00_INDEX.md b/agent/flow-trace/00_INDEX.md index 90b84a47a4..6d613cadc3 100644 --- a/agent/flow-trace/00_INDEX.md +++ b/agent/flow-trace/00_INDEX.md @@ -192,3 +192,5 @@ _Found during source-code cross-referencing of these trace documents._ | 5 | **Active-job load balancing bug fixed** | Info | The Rust `NodeStateStore.available_tickets()` subtracts `active_jobs` from total tickets, reducing the chance of busy nodes being selected for new E3s. Previously, the `Sortition` actor's `Handler` was missing match arms for `E3Failed` and `E3StageChanged`, causing these events to fall to the default `_ => ()` — the typed handlers for decrementing jobs were dead code. This has been fixed: E3Failed and E3StageChanged are now routed to their handlers, and `finalized_committees` is cleaned up in `decrement_jobs_for_e3` to prevent unbounded memory growth. | | 6 | **Committee member expulsion** | Info | `SlashingManager` can call `expelCommitteeMember()` mid-DKG. The `Sortition` actor enriches the raw `CommitteeMemberExpelled` event with the expelled member's `party_id` (resolved from its stored `Committee` list) and re-publishes it. `ThresholdKeyshare` then uses the enriched `party_id` to update its collectors, potentially completing DKG with fewer parties. `ThresholdKeyshare` itself does not hold committee state. | | 7 | **ProofRequestActor failure bridge fixed** | Info | `ProofRequestActor` no longer leaves proof publication suppressed under log-only "will not be published" exits. `ComputeRequestError` and local proof-signing failures for DKG-path proofs (`C0` through `C5`) now emit `E3Failed { failed_at_stage: CommitteeFinalized, reason: DKGInvalidShares }`, while decryption-path proofs (`C6` and `C7`) emit `E3Failed { failed_at_stage: CiphertextReady, reason: DecryptionInvalidShares }`. | +| 8 | **Settlement receipts isolated** | Resolved | Durable EVM receipts such as `RewardCredited` and `RewardClaimed` are global audit/projection facts. The E3 router no longer sends them into a completed per-E3 context, so reward fan-out cannot reopen a finished E3 or produce false `AlreadyCompleted` failures. | +| 9 | **Replay-safe compute effects** | Resolved | `ComputeEffectGate` subscribes before EventStore replay, buffers `ComputeRequest`s while effects are disabled, deduplicates equivalent requests, prefers the newest hydrated retry, cancels terminal E3 work, and releases pending effects only after `EffectsEnabled`. This closes the mid-E3 compute-loss window without changing durable event order. | diff --git a/agent/flow-trace/02_TOKENS_AND_ACTIVATION.md b/agent/flow-trace/02_TOKENS_AND_ACTIVATION.md index 0c0937388f..e7b96d5d29 100644 --- a/agent/flow-trace/02_TOKENS_AND_ACTIVATION.md +++ b/agent/flow-trace/02_TOKENS_AND_ACTIVATION.md @@ -473,3 +473,10 @@ enforcement based on immutable policy curves. Key changes: `BondingRegistry.getTicketBalanceAtBlock(node, c.requestBlock - 1)` — pass the value through unchanged; the parameter is now a timepoint per EIP-6372 rather than a block number, which is required for the tFOLD timestamp clock to be valid. + +### Node-operator event projection + +The EVM reader now emits typed `LicenseBondUpdated` and `CiphernodeDeregistrationRequested` events +alongside the existing ticket and activation events. The local dashboard rebuilds each chain's +registered/active node sets and the operator's ticket, license, and exit state from EventStore +history; it does not parse human-oriented CLI status output. diff --git a/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md b/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md index 7fe29113ba..e728beae5a 100644 --- a/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md +++ b/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md @@ -414,3 +414,10 @@ finalize the failure. Default `markFailedGracePeriod = 0` preserves the legacy p `Committee.requestBlock` stores `block.timestamp` (EIP-6372 timestamp mode) so that `getPastVotes` / `getTicketBalanceAtBlock` lookups against the `InterfoldTicketToken` resolve consistently across L1 and L2 clocks. The field name is preserved for storage / event ABI compatibility. + +### Committee observability events + +The EVM reader has typed coverage for `CommitteeFormationFailed`, `CommitteeActivationChanged`, and +`CommitteeViabilityUpdated` in addition to ticket submission, finalization, publication, and +expulsion. These facts are stored in the E3's chain aggregate and projected into the dashboard's +committee stage, including submitted/required thresholds and post-expulsion viability. diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index 6a29c5d6de..72bb1c05df 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -1070,3 +1070,21 @@ ACTIVE AGGREGATOR collects PK_share₁ + PK_share₂ + PK_share₃ → Produces aggregate_PK (public, published on-chain) → Anyone can encrypt, only committee can decrypt ``` + +## Durable flow tracing + +The dashboard renders event ID, causation ID, origin ID, HLC timestamp, block watermark, aggregate, +and source exactly as the local EventStore recorded them. Observability does not change the +sequencing or gossip path. Local cause/effect chains are exact; received network events follow the +protocol's established receiver-local context semantics. + +`InputPublished`, `RewardsDistributed`, `RewardCredited`, and `RewardClaimed` have typed EVM +translations. `CommitteePublished` and `PlaintextOutputPublished` are also translated from their +canonical on-chain logs. Every current interface signature is catalogued: logs without a +protocol-driving typed decoder become named, lossless `EvmLogObserved` facts, while a signature not +present in the running ABI catalog is exposed as `UnknownEvmLog` with raw topics/data. + +During restart, `ComputeEffectGate` observes replay before compute workers are effects-enabled. It +buffers and deduplicates `ComputeRequest`s, prefers the newest regenerated request, cancels terminal +E3 work, and releases pending jobs only after `EffectsEnabled`. The gate changes effect timing, not +durable event order or audit state. diff --git a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md index 63ad0c7d9f..41c0f5a8fd 100644 --- a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md +++ b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md @@ -1149,6 +1149,10 @@ Applied audit findings: **C-05, H-05, H-06, H-07, H-09, H-10, H-24, M-14, M-15, - `SlashProposed` and `SlashExecuted` carry a `Lane lane` field (`LaneA = 0`, `LaneB = 1`) so off-chain indexers can disambiguate the two paths without re-deriving from policy bits. +The Rust minimal ABI matches the current `SlashExecuted` signature, including `bool executed` and +`Lane lane` (`uint8` at the ABI boundary). The typed actor event keeps the fields required for +expulsion handling; the full contract log is also retained by the raw EVM observability fallback. + ### Upgrade posture - `SlashingManager` is **non-upgradeable** by design (transparent proxy removed). Migrations require diff --git a/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md b/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md index 4ca7fa79ef..109db382a8 100644 --- a/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md +++ b/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md @@ -220,11 +220,20 @@ On restart: │ 5. Historical libp2p sync retries failed aggregate fetches after reconnects │ and also on bounded retry intervals even without a new connection event │ 6. Sort & publish merged events by HLC timestamp +│ → ComputeEffectGate has already subscribed and buffers ComputeRequest +│ effects, deduplicating semantic retries while replay is in progress │ 7. Enable effects (writers may submit only after this point) +│ → Gate cancels work for terminal E3s and releases only the newest +│ pending request for each in-flight semantic compute operation │ 8. SyncEnded → live operations begin └─ Node resumes from where it left off ``` +Post-completion EVM receipts (`RewardsDistributed`, `RewardCredited`, `RewardClaimed`, and related +settlement observations) remain in EventStore for auditing and operator projections. The router does +not deliver them to a completed per-E3 context because they report settlement; they do not resume +protocol execution. + --- ## Rust-Side: E3 Lifecycle Coordinator (durable stage tracking) @@ -268,6 +277,11 @@ The coordinator is safe by construction during EventStore replay: observing a re event simply re-derives the same monotonic stage, so the restored map is identical whether built live or from replay. +The node-operator dashboard uses the same replay property. It pages every configured EventStore +aggregate and incrementally derives E3 stages, committees, tickets, failures, and rewards. The +projection is disposable and is rebuilt on restart; EventStore remains the only durable protocol +history. + --- ## Exit Queue Timing diff --git a/crates/ciphernode-builder/Cargo.toml b/crates/ciphernode-builder/Cargo.toml index 1ffeb8188d..684f1f85f1 100644 --- a/crates/ciphernode-builder/Cargo.toml +++ b/crates/ciphernode-builder/Cargo.toml @@ -21,6 +21,7 @@ e3-evm.workspace = true e3-fhe.workspace = true e3-fhe-params.workspace = true e3-keyshare.workspace = true +e3-logger.workspace = true e3-multithread.workspace = true e3-net.workspace = true e3-slashing.workspace = true diff --git a/crates/ciphernode-builder/src/ciphernode.rs b/crates/ciphernode-builder/src/ciphernode.rs index 60a88a9dcd..86986b06ab 100644 --- a/crates/ciphernode-builder/src/ciphernode.rs +++ b/crates/ciphernode-builder/src/ciphernode.rs @@ -8,9 +8,11 @@ use actix::Addr; use anyhow::Result; use e3_data::{DataStore, InMemStore, StoreAddr}; use e3_events::{BusHandle, HistoryCollector, InterfoldEvent}; -use e3_net::NetChannelBridge; +use e3_net::{NetChannelBridge, NetworkStatus}; use libp2p::PeerId; +use crate::global_eventstore_cache::EventStoreReader; + /// The kind of network interface backing a ciphernode. #[derive(Debug, Clone)] pub enum NetInterfaceKind { @@ -47,6 +49,9 @@ pub struct CiphernodeHandle { pub errors: Option>>, pub peer_id: PeerId, pub net_interface: NetInterfaceKind, + pub network_status: NetworkStatus, + pub eventstore: EventStoreReader, + pub aggregate_ids: Vec, } impl PartialEq for CiphernodeHandle { @@ -66,6 +71,9 @@ impl CiphernodeHandle { errors: Option>>, peer_id: PeerId, net_interface: NetInterfaceKind, + network_status: NetworkStatus, + eventstore: EventStoreReader, + aggregate_ids: Vec, ) -> Self { Self { address, @@ -75,6 +83,9 @@ impl CiphernodeHandle { errors, peer_id, net_interface, + network_status, + eventstore, + aggregate_ids, } } @@ -94,6 +105,18 @@ impl CiphernodeHandle { self.address.clone() } + pub fn network_status(&self) -> NetworkStatus { + self.network_status.clone() + } + + pub fn eventstore(&self) -> EventStoreReader { + self.eventstore.clone() + } + + pub fn aggregate_ids(&self) -> &[usize] { + &self.aggregate_ids + } + pub fn store(&self) -> &DataStore { &self.store } diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index d7c9fa9ff6..b0733df029 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -28,6 +28,7 @@ use e3_evm::{ }; use e3_fhe::ext::FheExtension; use e3_keyshare::ext::ThresholdKeyshareExtension; +use e3_logger::attach_protocol_logger; use e3_multithread::{Multithread, MultithreadReport, TaskPool}; use e3_net::{ create_channel_bridge, setup_libp2p_keypair, setup_net, setup_net_interface, @@ -72,6 +73,7 @@ pub struct CiphernodeBuilder { in_mem_store: Option>, keyshare: Option, logging: bool, + name: Option, multithread_cache: Option>, multithread_concurrent_jobs: Option, multithread_report: Option>, @@ -136,6 +138,7 @@ impl CiphernodeBuilder { in_mem_store: None, keyshare: None, logging: false, + name: None, multithread_cache: None, multithread_concurrent_jobs: None, multithread_report: None, @@ -171,6 +174,12 @@ impl CiphernodeBuilder { self } + /// Set the node name for dashboard display and log attribution. + pub fn with_name(mut self, name: &str) -> Self { + self.name = Some(name.to_string()); + self + } + /// Use the TrBFV feature pub fn with_trbfv(mut self) -> Self { self.keyshare = Some(KeyshareKind::Threshold); @@ -497,6 +506,11 @@ impl CiphernodeBuilder { let addr = provider_cache.ensure_signer().await?.address().to_string(); let bus = event_system.handle()?.enable(&addr); + if self.logging { + let logger_name = self.name.as_deref().unwrap_or("ciphernode"); + attach_protocol_logger(logger_name, &bus); + } + // Setup sortition let (sortition, ciphernode_selector) = self.setup_sortition(&bus, &repositories, &addr).await?; @@ -531,6 +545,7 @@ impl CiphernodeBuilder { // Setup networking let topic = "interfold-gossip"; let (peer_id, interface, net_kind) = self.setup_networking(&store, topic).await?; + let network_status = interface.status(); setup_net(topic, bus.clone(), eventstore.ts(), interface)?; // Run the sync routine @@ -551,6 +566,9 @@ impl CiphernodeBuilder { errors, peer_id, net_kind, + network_status, + eventstore, + aggregate_config.indexed_ids(), )) } diff --git a/crates/ciphernode-builder/src/global_eventstore_cache.rs b/crates/ciphernode-builder/src/global_eventstore_cache.rs index 4f60f4e936..2bd9af3deb 100644 --- a/crates/ciphernode-builder/src/global_eventstore_cache.rs +++ b/crates/ciphernode-builder/src/global_eventstore_cache.rs @@ -9,7 +9,7 @@ use std::sync::OnceLock; use actix::Recipient; use e3_events::{EventStoreQueryBy, SeqAgg, TsAgg}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct EventStoreReader { query_by_seq: Recipient>, query_by_ts: Recipient>, diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index ec51844f26..5dae6ba8c1 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -26,6 +26,7 @@ e3-crypto = { workspace = true } e3-entrypoint = { workspace = true } e3-events = { workspace = true } e3-evm = { workspace = true } +e3-logger = { workspace = true } e3-trbfv = { workspace = true } e3-fhe-params = { workspace = true } e3-zk-helpers = { workspace = true } diff --git a/crates/cli/src/helpers/telemetry.rs b/crates/cli/src/helpers/telemetry.rs index 57b4f5770e..1e0e696eb8 100644 --- a/crates/cli/src/helpers/telemetry.rs +++ b/crates/cli/src/helpers/telemetry.rs @@ -6,67 +6,82 @@ use anyhow::Result; use e3_config::AppConfig; +use e3_logger::{LogCollector, OperationalLogLayer}; use opentelemetry::trace::TracerProvider; use opentelemetry_otlp::{Protocol, WithExportConfig}; -use opentelemetry_sdk::trace::SdkTracerProvider; -use opentelemetry_sdk::Resource; +use opentelemetry_sdk::{trace::SdkTracerProvider, Resource}; +use std::path::PathBuf; use tracing::Level; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; pub fn setup_simple_tracing(log_level: Level) { - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) + LogCollector::init("interfold", None); + let _ = tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .compact() + .with_target(false), + ) + .with(OperationalLogLayer) .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .try_init() - .ok(); + .try_init(); } pub fn setup_tracing(config: &AppConfig, log_level: Level) -> Result<()> { let name = config.name(); - let maybe_otel_endpoint = config.otel(); + LogCollector::init(&name, Some(operational_log_path(config))); - match maybe_otel_endpoint { + match config.otel() { Some(endpoint) => { - let otlp_exporter = opentelemetry_otlp::SpanExporter::builder() + let exporter = opentelemetry_otlp::SpanExporter::builder() .with_tonic() .with_endpoint(endpoint) .with_protocol(Protocol::Grpc) .build()?; - - let resource = Resource::builder().with_service_name(name).build(); - let provider = SdkTracerProvider::builder() - .with_batch_exporter(otlp_exporter) - .with_resource(resource) + .with_batch_exporter(exporter) + .with_resource(Resource::builder().with_service_name(name).build()) .build(); + let telemetry = + tracing_opentelemetry::layer().with_tracer(provider.tracer("interfold-ciphernode")); - let tracer = provider.tracer("interfold"); - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) + let _ = tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .compact() + .with_target(false), + ) + .with(OperationalLogLayer) .with(telemetry) .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .try_init() - .ok(); + .try_init(); } None => { - // TODO: we might be able to dedupe this with above but there were - // issues with telemetry so have left this like so for now - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) + let _ = tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .compact() + .with_target(false), + ) + .with(OperationalLogLayer) .with(tracing_subscriber::filter::LevelFilter::from_level( log_level, )) - .try_init() - .ok(); + .try_init(); } } - Ok(()) } + +fn operational_log_path(config: &AppConfig) -> PathBuf { + config + .log_file() + .parent() + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(".")) + .join("ciphernode.jsonl") +} diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index ebe5d488c5..ba7349378c 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -36,16 +36,6 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { launch_socket_server(config.ctrl_port()); - if let Some(dashboard_port) = config.dashboard_port() { - let ctrl_port = config.ctrl_port(); - let node_name = config.name(); - let config_path = config.config_yaml().to_str().map(|s| s.to_string()); - tokio::task::spawn_local(async move { - e3_dashboard::start_dashboard(dashboard_port, ctrl_port, node_name, config_path).await; - }); - info!("Dashboard available at http://0.0.0.0:{}", dashboard_port); - } - let node = tokio::select! { // build the ciphernode and if it completes first return the result result = build_ciphernode(&mut config, peers) => result, @@ -56,6 +46,47 @@ pub async fn execute(mut config: AppConfig, peers: Vec) -> Result<()> { } }?; + if let Some(dashboard_port) = config.dashboard_port() { + let chains = node + .aggregate_ids() + .iter() + .copied() + .filter(|aggregate| *aggregate != 0) + .map(|aggregate| { + let id = aggregate as u64; + let name = config + .chains() + .iter() + .find(|chain| chain.chain_id == Some(id)) + .map(|chain| chain.name.clone()) + .unwrap_or_else(|| format!("Chain {id}")); + e3_dashboard::DashboardChain { id, name } + }) + .collect(); + let runtime = e3_dashboard::DashboardRuntime { + node_name: config.name(), + address: node.address.clone(), + peer_id: node.peer_id.to_string(), + quic_port: config.quic_port(), + dashboard_port, + version: env!("CARGO_PKG_VERSION").to_owned(), + chains, + }; + let state = e3_dashboard::DashboardState::new( + runtime, + node.eventstore(), + node.aggregate_ids().to_vec(), + node.network_status(), + config.chains().clone(), + ); + tokio::task::spawn_local(async move { + if let Err(error) = e3_dashboard::start_dashboard(dashboard_port, state).await { + error!(%error, "node dashboard stopped"); + } + }); + info!("Dashboard available at http://127.0.0.1:{dashboard_port}"); + } + info!( "LAUNCHING CIPHERNODE: ({}/{}/{})", config.name(), @@ -125,4 +156,7 @@ pub async fn graceful_shutdown(node: Option) { tokio::time::sleep(Duration::from_secs(2)).await; info!("Graceful shutdown complete"); + if let Some(logs) = e3_logger::LogCollector::global() { + logs.flush(); + } } diff --git a/crates/dashboard/Cargo.toml b/crates/dashboard/Cargo.toml index 4f953fac32..e3a655ef26 100644 --- a/crates/dashboard/Cargo.toml +++ b/crates/dashboard/Cargo.toml @@ -6,7 +6,17 @@ license.workspace = true description = "Lightweight node monitoring dashboard for Interfold ciphernodes" [dependencies] +actix-web.workspace = true +alloy.workspace = true anyhow.workspace = true +e3-ciphernode-builder.workspace = true +e3-config.workspace = true +e3-events.workspace = true +e3-evm.workspace = true +e3-logger = { workspace = true } +e3-net.workspace = true +e3-utils.workspace = true +futures.workspace = true reqwest.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/dashboard/assets/app.css b/crates/dashboard/assets/app.css new file mode 100644 index 0000000000..0ccd597df1 --- /dev/null +++ b/crates/dashboard/assets/app.css @@ -0,0 +1 @@ +.page{min-height:100vh;display:flex;flex-direction:column}.site-head{position:sticky;top:0;z-index:50;border-bottom:1px solid var(--rule);background:color-mix(in oklab,var(--paper) 91%,transparent);-webkit-backdrop-filter:blur(16px) saturate(130%);backdrop-filter:blur(16px) saturate(130%)}.site-head__inner{width:min(1500px,100%);height:64px;margin:0 auto;padding:0 28px;display:flex;align-items:center;gap:16px}.wordmark{width:140px;height:24px;border:0;padding:0;background:transparent;cursor:pointer}.wordmark span{display:block;width:100%;height:100%;background:var(--ink);mask:url(/assets/interfold.svg) no-repeat left center / contain;-webkit-mask:url(/assets/interfold.svg) no-repeat left center / contain}.product-name{padding-left:16px;border-left:1px solid var(--rule);color:var(--ink-3);font-size:13px;white-space:nowrap}.site-nav{display:flex;align-items:center;gap:3px;margin-left:24px}.site-nav__link{border:0;background:transparent;padding:7px 12px;border-radius:999px;color:var(--ink-3);font-size:14px;cursor:pointer;transition:.12s ease}.site-nav__link:hover{color:var(--ink);background:var(--rule-soft)}.site-nav__link--on{color:var(--accent-ink);background:var(--accent-bg)}.node-chip{margin-left:auto;display:inline-flex;align-items:center;gap:8px;max-width:230px;padding:6px 10px;border:1px solid var(--rule);border-radius:999px;background:var(--paper-2);color:var(--ink-2);font-size:12px}.node-chip__dot,.live-dot{width:7px;height:7px;flex:none;border-radius:50%;background:var(--accent-deep);box-shadow:0 0 0 4px var(--accent-bg)}.node-chip__dot--waiting{background:var(--warning);box-shadow:0 0 0 4px var(--warning-bg)}.app-main{flex:1;width:min(1320px,100%);margin:0 auto;padding:42px 28px 80px}.app-main--inspector{width:min(1600px,100%);padding-top:0;padding-bottom:0}.view-stack{display:flex;flex-direction:column;gap:28px}.view-intro,.view-title{display:flex;justify-content:space-between;gap:36px;align-items:flex-end}.view-intro>div:first-child{max-width:780px}.eyebrow,.section-kicker{display:flex;align-items:center;gap:9px;margin-bottom:10px;color:var(--accent-deep);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.view-intro h1,.view-title h1,.trace-head h1,.blank-state h1,.loading-page h1{margin:0;color:var(--ink);font-family:var(--serif);font-size:clamp(34px,4vw,52px);font-weight:400;line-height:1.08;letter-spacing:-.02em}.view-intro p,.view-title p,.trace-head p{margin:14px 0 0;color:var(--ink-3);font-size:16px}.identity-card{width:280px;padding:18px 20px;display:flex;flex-direction:column;gap:7px;border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);box-shadow:0 12px 28px -25px #14201b66}.identity-card>span:first-child{color:var(--ink-4);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.identity-card strong{color:var(--ink);font-size:13px}.identity-card>span:last-child{color:var(--ink-4);font-size:11px}.metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);overflow:hidden}.metric{min-width:0;padding:22px 24px;display:flex;flex-direction:column;border-right:1px solid var(--rule)}.metric:last-child{border-right:0}.metric__label{color:var(--ink-4);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.metric__value{margin:7px 0 3px;color:var(--ink);font-family:var(--serif);font-size:32px;font-weight:400;line-height:1}.metric__note{color:var(--ink-3);font-size:12px}.metric--bad .metric__value{color:var(--danger)}.overview-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:20px;align-items:start}.panel{min-width:0;padding:24px;border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);box-shadow:0 14px 36px -34px #14201b73}.panel--compact{padding:18px}.panel--wide{width:100%}.panel__head{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;margin-bottom:18px}.panel__head .section-kicker{margin-bottom:4px}.panel__head h2{margin:0;color:var(--ink);font-family:var(--serif);font-size:23px;font-weight:400;line-height:1.2}.panel__aside{color:var(--ink-4);font-size:11px;white-space:nowrap}.health-pill,.status-tag,.trace-status{display:inline-flex;align-items:center;border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.health-pill{padding:5px 9px;background:var(--accent-bg);color:var(--accent-deep)}.health-pill--bad{color:var(--warning);background:var(--warning-bg)}.peer-list,.chain-list{display:flex;flex-direction:column;gap:8px}.peer-row{display:grid;grid-template-columns:9px minmax(0,1fr) auto;gap:11px;align-items:center;padding:12px 0;border-bottom:1px solid var(--rule-soft)}.peer-row:last-child{border-bottom:0}.peer-row__status{width:7px;height:7px;border-radius:50%;background:var(--accent-deep)}.peer-row>div{min-width:0;display:flex;flex-direction:column}.peer-row strong{color:var(--ink-2);font-size:12px}.peer-row div span{color:var(--ink-4);font-size:10px;overflow:hidden;text-overflow:ellipsis}.peer-row__direction{color:var(--ink-3);font-size:10px}.listen-addresses{margin-top:14px;padding-top:14px;display:flex;flex-wrap:wrap;gap:7px;align-items:center;border-top:1px solid var(--rule-soft)}.listen-addresses>span{margin-right:4px;color:var(--ink-4);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.listen-addresses code{padding:3px 7px;border-radius:5px;background:var(--paper-3);color:var(--ink-3);font-size:9px}.chain-card{padding:14px;border:1px solid var(--rule-soft);border-radius:var(--radius-sm);background:#fdfdfb}.chain-card__head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.chain-card__head strong{font-size:13px}.status-tag{padding:4px 7px;background:var(--paper-3);color:var(--ink-3)}.status-tag--complete{background:var(--accent-bg);color:var(--accent-deep)}.status-tag--active{background:var(--warning-bg);color:var(--warning)}.status-tag--failed{background:var(--danger-bg);color:var(--danger)}.mini-dl{margin:0;display:grid;grid-template-columns:1fr 1fr;gap:10px 18px}.mini-dl div{min-width:0;display:flex;flex-direction:column}.mini-dl dt{color:var(--ink-4);font-size:9px;text-transform:uppercase;letter-spacing:.06em}.mini-dl dd{margin:2px 0 0;overflow:hidden;color:var(--ink-2);font-size:12px;text-overflow:ellipsis}.event-timeline{display:flex;flex-direction:column}.event-card{position:relative;border-bottom:1px solid var(--rule-soft);transition:background .15s,box-shadow .15s}.event-card:last-child{border-bottom:0}.event-card--located{z-index:1;border-radius:8px;background:var(--accent-bg);box-shadow:0 0 0 3px var(--accent-soft)}.event-card__summary{width:100%;min-height:64px;padding:10px 4px;display:grid;grid-template-columns:22px 88px minmax(0,1fr) auto;gap:10px;align-items:center;border:0;background:transparent;text-align:left;cursor:pointer}.event-card__summary:hover{background:#fafbf8}.event-card__rail{position:relative;align-self:stretch;display:flex;justify-content:center}.event-card__rail:after{content:"";position:absolute;top:27px;bottom:-22px;width:1px;background:var(--rule)}.event-card:last-child .event-card__rail:after{display:none}.event-card__dot{position:relative;z-index:1;width:8px;height:8px;margin-top:17px;border:2px solid var(--paper-2);border-radius:50%;background:var(--accent-deep);box-shadow:0 0 0 1px var(--accent-soft)}.event-card--warn .event-card__dot{background:var(--warning);box-shadow:0 0 0 1px #ead5a6}.event-card--error .event-card__dot{background:var(--danger);box-shadow:0 0 0 1px #e8b3b0}.event-card--debug .event-card__dot{background:var(--ink-4);box-shadow:0 0 0 1px var(--rule)}.event-card__time{color:var(--ink-4);font-size:10px}.event-card__identity{min-width:0;display:flex;flex-direction:column;gap:5px}.event-card__identity strong{overflow:hidden;color:var(--ink-2);font-size:13px;font-weight:560;text-overflow:ellipsis}.event-card__meta{display:flex;flex-wrap:wrap;gap:7px;align-items:center;color:var(--ink-4);font-size:10px}.source{padding:2px 5px;border-radius:4px;background:var(--accent-bg);color:var(--accent-deep);font-size:8px;font-weight:700;letter-spacing:.05em}.source--network{background:#f0edfa;color:var(--network)}.source--evm{background:#f9f0df;color:var(--evm)}.event-card__cause{display:flex;align-items:center;gap:12px;color:var(--ink-4);font-size:10px}.chevron{width:22px;height:22px;display:grid;place-items:center;border:1px solid var(--rule);border-radius:50%;color:var(--ink-3);font-size:13px}.event-card__detail{margin:0 4px 14px 32px;padding:16px;border:1px solid var(--rule-soft);border-radius:var(--radius-sm);background:#fbfcfa}.trace-grid{margin:0 0 14px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.trace-grid div{min-width:0;display:flex;flex-direction:column}.trace-grid dt,.payload-head{color:var(--ink-4);font-size:9px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.trace-grid dd{margin:3px 0 0;color:var(--ink-2);font-size:11px;overflow:hidden;text-overflow:ellipsis}.trace-link{padding:0;border:0;background:transparent;color:var(--accent-deep);cursor:pointer;text-decoration:underline;text-decoration-color:var(--accent-soft);text-underline-offset:3px}.trace-successors{max-height:82px;display:flex;flex-direction:column;gap:3px;overflow:auto!important}.nav-update-dot{width:6px;height:6px;margin-left:5px;display:inline-block;border-radius:50%;background:var(--warning);box-shadow:0 0 0 3px var(--warning-bg)}.flow-view-title{align-items:end}.flow-selector{min-width:250px;display:flex;flex-direction:column;gap:5px;color:var(--ink-4);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.flow-selector select{width:100%;padding:9px 11px;border:1px solid var(--rule);border-radius:7px;background:var(--paper-2);color:var(--ink-2);font:11px var(--mono)}.graph-metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;overflow:hidden;border:1px solid var(--rule);border-radius:var(--radius-sm);background:var(--rule)}.graph-metrics span{padding:10px 14px;background:var(--paper-2);color:var(--ink-3);font-size:10px}.graph-metrics strong{margin-right:5px;color:var(--ink);font:600 14px var(--mono)}.flow-layout{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px;align-items:start}.flow-graph-panel{min-width:0;padding:0;overflow:hidden}.flow-legend{min-height:42px;padding:10px 14px;display:flex;gap:14px;align-items:center;border-bottom:1px solid var(--rule-soft);color:var(--ink-3);font-size:9px}.flow-legend>span{display:inline-flex;gap:5px;align-items:center}.flow-legend__hint{margin-left:auto;color:var(--ink-4)}.legend-dot{width:7px;height:7px;display:inline-block;border-radius:50%;background:var(--accent-deep)}.legend-dot--network{background:var(--network)}.legend-dot--evm{background:var(--evm)}.flow-canvas{max-height:720px;overflow:auto;background-color:#fbfcf9;background-image:radial-gradient(var(--rule) .7px,transparent .7px);background-size:14px 14px}.flow-canvas svg{display:block}.flow-phase-title{fill:var(--ink-3);font:700 9px var(--sans);letter-spacing:.08em;text-transform:uppercase}.flow-phase-rule{stroke:var(--rule);stroke-width:1}.flow-edge{fill:none;stroke:#cbd2cd;stroke-width:1.2;opacity:.72;transition:.12s ease}.flow-edge--selected{stroke:var(--accent-deep);stroke-width:2;opacity:1}.flow-arrow{fill:#aeb8b1}.flow-event{cursor:pointer;outline:none}.flow-event rect{fill:var(--paper-2);stroke:var(--rule);stroke-width:1;filter:drop-shadow(0 2px 3px rgb(20 32 27 / .04));transition:.12s ease}.flow-event circle{fill:var(--accent-deep)}.flow-event--network circle{fill:var(--network)}.flow-event--evm circle{fill:var(--evm)}.flow-event--error rect{fill:var(--danger-bg);stroke:#dfaaa7}.flow-event--error circle{fill:var(--danger)}.flow-event:hover rect,.flow-event:focus-visible rect,.flow-event--selected rect{stroke:var(--accent-deep);stroke-width:2;filter:drop-shadow(0 4px 8px rgb(31 107 74 / .14))}.flow-event__title{fill:var(--ink);font:600 9px var(--sans)}.flow-event__meta{fill:var(--ink-4);font:8px var(--mono)}.flow-detail{position:sticky;top:82px;max-height:calc(100vh - 110px);overflow:auto}.flow-detail h2{margin:4px 0;overflow-wrap:anywhere;font:22px/1.15 var(--serif)}.flow-detail__time{margin:0 0 14px;color:var(--ink-4);font-size:9px}.flow-detail-grid{margin:0 0 14px;display:flex;flex-direction:column}.flow-detail-grid div{padding:8px 0;border-bottom:1px solid var(--rule-soft)}.flow-detail-grid dt{color:var(--ink-4);font-size:8px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}.flow-detail-grid dd{margin:3px 0 0;overflow-wrap:anywhere;color:var(--ink-2);font-size:9px}.flow-successor-list{margin:0 0 14px;display:flex;flex-direction:column;gap:4px}.flow-successor-list button{padding:7px 8px;display:flex;justify-content:space-between;border:1px solid var(--rule-soft);border-radius:6px;background:var(--paper-2);color:var(--accent-deep);font-size:9px;text-align:left;cursor:pointer}.update-grid{display:grid;grid-template-columns:minmax(300px,.8fr) minmax(480px,1.2fr);gap:14px}.update-status{display:flex;gap:18px;align-items:center}.update-status h2{margin:3px 0;font:28px var(--serif)}.update-status p{margin:0;color:var(--ink-3);font-size:11px}.update-orbit{width:76px;height:76px;flex:none;display:grid;place-items:center;border:1px solid var(--accent-soft);border-radius:50%;background:var(--accent-bg);box-shadow:inset 0 0 0 9px var(--paper-2)}.update-orbit i{width:16px;height:16px;display:block;border-radius:50%;background:var(--accent-deep)}.update-orbit--available{border-color:#efd89d;background:var(--warning-bg);box-shadow:inset 0 0 0 9px var(--paper-2),0 0 0 4px #fff6dcb3}.update-orbit--available i{background:var(--warning)}.update-steps{margin:0;padding:0;display:grid;grid-template-columns:repeat(5,1fr);list-style:none;counter-reset:updates}.update-steps li{min-width:0;padding:10px;display:flex;flex-direction:column;gap:5px;border-left:1px solid var(--rule-soft);counter-increment:updates}.update-steps li:first-child{border-left:0}.update-steps li:before{content:"0" counter(updates);color:var(--accent-deep);font:9px var(--mono)}.update-steps strong{font-size:10px}.update-steps span{color:var(--ink-3);font-size:9px}.copy-command{padding:5px 6px;border:1px solid var(--accent-soft);border-radius:5px;background:var(--accent-bg);color:var(--accent-deep);font-size:8px;cursor:pointer}.release-notes a{color:var(--accent-deep);font-size:10px;font-weight:600;text-decoration:none}.release-date{color:var(--ink-4);font:9px var(--mono)}.release-notes>pre{max-height:420px;margin:0;padding:15px;overflow:auto;border-radius:8px;background:var(--paper-3);color:var(--ink-2);font:10px/1.6 var(--mono);white-space:pre-wrap}.log-viz{display:grid;grid-template-columns:210px 1fr;gap:24px;align-items:center}.log-viz h2{margin:3px 0 0;font:22px var(--serif)}.log-viz__bars{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}.log-viz__bars>div{display:grid;grid-template-columns:1fr auto;gap:5px;color:var(--ink-4);font-size:8px;text-transform:uppercase}.log-viz__bars i{height:4px;grid-column:1 / -1;overflow:hidden;border-radius:4px;background:var(--rule-soft)}.log-viz__bar{height:100%;display:block;border-radius:inherit;background:var(--ink-4)}.log-viz__bar--error{background:var(--danger)}.log-viz__bar--warn{background:var(--warning)}.log-viz__bar--info{background:var(--accent-deep)}.log-trace-context{padding:8px 13px;display:flex;gap:14px;border-top:1px solid var(--rule-soft);background:var(--accent-bg);color:var(--ink-3);font-size:8px}.source-viz{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.source-viz__item{padding:11px 13px;display:grid;grid-template-columns:1fr auto;gap:8px;border:1px solid var(--rule);border-radius:8px;background:var(--paper-2);color:var(--ink-3);font-size:9px;text-align:left;text-transform:capitalize;cursor:pointer}.source-viz__item--selected{border-color:var(--accent-deep);box-shadow:0 0 0 2px var(--accent-bg)}.source-viz__item i{height:4px;grid-column:1 / -1;overflow:hidden;border-radius:4px;background:var(--rule-soft)}.source-viz__fill{height:100%;display:block;background:var(--accent-deep)}.source-viz__fill--network{background:var(--network)}.source-viz__fill--evm{background:var(--evm)}.payload-head{margin-bottom:6px}.payload{max-height:360px;margin:0;padding:13px;overflow:auto;border-radius:7px;background:var(--ink);color:#d9fce8;font-size:10px;line-height:1.55}.empty-inline{padding:26px 12px;color:var(--ink-4);font-size:12px;text-align:center}.alert,.stale-banner,.failure-banner{padding:11px 14px;border-radius:var(--radius-sm);font-size:12px}.alert--warning,.stale-banner{background:var(--warning-bg);color:var(--warning)}.stale-banner{margin-bottom:16px}.inspector-layout{min-height:calc(100vh - 64px);display:grid;grid-template-columns:284px minmax(0,1fr)}.e3-list{position:sticky;top:64px;height:calc(100vh - 64px);margin-left:-28px;display:flex;flex-direction:column;border-right:1px solid var(--rule);background:var(--paper-2)}.e3-list__head{height:54px;padding:0 16px 0 28px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--rule);color:var(--ink-3);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.e3-list__head strong{min-width:24px;padding:2px 6px;border-radius:999px;background:var(--paper-3);text-align:center}.e3-list__items{flex:1;overflow:auto;padding:8px}.e3-list__item{width:100%;padding:12px 10px;display:grid;grid-template-columns:8px minmax(0,1fr) auto;gap:9px;align-items:start;border:0;border-radius:9px;background:transparent;text-align:left;cursor:pointer}.e3-list__item:hover{background:var(--rule-soft)}.e3-list__item--selected{background:var(--accent-bg)}.status-dot{width:7px;height:7px;margin-top:5px;border-radius:50%;background:var(--warning)}.status-dot--complete{background:var(--accent-deep)}.status-dot--failed{background:var(--danger)}.e3-list__item>span:nth-child(2){min-width:0;display:flex;flex-direction:column}.e3-list__item strong{overflow:hidden;font-size:11px;text-overflow:ellipsis}.e3-list__item small{color:var(--ink-4);font-size:9px;text-transform:capitalize}.e3-list__time{color:var(--ink-4);font-size:8px}.inspector-main{min-width:0;padding:34px 0 70px 30px}.trace-head{display:flex;justify-content:space-between;align-items:flex-start;gap:30px;margin-bottom:24px}.trace-head h1{font-family:var(--mono);font-size:clamp(25px,3vw,39px)}.trace-status{gap:7px;padding:7px 11px;background:var(--warning-bg);color:var(--warning)}.trace-status>span{width:6px;height:6px;border-radius:50%;background:currentColor}.trace-status--complete{color:var(--accent-deep);background:var(--accent-bg)}.trace-status--failed{color:var(--danger);background:var(--danger-bg)}.failure-banner{margin-bottom:18px;display:flex;align-items:center;gap:12px;background:var(--danger-bg);color:var(--danger)}.failure-banner code{overflow:hidden;font-size:10px;text-overflow:ellipsis;white-space:nowrap}.stage-flow{display:flex;width:100%;margin-bottom:22px;overflow-x:auto;padding:3px}.stage-flow__unit{min-width:135px;flex:1;display:flex;align-items:center}.stage-node{min-width:120px;flex:1;min-height:84px;padding:11px;display:grid;grid-template-columns:23px minmax(0,1fr) auto;gap:7px;align-items:start;border:1px solid var(--rule);border-radius:10px;background:var(--paper-2);text-align:left;cursor:pointer;transition:.12s ease}.stage-node:hover{border-color:var(--accent-soft);transform:translateY(-1px)}.stage-node--selected{border-color:var(--accent-deep);box-shadow:0 0 0 2px var(--accent-bg)}.stage-node--complete{background:#fbfefc}.stage-node--active{background:var(--accent-bg);border-color:var(--accent-soft)}.stage-node--failed{background:var(--danger-bg);border-color:#efcbc8}.stage-node__index{color:var(--ink-4);font-family:var(--mono);font-size:9px}.stage-node__copy{min-width:0;display:flex;flex-direction:column;gap:5px}.stage-node__copy strong{font-size:10px;line-height:1.25}.stage-node__copy span{color:var(--ink-4);font-family:var(--mono);font-size:7px;line-height:1.35}.stage-node__state{color:var(--ink-4);font-size:12px}.stage-node--complete .stage-node__state,.stage-node--active .stage-node__state{color:var(--accent-deep)}.stage-node--failed .stage-node__state{color:var(--danger)}.stage-flow__connector{width:10px;height:1px;flex:none;background:var(--rule)}.stage-flow__connector--done{background:var(--accent-deep)}.trace-columns{display:grid;grid-template-columns:minmax(520px,1fr) 300px;gap:18px;align-items:start}.trace-events{min-height:500px}.flow-note{margin:-4px 0 12px;padding:9px 11px;border-radius:7px;background:var(--paper-3);color:var(--ink-3);font-size:10px}.trace-sidebar{display:flex;flex-direction:column;gap:12px}.committee-list,.ticket-list,.reward-list{display:flex;flex-direction:column}.committee-member{padding:9px 0;display:grid;grid-template-columns:30px minmax(0,1fr) auto;gap:9px;align-items:center;border-bottom:1px solid var(--rule-soft)}.committee-member:last-child{border-bottom:0}.committee-member--expelled{opacity:.55}.committee-member__party{width:30px;height:30px;display:grid;place-items:center;border-radius:50%;background:var(--accent-bg);color:var(--accent-deep);font-size:9px}.committee-member>div{min-width:0;display:flex;flex-direction:column}.committee-member strong{overflow:hidden;font-size:10px;text-overflow:ellipsis}.committee-member small{color:var(--ink-4);font-size:8px}.ticket-list>div,.reward-list>div{padding:8px 0;display:grid;grid-template-columns:50px minmax(0,1fr) auto;gap:8px;align-items:center;border-bottom:1px solid var(--rule-soft)}.ticket-list span,.ticket-list strong,.ticket-list small,.reward-list strong,.reward-list small{overflow:hidden;font-size:9px;text-overflow:ellipsis}.ticket-list small,.reward-list small{color:var(--ink-4)}.reward-state{padding:2px 4px;border-radius:4px;background:var(--accent-bg);color:var(--accent-deep);font-size:7px;text-align:center;text-transform:uppercase}.reward-state--claimed{background:var(--paper-3);color:var(--ink-3)}.blank-state,.loading-page{min-height:60vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.blank-state h1,.loading-page h1{margin-top:18px;font-size:34px}.blank-state p,.loading-page p{max-width:500px;color:var(--ink-3)}.blank-state__glyph{width:64px;height:64px;display:grid;place-items:center;border:1px solid var(--accent-soft);border-radius:50%;background:var(--accent-bg);color:var(--accent-deep);font-family:var(--serif);font-size:18px}.loader-ring{width:30px;height:30px;display:block;border:2px solid var(--rule);border-top-color:var(--accent-deep);border-radius:50%;animation:spin .75s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.view-title{align-items:flex-start}.view-title h1{font-size:42px}.filter-bar{margin-bottom:18px;display:flex;align-items:center;gap:9px}.search-field{min-width:260px;flex:1;height:38px;padding:0 11px;display:flex;align-items:center;gap:8px;border:1px solid var(--rule);border-radius:8px;background:#fdfdfb}.search-field span{color:var(--ink-4)}.search-field input{width:100%;border:0;outline:0;background:transparent;color:var(--ink);font-size:12px}.filter-bar select{height:38px;padding:0 28px 0 10px;border:1px solid var(--rule);border-radius:8px;background:var(--paper-2);color:var(--ink-3);font-size:11px}.filter-count{margin-left:5px;color:var(--ink-4);font-size:9px}.log-table{overflow:hidden;border:1px solid var(--rule-soft);border-radius:9px}.log-table__head,.log-row summary{display:grid;grid-template-columns:90px 58px 190px minmax(0,1fr);gap:10px;align-items:center}.log-table__head{padding:8px 12px;background:var(--paper-3);color:var(--ink-4);font-size:8px;font-weight:600;letter-spacing:.07em;text-transform:uppercase}.log-row{border-top:1px solid var(--rule-soft)}.log-row summary{min-height:42px;padding:7px 12px;cursor:pointer;list-style:none}.log-row summary::-webkit-details-marker{display:none}.log-row summary:hover{background:#fafbf8}.log-row time,.log-row summary>span:nth-child(3){color:var(--ink-4);font-size:9px;overflow:hidden;text-overflow:ellipsis}.log-row strong{overflow:hidden;font-size:11px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.log-level{width:fit-content;padding:2px 5px;border-radius:4px;background:var(--paper-3);color:var(--ink-3);font-size:7px;font-weight:700}.log-row--error .log-level{background:var(--danger-bg);color:var(--danger)}.log-row--warn .log-level{background:var(--warning-bg);color:var(--warning)}.log-row--info .log-level{background:var(--accent-bg);color:var(--accent-deep)}.log-row pre{max-height:320px;margin:0;padding:13px;overflow:auto;background:var(--ink);color:#d9fce8;font-size:9px}.site-foot{min-height:54px;margin-top:auto;padding:0 28px;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--rule);color:var(--ink-4);font-size:10px}@media (max-width: 1100px){.metrics-grid{grid-template-columns:1fr 1fr}.metric:nth-child(2){border-right:0}.metric:nth-child(-n+2){border-bottom:1px solid var(--rule)}.overview-grid,.trace-columns{grid-template-columns:1fr}.trace-sidebar{display:grid;grid-template-columns:repeat(3,1fr)}.site-nav{margin-left:4px}.flow-layout,.update-grid{grid-template-columns:1fr}.flow-detail{position:static;max-height:none}.update-steps{grid-template-columns:repeat(3,1fr)}}@media (max-width: 820px){.site-head__inner{padding:0 14px}.wordmark{width:108px}.product-name,.node-chip span,.site-nav__link{display:none}.site-nav{margin-left:auto}.site-nav__link--on{display:block}.node-chip{margin-left:0;padding:7px}.app-main{padding:28px 16px 60px}.app-main--inspector{padding-top:0}.view-intro{align-items:flex-start;flex-direction:column}.identity-card{width:100%}.inspector-layout{display:block}.e3-list{position:static;width:calc(100% + 32px);height:auto;max-height:230px;margin:0 -16px;border-right:0;border-bottom:1px solid var(--rule)}.e3-list__head{padding-left:16px}.inspector-main{padding:24px 0 60px}.trace-sidebar{grid-template-columns:1fr}.trace-head{flex-direction:column}.event-card__summary{grid-template-columns:18px 70px minmax(0,1fr)}.event-card__cause{display:none}.trace-grid{grid-template-columns:1fr 1fr}.filter-bar{align-items:stretch;flex-direction:column}.search-field{min-width:0}.filter-count{margin:4px 0}.log-table__head{display:none}.log-row summary{grid-template-columns:70px 50px minmax(0,1fr)}.log-row summary strong{grid-column:1 / -1}.flow-view-title{align-items:stretch;flex-direction:column}.flow-selector{width:100%}.graph-metrics{grid-template-columns:1fr 1fr}.flow-legend__hint{display:none!important}.update-steps{grid-template-columns:1fr}.update-steps li,.update-steps li:first-child{border-left:0;border-top:1px solid var(--rule-soft)}.update-steps li:first-child{border-top:0}.log-viz,.log-viz__bars{grid-template-columns:1fr}}@media (max-width: 520px){.metrics-grid{grid-template-columns:1fr}.metric{border-right:0;border-bottom:1px solid var(--rule)}.metric:last-child{border-bottom:0}.mini-dl,.trace-grid{grid-template-columns:1fr}.event-card__summary{grid-template-columns:18px minmax(0,1fr)}.event-card__time{display:none}.event-card__detail{margin-left:18px}.site-foot{align-items:flex-start;flex-direction:column;justify-content:center;gap:3px}.source-viz{grid-template-columns:1fr}}:root{--ink: #14201b;--ink-2: #2c3833;--ink-3: #5b6864;--ink-4: #8b9893;--rule: #e3e7e2;--rule-soft: #eef1ec;--paper: #f7f5ee;--paper-2: #ffffff;--paper-3: #f0ede4;--accent-bg: #e8faf0;--accent-soft: #cdeede;--accent-deep: #1f6b4a;--accent-ink: #163d2c;--danger: #a93632;--danger-bg: #fcebea;--warning: #906319;--warning-bg: #fff6dc;--network: #5d4f99;--evm: #8d5c16;--serif: Georgia, "Times New Roman", serif;--sans: "Geist", "Söhne", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;--mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;--radius: 14px;--radius-sm: 9px;color:var(--ink);background:var(--paper);font:16px/1.5 var(--sans);font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}*{box-sizing:border-box}html{min-width:320px;background:var(--paper)}body{margin:0;min-width:320px;min-height:100vh;background:var(--paper)}button,input,select{font:inherit}button{color:inherit}code,pre,.mono{font-family:var(--mono);font-feature-settings:"tnum";letter-spacing:-.015em}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--accent-deep);outline-offset:2px}::selection{background:var(--accent-soft);color:var(--accent-ink)} diff --git a/crates/dashboard/assets/app.js b/crates/dashboard/assets/app.js new file mode 100644 index 0000000000..8ce16fb644 --- /dev/null +++ b/crates/dashboard/assets/app.js @@ -0,0 +1,40 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const i of l)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(l){const i={};return l.integrity&&(i.integrity=l.integrity),l.referrerPolicy&&(i.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?i.credentials="include":l.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(l){if(l.ep)return;l.ep=!0;const i=n(l);fetch(l.href,i)}})();function xc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var nu={exports:{}},sl={},ru={exports:{}},R={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Zn=Symbol.for("react.element"),wc=Symbol.for("react.portal"),kc=Symbol.for("react.fragment"),_c=Symbol.for("react.strict_mode"),jc=Symbol.for("react.profiler"),Sc=Symbol.for("react.provider"),Nc=Symbol.for("react.context"),Ec=Symbol.for("react.forward_ref"),Cc=Symbol.for("react.suspense"),Pc=Symbol.for("react.memo"),Lc=Symbol.for("react.lazy"),Ws=Symbol.iterator;function zc(e){return e===null||typeof e!="object"?null:(e=Ws&&e[Ws]||e["@@iterator"],typeof e=="function"?e:null)}var lu={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},iu=Object.assign,su={};function on(e,t,n){this.props=e,this.context=t,this.refs=su,this.updater=n||lu}on.prototype.isReactComponent={};on.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};on.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function ou(){}ou.prototype=on.prototype;function Yi(e,t,n){this.props=e,this.context=t,this.refs=su,this.updater=n||lu}var Xi=Yi.prototype=new ou;Xi.constructor=Yi;iu(Xi,on.prototype);Xi.isPureReactComponent=!0;var Qs=Array.isArray,uu=Object.prototype.hasOwnProperty,Gi={current:null},au={key:!0,ref:!0,__self:!0,__source:!0};function cu(e,t,n){var r,l={},i=null,s=null;if(t!=null)for(r in t.ref!==void 0&&(s=t.ref),t.key!==void 0&&(i=""+t.key),t)uu.call(t,r)&&!au.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,Z=S[Q];if(0>>1;Ql(Sl,z))ytl(rr,Sl)?(S[Q]=rr,S[yt]=z,Q=yt):(S[Q]=Sl,S[gt]=z,Q=gt);else if(ytl(rr,z))S[Q]=rr,S[yt]=z,Q=yt;else break e}}return L}function l(S,L){var z=S.sortIndex-L.sortIndex;return z!==0?z:S.id-L.id}if(typeof performance=="object"&&typeof performance.now=="function"){var i=performance;e.unstable_now=function(){return i.now()}}else{var s=Date,u=s.now();e.unstable_now=function(){return s.now()-u}}var a=[],c=[],m=1,p=null,f=3,y=!1,k=!1,g=!1,P=typeof setTimeout=="function"?setTimeout:null,h=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function v(S){for(var L=n(c);L!==null;){if(L.callback===null)r(c);else if(L.startTime<=S)r(c),L.sortIndex=L.expirationTime,t(a,L);else break;L=n(c)}}function x(S){if(g=!1,v(S),!k)if(n(a)!==null)k=!0,_l(j);else{var L=n(c);L!==null&&jl(x,L.startTime-S)}}function j(S,L){k=!1,g&&(g=!1,h(C),C=-1),y=!0;var z=f;try{for(v(L),p=n(a);p!==null&&(!(p.expirationTime>L)||S&&!Pe());){var Q=p.callback;if(typeof Q=="function"){p.callback=null,f=p.priorityLevel;var Z=Q(p.expirationTime<=L);L=e.unstable_now(),typeof Z=="function"?p.callback=Z:p===n(a)&&r(a),v(L)}else r(a);p=n(a)}if(p!==null)var nr=!0;else{var gt=n(c);gt!==null&&jl(x,gt.startTime-L),nr=!1}return nr}finally{p=null,f=z,y=!1}}var N=!1,E=null,C=-1,W=5,O=-1;function Pe(){return!(e.unstable_now()-OS||125Q?(S.sortIndex=z,t(c,S),n(a)===null&&S===n(c)&&(g?(h(C),C=-1):g=!0,jl(x,z-Q))):(S.sortIndex=Z,t(a,S),k||y||(k=!0,_l(j))),S},e.unstable_shouldYield=Pe,e.unstable_wrapCallback=function(S){var L=f;return function(){var z=f;f=L;try{return S.apply(this,arguments)}finally{f=z}}}})(mu);hu.exports=mu;var Bc=hu.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Hc=T,xe=Bc;function w(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),ti=Object.prototype.hasOwnProperty,Wc=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Ys={},Xs={};function Qc(e){return ti.call(Xs,e)?!0:ti.call(Ys,e)?!1:Wc.test(e)?Xs[e]=!0:(Ys[e]=!0,!1)}function Kc(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Yc(e,t,n,r){if(t===null||typeof t>"u"||Kc(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ae(e,t,n,r,l,i,s){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=s}var te={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){te[e]=new ae(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];te[t]=new ae(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){te[e]=new ae(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){te[e]=new ae(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){te[e]=new ae(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){te[e]=new ae(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){te[e]=new ae(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){te[e]=new ae(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){te[e]=new ae(e,5,!1,e.toLowerCase(),null,!1,!1)});var Ji=/[\-:]([a-z])/g;function qi(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!1,!1)});te.xlinkHref=new ae("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!0,!0)});function bi(e,t,n,r){var l=te.hasOwnProperty(t)?te[t]:null;(l!==null?l.type!==0:r||!(2u||l[s]!==i[u]){var a=` +`+l[s].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=s&&0<=u);break}}}finally{Cl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?wn(e):""}function Xc(e){switch(e.tag){case 5:return wn(e.type);case 16:return wn("Lazy");case 13:return wn("Suspense");case 19:return wn("SuspenseList");case 0:case 2:case 15:return e=Pl(e.type,!1),e;case 11:return e=Pl(e.type.render,!1),e;case 1:return e=Pl(e.type,!0),e;default:return""}}function ii(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case It:return"Fragment";case Mt:return"Portal";case ni:return"Profiler";case es:return"StrictMode";case ri:return"Suspense";case li:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case yu:return(e.displayName||"Context")+".Consumer";case gu:return(e._context.displayName||"Context")+".Provider";case ts:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ns:return t=e.displayName||null,t!==null?t:ii(e.type)||"Memo";case qe:t=e._payload,e=e._init;try{return ii(e(t))}catch{}}return null}function Gc(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ii(t);case 8:return t===es?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ft(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function wu(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Zc(e){var t=wu(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(s){r=""+s,i.call(this,s)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(s){r=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function sr(e){e._valueTracker||(e._valueTracker=Zc(e))}function ku(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=wu(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $r(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function si(e,t){var n=t.checked;return B({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Zs(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ft(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function _u(e,t){t=t.checked,t!=null&&bi(e,"checked",t,!1)}function oi(e,t){_u(e,t);var n=ft(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?ui(e,t.type,n):t.hasOwnProperty("defaultValue")&&ui(e,t.type,ft(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Js(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function ui(e,t,n){(t!=="number"||$r(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var kn=Array.isArray;function Kt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=or.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Mn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Sn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Jc=["Webkit","ms","Moz","O"];Object.keys(Sn).forEach(function(e){Jc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Sn[t]=Sn[e]})});function Eu(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Sn.hasOwnProperty(e)&&Sn[e]?(""+t).trim():t+"px"}function Cu(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Eu(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var qc=B({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function di(e,t){if(t){if(qc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(w(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(w(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(w(61))}if(t.style!=null&&typeof t.style!="object")throw Error(w(62))}}function fi(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var pi=null;function rs(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var hi=null,Yt=null,Xt=null;function eo(e){if(e=bn(e)){if(typeof hi!="function")throw Error(w(280));var t=e.stateNode;t&&(t=dl(t),hi(e.stateNode,e.type,t))}}function Pu(e){Yt?Xt?Xt.push(e):Xt=[e]:Yt=e}function Lu(){if(Yt){var e=Yt,t=Xt;if(Xt=Yt=null,eo(e),t)for(e=0;e>>=0,e===0?32:31-(ad(e)/cd|0)|0}var ur=64,ar=4194304;function _n(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Ar(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,i=e.pingedLanes,s=n&268435455;if(s!==0){var u=s&~l;u!==0?r=_n(u):(i&=s,i!==0&&(r=_n(i)))}else s=n&~l,s!==0?r=_n(s):i!==0&&(r=_n(i));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,i=t&-t,l>=i||l===16&&(i&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Jn(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Oe(t),e[t]=n}function hd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=En),ao=" ",co=!1;function Gu(e,t){switch(e){case"keyup":return Bd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Zu(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var $t=!1;function Wd(e,t){switch(e){case"compositionend":return Zu(t);case"keypress":return t.which!==32?null:(co=!0,ao);case"textInput":return e=t.data,e===ao&&co?null:e;default:return null}}function Qd(e,t){if($t)return e==="compositionend"||!ds&&Gu(e,t)?(e=Yu(),Cr=us=nt=null,$t=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=mo(n)}}function ea(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?ea(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ta(){for(var e=window,t=$r();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$r(e.document)}return t}function fs(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function ef(e){var t=ta(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&ea(n.ownerDocument.documentElement,n)){if(r!==null&&fs(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,i=Math.min(r.start,l);r=r.end===void 0?i:Math.min(r.end,l),!e.extend&&i>r&&(l=r,r=i,i=l),l=vo(n,i);var s=vo(n,r);l&&s&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==s.node||e.focusOffset!==s.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(s.node,s.offset)):(t.setEnd(s.node,s.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Dt=null,wi=null,Pn=null,ki=!1;function go(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ki||Dt==null||Dt!==$r(r)||(r=Dt,"selectionStart"in r&&fs(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Pn&&An(Pn,r)||(Pn=r,r=Hr(wi,"onSelect"),0At||(e.current=Ci[At],Ci[At]=null,At--)}function $(e,t){At++,Ci[At]=e.current,e.current=t}var pt={},ie=mt(pt),pe=mt(!1),Et=pt;function en(e,t){var n=e.type.contextTypes;if(!n)return pt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},i;for(i in n)l[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function he(e){return e=e.childContextTypes,e!=null}function Qr(){F(pe),F(ie)}function So(e,t,n){if(ie.current!==pt)throw Error(w(168));$(ie,t),$(pe,n)}function ca(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(w(108,Gc(e)||"Unknown",l));return B({},n,r)}function Kr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||pt,Et=ie.current,$(ie,e),$(pe,pe.current),!0}function No(e,t,n){var r=e.stateNode;if(!r)throw Error(w(169));n?(e=ca(e,t,Et),r.__reactInternalMemoizedMergedChildContext=e,F(pe),F(ie),$(ie,e)):F(pe),$(pe,n)}var Be=null,fl=!1,Bl=!1;function da(e){Be===null?Be=[e]:Be.push(e)}function pf(e){fl=!0,da(e)}function vt(){if(!Bl&&Be!==null){Bl=!0;var e=0,t=I;try{var n=Be;for(I=1;e>=s,l-=s,He=1<<32-Oe(t)+l|n<C?(W=E,E=null):W=E.sibling;var O=f(h,E,v[C],x);if(O===null){E===null&&(E=W);break}e&&E&&O.alternate===null&&t(h,E),d=i(O,d,C),N===null?j=O:N.sibling=O,N=O,E=W}if(C===v.length)return n(h,E),U&&xt(h,C),j;if(E===null){for(;CC?(W=E,E=null):W=E.sibling;var Pe=f(h,E,O.value,x);if(Pe===null){E===null&&(E=W);break}e&&E&&Pe.alternate===null&&t(h,E),d=i(Pe,d,C),N===null?j=Pe:N.sibling=Pe,N=Pe,E=W}if(O.done)return n(h,E),U&&xt(h,C),j;if(E===null){for(;!O.done;C++,O=v.next())O=p(h,O.value,x),O!==null&&(d=i(O,d,C),N===null?j=O:N.sibling=O,N=O);return U&&xt(h,C),j}for(E=r(h,E);!O.done;C++,O=v.next())O=y(E,h,C,O.value,x),O!==null&&(e&&O.alternate!==null&&E.delete(O.key===null?C:O.key),d=i(O,d,C),N===null?j=O:N.sibling=O,N=O);return e&&E.forEach(function(cn){return t(h,cn)}),U&&xt(h,C),j}function P(h,d,v,x){if(typeof v=="object"&&v!==null&&v.type===It&&v.key===null&&(v=v.props.children),typeof v=="object"&&v!==null){switch(v.$$typeof){case ir:e:{for(var j=v.key,N=d;N!==null;){if(N.key===j){if(j=v.type,j===It){if(N.tag===7){n(h,N.sibling),d=l(N,v.props.children),d.return=h,h=d;break e}}else if(N.elementType===j||typeof j=="object"&&j!==null&&j.$$typeof===qe&&Po(j)===N.type){n(h,N.sibling),d=l(N,v.props),d.ref=gn(h,N,v),d.return=h,h=d;break e}n(h,N);break}else t(h,N);N=N.sibling}v.type===It?(d=Nt(v.props.children,h.mode,x,v.key),d.return=h,h=d):(x=Ir(v.type,v.key,v.props,null,h.mode,x),x.ref=gn(h,d,v),x.return=h,h=x)}return s(h);case Mt:e:{for(N=v.key;d!==null;){if(d.key===N)if(d.tag===4&&d.stateNode.containerInfo===v.containerInfo&&d.stateNode.implementation===v.implementation){n(h,d.sibling),d=l(d,v.children||[]),d.return=h,h=d;break e}else{n(h,d);break}else t(h,d);d=d.sibling}d=Zl(v,h.mode,x),d.return=h,h=d}return s(h);case qe:return N=v._init,P(h,d,N(v._payload),x)}if(kn(v))return k(h,d,v,x);if(fn(v))return g(h,d,v,x);vr(h,v)}return typeof v=="string"&&v!==""||typeof v=="number"?(v=""+v,d!==null&&d.tag===6?(n(h,d.sibling),d=l(d,v),d.return=h,h=d):(n(h,d),d=Gl(v,h.mode,x),d.return=h,h=d),s(h)):n(h,d)}return P}var nn=ma(!0),va=ma(!1),Gr=mt(null),Zr=null,Ht=null,vs=null;function gs(){vs=Ht=Zr=null}function ys(e){var t=Gr.current;F(Gr),e._currentValue=t}function zi(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Zt(e,t){Zr=e,vs=Ht=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(de=!0),e.firstContext=null)}function Ee(e){var t=e._currentValue;if(vs!==e)if(e={context:e,memoizedValue:t,next:null},Ht===null){if(Zr===null)throw Error(w(308));Ht=e,Zr.dependencies={lanes:0,firstContext:e}}else Ht=Ht.next=e;return t}var _t=null;function xs(e){_t===null?_t=[e]:_t.push(e)}function ga(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,xs(t)):(n.next=l.next,l.next=n),t.interleaved=n,Xe(e,r)}function Xe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var be=!1;function ws(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function ya(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Qe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function ut(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,M&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Xe(e,n)}return l=r.interleaved,l===null?(t.next=t,xs(r)):(t.next=l.next,l.next=t),r.interleaved=t,Xe(e,n)}function Lr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,is(e,n)}}function Lo(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var s={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?l=i=s:i=i.next=s,n=n.next}while(n!==null);i===null?l=i=t:i=i.next=t}else l=i=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Jr(e,t,n,r){var l=e.updateQueue;be=!1;var i=l.firstBaseUpdate,s=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var a=u,c=a.next;a.next=null,s===null?i=c:s.next=c,s=a;var m=e.alternate;m!==null&&(m=m.updateQueue,u=m.lastBaseUpdate,u!==s&&(u===null?m.firstBaseUpdate=c:u.next=c,m.lastBaseUpdate=a))}if(i!==null){var p=l.baseState;s=0,m=c=a=null,u=i;do{var f=u.lane,y=u.eventTime;if((r&f)===f){m!==null&&(m=m.next={eventTime:y,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var k=e,g=u;switch(f=t,y=n,g.tag){case 1:if(k=g.payload,typeof k=="function"){p=k.call(y,p,f);break e}p=k;break e;case 3:k.flags=k.flags&-65537|128;case 0:if(k=g.payload,f=typeof k=="function"?k.call(y,p,f):k,f==null)break e;p=B({},p,f);break e;case 2:be=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,f=l.effects,f===null?l.effects=[u]:f.push(u))}else y={eventTime:y,lane:f,tag:u.tag,payload:u.payload,callback:u.callback,next:null},m===null?(c=m=y,a=p):m=m.next=y,s|=f;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;f=u,u=f.next,f.next=null,l.lastBaseUpdate=f,l.shared.pending=null}}while(!0);if(m===null&&(a=p),l.baseState=a,l.firstBaseUpdate=c,l.lastBaseUpdate=m,t=l.shared.interleaved,t!==null){l=t;do s|=l.lane,l=l.next;while(l!==t)}else i===null&&(l.shared.lanes=0);Lt|=s,e.lanes=s,e.memoizedState=p}}function zo(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Wl.transition;Wl.transition={};try{e(!1),t()}finally{I=n,Wl.transition=r}}function Ia(){return Ce().memoizedState}function gf(e,t,n){var r=ct(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},$a(e))Da(t,n);else if(n=ga(e,t,n,r),n!==null){var l=oe();Me(n,e,r,l),Fa(n,t,r)}}function yf(e,t,n){var r=ct(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if($a(e))Da(t,l);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,u=i(s,n);if(l.hasEagerState=!0,l.eagerState=u,Ie(u,s)){var a=t.interleaved;a===null?(l.next=l,xs(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}finally{}n=ga(e,t,l,r),n!==null&&(l=oe(),Me(n,e,r,l),Fa(n,t,r))}}function $a(e){var t=e.alternate;return e===V||t!==null&&t===V}function Da(e,t){Ln=br=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Fa(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,is(e,n)}}var el={readContext:Ee,useCallback:ne,useContext:ne,useEffect:ne,useImperativeHandle:ne,useInsertionEffect:ne,useLayoutEffect:ne,useMemo:ne,useReducer:ne,useRef:ne,useState:ne,useDebugValue:ne,useDeferredValue:ne,useTransition:ne,useMutableSource:ne,useSyncExternalStore:ne,useId:ne,unstable_isNewReconciler:!1},xf={readContext:Ee,useCallback:function(e,t){return De().memoizedState=[e,t===void 0?null:t],e},useContext:Ee,useEffect:Ro,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Tr(4194308,4,za.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Tr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Tr(4,2,e,t)},useMemo:function(e,t){var n=De();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=De();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=gf.bind(null,V,e),[r.memoizedState,e]},useRef:function(e){var t=De();return e={current:e},t.memoizedState=e},useState:To,useDebugValue:Ps,useDeferredValue:function(e){return De().memoizedState=e},useTransition:function(){var e=To(!1),t=e[0];return e=vf.bind(null,e[1]),De().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=V,l=De();if(U){if(n===void 0)throw Error(w(407));n=n()}else{if(n=t(),q===null)throw Error(w(349));Pt&30||_a(r,t,n)}l.memoizedState=n;var i={value:n,getSnapshot:t};return l.queue=i,Ro(Sa.bind(null,r,i,e),[e]),r.flags|=2048,Xn(9,ja.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=De(),t=q.identifierPrefix;if(U){var n=We,r=He;n=(r&~(1<<32-Oe(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Kn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=s.createElement(n,{is:r.is}):(e=s.createElement(n),n==="select"&&(s=e,r.multiple?s.multiple=!0:r.size&&(s.size=r.size))):e=s.createElementNS(e,n),e[Fe]=t,e[Hn]=r,Xa(e,t,!1,!1),t.stateNode=e;e:{switch(s=fi(n,r),n){case"dialog":D("cancel",e),D("close",e),l=r;break;case"iframe":case"object":case"embed":D("load",e),l=r;break;case"video":case"audio":for(l=0;lsn&&(t.flags|=128,r=!0,yn(i,!1),t.lanes=4194304)}else{if(!r)if(e=qr(s),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),yn(i,!0),i.tail===null&&i.tailMode==="hidden"&&!s.alternate&&!U)return re(t),null}else 2*K()-i.renderingStartTime>sn&&n!==1073741824&&(t.flags|=128,r=!0,yn(i,!1),t.lanes=4194304);i.isBackwards?(s.sibling=t.child,t.child=s):(n=i.last,n!==null?n.sibling=s:t.child=s,i.last=s)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=K(),t.sibling=null,n=A.current,$(A,r?n&1|2:n&1),t):(re(t),null);case 22:case 23:return Ms(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ve&1073741824&&(re(t),t.subtreeFlags&6&&(t.flags|=8192)):re(t),null;case 24:return null;case 25:return null}throw Error(w(156,t.tag))}function Cf(e,t){switch(hs(t),t.tag){case 1:return he(t.type)&&Qr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return rn(),F(pe),F(ie),js(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return _s(t),null;case 13:if(F(A),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(w(340));tn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return F(A),null;case 4:return rn(),null;case 10:return ys(t.type._context),null;case 22:case 23:return Ms(),null;case 24:return null;default:return null}}var yr=!1,le=!1,Pf=typeof WeakSet=="function"?WeakSet:Set,_=null;function Wt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){H(e,t,r)}else n.current=null}function Ui(e,t,n){try{n()}catch(r){H(e,t,r)}}var Ho=!1;function Lf(e,t){if(_i=Vr,e=ta(),fs(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var s=0,u=-1,a=-1,c=0,m=0,p=e,f=null;t:for(;;){for(var y;p!==n||l!==0&&p.nodeType!==3||(u=s+l),p!==i||r!==0&&p.nodeType!==3||(a=s+r),p.nodeType===3&&(s+=p.nodeValue.length),(y=p.firstChild)!==null;)f=p,p=y;for(;;){if(p===e)break t;if(f===n&&++c===l&&(u=s),f===i&&++m===r&&(a=s),(y=p.nextSibling)!==null)break;p=f,f=p.parentNode}p=y}n=u===-1||a===-1?null:{start:u,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(ji={focusedElem:e,selectionRange:n},Vr=!1,_=t;_!==null;)if(t=_,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,_=e;else for(;_!==null;){t=_;try{var k=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(k!==null){var g=k.memoizedProps,P=k.memoizedState,h=t.stateNode,d=h.getSnapshotBeforeUpdate(t.elementType===t.type?g:ze(t.type,g),P);h.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var v=t.stateNode.containerInfo;v.nodeType===1?v.textContent="":v.nodeType===9&&v.documentElement&&v.removeChild(v.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(w(163))}}catch(x){H(t,t.return,x)}if(e=t.sibling,e!==null){e.return=t.return,_=e;break}_=t.return}return k=Ho,Ho=!1,k}function zn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var i=l.destroy;l.destroy=void 0,i!==void 0&&Ui(t,n,i)}l=l.next}while(l!==r)}}function ml(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ai(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ja(e){var t=e.alternate;t!==null&&(e.alternate=null,Ja(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Fe],delete t[Hn],delete t[Ei],delete t[df],delete t[ff])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function qa(e){return e.tag===5||e.tag===3||e.tag===4}function Wo(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||qa(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Vi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Wr));else if(r!==4&&(e=e.child,e!==null))for(Vi(e,t,n),e=e.sibling;e!==null;)Vi(e,t,n),e=e.sibling}function Bi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Bi(e,t,n),e=e.sibling;e!==null;)Bi(e,t,n),e=e.sibling}var b=null,Te=!1;function Je(e,t,n){for(n=n.child;n!==null;)ba(e,t,n),n=n.sibling}function ba(e,t,n){if(Ue&&typeof Ue.onCommitFiberUnmount=="function")try{Ue.onCommitFiberUnmount(ol,n)}catch{}switch(n.tag){case 5:le||Wt(n,t);case 6:var r=b,l=Te;b=null,Je(e,t,n),b=r,Te=l,b!==null&&(Te?(e=b,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):b.removeChild(n.stateNode));break;case 18:b!==null&&(Te?(e=b,n=n.stateNode,e.nodeType===8?Vl(e.parentNode,n):e.nodeType===1&&Vl(e,n),Fn(e)):Vl(b,n.stateNode));break;case 4:r=b,l=Te,b=n.stateNode.containerInfo,Te=!0,Je(e,t,n),b=r,Te=l;break;case 0:case 11:case 14:case 15:if(!le&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var i=l,s=i.destroy;i=i.tag,s!==void 0&&(i&2||i&4)&&Ui(n,t,s),l=l.next}while(l!==r)}Je(e,t,n);break;case 1:if(!le&&(Wt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){H(n,t,u)}Je(e,t,n);break;case 21:Je(e,t,n);break;case 22:n.mode&1?(le=(r=le)||n.memoizedState!==null,Je(e,t,n),le=r):Je(e,t,n);break;default:Je(e,t,n)}}function Qo(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Pf),t.forEach(function(r){var l=Ff.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Le(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=s),r&=~i}if(r=l,r=K()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Tf(r/1960))-r,10e?16:e,rt===null)var r=!1;else{if(e=rt,rt=null,rl=0,M&6)throw Error(w(331));var l=M;for(M|=4,_=e.current;_!==null;){var i=_,s=i.child;if(_.flags&16){var u=i.deletions;if(u!==null){for(var a=0;aK()-Rs?St(e,0):Ts|=n),me(e,t)}function oc(e,t){t===0&&(e.mode&1?(t=ar,ar<<=1,!(ar&130023424)&&(ar=4194304)):t=1);var n=oe();e=Xe(e,t),e!==null&&(Jn(e,t,n),me(e,n))}function Df(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),oc(e,n)}function Ff(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(w(314))}r!==null&&r.delete(t),oc(e,n)}var uc;uc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||pe.current)de=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return de=!1,Nf(e,t,n);de=!!(e.flags&131072)}else de=!1,U&&t.flags&1048576&&fa(t,Xr,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Rr(e,t),e=t.pendingProps;var l=en(t,ie.current);Zt(t,n),l=Ns(null,t,r,e,l,n);var i=Es();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,he(r)?(i=!0,Kr(t)):i=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,ws(t),l.updater=hl,t.stateNode=l,l._reactInternals=t,Ri(t,r,e,n),t=Ii(null,t,r,!0,i,n)):(t.tag=0,U&&i&&ps(t),se(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Rr(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Af(r),e=ze(r,e),l){case 0:t=Mi(null,t,r,e,n);break e;case 1:t=Ao(null,t,r,e,n);break e;case 11:t=Fo(null,t,r,e,n);break e;case 14:t=Uo(null,t,r,ze(r.type,e),n);break e}throw Error(w(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Mi(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Ao(e,t,r,l,n);case 3:e:{if(Qa(t),e===null)throw Error(w(387));r=t.pendingProps,i=t.memoizedState,l=i.element,ya(e,t),Jr(t,r,null,n);var s=t.memoizedState;if(r=s.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:s.cache,pendingSuspenseBoundaries:s.pendingSuspenseBoundaries,transitions:s.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){l=ln(Error(w(423)),t),t=Vo(e,t,r,n,l);break e}else if(r!==l){l=ln(Error(w(424)),t),t=Vo(e,t,r,n,l);break e}else for(ge=ot(t.stateNode.containerInfo.firstChild),ye=t,U=!0,Re=null,n=va(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(tn(),r===l){t=Ge(e,t,n);break e}se(e,t,r,n)}t=t.child}return t;case 5:return xa(t),e===null&&Li(t),r=t.type,l=t.pendingProps,i=e!==null?e.memoizedProps:null,s=l.children,Si(r,l)?s=null:i!==null&&Si(r,i)&&(t.flags|=32),Wa(e,t),se(e,t,s,n),t.child;case 6:return e===null&&Li(t),null;case 13:return Ka(e,t,n);case 4:return ks(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=nn(t,null,r,n):se(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Fo(e,t,r,l,n);case 7:return se(e,t,t.pendingProps,n),t.child;case 8:return se(e,t,t.pendingProps.children,n),t.child;case 12:return se(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,i=t.memoizedProps,s=l.value,$(Gr,r._currentValue),r._currentValue=s,i!==null)if(Ie(i.value,s)){if(i.children===l.children&&!pe.current){t=Ge(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var u=i.dependencies;if(u!==null){s=i.child;for(var a=u.firstContext;a!==null;){if(a.context===r){if(i.tag===1){a=Qe(-1,n&-n),a.tag=2;var c=i.updateQueue;if(c!==null){c=c.shared;var m=c.pending;m===null?a.next=a:(a.next=m.next,m.next=a),c.pending=a}}i.lanes|=n,a=i.alternate,a!==null&&(a.lanes|=n),zi(i.return,n,t),u.lanes|=n;break}a=a.next}}else if(i.tag===10)s=i.type===t.type?null:i.child;else if(i.tag===18){if(s=i.return,s===null)throw Error(w(341));s.lanes|=n,u=s.alternate,u!==null&&(u.lanes|=n),zi(s,n,t),s=i.sibling}else s=i.child;if(s!==null)s.return=i;else for(s=i;s!==null;){if(s===t){s=null;break}if(i=s.sibling,i!==null){i.return=s.return,s=i;break}s=s.return}i=s}se(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Zt(t,n),l=Ee(l),r=r(l),t.flags|=1,se(e,t,r,n),t.child;case 14:return r=t.type,l=ze(r,t.pendingProps),l=ze(r.type,l),Uo(e,t,r,l,n);case 15:return Ba(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Rr(e,t),t.tag=1,he(r)?(e=!0,Kr(t)):e=!1,Zt(t,n),Ua(t,r,l),Ri(t,r,l,n),Ii(null,t,r,!0,e,n);case 19:return Ya(e,t,n);case 22:return Ha(e,t,n)}throw Error(w(156,t.tag))};function ac(e,t){return $u(e,t)}function Uf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Se(e,t,n,r){return new Uf(e,t,n,r)}function $s(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Af(e){if(typeof e=="function")return $s(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ts)return 11;if(e===ns)return 14}return 2}function dt(e,t){var n=e.alternate;return n===null?(n=Se(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ir(e,t,n,r,l,i){var s=2;if(r=e,typeof e=="function")$s(e)&&(s=1);else if(typeof e=="string")s=5;else e:switch(e){case It:return Nt(n.children,l,i,t);case es:s=8,l|=8;break;case ni:return e=Se(12,n,t,l|2),e.elementType=ni,e.lanes=i,e;case ri:return e=Se(13,n,t,l),e.elementType=ri,e.lanes=i,e;case li:return e=Se(19,n,t,l),e.elementType=li,e.lanes=i,e;case xu:return gl(n,l,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case gu:s=10;break e;case yu:s=9;break e;case ts:s=11;break e;case ns:s=14;break e;case qe:s=16,r=null;break e}throw Error(w(130,e==null?e:typeof e,""))}return t=Se(s,n,t,l),t.elementType=e,t.type=r,t.lanes=i,t}function Nt(e,t,n,r){return e=Se(7,e,r,t),e.lanes=n,e}function gl(e,t,n,r){return e=Se(22,e,r,t),e.elementType=xu,e.lanes=n,e.stateNode={isHidden:!1},e}function Gl(e,t,n){return e=Se(6,e,null,t),e.lanes=n,e}function Zl(e,t,n){return t=Se(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Vf(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=zl(0),this.expirationTimes=zl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=zl(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Ds(e,t,n,r,l,i,s,u,a){return e=new Vf(e,t,n,u,a),t===1?(t=1,i===!0&&(t|=8)):t=0,i=Se(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},ws(i),e}function Bf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(pc)}catch(e){console.error(e)}}pc(),pu.exports=we;var Yf=pu.exports,bo=Yf;ei.createRoot=bo.createRoot,ei.hydrateRoot=bo.hydrateRoot;async function tr(e,t){const n=await fetch(e,{signal:t,cache:"no-store"});if(!n.ok){const r=await n.json().catch(()=>null);throw new Error((r==null?void 0:r.error)??`${n.status} ${n.statusText}`)}return n.json()}function Xf(e=2e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0,l,i=!1;const s=async()=>{if(!i){i=!0,l=new AbortController;try{const a=await tr("/api/snapshot",l.signal);r&&n({data:a,loading:!1})}catch(a){r&&!(a instanceof DOMException&&a.name==="AbortError")&&n(c=>({...c,error:a instanceof Error?a.message:String(a),loading:!1}))}finally{i=!1}}};s();const u=window.setInterval(()=>{document.visibilityState==="visible"&&s()},e);return()=>{r=!1,l==null||l.abort(),window.clearInterval(u)}},[e]),t}function hc(e,t=0){const[n,r]=T.useState({loading:!!e});return T.useEffect(()=>{if(!e){r({loading:!1});return}const l=new AbortController;return r(i=>({...i,loading:!i.data})),tr(`/api/e3?e3_id=${encodeURIComponent(e)}`,l.signal).then(i=>r({data:i,loading:!1})).catch(i=>{i instanceof DOMException&&i.name==="AbortError"||r(s=>({...s,error:i instanceof Error?i.message:String(i),loading:!1}))}),()=>l.abort()},[e,t]),n}function Gf(e=2e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/logs?limit=2000").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function Zf(e=2500){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/events?limit=2000").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function Jf(e=60*60*1e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/updates").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function fe(e,t=8,n=5){return!e||e.length<=t+n+2?e??"—":`${e.slice(0,t)}…${e.slice(-n)}`}function Vs(e){return e?new Intl.DateTimeFormat(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3}).format(new Date(e/1e3)):"—"}function mc(e){return new Date(e/1e3).toLocaleString()}function qf(e){return new Intl.NumberFormat().format(e)}function qt(e){if(!e)return"—";try{const t=BigInt(e);if(t<1000000n)return t.toLocaleString();const n=t.toString().length,r=n>18?"e18+":n>12?"T+":n>9?"B+":"M+";return`${t.toString().slice(0,4)}… ${r}`}catch{return e}}function vc(e){return JSON.stringify(e,null,2)}function bf({source:e}){const t=e==="network"?"NET":e.toUpperCase();return o.jsx("span",{className:`source source--${e}`,children:t})}function kr({value:e,label:t,onLocate:n}){return o.jsx("button",{className:"trace-link mono",type:"button",title:e,onClick:()=>n(e),children:t??fe(e,8,4)})}function ep({event:e,successors:t,onLocate:n}){const[r,l]=T.useState(!1),i=e.event_id===e.causation_id;return o.jsxs("article",{id:`event-${e.event_id}`,className:`event-card event-card--${e.severity}`,children:[o.jsxs("button",{className:"event-card__summary",type:"button",onClick:()=>l(s=>!s),"aria-expanded":r,children:[o.jsx("span",{className:"event-card__rail","aria-hidden":"true",children:o.jsx("span",{className:"event-card__dot"})}),o.jsx("span",{className:"event-card__time mono",title:mc(e.timestamp_us),children:Vs(e.timestamp_us)}),o.jsxs("span",{className:"event-card__identity",children:[o.jsx("strong",{children:e.event_type}),o.jsxs("span",{className:"event-card__meta",children:[o.jsx(bf,{source:e.source}),o.jsx("span",{children:fe(e.producer,12,6)}),e.block!=null&&o.jsxs("span",{className:"mono",children:["block #",e.block.toLocaleString()]})]})]}),o.jsxs("span",{className:"event-card__cause",children:[i?"origin":`after ${fe(e.causation_id,6,3)}`,o.jsx("span",{className:"chevron",children:r?"−":"+"})]})]}),r&&o.jsxs("div",{className:"event-card__detail",children:[o.jsxs("dl",{className:"trace-grid",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Event"}),o.jsx("dd",{children:o.jsx(kr,{value:e.event_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Caused by"}),o.jsx("dd",{children:o.jsx(kr,{value:e.causation_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Flow origin"}),o.jsx("dd",{children:o.jsx(kr,{value:e.origin_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Followed by"}),o.jsx("dd",{className:"trace-successors",children:t.length?t.map(s=>o.jsx(kr,{value:s.event_id,label:`${s.event_type} · ${fe(s.event_id,5,3)}`,onLocate:n},s.event_id)):o.jsx("span",{children:"Nothing observed yet"})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Local sequence"}),o.jsxs("dd",{className:"mono",children:["#",e.seq]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"HLC producer"}),o.jsx("dd",{className:"mono",children:e.producer_fingerprint})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Logical order"}),o.jsx("dd",{className:"mono",children:e.logical_counter})]})]}),o.jsx("div",{className:"payload-head",children:"Structured payload"}),o.jsx("pre",{className:"payload",children:vc(e.payload)})]})]})}function Bs({events:e,traceEvents:t=e,onNavigate:n,empty:r="No events observed for this stage yet."}){const l=T.useMemo(()=>new Set(e.map(u=>u.event_id)),[e]),i=T.useMemo(()=>{const u=new Map;for(const a of t){if(a.causation_id===a.event_id)continue;const c=u.get(a.causation_id)??[];c.push(a),u.set(a.causation_id,c)}return u},[t]),s=u=>{var c;if(n){n(u);return}const a=document.getElementById(`event-${u}`);a?(a.scrollIntoView({behavior:"smooth",block:"center"}),a.classList.add("event-card--located"),window.setTimeout(()=>a.classList.remove("event-card--located"),1400)):l.has(u)||(c=document.getElementById("flow-origin-note"))==null||c.scrollIntoView({behavior:"smooth",block:"center"})};return e.length===0?o.jsx("div",{className:"empty-inline",children:r}):o.jsx("div",{className:"event-timeline",children:e.map(u=>o.jsx(ep,{event:u,successors:i.get(u.event_id)??[],onLocate:s},`${u.aggregate_id}-${u.seq}`))})}function tp(){var p;const e=Zf(),t=T.useMemo(()=>{var f;return((f=e.data)==null?void 0:f.events)??[]},[(p=e.data)==null?void 0:p.events]),[n,r]=T.useState(""),[l,i]=T.useState("all"),[s,u]=T.useState("all"),a=T.useMemo(()=>({local:t.filter(f=>f.source==="local").length,network:t.filter(f=>f.source==="network").length,evm:t.filter(f=>f.source==="evm").length}),[t]),c=Math.max(1,a.local+a.network+a.evm),m=T.useMemo(()=>{const f=n.toLowerCase().trim();return t.filter(y=>l!=="all"&&y.source!==l||s!=="all"&&y.severity!==s?!1:!f||`${y.event_type} ${y.e3_id??""} ${y.producer} ${JSON.stringify(y.payload)}`.toLowerCase().includes(f))},[t,n,s,l]);return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"EventStore"}),o.jsx("h1",{children:"Raw protocol activity"}),o.jsx("p",{children:"The latest durable facts observed by this node across local, network, and EVM sources."})]})}),o.jsx("section",{className:"source-viz","aria-label":"Protocol event sources",children:["local","network","evm"].map(f=>o.jsxs("button",{type:"button",onClick:()=>i(f),className:l===f?"source-viz__item source-viz__item--selected":"source-viz__item",children:[o.jsx("span",{children:f}),o.jsx("strong",{className:"mono",children:a[f]}),o.jsx("i",{children:o.jsx("b",{className:`source-viz__fill source-viz__fill--${f}`,style:{width:`${a[f]/c*100}%`}})})]},f))}),o.jsxs("section",{className:"panel panel--wide",children:[e.error&&o.jsxs("div",{className:"alert alert--warning",children:["Live refresh paused: ",e.error]}),o.jsxs("div",{className:"filter-bar",children:[o.jsxs("label",{className:"search-field",children:[o.jsx("span",{children:"⌕"}),o.jsx("input",{value:n,onChange:f=>r(f.target.value),placeholder:"Search event, E3, node, or payload"})]}),o.jsxs("select",{value:l,onChange:f=>i(f.target.value),children:[o.jsx("option",{value:"all",children:"All sources"}),o.jsx("option",{value:"local",children:"Local"}),o.jsx("option",{value:"network",children:"Network"}),o.jsx("option",{value:"evm",children:"EVM"})]}),o.jsxs("select",{value:s,onChange:f=>u(f.target.value),children:[o.jsx("option",{value:"all",children:"All levels"}),o.jsx("option",{value:"error",children:"Errors"}),o.jsx("option",{value:"warn",children:"Warnings"}),o.jsx("option",{value:"info",children:"Info"}),o.jsx("option",{value:"debug",children:"Debug"})]}),o.jsxs("span",{className:"filter-count mono",children:[m.length," / ",t.length]})]}),o.jsx(Bs,{events:m,empty:"No durable events match these filters."})]})]})}const _r=["request","committee","dkg_setup","dkg_shares","key_publication","computation","decryption","settlement"],Jl=222,ql=170,bl=48,eu=66;function np(e){return e.length>24?`${e.slice(0,22)}…`:e}function rp({events:e,onSelect:t,selected:n}){const r=T.useMemo(()=>{const l=new Map,i=e.map(c=>{const m=c.phase??"request",p=l.get(m)??0;return l.set(m,p+1),{event:c,x:30+_r.indexOf(m)*Jl,y:74+p*eu}}),s=new Map(i.map(c=>[c.event.event_id,c])),u=i.flatMap(c=>{if(c.event.causation_id===c.event.event_id)return[];const m=s.get(c.event.causation_id);return m?[{cause:m,effect:c}]:[]}),a=Math.max(1,..._r.map(c=>l.get(c)??0));return{nodes:i,edges:u,byId:s,height:100+a*eu}},[e]);return o.jsx("div",{className:"flow-canvas",children:o.jsxs("svg",{width:_r.length*Jl+40,height:r.height,role:"img","aria-label":"Causal E3 event graph",children:[o.jsx("defs",{children:o.jsx("marker",{id:"flow-arrow",markerWidth:"8",markerHeight:"8",refX:"7",refY:"4",orient:"auto",children:o.jsx("path",{d:"M0,0 L8,4 L0,8 z",className:"flow-arrow"})})}),_r.map((l,i)=>o.jsxs("g",{transform:`translate(${30+i*Jl}, 20)`,children:[o.jsx("text",{className:"flow-phase-title",children:l.replaceAll("_"," ")}),o.jsx("line",{className:"flow-phase-rule",x1:"0",x2:ql,y1:"26",y2:"26"})]},l)),o.jsx("g",{className:"flow-edges",children:r.edges.map(({cause:l,effect:i})=>{const s=l.x+ql,u=l.y+bl/2,a=i.x,c=i.y+bl/2,m=Math.max(25,Math.abs(a-s)/2),p=n===l.event.event_id||n===i.event.event_id;return o.jsx("path",{className:p?"flow-edge flow-edge--selected":"flow-edge",d:`M ${s} ${u} C ${s+m} ${u}, ${a-m} ${c}, ${a} ${c}`,markerEnd:"url(#flow-arrow)"},`${l.event.event_id}-${i.event.event_id}`)})}),o.jsx("g",{className:"flow-nodes",children:r.nodes.map(({event:l,x:i,y:s})=>o.jsxs("g",{className:`flow-event flow-event--${l.source} flow-event--${l.severity} ${n===l.event_id?"flow-event--selected":""}`,transform:`translate(${i}, ${s})`,role:"button",tabIndex:0,onClick:()=>t(l),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&t(l)},children:[o.jsx("rect",{width:ql,height:bl,rx:"8"}),o.jsx("circle",{cx:"13",cy:"15",r:"4"}),o.jsx("text",{className:"flow-event__title",x:"24",y:"18",children:np(l.event_type)}),o.jsxs("text",{className:"flow-event__meta",x:"12",y:"36",children:[l.source.toUpperCase()," · #",l.seq," · ",fe(l.event_id,5,3)]})]},`${l.aggregate_id}-${l.seq}`))})]})})}function lp({e3s:e,selectedE3:t,onSelectE3:n,refreshKey:r}){var m,p,f,y,k;const l=hc(t,r),[i,s]=T.useState();T.useEffect(()=>{var g;s((g=l.data)==null?void 0:g.events.at(-1))},[t,(m=l.data)==null?void 0:m.events]);const u=((p=l.data)==null?void 0:p.events.filter(g=>{var P;return(P=l.data)==null?void 0:P.events.some(h=>h.event_id===g.causation_id)}).length)??0,a=(((f=l.data)==null?void 0:f.events.length)??0)-u,c=i?((y=l.data)==null?void 0:y.events.filter(g=>g.causation_id===i.event_id&&g.event_id!==i.event_id))??[]:[];return o.jsxs("div",{className:"view-stack",children:[o.jsxs("header",{className:"view-title flow-view-title",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Causal topology"}),o.jsx("h1",{children:"E3 event flow graph"}),o.jsx("p",{children:"Every edge means “caused by.” Select any node to inspect its stage, source, context, successors, and failure payload."})]}),o.jsxs("label",{className:"flow-selector",children:[o.jsx("span",{children:"Computation"}),o.jsx("select",{value:t??"",onChange:g=>n(g.target.value),children:e.map(g=>o.jsxs("option",{value:g.e3_id,children:["E3 ",g.e3_id," · ",g.status]},g.e3_id))})]})]}),l.data?o.jsxs(o.Fragment,{children:[o.jsxs("section",{className:"graph-metrics",children:[o.jsxs("span",{children:[o.jsx("strong",{children:l.data.events.length})," events"]}),o.jsxs("span",{children:[o.jsx("strong",{children:u})," causal edges"]}),o.jsxs("span",{children:[o.jsx("strong",{children:a})," observed roots"]}),o.jsxs("span",{children:[o.jsx("strong",{children:l.data.error_count})," failures"]})]}),o.jsxs("div",{className:"flow-layout",children:[o.jsxs("section",{className:"panel flow-graph-panel",children:[o.jsxs("div",{className:"flow-legend",children:[o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--local"}),"Local"]}),o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--network"}),"Network"]}),o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--evm"}),"EVM"]}),o.jsx("span",{className:"flow-legend__hint",children:"Scroll horizontally and vertically · click a node"})]}),o.jsx(rp,{events:l.data.events,selected:i==null?void 0:i.event_id,onSelect:s})]}),o.jsx("aside",{className:"panel flow-detail",children:i?o.jsxs(o.Fragment,{children:[o.jsx("span",{className:"section-kicker",children:((k=i.phase)==null?void 0:k.replaceAll("_"," "))??"unclassified"}),o.jsx("h2",{children:i.event_type}),o.jsx("p",{className:"mono flow-detail__time",children:mc(i.timestamp_us)}),o.jsxs("dl",{className:"flow-detail-grid",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Event"}),o.jsx("dd",{className:"mono",children:i.event_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Caused by"}),o.jsx("dd",{className:"mono",children:i.causation_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Origin"}),o.jsx("dd",{className:"mono",children:i.origin_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Observed from"}),o.jsxs("dd",{children:[i.source," · ",i.producer]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Successors"}),o.jsx("dd",{children:c.length||"None observed"})]})]}),c.length>0&&o.jsx("div",{className:"flow-successor-list",children:c.map(g=>o.jsxs("button",{type:"button",onClick:()=>s(g),children:[g.event_type," ",o.jsx("span",{className:"mono",children:fe(g.event_id,5,3)})]},g.event_id))}),o.jsx("div",{className:"payload-head",children:"Structured payload"}),o.jsx("pre",{className:"payload",children:vc(i.payload)})]}):o.jsx("div",{className:"empty-inline",children:"Select an event node."})})]})]}):o.jsxs("section",{className:"panel blank-state",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h2",{children:l.error??"Reconstructing the causal graph…"})]})]})}function ip({phases:e,selected:t,onSelect:n}){return o.jsx("div",{className:"stage-flow","aria-label":"E3 protocol stages",children:e.map((r,l)=>o.jsxs("div",{className:"stage-flow__unit",children:[o.jsxs("button",{type:"button",className:`stage-node stage-node--${r.state} ${t===r.id?"stage-node--selected":""}`,onClick:()=>n(r.id),children:[o.jsx("span",{className:"stage-node__index",children:String(l+1).padStart(2,"0")}),o.jsxs("span",{className:"stage-node__copy",children:[o.jsx("strong",{children:r.label}),o.jsxs("span",{children:[r.event_count," events · L",r.sources.local," N",r.sources.net," E",r.sources.evm]})]}),o.jsx("span",{className:"stage-node__state",children:r.state==="complete"?"✓":r.state==="failed"?"!":r.state==="active"?"●":"○"})]}),lo.jsxs("button",{type:"button",className:`e3-list__item ${t===r.e3_id?"e3-list__item--selected":""}`,onClick:()=>n(r.e3_id),children:[o.jsx("span",{className:`status-dot status-dot--${r.status}`}),o.jsxs("span",{children:[o.jsx("strong",{className:"mono",children:r.e3_id}),o.jsxs("small",{children:[r.current_phase.replaceAll("_"," ")," · ",r.event_count," events"]})]}),o.jsx("span",{className:"e3-list__time mono",children:Vs(r.last_seen_us)})]},r.e3_id)),!e.length&&o.jsx("div",{className:"empty-inline",children:"No E3s have reached this node yet."})]})]})}function op({e3s:e,selected:t,onSelect:n,refreshKey:r}){var f,y,k;const l=hc(t,r),[i,s]=T.useState("request"),[u,a]=T.useState(),c=(f=l.data)==null?void 0:f.current_phase;T.useEffect(()=>{c&&s(c)},[t,c]);const m=T.useMemo(()=>{var g;return((g=l.data)==null?void 0:g.events.filter(P=>P.phase===i))??[]},[i,(y=l.data)==null?void 0:y.events]);T.useEffect(()=>{if(!u)return;const g=window.requestAnimationFrame(()=>{const P=document.getElementById(`event-${u.id}`);P==null||P.scrollIntoView({behavior:"smooth",block:"center"}),P==null||P.classList.add("event-card--located"),window.setTimeout(()=>P==null?void 0:P.classList.remove("event-card--located"),1400)});return()=>window.cancelAnimationFrame(g)},[u,i]);const p=g=>{var h;const P=(h=l.data)==null?void 0:h.events.find(d=>d.event_id===g);P&&(P.phase&&s(P.phase),a({id:g,nonce:Date.now()}))};return o.jsxs("div",{className:"inspector-layout",children:[o.jsx(sp,{e3s:e,selected:t,onSelect:n}),o.jsx("main",{className:"inspector-main",children:t?l.error&&!l.data?o.jsxs("div",{className:"blank-state blank-state--error",children:[o.jsx("h1",{children:"Couldn’t load this trace"}),o.jsx("p",{children:l.error})]}):l.data?o.jsxs(o.Fragment,{children:[o.jsxs("header",{className:"trace-head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Encrypted execution environment"}),o.jsxs("h1",{className:"mono",children:["E3 ",l.data.e3_id]}),o.jsxs("p",{children:["Chain ",l.data.chain_id," · first observed ",Vs(l.data.first_seen_us)," · ",l.data.event_count," events"]})]}),o.jsxs("span",{className:`trace-status trace-status--${l.data.status}`,children:[o.jsx("span",{}),l.data.status]})]}),l.data.failure!=null&&o.jsxs("div",{className:"failure-banner",children:[o.jsx("strong",{children:"Failure localized"}),o.jsx("code",{children:JSON.stringify(l.data.failure)})]}),o.jsx(ip,{phases:l.data.phases,selected:i,onSelect:s}),o.jsxs("div",{className:"trace-columns",children:[o.jsxs("section",{className:"panel trace-events",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Selected stage"}),o.jsx("h2",{children:(k=l.data.phases.find(g=>g.id===i))==null?void 0:k.label})]}),o.jsxs("span",{className:"panel__aside",children:[m.length," events"]})]}),o.jsx("div",{id:"flow-origin-note",className:"flow-note",children:"Every row carries its durable event, cause, origin, and immediate successors. Causal links jump across stages while preserving this E3 trace."}),o.jsx(Bs,{events:m,traceEvents:l.data.events,onNavigate:p})]}),o.jsxs("aside",{className:"trace-sidebar",children:[o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Participants"}),o.jsx("h2",{children:"Committee"})]}),o.jsx("span",{className:"panel__aside",children:l.data.committee.length})]}),o.jsxs("div",{className:"committee-list",children:[l.data.committee.map(g=>o.jsxs("div",{className:`committee-member ${g.expelled?"committee-member--expelled":""}`,children:[o.jsxs("span",{className:"committee-member__party mono",children:["P",g.party_id]}),o.jsxs("div",{children:[o.jsx("strong",{className:"mono",title:g.address,children:fe(g.address,10,6)}),o.jsxs("small",{title:g.score,children:["score ",qt(g.score)]})]}),g.expelled&&o.jsx("span",{className:"status-tag status-tag--failed",children:"Expelled"})]},g.address)),!l.data.committee.length&&o.jsx("div",{className:"empty-inline",children:"Committee not finalized."})]})]}),o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Sortition"}),o.jsx("h2",{children:"Tickets"})]}),o.jsx("span",{className:"panel__aside",children:l.data.tickets.length})]}),o.jsxs("div",{className:"ticket-list",children:[l.data.tickets.slice(0,12).map(g=>o.jsxs("div",{children:[o.jsxs("span",{className:"mono",children:["#",g.ticket_id]}),o.jsx("strong",{className:"mono",children:fe(g.node,8,5)}),o.jsx("small",{children:qt(g.score)})]},`${g.node}-${g.ticket_id}`)),!l.data.tickets.length&&o.jsx("div",{className:"empty-inline",children:"No submitted tickets observed."})]})]}),o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Settlement"}),o.jsx("h2",{children:"Rewards"})]}),o.jsx("span",{className:"panel__aside",children:l.data.rewards.length})]}),o.jsxs("div",{className:"reward-list",children:[l.data.rewards.map((g,P)=>o.jsxs("div",{children:[o.jsx("span",{className:`reward-state ${g.claimed?"reward-state--claimed":""}`,children:g.claimed?"Claimed":"Credited"}),o.jsx("strong",{className:"mono",title:g.account,children:fe(g.account,8,5)}),o.jsx("small",{className:"mono",title:g.amount,children:qt(g.amount)})]},`${g.account}-${P}`)),!l.data.rewards.length&&o.jsx("div",{className:"empty-inline",children:"Rewards are recorded when the E3 settles."})]})]})]})]})]}):o.jsxs("div",{className:"blank-state",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h1",{children:"Rebuilding the trace"}),o.jsx("p",{children:"Reading durable events from this node…"})]}):o.jsxs("div",{className:"blank-state",children:[o.jsx("span",{className:"blank-state__glyph",children:"E3"}),o.jsx("h1",{children:"No computation selected"}),o.jsx("p",{children:"Select an E3 to inspect its complete local flow."})]})})]})}function up(){var u,a,c;const e=Gf(),[t,n]=T.useState(""),[r,l]=T.useState("all"),i=T.useMemo(()=>{var f;const m=new Map;for(const y of((f=e.data)==null?void 0:f.entries)??[])m.set(y.level.toLowerCase(),(m.get(y.level.toLowerCase())??0)+1);const p=Math.max(1,...m.values());return["error","warn","info","debug","trace"].map(y=>({name:y,count:m.get(y)??0,maximum:p}))},[(u=e.data)==null?void 0:u.entries]),s=T.useMemo(()=>{var p;const m=t.toLowerCase().trim();return(((p=e.data)==null?void 0:p.entries)??[]).filter(f=>r!=="all"&&f.level.toLowerCase()!==r?!1:!m||`${f.message} ${f.target} ${JSON.stringify(f.fields??{})}`.toLowerCase().includes(m)).reverse()},[r,(a=e.data)==null?void 0:a.entries,t]);return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Operational diagnostics"}),o.jsx("h1",{children:"Ciphernode logs"}),o.jsx("p",{children:"Structured tracing output for networking, RPC, proving, startup, and resource diagnostics. Protocol facts live in EventStore."})]})}),o.jsxs("section",{className:"panel log-viz","aria-label":"Log level distribution",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Current memory window"}),o.jsx("h2",{children:"Signal distribution"})]}),o.jsx("div",{className:"log-viz__bars",children:i.map(m=>o.jsxs("div",{children:[o.jsx("span",{children:m.name}),o.jsx("i",{children:o.jsx("b",{className:`log-viz__bar log-viz__bar--${m.name}`,style:{width:`${m.count/m.maximum*100}%`}})}),o.jsx("strong",{className:"mono",children:m.count})]},m.name))})]}),o.jsxs("section",{className:"panel panel--wide",children:[o.jsxs("div",{className:"filter-bar",children:[o.jsxs("label",{className:"search-field",children:[o.jsx("span",{children:"⌕"}),o.jsx("input",{value:t,onChange:m=>n(m.target.value),placeholder:"Search messages, targets, and fields"})]}),o.jsxs("select",{value:r,onChange:m=>l(m.target.value),children:[o.jsx("option",{value:"all",children:"All levels"}),o.jsx("option",{value:"error",children:"Error"}),o.jsx("option",{value:"warn",children:"Warn"}),o.jsx("option",{value:"info",children:"Info"}),o.jsx("option",{value:"debug",children:"Debug"}),o.jsx("option",{value:"trace",children:"Trace"})]}),o.jsxs("span",{className:"filter-count mono",children:[s.length," visible · ",((c=e.data)==null?void 0:c.total_stored)??0," stored"]})]}),e.error&&o.jsx("div",{className:"alert alert--warning",children:e.error}),o.jsxs("div",{className:"log-table",children:[o.jsxs("div",{className:"log-table__head",children:[o.jsx("span",{children:"Time"}),o.jsx("span",{children:"Level"}),o.jsx("span",{children:"Target"}),o.jsx("span",{children:"Message"})]}),s.map(m=>{var p,f,y;return o.jsxs("details",{className:`log-row log-row--${m.level.toLowerCase()}`,children:[o.jsxs("summary",{children:[o.jsx("time",{className:"mono",children:new Date(m.timestamp_ms).toLocaleTimeString()}),o.jsx("span",{className:"log-level",children:m.level}),o.jsx("span",{className:"mono",title:m.target,children:fe(m.target,24,10)}),o.jsx("strong",{children:m.message})]}),(((p=m.fields)==null?void 0:p.e3_id)||((f=m.fields)==null?void 0:f.event_id)||((y=m.fields)==null?void 0:y.stage))&&o.jsxs("div",{className:"log-trace-context",children:[m.fields.stage&&o.jsxs("span",{children:["stage ",o.jsx("strong",{children:String(m.fields.stage)})]}),m.fields.e3_id&&o.jsxs("span",{children:["E3 ",o.jsx("strong",{className:"mono",children:String(m.fields.e3_id)})]}),m.fields.event_id&&o.jsxs("span",{children:["event ",o.jsx("strong",{className:"mono",children:String(m.fields.event_id)})]})]}),o.jsx("pre",{children:JSON.stringify(m.fields??{},null,2)})]},m.seq)}),!s.length&&o.jsx("div",{className:"empty-inline",children:"No operational logs match these filters."})]})]})]})}function tu(e){try{return e.reduce((t,n)=>t+BigInt(n),0n).toLocaleString()}catch{return"—"}}function jr({label:e,value:t,note:n,tone:r}){return o.jsxs("div",{className:`metric ${r?`metric--${r}`:""}`,children:[o.jsx("span",{className:"metric__label",children:e}),o.jsx("strong",{className:"metric__value",children:typeof t=="number"?qf(t):t}),o.jsx("span",{className:"metric__note",children:n})]})}function ap({snapshot:e}){const{node:t,network:n,operator:r,protocol:l}=e,i=new Map(r.chains.map(p=>[p.chain_id,p])),s=new Map(l.chains.map(p=>[p.chain_id,p])),a=[...new Set([...t.chains.map(p=>p.id),...r.chains.map(p=>p.chain_id),...l.chains.map(p=>p.chain_id)])].sort((p,f)=>p-f).map(p=>{var k;const f=i.get(p),y=s.get(p);return{chainId:p,chainName:(f==null?void 0:f.chain_name)??((k=t.chains.find(g=>g.id===p))==null?void 0:k.name)??`Chain ${p}`,registeredNodes:(f==null?void 0:f.registered_nodes)??String((y==null?void 0:y.registered_nodes)??0),activeNodes:(f==null?void 0:f.active_nodes)??String((y==null?void 0:y.active_nodes)??0),registered:(f==null?void 0:f.operator_registered)??(y==null?void 0:y.operator_registered)??!1,active:(f==null?void 0:f.operator_active)??(y==null?void 0:y.operator_active)??!1,exitInProgress:(f==null?void 0:f.exit_in_progress)??(y==null?void 0:y.exit_unlock_at)!==void 0,ticketBalance:(f==null?void 0:f.ticket_balance)??(y==null?void 0:y.ticket_balance),availableTickets:f==null?void 0:f.available_tickets,licenseBond:(f==null?void 0:f.license_bond)??(y==null?void 0:y.license_bond),rewardCredits:(y==null?void 0:y.rewards_credited.length)??0}}),c=tu(a.map(p=>p.registeredNodes)),m=tu(a.map(p=>p.activeNodes));return o.jsxs("div",{className:"view-stack",children:[o.jsxs("section",{className:"view-intro",children:[o.jsxs("div",{children:[o.jsxs("div",{className:"eyebrow",children:[o.jsx("span",{className:"live-dot"})," Node online · ",t.node_name]}),o.jsx("h1",{children:"Everything your ciphernode knows, in one place."}),o.jsx("p",{children:"Live transport health, durable protocol history, and causal E3 traces projected from this node’s own EventStore."})]}),o.jsxs("div",{className:"identity-card",children:[o.jsx("span",{children:"Operator identity"}),o.jsx("strong",{className:"mono",title:t.address,children:fe(t.address,12,8)}),o.jsx("span",{className:"mono",title:t.peer_id,children:fe(t.peer_id,13,8)})]})]}),o.jsxs("section",{className:"metrics-grid",children:[o.jsx(jr,{label:"Network reach",value:`${n.connected_peers.length}/${n.configured_peers}`,note:"connected / configured peers",tone:n.last_error?"bad":"good"}),o.jsx(jr,{label:"Registered nodes",value:c,note:`${m} currently active`}),o.jsx(jr,{label:"E3 workload",value:l.e3_total,note:`${l.e3_active} active · ${l.e3_completed} complete`}),o.jsx(jr,{label:"Durable events",value:l.events_observed,note:"observed across all aggregates"})]}),o.jsxs("div",{className:"overview-grid",children:[o.jsxs("section",{className:"panel",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Transport"}),o.jsx("h2",{children:"Connected nodes"})]}),o.jsx("span",{className:`health-pill ${n.last_error?"health-pill--bad":""}`,children:n.last_error?"Attention":"Healthy"})]}),n.connected_peers.length?o.jsx("div",{className:"peer-list",children:n.connected_peers.map(p=>o.jsxs("div",{className:"peer-row",children:[o.jsx("span",{className:"peer-row__status"}),o.jsxs("div",{children:[o.jsx("strong",{className:"mono",children:fe(p.peer_id,12,7)}),o.jsx("span",{className:"mono",children:fe(p.remote_address,22,10)})]}),o.jsxs("span",{className:"peer-row__direction",children:[p.direction," · ",p.connections," conn."]})]},p.peer_id))}):o.jsx("div",{className:"empty-inline",children:"No live peer connections. The node will keep dialing its configured peers."}),n.last_error&&o.jsx("div",{className:"alert alert--warning",children:n.last_error}),o.jsxs("div",{className:"listen-addresses",children:[o.jsx("span",{children:"Listening"}),n.listen_addresses.map(p=>o.jsx("code",{children:p},p)),!n.listen_addresses.length&&o.jsxs("code",{children:["UDP / QUIC ",t.quic_port]})]})]}),o.jsxs("section",{className:"panel",children:[o.jsx("header",{className:"panel__head",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"On-chain position"}),o.jsx("h2",{children:"Operator state"})]})}),o.jsxs("div",{className:"chain-list",children:[a.map(p=>o.jsxs("div",{className:"chain-card",children:[o.jsxs("div",{className:"chain-card__head",children:[o.jsx("strong",{children:p.chainName}),o.jsx("span",{className:`status-tag status-tag--${p.active?"complete":p.registered?"active":"pending"}`,children:p.exitInProgress?"Exit queued":p.active?"Active":p.registered?"Registered":"Not registered"})]}),o.jsxs("dl",{className:"mini-dl",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Available tickets"}),o.jsx("dd",{className:"mono",title:p.availableTickets,children:qt(p.availableTickets)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Ticket balance"}),o.jsx("dd",{className:"mono",title:p.ticketBalance,children:qt(p.ticketBalance)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"License bond"}),o.jsx("dd",{className:"mono",title:p.licenseBond,children:qt(p.licenseBond)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Network"}),o.jsxs("dd",{children:[p.activeNodes," / ",p.registeredNodes," active"]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Rewards"}),o.jsxs("dd",{children:[p.rewardCredits," credits"]})]})]})]},p.chainId)),!a.length&&o.jsx("div",{className:"empty-inline",children:"Waiting for on-chain registry state to sync."}),r.error&&o.jsx("div",{className:"alert alert--warning",children:r.error})]})]})]}),o.jsxs("section",{className:"panel panel--wide",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Live activity"}),o.jsx("h2",{children:"Latest protocol events"})]}),o.jsx("span",{className:"panel__aside",children:"Newest first"})]}),o.jsx(Bs,{events:e.recent_events.slice(0,12),empty:"The EventStore is synced; no protocol events have been observed yet."})]})]})}function cp({updates:e,activeE3s:t}){var s,u,a,c,m;const[n,r]=T.useState(!1),l=()=>{navigator.clipboard.writeText("interfoldup update").then(()=>{r(!0),window.setTimeout(()=>r(!1),1500)})},i=e.data;return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Operator continuity"}),o.jsx("h1",{children:"Updates and release desk"}),o.jsx("p",{children:"Release awareness and a deliberately manual, auditable upgrade path. The dashboard never replaces a running binary itself."})]})}),o.jsxs("div",{className:"update-grid",children:[o.jsxs("section",{className:"panel update-status",children:[o.jsx("span",{className:`update-orbit ${i!=null&&i.update_available?"update-orbit--available":""}`,children:o.jsx("i",{})}),o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Installed"}),o.jsxs("h2",{children:["Interfold ",(i==null?void 0:i.current_version)??"…"]}),o.jsx("p",{children:i!=null&&i.update_available?`${((s=i.latest)==null?void 0:s.tag)??"A newer release"} is available.`:i!=null&&i.latest?`This node matches the latest stable release, ${i.latest.tag}.`:"Checking the release channel…"}),((i==null?void 0:i.error)||e.error)&&o.jsx("div",{className:"alert alert--warning",children:(i==null?void 0:i.error)??e.error})]})]}),o.jsxs("section",{className:"panel safe-update",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Runbook"}),o.jsx("h2",{children:"Safe update sequence"})]}),o.jsx("span",{className:`status-tag status-tag--${t?"active":"complete"}`,children:t?`${t} active E3`:"Safe window"})]}),o.jsxs("ol",{className:"update-steps",children:[o.jsxs("li",{children:[o.jsx("strong",{children:"Wait for active work."}),o.jsx("span",{children:t?"Do not stop yet; let the active E3s settle unless this is an emergency.":"No active E3 is currently projected."})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Stop gracefully."}),o.jsx("span",{children:"Send SIGTERM or Ctrl+C and wait for “Graceful shutdown complete.”"})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Install the release."}),o.jsx("button",{type:"button",className:"copy-command mono",onClick:l,children:n?"Copied":"interfoldup update"})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Restart identically."}),o.jsx("span",{children:"Use the same service account, configuration, database, and event-log paths."})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Verify recovery."}),o.jsx("span",{children:"Confirm version, peers, operator state, and the resumed E3 stage before leaving the node unattended."})]})]})]})]}),o.jsxs("section",{className:"panel panel--wide release-notes",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Release channel"}),o.jsx("h2",{children:((u=i==null?void 0:i.latest)==null?void 0:u.name)??"Latest stable release"})]}),o.jsx("a",{href:((a=i==null?void 0:i.latest)==null?void 0:a.url)??(i==null?void 0:i.releases_url),target:"_blank",rel:"noreferrer",children:"Open on GitHub ↗"})]}),((c=i==null?void 0:i.latest)==null?void 0:c.published_at)&&o.jsxs("p",{className:"release-date",children:["Published ",new Date(i.latest.published_at).toLocaleString()]}),o.jsx("pre",{children:((m=i==null?void 0:i.latest)==null?void 0:m.notes)||"Release notes will appear here when GitHub is reachable."})]})]})}const gc=[{id:"overview",label:"Overview"},{id:"e3",label:"E3 traces"},{id:"flow",label:"Flow graph"},{id:"events",label:"Events"},{id:"logs",label:"Logs"},{id:"updates",label:"Updates"}];function dp(){const e=window.location.hash.slice(1);return gc.some(t=>t.id===e)?e:"overview"}function fp({view:e,setView:t,nodeName:n,connected:r,updateAvailable:l}){const i=s=>{window.history.replaceState(null,"",`#${s}`),t(s)};return o.jsx("header",{className:"site-head",children:o.jsxs("div",{className:"site-head__inner",children:[o.jsx("button",{className:"wordmark",type:"button","aria-label":"Interfold node overview",onClick:()=>i("overview"),children:o.jsx("span",{})}),o.jsx("span",{className:"product-name",children:"Node observatory"}),o.jsx("nav",{className:"site-nav","aria-label":"Dashboard views",children:gc.map(s=>o.jsxs("button",{type:"button",className:e===s.id?"site-nav__link site-nav__link--on":"site-nav__link",onClick:()=>i(s.id),children:[s.label,s.id==="updates"&&l&&o.jsx("span",{className:"nav-update-dot"})]},s.id))}),o.jsxs("div",{className:"node-chip",children:[o.jsx("span",{className:r?"node-chip__dot":"node-chip__dot node-chip__dot--waiting"}),o.jsx("span",{children:n??"Connecting…"})]})]})})}function pp({error:e}){return o.jsxs("main",{className:"loading-page",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h1",{children:e?"The node API is unavailable":"Building the node picture"}),o.jsx("p",{children:e??"Reading protocol history and live transport state…"})]})}function hp(){var u,a,c;const[e,t]=T.useState(dp),[n,r]=T.useState(),l=Xf(),i=Jf();T.useEffect(()=>{var m;!n&&((m=l.data)!=null&&m.e3s[0])&&r(l.data.e3s[0].e3_id)},[n,(u=l.data)==null?void 0:u.e3s]);const s=!!(l.data&&!l.error);return o.jsxs("div",{className:"page",children:[o.jsx(fp,{view:e,setView:t,nodeName:(a=l.data)==null?void 0:a.node.node_name,connected:s,updateAvailable:!!((c=i.data)!=null&&c.update_available)}),l.data?o.jsxs(o.Fragment,{children:[o.jsxs("div",{className:e==="e3"?"app-main app-main--inspector":"app-main",children:[l.error&&o.jsxs("div",{className:"stale-banner",children:["Live refresh paused: ",l.error,". Showing the last successful snapshot."]}),e==="overview"&&o.jsx(ap,{snapshot:l.data}),e==="e3"&&o.jsx(op,{e3s:l.data.e3s,selected:n,onSelect:r,refreshKey:l.data.protocol.events_observed}),e==="flow"&&o.jsx(lp,{e3s:l.data.e3s,selectedE3:n,onSelectE3:r,refreshKey:l.data.protocol.events_observed}),e==="events"&&o.jsx(tp,{}),e==="logs"&&o.jsx(up,{}),e==="updates"&&o.jsx(cp,{updates:i,activeE3s:l.data.protocol.e3_active})]}),o.jsxs("footer",{className:"site-foot",children:[o.jsx("span",{children:"Local operator surface · bound to 127.0.0.1"}),o.jsxs("span",{className:"mono",children:["Interfold ",l.data.node.version]})]})]}):o.jsx(pp,{error:l.error})]})}ei.createRoot(document.getElementById("root")).render(o.jsx(Ic.StrictMode,{children:o.jsx(hp,{})})); diff --git a/crates/dashboard/assets/index.html b/crates/dashboard/assets/index.html new file mode 100644 index 0000000000..0f679f8bae --- /dev/null +++ b/crates/dashboard/assets/index.html @@ -0,0 +1,15 @@ + + + + + + + + Interfold · Node Observatory + + + + +
+ + diff --git a/crates/dashboard/assets/interfold.svg b/crates/dashboard/assets/interfold.svg new file mode 100644 index 0000000000..86dba271bd --- /dev/null +++ b/crates/dashboard/assets/interfold.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/dashboard/src/dashboard.html b/crates/dashboard/src/dashboard.html deleted file mode 100644 index d7930f59a3..0000000000 --- a/crates/dashboard/src/dashboard.html +++ /dev/null @@ -1,707 +0,0 @@ - - - - - - Interfold Node Dashboard - - - -
-
-

Interfold Node Dashboard

- -
-
-
Overview
-
Events
-
Status
-
-
- -
-
-

Node Information

-
-
Name...
-
Address...
-
Peer ID...
-
QUIC Port...
-
Ctrl Port...
-
-
-
-

Noir Prover

-
Loading...
-
-
- - -
-
- - - - - - - -
-
- - - - - - - - - - - -
SeqTimestampTypeE3 IDSource
-
- -
- - -
-
-

Ciphernode Status

-
Loading...
-
-
-

Wallet

-
Loading...
-
-
-
- - - - diff --git a/crates/dashboard/src/lib.rs b/crates/dashboard/src/lib.rs index 0976f7fbce..f8d54d69c5 100644 --- a/crates/dashboard/src/lib.rs +++ b/crates/dashboard/src/lib.rs @@ -4,259 +4,378 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use std::collections::HashMap; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::net::TcpStream; -use tracing::{error, info}; +mod projection; +mod updates; -const DASHBOARD_HTML: &str = include_str!("dashboard.html"); +use actix_web::{ + get, + http::header::{self, CacheControl, CacheDirective}, + middleware, web, App, HttpResponse, HttpServer, Responder, +}; +use anyhow::{Context, Result}; +use e3_ciphernode_builder::global_eventstore_cache::EventStoreReader; +use e3_config::chain_config::ChainConfig; +use e3_events::{ + AggregateId, CorrelationId, EventContextSeq, EventStoreQueryBy, EventStoreQueryResponse, SeqAgg, +}; +use e3_logger::LogCollector; +use e3_net::NetworkStatus; +use e3_utils::actix::channel as actix_toolbox; +use projection::TelemetryProjection; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + str::FromStr, + sync::Arc, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; +use tokio::sync::Mutex; +use tracing::info; -/// Start the dashboard HTTP server on the given port, proxying API calls to the daemon server. -/// `node_name` and `config_path` are included in proxied requests so the daemon loads the correct config. -pub async fn start_dashboard( - dashboard_port: u16, - ctrl_port: u16, - node_name: String, - config_path: Option, -) { - let addr = format!("0.0.0.0:{}", dashboard_port); - let listener = match tokio::net::TcpListener::bind(&addr).await { - Ok(l) => l, - Err(e) => { - error!("Failed to bind dashboard socket on {}: {}", addr, e); - return; - } - }; +const INDEX_HTML: &str = include_str!("../assets/index.html"); +const APP_JS: &str = include_str!("../assets/app.js"); +const APP_CSS: &str = include_str!("../assets/app.css"); +const INTERFOLD_LOGO: &str = include_str!("../assets/interfold.svg"); +const PAGE_SIZE: u64 = 2_000; - info!("Dashboard listening on http://{}", addr); +#[derive(Clone, Debug, Serialize)] +pub struct DashboardChain { + pub id: u64, + pub name: String, +} - loop { - match listener.accept().await { - Ok((stream, _)) => { - let ctrl = ctrl_port; - let name = node_name.clone(); - let cfg = config_path.clone(); - tokio::task::spawn_local(async move { - if let Err(e) = handle_request(stream, ctrl, &name, cfg.as_deref()).await { - error!("Dashboard connection error: {}", e); - } - }); - } - Err(e) => { - error!("Dashboard accept error: {}", e); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - } - } +#[derive(Clone, Debug, Serialize)] +pub struct DashboardRuntime { + pub node_name: String, + pub address: String, + pub peer_id: String, + pub quic_port: u16, + pub dashboard_port: u16, + pub version: String, + pub chains: Vec, } -struct HttpRequest { - method: String, - path: String, - query: HashMap, +#[derive(Clone)] +pub struct DashboardState { + runtime: DashboardRuntime, + eventstore: EventStoreReader, + aggregate_ids: Arc>, + network: NetworkStatus, + projection: Arc>, + chain_configs: Arc>, + operator_status: Arc>, + updates: updates::UpdateService, } -fn parse_request_line(line: &str) -> Option { - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() < 2 { - return None; - } - let method = parts[0].to_uppercase(); - let raw_path = parts[1]; +struct ProjectionState { + cursors: HashMap, + projection: TelemetryProjection, +} - let (path, query) = if let Some(idx) = raw_path.find('?') { - let p = &raw_path[..idx]; - let q = parse_query_string(&raw_path[idx + 1..]); - (p.to_string(), q) - } else { - (raw_path.to_string(), HashMap::new()) - }; +#[derive(Clone, Debug, Default, Serialize)] +struct OperatorStatusSnapshot { + chains: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + error: Option, + updated_at_ms: u64, +} - Some(HttpRequest { - method, - path, - query, - }) +#[derive(Default)] +struct OperatorStatusCache { + refreshed_at: Option, + snapshot: OperatorStatusSnapshot, } -fn parse_query_string(qs: &str) -> HashMap { - let mut map = HashMap::new(); - for pair in qs.split('&') { - if let Some(idx) = pair.find('=') { - let key = &pair[..idx]; - let value = &pair[idx + 1..]; - map.insert(key.to_string(), value.to_string()); +impl DashboardState { + pub fn new( + runtime: DashboardRuntime, + eventstore: EventStoreReader, + aggregate_ids: Vec, + network: NetworkStatus, + chain_configs: Vec, + ) -> Self { + let mut aggregate_ids = aggregate_ids; + aggregate_ids.sort_unstable(); + aggregate_ids.dedup(); + let projection = TelemetryProjection::new(runtime.address.clone()); + let update_service = updates::UpdateService::new(runtime.version.clone()); + Self { + runtime, + eventstore, + aggregate_ids: Arc::new(aggregate_ids), + network, + projection: Arc::new(Mutex::new(ProjectionState { + cursors: HashMap::new(), + projection, + })), + chain_configs: Arc::new(chain_configs), + operator_status: Arc::new(Mutex::new(OperatorStatusCache::default())), + updates: update_service, } } - map -} -async fn handle_request( - stream: TcpStream, - ctrl_port: u16, - node_name: &str, - config_path: Option<&str>, -) -> anyhow::Result<()> { - let (reader, mut writer) = stream.into_split(); - let mut buf_reader = BufReader::new(reader); + async fn refresh(&self) -> Result<()> { + let mut state = self.projection.lock().await; + for aggregate in self.aggregate_ids.iter().copied() { + loop { + let since = state.cursors.get(&aggregate).copied().unwrap_or(0); + let page = self.fetch_page(aggregate, since).await?; + if page.is_empty() { + break; + } + let event_count = page.len(); + let next = page + .iter() + .map(EventContextSeq::seq) + .max() + .unwrap_or(since) + .saturating_add(1); + for event in page { + state.projection.apply(event); + } + state.cursors.insert(aggregate, next); + if event_count < PAGE_SIZE as usize { + break; + } + } + } + Ok(()) + } - // Read the request line - let mut request_line = String::new(); - buf_reader.read_line(&mut request_line).await?; + async fn fetch_page( + &self, + aggregate: usize, + since: u64, + ) -> Result> { + let (recipient, response) = actix_toolbox::oneshot::(); + let query = EventStoreQueryBy::::new( + CorrelationId::new(), + HashMap::from([(AggregateId::new(aggregate), since)]), + recipient, + ) + .with_limit(PAGE_SIZE); + self.eventstore.seq().do_send(query); + let response = tokio::time::timeout(Duration::from_secs(5), response) + .await + .context("EventStore dashboard query timed out")??; + Ok(response.into_events()) + } - let req = match parse_request_line(request_line.trim()) { - Some(r) => r, - None => { - let resp = http_response("400 Bad Request", "text/plain", "Bad Request"); - writer.write_all(resp.as_bytes()).await?; - writer.shutdown().await?; - return Ok(()); + async fn refresh_operator_status(&self) -> OperatorStatusSnapshot { + let mut cache = self.operator_status.lock().await; + if cache + .refreshed_at + .is_some_and(|updated| updated.elapsed() < Duration::from_secs(15)) + { + return cache.snapshot.clone(); } - }; - // Read remaining headers (we don't need them but must consume them) - loop { - let mut line = String::new(); - buf_reader.read_line(&mut line).await?; - if line.trim().is_empty() { - break; + let operator = match alloy::primitives::Address::from_str(&self.runtime.address) { + Ok(operator) => operator, + Err(error) => { + cache.snapshot.error = Some(format!("invalid operator address: {error}")); + return cache.snapshot.clone(); + } + }; + let mut chains = Vec::new(); + let mut errors = Vec::new(); + let queries = self + .chain_configs + .iter() + .filter(|chain| chain.enabled.unwrap_or(true)) + .map(|chain| async move { + ( + chain.name.clone(), + tokio::time::timeout( + Duration::from_secs(8), + e3_evm::fetch_operator_status(chain, operator), + ) + .await, + ) + }); + for (chain_name, result) in futures::future::join_all(queries).await { + match result { + Ok(Ok(status)) => chains.push(status), + Ok(Err(error)) => errors.push(format!("{chain_name}: {error}")), + Err(_) => errors.push(format!("{chain_name}: RPC query timed out")), + } } + cache.refreshed_at = Some(Instant::now()); + cache.snapshot = OperatorStatusSnapshot { + chains, + error: (!errors.is_empty()).then(|| errors.join("; ")), + updated_at_ms: SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0), + }; + cache.snapshot.clone() } +} - // Handle OPTIONS for CORS preflight - if req.method == "OPTIONS" { - let resp = cors_preflight_response(); - writer.write_all(resp.as_bytes()).await?; - writer.shutdown().await?; - return Ok(()); - } - - let (status, content_type, body) = route(&req, ctrl_port, node_name, config_path).await; +#[get("/api/updates")] +async fn release_updates(state: web::Data) -> impl Responder { + HttpResponse::Ok().json(state.updates.snapshot().await) +} - let resp = http_response_with_cors(&status, &content_type, &body); - writer.write_all(resp.as_bytes()).await?; - writer.shutdown().await?; - Ok(()) +#[derive(Serialize)] +struct DashboardSnapshot { + node: DashboardRuntime, + network: e3_net::NetworkSnapshot, + protocol: projection::ProtocolOverview, + operator: OperatorStatusSnapshot, + e3s: Vec, + recent_events: Vec, } -async fn route( - req: &HttpRequest, - ctrl_port: u16, - node_name: &str, - config_path: Option<&str>, -) -> (String, String, String) { - if req.method == "GET" && req.path == "/" { - return ( - "200 OK".to_string(), - "text/html; charset=utf-8".to_string(), - DASHBOARD_HTML.to_string(), - ); +#[get("/api/snapshot")] +async fn snapshot(state: web::Data) -> impl Responder { + match state.refresh().await { + Ok(()) => { + let operator = state.refresh_operator_status().await; + let projection = state.projection.lock().await; + HttpResponse::Ok().json(DashboardSnapshot { + node: state.runtime.clone(), + network: state.network.snapshot(), + protocol: projection.projection.overview(), + operator, + e3s: projection.projection.summaries(), + recent_events: projection.projection.recent_events(40), + }) + } + Err(error) => api_error(error), } +} - if req.method != "GET" { - return ( - "405 Method Not Allowed".to_string(), - "text/plain".to_string(), - "Method Not Allowed".to_string(), - ); - } +#[derive(Deserialize)] +struct E3Query { + e3_id: String, +} - let command = match req.path.as_str() { - "/api/events" => { - let since = req.query.get("since").and_then(|v| v.parse::().ok()); - let limit = req.query.get("limit").and_then(|v| v.parse::().ok()); - let agg = req.query.get("agg").and_then(|v| v.parse::().ok()); - serde_json::json!({ "EventsQuery": { "agg": agg, "since": since, "limit": limit } }) - } - "/api/config" => { - let param = req.query.get("param").cloned(); - serde_json::json!({ "ConfigGet": { "param": param } }) - } - "/api/status" => { - let chain = req.query.get("chain").cloned(); - serde_json::json!({ "CiphernodeStatus": { "chain": { "chain": chain } } }) +#[get("/api/e3")] +async fn e3_trace(state: web::Data, query: web::Query) -> impl Responder { + match state.refresh().await { + Ok(()) => { + let projection = state.projection.lock().await; + match projection.projection.trace(&query.e3_id) { + Some(trace) => HttpResponse::Ok().json(trace), + None => HttpResponse::NotFound().json(serde_json::json!({ + "error": format!("unknown E3 {}", query.e3_id), + })), + } } - "/api/noir" => serde_json::json!("NoirStatus"), - "/api/wallet" => serde_json::json!("WalletGet"), - "/api/peer-id" => serde_json::json!("NetGetPeerId"), - _ => { - return ( - "404 Not Found".to_string(), - "text/plain".to_string(), - "Not Found".to_string(), - ); + Err(error) => api_error(error), + } +} + +#[derive(Deserialize)] +struct EventsQuery { + limit: Option, +} + +#[get("/api/events")] +async fn protocol_events( + state: web::Data, + query: web::Query, +) -> impl Responder { + match state.refresh().await { + Ok(()) => { + let projection = state.projection.lock().await; + HttpResponse::Ok().json(serde_json::json!({ + "events": projection + .projection + .recent_events(query.limit.unwrap_or(500).min(2_000)), + })) } - }; + Err(error) => api_error(error), + } +} - let json_body = serde_json::json!({ - "name": node_name, - "config": config_path, - "command": command, - "verbose": 0, - "quiet": false - }); +#[derive(Deserialize)] +struct LogsQuery { + since: Option, + limit: Option, + level: Option, + target: Option, + text: Option, +} - match proxy_to_daemon(ctrl_port, &json_body).await { - Ok(response) => ( - "200 OK".to_string(), - "application/json".to_string(), - response, - ), - Err(e) => ( - "502 Bad Gateway".to_string(), - "text/plain".to_string(), - format!("Failed to reach daemon: {}", e), - ), +#[get("/api/logs")] +async fn logs(query: web::Query) -> impl Responder { + match LogCollector::global() { + Some(collector) => HttpResponse::Ok().json(collector.query( + query.since, + query.limit, + query.level.as_deref(), + query.target.as_deref(), + query.text.as_deref(), + )), + None => HttpResponse::Ok().json(serde_json::json!({ + "entries": [], + "next_cursor": 0, + "oldest_cursor": 0, + "total_stored": 0, + })), } } -async fn proxy_to_daemon(ctrl_port: u16, body: &serde_json::Value) -> anyhow::Result { - let url = format!("http://127.0.0.1:{}", ctrl_port); - let client = reqwest::Client::new(); - let resp = client - .post(&url) - .json(body) - .timeout(std::time::Duration::from_secs(10)) - .send() - .await?; - // Don't use error_for_status() — return the body even on 500 - // so the dashboard can display the actual error message. - let text = resp.text().await?; - Ok(text) +async fn index() -> impl Responder { + HttpResponse::Ok() + .insert_header((header::CONTENT_TYPE, "text/html; charset=utf-8")) + .insert_header(CacheControl(vec![CacheDirective::NoCache])) + .body(INDEX_HTML) +} + +async fn app_js() -> impl Responder { + static_asset("text/javascript; charset=utf-8", APP_JS) } -fn http_response(status: &str, content_type: &str, body: &str) -> String { - format!( - "HTTP/1.1 {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", - status, - content_type, - body.len(), - body - ) +async fn app_css() -> impl Responder { + static_asset("text/css; charset=utf-8", APP_CSS) } -fn http_response_with_cors(status: &str, content_type: &str, body: &str) -> String { - format!( - "HTTP/1.1 {}\r\n\ - Content-Type: {}\r\n\ - Content-Length: {}\r\n\ - Access-Control-Allow-Origin: *\r\n\ - Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n\ - Access-Control-Allow-Headers: Content-Type\r\n\ - Connection: close\r\n\r\n{}", - status, - content_type, - body.len(), - body - ) +async fn logo() -> impl Responder { + static_asset("image/svg+xml", INTERFOLD_LOGO) } -fn cors_preflight_response() -> String { - "HTTP/1.1 204 No Content\r\n\ - Access-Control-Allow-Origin: *\r\n\ - Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n\ - Access-Control-Allow-Headers: Content-Type\r\n\ - Access-Control-Max-Age: 86400\r\n\ - Connection: close\r\n\r\n" - .to_string() +fn static_asset(content_type: &'static str, body: &'static str) -> HttpResponse { + HttpResponse::Ok() + .insert_header((header::CONTENT_TYPE, content_type)) + .insert_header(CacheControl(vec![ + CacheDirective::Public, + CacheDirective::MaxAge(31_536_000), + ])) + .body(body) +} + +fn api_error(error: anyhow::Error) -> HttpResponse { + HttpResponse::ServiceUnavailable().json(serde_json::json!({ + "error": error.to_string(), + })) +} + +/// Serve the node-operator dashboard. It binds to loopback because the API +/// exposes detailed protocol payloads and is intentionally unauthenticated. +pub async fn start_dashboard(port: u16, state: DashboardState) -> std::io::Result<()> { + let address = ("127.0.0.1", port); + info!(port, "node dashboard listening on http://127.0.0.1:{port}"); + HttpServer::new(move || { + App::new() + .wrap(middleware::Compress::default()) + .app_data(web::Data::new(state.clone())) + .service(snapshot) + .service(e3_trace) + .service(protocol_events) + .service(logs) + .service(release_updates) + .route("/", web::get().to(index)) + .route("/assets/app.js", web::get().to(app_js)) + .route("/assets/app.css", web::get().to(app_css)) + .route("/assets/interfold.svg", web::get().to(logo)) + .route("/interfold.svg", web::get().to(logo)) + .default_service(web::get().to(index)) + }) + .bind(address)? + .run() + .await } diff --git a/crates/dashboard/src/projection.rs b/crates/dashboard/src/projection.rs new file mode 100644 index 0000000000..a12f15ca45 --- /dev/null +++ b/crates/dashboard/src/projection.rs @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Pure EventStore-to-dashboard projection. + +use e3_events::{ + hlc::HlcTimestamp, E3Stage, Event, EventContextAccessors, EventContextSeq, EventSource, + InterfoldEvent, InterfoldEventData, +}; +use serde::Serialize; +use serde_json::{json, Map, Value}; +use std::collections::{BTreeMap, BTreeSet}; + +const LARGE_ARRAY: usize = 96; +const STRING_PREVIEW: usize = 512; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum E3Phase { + Request, + Committee, + DkgSetup, + DkgShares, + KeyPublication, + Computation, + Decryption, + Settlement, +} + +impl E3Phase { + pub const ALL: [Self; 8] = [ + Self::Request, + Self::Committee, + Self::DkgSetup, + Self::DkgShares, + Self::KeyPublication, + Self::Computation, + Self::Decryption, + Self::Settlement, + ]; + + fn label(self) -> &'static str { + match self { + Self::Request => "Request", + Self::Committee => "Committee formation", + Self::DkgSetup => "DKG · C0", + Self::DkgShares => "DKG · C1–C4", + Self::KeyPublication => "Key publication · C5", + Self::Computation => "Encrypted computation", + Self::Decryption => "Decryption · C6–C7", + Self::Settlement => "Output & rewards", + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum EventSeverity { + Debug, + Info, + Warn, + Error, +} + +#[derive(Clone, Debug, Serialize)] +pub struct EventView { + pub seq: u64, + pub aggregate_id: usize, + pub timestamp_us: u64, + pub logical_counter: u32, + pub producer_fingerprint: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub block: Option, + pub source: String, + pub producer: String, + pub event_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub e3_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub phase: Option, + pub severity: EventSeverity, + pub event_id: String, + pub causation_id: String, + pub origin_id: String, + pub payload: Value, +} + +#[derive(Clone, Debug, Serialize)] +pub struct SourceCounts { + pub local: usize, + pub net: usize, + pub evm: usize, +} + +#[derive(Clone, Debug, Serialize)] +pub struct PhaseView { + pub id: E3Phase, + pub label: &'static str, + pub state: &'static str, + pub event_count: usize, + pub sources: SourceCounts, + pub errors: usize, + pub warnings: usize, +} + +#[derive(Clone, Debug, Serialize)] +pub struct CommitteeMemberView { + pub address: String, + pub party_id: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub score: Option, + pub expelled: bool, +} + +#[derive(Clone, Debug, Serialize)] +pub struct TicketView { + pub node: String, + pub ticket_id: u64, + pub score: String, +} + +#[derive(Clone, Debug, Serialize)] +pub struct RewardView { + pub account: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, + pub amount: String, + pub claimed: bool, +} + +#[derive(Clone, Debug, Serialize)] +pub struct E3Summary { + pub e3_id: String, + pub chain_id: u64, + pub status: String, + pub current_phase: E3Phase, + pub event_count: usize, + pub error_count: usize, + pub warning_count: usize, + pub committee_size: usize, + pub first_seen_us: u64, + pub last_seen_us: u64, +} + +#[derive(Clone, Debug, Serialize)] +pub struct E3Trace { + #[serde(flatten)] + pub summary: E3Summary, + pub phases: Vec, + pub committee: Vec, + pub tickets: Vec, + pub rewards: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub failure: Option, + pub events: Vec, +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct ChainOperatorView { + pub chain_id: u64, + pub registered_nodes: usize, + pub active_nodes: usize, + pub operator_registered: bool, + pub operator_active: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub ticket_balance: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub license_bond: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub exit_unlock_at: Option, + pub rewards_credited: Vec, +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct ProtocolOverview { + pub chains: Vec, + pub e3_total: usize, + pub e3_active: usize, + pub e3_completed: usize, + pub e3_failed: usize, + pub events_observed: usize, +} + +#[derive(Default)] +struct ChainState { + registered: BTreeSet, + active: BTreeSet, + ticket_balance: Option, + license_bond: Option, + exit_unlock_at: Option, + rewards: Vec, +} + +#[derive(Default)] +struct E3State { + e3_id: String, + chain_id: u64, + events: Vec, + status: String, + current_phase: Option, + committee: Vec, + scores: Vec, + tickets: Vec, + expelled: BTreeSet, + rewards: Vec, + failure: Option, + failed_phase: Option, + first_seen_us: u64, + last_seen_us: u64, +} + +#[derive(Default)] +pub struct TelemetryProjection { + local_address: String, + events: Vec, + e3s: BTreeMap, + chains: BTreeMap, +} + +impl TelemetryProjection { + pub fn new(local_address: impl Into) -> Self { + Self { + local_address: normalize_address(&local_address.into()), + ..Self::default() + } + } + + pub fn apply(&mut self, event: InterfoldEvent) { + let view = project_event(&event, &self.local_address); + self.apply_operator_state(event.get_data()); + if let Some(e3_id) = view.e3_id.clone() { + self.apply_e3_state(&e3_id, event.get_data(), &view); + } + self.events.push(view); + } + + pub fn summaries(&self) -> Vec { + let mut summaries: Vec<_> = self.e3s.values().map(summary).collect(); + summaries.sort_by(|a, b| b.last_seen_us.cmp(&a.last_seen_us)); + summaries + } + + pub fn trace(&self, e3_id: &str) -> Option { + self.e3s.get(e3_id).map(trace) + } + + pub fn recent_events(&self, limit: usize) -> Vec { + self.events.iter().rev().take(limit).cloned().collect() + } + + pub fn overview(&self) -> ProtocolOverview { + let summaries = self.summaries(); + ProtocolOverview { + chains: self + .chains + .iter() + .map(|(chain_id, state)| ChainOperatorView { + chain_id: *chain_id, + registered_nodes: state.registered.len(), + active_nodes: state.active.len(), + operator_registered: state.registered.contains(&self.local_address), + operator_active: state.active.contains(&self.local_address), + ticket_balance: state.ticket_balance.clone(), + license_bond: state.license_bond.clone(), + exit_unlock_at: state.exit_unlock_at, + rewards_credited: state.rewards.clone(), + }) + .collect(), + e3_total: summaries.len(), + e3_active: summaries + .iter() + .filter(|summary| summary.status == "active") + .count(), + e3_completed: summaries + .iter() + .filter(|summary| summary.status == "complete") + .count(), + e3_failed: summaries + .iter() + .filter(|summary| summary.status == "failed") + .count(), + events_observed: self.events.len(), + } + } + + fn apply_operator_state(&mut self, data: &InterfoldEventData) { + match data { + InterfoldEventData::CiphernodeAdded(event) => { + self.chains + .entry(event.chain_id) + .or_default() + .registered + .insert(normalize_address(&event.address)); + } + InterfoldEventData::CiphernodeRemoved(event) => { + let state = self.chains.entry(event.chain_id).or_default(); + let address = normalize_address(&event.address); + state.registered.remove(&address); + state.active.remove(&address); + } + InterfoldEventData::OperatorActivationChanged(event) => { + let state = self.chains.entry(event.chain_id).or_default(); + let operator = normalize_address(&event.operator); + if event.active { + state.active.insert(operator); + } else { + state.active.remove(&operator); + } + } + InterfoldEventData::TicketBalanceUpdated(event) + if normalize_address(&event.operator) == self.local_address => + { + self.chains + .entry(event.chain_id) + .or_default() + .ticket_balance = Some(event.new_balance.to_string()); + } + InterfoldEventData::LicenseBondUpdated(event) + if normalize_address(&event.operator) == self.local_address => + { + self.chains.entry(event.chain_id).or_default().license_bond = + Some(event.new_bond.to_string()); + } + InterfoldEventData::CiphernodeDeregistrationRequested(event) + if normalize_address(&event.operator) == self.local_address => + { + self.chains + .entry(event.chain_id) + .or_default() + .exit_unlock_at = Some(event.unlock_at); + } + InterfoldEventData::RewardCredited(event) + if normalize_address(&event.account) == self.local_address => + { + self.chains + .entry(event.e3_id.chain_id()) + .or_default() + .rewards + .push(RewardView { + account: event.account.clone(), + token: Some(event.token.clone()), + amount: event.amount.clone(), + claimed: false, + }); + } + InterfoldEventData::RewardClaimed(event) + if normalize_address(&event.account) == self.local_address => + { + let state = self.chains.entry(event.e3_id.chain_id()).or_default(); + if let Some(reward) = state.rewards.iter_mut().rev().find(|reward| { + normalize_address(&reward.account) == self.local_address + && reward.token.as_deref() == Some(event.token.as_str()) + && reward.amount == event.amount + }) { + reward.claimed = true; + } + } + _ => {} + } + } + + fn apply_e3_state(&mut self, e3_id: &str, data: &InterfoldEventData, view: &EventView) { + let chain_id = e3_id + .split_once(':') + .and_then(|(chain, _)| chain.parse().ok()) + .unwrap_or(0); + let state = self.e3s.entry(e3_id.to_owned()).or_insert_with(|| E3State { + e3_id: e3_id.to_owned(), + chain_id, + status: "active".to_owned(), + first_seen_us: view.timestamp_us, + ..E3State::default() + }); + state.first_seen_us = state.first_seen_us.min(view.timestamp_us); + state.last_seen_us = state.last_seen_us.max(view.timestamp_us); + if state.status != "failed" { + if let Some(phase) = view.phase { + state.current_phase = Some( + state + .current_phase + .map_or(phase, |current| current.max(phase)), + ); + } + } + + match data { + InterfoldEventData::CommitteeFinalized(event) => { + state.committee = event.committee.clone(); + state.scores = event.scores.clone(); + } + InterfoldEventData::CommitteePublished(event) if state.committee.is_empty() => { + state.committee = event.nodes.clone(); + } + InterfoldEventData::TicketSubmitted(event) => state.tickets.push(TicketView { + node: event.node.clone(), + ticket_id: event.ticket_id, + score: event.score.clone(), + }), + InterfoldEventData::CommitteeMemberExpelled(event) => { + state + .expelled + .insert(normalize_address(&event.node.to_string())); + } + InterfoldEventData::E3Failed(event) => { + state.status = "failed".to_owned(); + state.failed_phase = view.phase; + state.failure = Some(json!({ + "failed_at_stage": event.failed_at_stage, + "reason": event.reason, + })); + } + InterfoldEventData::CommitteeFormationFailed(event) => { + state.status = "failed".to_owned(); + state.failed_phase = Some(E3Phase::Committee); + state.failure = Some(json!({ + "reason": "CommitteeFormationFailed", + "nodes_submitted": event.nodes_submitted, + "threshold_required": event.threshold_required, + })); + } + InterfoldEventData::E3RequestComplete(_) + | InterfoldEventData::PlaintextOutputPublished(_) => { + state.status = "complete".to_owned(); + } + InterfoldEventData::E3StageChanged(event) => match event.new_stage { + E3Stage::Complete => state.status = "complete".to_owned(), + E3Stage::Failed => { + let failed_phase = stage_phase(&event.previous_stage); + state.status = "failed".to_owned(); + state.failed_phase = Some(failed_phase); + state.current_phase = Some(failed_phase); + } + _ => {} + }, + InterfoldEventData::RewardsDistributed(event) => { + state.rewards = event + .nodes + .iter() + .zip(&event.amounts) + .map(|(account, amount)| RewardView { + account: account.clone(), + token: None, + amount: amount.clone(), + claimed: false, + }) + .collect(); + } + InterfoldEventData::RewardCredited(event) => { + state.rewards.push(RewardView { + account: event.account.clone(), + token: Some(event.token.clone()), + amount: event.amount.clone(), + claimed: false, + }); + } + InterfoldEventData::RewardClaimed(event) => { + if let Some(reward) = state.rewards.iter_mut().rev().find(|reward| { + normalize_address(&reward.account) == normalize_address(&event.account) + && reward.token.as_deref() == Some(event.token.as_str()) + && reward.amount == event.amount + }) { + reward.claimed = true; + } + } + _ => {} + } + state.events.push(view.clone()); + } +} + +fn project_event(event: &InterfoldEvent, local_address: &str) -> EventView { + let timestamp = HlcTimestamp::from(event.ts()); + let source = match event.source() { + EventSource::Local => "local", + EventSource::Net => "network", + EventSource::Evm => "evm", + }; + let producer = match (event.source(), event.get_data()) { + (EventSource::Local, _) => local_address.to_owned(), + (EventSource::Evm, InterfoldEventData::EvmLogObserved(observed)) => { + observed.contract.clone() + } + (EventSource::Evm, _) => "on-chain contract".to_owned(), + (EventSource::Net, _) => format!("node:{:08x}", timestamp.node), + }; + let event_type = match event.get_data() { + InterfoldEventData::EvmLogObserved(observed) => { + format!("{}::{}", observed.contract, observed.event_name) + } + _ => event.event_type(), + }; + EventView { + seq: event.clone().seq(), + aggregate_id: event.aggregate_id().to_usize(), + timestamp_us: timestamp.ts, + logical_counter: timestamp.counter, + producer_fingerprint: format!("{:08x}", timestamp.node), + block: event.block(), + source: source.to_owned(), + producer, + event_type, + e3_id: event.get_e3_id().map(|id| id.to_string()), + phase: phase(event.get_data()), + severity: severity(event.get_data()), + event_id: event.id().to_string(), + causation_id: event.causation_id().to_string(), + origin_id: event.origin_id().to_string(), + payload: compact_payload(event.get_data()), + } +} + +fn phase(data: &InterfoldEventData) -> Option { + use E3Phase as P; + use InterfoldEventData as E; + match data { + E::E3Requested(_) => Some(P::Request), + E::CommitteeRequested(_) + | E::CiphernodeSelected(_) + | E::TicketGenerated(_) + | E::TicketSubmitted(_) + | E::CommitteeFinalizeRequested(_) + | E::CommitteeFinalized(_) + | E::CommitteeFormationFailed(_) + | E::CommitteeMemberExpelled(_) + | E::CommitteeViabilityUpdated(_) => Some(P::Committee), + E::EncryptionKeyPending(_) + | E::EncryptionKeyCreated(_) + | E::EncryptionKeyReceived(_) + | E::EncryptionKeyCollectionFailed(_) => Some(P::DkgSetup), + E::ThresholdSharePending(_) + | E::ThresholdShareCreated(_) + | E::ThresholdShareCollectionFailed(_) + | E::DkgProofSigned(_) + | E::ShareVerificationDispatched(_) + | E::ShareVerificationComplete(_) + | E::DecryptionKeyShared(_) + | E::DKGInnerProofReady(_) + | E::DKGRecursiveAggregationComplete(_) + | E::CommitmentConsistencyCheckRequested(_) + | E::CommitmentConsistencyCheckComplete(_) + | E::CommitmentConsistencyViolation(_) => Some(P::DkgShares), + E::KeyshareCreated(_) + | E::PkGenerationProofSigned(_) + | E::PkAggregationProofPending(_) + | E::PkAggregationProofSigned(_) + | E::PublicKeyAggregated(_) + | E::CommitteePublished(_) => Some(P::KeyPublication), + E::InputPublished(_) + | E::ComputeRequest(_) + | E::ComputeResponse(_) + | E::ComputeRequestError(_) + | E::CiphertextOutputPublished(_) => Some(P::Computation), + E::DecryptionShareProofsPending(_) + | E::DecryptionShareProofSigned(_) + | E::ShareDecryptionProofPending(_) + | E::DecryptionshareCreated(_) + | E::AggregationProofPending(_) + | E::AggregationProofSigned(_) + | E::PlaintextAggregated(_) => Some(P::Decryption), + E::PlaintextOutputPublished(_) + | E::RewardsDistributed(_) + | E::RewardCredited(_) + | E::RewardClaimed(_) + | E::E3RequestComplete(_) + | E::CommitteeActivationChanged(_) => Some(P::Settlement), + E::E3StageChanged(event) => Some(match event.new_stage { + E3Stage::None | E3Stage::Requested => P::Request, + E3Stage::CommitteeFinalized => P::Committee, + E3Stage::KeyPublished => P::KeyPublication, + E3Stage::CiphertextReady => P::Computation, + E3Stage::Complete | E3Stage::Failed => P::Settlement, + }), + E::E3Failed(event) => Some(match event.failed_at_stage { + E3Stage::None | E3Stage::Requested => P::Request, + E3Stage::CommitteeFinalized => P::DkgShares, + E3Stage::KeyPublished => P::Computation, + E3Stage::CiphertextReady => P::Decryption, + E3Stage::Complete | E3Stage::Failed => P::Settlement, + }), + E::ProofVerificationFailed(event) => { + Some(proof_phase(format!("{:?}", event.proof_type).as_str())) + } + E::ProofVerificationPassed(event) => { + Some(proof_phase(format!("{:?}", event.proof_type).as_str())) + } + E::SignedProofFailed(event) => { + Some(proof_phase(format!("{:?}", event.proof_type).as_str())) + } + E::ProofFailureAccusation(_) + | E::AccusationVote(_) + | E::AccusationQuorumReached(_) + | E::SlashExecuted(_) => Some(P::DkgShares), + E::EvmLogObserved(event) => match event.event_name.as_str() { + "CommitteeFormed" | "CommitteeFinalized" => Some(P::Committee), + "TreasuryCredited" + | "TreasuryClaimed" + | "SlashedFundsEscrowed" + | "SlashedFundsEscrowedToRefund" + | "RoutingFailed" + | "E3FailureProcessed" + | "SlashProposed" => Some(P::Settlement), + _ => None, + }, + _ => None, + } +} + +fn proof_phase(proof: &str) -> E3Phase { + if proof.contains("C6") || proof.contains("C7") || proof.contains("Decryption") { + E3Phase::Decryption + } else if proof.contains("C5") || proof.contains("Pk") { + E3Phase::KeyPublication + } else { + E3Phase::DkgShares + } +} + +fn stage_phase(stage: &E3Stage) -> E3Phase { + match stage { + E3Stage::None | E3Stage::Requested => E3Phase::Request, + E3Stage::CommitteeFinalized => E3Phase::DkgShares, + E3Stage::KeyPublished => E3Phase::Computation, + E3Stage::CiphertextReady => E3Phase::Decryption, + E3Stage::Complete | E3Stage::Failed => E3Phase::Settlement, + } +} + +fn severity(data: &InterfoldEventData) -> EventSeverity { + use InterfoldEventData as E; + match data { + E::InterfoldError(_) + | E::E3Failed(_) + | E::CommitteeFormationFailed(_) + | E::ProofVerificationFailed(_) + | E::SignedProofFailed(_) + | E::ThresholdShareCollectionFailed(_) + | E::EncryptionKeyCollectionFailed(_) + | E::ComputeRequestError(_) + | E::CommitmentConsistencyViolation(_) => EventSeverity::Error, + E::ProofFailureAccusation(_) + | E::AccusationVote(_) + | E::AccusationQuorumReached(_) + | E::SlashExecuted(_) + | E::CommitteeMemberExpelled(_) => EventSeverity::Warn, + E::EvmLogObserved(event) if !event.known => EventSeverity::Warn, + _ if phase(data).is_some() => EventSeverity::Info, + _ => EventSeverity::Debug, + } +} + +fn compact_payload(data: &InterfoldEventData) -> Value { + let serialized = serde_json::to_value(data) + .unwrap_or_else(|error| json!({ "serialization_error": error.to_string() })); + let payload = match serialized { + Value::Object(mut object) if object.len() == 1 => object + .remove(&data.event_type()) + .unwrap_or(Value::Object(object)), + other => other, + }; + compact_value(payload) +} + +fn compact_value(value: Value) -> Value { + match value { + Value::Array(values) if values.len() > LARGE_ARRAY => { + let length = values.len(); + let preview = values + .into_iter() + .take(12) + .map(compact_value) + .collect::>(); + json!({ + "kind": "large_array", + "length": length, + "preview": preview, + "truncated": true, + }) + } + Value::Array(values) => Value::Array(values.into_iter().map(compact_value).collect()), + Value::Object(values) => Value::Object( + values + .into_iter() + .map(|(key, value)| (key, compact_value(value))) + .collect::>(), + ), + Value::String(value) if value.len() > STRING_PREVIEW => json!({ + "kind": "large_string", + "length": value.len(), + "preview": &value[..safe_boundary(&value, STRING_PREVIEW)], + "truncated": true, + }), + other => other, + } +} + +fn safe_boundary(value: &str, maximum: usize) -> usize { + let mut boundary = maximum.min(value.len()); + while !value.is_char_boundary(boundary) { + boundary -= 1; + } + boundary +} + +fn summary(state: &E3State) -> E3Summary { + E3Summary { + e3_id: state.e3_id.clone(), + chain_id: state.chain_id, + status: state.status.clone(), + current_phase: state.current_phase.unwrap_or(E3Phase::Request), + event_count: state.events.len(), + error_count: state + .events + .iter() + .filter(|event| event.severity == EventSeverity::Error) + .count(), + warning_count: state + .events + .iter() + .filter(|event| event.severity == EventSeverity::Warn) + .count(), + committee_size: state.committee.len(), + first_seen_us: state.first_seen_us, + last_seen_us: state.last_seen_us, + } +} + +fn trace(state: &E3State) -> E3Trace { + let current = state.current_phase.unwrap_or(E3Phase::Request); + let phases = E3Phase::ALL + .into_iter() + .map(|phase| { + let events: Vec<_> = state + .events + .iter() + .filter(|event| event.phase == Some(phase)) + .collect(); + let source_count = + |source: &str| events.iter().filter(|event| event.source == source).count(); + let phase_state = if state.status == "failed" { + let failed_phase = state.failed_phase.unwrap_or(current); + if phase == failed_phase { + "failed" + } else if phase < failed_phase { + "complete" + } else { + "pending" + } + } else if phase < current || (phase == current && state.status == "complete") { + "complete" + } else if phase == current { + "active" + } else { + "pending" + }; + PhaseView { + id: phase, + label: phase.label(), + state: phase_state, + event_count: events.len(), + sources: SourceCounts { + local: source_count("local"), + net: source_count("network"), + evm: source_count("evm"), + }, + errors: events + .iter() + .filter(|event| event.severity == EventSeverity::Error) + .count(), + warnings: events + .iter() + .filter(|event| event.severity == EventSeverity::Warn) + .count(), + } + }) + .collect(); + E3Trace { + summary: summary(state), + phases, + committee: state + .committee + .iter() + .enumerate() + .map(|(party_id, address)| CommitteeMemberView { + address: address.clone(), + party_id, + score: state.scores.get(party_id).cloned(), + expelled: state.expelled.contains(&normalize_address(address)), + }) + .collect(), + tickets: state.tickets.clone(), + rewards: state.rewards.clone(), + failure: state.failure.clone(), + events: state.events.clone(), + } +} + +fn normalize_address(value: &str) -> String { + value.to_ascii_lowercase() +} + +#[cfg(test)] +mod tests { + use super::*; + use e3_events::{ + EventConstructorWithTimestamp, RewardClaimed, RewardCredited, RewardsDistributed, + Unsequenced, + }; + + #[test] + fn compacts_large_payloads_at_unicode_boundaries() { + let value = Value::String("é".repeat(400)); + let compact = compact_value(value); + assert_eq!(compact["truncated"], true); + assert!(compact["preview"].as_str().is_some()); + } + + #[test] + fn phases_are_stable_and_ordered() { + assert_eq!(E3Phase::ALL[0], E3Phase::Request); + assert_eq!(E3Phase::ALL[7], E3Phase::Settlement); + assert!(E3Phase::DkgSetup < E3Phase::DkgShares); + } + + #[test] + fn failure_leaves_later_phases_pending() { + let state = E3State { + e3_id: "1:1".into(), + chain_id: 1, + status: "failed".into(), + current_phase: Some(E3Phase::Committee), + failed_phase: Some(E3Phase::Committee), + ..E3State::default() + }; + let trace = trace(&state); + assert_eq!(trace.phases[0].state, "complete"); + assert_eq!(trace.phases[1].state, "failed"); + assert_eq!(trace.phases[2].state, "pending"); + } + + #[test] + fn eventstore_replay_rebuilds_the_same_projection() { + let e3_id = e3_events::E3id::new("9", 31337); + let account = "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65"; + let token = "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512"; + let events = vec![ + replay_event( + RewardsDistributed { + e3_id: e3_id.clone(), + nodes: vec![account.into()], + amounts: vec!["42".into()], + } + .into(), + 1, + 10, + ), + replay_event( + RewardCredited { + e3_id: e3_id.clone(), + account: account.into(), + token: token.into(), + amount: "42".into(), + } + .into(), + 2, + 20, + ), + replay_event( + RewardClaimed { + e3_id, + account: account.into(), + token: token.into(), + amount: "42".into(), + } + .into(), + 3, + 30, + ), + ]; + + let mut live = TelemetryProjection::new(account); + let mut rebuilt = TelemetryProjection::new(account); + for event in &events { + live.apply(event.clone()); + } + for event in events { + rebuilt.apply(event); + } + + assert_eq!( + serde_json::to_value(live.overview()).unwrap(), + serde_json::to_value(rebuilt.overview()).unwrap() + ); + assert_eq!( + serde_json::to_value(live.summaries()).unwrap(), + serde_json::to_value(rebuilt.summaries()).unwrap() + ); + assert_eq!( + serde_json::to_value(live.trace("31337:9")).unwrap(), + serde_json::to_value(rebuilt.trace("31337:9")).unwrap() + ); + } + + fn replay_event(data: InterfoldEventData, seq: u64, timestamp: u128) -> InterfoldEvent { + InterfoldEvent::::new_with_timestamp( + data, + None, + timestamp, + Some(100 + seq), + EventSource::Evm, + ) + .into_sequenced(seq) + } +} diff --git a/crates/dashboard/src/updates.rs b/crates/dashboard/src/updates.rs new file mode 100644 index 0000000000..fcc5aef7f0 --- /dev/null +++ b/crates/dashboard/src/updates.rs @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Read-only release awareness for node operators. + +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + sync::Arc, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; +use tokio::sync::Mutex; + +const LATEST_RELEASE: &str = "https://api.github.com/repos/gnosisguild/interfold/releases/latest"; +const RELEASES_PAGE: &str = "https://github.com/gnosisguild/interfold/releases"; +const CACHE_TTL: Duration = Duration::from_secs(60 * 60); +const ERROR_CACHE_TTL: Duration = Duration::from_secs(5 * 60); + +#[derive(Clone, Debug, Serialize)] +pub(crate) struct ReleaseInfo { + tag: String, + name: String, + url: String, + published_at: Option, + notes: String, +} + +#[derive(Clone, Debug, Serialize)] +pub(crate) struct UpdateSnapshot { + current_version: String, + latest: Option, + update_available: bool, + releases_url: &'static str, + checked_at_ms: u64, + #[serde(skip_serializing_if = "Option::is_none")] + error: Option, +} + +#[derive(Debug, Deserialize)] +struct GitHubRelease { + tag_name: String, + name: Option, + html_url: String, + published_at: Option, + body: Option, +} + +#[derive(Default)] +struct Cache { + refreshed_at: Option, + snapshot: Option, +} + +#[derive(Clone)] +pub(crate) struct UpdateService { + client: reqwest::Client, + current_version: String, + cache: Arc>, +} + +impl UpdateService { + pub(crate) fn new(current_version: impl Into) -> Self { + Self { + client: reqwest::Client::new(), + current_version: current_version.into(), + cache: Arc::new(Mutex::new(Cache::default())), + } + } + + pub(crate) async fn snapshot(&self) -> UpdateSnapshot { + let mut cache = self.cache.lock().await; + let ttl = if cache + .snapshot + .as_ref() + .is_some_and(|snapshot| snapshot.error.is_some()) + { + ERROR_CACHE_TTL + } else { + CACHE_TTL + }; + if cache + .refreshed_at + .is_some_and(|refreshed| refreshed.elapsed() < ttl) + { + if let Some(snapshot) = &cache.snapshot { + return snapshot.clone(); + } + } + + let fetched = tokio::time::timeout(Duration::from_secs(8), self.fetch()).await; + let snapshot = match fetched { + Ok(Ok(release)) => UpdateSnapshot { + update_available: compare_versions(&release.tag, &self.current_version) + == Ordering::Greater, + current_version: self.current_version.clone(), + latest: Some(release), + releases_url: RELEASES_PAGE, + checked_at_ms: now_ms(), + error: None, + }, + Ok(Err(error)) => UpdateSnapshot { + current_version: self.current_version.clone(), + latest: cache + .snapshot + .as_ref() + .and_then(|snapshot| snapshot.latest.clone()), + update_available: cache + .snapshot + .as_ref() + .is_some_and(|snapshot| snapshot.update_available), + releases_url: RELEASES_PAGE, + checked_at_ms: now_ms(), + error: Some(error.to_string()), + }, + Err(_) => UpdateSnapshot { + current_version: self.current_version.clone(), + latest: cache + .snapshot + .as_ref() + .and_then(|snapshot| snapshot.latest.clone()), + update_available: cache + .snapshot + .as_ref() + .is_some_and(|snapshot| snapshot.update_available), + releases_url: RELEASES_PAGE, + checked_at_ms: now_ms(), + error: Some("release check timed out".into()), + }, + }; + cache.refreshed_at = Some(Instant::now()); + cache.snapshot = Some(snapshot.clone()); + snapshot + } + + async fn fetch(&self) -> anyhow::Result { + let release = self + .client + .get(LATEST_RELEASE) + .header( + reqwest::header::USER_AGENT, + "interfold-ciphernode-dashboard", + ) + .header(reqwest::header::ACCEPT, "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .send() + .await? + .error_for_status()? + .json::() + .await?; + let notes = release.body.unwrap_or_default(); + Ok(ReleaseInfo { + name: release + .name + .filter(|name| !name.trim().is_empty()) + .unwrap_or_else(|| release.tag_name.clone()), + tag: release.tag_name, + url: release.html_url, + published_at: release.published_at, + notes: notes.chars().take(8_000).collect(), + }) + } +} + +fn compare_versions(left: &str, right: &str) -> Ordering { + version_parts(left).cmp(&version_parts(right)) +} + +fn version_parts(value: &str) -> [u64; 3] { + let mut result = [0; 3]; + for (index, part) in value + .trim_start_matches('v') + .split(['.', '-']) + .take(3) + .enumerate() + { + result[index] = part.parse().unwrap_or(0); + } + result +} + +fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compares_release_tags_without_extra_dependencies() { + assert_eq!(compare_versions("v0.3.0", "0.2.2"), Ordering::Greater); + assert_eq!(compare_versions("v0.2.2", "0.2.2"), Ordering::Equal); + assert_eq!(compare_versions("v0.2.1", "0.2.2"), Ordering::Less); + } +} diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index 5a313ebc24..15499c2099 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -32,6 +32,8 @@ pub async fn execute(config: &AppConfig) -> Result { ); let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) + .with_name(&config.name()) + .with_logging() .with_persistence(&config.log_file(), &config.db_file()) .with_sortition_score() .with_chains(config.chains()) diff --git a/crates/events/src/eventbus.rs b/crates/events/src/eventbus.rs index 1366d65da0..a961b45b49 100644 --- a/crates/events/src/eventbus.rs +++ b/crates/events/src/eventbus.rs @@ -8,7 +8,7 @@ use crate::traits::{ErrorEvent, Event}; use crate::EventType; use actix::prelude::*; use bloom::{BloomFilter, ASMS}; -use e3_utils::{colorize, Color, MAILBOX_LIMIT, MAILBOX_LIMIT_LARGE}; +use e3_utils::{MAILBOX_LIMIT, MAILBOX_LIMIT_LARGE}; use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; @@ -136,8 +136,12 @@ impl Handler for EventBus { } } - // TODO: workshop to work out best display format - tracing::info!("{} {}", colorize(">>>", Color::Yellow), event); + // The structured protocol logger owns operator-visible event output. + // Keep the bus dispatch record payload-free and opt-in for developers. + tracing::trace!( + event_type = %event.event_type(), + "event dispatched" + ); self.track(event); } } diff --git a/crates/events/src/interfold_event/ciphernode_deregistration_requested.rs b/crates/events/src/interfold_event/ciphernode_deregistration_requested.rs new file mode 100644 index 0000000000..46f7f2e141 --- /dev/null +++ b/crates/events/src/interfold_event/ciphernode_deregistration_requested.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CiphernodeDeregistrationRequested { + pub operator: String, + pub unlock_at: u64, + pub chain_id: u64, +} + +impl Display for CiphernodeDeregistrationRequested { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CiphernodeDeregistrationRequested {{ operator: {}, unlock_at: {}, chain_id: {} }}", + self.operator, self.unlock_at, self.chain_id + ) + } +} diff --git a/crates/events/src/interfold_event/committee_activation_changed.rs b/crates/events/src/interfold_event/committee_activation_changed.rs new file mode 100644 index 0000000000..8dbc8b6bbb --- /dev/null +++ b/crates/events/src/interfold_event/committee_activation_changed.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CommitteeActivationChanged { + pub e3_id: E3id, + pub active: bool, +} + +impl Display for CommitteeActivationChanged { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CommitteeActivationChanged {{ e3_id: {}, active: {} }}", + self.e3_id, self.active + ) + } +} diff --git a/crates/events/src/interfold_event/committee_formation_failed.rs b/crates/events/src/interfold_event/committee_formation_failed.rs new file mode 100644 index 0000000000..3ae3f9e9ed --- /dev/null +++ b/crates/events/src/interfold_event/committee_formation_failed.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CommitteeFormationFailed { + pub e3_id: E3id, + pub nodes_submitted: String, + pub threshold_required: String, +} + +impl Display for CommitteeFormationFailed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CommitteeFormationFailed {{ e3_id: {}, submitted: {}, required: {} }}", + self.e3_id, self.nodes_submitted, self.threshold_required + ) + } +} diff --git a/crates/events/src/interfold_event/committee_viability_updated.rs b/crates/events/src/interfold_event/committee_viability_updated.rs new file mode 100644 index 0000000000..e90ebafec2 --- /dev/null +++ b/crates/events/src/interfold_event/committee_viability_updated.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct CommitteeViabilityUpdated { + pub e3_id: E3id, + pub active_count: String, + pub threshold_m: String, + pub viable: bool, +} + +impl Display for CommitteeViabilityUpdated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CommitteeViabilityUpdated {{ e3_id: {}, active_count: {}, threshold_m: {}, viable: {} }}", + self.e3_id, self.active_count, self.threshold_m, self.viable + ) + } +} diff --git a/crates/events/src/interfold_event/evm_log_observed.rs b/crates/events/src/interfold_event/evm_log_observed.rs new file mode 100644 index 0000000000..f288511844 --- /dev/null +++ b/crates/events/src/interfold_event/evm_log_observed.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use actix::Message; +use e3_utils::ArcBytes; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +use crate::E3id; + +/// Lossless observation of a watched contract log that has no protocol-driving +/// typed event. +/// +/// Current ABI events are named through the EVM event catalog. `known` is false +/// only when the deployed contract emits a signature absent from this binary. +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct EvmLogObserved { + pub contract: String, + pub chain_id: u64, + pub e3_id: Option, + pub event_name: String, + pub signature: Option, + pub known: bool, + pub topics: Vec, + pub data: ArcBytes, +} + +impl Display for EvmLogObserved { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "EvmLogObserved {{ contract: {}, event: {}, chain_id: {}, known: {}, data_len: {} }}", + self.contract, + self.event_name, + self.chain_id, + self.known, + self.data.len() + ) + } +} diff --git a/crates/events/src/interfold_event/input_published.rs b/crates/events/src/interfold_event/input_published.rs new file mode 100644 index 0000000000..fe1b3403e9 --- /dev/null +++ b/crates/events/src/interfold_event/input_published.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use e3_utils::ArcBytes; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct InputPublished { + pub e3_id: E3id, + pub data: ArcBytes, + pub input_hash: String, + pub index: String, +} + +impl Display for InputPublished { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "InputPublished {{ e3_id: {}, index: {}, data_len: {} }}", + self.e3_id, + self.index, + self.data.len() + ) + } +} diff --git a/crates/events/src/interfold_event/license_bond_updated.rs b/crates/events/src/interfold_event/license_bond_updated.rs new file mode 100644 index 0000000000..ad171ae461 --- /dev/null +++ b/crates/events/src/interfold_event/license_bond_updated.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use actix::Message; +use alloy::primitives::{I256, U256}; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct LicenseBondUpdated { + pub operator: String, + pub delta: I256, + pub new_bond: U256, + pub reason: [u8; 32], + pub chain_id: u64, +} + +impl Display for LicenseBondUpdated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "LicenseBondUpdated {{ operator: {}, new_bond: {}, chain_id: {} }}", + self.operator, self.new_bond, self.chain_id + ) + } +} diff --git a/crates/events/src/interfold_event/mod.rs b/crates/events/src/interfold_event/mod.rs index 98ea37414c..e0a08943c0 100644 --- a/crates/events/src/interfold_event/mod.rs +++ b/crates/events/src/interfold_event/mod.rs @@ -10,14 +10,18 @@ mod aggregation_proof_pending; mod aggregation_proof_signed; mod aggregator_changed; mod ciphernode_added; +mod ciphernode_deregistration_requested; mod ciphernode_removed; mod ciphernode_selected; mod ciphertext_output_published; mod commitment_consistency; +mod committee_activation_changed; mod committee_finalize_requested; mod committee_finalized; +mod committee_formation_failed; mod committee_published; mod committee_requested; +mod committee_viability_updated; mod compute_request; mod configuration_updated; mod decryption_key_shared; @@ -37,8 +41,11 @@ mod encryption_key_collection_failed; mod encryption_key_created; mod encryption_key_pending; mod encryption_key_received; +mod evm_log_observed; +mod input_published; mod interfold_error; mod keyshare_created; +mod license_bond_updated; mod net_ready; mod operator_activation_changed; mod outgoing_sync_requested; @@ -53,6 +60,9 @@ mod proof_verification_failed; mod proof_verification_passed; mod publickey_aggregated; mod publish_document; +mod reward_claimed; +mod reward_credited; +mod rewards_distributed; mod share_computation_proof_signed; mod share_decryption_proof_pending; mod share_verification; @@ -77,14 +87,18 @@ pub use aggregation_proof_pending::*; pub use aggregation_proof_signed::*; pub use aggregator_changed::*; pub use ciphernode_added::*; +pub use ciphernode_deregistration_requested::*; pub use ciphernode_removed::*; pub use ciphernode_selected::*; pub use ciphertext_output_published::*; pub use commitment_consistency::*; +pub use committee_activation_changed::*; pub use committee_finalize_requested::*; pub use committee_finalized::*; +pub use committee_formation_failed::*; pub use committee_published::*; pub use committee_requested::*; +pub use committee_viability_updated::*; pub use compute_request::*; pub use configuration_updated::*; pub use decryption_key_shared::*; @@ -105,8 +119,11 @@ pub use encryption_key_collection_failed::*; pub use encryption_key_created::*; pub use encryption_key_pending::*; pub use encryption_key_received::*; +pub use evm_log_observed::*; +pub use input_published::*; pub use interfold_error::*; pub use keyshare_created::*; +pub use license_bond_updated::*; pub use net_ready::*; pub use operator_activation_changed::*; pub use outgoing_sync_requested::*; @@ -121,6 +138,9 @@ pub use proof_verification_failed::*; pub use proof_verification_passed::*; pub use publickey_aggregated::*; pub use publish_document::*; +pub use reward_claimed::*; +pub use reward_credited::*; +pub use rewards_distributed::*; pub use share_computation_proof_signed::*; pub use share_decryption_proof_pending::*; pub use share_verification::*; @@ -309,6 +329,16 @@ pub enum InterfoldEventData { CommitmentConsistencyViolation(CommitmentConsistencyViolation), /// This is a test event to use in testing TestEvent(TestEvent), + InputPublished(InputPublished), + RewardsDistributed(RewardsDistributed), + RewardCredited(RewardCredited), + RewardClaimed(RewardClaimed), + LicenseBondUpdated(LicenseBondUpdated), + CiphernodeDeregistrationRequested(CiphernodeDeregistrationRequested), + CommitteeFormationFailed(CommitteeFormationFailed), + CommitteeActivationChanged(CommitteeActivationChanged), + CommitteeViabilityUpdated(CommitteeViabilityUpdated), + EvmLogObserved(EvmLogObserved), } impl InterfoldEventData { @@ -601,6 +631,14 @@ impl InterfoldEventData { InterfoldEventData::CommitmentConsistencyViolation(ref data) => { Some(data.e3_id.clone()) } + InterfoldEventData::InputPublished(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::RewardsDistributed(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::RewardCredited(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::RewardClaimed(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::CommitteeFormationFailed(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::CommitteeActivationChanged(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::CommitteeViabilityUpdated(ref data) => Some(data.e3_id.clone()), + InterfoldEventData::EvmLogObserved(ref data) => data.e3_id.clone(), _ => None, } } @@ -608,6 +646,9 @@ impl InterfoldEventData { impl WithAggregateId for InterfoldEventData { fn get_aggregate_id(&self) -> AggregateId { + if let InterfoldEventData::EvmLogObserved(event) = self { + return AggregateId::from_chain_id(Some(event.chain_id)); + } let chain_id = self.get_e3_id().map(|e3_id| e3_id.chain_id()); AggregateId::from_chain_id(chain_id) } @@ -696,7 +737,17 @@ impl_event_types!( DKGRecursiveAggregationComplete, CommitmentConsistencyCheckRequested, CommitmentConsistencyCheckComplete, - CommitmentConsistencyViolation + CommitmentConsistencyViolation, + InputPublished, + RewardsDistributed, + RewardCredited, + RewardClaimed, + LicenseBondUpdated, + CiphernodeDeregistrationRequested, + CommitteeFormationFailed, + CommitteeActivationChanged, + CommitteeViabilityUpdated, + EvmLogObserved ); impl TryFrom<&InterfoldEvent> for InterfoldError { diff --git a/crates/events/src/interfold_event/reward_claimed.rs b/crates/events/src/interfold_event/reward_claimed.rs new file mode 100644 index 0000000000..5149df7d7b --- /dev/null +++ b/crates/events/src/interfold_event/reward_claimed.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct RewardClaimed { + pub e3_id: E3id, + pub account: String, + pub token: String, + pub amount: String, +} + +impl Display for RewardClaimed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RewardClaimed {{ e3_id: {}, account: {}, amount: {} }}", + self.e3_id, self.account, self.amount + ) + } +} diff --git a/crates/events/src/interfold_event/reward_credited.rs b/crates/events/src/interfold_event/reward_credited.rs new file mode 100644 index 0000000000..69f6a4e3c8 --- /dev/null +++ b/crates/events/src/interfold_event/reward_credited.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct RewardCredited { + pub e3_id: E3id, + pub account: String, + pub token: String, + pub amount: String, +} + +impl Display for RewardCredited { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RewardCredited {{ e3_id: {}, account: {}, amount: {} }}", + self.e3_id, self.account, self.amount + ) + } +} diff --git a/crates/events/src/interfold_event/rewards_distributed.rs b/crates/events/src/interfold_event/rewards_distributed.rs new file mode 100644 index 0000000000..c03b94b774 --- /dev/null +++ b/crates/events/src/interfold_event/rewards_distributed.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct RewardsDistributed { + pub e3_id: E3id, + pub nodes: Vec, + pub amounts: Vec, +} + +impl Display for RewardsDistributed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RewardsDistributed {{ e3_id: {}, recipients: {} }}", + self.e3_id, + self.nodes.len() + ) + } +} diff --git a/crates/evm/src/contracts.rs b/crates/evm/src/contracts.rs index 3ed2295612..5830c3b8e0 100644 --- a/crates/evm/src/contracts.rs +++ b/crates/evm/src/contracts.rs @@ -53,8 +53,13 @@ sol! { // ── Events ────────────────────────────────────────────────────────── event E3Requested(uint256 e3Id, E3 e3, address indexed e3Program); + event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); - event E3Failed(uint256 e3Id, uint8 failedAtStage, uint8 reason); + event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput, bytes proof); + event RewardsDistributed(uint256 indexed e3Id, address[] nodes, uint256[] amounts); + event RewardCredited(uint256 indexed e3Id, address indexed account, address indexed token, uint256 amount); + event RewardClaimed(uint256 indexed e3Id, address indexed account, address indexed token, uint256 amount); + event E3Failed(uint256 indexed e3Id, uint8 failedAtStage, uint8 reason); event E3StageChanged(uint256 indexed e3Id, uint8 previousStage, uint8 newStage); // ── Errors (only those our called functions can revert with) ──────── @@ -93,12 +98,14 @@ sol! { // ── Events ────────────────────────────────────────────────────────── event SlashExecuted( - uint256 proposalId, + uint256 indexed proposalId, uint256 e3Id, - address operator, - bytes32 reason, + address indexed operator, + bytes32 indexed reason, uint256 ticketAmount, - uint256 licenseAmount + uint256 licenseAmount, + bool executed, + uint8 lane ); // ── Errors (only those our called functions can revert with) ──────── @@ -163,6 +170,8 @@ sol! { function accusationVoteValidity() external view returns (uint256); + function numCiphernodes() external view returns (uint256); + // ── Events ────────────────────────────────────────────────────────── event CiphernodeAdded( address indexed node, @@ -192,6 +201,29 @@ sol! { uint256[] scores ); + event CommitteeFormationFailed( + uint256 indexed e3Id, + uint256 nodesSubmitted, + uint256 thresholdRequired + ); + + event CommitteePublished( + uint256 indexed e3Id, + address[] nodes, + bytes publicKey, + bytes32 pkCommitment, + bytes proof + ); + + event CommitteeActivationChanged(uint256 indexed e3Id, bool active); + + event CommitteeViabilityUpdated( + uint256 indexed e3Id, + uint256 activeCount, + uint256 thresholdM, + bool viable + ); + event TicketSubmitted( uint256 indexed e3Id, address indexed node, @@ -231,6 +263,14 @@ sol! { #[sol(rpc)] #[derive(Debug)] interface IBondingRegistry { + function getTicketBalance(address operator) external view returns (uint256); + function getLicenseBond(address operator) external view returns (uint256); + function availableTickets(address operator) external view returns (uint256); + function isRegistered(address operator) external view returns (bool); + function isActive(address operator) external view returns (bool); + function numActiveOperators() external view returns (uint256); + function hasExitInProgress(address operator) external view returns (bool); + event TicketBalanceUpdated( address indexed operator, int256 delta, @@ -238,6 +278,15 @@ sol! { bytes32 indexed reason ); + event LicenseBondUpdated( + address indexed operator, + int256 delta, + uint256 newBond, + bytes32 indexed reason + ); + + event CiphernodeDeregistrationRequested(address indexed operator, uint64 unlockAt); + event OperatorActivationChanged(address indexed operator, bool active); event ConfigurationUpdated( diff --git a/crates/evm/src/domain/bonding_registry_events.rs b/crates/evm/src/domain/bonding_registry_events.rs index 30b8288009..d00aba8a58 100644 --- a/crates/evm/src/domain/bonding_registry_events.rs +++ b/crates/evm/src/domain/bonding_registry_events.rs @@ -11,7 +11,7 @@ use alloy::{ primitives::{LogData, B256}, sol_types::SolEvent, }; -use e3_events::InterfoldEventData; +use e3_events::{CiphernodeDeregistrationRequested, InterfoldEventData, LicenseBondUpdated}; use tracing::{error, trace}; struct TicketBalanceUpdatedWithChainId(pub IBondingRegistry::TicketBalanceUpdated, pub u64); @@ -87,6 +87,47 @@ impl From for InterfoldEventData { } } +struct LicenseBondUpdatedWithChainId(pub IBondingRegistry::LicenseBondUpdated, pub u64); + +impl From for LicenseBondUpdated { + fn from(value: LicenseBondUpdatedWithChainId) -> Self { + Self { + operator: value.0.operator.to_string(), + delta: value.0.delta, + new_bond: value.0.newBond, + reason: value.0.reason.into(), + chain_id: value.1, + } + } +} + +impl From for InterfoldEventData { + fn from(value: LicenseBondUpdatedWithChainId) -> Self { + LicenseBondUpdated::from(value).into() + } +} + +struct CiphernodeDeregistrationRequestedWithChainId( + pub IBondingRegistry::CiphernodeDeregistrationRequested, + pub u64, +); + +impl From for CiphernodeDeregistrationRequested { + fn from(value: CiphernodeDeregistrationRequestedWithChainId) -> Self { + Self { + operator: value.0.operator.to_string(), + unlock_at: value.0.unlockAt, + chain_id: value.1, + } + } +} + +impl From for InterfoldEventData { + fn from(value: CiphernodeDeregistrationRequestedWithChainId) -> Self { + CiphernodeDeregistrationRequested::from(value).into() + } +} + pub(crate) fn extractor( data: &LogData, topics: &[B256], @@ -112,6 +153,22 @@ pub(crate) fn extractor( OperatorActivationChangedWithChainId(event, chain_id), )) } + Some(&IBondingRegistry::LicenseBondUpdated::SIGNATURE_HASH) => { + let Ok(event) = IBondingRegistry::LicenseBondUpdated::decode_log_data(data) else { + error!("Error parsing event LicenseBondUpdated after topic matched!"); + return None; + }; + Some(LicenseBondUpdatedWithChainId(event, chain_id).into()) + } + Some(&IBondingRegistry::CiphernodeDeregistrationRequested::SIGNATURE_HASH) => { + let Ok(event) = + IBondingRegistry::CiphernodeDeregistrationRequested::decode_log_data(data) + else { + error!("Error parsing CiphernodeDeregistrationRequested after topic matched!"); + return None; + }; + Some(CiphernodeDeregistrationRequestedWithChainId(event, chain_id).into()) + } Some(&IBondingRegistry::ConfigurationUpdated::SIGNATURE_HASH) => { let Ok(event) = IBondingRegistry::ConfigurationUpdated::decode_log_data(data) else { error!("Error parsing event ConfigurationUpdated after topic was matched!"); @@ -124,9 +181,14 @@ pub(crate) fn extractor( _ => { trace!( topic=?topics.first(), - "Unknown event was received by BondingRegistry.sol parser but was ignored" + "Preserving event without a typed BondingRegistry decoder" ); - None + Some(crate::domain::evm_log_observation::observe( + "BondingRegistry", + data, + topics, + chain_id, + )) } } } @@ -173,8 +235,11 @@ mod tests { } #[test] - fn test_extractor_ignores_unknown_topic() { + fn test_extractor_preserves_unknown_topic() { let log_data = LogData::default(); - assert!(extractor(&log_data, &[B256::ZERO], 1).is_none()); + assert!(matches!( + extractor(&log_data, &[B256::ZERO], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); } } diff --git a/crates/evm/src/domain/ciphernode_registry_events.rs b/crates/evm/src/domain/ciphernode_registry_events.rs index 7e2152e770..aa49d2e956 100644 --- a/crates/evm/src/domain/ciphernode_registry_events.rs +++ b/crates/evm/src/domain/ciphernode_registry_events.rs @@ -11,7 +11,11 @@ use alloy::{ primitives::{LogData, B256}, sol_types::SolEvent, }; -use e3_events::{CommitteeFinalized, E3id, InterfoldEventData, Seed}; +use e3_events::{ + CommitteeActivationChanged, CommitteeFinalized, CommitteeFormationFailed, CommitteePublished, + CommitteeViabilityUpdated, E3id, InterfoldEventData, Seed, +}; +use e3_utils::ArcBytes; use tracing::{error, info, trace}; struct CiphernodeAddedWithChainId(pub ICiphernodeRegistry::CiphernodeAdded, pub u64); @@ -168,6 +172,89 @@ impl From for InterfoldEventData { } } +struct CommitteePublishedWithChainId(pub ICiphernodeRegistry::CommitteePublished, pub u64); + +impl From for CommitteePublished { + fn from(value: CommitteePublishedWithChainId) -> Self { + CommitteePublished { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + nodes: value.0.nodes.iter().map(|a| a.to_string()).collect(), + public_key: ArcBytes::from_bytes(value.0.publicKey.as_ref()), + proof: ArcBytes::from_bytes(value.0.proof.as_ref()), + } + } +} + +impl From for InterfoldEventData { + fn from(value: CommitteePublishedWithChainId) -> Self { + let payload: CommitteePublished = value.into(); + InterfoldEventData::from(payload) + } +} + +struct CommitteeFormationFailedWithChainId( + pub ICiphernodeRegistry::CommitteeFormationFailed, + pub u64, +); + +impl From for CommitteeFormationFailed { + fn from(value: CommitteeFormationFailedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + nodes_submitted: value.0.nodesSubmitted.to_string(), + threshold_required: value.0.thresholdRequired.to_string(), + } + } +} + +impl From for InterfoldEventData { + fn from(value: CommitteeFormationFailedWithChainId) -> Self { + CommitteeFormationFailed::from(value).into() + } +} + +struct CommitteeActivationChangedWithChainId( + pub ICiphernodeRegistry::CommitteeActivationChanged, + pub u64, +); + +impl From for CommitteeActivationChanged { + fn from(value: CommitteeActivationChangedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + active: value.0.active, + } + } +} + +impl From for InterfoldEventData { + fn from(value: CommitteeActivationChangedWithChainId) -> Self { + CommitteeActivationChanged::from(value).into() + } +} + +struct CommitteeViabilityUpdatedWithChainId( + pub ICiphernodeRegistry::CommitteeViabilityUpdated, + pub u64, +); + +impl From for CommitteeViabilityUpdated { + fn from(value: CommitteeViabilityUpdatedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + active_count: value.0.activeCount.to_string(), + threshold_m: value.0.thresholdM.to_string(), + viable: value.0.viable, + } + } +} + +impl From for InterfoldEventData { + fn from(value: CommitteeViabilityUpdatedWithChainId) -> Self { + CommitteeViabilityUpdated::from(value).into() + } +} + pub(crate) fn extractor( data: &LogData, topics: &[B256], @@ -211,6 +298,18 @@ pub(crate) fn extractor( event, chain_id, ))) } + Some(&ICiphernodeRegistry::CommitteeFormationFailed::SIGNATURE_HASH) => { + let Ok(mut event) = + ICiphernodeRegistry::CommitteeFormationFailed::decode_log_data(data) + else { + error!("Error parsing event CommitteeFormationFailed after topic matched!"); + return None; + }; + event.e3Id = topics + .get(1) + .map(|topic| alloy::primitives::U256::from_be_bytes(topic.0))?; + Some(CommitteeFormationFailedWithChainId(event, chain_id).into()) + } Some(&ICiphernodeRegistry::TicketSubmitted::SIGNATURE_HASH) => { let Ok(event) = ICiphernodeRegistry::TicketSubmitted::decode_log_data(data) else { error!("Error parsing event TicketSubmitted after topic was matched!"); @@ -234,12 +333,62 @@ pub(crate) fn extractor( CommitteeMemberExpelledWithChainId(event, chain_id), )) } + Some(&ICiphernodeRegistry::CommitteePublished::SIGNATURE_HASH) => { + let Ok(mut event) = ICiphernodeRegistry::CommitteePublished::decode_log_data(data) + else { + error!("Error parsing event CommitteePublished after topic was matched!"); + return None; + }; + // e3Id is indexed → extract from topics[1], not log data + if let Some(e3_id_topic) = topics.get(1) { + event.e3Id = alloy::primitives::U256::from_be_bytes(e3_id_topic.0); + } else { + error!("CommitteePublished missing indexed e3Id in topics!"); + return None; + } + info!( + "CommitteePublished event received: e3_id={}, nodes={:?}", + event.e3Id, event.nodes + ); + Some(InterfoldEventData::from(CommitteePublishedWithChainId( + event, chain_id, + ))) + } + Some(&ICiphernodeRegistry::CommitteeActivationChanged::SIGNATURE_HASH) => { + let Ok(mut event) = + ICiphernodeRegistry::CommitteeActivationChanged::decode_log_data(data) + else { + error!("Error parsing event CommitteeActivationChanged after topic matched!"); + return None; + }; + event.e3Id = topics + .get(1) + .map(|topic| alloy::primitives::U256::from_be_bytes(topic.0))?; + Some(CommitteeActivationChangedWithChainId(event, chain_id).into()) + } + Some(&ICiphernodeRegistry::CommitteeViabilityUpdated::SIGNATURE_HASH) => { + let Ok(mut event) = + ICiphernodeRegistry::CommitteeViabilityUpdated::decode_log_data(data) + else { + error!("Error parsing event CommitteeViabilityUpdated after topic matched!"); + return None; + }; + event.e3Id = topics + .get(1) + .map(|topic| alloy::primitives::U256::from_be_bytes(topic.0))?; + Some(CommitteeViabilityUpdatedWithChainId(event, chain_id).into()) + } _ => { trace!( topic=?topics.first(), - "Unknown event was received by CiphernodeRegistry.sol parser but was ignored" + "Preserving event without a typed CiphernodeRegistry decoder" ); - None + Some(crate::domain::evm_log_observation::observe( + "CiphernodeRegistry", + data, + topics, + chain_id, + )) } } } @@ -247,7 +396,7 @@ pub(crate) fn extractor( #[cfg(test)] mod tests { use super::*; - use alloy::primitives::{Address, U256}; + use alloy::primitives::{Address, Bytes, U256}; #[test] fn test_extractor_decodes_ciphernode_added() { @@ -292,8 +441,33 @@ mod tests { } #[test] - fn test_extractor_ignores_unknown_topic() { + fn test_extractor_preserves_unknown_topic() { let log_data = LogData::default(); - assert!(extractor(&log_data, &[B256::ZERO], 1).is_none()); + assert!(matches!( + extractor(&log_data, &[B256::ZERO], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); + } + + #[test] + fn test_extractor_decodes_committee_publication() { + let node = Address::repeat_byte(0x33); + let event = ICiphernodeRegistry::CommitteePublished { + e3Id: U256::from(12), + nodes: vec![node], + publicKey: Bytes::from_static(b"public-key"), + pkCommitment: B256::repeat_byte(0x44), + proof: Bytes::from_static(b"proof"), + }; + let log = event.encode_log_data(); + let out = extractor(&log, log.topics(), 100); + match out { + Some(InterfoldEventData::CommitteePublished(event)) => { + assert_eq!(event.e3_id, E3id::new("12", 100)); + assert_eq!(event.nodes, vec![node.to_string()]); + assert_eq!(event.public_key.extract_bytes(), b"public-key"); + } + other => panic!("expected CommitteePublished, got {other:?}"), + } } } diff --git a/crates/evm/src/domain/evm_event_catalog.rs b/crates/evm/src/domain/evm_event_catalog.rs new file mode 100644 index 0000000000..6c8c284736 --- /dev/null +++ b/crates/evm/src/domain/evm_event_catalog.rs @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Signatures emitted by the four contracts watched by the ciphernode. +//! +//! Protocol-driving events still have dedicated typed decoders. This catalog +//! names every other event in the current implementation ABIs so raw audit +//! records remain understandable without creating dozens of actor messages. + +use alloy::primitives::{keccak256, B256}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct EvmEventDefinition { + pub name: &'static str, + pub signature: &'static str, + /// Position in the complete topics array (`topic0` is the signature). + pub e3_id_topic: Option, +} + +impl EvmEventDefinition { + const fn new(name: &'static str, signature: &'static str, e3_id_topic: Option) -> Self { + Self { + name, + signature, + e3_id_topic, + } + } +} + +pub(crate) fn find(contract: &str, topic0: B256) -> Option<&'static EvmEventDefinition> { + catalog(contract) + .iter() + .find(|event| keccak256(event.signature.as_bytes()) == topic0) +} + +fn catalog(contract: &str) -> &'static [EvmEventDefinition] { + match contract { + "Interfold" => INTERFOLD, + "BondingRegistry" => BONDING_REGISTRY, + "CiphernodeRegistry" => CIPHERNODE_REGISTRY, + "SlashingManager" => SLASHING_MANAGER, + _ => &[], + } +} + +const INTERFOLD: &[EvmEventDefinition] = &[ + EvmEventDefinition::new("BondingRegistrySet", "BondingRegistrySet(address)", None), + EvmEventDefinition::new("CiphernodeRegistrySet", "CiphernodeRegistrySet(address)", None), + EvmEventDefinition::new( + "CiphertextOutputPublished", + "CiphertextOutputPublished(uint256,bytes)", + Some(1), + ), + EvmEventDefinition::new("CommitteeFinalized", "CommitteeFinalized(uint256)", Some(1)), + EvmEventDefinition::new("CommitteeFormed", "CommitteeFormed(uint256)", Some(1)), + EvmEventDefinition::new( + "CommitteeThresholdsUpdated", + "CommitteeThresholdsUpdated(uint8,uint32[2])", + None, + ), + EvmEventDefinition::new("E3Failed", "E3Failed(uint256,uint8,uint8)", Some(1)), + EvmEventDefinition::new( + "E3FailureProcessed", + "E3FailureProcessed(uint256,uint256,uint256)", + Some(1), + ), + EvmEventDefinition::new("E3ProgramRegistered", "E3ProgramRegistered(address)", None), + EvmEventDefinition::new("E3RefundManagerSet", "E3RefundManagerSet(address)", None), + EvmEventDefinition::new( + "E3Requested", + "E3Requested(uint256,(uint256,uint8,uint256,uint256[2],bytes32,address,uint8,bytes,address,address,bytes32,bytes32,bytes,address,bool),address)", + None, + ), + EvmEventDefinition::new("E3StageChanged", "E3StageChanged(uint256,uint8,uint8)", Some(1)), + EvmEventDefinition::new( + "EncryptionSchemeDisabled", + "EncryptionSchemeDisabled(bytes32)", + None, + ), + EvmEventDefinition::new( + "EncryptionSchemeEnabled", + "EncryptionSchemeEnabled(bytes32)", + None, + ), + EvmEventDefinition::new("FeeTokenAllowed", "FeeTokenAllowed(address,bool)", None), + EvmEventDefinition::new("FeeTokenSet", "FeeTokenSet(address)", None), + EvmEventDefinition::new("Initialized", "Initialized(uint64)", None), + EvmEventDefinition::new( + "InputPublished", + "InputPublished(uint256,bytes,uint256,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "MarkFailedGracePeriodSet", + "MarkFailedGracePeriodSet(uint256)", + None, + ), + EvmEventDefinition::new("MaxDurationSet", "MaxDurationSet(uint256)", None), + EvmEventDefinition::new( + "OwnershipTransferStarted", + "OwnershipTransferStarted(address,address)", + None, + ), + EvmEventDefinition::new( + "OwnershipTransferred", + "OwnershipTransferred(address,address)", + None, + ), + EvmEventDefinition::new("ParamSetRegistered", "ParamSetRegistered(uint8,bytes)", None), + EvmEventDefinition::new("ParamSetUpdated", "ParamSetUpdated(uint8,bytes,bytes)", None), + EvmEventDefinition::new("PkVerifierSet", "PkVerifierSet(bytes32,address)", None), + EvmEventDefinition::new( + "PlaintextOutputPublished", + "PlaintextOutputPublished(uint256,bytes,bytes)", + Some(1), + ), + EvmEventDefinition::new( + "PricingConfigUpdated", + "PricingConfigUpdated((uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,uint16,uint16,uint16,uint16,uint16,uint32,uint32))", + None, + ), + EvmEventDefinition::new( + "RewardClaimed", + "RewardClaimed(uint256,address,address,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "RewardCredited", + "RewardCredited(uint256,address,address,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "RewardsDistributed", + "RewardsDistributed(uint256,address[],uint256[])", + Some(1), + ), + EvmEventDefinition::new( + "SlashedFundsEscrowed", + "SlashedFundsEscrowed(uint256,uint256)", + Some(1), + ), + EvmEventDefinition::new("SlashingManagerSet", "SlashingManagerSet(address)", None), + EvmEventDefinition::new( + "TimeoutConfigUpdated", + "TimeoutConfigUpdated((uint256,uint256,uint256))", + None, + ), + EvmEventDefinition::new( + "TreasuryClaimed", + "TreasuryClaimed(address,address,uint256)", + None, + ), + EvmEventDefinition::new( + "TreasuryCredited", + "TreasuryCredited(uint256,address,address,uint256)", + Some(1), + ), +]; + +const BONDING_REGISTRY: &[EvmEventDefinition] = &[ + EvmEventDefinition::new( + "AssetsClaimed", + "AssetsClaimed(address,uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "AssetsQueuedForExit", + "AssetsQueuedForExit(address,uint256,uint256,uint64)", + None, + ), + EvmEventDefinition::new( + "CiphernodeDeregistrationRequested", + "CiphernodeDeregistrationRequested(address,uint64)", + None, + ), + EvmEventDefinition::new( + "ConfigurationUpdated", + "ConfigurationUpdated(bytes32,uint256,uint256)", + None, + ), + EvmEventDefinition::new("Initialized", "Initialized(uint64)", None), + EvmEventDefinition::new( + "LicenseBondUpdated", + "LicenseBondUpdated(address,int256,uint256,bytes32)", + None, + ), + EvmEventDefinition::new("LicenseTokenSet", "LicenseTokenSet(address)", None), + EvmEventDefinition::new( + "LicenseTransferShortfall", + "LicenseTransferShortfall(address,uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "OperatorActivationChanged", + "OperatorActivationChanged(address,bool)", + None, + ), + EvmEventDefinition::new( + "OwnershipTransferStarted", + "OwnershipTransferStarted(address,address)", + None, + ), + EvmEventDefinition::new( + "OwnershipTransferred", + "OwnershipTransferred(address,address)", + None, + ), + EvmEventDefinition::new( + "PendingAssetsSlashed", + "PendingAssetsSlashed(address,uint256,uint256,bool)", + None, + ), + EvmEventDefinition::new("RegistrySet", "RegistrySet(address)", None), + EvmEventDefinition::new( + "RewardDistributorUpdated", + "RewardDistributorUpdated(address,bool)", + None, + ), + EvmEventDefinition::new( + "SlashedFundsTreasurySet", + "SlashedFundsTreasurySet(address)", + None, + ), + EvmEventDefinition::new( + "SlashedFundsWithdrawn", + "SlashedFundsWithdrawn(address,uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "SlashingManagerUpdated", + "SlashingManagerUpdated(address,address)", + None, + ), + EvmEventDefinition::new( + "TicketBalanceUpdated", + "TicketBalanceUpdated(address,int256,uint256,bytes32)", + None, + ), + EvmEventDefinition::new("TicketTokenSet", "TicketTokenSet(address)", None), +]; + +const CIPHERNODE_REGISTRY: &[EvmEventDefinition] = &[ + EvmEventDefinition::new( + "AccusationVoteValidityProposalCancelled", + "AccusationVoteValidityProposalCancelled(uint256)", + None, + ), + EvmEventDefinition::new( + "AccusationVoteValidityProposed", + "AccusationVoteValidityProposed(uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "AccusationVoteValiditySet", + "AccusationVoteValiditySet(uint256)", + None, + ), + EvmEventDefinition::new("BondingRegistrySet", "BondingRegistrySet(address)", None), + EvmEventDefinition::new( + "CiphernodeAdded", + "CiphernodeAdded(address,uint256,uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "CiphernodeRemoved", + "CiphernodeRemoved(address,uint256,uint256,uint256)", + None, + ), + EvmEventDefinition::new( + "CommitteeActivationChanged", + "CommitteeActivationChanged(uint256,bool)", + Some(1), + ), + EvmEventDefinition::new( + "CommitteeFormationFailed", + "CommitteeFormationFailed(uint256,uint256,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "CommitteeMemberExpelled", + "CommitteeMemberExpelled(uint256,address,bytes32,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "CommitteePublished", + "CommitteePublished(uint256,address[],bytes,bytes32,bytes)", + Some(1), + ), + EvmEventDefinition::new( + "CommitteeRequested", + "CommitteeRequested(uint256,uint256,uint32[2],uint256,uint256)", + Some(1), + ), + EvmEventDefinition::new( + "CommitteeViabilityUpdated", + "CommitteeViabilityUpdated(uint256,uint256,uint256,bool)", + Some(1), + ), + EvmEventDefinition::new( + "DkgFoldAttestationVerifierProposalCancelled", + "DkgFoldAttestationVerifierProposalCancelled(address)", + None, + ), + EvmEventDefinition::new( + "DkgFoldAttestationVerifierProposed", + "DkgFoldAttestationVerifierProposed(address,uint256)", + None, + ), + EvmEventDefinition::new( + "DkgFoldAttestationVerifierUpdated", + "DkgFoldAttestationVerifierUpdated(address)", + None, + ), + EvmEventDefinition::new("Initialized", "Initialized(uint64)", None), + EvmEventDefinition::new("InterfoldSet", "InterfoldSet(address)", None), + EvmEventDefinition::new( + "OwnershipTransferStarted", + "OwnershipTransferStarted(address,address)", + None, + ), + EvmEventDefinition::new( + "OwnershipTransferred", + "OwnershipTransferred(address,address)", + None, + ), + EvmEventDefinition::new( + "RegistrySlashingManagerSet", + "RegistrySlashingManagerSet(address)", + None, + ), + EvmEventDefinition::new("SlashingManagerSet", "SlashingManagerSet(address)", None), + EvmEventDefinition::new( + "SortitionCommitteeFinalized", + "SortitionCommitteeFinalized(uint256,address[],uint256[])", + Some(1), + ), + EvmEventDefinition::new( + "SortitionSubmissionWindowSet", + "SortitionSubmissionWindowSet(uint256)", + None, + ), + EvmEventDefinition::new( + "TicketSubmitted", + "TicketSubmitted(uint256,address,uint256,uint256)", + Some(1), + ), +]; + +const SLASHING_MANAGER: &[EvmEventDefinition] = &[ + EvmEventDefinition::new( + "AppealFiled", + "AppealFiled(uint256,address,bytes32,string)", + None, + ), + EvmEventDefinition::new( + "AppealResolved", + "AppealResolved(uint256,address,bool,address,string)", + None, + ), + EvmEventDefinition::new("BanCancelled", "BanCancelled(address,address)", None), + EvmEventDefinition::new("BanProposed", "BanProposed(address,bytes32,address)", None), + EvmEventDefinition::new("BondingRegistrySet", "BondingRegistrySet(address)", None), + EvmEventDefinition::new( + "BondingRegistryUpdated", + "BondingRegistryUpdated(address,address)", + None, + ), + EvmEventDefinition::new( + "CiphernodeRegistrySet", + "CiphernodeRegistrySet(address)", + None, + ), + EvmEventDefinition::new( + "CiphernodeRegistryUpdated", + "CiphernodeRegistryUpdated(address,address)", + None, + ), + EvmEventDefinition::new( + "DefaultAdminDelayChangeCanceled", + "DefaultAdminDelayChangeCanceled()", + None, + ), + EvmEventDefinition::new( + "DefaultAdminDelayChangeScheduled", + "DefaultAdminDelayChangeScheduled(uint48,uint48)", + None, + ), + EvmEventDefinition::new( + "DefaultAdminTransferCanceled", + "DefaultAdminTransferCanceled()", + None, + ), + EvmEventDefinition::new( + "DefaultAdminTransferScheduled", + "DefaultAdminTransferScheduled(address,uint48)", + None, + ), + EvmEventDefinition::new("E3RefundManagerSet", "E3RefundManagerSet(address)", None), + EvmEventDefinition::new( + "E3RefundManagerUpdated", + "E3RefundManagerUpdated(address,address)", + None, + ), + EvmEventDefinition::new("EIP712DomainChanged", "EIP712DomainChanged()", None), + EvmEventDefinition::new("InterfoldSet", "InterfoldSet(address)", None), + EvmEventDefinition::new( + "InterfoldUpdated", + "InterfoldUpdated(address,address)", + None, + ), + EvmEventDefinition::new( + "NodeBanUpdated", + "NodeBanUpdated(address,bool,bytes32,address)", + None, + ), + EvmEventDefinition::new( + "RoleAdminChanged", + "RoleAdminChanged(bytes32,bytes32,bytes32)", + None, + ), + EvmEventDefinition::new("RoleGranted", "RoleGranted(bytes32,address,address)", None), + EvmEventDefinition::new("RoleRevoked", "RoleRevoked(bytes32,address,address)", None), + EvmEventDefinition::new("RoutingFailed", "RoutingFailed(uint256,uint256)", Some(1)), + EvmEventDefinition::new( + "SlashExecuted", + "SlashExecuted(uint256,uint256,address,bytes32,uint256,uint256,bool,uint8)", + None, + ), + EvmEventDefinition::new( + "SlashPolicyUpdated", + "SlashPolicyUpdated(bytes32,(uint256,uint256,bool,address,bool,uint256,bool,bool,uint8))", + None, + ), + EvmEventDefinition::new( + "SlashProposed", + "SlashProposed(uint256,uint256,address,bytes32,uint256,uint256,uint256,address,uint8)", + Some(2), + ), + EvmEventDefinition::new( + "SlashedFundsEscrowedToRefund", + "SlashedFundsEscrowedToRefund(uint256,uint256)", + Some(1), + ), +]; + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashSet; + + #[test] + fn every_contract_catalog_has_unique_topics() { + for contract in [ + "Interfold", + "BondingRegistry", + "CiphernodeRegistry", + "SlashingManager", + ] { + let mut topics = HashSet::new(); + for event in catalog(contract) { + assert!( + topics.insert(keccak256(event.signature.as_bytes())), + "duplicate event topic for {contract}: {}", + event.signature + ); + } + } + } + + #[test] + fn resolves_current_admin_and_e3_events() { + let ownership = keccak256("OwnershipTransferred(address,address)"); + assert_eq!( + find("BondingRegistry", ownership).map(|event| event.name), + Some("OwnershipTransferred") + ); + + let treasury = keccak256("TreasuryCredited(uint256,address,address,uint256)"); + let definition = find("Interfold", treasury).unwrap(); + assert_eq!(definition.name, "TreasuryCredited"); + assert_eq!(definition.e3_id_topic, Some(1)); + } +} diff --git a/crates/evm/src/domain/evm_log_observation.rs b/crates/evm/src/domain/evm_log_observation.rs new file mode 100644 index 0000000000..8fa20efebe --- /dev/null +++ b/crates/evm/src/domain/evm_log_observation.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use alloy::primitives::{LogData, B256, U256}; +use e3_events::{E3id, EvmLogObserved, InterfoldEventData}; +use e3_utils::ArcBytes; + +use super::evm_event_catalog; + +/// Preserve a watched contract log losslessly and attach the current ABI name +/// when it is not one of the protocol-driving typed events. +pub(crate) fn observe( + contract: &str, + data: &LogData, + topics: &[B256], + chain_id: u64, +) -> InterfoldEventData { + let definition = topics + .first() + .and_then(|topic| evm_event_catalog::find(contract, *topic)); + let e3_id = definition + .and_then(|event| event.e3_id_topic) + .and_then(|index| topics.get(index)) + .map(|topic| E3id::new(U256::from_be_bytes(topic.0).to_string(), chain_id)); + + EvmLogObserved { + contract: contract.to_owned(), + chain_id, + e3_id, + event_name: definition + .map(|event| event.name.to_owned()) + .unwrap_or_else(|| "UnknownEvmLog".to_owned()), + signature: definition.map(|event| event.signature.to_owned()), + known: definition.is_some(), + topics: topics.iter().map(ToString::to_string).collect(), + data: ArcBytes::from_bytes(data.data.as_ref()), + } + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::keccak256; + + #[test] + fn names_known_logs_and_extracts_indexed_e3_id() { + let topic0 = keccak256("TreasuryCredited(uint256,address,address,uint256)"); + let e3_id = B256::from(U256::from(17).to_be_bytes::<32>()); + let event = observe("Interfold", &LogData::default(), &[topic0, e3_id], 31337); + + match event { + InterfoldEventData::EvmLogObserved(event) => { + assert!(event.known); + assert_eq!(event.event_name, "TreasuryCredited"); + assert_eq!(event.e3_id, Some(E3id::new("17", 31337))); + } + other => panic!("expected EvmLogObserved, got {other:?}"), + } + } + + #[test] + fn unknown_logs_remain_lossless_and_explicit() { + let data = LogData::new_unchecked(vec![B256::ZERO], vec![1, 2, 3].into()); + let event = observe("Interfold", &data, &[B256::ZERO], 1); + match event { + InterfoldEventData::EvmLogObserved(event) => { + assert!(!event.known); + assert_eq!(event.event_name, "UnknownEvmLog"); + assert_eq!(event.data.extract_bytes(), &[1, 2, 3]); + } + other => panic!("expected EvmLogObserved, got {other:?}"), + } + } +} diff --git a/crates/evm/src/domain/interfold_events.rs b/crates/evm/src/domain/interfold_events.rs index ee74b6f823..7bab07554b 100644 --- a/crates/evm/src/domain/interfold_events.rs +++ b/crates/evm/src/domain/interfold_events.rs @@ -11,7 +11,10 @@ use alloy::primitives::{LogData, B256}; use alloy::sol_types::SolEvent; use e3_events::E3id; use e3_events::InterfoldEventData; -use e3_events::{E3Failed, E3Stage, E3StageChanged, FailureReason}; +use e3_events::{ + E3Failed, E3Stage, E3StageChanged, FailureReason, InputPublished, PlaintextOutputPublished, + RewardClaimed, RewardCredited, RewardsDistributed, +}; use e3_fhe_params::{encode_bfv_params, BfvParamSet, BfvPreset}; use e3_trbfv::helpers::calculate_error_size; use e3_utils::ArcBytes; @@ -185,6 +188,124 @@ impl From for InterfoldEventData { } } +struct PlaintextOutputPublishedWithChainId(pub IInterfold::PlaintextOutputPublished, pub u64); + +impl From for PlaintextOutputPublished { + fn from(value: PlaintextOutputPublishedWithChainId) -> Self { + PlaintextOutputPublished { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + plaintext_output: ArcBytes::from_bytes(value.0.plaintextOutput.as_ref()), + proof: ArcBytes::from_bytes(value.0.proof.as_ref()), + } + } +} + +impl From for InterfoldEventData { + fn from(value: PlaintextOutputPublishedWithChainId) -> Self { + let payload: PlaintextOutputPublished = value.into(); + payload.into() + } +} + +struct InputPublishedWithChainId(pub IInterfold::InputPublished, pub u64); + +impl From for InputPublished { + fn from(value: InputPublishedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + data: ArcBytes::from_bytes(value.0.data.as_ref()), + input_hash: value.0.inputHash.to_string(), + index: value.0.index.to_string(), + } + } +} + +impl From for InterfoldEventData { + fn from(value: InputPublishedWithChainId) -> Self { + InputPublished::from(value).into() + } +} + +struct RewardsDistributedWithChainId(pub IInterfold::RewardsDistributed, pub u64); + +impl From for RewardsDistributed { + fn from(value: RewardsDistributedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + nodes: value + .0 + .nodes + .into_iter() + .map(|node| node.to_string()) + .collect(), + amounts: value + .0 + .amounts + .into_iter() + .map(|amount| amount.to_string()) + .collect(), + } + } +} + +impl From for InterfoldEventData { + fn from(value: RewardsDistributedWithChainId) -> Self { + RewardsDistributed::from(value).into() + } +} + +struct RewardCreditedWithChainId(pub IInterfold::RewardCredited, pub u64); + +impl From for RewardCredited { + fn from(value: RewardCreditedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + account: value.0.account.to_string(), + token: value.0.token.to_string(), + amount: value.0.amount.to_string(), + } + } +} + +impl From for InterfoldEventData { + fn from(value: RewardCreditedWithChainId) -> Self { + RewardCredited::from(value).into() + } +} + +struct RewardClaimedWithChainId(pub IInterfold::RewardClaimed, pub u64); + +impl From for RewardClaimed { + fn from(value: RewardClaimedWithChainId) -> Self { + Self { + e3_id: E3id::new(value.0.e3Id.to_string(), value.1), + account: value.0.account.to_string(), + token: value.0.token.to_string(), + amount: value.0.amount.to_string(), + } + } +} + +impl From for InterfoldEventData { + fn from(value: RewardClaimedWithChainId) -> Self { + RewardClaimed::from(value).into() + } +} + +fn indexed_u256( + topics: &[B256], + index: usize, + event_type: &str, +) -> Option { + topics + .get(index) + .map(|topic| alloy::primitives::U256::from_be_bytes(topic.0)) + .or_else(|| { + error!("{event_type} missing indexed topic {index}"); + None + }) +} + pub(crate) fn extractor( data: &LogData, topics: &[B256], @@ -228,11 +349,20 @@ pub(crate) fn extractor( CiphertextOutputPublishedWithChainId(event, chain_id), )) } + Some(&IInterfold::InputPublished::SIGNATURE_HASH) => { + let Ok(mut event) = IInterfold::InputPublished::decode_log_data(data) else { + error!("Error parsing event InputPublished after topic matched!"); + return None; + }; + event.e3Id = indexed_u256(topics, 1, "InputPublished")?; + Some(InputPublishedWithChainId(event, chain_id).into()) + } Some(&IInterfold::E3Failed::SIGNATURE_HASH) => { - let Ok(event) = IInterfold::E3Failed::decode_log_data(data) else { + let Ok(mut event) = IInterfold::E3Failed::decode_log_data(data) else { error!("Error parsing event E3Failed after topic matched!"); return None; }; + event.e3Id = indexed_u256(topics, 1, "E3Failed")?; info!( "E3Failed event received: e3_id={}, stage={:?}, reason={:?}", event.e3Id, event.failedAtStage, event.reason @@ -263,12 +393,61 @@ pub(crate) fn extractor( event, chain_id, ))) } + Some(&IInterfold::PlaintextOutputPublished::SIGNATURE_HASH) => { + let Ok(mut event) = IInterfold::PlaintextOutputPublished::decode_log_data(data) else { + error!("Error parsing event PlaintextOutputPublished after topic matched!"); + return None; + }; + // e3Id is indexed → extract from topics[1], not log data + if let Some(e3_id_topic) = topics.get(1) { + event.e3Id = alloy::primitives::U256::from_be_bytes(e3_id_topic.0); + } else { + error!("PlaintextOutputPublished missing indexed e3Id in topics!"); + return None; + } + info!( + "PlaintextOutputPublished event received: e3_id={}", + event.e3Id + ); + Some(InterfoldEventData::from( + PlaintextOutputPublishedWithChainId(event, chain_id), + )) + } + Some(&IInterfold::RewardsDistributed::SIGNATURE_HASH) => { + let Ok(mut event) = IInterfold::RewardsDistributed::decode_log_data(data) else { + error!("Error parsing event RewardsDistributed after topic matched!"); + return None; + }; + event.e3Id = indexed_u256(topics, 1, "RewardsDistributed")?; + Some(RewardsDistributedWithChainId(event, chain_id).into()) + } + Some(&IInterfold::RewardCredited::SIGNATURE_HASH) => { + let Ok(mut event) = IInterfold::RewardCredited::decode_log_data(data) else { + error!("Error parsing event RewardCredited after topic matched!"); + return None; + }; + event.e3Id = indexed_u256(topics, 1, "RewardCredited")?; + Some(RewardCreditedWithChainId(event, chain_id).into()) + } + Some(&IInterfold::RewardClaimed::SIGNATURE_HASH) => { + let Ok(mut event) = IInterfold::RewardClaimed::decode_log_data(data) else { + error!("Error parsing event RewardClaimed after topic matched!"); + return None; + }; + event.e3Id = indexed_u256(topics, 1, "RewardClaimed")?; + Some(RewardClaimedWithChainId(event, chain_id).into()) + } _ => { trace!( topic=?topic0, - "Unknown event received by Interfold.sol parser but was ignored" + "Preserving event without a typed Interfold.sol decoder" ); - None + Some(crate::domain::evm_log_observation::observe( + "Interfold", + data, + topics, + chain_id, + )) } } } @@ -276,7 +455,7 @@ pub(crate) fn extractor( #[cfg(test)] mod tests { use super::*; - use alloy::primitives::U256; + use alloy::primitives::{Address, Bytes, U256}; #[test] fn test_convert_u8_to_e3_stage_known_and_unknown() { @@ -323,9 +502,71 @@ mod tests { } #[test] - fn test_extractor_ignores_unknown_topic() { + fn test_extractor_preserves_unknown_topic() { let log_data = LogData::default(); - assert!(extractor(&log_data, &[B256::ZERO], 1).is_none()); - assert!(extractor(&log_data, &[], 1).is_none()); + assert!(matches!( + extractor(&log_data, &[B256::ZERO], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); + assert!(matches!( + extractor(&log_data, &[], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); + } + + #[test] + fn test_extractor_decodes_input_and_reward_events() { + let input = IInterfold::InputPublished { + e3Id: U256::from(8), + data: Bytes::from_static(b"ciphertext"), + inputHash: U256::from(99), + index: U256::from(3), + }; + let input_log = input.encode_log_data(); + let out = extractor(&input_log, input_log.topics(), 10); + match out { + Some(InterfoldEventData::InputPublished(event)) => { + assert_eq!(event.e3_id, E3id::new("8", 10)); + assert_eq!(event.index, "3"); + assert_eq!(event.data.extract_bytes(), b"ciphertext"); + } + other => panic!("expected InputPublished, got {other:?}"), + } + + let reward = IInterfold::RewardCredited { + e3Id: U256::from(8), + account: Address::repeat_byte(0x11), + token: Address::repeat_byte(0x22), + amount: U256::from(500), + }; + let reward_log = reward.encode_log_data(); + let out = extractor(&reward_log, reward_log.topics(), 10); + match out { + Some(InterfoldEventData::RewardCredited(event)) => { + assert_eq!(event.e3_id, E3id::new("8", 10)); + assert_eq!(event.amount, "500"); + assert_eq!(event.account, Address::repeat_byte(0x11).to_string()); + } + other => panic!("expected RewardCredited, got {other:?}"), + } + } + + #[test] + fn test_extractor_decodes_plaintext_publication() { + let event = IInterfold::PlaintextOutputPublished { + e3Id: U256::from(18), + plaintextOutput: Bytes::from_static(b"plain"), + proof: Bytes::from_static(b"c7"), + }; + let log = event.encode_log_data(); + let out = extractor(&log, log.topics(), 100); + match out { + Some(InterfoldEventData::PlaintextOutputPublished(event)) => { + assert_eq!(event.e3_id, E3id::new("18", 100)); + assert_eq!(event.plaintext_output.extract_bytes(), b"plain"); + assert_eq!(event.proof.extract_bytes(), b"c7"); + } + other => panic!("expected PlaintextOutputPublished, got {other:?}"), + } } } diff --git a/crates/evm/src/domain/mod.rs b/crates/evm/src/domain/mod.rs index babe059d52..3928d41fdc 100644 --- a/crates/evm/src/domain/mod.rs +++ b/crates/evm/src/domain/mod.rs @@ -18,6 +18,8 @@ pub(crate) mod backoff; pub(crate) mod bonding_registry_events; pub(crate) mod chain_sync_state; pub(crate) mod ciphernode_registry_events; +pub(crate) mod evm_event_catalog; +pub(crate) mod evm_log_observation; pub(crate) mod historical_order_fixer; pub(crate) mod interfold_events; pub(crate) mod log_timestamp; diff --git a/crates/evm/src/domain/slashing_events.rs b/crates/evm/src/domain/slashing_events.rs index 46b718cf99..978d34859c 100644 --- a/crates/evm/src/domain/slashing_events.rs +++ b/crates/evm/src/domain/slashing_events.rs @@ -77,9 +77,14 @@ pub(crate) fn extractor( _ => { trace!( topic=?topics.first(), - "Unknown event was received by SlashingManager.sol parser but was ignored" + "Preserving event without a typed SlashingManager decoder" ); - None + Some(crate::domain::evm_log_observation::observe( + "SlashingManager", + data, + topics, + chain_id, + )) } } } @@ -87,6 +92,7 @@ pub(crate) fn extractor( #[cfg(test)] mod tests { use super::*; + use alloy::primitives::Address; #[test] fn test_safe_u256_to_u128_within_range() { @@ -101,9 +107,39 @@ mod tests { } #[test] - fn test_extractor_ignores_unknown_topic() { + fn test_extractor_preserves_unknown_topic() { let log_data = LogData::default(); - assert!(extractor(&log_data, &[B256::ZERO], 1).is_none()); - assert!(extractor(&log_data, &[], 1).is_none()); + assert!(matches!( + extractor(&log_data, &[B256::ZERO], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); + assert!(matches!( + extractor(&log_data, &[], 1), + Some(InterfoldEventData::EvmLogObserved(_)) + )); + } + + #[test] + fn test_extractor_matches_current_slash_executed_signature() { + let event = ISlashingManager::SlashExecuted { + proposalId: U256::from(3), + e3Id: U256::from(9), + operator: Address::repeat_byte(0x44), + reason: B256::repeat_byte(0x55), + ticketAmount: U256::from(100), + licenseAmount: U256::from(200), + executed: true, + lane: 1, + }; + let log = event.encode_log_data(); + let out = extractor(&log, log.topics(), 31337); + match out { + Some(InterfoldEventData::SlashExecuted(event)) => { + assert_eq!(event.e3_id, E3id::new("9", 31337)); + assert_eq!(event.ticket_amount, 100); + assert_eq!(event.license_amount, 200); + } + other => panic!("expected SlashExecuted, got {other:?}"), + } } } diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 2812fdfa37..416faca905 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -16,6 +16,7 @@ mod actors; mod contracts; mod domain; mod messages; +mod operator_status; mod repo; pub mod helpers; @@ -27,4 +28,5 @@ pub use actors::*; pub use domain::encode_attestation_evidence; pub use helpers::*; pub use messages::*; +pub use operator_status::*; pub use repo::*; diff --git a/crates/evm/src/operator_status.rs b/crates/evm/src/operator_status.rs new file mode 100644 index 0000000000..23e17175a9 --- /dev/null +++ b/crates/evm/src/operator_status.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Read-only, operator-facing on-chain status query. + +use crate::{ + contracts::{IBondingRegistry, ICiphernodeRegistry}, + ProviderConfig, +}; +use alloy::primitives::Address; +use e3_config::chain_config::ChainConfig; +use serde::Serialize; +use std::str::FromStr; + +#[derive(Clone, Debug, Serialize)] +pub struct OperatorChainStatus { + pub chain_id: u64, + pub chain_name: String, + pub registered_nodes: String, + pub active_nodes: String, + pub operator_registered: bool, + pub operator_active: bool, + pub exit_in_progress: bool, + pub ticket_balance: String, + pub available_tickets: String, + pub license_bond: String, +} + +pub async fn fetch_operator_status( + chain: &ChainConfig, + operator: Address, +) -> anyhow::Result { + let provider = ProviderConfig::new(chain.rpc_url()?, chain.rpc_auth.clone()) + .create_readonly_provider() + .await?; + let client = provider.provider().clone(); + let bonding_address = Address::from_str(chain.contracts.bonding_registry.address_str())?; + let registry_address = Address::from_str(chain.contracts.ciphernode_registry.address_str())?; + let bonding = IBondingRegistry::new(bonding_address, client.clone()); + let registry = ICiphernodeRegistry::new(registry_address, client); + + let ( + ticket_balance, + license_bond, + available_tickets, + operator_registered, + operator_active, + active_nodes, + registered_nodes, + exit_in_progress, + ) = tokio::try_join!( + async { bonding.getTicketBalance(operator).call().await }, + async { bonding.getLicenseBond(operator).call().await }, + async { bonding.availableTickets(operator).call().await }, + async { bonding.isRegistered(operator).call().await }, + async { bonding.isActive(operator).call().await }, + async { bonding.numActiveOperators().call().await }, + async { registry.numCiphernodes().call().await }, + async { bonding.hasExitInProgress(operator).call().await }, + )?; + + Ok(OperatorChainStatus { + chain_id: provider.chain_id(), + chain_name: chain.name.clone(), + registered_nodes: registered_nodes.to_string(), + active_nodes: active_nodes.to_string(), + operator_registered, + operator_active, + exit_in_progress, + ticket_balance: ticket_balance.to_string(), + available_tickets: available_tickets.to_string(), + license_bond: license_bond.to_string(), + }) +} diff --git a/crates/logger/Cargo.toml b/crates/logger/Cargo.toml index 771c7ec180..603cb9b472 100644 --- a/crates/logger/Cargo.toml +++ b/crates/logger/Cargo.toml @@ -11,3 +11,6 @@ e3-events = { workspace = true } actix = { workspace = true } base64 = { workspace = true } tracing = { workspace = true } +tracing-subscriber = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/crates/logger/src/lib.rs b/crates/logger/src/lib.rs index db600f1a50..75a6462503 100644 --- a/crates/logger/src/lib.rs +++ b/crates/logger/src/lib.rs @@ -4,5 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +mod log_collector; mod logger; +mod tracing_layer; + +pub use log_collector::*; pub use logger::*; +pub use tracing_layer::*; diff --git a/crates/logger/src/log_collector.rs b/crates/logger/src/log_collector.rs new file mode 100644 index 0000000000..3eb396db1e --- /dev/null +++ b/crates/logger/src/log_collector.rs @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Bounded operational-log store used by the local node dashboard. +//! +//! Protocol events do not live here: their authoritative, durable source is +//! the EventStore. This collector is for `tracing` diagnostics such as RPC, +//! networking, startup, and resource warnings. + +use serde::Serialize; +use serde_json::{Map, Value}; +use std::{ + collections::VecDeque, + fs::{self, File, OpenOptions}, + io::{BufWriter, Write}, + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicU64, Ordering}, + Mutex, OnceLock, + }, + time::{SystemTime, UNIX_EPOCH}, +}; + +const DEFAULT_CAPACITY: usize = 20_000; +const DEFAULT_QUERY_LIMIT: usize = 500; +const MAX_QUERY_LIMIT: usize = 2_000; +const MAX_FILE_BYTES: u64 = 25 * 1024 * 1024; +const ROTATED_FILES: usize = 4; + +#[derive(Clone, Debug, Serialize)] +pub struct LogEntry { + pub seq: u64, + pub timestamp_ms: u64, + pub level: String, + pub target: String, + pub message: String, + pub node: String, + #[serde(skip_serializing_if = "Map::is_empty")] + pub fields: Map, +} + +#[derive(Clone, Debug, Serialize)] +pub struct LogQueryResult { + pub entries: Vec, + pub next_cursor: u64, + pub oldest_cursor: u64, + pub total_stored: usize, +} + +pub struct LogCollector { + entries: Mutex>, + writer: Mutex>, + capacity: usize, + next_seq: AtomicU64, + node: String, +} + +impl LogCollector { + /// Initialize the process-local collector. Repeated calls return the first + /// initialized instance, matching tracing's process-global subscriber. + pub fn init(node: &str, path: Option) -> &'static Self { + INSTANCE.get_or_init(|| Self::new(node, path, DEFAULT_CAPACITY)) + } + + pub fn global() -> Option<&'static Self> { + INSTANCE.get() + } + + fn new(node: &str, path: Option, capacity: usize) -> Self { + let writer = path.and_then(|path| match RollingJsonWriter::open(path) { + Ok(writer) => Some(writer), + Err(error) => { + eprintln!("failed to open ciphernode operational log: {error}"); + None + } + }); + Self { + entries: Mutex::new(VecDeque::with_capacity(capacity)), + writer: Mutex::new(writer), + capacity, + next_seq: AtomicU64::new(0), + node: node.to_owned(), + } + } + + pub fn record(&self, level: &str, target: &str, message: String, fields: Map) { + let entry = LogEntry { + seq: self.next_seq.fetch_add(1, Ordering::Relaxed), + timestamp_ms: now_ms(), + level: level.to_owned(), + target: target.to_owned(), + message, + node: self.node.clone(), + fields, + }; + + if let Ok(mut entries) = self.entries.lock() { + if entries.len() == self.capacity { + entries.pop_front(); + } + entries.push_back(entry.clone()); + } + + if let Ok(mut writer_guard) = self.writer.lock() { + let failed = writer_guard + .as_mut() + .and_then(|writer| writer.write(&entry).err()); + if let Some(error) = failed { + eprintln!("failed to write ciphernode operational log: {error}"); + *writer_guard = None; + } + } + } + + pub fn query( + &self, + since: Option, + limit: Option, + level: Option<&str>, + target: Option<&str>, + text: Option<&str>, + ) -> LogQueryResult { + let entries = match self.entries.lock() { + Ok(entries) => entries, + Err(poisoned) => poisoned.into_inner(), + }; + let oldest_cursor = entries + .front() + .map(|entry| entry.seq) + .unwrap_or_else(|| self.next_seq.load(Ordering::Relaxed)); + let head_cursor = self.next_seq.load(Ordering::Relaxed); + let limit = limit + .unwrap_or(DEFAULT_QUERY_LIMIT) + .clamp(1, MAX_QUERY_LIMIT); + let needle = text.map(str::to_lowercase); + let matches = |entry: &LogEntry| { + level.is_none_or(|value| entry.level.eq_ignore_ascii_case(value)) + && target.is_none_or(|value| entry.target.contains(value)) + && needle.as_ref().is_none_or(|value| { + entry.message.to_lowercase().contains(value) + || entry.target.to_lowercase().contains(value) + || serde_json::to_string(&entry.fields) + .unwrap_or_default() + .to_lowercase() + .contains(value) + }) + }; + + let selected: Vec = if let Some(since) = since { + entries + .iter() + .filter(|entry| entry.seq >= since.max(oldest_cursor)) + .filter(|entry| matches(entry)) + .take(limit) + .cloned() + .collect() + } else { + let mut latest: Vec<_> = entries + .iter() + .rev() + .filter(|entry| matches(entry)) + .take(limit) + .cloned() + .collect(); + latest.reverse(); + latest + }; + let next_cursor = if selected.len() == limit { + selected + .last() + .map(|entry| entry.seq.saturating_add(1)) + .unwrap_or(head_cursor) + } else { + head_cursor + }; + + LogQueryResult { + entries: selected, + next_cursor, + oldest_cursor, + total_stored: entries.len(), + } + } + + pub fn flush(&self) { + if let Ok(mut writer) = self.writer.lock() { + if let Some(writer) = writer.as_mut() { + let _ = writer.flush(); + } + } + } +} + +static INSTANCE: OnceLock = OnceLock::new(); + +struct RollingJsonWriter { + path: PathBuf, + file: Option>, + bytes_written: u64, + pending_lines: usize, +} + +impl RollingJsonWriter { + fn open(path: PathBuf) -> std::io::Result { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + let file = OpenOptions::new().create(true).append(true).open(&path)?; + let bytes_written = file.metadata()?.len(); + Ok(Self { + path, + file: Some(BufWriter::new(file)), + bytes_written, + pending_lines: 0, + }) + } + + fn write(&mut self, entry: &LogEntry) -> std::io::Result<()> { + let mut line = serde_json::to_vec(entry).map_err(std::io::Error::other)?; + line.push(b'\n'); + if self.bytes_written.saturating_add(line.len() as u64) > MAX_FILE_BYTES { + self.rotate()?; + } + if let Some(file) = self.file.as_mut() { + file.write_all(&line)?; + self.pending_lines += 1; + if self.pending_lines >= 64 { + file.flush()?; + self.pending_lines = 0; + } + } + self.bytes_written = self.bytes_written.saturating_add(line.len() as u64); + Ok(()) + } + + fn rotate(&mut self) -> std::io::Result<()> { + if let Some(mut file) = self.file.take() { + file.flush()?; + } + for index in (1..ROTATED_FILES).rev() { + let from = rotated_path(&self.path, index); + let to = rotated_path(&self.path, index + 1); + if from.exists() { + let _ = fs::remove_file(&to); + fs::rename(from, to)?; + } + } + if self.path.exists() { + let first = rotated_path(&self.path, 1); + let _ = fs::remove_file(&first); + fs::rename(&self.path, first)?; + } + let file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&self.path)?; + self.file = Some(BufWriter::new(file)); + self.bytes_written = 0; + self.pending_lines = 0; + Ok(()) + } + + fn flush(&mut self) -> std::io::Result<()> { + if let Some(file) = self.file.as_mut() { + file.flush()?; + } + self.pending_lines = 0; + Ok(()) + } +} + +fn rotated_path(path: &Path, index: usize) -> PathBuf { + PathBuf::from(format!("{}.{}", path.display(), index)) +} + +fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bounded_store_remains_in_sequence_order() { + let collector = LogCollector::new("node", None, 2); + collector.record("INFO", "a", "one".into(), Map::new()); + collector.record("WARN", "b", "two".into(), Map::new()); + collector.record("ERROR", "c", "three".into(), Map::new()); + + let result = collector.query(None, None, None, None, None); + assert_eq!(result.oldest_cursor, 1); + assert_eq!( + result + .entries + .iter() + .map(|entry| entry.seq) + .collect::>(), + vec![1, 2] + ); + } + + #[test] + fn filters_structured_logs() { + let collector = LogCollector::new("node", None, 4); + let mut fields = Map::new(); + fields.insert("e3_id".into(), Value::String("1:9".into())); + collector.record("INFO", "e3::worker", "proof complete".into(), fields); + + let result = collector.query(None, None, Some("info"), Some("worker"), Some("1:9")); + assert_eq!(result.entries.len(), 1); + } + + #[test] + fn query_without_cursor_returns_the_newest_window() { + let collector = LogCollector::new("node", None, 5); + for value in 0..5 { + collector.record("INFO", "test", value.to_string(), Map::new()); + } + + let result = collector.query(None, Some(2), None, None, None); + assert_eq!( + result + .entries + .iter() + .map(|entry| entry.seq) + .collect::>(), + vec![3, 4] + ); + assert_eq!(result.next_cursor, 5); + } + + #[test] + fn exhausted_filter_advances_cursor_to_the_log_head() { + let collector = LogCollector::new("node", None, 5); + collector.record("INFO", "test", "one".into(), Map::new()); + collector.record("DEBUG", "test", "two".into(), Map::new()); + + let result = collector.query(Some(0), Some(5), Some("error"), None, None); + assert!(result.entries.is_empty()); + assert_eq!(result.next_cursor, 2); + } +} diff --git a/crates/logger/src/logger.rs b/crates/logger/src/logger.rs index 23cbab0d25..c18e07927b 100644 --- a/crates/logger/src/logger.rs +++ b/crates/logger/src/logger.rs @@ -6,15 +6,30 @@ use actix::{Actor, Addr, Context, Handler}; use e3_events::{ - prelude::Event, EventBus, EventType, InterfoldEvent, InterfoldEventData, SeqState, Subscribe, + prelude::Event, BusHandle, E3Stage, EventBus, EventContextAccessors, EventType, InterfoldEvent, + InterfoldEventData, SeqState, Subscribe, }; use std::marker::PhantomData; -use tracing::{error, info}; +use tracing::{debug, error, info, warn}; pub trait EventLogging: Event { fn log(&self, logger_name: &str); } +/// Attach the standard, passive protocol-event observer to a node bus. +/// +/// Keeping this wiring here prevents node assembly from depending on logger +/// actor details. The observer only subscribes; it never publishes or changes +/// event ordering. +pub fn attach_protocol_logger(name: &str, bus: &BusHandle) -> Addr> { + SimpleLogger::::attach(name, bus.event_bus().clone()) +} + +/// Optional protocol-event logger. +/// +/// EventStore remains the authoritative event history. This actor emits a +/// compact, structured tracing record for operators and OTLP collectors without +/// serializing multi-megabyte cryptographic payloads into application logs. pub struct SimpleLogger { name: String, _p: PhantomData, @@ -31,7 +46,7 @@ impl SimpleLogger { EventType::All, addr.clone().recipient(), )); - info!(node=%name, "READY!"); + info!(node = %name, "protocol event logging ready"); addr } } @@ -48,17 +63,247 @@ impl Handler for SimpleLogger { } } +#[derive(Clone, Copy)] +enum Severity { + Error, + Warn, + Info, + Debug, +} + +fn severity(data: &InterfoldEventData) -> Severity { + use InterfoldEventData as E; + match data { + E::InterfoldError(_) + | E::E3Failed(_) + | E::CommitteeFormationFailed(_) + | E::ProofVerificationFailed(_) + | E::SignedProofFailed(_) + | E::ThresholdShareCollectionFailed(_) + | E::EncryptionKeyCollectionFailed(_) + | E::ComputeRequestError(_) + | E::CommitmentConsistencyViolation(_) => Severity::Error, + + E::ProofFailureAccusation(_) + | E::AccusationVote(_) + | E::AccusationQuorumReached(_) + | E::SlashExecuted(_) + | E::CommitteeMemberExpelled(_) + | E::AggregatorChanged(_) => Severity::Warn, + + E::EvmLogObserved(event) if !event.known => Severity::Warn, + + E::E3Requested(_) + | E::CommitteeRequested(_) + | E::TicketGenerated(_) + | E::TicketSubmitted(_) + | E::CommitteeFinalizeRequested(_) + | E::CommitteeFinalized(_) + | E::CommitteePublished(_) + | E::E3StageChanged(_) + | E::CiphertextOutputPublished(_) + | E::PlaintextOutputPublished(_) + | E::E3RequestComplete(_) + | E::PublicKeyAggregated(_) + | E::PlaintextAggregated(_) + | E::KeyshareCreated(_) + | E::DecryptionshareCreated(_) + | E::CiphernodeAdded(_) + | E::CiphernodeRemoved(_) + | E::TicketBalanceUpdated(_) + | E::OperatorActivationChanged(_) => Severity::Info, + + _ => Severity::Debug, + } +} + +fn stage(data: &InterfoldEventData) -> &'static str { + use InterfoldEventData as E; + match data { + E::E3Requested(_) => "request", + E::CommitteeRequested(_) + | E::CiphernodeSelected(_) + | E::TicketGenerated(_) + | E::TicketSubmitted(_) + | E::CommitteeFinalizeRequested(_) + | E::CommitteeFinalized(_) + | E::CommitteeFormationFailed(_) + | E::CommitteeMemberExpelled(_) + | E::CommitteeViabilityUpdated(_) => "committee", + E::EncryptionKeyPending(_) + | E::EncryptionKeyCreated(_) + | E::EncryptionKeyReceived(_) + | E::EncryptionKeyCollectionFailed(_) => "dkg_setup", + E::ThresholdSharePending(_) + | E::ThresholdShareCreated(_) + | E::ThresholdShareCollectionFailed(_) + | E::DkgProofSigned(_) + | E::ShareVerificationDispatched(_) + | E::ShareVerificationComplete(_) + | E::DecryptionKeyShared(_) + | E::DKGInnerProofReady(_) + | E::DKGRecursiveAggregationComplete(_) + | E::CommitmentConsistencyCheckRequested(_) + | E::CommitmentConsistencyCheckComplete(_) + | E::CommitmentConsistencyViolation(_) => "dkg_shares", + E::KeyshareCreated(_) + | E::PkGenerationProofSigned(_) + | E::PkAggregationProofPending(_) + | E::PkAggregationProofSigned(_) + | E::PublicKeyAggregated(_) + | E::CommitteePublished(_) => "key_publication", + E::InputPublished(_) + | E::ComputeRequest(_) + | E::ComputeResponse(_) + | E::ComputeRequestError(_) + | E::CiphertextOutputPublished(_) => "computation", + E::DecryptionShareProofsPending(_) + | E::DecryptionShareProofSigned(_) + | E::ShareDecryptionProofPending(_) + | E::DecryptionshareCreated(_) + | E::AggregationProofPending(_) + | E::AggregationProofSigned(_) + | E::PlaintextAggregated(_) => "decryption", + E::PlaintextOutputPublished(_) + | E::RewardsDistributed(_) + | E::RewardCredited(_) + | E::RewardClaimed(_) + | E::E3RequestComplete(_) + | E::CommitteeActivationChanged(_) => "settlement", + E::E3StageChanged(event) => lifecycle_stage(&event.new_stage), + E::E3Failed(event) => failure_stage(&event.failed_at_stage), + E::ProofVerificationFailed(event) => proof_stage(&format!("{:?}", event.proof_type)), + E::ProofVerificationPassed(event) => proof_stage(&format!("{:?}", event.proof_type)), + E::SignedProofFailed(event) => proof_stage(&format!("{:?}", event.proof_type)), + E::ProofFailureAccusation(_) + | E::AccusationVote(_) + | E::AccusationQuorumReached(_) + | E::SlashExecuted(_) => "dkg_shares", + E::EvmLogObserved(event) => match event.event_name.as_str() { + "CommitteeFormed" | "CommitteeFinalized" => "committee", + "TreasuryCredited" + | "TreasuryClaimed" + | "SlashedFundsEscrowed" + | "SlashedFundsEscrowedToRefund" + | "RoutingFailed" + | "E3FailureProcessed" + | "SlashProposed" => "settlement", + _ => "node", + }, + _ => "node", + } +} + +fn lifecycle_stage(stage: &E3Stage) -> &'static str { + match stage { + E3Stage::None | E3Stage::Requested => "request", + E3Stage::CommitteeFinalized => "committee", + E3Stage::KeyPublished => "key_publication", + E3Stage::CiphertextReady => "computation", + E3Stage::Complete | E3Stage::Failed => "settlement", + } +} + +fn failure_stage(stage: &E3Stage) -> &'static str { + match stage { + E3Stage::None | E3Stage::Requested => "request", + E3Stage::CommitteeFinalized => "dkg_shares", + E3Stage::KeyPublished => "computation", + E3Stage::CiphertextReady => "decryption", + E3Stage::Complete | E3Stage::Failed => "settlement", + } +} + +fn proof_stage(proof: &str) -> &'static str { + if proof.contains("C6") || proof.contains("C7") || proof.contains("Decryption") { + "decryption" + } else if proof.contains("C5") || proof.contains("Pk") { + "key_publication" + } else { + "dkg_shares" + } +} + +fn compact_error(value: &str) -> String { + const MAX_CHARS: usize = 600; + let mut chars = value.chars(); + let compact: String = chars.by_ref().take(MAX_CHARS).collect(); + if chars.next().is_some() { + format!("{compact}…") + } else { + compact + } +} + impl EventLogging for InterfoldEvent { fn log(&self, logger_name: &str) { - match self.get_data() { - InterfoldEventData::InterfoldError(_) => error!(event=%self, "ERROR!"), - _ => match self.get_e3_id() { - Some(e3_id) => { - println!("{logger_name}: {e3_id} Event Broadcasted"); - info!(me=logger_name, evt=%self, e3_id=%e3_id, "Event Broadcasted") - } - None => info!(me=logger_name, evt=%self, "Event Broadcasted"), - }, + let data = self.get_data(); + let event_type = match data { + InterfoldEventData::EvmLogObserved(event) => { + format!("{}::{}", event.contract, event.event_name) + } + _ => self.event_type(), + }; + let e3_id = self + .get_e3_id() + .map(|id| id.to_string()) + .unwrap_or_default(); + let error_message = match data { + InterfoldEventData::InterfoldError(error) => compact_error(&error.message), + _ => String::new(), }; + let stage = stage(data); + let observation = matches!( + data, + InterfoldEventData::EvmLogObserved(_) + | InterfoldEventData::InputPublished(_) + | InterfoldEventData::PlaintextOutputPublished(_) + | InterfoldEventData::RewardsDistributed(_) + | InterfoldEventData::RewardCredited(_) + | InterfoldEventData::RewardClaimed(_) + | InterfoldEventData::CommitteeFormationFailed(_) + | InterfoldEventData::CommitteeActivationChanged(_) + | InterfoldEventData::CommitteeViabilityUpdated(_) + ); + + macro_rules! emit { + ($macro:ident) => { + $macro!( + node = %logger_name, + event_type = %event_type, + e3_id = %e3_id, + stage, + observation, + event_id = %self.id(), + causation_id = %self.causation_id(), + origin_id = %self.origin_id(), + source = ?self.source(), + block = ?self.block(), + error = %error_message, + "protocol event" + ) + }; + } + + match severity(data) { + Severity::Error => emit!(error), + Severity::Warn => emit!(warn), + Severity::Info => emit!(info), + Severity::Debug => emit!(debug), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lifecycle_and_failure_stages_are_explicit() { + assert_eq!(lifecycle_stage(&E3Stage::CommitteeFinalized), "committee"); + assert_eq!(failure_stage(&E3Stage::CommitteeFinalized), "dkg_shares"); + assert_eq!(failure_stage(&E3Stage::CiphertextReady), "decryption"); + assert_eq!(proof_stage("C5PkAggregation"), "key_publication"); + assert_eq!(proof_stage("C7DecryptionAggregation"), "decryption"); } } diff --git a/crates/logger/src/tracing_layer.rs b/crates/logger/src/tracing_layer.rs new file mode 100644 index 0000000000..13db806e16 --- /dev/null +++ b/crates/logger/src/tracing_layer.rs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! `tracing` integration for the operator log store. + +use serde_json::{Map, Number, Value}; +use tracing::{span, Id, Subscriber}; +use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; + +use crate::LogCollector; + +/// Captures structured tracing events for the bounded dashboard store and +/// rotating JSONL file. It does not subscribe to or mutate the protocol bus. +#[derive(Clone, Copy, Debug, Default)] +pub struct OperationalLogLayer; + +impl Layer for OperationalLogLayer +where + S: Subscriber + for<'lookup> LookupSpan<'lookup>, +{ + fn on_new_span(&self, attributes: &span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + let mut visitor = LogVisitor::default(); + attributes.record(&mut visitor); + if let Some(span) = ctx.span(id) { + span.extensions_mut().insert(SpanFields(visitor.fields)); + } + } + + fn on_record(&self, id: &Id, values: &span::Record<'_>, ctx: Context<'_, S>) { + let Some(span) = ctx.span(id) else { + return; + }; + let mut visitor = LogVisitor::default(); + values.record(&mut visitor); + let mut extensions = span.extensions_mut(); + if let Some(fields) = extensions.get_mut::() { + fields.0.extend(visitor.fields); + } else { + extensions.insert(SpanFields(visitor.fields)); + } + } + + fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) { + let Some(collector) = LogCollector::global() else { + return; + }; + let mut visitor = LogVisitor::default(); + if let Some(scope) = ctx.event_scope(event) { + for span in scope.from_root() { + if let Some(fields) = span.extensions().get::() { + visitor.fields.extend(fields.0.clone()); + } + } + } + event.record(&mut visitor); + let message = visitor + .message + .take() + .unwrap_or_else(|| event.metadata().name().to_owned()); + collector.record( + event.metadata().level().as_str(), + event.metadata().target(), + message, + visitor.fields, + ); + } +} + +struct SpanFields(Map); + +#[derive(Default)] +struct LogVisitor { + message: Option, + fields: Map, +} + +impl LogVisitor { + fn record_value(&mut self, field: &tracing::field::Field, value: Value) { + if field.name() == "message" { + self.message = value + .as_str() + .map(str::to_owned) + .or_else(|| Some(value.to_string())); + } else { + self.fields.insert(field.name().to_owned(), value); + } + } +} + +impl tracing::field::Visit for LogVisitor { + fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { + self.record_value(field, Value::Bool(value)); + } + + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { + self.record_value(field, Value::Number(value.into())); + } + + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + self.record_value(field, Value::Number(value.into())); + } + + fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { + let value = Number::from_f64(value) + .map(Value::Number) + .unwrap_or(Value::Null); + self.record_value(field, value); + } + + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + self.record_value(field, Value::String(value.to_owned())); + } + + fn record_error( + &mut self, + field: &tracing::field::Field, + value: &(dyn std::error::Error + 'static), + ) { + self.record_value(field, Value::String(value.to_string())); + } + + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + self.record_value(field, Value::String(format!("{value:?}"))); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tracing_subscriber::prelude::*; + + #[test] + fn events_inherit_structured_span_fields() { + let collector = LogCollector::init("test-node", None); + let subscriber = tracing_subscriber::registry().with(OperationalLogLayer); + + tracing::subscriber::with_default(subscriber, || { + let span = tracing::info_span!( + "compute", + e3_id = "31337:9", + stage = "computation", + operation = "verify_c5" + ); + let _guard = span.enter(); + tracing::info!(duration_ms = 7_u64, "span inheritance test"); + }); + + let result = collector.query(None, Some(10), None, None, Some("span inheritance test")); + let entry = result.entries.last().expect("captured tracing event"); + assert_eq!(entry.fields["e3_id"], "31337:9"); + assert_eq!(entry.fields["stage"], "computation"); + assert_eq!(entry.fields["operation"], "verify_c5"); + assert_eq!(entry.fields["duration_ms"], 7); + } +} diff --git a/crates/multithread/src/effect_gate.rs b/crates/multithread/src/effect_gate.rs new file mode 100644 index 0000000000..b46597b123 --- /dev/null +++ b/crates/multithread/src/effect_gate.rs @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Replay-safe gate for value-bearing compute effects. + +use actix::{Actor, Context, Handler, Recipient}; +use e3_events::{ + ComputeRequestKind, E3Stage, E3id, Event, EventContextAccessors, EventSubscriber, EventType, + InterfoldEvent, InterfoldEventData, +}; +use e3_utils::MAILBOX_LIMIT; +use std::collections::HashMap; +use tracing::info; + +use e3_events::BusHandle; + +type RequestKey = (E3id, ComputeRequestKind); + +/// Buffers compute effects observed during EventStore replay. Identical +/// semantic requests are deduplicated independently of their correlation ID; +/// the newest HLC context wins so a request regenerated by a hydrated actor is +/// preferred over its stale pre-crash copy. +pub(crate) struct ComputeEffectGate { + target: Recipient, + enabled: bool, + pending: HashMap, +} + +impl ComputeEffectGate { + fn new(target: Recipient) -> Self { + Self { + target, + enabled: false, + pending: HashMap::new(), + } + } + + pub(crate) fn attach(bus: &BusHandle, target: Recipient) { + let gate = Self::new(target).start(); + bus.subscribe_all( + &[ + EventType::ComputeRequest, + EventType::EffectsEnabled, + EventType::E3RequestComplete, + EventType::E3Failed, + EventType::E3StageChanged, + ], + gate.recipient(), + ); + } + + fn queue(&mut self, event: InterfoldEvent) { + let InterfoldEventData::ComputeRequest(request) = event.get_data() else { + return; + }; + let key = (request.e3_id.clone(), request.request.clone()); + match self.pending.entry(key) { + std::collections::hash_map::Entry::Occupied(mut entry) + if event.ts() > entry.get().ts() => + { + entry.insert(event); + } + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(event); + } + _ => {} + } + } + + fn enable(&mut self) { + self.enabled = true; + let mut pending: Vec<_> = self.pending.drain().map(|(_, event)| event).collect(); + pending.sort_by_key(EventContextAccessors::ts); + let count = pending.len(); + for event in pending { + self.target.do_send(event); + } + info!(count, "released replay-safe compute effects"); + } + + fn cancel(&mut self, e3_id: &E3id) { + self.pending + .retain(|(pending_id, _), _| pending_id != e3_id); + } +} + +impl Actor for ComputeEffectGate { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + ctx.set_mailbox_capacity(MAILBOX_LIMIT); + } +} + +impl Handler for ComputeEffectGate { + type Result = (); + + fn handle(&mut self, event: InterfoldEvent, _: &mut Self::Context) { + match event.get_data() { + InterfoldEventData::ComputeRequest(_) if self.enabled => self.target.do_send(event), + InterfoldEventData::ComputeRequest(_) => self.queue(event), + InterfoldEventData::EffectsEnabled(_) => self.enable(), + InterfoldEventData::E3RequestComplete(complete) => self.cancel(&complete.e3_id), + InterfoldEventData::E3Failed(failed) => self.cancel(&failed.e3_id), + InterfoldEventData::E3StageChanged(stage) + if matches!(stage.new_stage, E3Stage::Complete | E3Stage::Failed) => + { + self.cancel(&stage.e3_id) + } + _ => {} + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use actix::{Message, ResponseFuture}; + use e3_events::{ + ComputeRequest, CorrelationId, E3RequestComplete, EffectsEnabled, + EventConstructorWithTimestamp, EventSource, InterfoldEvent, PkBfvProofRequest, Unsequenced, + ZkRequest, + }; + use e3_fhe_params::BfvPreset; + use e3_utils::ArcBytes; + use e3_zk_helpers::CiphernodesCommitteeSize; + + #[derive(Message)] + #[rtype(result = "Vec")] + struct Received; + + #[derive(Default)] + struct Recorder(Vec); + + impl Actor for Recorder { + type Context = Context; + } + + impl Handler for Recorder { + type Result = (); + + fn handle(&mut self, event: InterfoldEvent, _: &mut Self::Context) { + if let InterfoldEventData::ComputeRequest(request) = event.get_data() { + self.0.push(request.correlation_id); + } + } + } + + impl Handler for Recorder { + type Result = ResponseFuture>; + + fn handle(&mut self, _: Received, _: &mut Self::Context) -> Self::Result { + let received = self.0.clone(); + Box::pin(async move { received }) + } + } + + fn compute(correlation_id: CorrelationId, timestamp: u128) -> InterfoldEvent { + let request = ComputeRequest::zk( + ZkRequest::PkBfv(PkBfvProofRequest::new( + ArcBytes::from_bytes(&[]), + BfvPreset::default(), + CiphernodesCommitteeSize::Micro, + )), + correlation_id, + E3id::new("4", 1), + ); + InterfoldEvent::::new_with_timestamp( + request.into(), + None, + timestamp, + None, + EventSource::Local, + ) + .into_sequenced(1) + } + + fn effects_enabled() -> InterfoldEvent { + InterfoldEvent::::new_with_timestamp( + EffectsEnabled::new().into(), + None, + 30, + None, + EventSource::Local, + ) + .into_sequenced(2) + } + + fn completed() -> InterfoldEvent { + InterfoldEvent::::new_with_timestamp( + E3RequestComplete { + e3_id: E3id::new("4", 1), + } + .into(), + None, + 25, + None, + EventSource::Local, + ) + .into_sequenced(2) + } + + #[actix::test] + async fn buffers_until_enabled_and_keeps_newest_semantic_retry() { + let recorder = Recorder::default().start(); + let gate = ComputeEffectGate::new(recorder.clone().recipient()).start(); + let stale = CorrelationId::new(); + let regenerated = CorrelationId::new(); + + gate.send(compute(stale, 10)).await.unwrap(); + gate.send(compute(regenerated, 20)).await.unwrap(); + assert!(recorder.send(Received).await.unwrap().is_empty()); + + gate.send(effects_enabled()).await.unwrap(); + assert_eq!(recorder.send(Received).await.unwrap(), vec![regenerated]); + } + + #[actix::test] + async fn terminal_e3_cancels_buffered_effects() { + let recorder = Recorder::default().start(); + let gate = ComputeEffectGate::new(recorder.clone().recipient()).start(); + + gate.send(compute(CorrelationId::new(), 10)).await.unwrap(); + gate.send(completed()).await.unwrap(); + gate.send(effects_enabled()).await.unwrap(); + + assert!(recorder.send(Received).await.unwrap().is_empty()); + } +} diff --git a/crates/multithread/src/lib.rs b/crates/multithread/src/lib.rs index c98c55dc29..951355126b 100644 --- a/crates/multithread/src/lib.rs +++ b/crates/multithread/src/lib.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +mod effect_gate; mod multithread; mod pool; mod report; diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 1adafa3e61..60f9b17bba 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -19,11 +19,9 @@ use actix::prelude::*; use actix::{Actor, Handler}; use anyhow::Result; use e3_crypto::Cipher; -use e3_events::run_once; use e3_events::trap_fut; use e3_events::EType; -use e3_events::EffectsEnabled; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, ComputeResponse, DecryptedSharesAggregationProofRequest, @@ -87,6 +85,8 @@ use num_bigint::BigInt; use rand::Rng; use tracing::{error, info}; +use crate::effect_gate::ComputeEffectGate; + /// Multithread actor pub struct Multithread { bus: BusHandle, @@ -139,21 +139,8 @@ impl Multithread { ) -> Addr { let addr = Self::new(bus.clone(), rng.clone(), cipher.clone(), task_pool, report).start(); - // Gate ComputeRequest behind EffectsEnabled — proof generation should - // not trigger during historical event replay. - bus.subscribe( - EventType::EffectsEnabled, - run_once::({ - let bus = bus.clone(); - let addr = addr.clone(); - move |_| { - bus.subscribe(EventType::ComputeRequest, addr.clone().recipient()); - info!("Multithread actor listening for events."); - Ok(()) - } - }) - .recipient(), - ); + ComputeEffectGate::attach(bus, addr.clone().recipient()); + info!("Multithread actor waiting behind the replay-safe effect gate."); addr } @@ -179,19 +166,8 @@ impl Multithread { addr.clone().into(), ); - bus.subscribe( - EventType::EffectsEnabled, - run_once::({ - let bus = bus.clone(); - let addr = addr.clone(); - move |_| { - bus.subscribe(EventType::ComputeRequest, addr.clone().recipient()); - info!("Multithread actor with ZK listening for events."); - Ok(()) - } - }) - .recipient(), - ); + ComputeEffectGate::attach(bus, addr.clone().recipient()); + info!("Multithread actor with ZK waiting behind the replay-safe effect gate."); addr } diff --git a/crates/net/src/domain/mod.rs b/crates/net/src/domain/mod.rs index dc00cf9578..65c5c9f72c 100644 --- a/crates/net/src/domain/mod.rs +++ b/crates/net/src/domain/mod.rs @@ -15,10 +15,12 @@ pub(crate) mod event_conversion; pub(crate) mod event_translation; pub(crate) mod net_buffer; pub(crate) mod net_event_batch; +mod network_status; pub(crate) mod peer_failure_tracker; pub(crate) mod sync_coordinator; pub use document_publishing::{datetime_to_instant_from_now, DocumentPublishingService}; pub use event_conversion::{EventConversionService, IncomingDocument}; pub use event_translation::EventTranslationService; +pub use network_status::{ConnectedPeer, NetworkSnapshot, NetworkStatus}; pub use sync_coordinator::{build_sync_batch, NetReadiness, ReadinessDecision, SyncBatchOutcome}; diff --git a/crates/net/src/domain/network_status.rs b/crates/net/src/domain/network_status.rs new file mode 100644 index 0000000000..caf585904f --- /dev/null +++ b/crates/net/src/domain/network_status.rs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Thread-safe, read-only projection of live libp2p connection state. +//! +//! This is operational state, not protocol state. It is rebuilt by libp2p as +//! connections change and is therefore intentionally not in the EventStore. + +use serde::Serialize; +use std::{ + collections::BTreeMap, + sync::{Arc, RwLock}, + time::{SystemTime, UNIX_EPOCH}, +}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +pub struct ConnectedPeer { + pub peer_id: String, + pub remote_address: String, + pub direction: String, + pub connections: u32, + pub connected_at_ms: u64, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)] +pub struct NetworkSnapshot { + pub configured_peers: usize, + pub connected_peers: Vec, + pub listen_addresses: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_error: Option, +} + +#[derive(Default)] +struct NetworkState { + configured_peers: usize, + connected_peers: BTreeMap, + listen_addresses: Vec, + last_error: Option, +} + +/// Cheaply cloneable handle shared by the transport and node dashboard. +#[derive(Clone, Default)] +pub struct NetworkStatus(Arc>); + +impl std::fmt::Debug for NetworkStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("NetworkStatus") + .field("snapshot", &self.snapshot()) + .finish() + } +} + +impl NetworkStatus { + pub fn new(configured_peers: usize) -> Self { + Self(Arc::new(RwLock::new(NetworkState { + configured_peers, + ..NetworkState::default() + }))) + } + + pub fn connected( + &self, + peer_id: impl Into, + remote_address: impl Into, + direction: impl Into, + connections: u32, + ) { + let peer_id = peer_id.into(); + if let Ok(mut state) = self.0.write() { + let connected_at_ms = state + .connected_peers + .get(&peer_id) + .map(|peer| peer.connected_at_ms) + .unwrap_or_else(now_ms); + state.connected_peers.insert( + peer_id.clone(), + ConnectedPeer { + peer_id, + remote_address: remote_address.into(), + direction: direction.into(), + connections, + connected_at_ms, + }, + ); + state.last_error = None; + } + } + + pub fn disconnected(&self, peer_id: &str, remaining_connections: u32) { + if let Ok(mut state) = self.0.write() { + if remaining_connections == 0 { + state.connected_peers.remove(peer_id); + } else if let Some(peer) = state.connected_peers.get_mut(peer_id) { + peer.connections = remaining_connections; + } + } + } + + pub fn listening_on(&self, address: impl Into) { + let address = address.into(); + if let Ok(mut state) = self.0.write() { + if !state.listen_addresses.contains(&address) { + state.listen_addresses.push(address); + state.listen_addresses.sort(); + } + } + } + + pub fn stopped_listening(&self, addresses: I) + where + I: IntoIterator, + S: AsRef, + { + if let Ok(mut state) = self.0.write() { + for address in addresses { + state + .listen_addresses + .retain(|current| current != address.as_ref()); + } + } + } + + pub fn record_error(&self, error: impl Into) { + if let Ok(mut state) = self.0.write() { + state.last_error = Some(error.into()); + } + } + + pub fn snapshot(&self) -> NetworkSnapshot { + self.0 + .read() + .map(|state| NetworkSnapshot { + configured_peers: state.configured_peers, + connected_peers: state.connected_peers.values().cloned().collect(), + listen_addresses: state.listen_addresses.clone(), + last_error: state.last_error.clone(), + }) + .unwrap_or_default() + } +} + +fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tracks_connections_without_resetting_first_seen_time() { + let status = NetworkStatus::new(2); + status.connected("peer-a", "/ip4/127.0.0.1", "outbound", 1); + let first_seen = status.snapshot().connected_peers[0].connected_at_ms; + + status.connected("peer-a", "/ip4/127.0.0.1", "outbound", 2); + status.listening_on("/ip4/127.0.0.1/udp/9090/quic-v1"); + let snapshot = status.snapshot(); + assert_eq!(snapshot.configured_peers, 2); + assert_eq!(snapshot.connected_peers.len(), 1); + assert_eq!(snapshot.connected_peers[0].connections, 2); + assert_eq!(snapshot.connected_peers[0].connected_at_ms, first_seen); + + status.stopped_listening(["/ip4/127.0.0.1/udp/9090/quic-v1"]); + assert!(status.snapshot().listen_addresses.is_empty()); + + status.disconnected("peer-a", 0); + assert!(status.snapshot().connected_peers.is_empty()); + } +} diff --git a/crates/net/src/lib.rs b/crates/net/src/lib.rs index f5a3df5267..f8dd13fe7e 100644 --- a/crates/net/src/lib.rs +++ b/crates/net/src/lib.rs @@ -31,6 +31,7 @@ use actors::{NetEventBuffer, NetSyncManager}; pub use actors::*; pub use cid::ContentHash; +pub use domain::{ConnectedPeer, NetworkSnapshot, NetworkStatus}; pub use keypair::*; pub use net_interface::*; pub use net_interface_handle::*; diff --git a/crates/net/src/net_interface.rs b/crates/net/src/net_interface.rs index 7829c17fe1..2977b3ae10 100644 --- a/crates/net/src/net_interface.rs +++ b/crates/net/src/net_interface.rs @@ -18,6 +18,7 @@ use crate::{ events::{IncomingResponse, OutgoingRequest, ProtocolResponse}, keypair::Libp2pKeypair, net_interface_handle::NetInterfaceHandle, + NetworkStatus, }; use anyhow::{bail, Context, Result}; use e3_events::CorrelationId; @@ -133,6 +134,8 @@ pub struct Libp2pNetInterface { cmd_tx: mpsc::Sender, /// Local receiver to process NetCommands from cmd_rx: mpsc::Receiver, + /// Live operational connection state exposed to node operators. + status: NetworkStatus, } impl Libp2pNetInterface { @@ -144,6 +147,7 @@ impl Libp2pNetInterface { ) -> Result { let (event_tx, _) = broadcast::channel(EVENT_CHANNEL_SIZE); let (cmd_tx, cmd_rx) = mpsc::channel(CMD_CHANNEL_SIZE); + let status = NetworkStatus::new(peers.len()); let swarm = libp2p::SwarmBuilder::with_existing_identity(id.into_keypair()) .with_tokio() @@ -164,11 +168,16 @@ impl Libp2pNetInterface { event_tx, cmd_tx, cmd_rx, + status, }) } pub fn handle(&self) -> NetInterfaceHandle { - NetInterfaceHandle::new(self.cmd_tx.clone(), self.event_tx.subscribe()) + NetInterfaceHandle::new( + self.cmd_tx.clone(), + self.event_tx.subscribe(), + self.status.clone(), + ) } pub async fn start(&mut self) -> Result<()> { @@ -238,7 +247,7 @@ impl Libp2pNetInterface { } // Process events event = self.swarm.select_next_some() => { - match process_swarm_event(&mut self.swarm, &event_tx, &cmd_tx, &mut correlator, &mut peer_failures, &mut peer_id_mismatches, event).await { + match process_swarm_event(&mut self.swarm, &event_tx, &cmd_tx, &mut correlator, &mut peer_failures, &mut peer_id_mismatches, &self.status, event).await { Ok(_) => (), Err(e) => error!("Error processing NetEvent: {e}") } @@ -322,6 +331,7 @@ async fn process_swarm_event( correlator: &mut Correlator, peer_failures: &mut PeerFailureTracker, peer_id_mismatches: &mut PeerFailureTracker, + status: &NetworkStatus, event: SwarmEvent, ) -> Result<()> { match event { @@ -340,6 +350,16 @@ async fn process_swarm_event( info!("Peer connected: {peer_id} (total: {total})"); } let remote_addr = endpoint.get_remote_address().clone(); + status.connected( + peer_id.to_string(), + remote_addr.to_string(), + if endpoint.is_dialer() { + "outbound" + } else { + "inbound" + }, + num_established.get(), + ); if !(should_filter_loopback(swarm) && is_loopback_addr(&remote_addr)) { swarm .behaviour_mut() @@ -362,6 +382,7 @@ async fn process_swarm_event( error, connection_id, } => { + status.record_error(format!("connection {connection_id}: {error}")); if let Some(ref failed_peer) = peer_id { if let DialError::WrongPeerId { obtained, @@ -465,6 +486,7 @@ async fn process_swarm_event( if error_str.contains("Local peer ID") || error_str.contains("aborted by peer") { debug!("{}", error_str); } else { + status.record_error(format!("incoming connection: {error_str}")); warn!("Incoming connection error: {}", error_str); } } @@ -552,6 +574,7 @@ async fn process_swarm_event( } SwarmEvent::NewListenAddr { address, .. } => { + status.listening_on(address.to_string()); trace!("Local node is listening on {address}"); } @@ -666,6 +689,7 @@ async fn process_swarm_event( cause, .. } => { + status.disconnected(&peer_id.to_string(), num_established); if num_established == 0 { let total = swarm.connected_peers().count(); info!("Peer disconnected: {peer_id} (total: {total}, cause: {cause:?})"); @@ -675,10 +699,13 @@ async fn process_swarm_event( SwarmEvent::ListenerClosed { addresses, reason, .. } => { + status.stopped_listening(addresses.iter().map(ToString::to_string)); + status.record_error(format!("listener closed: {reason:?}")); warn!("Listener closed on {addresses:?}: {reason:?}"); } SwarmEvent::ListenerError { error, .. } => { + status.record_error(format!("listener error: {error}")); error!("Listener error: {error}"); } diff --git a/crates/net/src/net_interface_handle.rs b/crates/net/src/net_interface_handle.rs index 6acda2f293..1bc77544c7 100644 --- a/crates/net/src/net_interface_handle.rs +++ b/crates/net/src/net_interface_handle.rs @@ -11,22 +11,35 @@ use tokio::{ time::sleep, }; -use crate::events::{NetCommand, NetEvent}; +use crate::{ + events::{NetCommand, NetEvent}, + NetworkStatus, +}; #[derive(Debug)] pub struct NetInterfaceHandle { tx: mpsc::Sender, rx: broadcast::Receiver, + status: NetworkStatus, } impl NetInterfaceHandle { - pub fn new(tx: mpsc::Sender, rx: broadcast::Receiver) -> Self { - Self { tx, rx } + pub fn new( + tx: mpsc::Sender, + rx: broadcast::Receiver, + status: NetworkStatus, + ) -> Self { + Self { tx, rx, status } + } + + pub fn status(&self) -> NetworkStatus { + self.status.clone() } } pub trait NetInterface: Sized { fn tx(&self) -> mpsc::Sender; fn rx(&self) -> broadcast::Receiver; + fn status(&self) -> NetworkStatus; fn handle(&self) -> NetInterfaceHandle { NetInterfaceHandle::from(self) } @@ -46,6 +59,7 @@ impl NetInterfaceHandle { Self { tx: interface.tx(), rx: interface.rx(), + status: interface.status(), } } } @@ -57,6 +71,10 @@ impl NetInterface for NetInterfaceHandle { fn tx(&self) -> mpsc::Sender { self.tx.clone() } + + fn status(&self) -> NetworkStatus { + self.status.clone() + } } /// This creates a channel bridge which allows for network events to be connected between test nodes @@ -85,6 +103,7 @@ pub fn create_channel_bridge() -> (NetInterfaceHandle, NetChannelBridge) { let handle = NetInterfaceHandle { tx: m_cmd_tx.clone(), rx: b_evt_tx.subscribe(), + status: NetworkStatus::new(0), }; let inverted = NetChannelBridge { diff --git a/crates/request/src/actors/router.rs b/crates/request/src/actors/router.rs index c776667dc7..5f1a1f3a8a 100644 --- a/crates/request/src/actors/router.rs +++ b/crates/request/src/actors/router.rs @@ -148,9 +148,13 @@ impl Handler for E3Router { } RoutingDecision::Ignore => Ok(()), RoutingDecision::AlreadyCompleted(e3_id) => Err(anyhow!( - "Received the following event to E3Id({}) despite already being completed:\n\n{:?}\n\n", + "unexpected {} for completed E3 {} (event={}, origin={}, source={:?}, block={:?})", + msg.event_type(), e3_id, - msg + msg.id(), + msg.origin_id(), + msg.source(), + msg.block(), )), RoutingDecision::Process { e3_id, @@ -291,3 +295,94 @@ impl E3RouterBuilder { Ok(addr) } } + +#[cfg(test)] +mod recovery_tests { + use super::*; + use crate::E3ContextSnapshot; + use actix::{Actor, Handler}; + use async_trait::async_trait; + use e3_data::{InMemStore, RepositoriesFactory}; + use e3_events::{hlc_factory::HlcFactory, BusHandle, EventBus, Sequencer, StoreEventRequested}; + use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }; + + struct StoreSink; + + impl Actor for StoreSink { + type Context = Context; + } + + impl Handler for StoreSink { + type Result = (); + + fn handle(&mut self, _: StoreEventRequested, _: &mut Self::Context) {} + } + + struct RecoveryExtension { + hydrations: Arc, + } + + #[async_trait] + impl E3Extension for RecoveryExtension { + fn on_event(&self, _: &mut E3Context, _: &InterfoldEvent) {} + + async fn hydrate(&self, _: &mut E3Context, _: &E3ContextSnapshot) -> Result<()> { + self.hydrations.fetch_add(1, Ordering::SeqCst); + Ok(()) + } + } + + fn test_bus() -> BusHandle { + let event_bus = EventBus::::default().start(); + let store = StoreSink.start(); + let sequencer = Sequencer::new(&event_bus, store.recipient()).start(); + BusHandle::new(event_bus, sequencer, HlcFactory::new()).enable("router-recovery-test") + } + + #[actix::test] + async fn mid_e3_context_and_completed_set_survive_hydration() -> Result<()> { + let active = E3id::new("7", 31337); + let complete = E3id::new("6", 31337); + let store = DataStore::from_in_mem(&InMemStore::new(false).start()); + let repositories = store.repositories(); + let router_store = repositories.router(); + router_store + .repositories() + .context(&active) + .write_sync(&E3ContextSnapshot { + e3_id: active.clone(), + recipients: vec!["threshold_keyshare".into()], + dependencies: vec!["meta".into()], + }) + .await?; + + let hydrations = Arc::new(AtomicUsize::new(0)); + let params = E3RouterParams { + extensions: Arc::new(vec![Box::new(RecoveryExtension { + hydrations: hydrations.clone(), + })]), + bus: test_bus(), + store: router_store, + }; + let recovered = E3Router::from_snapshot( + params, + E3RouterSnapshot { + contexts: vec![active.clone()], + completed: HashSet::from([complete.clone()]), + }, + ) + .await?; + + assert!(recovered.contexts.contains_key(&active)); + assert!(recovered.completed.contains(&complete)); + assert_eq!(hydrations.load(Ordering::SeqCst), 1); + + let roundtrip = recovered.snapshot()?; + assert_eq!(roundtrip.contexts, vec![active]); + assert_eq!(roundtrip.completed, HashSet::from([complete])); + Ok(()) + } +} diff --git a/crates/request/src/domain/routing.rs b/crates/request/src/domain/routing.rs index 5f63cf6534..aafa24cf67 100644 --- a/crates/request/src/domain/routing.rs +++ b/crates/request/src/domain/routing.rs @@ -59,6 +59,25 @@ impl RequestRouter { return RoutingDecision::Broadcast; } + // Durable observational EVM facts are consumed directly by projections + // and global observers. They describe an E3, but they do not drive its + // per-E3 actors. Routing them into a context would create contexts for + // historical observations and report expected post-settlement receipts + // as errors after teardown. + if matches!( + msg.get_data(), + InterfoldEventData::InputPublished(_) + | InterfoldEventData::RewardsDistributed(_) + | InterfoldEventData::RewardCredited(_) + | InterfoldEventData::RewardClaimed(_) + | InterfoldEventData::CommitteeFormationFailed(_) + | InterfoldEventData::CommitteeActivationChanged(_) + | InterfoldEventData::CommitteeViabilityUpdated(_) + | InterfoldEventData::EvmLogObserved(_) + ) { + return RoutingDecision::Ignore; + } + // Only process events with e3_ids. let Some(e3_id) = msg.get_e3_id() else { return RoutingDecision::Ignore; @@ -117,7 +136,7 @@ mod tests { use super::*; use e3_events::{ E3Failed, E3RequestComplete, E3Stage, E3StageChanged, FailureReason, InterfoldEvent, - PlaintextAggregated, Sequenced, Shutdown, + PlaintextAggregated, RewardCredited, Sequenced, Shutdown, }; fn e3id() -> E3id { @@ -181,6 +200,24 @@ mod tests { ); } + #[test] + fn settlement_receipt_is_not_routed_to_completed_context() { + let id = e3id(); + let mut completed = HashSet::new(); + completed.insert(id.clone()); + let msg = from_data(RewardCredited { + e3_id: id, + account: "0x01".into(), + token: "0x02".into(), + amount: "10".into(), + }); + + assert_eq!( + RequestRouter::route(&msg, &completed), + RoutingDecision::Ignore + ); + } + #[test] fn stage_changed_to_complete_ignored_when_already_completed() { // E3StageChanged(Complete) arriving from the EVM after local teardown is expected — diff --git a/crates/wasm/package.json b/crates/wasm/package.json index b1ab4e8556..615d63a9e2 100644 --- a/crates/wasm/package.json +++ b/crates/wasm/package.json @@ -72,6 +72,6 @@ "execa": "^8.0.1", "playwright": "1.52.0", "replace-in-file": "^7.2.0", - "wasm-pack": "^0.13.1" + "wasm-pack": "^0.15.0" } } diff --git a/examples/CRISP/interfold.config.yaml b/examples/CRISP/interfold.config.yaml index 3ce0226bd2..7c2b28a473 100644 --- a/examples/CRISP/interfold.config.yaml +++ b/examples/CRISP/interfold.config.yaml @@ -6,22 +6,22 @@ chains: contracts: e3_program: address: "0xc351628EB244ec633d5f21fBD6621e1a683B1181" - deploy_block: 45 + deploy_block: 40 interfold: address: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" - deploy_block: 21 + deploy_block: 16 ciphernode_registry: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 13 + deploy_block: 8 bonding_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 14 + deploy_block: 9 slashing_manager: address: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" - deploy_block: 12 + deploy_block: 7 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 10 + deploy_block: 5 program: dev: true # risc0: diff --git a/examples/CRISP/packages/crisp-zk-inputs/package.json b/examples/CRISP/packages/crisp-zk-inputs/package.json index 5f22098823..90e3185338 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/package.json +++ b/examples/CRISP/packages/crisp-zk-inputs/package.json @@ -33,6 +33,6 @@ "execa": "^8.0.1", "playwright": "1.52.0", "replace-in-file": "^7.2.0", - "wasm-pack": "^0.13.1" + "wasm-pack": "^0.15.0" } } diff --git a/packages/interfold-node/.gitignore b/packages/interfold-node/.gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/packages/interfold-node/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/interfold-node/README.md b/packages/interfold-node/README.md new file mode 100644 index 0000000000..8874cd3212 --- /dev/null +++ b/packages/interfold-node/README.md @@ -0,0 +1,20 @@ +# Interfold Node Observatory + +The local ciphernode operator dashboard. The React source is built by Vite into +`crates/dashboard/assets/`; the Rust dashboard server embeds those production assets into the +`interfold` binary. + +Protocol history comes from the node's durable EventStore. Operational tracing logs come from the +bounded log collector. These are intentionally separate: an application log is not a protocol +ledger. + +```bash +pnpm --filter @interfold/node-dashboard build +pnpm --filter @interfold/node-dashboard dev +``` + +The development server proxies `/api` to `http://127.0.0.1:9092`. Change the proxy port in +`vite.config.ts` if the node's configured dashboard port differs. + +The production server binds to loopback because event payloads and node diagnostics are detailed and +the dashboard has no authentication layer. diff --git a/packages/interfold-node/eslint.config.js b/packages/interfold-node/eslint.config.js new file mode 100644 index 0000000000..d8aad6a121 --- /dev/null +++ b/packages/interfold-node/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]) +// SPDX-License-Identifier: LGPL-3.0-only diff --git a/packages/interfold-node/index.html b/packages/interfold-node/index.html new file mode 100644 index 0000000000..a5d01cd7e2 --- /dev/null +++ b/packages/interfold-node/index.html @@ -0,0 +1,14 @@ + + + + + + + + Interfold · Node Observatory + + +
+ + + diff --git a/packages/interfold-node/package.json b/packages/interfold-node/package.json new file mode 100644 index 0000000000..c40f777253 --- /dev/null +++ b/packages/interfold-node/package.json @@ -0,0 +1,33 @@ +{ + "name": "@interfold/node-dashboard", + "private": true, + "version": "0.2.2", + "license": "LGPL-3.0-only", + "description": "Local ciphernode observability dashboard", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "typecheck": "tsc --noEmit", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "22.7.5", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.11.0", + "typescript": "5.8.3", + "typescript-eslint": "^8.46.1", + "vite": "^5.4.0" + } +} diff --git a/packages/interfold-node/src/App.css b/packages/interfold-node/src/App.css new file mode 100644 index 0000000000..9daab14642 --- /dev/null +++ b/packages/interfold-node/src/App.css @@ -0,0 +1,1779 @@ +/* SPDX-License-Identifier: LGPL-3.0-only */ + +.page { + min-height: 100vh; + display: flex; + flex-direction: column; +} +.site-head { + position: sticky; + top: 0; + z-index: 50; + border-bottom: 1px solid var(--rule); + background: color-mix(in oklab, var(--paper) 91%, transparent); + backdrop-filter: blur(16px) saturate(130%); +} +.site-head__inner { + width: min(1500px, 100%); + height: 64px; + margin: 0 auto; + padding: 0 28px; + display: flex; + align-items: center; + gap: 16px; +} +.wordmark { + width: 140px; + height: 24px; + border: 0; + padding: 0; + background: transparent; + cursor: pointer; +} +.wordmark span { + display: block; + width: 100%; + height: 100%; + background: var(--ink); + mask: url('/interfold.svg') no-repeat left center / contain; + -webkit-mask: url('/interfold.svg') no-repeat left center / contain; +} +.product-name { + padding-left: 16px; + border-left: 1px solid var(--rule); + color: var(--ink-3); + font-size: 13px; + white-space: nowrap; +} +.site-nav { + display: flex; + align-items: center; + gap: 3px; + margin-left: 24px; +} +.site-nav__link { + border: 0; + background: transparent; + padding: 7px 12px; + border-radius: 999px; + color: var(--ink-3); + font-size: 14px; + cursor: pointer; + transition: 120ms ease; +} +.site-nav__link:hover { + color: var(--ink); + background: var(--rule-soft); +} +.site-nav__link--on { + color: var(--accent-ink); + background: var(--accent-bg); +} +.node-chip { + margin-left: auto; + display: inline-flex; + align-items: center; + gap: 8px; + max-width: 230px; + padding: 6px 10px; + border: 1px solid var(--rule); + border-radius: 999px; + background: var(--paper-2); + color: var(--ink-2); + font-size: 12px; +} +.node-chip__dot, +.live-dot { + width: 7px; + height: 7px; + flex: none; + border-radius: 50%; + background: var(--accent-deep); + box-shadow: 0 0 0 4px var(--accent-bg); +} +.node-chip__dot--waiting { + background: var(--warning); + box-shadow: 0 0 0 4px var(--warning-bg); +} + +.app-main { + flex: 1; + width: min(1320px, 100%); + margin: 0 auto; + padding: 42px 28px 80px; +} +.app-main--inspector { + width: min(1600px, 100%); + padding-top: 0; + padding-bottom: 0; +} +.view-stack { + display: flex; + flex-direction: column; + gap: 28px; +} +.view-intro, +.view-title { + display: flex; + justify-content: space-between; + gap: 36px; + align-items: flex-end; +} +.view-intro > div:first-child { + max-width: 780px; +} +.eyebrow, +.section-kicker { + display: flex; + align-items: center; + gap: 9px; + margin-bottom: 10px; + color: var(--accent-deep); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.view-intro h1, +.view-title h1, +.trace-head h1, +.blank-state h1, +.loading-page h1 { + margin: 0; + color: var(--ink); + font-family: var(--serif); + font-size: clamp(34px, 4vw, 52px); + font-weight: 400; + line-height: 1.08; + letter-spacing: -0.02em; +} +.view-intro p, +.view-title p, +.trace-head p { + margin: 14px 0 0; + color: var(--ink-3); + font-size: 16px; +} +.identity-card { + width: 280px; + padding: 18px 20px; + display: flex; + flex-direction: column; + gap: 7px; + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + box-shadow: 0 12px 28px -25px rgba(20, 32, 27, 0.4); +} +.identity-card > span:first-child { + color: var(--ink-4); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.identity-card strong { + color: var(--ink); + font-size: 13px; +} +.identity-card > span:last-child { + color: var(--ink-4); + font-size: 11px; +} + +.metrics-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + overflow: hidden; +} +.metric { + min-width: 0; + padding: 22px 24px; + display: flex; + flex-direction: column; + border-right: 1px solid var(--rule); +} +.metric:last-child { + border-right: 0; +} +.metric__label { + color: var(--ink-4); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.metric__value { + margin: 7px 0 3px; + color: var(--ink); + font-family: var(--serif); + font-size: 32px; + font-weight: 400; + line-height: 1; +} +.metric__note { + color: var(--ink-3); + font-size: 12px; +} +.metric--bad .metric__value { + color: var(--danger); +} +.overview-grid { + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 20px; + align-items: start; +} +.panel { + min-width: 0; + padding: 24px; + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + box-shadow: 0 14px 36px -34px rgba(20, 32, 27, 0.45); +} +.panel--compact { + padding: 18px; +} +.panel--wide { + width: 100%; +} +.panel__head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + margin-bottom: 18px; +} +.panel__head .section-kicker { + margin-bottom: 4px; +} +.panel__head h2 { + margin: 0; + color: var(--ink); + font-family: var(--serif); + font-size: 23px; + font-weight: 400; + line-height: 1.2; +} +.panel__aside { + color: var(--ink-4); + font-size: 11px; + white-space: nowrap; +} +.health-pill, +.status-tag, +.trace-status { + display: inline-flex; + align-items: center; + border-radius: 999px; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; +} +.health-pill { + padding: 5px 9px; + background: var(--accent-bg); + color: var(--accent-deep); +} +.health-pill--bad { + color: var(--warning); + background: var(--warning-bg); +} +.peer-list, +.chain-list { + display: flex; + flex-direction: column; + gap: 8px; +} +.peer-row { + display: grid; + grid-template-columns: 9px minmax(0, 1fr) auto; + gap: 11px; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid var(--rule-soft); +} +.peer-row:last-child { + border-bottom: 0; +} +.peer-row__status { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--accent-deep); +} +.peer-row > div { + min-width: 0; + display: flex; + flex-direction: column; +} +.peer-row strong { + color: var(--ink-2); + font-size: 12px; +} +.peer-row div span { + color: var(--ink-4); + font-size: 10px; + overflow: hidden; + text-overflow: ellipsis; +} +.peer-row__direction { + color: var(--ink-3); + font-size: 10px; +} +.listen-addresses { + margin-top: 14px; + padding-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 7px; + align-items: center; + border-top: 1px solid var(--rule-soft); +} +.listen-addresses > span { + margin-right: 4px; + color: var(--ink-4); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.listen-addresses code { + padding: 3px 7px; + border-radius: 5px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 9px; +} +.chain-card { + padding: 14px; + border: 1px solid var(--rule-soft); + border-radius: var(--radius-sm); + background: #fdfdfb; +} +.chain-card__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} +.chain-card__head strong { + font-size: 13px; +} +.status-tag { + padding: 4px 7px; + background: var(--paper-3); + color: var(--ink-3); +} +.status-tag--complete { + background: var(--accent-bg); + color: var(--accent-deep); +} +.status-tag--active { + background: var(--warning-bg); + color: var(--warning); +} +.status-tag--failed { + background: var(--danger-bg); + color: var(--danger); +} +.mini-dl { + margin: 0; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px 18px; +} +.mini-dl div { + min-width: 0; + display: flex; + flex-direction: column; +} +.mini-dl dt { + color: var(--ink-4); + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.mini-dl dd { + margin: 2px 0 0; + overflow: hidden; + color: var(--ink-2); + font-size: 12px; + text-overflow: ellipsis; +} + +.event-timeline { + display: flex; + flex-direction: column; +} +.event-card { + position: relative; + border-bottom: 1px solid var(--rule-soft); + transition: + background 150ms, + box-shadow 150ms; +} +.event-card:last-child { + border-bottom: 0; +} +.event-card--located { + z-index: 1; + border-radius: 8px; + background: var(--accent-bg); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.event-card__summary { + width: 100%; + min-height: 64px; + padding: 10px 4px; + display: grid; + grid-template-columns: 22px 88px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + border: 0; + background: transparent; + text-align: left; + cursor: pointer; +} +.event-card__summary:hover { + background: #fafbf8; +} +.event-card__rail { + position: relative; + align-self: stretch; + display: flex; + justify-content: center; +} +.event-card__rail::after { + content: ''; + position: absolute; + top: 27px; + bottom: -22px; + width: 1px; + background: var(--rule); +} +.event-card:last-child .event-card__rail::after { + display: none; +} +.event-card__dot { + position: relative; + z-index: 1; + width: 8px; + height: 8px; + margin-top: 17px; + border: 2px solid var(--paper-2); + border-radius: 50%; + background: var(--accent-deep); + box-shadow: 0 0 0 1px var(--accent-soft); +} +.event-card--warn .event-card__dot { + background: var(--warning); + box-shadow: 0 0 0 1px #ead5a6; +} +.event-card--error .event-card__dot { + background: var(--danger); + box-shadow: 0 0 0 1px #e8b3b0; +} +.event-card--debug .event-card__dot { + background: var(--ink-4); + box-shadow: 0 0 0 1px var(--rule); +} +.event-card__time { + color: var(--ink-4); + font-size: 10px; +} +.event-card__identity { + min-width: 0; + display: flex; + flex-direction: column; + gap: 5px; +} +.event-card__identity strong { + overflow: hidden; + color: var(--ink-2); + font-size: 13px; + font-weight: 560; + text-overflow: ellipsis; +} +.event-card__meta { + display: flex; + flex-wrap: wrap; + gap: 7px; + align-items: center; + color: var(--ink-4); + font-size: 10px; +} +.source { + padding: 2px 5px; + border-radius: 4px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 8px; + font-weight: 700; + letter-spacing: 0.05em; +} +.source--network { + background: #f0edfa; + color: var(--network); +} +.source--evm { + background: #f9f0df; + color: var(--evm); +} +.event-card__cause { + display: flex; + align-items: center; + gap: 12px; + color: var(--ink-4); + font-size: 10px; +} +.chevron { + width: 22px; + height: 22px; + display: grid; + place-items: center; + border: 1px solid var(--rule); + border-radius: 50%; + color: var(--ink-3); + font-size: 13px; +} +.event-card__detail { + margin: 0 4px 14px 32px; + padding: 16px; + border: 1px solid var(--rule-soft); + border-radius: var(--radius-sm); + background: #fbfcfa; +} +.trace-grid { + margin: 0 0 14px; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} +.trace-grid div { + min-width: 0; + display: flex; + flex-direction: column; +} +.trace-grid dt, +.payload-head { + color: var(--ink-4); + font-size: 9px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; +} +.trace-grid dd { + margin: 3px 0 0; + color: var(--ink-2); + font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; +} +.trace-link { + padding: 0; + border: 0; + background: transparent; + color: var(--accent-deep); + cursor: pointer; + text-decoration: underline; + text-decoration-color: var(--accent-soft); + text-underline-offset: 3px; +} +.trace-successors { + max-height: 82px; + display: flex; + flex-direction: column; + gap: 3px; + overflow: auto !important; +} + +/* Causal flow graph */ +.nav-update-dot { + width: 6px; + height: 6px; + margin-left: 5px; + display: inline-block; + border-radius: 50%; + background: var(--warning); + box-shadow: 0 0 0 3px var(--warning-bg); +} +.flow-view-title { + align-items: end; +} +.flow-selector { + min-width: 250px; + display: flex; + flex-direction: column; + gap: 5px; + color: var(--ink-4); + font-size: 9px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.flow-selector select { + width: 100%; + padding: 9px 11px; + border: 1px solid var(--rule); + border-radius: 7px; + background: var(--paper-2); + color: var(--ink-2); + font: 11px var(--mono); +} +.graph-metrics { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1px; + overflow: hidden; + border: 1px solid var(--rule); + border-radius: var(--radius-sm); + background: var(--rule); +} +.graph-metrics span { + padding: 10px 14px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 10px; +} +.graph-metrics strong { + margin-right: 5px; + color: var(--ink); + font: 600 14px var(--mono); +} +.flow-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + gap: 14px; + align-items: start; +} +.flow-graph-panel { + min-width: 0; + padding: 0; + overflow: hidden; +} +.flow-legend { + min-height: 42px; + padding: 10px 14px; + display: flex; + gap: 14px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); + color: var(--ink-3); + font-size: 9px; +} +.flow-legend > span { + display: inline-flex; + gap: 5px; + align-items: center; +} +.flow-legend__hint { + margin-left: auto; + color: var(--ink-4); +} +.legend-dot { + width: 7px; + height: 7px; + display: inline-block; + border-radius: 50%; + background: var(--accent-deep); +} +.legend-dot--network { + background: var(--network); +} +.legend-dot--evm { + background: var(--evm); +} +.flow-canvas { + max-height: 720px; + overflow: auto; + background-color: #fbfcf9; + background-image: radial-gradient(var(--rule) 0.7px, transparent 0.7px); + background-size: 14px 14px; +} +.flow-canvas svg { + display: block; +} +.flow-phase-title { + fill: var(--ink-3); + font: 700 9px var(--sans); + letter-spacing: 0.08em; + text-transform: uppercase; +} +.flow-phase-rule { + stroke: var(--rule); + stroke-width: 1; +} +.flow-edge { + fill: none; + stroke: #cbd2cd; + stroke-width: 1.2; + opacity: 0.72; + transition: 120ms ease; +} +.flow-edge--selected { + stroke: var(--accent-deep); + stroke-width: 2; + opacity: 1; +} +.flow-arrow { + fill: #aeb8b1; +} +.flow-event { + cursor: pointer; + outline: none; +} +.flow-event rect { + fill: var(--paper-2); + stroke: var(--rule); + stroke-width: 1; + filter: drop-shadow(0 2px 3px rgb(20 32 27 / 0.04)); + transition: 120ms ease; +} +.flow-event circle { + fill: var(--accent-deep); +} +.flow-event--network circle { + fill: var(--network); +} +.flow-event--evm circle { + fill: var(--evm); +} +.flow-event--error rect { + fill: var(--danger-bg); + stroke: #dfaaa7; +} +.flow-event--error circle { + fill: var(--danger); +} +.flow-event:hover rect, +.flow-event:focus-visible rect, +.flow-event--selected rect { + stroke: var(--accent-deep); + stroke-width: 2; + filter: drop-shadow(0 4px 8px rgb(31 107 74 / 0.14)); +} +.flow-event__title { + fill: var(--ink); + font: 600 9px var(--sans); +} +.flow-event__meta { + fill: var(--ink-4); + font: 8px var(--mono); +} +.flow-detail { + position: sticky; + top: 82px; + max-height: calc(100vh - 110px); + overflow: auto; +} +.flow-detail h2 { + margin: 4px 0; + overflow-wrap: anywhere; + font: 22px/1.15 var(--serif); +} +.flow-detail__time { + margin: 0 0 14px; + color: var(--ink-4); + font-size: 9px; +} +.flow-detail-grid { + margin: 0 0 14px; + display: flex; + flex-direction: column; +} +.flow-detail-grid div { + padding: 8px 0; + border-bottom: 1px solid var(--rule-soft); +} +.flow-detail-grid dt { + color: var(--ink-4); + font-size: 8px; + font-weight: 700; + letter-spacing: 0.07em; + text-transform: uppercase; +} +.flow-detail-grid dd { + margin: 3px 0 0; + overflow-wrap: anywhere; + color: var(--ink-2); + font-size: 9px; +} +.flow-successor-list { + margin: 0 0 14px; + display: flex; + flex-direction: column; + gap: 4px; +} +.flow-successor-list button { + padding: 7px 8px; + display: flex; + justify-content: space-between; + border: 1px solid var(--rule-soft); + border-radius: 6px; + background: var(--paper-2); + color: var(--accent-deep); + font-size: 9px; + text-align: left; + cursor: pointer; +} + +/* Release and safe-update desk */ +.update-grid { + display: grid; + grid-template-columns: minmax(300px, 0.8fr) minmax(480px, 1.2fr); + gap: 14px; +} +.update-status { + display: flex; + gap: 18px; + align-items: center; +} +.update-status h2 { + margin: 3px 0; + font: 28px var(--serif); +} +.update-status p { + margin: 0; + color: var(--ink-3); + font-size: 11px; +} +.update-orbit { + width: 76px; + height: 76px; + flex: none; + display: grid; + place-items: center; + border: 1px solid var(--accent-soft); + border-radius: 50%; + background: var(--accent-bg); + box-shadow: inset 0 0 0 9px var(--paper-2); +} +.update-orbit i { + width: 16px; + height: 16px; + display: block; + border-radius: 50%; + background: var(--accent-deep); +} +.update-orbit--available { + border-color: #efd89d; + background: var(--warning-bg); + box-shadow: + inset 0 0 0 9px var(--paper-2), + 0 0 0 4px rgb(255 246 220 / 0.7); +} +.update-orbit--available i { + background: var(--warning); +} +.update-steps { + margin: 0; + padding: 0; + display: grid; + grid-template-columns: repeat(5, 1fr); + list-style: none; + counter-reset: updates; +} +.update-steps li { + min-width: 0; + padding: 10px; + display: flex; + flex-direction: column; + gap: 5px; + border-left: 1px solid var(--rule-soft); + counter-increment: updates; +} +.update-steps li:first-child { + border-left: 0; +} +.update-steps li::before { + content: '0' counter(updates); + color: var(--accent-deep); + font: 9px var(--mono); +} +.update-steps strong { + font-size: 10px; +} +.update-steps span { + color: var(--ink-3); + font-size: 9px; +} +.copy-command { + padding: 5px 6px; + border: 1px solid var(--accent-soft); + border-radius: 5px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 8px; + cursor: pointer; +} +.release-notes a { + color: var(--accent-deep); + font-size: 10px; + font-weight: 600; + text-decoration: none; +} +.release-date { + color: var(--ink-4); + font: 9px var(--mono); +} +.release-notes > pre { + max-height: 420px; + margin: 0; + padding: 15px; + overflow: auto; + border-radius: 8px; + background: var(--paper-3); + color: var(--ink-2); + font: 10px/1.6 var(--mono); + white-space: pre-wrap; +} + +/* Compact diagnostic distributions */ +.log-viz { + display: grid; + grid-template-columns: 210px 1fr; + gap: 24px; + align-items: center; +} +.log-viz h2 { + margin: 3px 0 0; + font: 22px var(--serif); +} +.log-viz__bars { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 10px; +} +.log-viz__bars > div { + display: grid; + grid-template-columns: 1fr auto; + gap: 5px; + color: var(--ink-4); + font-size: 8px; + text-transform: uppercase; +} +.log-viz__bars i { + height: 4px; + grid-column: 1 / -1; + overflow: hidden; + border-radius: 4px; + background: var(--rule-soft); +} +.log-viz__bar { + height: 100%; + display: block; + border-radius: inherit; + background: var(--ink-4); +} +.log-viz__bar--error { + background: var(--danger); +} +.log-viz__bar--warn { + background: var(--warning); +} +.log-viz__bar--info { + background: var(--accent-deep); +} +.log-trace-context { + padding: 8px 13px; + display: flex; + gap: 14px; + border-top: 1px solid var(--rule-soft); + background: var(--accent-bg); + color: var(--ink-3); + font-size: 8px; +} +.source-viz { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; +} +.source-viz__item { + padding: 11px 13px; + display: grid; + grid-template-columns: 1fr auto; + gap: 8px; + border: 1px solid var(--rule); + border-radius: 8px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 9px; + text-align: left; + text-transform: capitalize; + cursor: pointer; +} +.source-viz__item--selected { + border-color: var(--accent-deep); + box-shadow: 0 0 0 2px var(--accent-bg); +} +.source-viz__item i { + height: 4px; + grid-column: 1 / -1; + overflow: hidden; + border-radius: 4px; + background: var(--rule-soft); +} +.source-viz__fill { + height: 100%; + display: block; + background: var(--accent-deep); +} +.source-viz__fill--network { + background: var(--network); +} +.source-viz__fill--evm { + background: var(--evm); +} +.payload-head { + margin-bottom: 6px; +} +.payload { + max-height: 360px; + margin: 0; + padding: 13px; + overflow: auto; + border-radius: 7px; + background: var(--ink); + color: #d9fce8; + font-size: 10px; + line-height: 1.55; +} +.empty-inline { + padding: 26px 12px; + color: var(--ink-4); + font-size: 12px; + text-align: center; +} +.alert, +.stale-banner, +.failure-banner { + padding: 11px 14px; + border-radius: var(--radius-sm); + font-size: 12px; +} +.alert--warning, +.stale-banner { + background: var(--warning-bg); + color: var(--warning); +} +.stale-banner { + margin-bottom: 16px; +} + +/* E3 inspector */ +.inspector-layout { + min-height: calc(100vh - 64px); + display: grid; + grid-template-columns: 284px minmax(0, 1fr); +} +.e3-list { + position: sticky; + top: 64px; + height: calc(100vh - 64px); + margin-left: -28px; + display: flex; + flex-direction: column; + border-right: 1px solid var(--rule); + background: var(--paper-2); +} +.e3-list__head { + height: 54px; + padding: 0 16px 0 28px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--rule); + color: var(--ink-3); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; +} +.e3-list__head strong { + min-width: 24px; + padding: 2px 6px; + border-radius: 999px; + background: var(--paper-3); + text-align: center; +} +.e3-list__items { + flex: 1; + overflow: auto; + padding: 8px; +} +.e3-list__item { + width: 100%; + padding: 12px 10px; + display: grid; + grid-template-columns: 8px minmax(0, 1fr) auto; + gap: 9px; + align-items: start; + border: 0; + border-radius: 9px; + background: transparent; + text-align: left; + cursor: pointer; +} +.e3-list__item:hover { + background: var(--rule-soft); +} +.e3-list__item--selected { + background: var(--accent-bg); +} +.status-dot { + width: 7px; + height: 7px; + margin-top: 5px; + border-radius: 50%; + background: var(--warning); +} +.status-dot--complete { + background: var(--accent-deep); +} +.status-dot--failed { + background: var(--danger); +} +.e3-list__item > span:nth-child(2) { + min-width: 0; + display: flex; + flex-direction: column; +} +.e3-list__item strong { + overflow: hidden; + font-size: 11px; + text-overflow: ellipsis; +} +.e3-list__item small { + color: var(--ink-4); + font-size: 9px; + text-transform: capitalize; +} +.e3-list__time { + color: var(--ink-4); + font-size: 8px; +} +.inspector-main { + min-width: 0; + padding: 34px 0 70px 30px; +} +.trace-head { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 30px; + margin-bottom: 24px; +} +.trace-head h1 { + font-family: var(--mono); + font-size: clamp(25px, 3vw, 39px); +} +.trace-status { + gap: 7px; + padding: 7px 11px; + background: var(--warning-bg); + color: var(--warning); +} +.trace-status > span { + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; +} +.trace-status--complete { + color: var(--accent-deep); + background: var(--accent-bg); +} +.trace-status--failed { + color: var(--danger); + background: var(--danger-bg); +} +.failure-banner { + margin-bottom: 18px; + display: flex; + align-items: center; + gap: 12px; + background: var(--danger-bg); + color: var(--danger); +} +.failure-banner code { + overflow: hidden; + font-size: 10px; + text-overflow: ellipsis; + white-space: nowrap; +} +.stage-flow { + display: flex; + width: 100%; + margin-bottom: 22px; + overflow-x: auto; + padding: 3px; +} +.stage-flow__unit { + min-width: 135px; + flex: 1; + display: flex; + align-items: center; +} +.stage-node { + min-width: 120px; + flex: 1; + min-height: 84px; + padding: 11px; + display: grid; + grid-template-columns: 23px minmax(0, 1fr) auto; + gap: 7px; + align-items: start; + border: 1px solid var(--rule); + border-radius: 10px; + background: var(--paper-2); + text-align: left; + cursor: pointer; + transition: 120ms ease; +} +.stage-node:hover { + border-color: var(--accent-soft); + transform: translateY(-1px); +} +.stage-node--selected { + border-color: var(--accent-deep); + box-shadow: 0 0 0 2px var(--accent-bg); +} +.stage-node--complete { + background: #fbfefc; +} +.stage-node--active { + background: var(--accent-bg); + border-color: var(--accent-soft); +} +.stage-node--failed { + background: var(--danger-bg); + border-color: #efcbc8; +} +.stage-node__index { + color: var(--ink-4); + font-family: var(--mono); + font-size: 9px; +} +.stage-node__copy { + min-width: 0; + display: flex; + flex-direction: column; + gap: 5px; +} +.stage-node__copy strong { + font-size: 10px; + line-height: 1.25; +} +.stage-node__copy span { + color: var(--ink-4); + font-family: var(--mono); + font-size: 7px; + line-height: 1.35; +} +.stage-node__state { + color: var(--ink-4); + font-size: 12px; +} +.stage-node--complete .stage-node__state, +.stage-node--active .stage-node__state { + color: var(--accent-deep); +} +.stage-node--failed .stage-node__state { + color: var(--danger); +} +.stage-flow__connector { + width: 10px; + height: 1px; + flex: none; + background: var(--rule); +} +.stage-flow__connector--done { + background: var(--accent-deep); +} +.trace-columns { + display: grid; + grid-template-columns: minmax(520px, 1fr) 300px; + gap: 18px; + align-items: start; +} +.trace-events { + min-height: 500px; +} +.flow-note { + margin: -4px 0 12px; + padding: 9px 11px; + border-radius: 7px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 10px; +} +.trace-sidebar { + display: flex; + flex-direction: column; + gap: 12px; +} +.committee-list, +.ticket-list, +.reward-list { + display: flex; + flex-direction: column; +} +.committee-member { + padding: 9px 0; + display: grid; + grid-template-columns: 30px minmax(0, 1fr) auto; + gap: 9px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); +} +.committee-member:last-child { + border-bottom: 0; +} +.committee-member--expelled { + opacity: 0.55; +} +.committee-member__party { + width: 30px; + height: 30px; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 9px; +} +.committee-member > div { + min-width: 0; + display: flex; + flex-direction: column; +} +.committee-member strong { + overflow: hidden; + font-size: 10px; + text-overflow: ellipsis; +} +.committee-member small { + color: var(--ink-4); + font-size: 8px; +} +.ticket-list > div, +.reward-list > div { + padding: 8px 0; + display: grid; + grid-template-columns: 50px minmax(0, 1fr) auto; + gap: 8px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); +} +.ticket-list span, +.ticket-list strong, +.ticket-list small, +.reward-list strong, +.reward-list small { + overflow: hidden; + font-size: 9px; + text-overflow: ellipsis; +} +.ticket-list small, +.reward-list small { + color: var(--ink-4); +} +.reward-state { + padding: 2px 4px; + border-radius: 4px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 7px; + text-align: center; + text-transform: uppercase; +} +.reward-state--claimed { + background: var(--paper-3); + color: var(--ink-3); +} +.blank-state, +.loading-page { + min-height: 60vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} +.blank-state h1, +.loading-page h1 { + margin-top: 18px; + font-size: 34px; +} +.blank-state p, +.loading-page p { + max-width: 500px; + color: var(--ink-3); +} +.blank-state__glyph { + width: 64px; + height: 64px; + display: grid; + place-items: center; + border: 1px solid var(--accent-soft); + border-radius: 50%; + background: var(--accent-bg); + color: var(--accent-deep); + font-family: var(--serif); + font-size: 18px; +} +.loader-ring { + width: 30px; + height: 30px; + display: block; + border: 2px solid var(--rule); + border-top-color: var(--accent-deep); + border-radius: 50%; + animation: spin 750ms linear infinite; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Filters and operational logs */ +.view-title { + align-items: flex-start; +} +.view-title h1 { + font-size: 42px; +} +.filter-bar { + margin-bottom: 18px; + display: flex; + align-items: center; + gap: 9px; +} +.search-field { + min-width: 260px; + flex: 1; + height: 38px; + padding: 0 11px; + display: flex; + align-items: center; + gap: 8px; + border: 1px solid var(--rule); + border-radius: 8px; + background: #fdfdfb; +} +.search-field span { + color: var(--ink-4); +} +.search-field input { + width: 100%; + border: 0; + outline: 0; + background: transparent; + color: var(--ink); + font-size: 12px; +} +.filter-bar select { + height: 38px; + padding: 0 28px 0 10px; + border: 1px solid var(--rule); + border-radius: 8px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 11px; +} +.filter-count { + margin-left: 5px; + color: var(--ink-4); + font-size: 9px; +} +.log-table { + overflow: hidden; + border: 1px solid var(--rule-soft); + border-radius: 9px; +} +.log-table__head, +.log-row summary { + display: grid; + grid-template-columns: 90px 58px 190px minmax(0, 1fr); + gap: 10px; + align-items: center; +} +.log-table__head { + padding: 8px 12px; + background: var(--paper-3); + color: var(--ink-4); + font-size: 8px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; +} +.log-row { + border-top: 1px solid var(--rule-soft); +} +.log-row summary { + min-height: 42px; + padding: 7px 12px; + cursor: pointer; + list-style: none; +} +.log-row summary::-webkit-details-marker { + display: none; +} +.log-row summary:hover { + background: #fafbf8; +} +.log-row time, +.log-row summary > span:nth-child(3) { + color: var(--ink-4); + font-size: 9px; + overflow: hidden; + text-overflow: ellipsis; +} +.log-row strong { + overflow: hidden; + font-size: 11px; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; +} +.log-level { + width: fit-content; + padding: 2px 5px; + border-radius: 4px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 7px; + font-weight: 700; +} +.log-row--error .log-level { + background: var(--danger-bg); + color: var(--danger); +} +.log-row--warn .log-level { + background: var(--warning-bg); + color: var(--warning); +} +.log-row--info .log-level { + background: var(--accent-bg); + color: var(--accent-deep); +} +.log-row pre { + max-height: 320px; + margin: 0; + padding: 13px; + overflow: auto; + background: var(--ink); + color: #d9fce8; + font-size: 9px; +} + +.site-foot { + min-height: 54px; + margin-top: auto; + padding: 0 28px; + display: flex; + align-items: center; + justify-content: space-between; + border-top: 1px solid var(--rule); + color: var(--ink-4); + font-size: 10px; +} + +@media (max-width: 1100px) { + .metrics-grid { + grid-template-columns: 1fr 1fr; + } + .metric:nth-child(2) { + border-right: 0; + } + .metric:nth-child(-n + 2) { + border-bottom: 1px solid var(--rule); + } + .overview-grid { + grid-template-columns: 1fr; + } + .trace-columns { + grid-template-columns: 1fr; + } + .trace-sidebar { + display: grid; + grid-template-columns: repeat(3, 1fr); + } + .site-nav { + margin-left: 4px; + } + .flow-layout, + .update-grid { + grid-template-columns: 1fr; + } + .flow-detail { + position: static; + max-height: none; + } + .update-steps { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (max-width: 820px) { + .site-head__inner { + padding: 0 14px; + } + .wordmark { + width: 108px; + } + .product-name, + .node-chip span, + .site-nav__link { + display: none; + } + .site-nav { + margin-left: auto; + } + .site-nav__link--on { + display: block; + } + .node-chip { + margin-left: 0; + padding: 7px; + } + .app-main { + padding: 28px 16px 60px; + } + .app-main--inspector { + padding-top: 0; + } + .view-intro { + align-items: flex-start; + flex-direction: column; + } + .identity-card { + width: 100%; + } + .inspector-layout { + display: block; + } + .e3-list { + position: static; + width: calc(100% + 32px); + height: auto; + max-height: 230px; + margin: 0 -16px; + border-right: 0; + border-bottom: 1px solid var(--rule); + } + .e3-list__head { + padding-left: 16px; + } + .inspector-main { + padding: 24px 0 60px; + } + .trace-sidebar { + grid-template-columns: 1fr; + } + .trace-head { + flex-direction: column; + } + .event-card__summary { + grid-template-columns: 18px 70px minmax(0, 1fr); + } + .event-card__cause { + display: none; + } + .trace-grid { + grid-template-columns: 1fr 1fr; + } + .filter-bar { + align-items: stretch; + flex-direction: column; + } + .search-field { + min-width: 0; + } + .filter-count { + margin: 4px 0; + } + .log-table__head { + display: none; + } + .log-row summary { + grid-template-columns: 70px 50px minmax(0, 1fr); + } + .log-row summary strong { + grid-column: 1 / -1; + } + .flow-view-title { + align-items: stretch; + flex-direction: column; + } + .flow-selector { + width: 100%; + } + .graph-metrics { + grid-template-columns: 1fr 1fr; + } + .flow-legend__hint { + display: none !important; + } + .update-steps { + grid-template-columns: 1fr; + } + .update-steps li, + .update-steps li:first-child { + border-left: 0; + border-top: 1px solid var(--rule-soft); + } + .update-steps li:first-child { + border-top: 0; + } + .log-viz { + grid-template-columns: 1fr; + } + .log-viz__bars { + grid-template-columns: 1fr; + } +} + +@media (max-width: 520px) { + .metrics-grid { + grid-template-columns: 1fr; + } + .metric { + border-right: 0; + border-bottom: 1px solid var(--rule); + } + .metric:last-child { + border-bottom: 0; + } + .mini-dl { + grid-template-columns: 1fr; + } + .trace-grid { + grid-template-columns: 1fr; + } + .event-card__summary { + grid-template-columns: 18px minmax(0, 1fr); + } + .event-card__time { + display: none; + } + .event-card__detail { + margin-left: 18px; + } + .site-foot { + align-items: flex-start; + flex-direction: column; + justify-content: center; + gap: 3px; + } + .source-viz { + grid-template-columns: 1fr; + } +} diff --git a/packages/interfold-node/src/App.tsx b/packages/interfold-node/src/App.tsx new file mode 100644 index 0000000000..0cd954181f --- /dev/null +++ b/packages/interfold-node/src/App.tsx @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useEffect, useState } from 'react' +import { useSnapshot, useUpdates } from './api' +import type { ViewId } from './types' +import EventsView from './views/EventsView' +import FlowGraphView from './views/FlowGraphView' +import E3Inspector from './views/E3Inspector' +import LogsView from './views/LogsView' +import Overview from './views/Overview' +import UpdatesView from './views/UpdatesView' +import './App.css' + +const NAV: Array<{ id: ViewId; label: string }> = [ + { id: 'overview', label: 'Overview' }, + { id: 'e3', label: 'E3 traces' }, + { id: 'flow', label: 'Flow graph' }, + { id: 'events', label: 'Events' }, + { id: 'logs', label: 'Logs' }, + { id: 'updates', label: 'Updates' }, +] + +function initialView(): ViewId { + const hash = window.location.hash.slice(1) + return NAV.some((item) => item.id === hash) ? (hash as ViewId) : 'overview' +} + +function ShellHeader({ + view, + setView, + nodeName, + connected, + updateAvailable, +}: { + view: ViewId + setView: (view: ViewId) => void + nodeName?: string + connected: boolean + updateAvailable: boolean +}) { + const navigate = (next: ViewId) => { + window.history.replaceState(null, '', `#${next}`) + setView(next) + } + return ( +
+
+ + Node observatory + +
+ + {nodeName ?? 'Connecting…'} +
+
+
+ ) +} + +function LoadingState({ error }: { error?: string }) { + return ( +
+ +

{error ? 'The node API is unavailable' : 'Building the node picture'}

+

{error ?? 'Reading protocol history and live transport state…'}

+
+ ) +} + +export default function App() { + const [view, setView] = useState(initialView) + const [selectedE3, setSelectedE3] = useState() + const snapshot = useSnapshot() + const updates = useUpdates() + + useEffect(() => { + if (!selectedE3 && snapshot.data?.e3s[0]) setSelectedE3(snapshot.data.e3s[0].e3_id) + }, [selectedE3, snapshot.data?.e3s]) + + const connected = Boolean(snapshot.data && !snapshot.error) + return ( +
+ + {!snapshot.data ? ( + + ) : ( + <> +
+ {snapshot.error && ( +
Live refresh paused: {snapshot.error}. Showing the last successful snapshot.
+ )} + {view === 'overview' && } + {view === 'e3' && ( + + )} + {view === 'flow' && ( + + )} + {view === 'events' && } + {view === 'logs' && } + {view === 'updates' && } +
+
+ Local operator surface · bound to 127.0.0.1 + Interfold {snapshot.data.node.version} +
+ + )} +
+ ) +} diff --git a/packages/interfold-node/src/api.ts b/packages/interfold-node/src/api.ts new file mode 100644 index 0000000000..16bb91e2c6 --- /dev/null +++ b/packages/interfold-node/src/api.ts @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useEffect, useState } from 'react' +import type { DashboardSnapshot, E3Trace, EventsResponse, LogResponse, UpdateSnapshot } from './types' + +export interface QueryState { + data?: T + error?: string + loading: boolean +} + +async function getJson(path: string, signal?: AbortSignal): Promise { + const response = await fetch(path, { signal, cache: 'no-store' }) + if (!response.ok) { + const detail = await response.json().catch(() => null) + throw new Error(detail?.error ?? `${response.status} ${response.statusText}`) + } + return response.json() as Promise +} + +export function useSnapshot(intervalMs = 2_000): QueryState { + const [state, setState] = useState>({ loading: true }) + + useEffect(() => { + let active = true + let controller: AbortController | undefined + let refreshing = false + const refresh = async () => { + if (refreshing) return + refreshing = true + controller = new AbortController() + try { + const data = await getJson('/api/snapshot', controller.signal) + if (active) setState({ data, loading: false }) + } catch (error) { + if (active && !(error instanceof DOMException && error.name === 'AbortError')) { + setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) + } + } finally { + refreshing = false + } + } + void refresh() + const timer = window.setInterval(() => { + if (document.visibilityState === 'visible') void refresh() + }, intervalMs) + return () => { + active = false + controller?.abort() + window.clearInterval(timer) + } + }, [intervalMs]) + + return state +} + +export function useE3Trace(e3Id?: string, refreshKey = 0): QueryState { + const [state, setState] = useState>({ loading: Boolean(e3Id) }) + + useEffect(() => { + if (!e3Id) { + setState({ loading: false }) + return + } + const controller = new AbortController() + setState((previous) => ({ ...previous, loading: !previous.data })) + getJson(`/api/e3?e3_id=${encodeURIComponent(e3Id)}`, controller.signal) + .then((data) => setState({ data, loading: false })) + .catch((error) => { + if (!(error instanceof DOMException && error.name === 'AbortError')) { + setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) + } + }) + return () => controller.abort() + }, [e3Id, refreshKey]) + + return state +} + +export function useLogs(intervalMs = 2_000): QueryState { + const [state, setState] = useState>({ loading: true }) + useEffect(() => { + let active = true + const refresh = () => { + getJson('/api/logs?limit=2000') + .then((data) => { + if (active) setState({ data, loading: false }) + }) + .catch((error) => { + if (active) + setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) + }) + } + refresh() + const timer = window.setInterval(refresh, intervalMs) + return () => { + active = false + window.clearInterval(timer) + } + }, [intervalMs]) + return state +} + +export function useEvents(intervalMs = 2_500): QueryState { + const [state, setState] = useState>({ loading: true }) + useEffect(() => { + let active = true + const refresh = () => { + getJson('/api/events?limit=2000') + .then((data) => { + if (active) setState({ data, loading: false }) + }) + .catch((error) => { + if (active) + setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) + }) + } + refresh() + const timer = window.setInterval(refresh, intervalMs) + return () => { + active = false + window.clearInterval(timer) + } + }, [intervalMs]) + return state +} + +export function useUpdates(intervalMs = 60 * 60 * 1_000): QueryState { + const [state, setState] = useState>({ loading: true }) + useEffect(() => { + let active = true + const refresh = () => { + getJson('/api/updates') + .then((data) => { + if (active) setState({ data, loading: false }) + }) + .catch((error) => { + if (active) + setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) + }) + } + refresh() + const timer = window.setInterval(refresh, intervalMs) + return () => { + active = false + window.clearInterval(timer) + } + }, [intervalMs]) + return state +} diff --git a/packages/interfold-node/src/assets/hero.png b/packages/interfold-node/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..02251f4b956c55af2d76fd0788124d7eee2b45eb GIT binary patch literal 13057 zcmV+cGycqpP)V|)f$;Qooc7=_G zlYe)HToTQIc!$)^+J1M1y0*T%w!p~7%ux`!eRhO?c80XDxKQ*R^lUUMnA>6NT^?feoZ8xxvP32D&s-9ow zqjcM}eesrC)NeDmsf)*P7wJ|K!&xP%Zy4iI8lF)Tv2!reW)tCzg_1=PmOwd1SQfxa z8;58t!=z~Ba7CYlNWVG>he8aRPY|+-JmozNhn!#9i#77Aa_Edt$ijyCWL#=~I>~2X zZNrQ8I0=D+NWD4pq=7~(i zhfThMNw|G>g^y9pGzxX7ZSApl@tIxFcs{p#MX{Ax&XZT+cR#U+OWc@S)pkIuI}dzu zH?^Q=<(y&Vq-oxSLfc0Zmq81bjZWf}RnssBaD6}2g-XJHLcN_|*IOu>m|x$nbm(?E zyNy!Zp=RroS;?Vg*kmoJYBi!n5{_^@rA!)=t#a^;N$8GL!*DsQb}`yvEuX!G@||An znOfUZAevPrkV_qjl|<~3QRZzG&h@C9Y5z zqpNH4xqbF_InIPh)kX}Vn^5kyed|mOuq+2>M;v~KO37a#yrEn3XDqtOl=rc6_KZ!; zreo)DFVB4|>1Zd(bvMI%8uM;3!)YMYu&cG?(PE!B~y@3yKBMt|R zAf=I16tFwPsl)!jDqvYkLHaAQ+f@W1m6F5aZvwhm4JL z{_l)@b;)mDSzle2gyFP5-r1x-5X{G}ot%VyWP@vEW80!Q=f%RTfpg>B*TA^pyWYUQ z<=xPtz}WcZ!;rFl4m1D&FFHv?K~#9!?A%+fn=lXt;9!Fc#kQ;zk~gZFsH z8e5iu@c_pzX&qb8&Dum*oXwB+fm6l6gFfC|o*wgEiy6tw~&co z9Vd_4)P%wP-KwQW7|lN-znGK#?N+j24U=$982myIBM+vsiKsc*@4-rwJxuAaHKna6 zT3wi!C~a4ZKH03qU}_1bKyx0&$CaK7_%Z+Kl$)fF5^op zZApQF2TvDav!s|krTjw-8US6ep z%!VmX4luub+fseQz_D9ATJQ?iQQwD}TZz{-yo#l12a%+7bT@E(X-hyaVS-5vuXc#^ zx^w;L21;NphGVoj*{s3f4dme0y2LC=G1-7THd`#z?;tuC{^9k(dM{Rf2GOxg7Jzho z7nSZHl7?M9kdalX`)YgoKEfiae5+;$(OGeN1eqxrv!ZCVKyH>xiyNqfe8xzY8*7)H zQls8KMp)F4D>ED;idMOU^^WhVF@q>ZSmeB0y~qC~|DB648hr%Sh|*T(4q|w2l?m2+ zvBVw3@7+Mz?^Yc#+se6KM;a<=(W-I>k)$-qL2V*t}VaW`;?P4)WqI%maIDq8!oUcSYAD`}wWjkSyAVsnF65#2zQ zZ>(K*TlS(E#4y$4Zq+e^_&}d)q20hCe3!LfLYP%nQpLJ~gM6a1hJlz3)aS<9C9me| zAcmJ#>tOwBy{HoP0Sm1&_(E+S@6 zgBIFUoei8zJmdpiq8q5=OY7t@`)JWxn_&GvKVr=Zdb_pEL_j|=?f;WK^U9Q0efd#K z9q7SfJTl4pmA$jsZ5oK8@O9#!I3Cv-kL)<8SalSsp#dcpvJ}Nz#G6FC0%9|7Fi#8; zGDJXtj!&GljT3*HE@0EE>G8Se&d)*nkqe}-?`3vPl&UqK?xG z!3XJ4M-x`EuQjhBbu?ik-)rmIt=DF_N?TVMP)8Gjn)TZ2V%H|zENbeix}kOxd@0}Q z>)HuH6Ean!uS#~4g2Ne2WsMGel|h%j9*W_quQheG^JqmKhc*RYzp0wKlGjBq2VzY_ zgOv8WC1+%W=W)k)Yp_`8kfE=uiiwOZTXi8Uj9YGr$f@yJcJ;#&-Nq~sJ7anE(@;QN z=~br%7%7`isKStX|7!1?L(apl^QvPKlrHV4S+6tNVQ*R1iGdC~WMNE1$a+=rpQmcB z>wxiLIBvOnm;u*;9Y!kJdy(T4lk|8>JAm(&wEsFIF1$_*{>2ZNd$V6DS=SfrGxAv0 zzKe377JI`&o9Ljr+VnS*EwehA{f&{cKZF(6*MG5!p5MvrFA3ll{fmRG*L@6^cb;o^ z3Wm8c?Sc6$`>~VEWw(c$Y?nRO;2Q$=ulpqPtM^=1IZx;@xK0PgO7rKQ^WHVLwtgUT z%|JF{^f(VH)wLKQ%dYiu2RmchBdxL0-M?wxxul_z*{h6ZZ`>-k(vizs((vW8Lt6Z6 zY;Dt?@JWyN`O`f;&d1Mb?e%9oyRK1ql?EE5XB2(W)|D1~Rx35$H6@6)$F?)7V|zEO zI}fu0-0}8W5=6sg$fPnZ~7=tTudl?Ecb@pxbo)vni%gP-?hL|%*?62C;x6?@E`VRnJv z?fTb;k4x;TS7Cu-z%J}uy}e-pwpLQ17Q@4DC+FCdAmNKklG$`I_pyw7E{fYmw~{Fj zi?6KcVy=Wrel)EB_DWO|0CKmI|13!gBV?X`Ozp7x>?6jr`>Qz=^4ea35!$*f}) zS$i+x_k+@P2q1RFUH^ZTTk7=n?cjfR>hTq3l3SY~#w+I8SSutXGyhw;Ws~=zMQ%Vc z>$On~47Ut?P*_!TOQ&PFmLAyJieB2X4_Fd_!WxI-AY`q1Lc-oK?+qcOTzlQ?@~x@OT}*9jTVNfl@3rGvZpWI=eKg>T zZb@6YWz)J=IhP7CF|c?G62vMEG%#U}?#86$0jR4sG~i(jRd#jmn`7b(O#?N;3a;1t zhXLssmUwGhp79luw#(*V8WL0|8+E z6=YZ_O@er~$LrD_PYGc(kJgB=;yw#+Z3X6LDUZ(NcwN=B-hjdiHm!JFar%m{(5bEW z@@_VEtG$5;`EJZ|OkJ@l&G9n((w@uNFwmU%bG|s#TbcJJos!{e+bjCjrCq_}LcN!UFgKtgg7siV*7# z!}1whTRRi*-avJPu->C}Z8EiuK$#886+H_#_!btv+rsiBbv2jAJvJ+O0{#}y(%L3H zfjU-kq_-L@2XrL*ae{{qYJkD{@dw%*bkh2P&YS-0!Xt!PRz7KHV0+~j(t9W8lAVWR zt@B*DgURgEz4>WuN>o?_iKcw$?k{||Pg7{Q2o4|VmJ)mg?{VQJA<}zEr^YAAS zgGm5RT4T3p)U;yz-tfBO^kw8?IoG!IVmc+Z3m#}AOQ?5MRa>)OcU!$N^_+yK6ayn? zK>~WK0!#ysuj^oNLakm)Zvu+J)OSubX^kv!c*xgdIvs;kln!rgG4*uZ;w0mQQO4XD zO9P{GNdv!=cQ(CAL{S(%KtuV^zC&Q{%g)PoXnp^gn^>c*`E>$hLYg2HjnbVGtWLa{7zHdG1jT@B{|Dm16 z7K2(jsfG+m*Zxof)iXxu+!H5Mo-0$pkyV3VV4B@Qms46M zuBxGRV@HxU7Wwx-6CB zaU*HO<_qn$5GH>&@?nRy1{z zkik!sLfWQ)r#75)vVwCBU*r_)Q6mp?!j85{#Xqse)ApRdE$V0%I0*~e(_{)5H)`Mk z#rExC>yjhZxuL@|+#v4#<Axw$+VpV zuT;!2Vww$je$DpAW`$FX_Ab|Ip%$;&T$-lW8jS~B$>G}rd>eQG+$h9lQx4Mx0w={m zx9?T6VU`>sR}XClkAhHEShOUe8awiq zmizhL+}5UKs3}6~It7vBTig9dfQ2Q8coo+Miiaw7n~>4ybv2Ptt0^^=VqX(t*Yya9 zr`FxxFX8(v*H=+uJ#JJWIB2A(==HDYx~^zZ2nu?2`}|Wsa*f3h3ixc+U|FDtAG$Y! z*lc_7se5Oso-Cgqe0){{!8H4g$3<8!R<6JOurD;((({c$1(pwb>(#TT!sge@4>r2@ zVL7>U`0`nsWAYErezk4(Z!gMI2?UTo{J3Ajo(u4)KYIRd>BRcG4BoS3G0EXyEp@tw z%P7__?A^a>Q&AKL@ayDO9D*Qkc!NHnO9l}kpp_6hXbMppYL(X1L?njdFT|-h2<_$; zAtDZ!1Rf%|yb!qbWKd}%0b`LzBeyNy43|QO(&h2mxQLUL)|0%agVOW)6TV!&Ip^Ls z`PG2cygM8)IecQx=Fc+nqYRo4hS^^-nM_&-y8?EJXUczP=DIw(GkTJdpEdh<_STs{ z|A)4n1GKdE=Wu!!nYoZHcUQ4S&R;oDOKX2lrkdF(mK>hz<$Pp>igjOcvoRIjlN=W8 zu8Gx5(roqn8$>gEE5vy{GiGeW8Tq{vnf3hS-V=$tZkQuftUVuU8o6k&dn=Yg3)6MOIH>nlK^-2+C6BZITr~1@So?NvG#TwL)|~=1YXGMTLpS<)ziK_CSOabe z=cB#5)yz|@0i9dSo?*CX)}UP=s6)B+F@~Em(u@Q(I9J9i_V{LmMu8BfXYMh~*oPP+ z!3~xTv|(>|=n6ZOtT~C@V!z!w%18*8T2t6}U2S##rC)mekBql&VsBX;$~ByGE$oA9 z`0Wzq8p?R{4)$l*on;!cLa}Dh^Xe?owiQZt9nH1fxxh$pN9K%CtOw?u3>85L7rr!d zXs)l{TZ{xXP&U8exz?9cv~dNNibOmt*K4I$?RxqIBZ0(?Mg-9FS{*9Bc49Qc1`=sIF-rye`aNT1G@4NwXcnyc@+bw_mTsR>5< zF<2;X0QesG_pw|TonqVBhRtfqI>ty(SIu&VOXd0CrLlfp+;WH7HYjhqnu^oAY!9cB z=B6#R?Rfz9BP`dJ=@v_?70s3HxQPk+{6Y+lM85f2NF^00*^OcM0~?JOZfR9ZPYF+# zYSs}(_BUYV8{n@2a1hD^SV41bwmi2uztR;PeBgF1F-`9>`zoNss-@3LaF2sjl~>OaaVmp7PNp+UT`6@}gR%uzqHDVeEZ14{Yt?n%JeQm+t(1_u zSc}oj^{b;+rlS|ME%+LjzSI&xu0Bblxo$MJ-J$kJ?Qu_XUXh}*@*-x@ny|}wVM%Lg z3tNB`yvr*}N?ClGL;H2cglcvErIccU3(eP7>@~4nOIcI~-`P8tSQnx=jI&{9)!1}l z;gQ%_h>ZlPSV@o@Azq1R$C6ja5!^ZGh;YRhhxs58qJWo9@Bceac&yy(pET1hnn`~7@}2L0&dfPKYs$ih7m2}R!25!(hxqA(!UIw; zK4+~Jowy3=RNC6nE=ncU{LH5?*9@W24lacJlvCZXB$CYtE@>c+~H zkV=(5I&gb{xn2!~f&fs2NQgAL6`p|kyt6kpWk}iVlqIp(H;ig`{_U9yxs1jzu^ETM z7~)Rg8C-NueqTYP&U8l{DY=Y47cR zOR@U%$KQV{mkRF|4)z9Y^t3K`@p>duY&QLUFeh6VoV`a`$U@)(z!-N*5Cj<11$EZW&hJLX83TO{lJYP74rlDZQPkm@t<=U^I)x@|UnHHkdQlh?!ltZwl92rE;;^ zZuIappj4dhld1}kttYYV-j|KF1Kus zWBnzttD^00%LFK(wrwNragFub6xiV8QE2rm<`&fcR4SLFcdtLxVuN!Aal-g6dE4%k zARZ}|xeo;K{0yf7@9aua%2j5o)CPcIOc6uLHFJOcgtB5owlcNAwyAHc0QB0Dts?c@ zUemG~j_E&W7R%+x-IO4FJl8e&*2Blmp1S#RA|)geVrxvP)NHdYuxi~g&Etn?QdNK8ZDKZ?QFLU?zh30G|t9G>a_X4zk}Ygw<^$7K!GIn(Io$>(d4ODJQ2XSd%jpK zm7>ptl$a3GyB}5-%p4>Q*p#VL^B{yQMuFCM^#l#+N!Ne z5_PrJWB=@Iy+t)H`g1lX`{bm($KE5I?0c(JEYm#t{F}j!xtsbob0{xu@0TB_*>G7w0ICn zr#VoBktqHZ~XxhiKD*lcG|b;H*|Ny3P^8ceV`sfBRfrhwZ!T+MFZ!F1Bt{q$8d9i6o?~ zODj^POr}&ivSa^R^YFIq7o0giLBKCycH_aU`F6)O6JX%nPTwh~Q`eq6*0iE#Srj2^ z*_hN3%*b83zfafy60@Cp3{J({RlSaEn&E?mrxRNC9GQ7#+f=s! z0KBf-9Ny_v2VbE%aB|Di)5kNJ^t&C`4D(>t7zYUWUFtbxt+Oq=!@O7BU)}>d*R72o zFF)3jQD_lLe4is&xzyJYC1-c{8TX$RU>&>P$%)ufpez0XSAukmh!xcekg`s$c<>-q zI#zn^JU0zzF}V60)o$_gY}PQH>b2M9&8fRZa#OauglPb zeQ@pMm&=!vNgos4CluQjLMV!pfkmxK+35bi^k&=k>9h02?l+u+m0agG;(h2|Jslc-llvtEwn~*w3bx7qnvZACG<8}AGeaDVvcHbKd2>3G^ zSFPULUn-?Pmo^-_`mLZr??uNH`2=I&yajlrF{DtUxMy#Nu}z=3y7qbUA;5`)hibMR zhXL@@uKyV0-2&A@t@!xyrBnMJl&^o@Gx$&5_q6?D=ji5grd-~=?dlg;ur(_V0wjh! zA=JV^C1m+DDkOsgr<%O9ZQFg!0}pD(#PSz4Dr_EyS5$`)VIAv);4n-SFP~YtC7sH= z7&*MfpH;gd*FHbkmD#)hVxb6xjc9~`t?_{=JS+@ip_cTicXxG<=7m9& zPX+Z8IC*GSAXuGCrZDHgR$r%jyk-fctis2Kx4HvZ|B~8uC@o)m^>Hy-O!&TKA?$&n zkP2Xc54w~!=z2?^NafyL*L0V9cbYrugHBBUj`xVyZmGFR&kvk#>1J*Z~i zNTz}?IAdJ$gkqd2!Gw(%LzE!O5s4C7q4%T~e_P{+z=DNDKrG**p=U`d5yg^vp`;Zn zsU=8gd0a9s4s0FPJePWR9eH5=+O^Kks&kC-iblNqTh2&Pw*^(4384f+D8N|fewZu_ zg2ejQ)ov;ztz;NQl7yj;A`(!H!XQu_$sqY9h_IrH*}_%1{L&_YLDvO?%R5Z-t+ClW z_qERbL?HKUZ!nt+!E9S`uoh^5A|DaIHe*_gf1`E_Vq+}{&T@t$EGhMnRjJ4z2w_W8 zp+qjs7as22^&S3wY1?+}^j-I=RcCE>#|39)g(lU7v_8;?=qK(9D8-*pPdiy)P3lIblG`+?%ea| zYoD3dopYt!tKgFicfNmNi(EWE=E4hC6(r|PYtanqJlmt57YOVrr2^tfrG(eG9C##X zu&1t@%L$RIvpj!wUA z8i>Pqot#_+Cnp6L2XPcZy1ar|9MnY+7eNvK1E)@Tr#2KsXq1*>)uUCozT7L##ok?o zhA6ofP4E|b*9tAfG?uf$#}>TIR&1A!yslP8}i7w-EzW(x#9VEvx18k%Tn=-$VV zkOtUr0b2!w3t>h?#8AZl^Az*(6KCGlD;4j~yx};`#2gN1_gv=%7KVzecIRakN{f*4 zeaI>yH;-o4OGhvGTU)(quWI)-q?V*(sVesSMv|wMUQ3hLEt=lBB$KZ9TyHr>)f7o%) zPYeU<3P)*P10*7vE)nA5#{c=6-E-_>r_u4e3i!I2+UksELwDqwMeBZ9FSP$;^Ajro z_@M#_Ss$?ejoB@!wN|kbGKs(0zLo%0QpQXW#t;oC$B0MZYZ&Ej?8~fNhcCVvPo3vo zFn0WWZaPliF^8_}yzb`*f@yg0uWv6HgNI)xa=pO%Ck(C<=-60l#uD3(wXP~c7!NoX z0&^6=N`zcc90F#qt@=Rn@r!3(*1v(Tl{B!m?Mc7yIA+nEHpY{YWr$=)F7rhR1P}(v zt{YhY#;jsW6G>#xhP*B`OCk|Pf+NN;ju1rxa*HAgoGq*rvqw&xe~;t1JA31$s?GBb z*g7&@cbKo4n<`>)!UlIAgR6q&))B0KYU8r66GbFj?8Guw4E%&}Qi_lT003LtoIZei zwD~=XZmeo+yZ2Pq3KYCF-R&11^p= z@H%s+=G`}wrbJ{()Mh71#2SP3Zy3m>l1n?0N-N1Q;z6?oSxr-G(H5m4EO>~&;}VKi zfY}3w+9z>vp#d)hVuu`)vG_aaH%3b=WKMnSu&c31;<3O;bz2iD=w+o4#oBb36 z5ZCF*Gu?zjZIR0S>_%pHY2$k8D^n7Sz_K8tCDeXM+dO<#LSg%h6`~dnVG1N@T7v&e z%wEd1!k{^zfz_1BTW{!$!B%g)J^2b87!9Y>>100X1SgT7s0z$o>^lAA=Gp_cC1(h=*5Tmf8z&LGJJ>$|K^~s`z9*OWz5MFUr?>Bi?_PGBB)#psD5?>n+q{o_ zz7~ez&;t#h8l$jwGPCC&xq2YetXYQT+0F3j(`xmNGf8dj#an|p#I*pvI*kwW4iuB> z+q3_7xB8y;pLzHG-S%+UHQA zvqp;$kmGJY>lLsN4C~&TcvAS1SErTcwcw0r@wngk zShAUA1M9b#g}^pL-zH7Q#z^&j#r9F8BTVfkR&qF<=e35goTu7c|GN)0mokj4m0%~0 zXJ8j4Hc_l;HJ&uU*Iw`8d_EscJ``s0tk9mkKo^&#TYXm-EoAzTQObxa@^u~g2t#T) zJz|rE!I_?i4dCJC=B8(_pZ{YR>|V?0iCcnU;E@$239^x?SYCfNaMHN;CtHIS_zHN9 zTkQc1v@O35okiFtq5_u+5FkY55ap@pi)O?}x0D1c*qB0KpYR}>Ul+B0Vmr}Z@+%mJ|As}sis_=ROPbov@*2thpE&?!V#Qgu$snYvCZ zrkhmkMU+fSf-s8(L37fPr&M*jRs{{THb!aXQu|P9l_-vJhHvLzMGH zE?1U0H_+PmNABp9`|KzkGfrrZ%XvdGo6*<{d5m9~L7 z_^`M;X6xDo=m6LY6RfvJEvsTK1!u8d2HPx|$S}p;sRy!I zWL55Yxu~_B`OP@~(q6&W3#)~I&+MGL%GWR$#udC151^wsswhqlii;rP9jJpiI7o&Z zAb})=HY7?4HA|re3ns`%$)FuvKCFWjhb~?IE)F6dF2K5}poj-NK6Gf;hw$t3=1txY zoxQxZWrQU6K!%|~!m?~Bnw-6Rr!F3BZ{u5!LqnZTDON}Coj9^@&le)V!NYrVwS~B% zEL+>Sr@}qGwGvu|HrOo|gSt__ezN^&%~{*)a=rf7y1HujUcr`zZB<4#l@T#eN)si} z)lZA<{=tKx8E%c9>A(##6}_p+~EZpKsl5a4pj`E*;_-6`ysiv zffA!7=MT1vCz}-m4~tjVey1b2KSR4OEtLd-(_DdUqYZ74LaDkhH?KFh?%WAOP2WbX zp@zT+Dx|5_f%JQiAGvVw!oh+g3e50u!aPfMxdC=E)XB{F5IcEZhePIM- zph6Y`$Oy?JBL<8Ex(SqEhLeQ@XcrdA>a?rx+_~HLA;l14)WmmpH}_w?Pg#HBZs0eS zwypwAW?M-x+3AU-(GGWSJ=ngxUEcEZ5OsX(Qlt!MQ zn^(`S{GHkAv(8@D`EAfSYig%Cxv?z!{=w^F#y)5_d7FuKZH7qlR-#5B0bt806%D0I zT7VdVP_?q*%Rq8UR;JkD4i^RXowt+E%#V2U>TfDqzZSDZ+dR!a#T3I>-z_$q9@k|m zy5~A*m~&JWP@E7a=pc}4kVHTc4h&R;Li7d@f`|hKMLkbb^uhOakNr3&FLjlm~i5NBM< zFaYI{;cpiHCNRdE0dg*>qIm(_t?#$h=(SCw?h3rJV2*ER8{O4^3#=dO)KwklZkoqU zS8i5c%YL*y*4;FY#D=XmkQnYj%LH)?02~gSJH`Qp1XY64g>%c_K$xseI&|e)7vRoL zAqRba$G@%fSGA7X7hQk%_3NVOYVS+$leU_!&6*5uN)8#5ZBz_6ASCA;azYS-Rt@ki zg2NWz(=;t}SC(~Ibl63$5C8FPmhXqb^)5#jaJ~I{Ex3xZ!+2h8$}}h_g@Be>HZ;72 z6#y#>AY3^skuVKF#0WxFBQ()5d5_nWb?c6c>EeMM|Mh+*&wEpPyxHCq{R-Gdr-`hN zF=1sxl&mBoK+#qRLl9#CEN|Fg8>nbmsTg3a1;#M9enQ$RgWk}kp#-5wh=EF&1tl%mJln2V^8o%Qv(*=zEuO7y z=m*8?xpUn-*@h5Cl_3BK3joiGkyaScK+>|MWdMRWm@RT!Q1piAlv5hL@B6>3&GI8) zP!xBc6}ZNIpJLL%2a8Y!+(<=f%WX>_uWVxlga9!D*oYt$l0cxRDMvqfU;Kq_mLK5k z)dvqYcgLa_Lz?3HyeF)@$%$&6lI?r4I>6W#M*<)vq{?&Oqrx``d`mhpVPr> z#q078F6gw_X<=?KR>8%^t%@wbITvNMu!hKiTSkCTJkw>1!e*Y{%31#_yMf=LW7{RJ zYoC^w$6%3cBtVG5)x#{Hg6IVTh9XEcM{gQwXk!R^y95^f-hZ`d{aVa+xW1EO4wDV4 zB?JgD7*?qkvc|$nIykTvNl2x0j3Q!MXoLL^)~}d7jcYf(H8D~c+?$pKL(px>Z3`eb z04RzS6_AgFT6Pn#iZAg$Sl_j8#;6ShF%&(Fag#E2asU@@LaN;=b=Wf7sgPKhfzhBM zC@eFL8^MrnA*9&Khe*Ab@CC9*uyJGXyi(;y2>lQLJZt;ShtJi?3Yf_t`F+$hY!+Q2Ndsx=U+bjTiAy7djLji>7k%k`$9&--f<*BNA3Hy&ZrHH|4 zG5H&9cB?O#zI1_OOf0Ce%mDfQxdtp3vU%(iY6yji3iISS61XLv#z|!zI_sZqza@B+ zyu9st5-h+`H7QUKx9}3w@oU@EO}&cEzG?fu!!bLO->%zkcg;i9^j`S~=WKMnDi1f= P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/packages/interfold-node/src/components/EventTimeline.tsx b/packages/interfold-node/src/components/EventTimeline.tsx new file mode 100644 index 0000000000..5be1dda532 --- /dev/null +++ b/packages/interfold-node/src/components/EventTimeline.tsx @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useMemo, useState } from 'react' +import { absoluteTime, eventTime, json, short } from '../format' +import type { EventView } from '../types' + +function SourceBadge({ source }: { source: EventView['source'] }) { + const label = source === 'network' ? 'NET' : source.toUpperCase() + return {label} +} + +function TraceLink({ value, label, onLocate }: { value: string; label?: string; onLocate: (id: string) => void }) { + return ( + + ) +} + +function EventCard({ event, successors, onLocate }: { event: EventView; successors: EventView[]; onLocate: (id: string) => void }) { + const [open, setOpen] = useState(false) + const isRoot = event.event_id === event.causation_id + return ( +
+ + {open && ( +
+
+
+
Event
+
+ +
+
+
+
Caused by
+
+ +
+
+
+
Flow origin
+
+ +
+
+
+
Followed by
+
+ {successors.length ? ( + successors.map((successor) => ( + + )) + ) : ( + Nothing observed yet + )} +
+
+
+
Local sequence
+
#{event.seq}
+
+
+
HLC producer
+
{event.producer_fingerprint}
+
+
+
Logical order
+
{event.logical_counter}
+
+
+
Structured payload
+
{json(event.payload)}
+
+ )} +
+ ) +} + +export default function EventTimeline({ + events, + traceEvents = events, + onNavigate, + empty = 'No events observed for this stage yet.', +}: { + events: EventView[] + traceEvents?: EventView[] + onNavigate?: (id: string) => void + empty?: string +}) { + const ids = useMemo(() => new Set(events.map((event) => event.event_id)), [events]) + const successors = useMemo(() => { + const result = new Map() + for (const event of traceEvents) { + if (event.causation_id === event.event_id) continue + const following = result.get(event.causation_id) ?? [] + following.push(event) + result.set(event.causation_id, following) + } + return result + }, [traceEvents]) + const locate = (id: string) => { + if (onNavigate) { + onNavigate(id) + return + } + const target = document.getElementById(`event-${id}`) + if (target) { + target.scrollIntoView({ behavior: 'smooth', block: 'center' }) + target.classList.add('event-card--located') + window.setTimeout(() => target.classList.remove('event-card--located'), 1_400) + } else if (!ids.has(id)) { + document.getElementById('flow-origin-note')?.scrollIntoView({ behavior: 'smooth', block: 'center' }) + } + } + + if (events.length === 0) return
{empty}
+ return ( +
+ {events.map((event) => ( + + ))} +
+ ) +} diff --git a/packages/interfold-node/src/components/StageFlow.tsx b/packages/interfold-node/src/components/StageFlow.tsx new file mode 100644 index 0000000000..87e21c4943 --- /dev/null +++ b/packages/interfold-node/src/components/StageFlow.tsx @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import type { E3PhaseId, PhaseView } from '../types' + +export default function StageFlow({ + phases, + selected, + onSelect, +}: { + phases: PhaseView[] + selected: E3PhaseId + onSelect: (phase: E3PhaseId) => void +}) { + return ( +
+ {phases.map((phase, index) => ( +
+ + {index < phases.length - 1 && ( + + )} +
+ ))} +
+ ) +} diff --git a/packages/interfold-node/src/format.ts b/packages/interfold-node/src/format.ts new file mode 100644 index 0000000000..43be4be4db --- /dev/null +++ b/packages/interfold-node/src/format.ts @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +export function short(value?: string, head = 8, tail = 5): string { + if (!value || value.length <= head + tail + 2) return value ?? '—' + return `${value.slice(0, head)}…${value.slice(-tail)}` +} + +export function eventTime(timestampUs: number): string { + if (!timestampUs) return '—' + return new Intl.DateTimeFormat(undefined, { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + fractionalSecondDigits: 3, + }).format(new Date(timestampUs / 1_000)) +} + +export function absoluteTime(timestampUs: number): string { + return new Date(timestampUs / 1_000).toLocaleString() +} + +export function number(value: number): string { + return new Intl.NumberFormat().format(value) +} + +export function compactInteger(value?: string): string { + if (!value) return '—' + try { + const integer = BigInt(value) + if (integer < 1_000_000n) return integer.toLocaleString() + const digits = integer.toString().length + const unit = digits > 18 ? 'e18+' : digits > 12 ? 'T+' : digits > 9 ? 'B+' : 'M+' + return `${integer.toString().slice(0, 4)}… ${unit}` + } catch { + return value + } +} + +export function json(value: unknown): string { + return JSON.stringify(value, null, 2) +} diff --git a/packages/interfold-node/src/index.css b/packages/interfold-node/src/index.css new file mode 100644 index 0000000000..3eddf0c564 --- /dev/null +++ b/packages/interfold-node/src/index.css @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-3.0-only */ + +:root { + --ink: #14201b; + --ink-2: #2c3833; + --ink-3: #5b6864; + --ink-4: #8b9893; + --rule: #e3e7e2; + --rule-soft: #eef1ec; + --paper: #f7f5ee; + --paper-2: #ffffff; + --paper-3: #f0ede4; + --accent-bg: #e8faf0; + --accent-soft: #cdeede; + --accent-deep: #1f6b4a; + --accent-ink: #163d2c; + --danger: #a93632; + --danger-bg: #fcebea; + --warning: #906319; + --warning-bg: #fff6dc; + --network: #5d4f99; + --evm: #8d5c16; + --serif: Georgia, 'Times New Roman', serif; + --sans: 'Geist', 'Söhne', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; + --mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace; + --radius: 14px; + --radius-sm: 9px; + color: var(--ink); + background: var(--paper); + font: 16px/1.5 var(--sans); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +* { + box-sizing: border-box; +} +html { + min-width: 320px; + background: var(--paper); +} +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: var(--paper); +} +button, +input, +select { + font: inherit; +} +button { + color: inherit; +} +code, +pre, +.mono { + font-family: var(--mono); + font-feature-settings: 'tnum'; + letter-spacing: -0.015em; +} +button:focus-visible, +input:focus-visible, +select:focus-visible { + outline: 2px solid var(--accent-deep); + outline-offset: 2px; +} +::selection { + background: var(--accent-soft); + color: var(--accent-ink); +} diff --git a/packages/interfold-node/src/main.tsx b/packages/interfold-node/src/main.tsx new file mode 100644 index 0000000000..1168a242bb --- /dev/null +++ b/packages/interfold-node/src/main.tsx @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/packages/interfold-node/src/types.ts b/packages/interfold-node/src/types.ts new file mode 100644 index 0000000000..6a9b658ac6 --- /dev/null +++ b/packages/interfold-node/src/types.ts @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +export type ViewId = 'overview' | 'e3' | 'flow' | 'events' | 'logs' | 'updates' +export type E3PhaseId = + | 'request' + | 'committee' + | 'dkg_setup' + | 'dkg_shares' + | 'key_publication' + | 'computation' + | 'decryption' + | 'settlement' +export type EventSeverity = 'debug' | 'info' | 'warn' | 'error' +export type EventSource = 'local' | 'network' | 'evm' + +export interface DashboardChain { + id: number + name: string +} + +export interface DashboardRuntime { + node_name: string + address: string + peer_id: string + quic_port: number + dashboard_port: number + version: string + chains: DashboardChain[] +} + +export interface ConnectedPeer { + peer_id: string + remote_address: string + direction: string + connections: number + connected_at_ms: number +} + +export interface NetworkSnapshot { + configured_peers: number + connected_peers: ConnectedPeer[] + listen_addresses: string[] + last_error?: string +} + +export interface RewardView { + account: string + token?: string + amount: string + claimed: boolean +} + +export interface ChainOperatorView { + chain_id: number + registered_nodes: number + active_nodes: number + operator_registered: boolean + operator_active: boolean + ticket_balance?: string + license_bond?: string + exit_unlock_at?: number + rewards_credited: RewardView[] +} + +export interface ProtocolOverview { + chains: ChainOperatorView[] + e3_total: number + e3_active: number + e3_completed: number + e3_failed: number + events_observed: number +} + +export interface OperatorChainStatus { + chain_id: number + chain_name: string + registered_nodes: string + active_nodes: string + operator_registered: boolean + operator_active: boolean + exit_in_progress: boolean + ticket_balance: string + available_tickets: string + license_bond: string +} + +export interface OperatorStatusSnapshot { + chains: OperatorChainStatus[] + error?: string + updated_at_ms: number +} + +export interface EventView { + seq: number + aggregate_id: number + timestamp_us: number + logical_counter: number + producer_fingerprint: string + block?: number + source: EventSource + producer: string + event_type: string + e3_id?: string + phase?: E3PhaseId + severity: EventSeverity + event_id: string + causation_id: string + origin_id: string + payload: unknown +} + +export interface E3Summary { + e3_id: string + chain_id: number + status: 'active' | 'complete' | 'failed' + current_phase: E3PhaseId + event_count: number + error_count: number + warning_count: number + committee_size: number + first_seen_us: number + last_seen_us: number +} + +export interface SourceCounts { + local: number + net: number + evm: number +} + +export interface PhaseView { + id: E3PhaseId + label: string + state: 'pending' | 'active' | 'complete' | 'failed' + event_count: number + sources: SourceCounts + errors: number + warnings: number +} + +export interface CommitteeMemberView { + address: string + party_id: number + score?: string + expelled: boolean +} + +export interface TicketView { + node: string + ticket_id: number + score: string +} + +export interface E3Trace extends E3Summary { + phases: PhaseView[] + committee: CommitteeMemberView[] + tickets: TicketView[] + rewards: RewardView[] + failure?: unknown + events: EventView[] +} + +export interface DashboardSnapshot { + node: DashboardRuntime + network: NetworkSnapshot + protocol: ProtocolOverview + operator: OperatorStatusSnapshot + e3s: E3Summary[] + recent_events: EventView[] +} + +export interface EventsResponse { + events: EventView[] +} + +export interface LogEntry { + seq: number + timestamp_ms: number + level: string + target: string + message: string + node: string + fields?: Record +} + +export interface LogResponse { + entries: LogEntry[] + next_cursor: number + oldest_cursor: number + total_stored: number +} + +export interface ReleaseInfo { + tag: string + name: string + url: string + published_at?: string + notes: string +} + +export interface UpdateSnapshot { + current_version: string + latest?: ReleaseInfo + update_available: boolean + releases_url: string + checked_at_ms: number + error?: string +} diff --git a/packages/interfold-node/src/views/E3Inspector.tsx b/packages/interfold-node/src/views/E3Inspector.tsx new file mode 100644 index 0000000000..62c4818ff2 --- /dev/null +++ b/packages/interfold-node/src/views/E3Inspector.tsx @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useEffect, useMemo, useState } from 'react' +import { useE3Trace } from '../api' +import EventTimeline from '../components/EventTimeline' +import StageFlow from '../components/StageFlow' +import { compactInteger, eventTime, short } from '../format' +import type { E3PhaseId, E3Summary } from '../types' + +function E3List({ e3s, selected, onSelect }: { e3s: E3Summary[]; selected?: string; onSelect: (id: string) => void }) { + return ( + + ) +} + +export default function E3Inspector({ + e3s, + selected, + onSelect, + refreshKey, +}: { + e3s: E3Summary[] + selected?: string + onSelect: (id: string) => void + refreshKey: number +}) { + const trace = useE3Trace(selected, refreshKey) + const [phase, setPhase] = useState('request') + const [focus, setFocus] = useState<{ id: string; nonce: number }>() + const currentPhase = trace.data?.current_phase + + useEffect(() => { + if (currentPhase) setPhase(currentPhase) + }, [selected, currentPhase]) + + const stageEvents = useMemo(() => trace.data?.events.filter((event) => event.phase === phase) ?? [], [phase, trace.data?.events]) + + useEffect(() => { + if (!focus) return + const frame = window.requestAnimationFrame(() => { + const target = document.getElementById(`event-${focus.id}`) + target?.scrollIntoView({ behavior: 'smooth', block: 'center' }) + target?.classList.add('event-card--located') + window.setTimeout(() => target?.classList.remove('event-card--located'), 1_400) + }) + return () => window.cancelAnimationFrame(frame) + }, [focus, phase]) + + const navigateToEvent = (id: string) => { + const target = trace.data?.events.find((event) => event.event_id === id) + if (!target) return + if (target.phase) setPhase(target.phase) + setFocus({ id, nonce: Date.now() }) + } + return ( +
+ +
+ {!selected ? ( +
+ E3 +

No computation selected

+

Select an E3 to inspect its complete local flow.

+
+ ) : trace.error && !trace.data ? ( +
+

Couldn’t load this trace

+

{trace.error}

+
+ ) : !trace.data ? ( +
+ +

Rebuilding the trace

+

Reading durable events from this node…

+
+ ) : ( + <> +
+
+ Encrypted execution environment +

E3 {trace.data.e3_id}

+

+ Chain {trace.data.chain_id} · first observed {eventTime(trace.data.first_seen_us)} · {trace.data.event_count} events +

+
+ + + {trace.data.status} + +
+ + {trace.data.failure != null && ( +
+ Failure localized + {JSON.stringify(trace.data.failure)} +
+ )} + + +
+
+
+
+ Selected stage +

{trace.data.phases.find((item) => item.id === phase)?.label}

+
+ {stageEvents.length} events +
+
+ Every row carries its durable event, cause, origin, and immediate successors. Causal links jump across stages while + preserving this E3 trace. +
+ +
+ + +
+ + )} +
+
+ ) +} diff --git a/packages/interfold-node/src/views/EventsView.tsx b/packages/interfold-node/src/views/EventsView.tsx new file mode 100644 index 0000000000..31e2ec0af7 --- /dev/null +++ b/packages/interfold-node/src/views/EventsView.tsx @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useMemo, useState } from 'react' +import { useEvents } from '../api' +import EventTimeline from '../components/EventTimeline' +import type { EventSeverity, EventSource } from '../types' + +export default function EventsView() { + const query = useEvents() + const events = useMemo(() => query.data?.events ?? [], [query.data?.events]) + const [search, setSearch] = useState('') + const [source, setSource] = useState('all') + const [severity, setSeverity] = useState('all') + const sourceCounts = useMemo( + () => ({ + local: events.filter((event) => event.source === 'local').length, + network: events.filter((event) => event.source === 'network').length, + evm: events.filter((event) => event.source === 'evm').length, + }), + [events], + ) + const sourceTotal = Math.max(1, sourceCounts.local + sourceCounts.network + sourceCounts.evm) + const filtered = useMemo(() => { + const needle = search.toLowerCase().trim() + return events.filter((event) => { + if (source !== 'all' && event.source !== source) return false + if (severity !== 'all' && event.severity !== severity) return false + return ( + !needle || + `${event.event_type} ${event.e3_id ?? ''} ${event.producer} ${JSON.stringify(event.payload)}`.toLowerCase().includes(needle) + ) + }) + }, [events, search, severity, source]) + return ( +
+
+
+ EventStore +

Raw protocol activity

+

The latest durable facts observed by this node across local, network, and EVM sources.

+
+
+
+ {(['local', 'network', 'evm'] as const).map((name) => ( + + ))} +
+
+ {query.error &&
Live refresh paused: {query.error}
} +
+ + + + + {filtered.length} / {events.length} + +
+ +
+
+ ) +} diff --git a/packages/interfold-node/src/views/FlowGraphView.tsx b/packages/interfold-node/src/views/FlowGraphView.tsx new file mode 100644 index 0000000000..eac000dc2d --- /dev/null +++ b/packages/interfold-node/src/views/FlowGraphView.tsx @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useEffect, useMemo, useState } from 'react' +import { useE3Trace } from '../api' +import { absoluteTime, json, short } from '../format' +import type { E3PhaseId, E3Summary, EventView } from '../types' + +const PHASES: E3PhaseId[] = [ + 'request', + 'committee', + 'dkg_setup', + 'dkg_shares', + 'key_publication', + 'computation', + 'decryption', + 'settlement', +] +const COLUMN_WIDTH = 222 +const NODE_WIDTH = 170 +const NODE_HEIGHT = 48 +const ROW_HEIGHT = 66 + +interface GraphNode { + event: EventView + x: number + y: number +} + +function eventTitle(value: string): string { + return value.length > 24 ? `${value.slice(0, 22)}…` : value +} + +function E3FlowGraph({ events, onSelect, selected }: { events: EventView[]; onSelect: (event: EventView) => void; selected?: string }) { + const graph = useMemo(() => { + const rows = new Map() + const nodes: GraphNode[] = events.map((event) => { + const phase = event.phase ?? 'request' + const row = rows.get(phase) ?? 0 + rows.set(phase, row + 1) + return { + event, + x: 30 + PHASES.indexOf(phase) * COLUMN_WIDTH, + y: 74 + row * ROW_HEIGHT, + } + }) + const byId = new Map(nodes.map((node) => [node.event.event_id, node])) + const edges = nodes.flatMap((node) => { + if (node.event.causation_id === node.event.event_id) return [] + const cause = byId.get(node.event.causation_id) + return cause ? [{ cause, effect: node }] : [] + }) + const maxRows = Math.max(1, ...PHASES.map((phase) => rows.get(phase) ?? 0)) + return { nodes, edges, byId, height: 100 + maxRows * ROW_HEIGHT } + }, [events]) + + return ( +
+ + + + + + + {PHASES.map((phase, index) => ( + + {phase.replaceAll('_', ' ')} + + + ))} + + {graph.edges.map(({ cause, effect }) => { + const startX = cause.x + NODE_WIDTH + const startY = cause.y + NODE_HEIGHT / 2 + const endX = effect.x + const endY = effect.y + NODE_HEIGHT / 2 + const bend = Math.max(25, Math.abs(endX - startX) / 2) + const highlighted = selected === cause.event.event_id || selected === effect.event.event_id + return ( + + ) + })} + + + {graph.nodes.map(({ event, x, y }) => ( + onSelect(event)} + onKeyDown={(keyEvent) => { + if (keyEvent.key === 'Enter' || keyEvent.key === ' ') onSelect(event) + }} + > + + + + {eventTitle(event.event_type)} + + + {event.source.toUpperCase()} · #{event.seq} · {short(event.event_id, 5, 3)} + + + ))} + + +
+ ) +} + +export default function FlowGraphView({ + e3s, + selectedE3, + onSelectE3, + refreshKey, +}: { + e3s: E3Summary[] + selectedE3?: string + onSelectE3: (id: string) => void + refreshKey: number +}) { + const trace = useE3Trace(selectedE3, refreshKey) + const [selectedEvent, setSelectedEvent] = useState() + + useEffect(() => { + setSelectedEvent(trace.data?.events.at(-1)) + }, [selectedE3, trace.data?.events]) + + const edges = + trace.data?.events.filter((event) => trace.data?.events.some((candidate) => candidate.event_id === event.causation_id)).length ?? 0 + const roots = (trace.data?.events.length ?? 0) - edges + const successors = selectedEvent + ? (trace.data?.events.filter((event) => event.causation_id === selectedEvent.event_id && event.event_id !== selectedEvent.event_id) ?? + []) + : [] + + return ( +
+
+
+ Causal topology +

E3 event flow graph

+

Every edge means “caused by.” Select any node to inspect its stage, source, context, successors, and failure payload.

+
+ +
+ + {!trace.data ? ( +
+ +

{trace.error ?? 'Reconstructing the causal graph…'}

+
+ ) : ( + <> +
+ + {trace.data.events.length} events + + + {edges} causal edges + + + {roots} observed roots + + + {trace.data.error_count} failures + +
+
+
+
+ + + Local + + + + Network + + + + EVM + + Scroll horizontally and vertically · click a node +
+ +
+ +
+ + )} +
+ ) +} diff --git a/packages/interfold-node/src/views/LogsView.tsx b/packages/interfold-node/src/views/LogsView.tsx new file mode 100644 index 0000000000..7fb3a9fb27 --- /dev/null +++ b/packages/interfold-node/src/views/LogsView.tsx @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useMemo, useState } from 'react' +import { useLogs } from '../api' +import { short } from '../format' + +export default function LogsView() { + const logs = useLogs() + const [search, setSearch] = useState('') + const [level, setLevel] = useState('all') + const levelCounts = useMemo(() => { + const counts = new Map() + for (const entry of logs.data?.entries ?? []) counts.set(entry.level.toLowerCase(), (counts.get(entry.level.toLowerCase()) ?? 0) + 1) + const maximum = Math.max(1, ...counts.values()) + return ['error', 'warn', 'info', 'debug', 'trace'].map((name) => ({ name, count: counts.get(name) ?? 0, maximum })) + }, [logs.data?.entries]) + const entries = useMemo(() => { + const needle = search.toLowerCase().trim() + return (logs.data?.entries ?? []) + .filter((entry) => { + if (level !== 'all' && entry.level.toLowerCase() !== level) return false + return !needle || `${entry.message} ${entry.target} ${JSON.stringify(entry.fields ?? {})}`.toLowerCase().includes(needle) + }) + .reverse() + }, [level, logs.data?.entries, search]) + return ( +
+
+
+ Operational diagnostics +

Ciphernode logs

+

+ Structured tracing output for networking, RPC, proving, startup, and resource diagnostics. Protocol facts live in EventStore. +

+
+
+
+
+ Current memory window +

Signal distribution

+
+
+ {levelCounts.map((item) => ( +
+ {item.name} + + + + {item.count} +
+ ))} +
+
+
+
+ + + + {entries.length} visible · {logs.data?.total_stored ?? 0} stored + +
+ {logs.error &&
{logs.error}
} +
+
+ Time + Level + Target + Message +
+ {entries.map((entry) => ( +
+ + + {entry.level} + + {short(entry.target, 24, 10)} + + {entry.message} + + {(entry.fields?.e3_id || entry.fields?.event_id || entry.fields?.stage) && ( +
+ {entry.fields.stage && ( + + stage {String(entry.fields.stage)} + + )} + {entry.fields.e3_id && ( + + E3 {String(entry.fields.e3_id)} + + )} + {entry.fields.event_id && ( + + event {String(entry.fields.event_id)} + + )} +
+ )} +
{JSON.stringify(entry.fields ?? {}, null, 2)}
+
+ ))} + {!entries.length &&
No operational logs match these filters.
} +
+
+
+ ) +} diff --git a/packages/interfold-node/src/views/Overview.tsx b/packages/interfold-node/src/views/Overview.tsx new file mode 100644 index 0000000000..e6acfb52f4 --- /dev/null +++ b/packages/interfold-node/src/views/Overview.tsx @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import EventTimeline from '../components/EventTimeline' +import { compactInteger, number, short } from '../format' +import type { DashboardSnapshot } from '../types' + +interface OperatorCard { + chainId: number + chainName: string + registeredNodes: string + activeNodes: string + registered: boolean + active: boolean + exitInProgress: boolean + ticketBalance?: string + availableTickets?: string + licenseBond?: string + rewardCredits: number +} + +function sumIntegerStrings(values: string[]): string { + try { + return values.reduce((sum, value) => sum + BigInt(value), 0n).toLocaleString() + } catch { + return '—' + } +} + +function Metric({ label, value, note, tone }: { label: string; value: string | number; note: string; tone?: string }) { + return ( +
+ {label} + {typeof value === 'number' ? number(value) : value} + {note} +
+ ) +} + +export default function Overview({ snapshot }: { snapshot: DashboardSnapshot }) { + const { node, network, operator, protocol } = snapshot + const liveChains = new Map(operator.chains.map((chain) => [chain.chain_id, chain])) + const projectedChains = new Map(protocol.chains.map((chain) => [chain.chain_id, chain])) + const chainIds = new Set([ + ...node.chains.map((chain) => chain.id), + ...operator.chains.map((chain) => chain.chain_id), + ...protocol.chains.map((chain) => chain.chain_id), + ]) + const operatorCards: OperatorCard[] = [...chainIds] + .sort((left, right) => left - right) + .map((chainId) => { + const live = liveChains.get(chainId) + const projected = projectedChains.get(chainId) + return { + chainId, + chainName: live?.chain_name ?? node.chains.find((chain) => chain.id === chainId)?.name ?? `Chain ${chainId}`, + registeredNodes: live?.registered_nodes ?? String(projected?.registered_nodes ?? 0), + activeNodes: live?.active_nodes ?? String(projected?.active_nodes ?? 0), + registered: live?.operator_registered ?? projected?.operator_registered ?? false, + active: live?.operator_active ?? projected?.operator_active ?? false, + exitInProgress: live?.exit_in_progress ?? projected?.exit_unlock_at !== undefined, + ticketBalance: live?.ticket_balance ?? projected?.ticket_balance, + availableTickets: live?.available_tickets, + licenseBond: live?.license_bond ?? projected?.license_bond, + rewardCredits: projected?.rewards_credited.length ?? 0, + } + }) + const totalNodes = sumIntegerStrings(operatorCards.map((chain) => chain.registeredNodes)) + const totalActive = sumIntegerStrings(operatorCards.map((chain) => chain.activeNodes)) + return ( +
+
+
+
+ Node online · {node.node_name} +
+

Everything your ciphernode knows, in one place.

+

Live transport health, durable protocol history, and causal E3 traces projected from this node’s own EventStore.

+
+
+ Operator identity + + {short(node.address, 12, 8)} + + + {short(node.peer_id, 13, 8)} + +
+
+ +
+ + + + +
+ +
+
+
+
+ Transport +

Connected nodes

+
+ + {network.last_error ? 'Attention' : 'Healthy'} + +
+ {network.connected_peers.length ? ( +
+ {network.connected_peers.map((peer) => ( +
+ +
+ {short(peer.peer_id, 12, 7)} + {short(peer.remote_address, 22, 10)} +
+ + {peer.direction} · {peer.connections} conn. + +
+ ))} +
+ ) : ( +
No live peer connections. The node will keep dialing its configured peers.
+ )} + {network.last_error &&
{network.last_error}
} +
+ Listening + {network.listen_addresses.map((address) => ( + {address} + ))} + {!network.listen_addresses.length && UDP / QUIC {node.quic_port}} +
+
+ +
+
+
+ On-chain position +

Operator state

+
+
+
+ {operatorCards.map((chain) => ( +
+
+ {chain.chainName} + + {chain.exitInProgress ? 'Exit queued' : chain.active ? 'Active' : chain.registered ? 'Registered' : 'Not registered'} + +
+
+
+
Available tickets
+
+ {compactInteger(chain.availableTickets)} +
+
+
+
Ticket balance
+
+ {compactInteger(chain.ticketBalance)} +
+
+
+
License bond
+
+ {compactInteger(chain.licenseBond)} +
+
+
+
Network
+
+ {chain.activeNodes} / {chain.registeredNodes} active +
+
+
+
Rewards
+
{chain.rewardCredits} credits
+
+
+
+ ))} + {!operatorCards.length &&
Waiting for on-chain registry state to sync.
} + {operator.error &&
{operator.error}
} +
+
+
+ +
+
+
+ Live activity +

Latest protocol events

+
+ Newest first +
+ +
+
+ ) +} diff --git a/packages/interfold-node/src/views/UpdatesView.tsx b/packages/interfold-node/src/views/UpdatesView.tsx new file mode 100644 index 0000000000..f51f8ad503 --- /dev/null +++ b/packages/interfold-node/src/views/UpdatesView.tsx @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import { useState } from 'react' +import type { QueryState } from '../api' +import type { UpdateSnapshot } from '../types' + +export default function UpdatesView({ updates, activeE3s }: { updates: QueryState; activeE3s: number }) { + const [copied, setCopied] = useState(false) + const copyUpdate = () => { + void navigator.clipboard.writeText('interfoldup update').then(() => { + setCopied(true) + window.setTimeout(() => setCopied(false), 1_500) + }) + } + const data = updates.data + return ( +
+
+
+ Operator continuity +

Updates and release desk

+

Release awareness and a deliberately manual, auditable upgrade path. The dashboard never replaces a running binary itself.

+
+
+ +
+
+ + + +
+ Installed +

Interfold {data?.current_version ?? '…'}

+

+ {data?.update_available + ? `${data.latest?.tag ?? 'A newer release'} is available.` + : data?.latest + ? `This node matches the latest stable release, ${data.latest.tag}.` + : 'Checking the release channel…'} +

+ {(data?.error || updates.error) &&
{data?.error ?? updates.error}
} +
+
+ +
+
+
+ Runbook +

Safe update sequence

+
+ + {activeE3s ? `${activeE3s} active E3` : 'Safe window'} + +
+
    +
  1. + Wait for active work. + + {activeE3s + ? 'Do not stop yet; let the active E3s settle unless this is an emergency.' + : 'No active E3 is currently projected.'} + +
  2. +
  3. + Stop gracefully. + Send SIGTERM or Ctrl+C and wait for “Graceful shutdown complete.” +
  4. +
  5. + Install the release. + +
  6. +
  7. + Restart identically. + Use the same service account, configuration, database, and event-log paths. +
  8. +
  9. + Verify recovery. + Confirm version, peers, operator state, and the resumed E3 stage before leaving the node unattended. +
  10. +
+
+
+ +
+
+
+ Release channel +

{data?.latest?.name ?? 'Latest stable release'}

+
+ + Open on GitHub ↗ + +
+ {data?.latest?.published_at &&

Published {new Date(data.latest.published_at).toLocaleString()}

} +
{data?.latest?.notes || 'Release notes will appear here when GitHub is reachable.'}
+
+
+ ) +} diff --git a/packages/interfold-node/tsconfig.app.json b/packages/interfold-node/tsconfig.app.json new file mode 100644 index 0000000000..5c5ae93ef5 --- /dev/null +++ b/packages/interfold-node/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "esnext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/interfold-node/tsconfig.json b/packages/interfold-node/tsconfig.json new file mode 100644 index 0000000000..d32ff68200 --- /dev/null +++ b/packages/interfold-node/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/packages/interfold-node/tsconfig.node.json b/packages/interfold-node/tsconfig.node.json new file mode 100644 index 0000000000..d3c52ea64c --- /dev/null +++ b/packages/interfold-node/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/interfold-node/vite.config.ts b/packages/interfold-node/vite.config.ts new file mode 100644 index 0000000000..3525c32ded --- /dev/null +++ b/packages/interfold-node/vite.config.ts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +const here = path.dirname(fileURLToPath(import.meta.url)) + +export default defineConfig({ + base: '/assets/', + plugins: [react()], + publicDir: path.resolve(here, '../interfold-dashboard/src/assets'), + build: { + outDir: path.resolve(here, '../../crates/dashboard/assets'), + emptyOutDir: true, + rollupOptions: { + output: { + entryFileNames: 'app.js', + chunkFileNames: '[name].js', + assetFileNames: (asset) => (asset.name?.endsWith('.css') ? 'app.css' : '[name][extname]'), + }, + }, + }, + server: { + port: 5174, + proxy: { + '/api': 'http://127.0.0.1:9092', + }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da33e174cf..1c4079b5c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: specifier: ^7.2.0 version: 7.2.0 wasm-pack: - specifier: ^0.13.1 - version: 0.13.1 + specifier: ^0.15.0 + version: 0.15.0 docs: dependencies: @@ -386,8 +386,8 @@ importers: specifier: ^7.2.0 version: 7.2.0 wasm-pack: - specifier: ^0.13.1 - version: 0.13.1 + specifier: ^0.15.0 + version: 0.15.0 packages/interfold-config: dependencies: @@ -520,7 +520,7 @@ importers: version: 3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat-gas-reporter: specifier: ^2.2.0 - version: 2.3.0(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + version: 2.3.0(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -611,6 +611,52 @@ importers: specifier: 5.8.3 version: 5.8.3 + packages/interfold-node: + dependencies: + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + devDependencies: + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@types/node': + specifier: 22.7.5 + version: 22.7.5 + '@types/react': + specifier: ^18.3.3 + version: 18.3.26 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.7(@types/react@18.3.26) + '@vitejs/plugin-react': + specifier: ^4.3.1 + version: 4.7.0(vite@5.4.21(@types/node@22.7.5)) + eslint: + specifier: ^9.39.1 + version: 9.39.1(jiti@1.21.7) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react-refresh: + specifier: ^0.4.14 + version: 0.4.24(eslint@9.39.1(jiti@1.21.7)) + globals: + specifier: ^15.11.0 + version: 15.15.0 + typescript: + specifier: 5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.46.1 + version: 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + vite: + specifier: ^5.4.0 + version: 5.4.21(@types/node@22.7.5) + packages/interfold-react: dependencies: '@interfold/sdk': @@ -621,10 +667,10 @@ importers: version: 18.3.1 viem: specifier: 2.38.6 - version: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: specifier: ^2.14.16 - version: 2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12) + version: 2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) devDependencies: '@interfold/config': specifier: workspace:* @@ -891,8 +937,8 @@ importers: specifier: ^4.3.2 version: 4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@22.7.5)) wasm-pack: - specifier: ^0.13.1 - version: 0.13.1 + specifier: ^0.15.0 + version: 0.15.0 packages: @@ -2054,6 +2100,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -2394,6 +2446,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2475,9 +2531,11 @@ packages: '@metamask/sdk-analytics@0.0.5': resolution: {integrity: sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==} + deprecated: No longer maintained, superseded by @metamask/connect-analytics '@metamask/sdk-communication-layer@0.33.1': resolution: {integrity: sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==} + deprecated: No longer maintained, superseded by https://docs.metamask.io/metamask-connect peerDependencies: cross-fetch: ^4.0.0 eciesjs: '*' @@ -2487,9 +2545,11 @@ packages: '@metamask/sdk-install-modal-web@0.32.1': resolution: {integrity: sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==} + deprecated: No longer maintained, superseded by https://docs.metamask.io/metamask-connect '@metamask/sdk@0.33.1': resolution: {integrity: sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==} + deprecated: No longer maintained, superseded by https://docs.metamask.io/metamask-connect '@metamask/superstruct@3.2.1': resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} @@ -2991,7 +3051,7 @@ packages: '@paulmillr/qr@0.2.1': resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} - deprecated: 'The package is now available as "qr": npm install qr' + deprecated: 'Switch to "qr" (new package name) for security updates: npm install qr' '@phosphor-icons/react@2.1.10': resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} @@ -4048,6 +4108,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: 5.8.3 + '@typescript-eslint/eslint-plugin@8.61.1': + resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.61.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: 5.8.3 + '@typescript-eslint/parser@7.18.0': resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4065,12 +4133,25 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: 5.8.3 + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: 5.8.3 + '@typescript-eslint/project-service@8.47.0': resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: 5.8.3 + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: 5.8.3 + '@typescript-eslint/scope-manager@7.18.0': resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4079,12 +4160,22 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.47.0': resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: 5.8.3 + '@typescript-eslint/type-utils@7.18.0': resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4102,6 +4193,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: 5.8.3 + '@typescript-eslint/type-utils@8.61.1': + resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: 5.8.3 + '@typescript-eslint/types@7.18.0': resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4110,6 +4208,10 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@7.18.0': resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4125,6 +4227,12 @@ packages: peerDependencies: typescript: 5.8.3 + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: 5.8.3 + '@typescript-eslint/utils@7.18.0': resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4138,6 +4246,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: 5.8.3 + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: 5.8.3 + '@typescript-eslint/visitor-keys@7.18.0': resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -4146,6 +4261,10 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} deprecated: Potential CWE-502 - Update to 1.3.1 or higher @@ -4602,9 +4721,6 @@ packages: axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} - axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} - axios@1.13.2: resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} @@ -4641,6 +4757,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} @@ -4677,11 +4797,6 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - binary-install@1.1.2: - resolution: {integrity: sha512-ZS2cqFHPZOy4wLxvzqfQvDjCOifn+7uCPqNmYRIBM/03+yllON+4fNnsD0VJdW0p97y+E+dTRNPStWNqMBq+9g==} - engines: {node: '>=10'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - binary@0.3.0: resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} @@ -4720,6 +4835,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -4946,9 +5065,9 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} cipher-base@1.0.7: resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} @@ -5754,6 +5873,12 @@ packages: peerDependencies: eslint: '>=7.0.0' + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react-hooks@7.0.1: resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} engines: {node: '>=18'} @@ -5777,6 +5902,10 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@9.39.1: resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6181,10 +6310,6 @@ packages: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - fs-readdir-recursive@1.1.0: resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==} @@ -6288,6 +6413,7 @@ packages: glob@10.3.10: resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@10.4.5: @@ -6298,24 +6424,25 @@ packages: glob@11.0.3: resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@5.0.15: resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} @@ -7512,6 +7639,10 @@ packages: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -7526,21 +7657,13 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} mipd@0.0.7: resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} @@ -8712,11 +8835,6 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@5.0.10: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true @@ -9214,9 +9332,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + engines: {node: '>=18'} text-encoding-utf-8@1.0.2: resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} @@ -9328,6 +9446,12 @@ packages: peerDependencies: typescript: 5.8.3 + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: 5.8.3 + ts-command-line-args@2.5.1: resolution: {integrity: sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==} hasBin: true @@ -9368,6 +9492,7 @@ packages: tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} + deprecated: unmaintained hasBin: true peerDependencies: typescript: 5.8.3 @@ -9447,6 +9572,13 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript-eslint@8.61.1: + resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: 5.8.3 + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -9899,8 +10031,9 @@ packages: engines: {node: '>=12.0.0'} hasBin: true - wasm-pack@0.13.1: - resolution: {integrity: sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==} + wasm-pack@0.15.0: + resolution: {integrity: sha512-DdqtGWc3+iFx+7lL7QU5LBWs7qMnwQSWxF0htSfE15sNa3roVwHjAkTm2JXgueU2GGfSwNqbq2EzyC2b/biKDA==} + engines: {node: '>=16'} hasBin: true web-namespaces@2.0.1: @@ -10060,6 +10193,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -11002,16 +11139,16 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-org/account@2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12)': + '@base-org/account@2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)': dependencies: '@coinbase/cdp-sdk': 1.38.5(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.8.3)(zod@4.1.12) + ox: 0.6.9(typescript@5.8.3)(zod@3.25.76) preact: 10.24.2 - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) transitivePeerDependencies: - '@types/react' @@ -11114,6 +11251,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@coinbase/wallet-sdk@4.3.6(@types/react@18.3.26)(bufferutil@4.0.9)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.8.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + '@coinbase/wallet-sdk@4.3.6(@types/react@18.3.26)(bufferutil@4.0.9)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@noble/hashes': 1.4.0 @@ -11480,6 +11637,11 @@ snapshots: eslint: 9.39.1(jiti@1.21.7) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.1(jiti@1.21.7))': + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.1': @@ -11810,6 +11972,14 @@ snapshots: '@semaphore-protocol/contracts': 4.14.0 solady: 0.1.4 + '@gemini-wallet/core@0.3.1(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - supports-color + '@gemini-wallet/core@0.3.1(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))': dependencies: '@metamask/rpc-errors': 7.0.2 @@ -11961,6 +12131,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 @@ -12818,6 +12992,17 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: big.js: 6.2.2 @@ -12829,13 +13014,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@reown/appkit-controllers@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12864,14 +13049,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@reown/appkit-controllers@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) - lit: 3.3.0 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12900,18 +13084,14 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-polyfills@1.7.8': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-scaffold-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12)': + '@reown/appkit-pay@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76) lit: 3.3.0 + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12938,16 +13118,16 @@ snapshots: - typescript - uploadthing - utf-8-validate - - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@reown/appkit-pay@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) lit: 3.3.0 - qrcode: 1.5.3 + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12976,16 +13156,18 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12)': + '@reown/appkit-polyfills@1.7.8': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-polyfills': 1.7.8 + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -13012,34 +13194,17 @@ snapshots: - typescript - uploadthing - utf-8-validate + - valtio - zod - '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-polyfills': 1.7.8 - '@walletconnect/logger': 2.1.2 - zod: 3.22.4 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - '@reown/appkit@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-pay': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - bs58: 6.0.0 - valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -13066,24 +13231,268 @@ snapshots: - typescript - uploadthing - utf-8-validate + - valtio - zod - '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': {} - - '@rolldown/pluginutils@1.0.0-beta.27': {} - - '@rollup/plugin-inject@5.0.5(rollup@4.52.5)': + '@reown/appkit-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) - estree-walker: 2.0.2 - magic-string: 0.30.21 - optionalDependencies: - rollup: 4.52.5 - - '@rollup/plugin-virtual@3.0.2(rollup@4.52.5)': - optionalDependencies: - rollup: 4.52.5 - + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-ui@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-polyfills': 1.7.8 + '@walletconnect/logger': 2.1.2 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit@1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-pay': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.26)(react@18.3.1))(zod@4.1.12) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + bs58: 6.0.0 + valtio: 1.13.2(@types/react@18.3.26)(react@18.3.1) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@risc0/ethereum@file:templates/default/lib/risc0-ethereum': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-inject@5.0.5(rollup@4.52.5)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.52.5 + + '@rollup/plugin-virtual@3.0.2(rollup@4.52.5)': + optionalDependencies: + rollup: 4.52.5 + '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 @@ -13197,6 +13606,16 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) @@ -13207,6 +13626,16 @@ snapshots: - utf-8-validate - zod + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 @@ -14404,6 +14833,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.61.1 + eslint: 9.39.1(jiti@1.21.7) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 @@ -14429,6 +14874,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.47.0(typescript@5.8.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.8.3) @@ -14438,6 +14895,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.61.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.8.3) + '@typescript-eslint/types': 8.61.1 + debug: 4.4.3(supports-color@5.5.0) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 @@ -14448,10 +14914,19 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/scope-manager@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.8.3)': dependencies: typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) @@ -14464,14 +14939,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.47.0 + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.1(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) debug: 4.4.3(supports-color@5.5.0) eslint: 9.39.1(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.8.3) + ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -14480,6 +14967,8 @@ snapshots: '@typescript-eslint/types@8.47.0': {} + '@typescript-eslint/types@8.61.1': {} + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 7.18.0 @@ -14511,6 +15000,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.61.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.61.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.8.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3(supports-color@5.5.0) + minimatch: 10.2.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) @@ -14533,6 +15037,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.1(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@7.18.0': dependencies: '@typescript-eslint/types': 7.18.0 @@ -14543,6 +15058,11 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} '@viem/anvil@0.0.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': @@ -14642,9 +15162,9 @@ snapshots: '@vue/shared@3.5.22': {} - '@wagmi/connectors@6.1.3(0b1a0c7e1852d0f2478f1048dd2722c1)': + '@wagmi/connectors@6.1.3(615998432ed1538eed571631714b7da2)': dependencies: - '@base-org/account': 2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12) + '@base-org/account': 2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12) '@coinbase/wallet-sdk': 4.3.6(@types/react@18.3.26)(bufferutil@4.0.9)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@4.1.12) '@gemini-wallet/core': 0.3.1(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -14653,7 +15173,7 @@ snapshots: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12)) + porto: 0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12)) viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) optionalDependencies: typescript: 5.8.3 @@ -14696,19 +15216,19 @@ snapshots: - ws - zod - '@wagmi/connectors@6.1.3(615998432ed1538eed571631714b7da2)': + '@wagmi/connectors@6.1.3(d4aa626e1ce01c77feebfbcfe6b62584)': dependencies: - '@base-org/account': 2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12) - '@coinbase/wallet-sdk': 4.3.6(@types/react@18.3.26)(bufferutil@4.0.9)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@4.1.12) - '@gemini-wallet/core': 0.3.1(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + '@base-org/account': 2.4.0(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@18.3.26)(bufferutil@4.0.9)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@gemini-wallet/core': 0.3.1(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12)) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + porto: 0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -14750,6 +15270,21 @@ snapshots: - ws - zod + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.8.3) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/query-core': 5.90.6 + typescript: 5.8.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))': dependencies: eventemitter3: 5.0.1 @@ -14765,6 +15300,50 @@ snapshots: - react - use-sync-external-store + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@walletconnect/heartbeat': 1.2.2 @@ -14809,6 +15388,50 @@ snapshots: - utf-8-validate - zod + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@walletconnect/heartbeat': 1.2.2 @@ -14857,6 +15480,47 @@ snapshots: dependencies: tslib: 1.14.1 + '@walletconnect/ethereum-provider@2.21.1(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/ethereum-provider@2.21.1(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@reown/appkit': 1.7.8(@types/react@18.3.26)(bufferutil@4.0.9)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) @@ -14970,37 +15634,109 @@ snapshots: - ioredis - uploadthing - '@walletconnect/logger@2.1.2': - dependencies: - '@walletconnect/safe-json': 1.0.2 - pino: 7.11.0 - - '@walletconnect/relay-api@1.0.11': - dependencies: - '@walletconnect/jsonrpc-types': 1.0.4 - - '@walletconnect/relay-auth@1.1.0': - dependencies: - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - uint8arrays: 3.1.0 - - '@walletconnect/safe-json@1.0.2': - dependencies: - tslib: 1.14.1 - - '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@walletconnect/logger@2.1.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 7.11.0 + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.0 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -15125,6 +15861,46 @@ snapshots: - ioredis - uploadthing + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@walletconnect/events': 1.0.1 @@ -15165,6 +15941,46 @@ snapshots: - utf-8-validate - zod + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@walletconnect/events': 1.0.1 @@ -15205,6 +16021,50 @@ snapshots: - utf-8-validate - zod + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@noble/ciphers': 1.2.1 @@ -15249,6 +16109,50 @@ snapshots: - utf-8-validate - zod + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)': dependencies: '@noble/ciphers': 1.2.1 @@ -15577,12 +16481,6 @@ snapshots: transitivePeerDependencies: - debug - axios@0.26.1: - dependencies: - follow-redirects: 1.15.11(debug@4.4.3) - transitivePeerDependencies: - - debug - axios@1.13.2: dependencies: follow-redirects: 1.15.11(debug@4.4.3) @@ -15645,6 +16543,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 @@ -15674,14 +16574,6 @@ snapshots: binary-extensions@2.3.0: {} - binary-install@1.1.2: - dependencies: - axios: 0.26.1 - rimraf: 3.0.2 - tar: 6.2.1 - transitivePeerDependencies: - - debug - binary@0.3.0: dependencies: buffers: 0.1.1 @@ -15742,6 +16634,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -15997,7 +16893,7 @@ snapshots: dependencies: readdirp: 4.1.2 - chownr@2.0.0: {} + chownr@3.0.0: {} cipher-base@1.0.7: dependencies: @@ -16897,6 +17793,10 @@ snapshots: dependencies: eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@1.21.7)): dependencies: '@babel/core': 7.28.5 @@ -16921,6 +17821,8 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} + eslint@9.39.1(jiti@1.21.7): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) @@ -17510,10 +18412,6 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - fs-readdir-recursive@1.1.0: {} fs.realpath@1.0.0: {} @@ -17805,7 +18703,7 @@ snapshots: - debug - utf-8-validate - hardhat-gas-reporter@2.3.0(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10): + hardhat-gas-reporter@2.3.0(bufferutil@4.0.9)(hardhat@3.0.11(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/bytes': 5.8.0 @@ -17822,7 +18720,7 @@ snapshots: lodash: 4.17.21 markdown-table: 2.0.0 sha1: 1.1.1 - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - debug @@ -19411,6 +20309,10 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.0 + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -19425,18 +20327,11 @@ snapshots: minimist@1.2.8: {} - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: + minizlib@3.1.0: dependencies: - minipass: 3.3.6 - yallist: 4.0.0 + minipass: 7.1.2 mipd@0.0.7(typescript@5.8.3): optionalDependencies: @@ -19831,6 +20726,20 @@ snapshots: os-browserify@0.3.0: {} + ox@0.6.9(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.8.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - zod + ox@0.6.9(typescript@5.8.3)(zod@4.1.12): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -20137,21 +21046,21 @@ snapshots: style-value-types: 5.0.0 tslib: 2.8.1 - porto@0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12)): + porto@0.2.35(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)) hono: 4.10.4 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.8.3) ox: 0.9.14(typescript@5.8.3)(zod@4.1.12) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 4.1.12 zustand: 5.0.8(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) optionalDependencies: '@tanstack/react-query': 5.90.6(react@18.3.1) react: 18.3.1 typescript: 5.8.3 - wagmi: 2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12) + wagmi: 2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) transitivePeerDependencies: - '@types/react' - immer @@ -20692,10 +21601,6 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rimraf@5.0.10: dependencies: glob: 10.4.5 @@ -21368,14 +22273,13 @@ snapshots: - tsx - yaml - tar@6.2.1: + tar@7.5.16: dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 text-encoding-utf-8@1.0.2: {} @@ -21484,6 +22388,10 @@ snapshots: dependencies: typescript: 5.8.3 + ts-api-utils@2.5.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + ts-command-line-args@2.5.1: dependencies: chalk: 4.1.2 @@ -21627,6 +22535,17 @@ snapshots: typedarray@0.0.6: {} + typescript-eslint@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + typescript@5.8.3: {} typical@4.0.0: {} @@ -22102,14 +23021,14 @@ snapshots: vscode-uri@3.1.0: {} - wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.12): + wagmi@2.19.2(@tanstack/query-core@5.90.6)(@tanstack/react-query@5.90.6(react@18.3.1))(@types/react@18.3.26)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): dependencies: '@tanstack/react-query': 5.90.6(react@18.3.1) - '@wagmi/connectors': 6.1.3(0b1a0c7e1852d0f2478f1048dd2722c1) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + '@wagmi/connectors': 6.1.3(d4aa626e1ce01c77feebfbcfe6b62584) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.6)(@types/react@18.3.26)(immer@10.0.2)(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)) react: 18.3.1 use-sync-external-store: 1.4.0(react@18.3.1) - viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.38.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -22204,11 +23123,9 @@ snapshots: transitivePeerDependencies: - debug - wasm-pack@0.13.1: + wasm-pack@0.15.0: dependencies: - binary-install: 1.1.2 - transitivePeerDependencies: - - debug + tar: 7.5.16 web-namespaces@2.0.1: {} @@ -22342,6 +23259,8 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@1.10.2: {} yaml@2.8.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c5e4045bcb..f641765469 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,6 +12,7 @@ packages: - packages/interfold-contracts - packages/interfold-mcp - packages/interfold-dashboard + - packages/interfold-node - templates/default - templates/default/client linkWorkspacePackages: true diff --git a/templates/default/client/package.json b/templates/default/client/package.json index 3b27b4924b..efc29bf65d 100644 --- a/templates/default/client/package.json +++ b/templates/default/client/package.json @@ -42,7 +42,7 @@ "vite-plugin-top-level-await": "^1.4.1", "vite-plugin-wasm": "^3.3.0", "vite-tsconfig-paths": "^4.3.2", - "wasm-pack": "^0.13.1" + "wasm-pack": "^0.15.0" }, "packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808" } From 704a7023643094ace90356478e7de7e7f130e1a3 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 01:49:01 +0500 Subject: [PATCH 02/10] fix: add license header --- crates/dashboard/assets/app.js | 6 ++++++ packages/interfold-node/eslint.config.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/crates/dashboard/assets/app.js b/crates/dashboard/assets/app.js index 8ce16fb644..b3f57e1501 100644 --- a/crates/dashboard/assets/app.js +++ b/crates/dashboard/assets/app.js @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const i of l)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(l){const i={};return l.integrity&&(i.integrity=l.integrity),l.referrerPolicy&&(i.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?i.credentials="include":l.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(l){if(l.ep)return;l.ep=!0;const i=n(l);fetch(l.href,i)}})();function xc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var nu={exports:{}},sl={},ru={exports:{}},R={};/** * @license React * react.production.min.js diff --git a/packages/interfold-node/eslint.config.js b/packages/interfold-node/eslint.config.js index d8aad6a121..799c092574 100644 --- a/packages/interfold-node/eslint.config.js +++ b/packages/interfold-node/eslint.config.js @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + import js from '@eslint/js' import globals from 'globals' import reactHooks from 'eslint-plugin-react-hooks' From 87dd9d1cd7802f615daae78bd9abb5d225130996 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 02:49:44 +0500 Subject: [PATCH 03/10] fix: events --- crates/events/src/eventbus.rs | 9 ++------- .../packages/crisp-contracts/deployed_contracts.json | 9 +++++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/events/src/eventbus.rs b/crates/events/src/eventbus.rs index a961b45b49..5e5cc018c3 100644 --- a/crates/events/src/eventbus.rs +++ b/crates/events/src/eventbus.rs @@ -8,7 +8,7 @@ use crate::traits::{ErrorEvent, Event}; use crate::EventType; use actix::prelude::*; use bloom::{BloomFilter, ASMS}; -use e3_utils::{MAILBOX_LIMIT, MAILBOX_LIMIT_LARGE}; +use e3_utils::{colorize, Color, MAILBOX_LIMIT, MAILBOX_LIMIT_LARGE}; use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; @@ -136,12 +136,7 @@ impl Handler for EventBus { } } - // The structured protocol logger owns operator-visible event output. - // Keep the bus dispatch record payload-free and opt-in for developers. - tracing::trace!( - event_type = %event.event_type(), - "event dispatched" - ); + tracing::info!("{} {}", colorize(">>>", Color::Yellow), event); self.track(event); } } diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index d8bbfa6577..60764aa478 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -67,12 +67,21 @@ }, "InterfoldToken": { "constructorArgs": { +<<<<<<< HEAD "owner": "0x8837e47c4Bb520ADE83AAB761C3B60679443af1B", "ccaStart": "1782210660", "ccaEnd": "1782815460", "noMoreLocks": "1912847460", "claimSource": "0x8837e47c4Bb520ADE83AAB761C3B60679443af1B", "bondingRegistry": "0xfdE5DAbaD974223C401c867eDF3bB6262175F578" +======= + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ccaStart": "1782168274", + "ccaEnd": "1782773074", + "claimSource": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "bondingRegistry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "noMoreLocks": "1912805074" +>>>>>>> a5c9072e5 (fix: events) }, "blockNumber": 11121982, "address": "0x8d299447dc025c9dDc4639AcbBe2453edb5B950B" From c7ee342cdb217ae13d62a3526f8ef6c0677aff41 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 12:05:30 +0500 Subject: [PATCH 04/10] fix: interfold error display --- crates/cli/Cargo.toml | 2 +- crates/events/src/eventstore_router.rs | 6 +++--- crates/request/src/domain/routing.rs | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 5dae6ba8c1..849da29849 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -23,6 +23,7 @@ e3-config = { workspace = true } e3-console = { workspace = true } e3-ciphernode-builder = { workspace = true } e3-crypto = { workspace = true } +e3-dashboard = { workspace = true } e3-entrypoint = { workspace = true } e3-events = { workspace = true } e3-evm = { workspace = true } @@ -34,7 +35,6 @@ chrono = { workspace = true } e3-init = { workspace = true } e3-support-scripts = { workspace = true } e3-daemon-server = { workspace = true } -e3-dashboard = { workspace = true } e3-utils = { workspace = true } e3-zk-prover = { workspace = true } hex = { workspace = true } diff --git a/crates/events/src/eventstore_router.rs b/crates/events/src/eventstore_router.rs index 97bd50ad8c..7816a0cc7e 100644 --- a/crates/events/src/eventstore_router.rs +++ b/crates/events/src/eventstore_router.rs @@ -13,7 +13,7 @@ use actix::{Actor, ActorContext, Addr, AsyncContext, Context, Handler, Recipient use anyhow::Result; use e3_utils::MAILBOX_LIMIT_LARGE; use std::collections::HashMap; -use tracing::{debug, error, info, warn}; +use tracing::{debug, error, warn}; /// QueryAggregator - handles a single query's lifecycle struct QueryAggregator { @@ -54,7 +54,7 @@ impl Handler for QueryAggregator { let sub_query_id = msg.id(); if let Some(aggregate_id) = self.pending.remove(&sub_query_id) { - info!( + debug!( "Received response for aggregate {:?}, {} pending", aggregate_id, self.pending.len() @@ -62,7 +62,7 @@ impl Handler for QueryAggregator { self.collected_events.extend(msg.into_events()); if self.pending.is_empty() { - info!("All aggregates fulfilled, sending response"); + debug!("All aggregates fulfilled, sending response"); let response = EventStoreQueryResponse::new( self.parent_id, std::mem::take(&mut self.collected_events), diff --git a/crates/request/src/domain/routing.rs b/crates/request/src/domain/routing.rs index aafa24cf67..87c224be5d 100644 --- a/crates/request/src/domain/routing.rs +++ b/crates/request/src/domain/routing.rs @@ -97,6 +97,9 @@ impl RequestRouter { // E3Failed from on-chain markE3Failed may arrive after a local timeout already // cleaned up the context. InterfoldEventData::E3Failed(data) if data.reason.is_timeout() => true, + // Settlement receipts (PlaintextOutputPublished, etc.) can arrive in the same + // EVM block after E3StageChanged(Complete) already tore down the context. + InterfoldEventData::PlaintextOutputPublished(_) => true, _ => false, }; if is_late_terminal { From 2114d53e26ca144569c72525375d717828826983 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 12:16:34 +0500 Subject: [PATCH 05/10] fix: lint errors --- eslint.config.mjs | 2 ++ packages/interfold-node/src/App.tsx | 2 ++ packages/interfold-node/src/api.ts | 7 +++---- packages/interfold-node/src/views/E3Inspector.tsx | 2 ++ packages/interfold-node/src/views/FlowGraphView.tsx | 2 ++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 434e20d67d..191c79bfe2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -24,6 +24,8 @@ export default defineConfig([ '**/playwright-report/**', // Generated WASM bindings '**/pkg/**', + // Bundled dashboard assets + 'crates/dashboard/assets/**', ]), { extends: [config], diff --git a/packages/interfold-node/src/App.tsx b/packages/interfold-node/src/App.tsx index 0cd954181f..c383b3c431 100644 --- a/packages/interfold-node/src/App.tsx +++ b/packages/interfold-node/src/App.tsx @@ -87,7 +87,9 @@ export default function App() { const snapshot = useSnapshot() const updates = useUpdates() + // Auto-select the first E3 when data first loads and none is selected. useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect if (!selectedE3 && snapshot.data?.e3s[0]) setSelectedE3(snapshot.data.e3s[0].e3_id) }, [selectedE3, snapshot.data?.e3s]) diff --git a/packages/interfold-node/src/api.ts b/packages/interfold-node/src/api.ts index 16bb91e2c6..9852709821 100644 --- a/packages/interfold-node/src/api.ts +++ b/packages/interfold-node/src/api.ts @@ -58,11 +58,9 @@ export function useE3Trace(e3Id?: string, refreshKey = 0): QueryState { const [state, setState] = useState>({ loading: Boolean(e3Id) }) useEffect(() => { - if (!e3Id) { - setState({ loading: false }) - return - } + if (!e3Id) return const controller = new AbortController() + // eslint-disable-next-line react-hooks/set-state-in-effect setState((previous) => ({ ...previous, loading: !previous.data })) getJson(`/api/e3?e3_id=${encodeURIComponent(e3Id)}`, controller.signal) .then((data) => setState({ data, loading: false })) @@ -74,6 +72,7 @@ export function useE3Trace(e3Id?: string, refreshKey = 0): QueryState { return () => controller.abort() }, [e3Id, refreshKey]) + if (!e3Id) return { loading: false } return state } diff --git a/packages/interfold-node/src/views/E3Inspector.tsx b/packages/interfold-node/src/views/E3Inspector.tsx index 62c4818ff2..e1aeb71212 100644 --- a/packages/interfold-node/src/views/E3Inspector.tsx +++ b/packages/interfold-node/src/views/E3Inspector.tsx @@ -54,7 +54,9 @@ export default function E3Inspector({ const [focus, setFocus] = useState<{ id: string; nonce: number }>() const currentPhase = trace.data?.current_phase + // Sync phase with trace's current_phase on E3 selection change. useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect if (currentPhase) setPhase(currentPhase) }, [selected, currentPhase]) diff --git a/packages/interfold-node/src/views/FlowGraphView.tsx b/packages/interfold-node/src/views/FlowGraphView.tsx index eac000dc2d..b48aa0836e 100644 --- a/packages/interfold-node/src/views/FlowGraphView.tsx +++ b/packages/interfold-node/src/views/FlowGraphView.tsx @@ -128,7 +128,9 @@ export default function FlowGraphView({ const trace = useE3Trace(selectedE3, refreshKey) const [selectedEvent, setSelectedEvent] = useState() + // Auto-select the most recent event when E3 selection or events change. useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setSelectedEvent(trace.data?.events.at(-1)) }, [selectedE3, trace.data?.events]) From d34d4fef9528f38c6fc9723c7a2edf45ce833c96 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 12:57:23 +0500 Subject: [PATCH 06/10] fix: test --- crates/test-helpers/src/ciphernode_system.rs | 28 ++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/test-helpers/src/ciphernode_system.rs b/crates/test-helpers/src/ciphernode_system.rs index 5137a87960..a0766d7bb8 100644 --- a/crates/test-helpers/src/ciphernode_system.rs +++ b/crates/test-helpers/src/ciphernode_system.rs @@ -358,11 +358,28 @@ impl Deref for CiphernodeHistory { mod tests { use super::*; use actix::prelude::*; - use e3_ciphernode_builder::{EventSystem, NetInterfaceKind}; + use e3_ciphernode_builder::{ + global_eventstore_cache::EventStoreReader, EventSystem, NetInterfaceKind, + }; use e3_data::InMemStore; - use e3_events::{EventBus, EventBusConfig}; + use e3_events::{EventBus, EventBusConfig, EventStoreQueryBy, SeqAgg, TsAgg}; + use e3_net::NetworkStatus; use libp2p::PeerId; + // Minimal mock actor that satisfies the EventStoreReader recipient requirements. + struct MockEventStore; + impl Actor for MockEventStore { + type Context = Context; + } + impl Handler> for MockEventStore { + type Result = (); + fn handle(&mut self, _msg: EventStoreQueryBy, _ctx: &mut Self::Context) {} + } + impl Handler> for MockEventStore { + type Result = (); + fn handle(&mut self, _msg: EventStoreQueryBy, _ctx: &mut Self::Context) {} + } + async fn mock_setup_node(address: String) -> Result { // Create mock actors for the test let store = InMemStore::new(true).start(); @@ -375,6 +392,10 @@ mod tests { .handle()? .enable("test"); + // Mock event store for EventStoreReader + let mock_es = MockEventStore.start(); + let eventstore = EventStoreReader::new(mock_es.clone().recipient(), mock_es.recipient()); + Ok(CiphernodeHandle { address, store: (&store).into(), @@ -383,6 +404,9 @@ mod tests { errors: Some(errors), peer_id: PeerId::random(), net_interface: NetInterfaceKind::Libp2p, + network_status: NetworkStatus::default(), + eventstore, + aggregate_ids: vec![], }) } From bb4c8e637773f456b93c477c30bfe1dc47afdd8e Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Tue, 23 Jun 2026 13:02:19 +0500 Subject: [PATCH 07/10] fix: licence header --- docs/next-env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts index b56aca0bd2..030ecabb5d 100644 --- a/docs/next-env.d.ts +++ b/docs/next-env.d.ts @@ -3,7 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -// + /// /// From d29aac8ae15126c6237574a4eab3784dbe4eade2 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:37:50 +0100 Subject: [PATCH 08/10] chore: pr comments --- crates/logger/src/log_collector.rs | 45 +++++++++----- crates/logger/src/tracing_layer.rs | 41 ++++++++++-- crates/multithread/src/effect_gate.rs | 63 +++++++++++++++++-- crates/net/src/net_interface.rs | 26 ++++---- packages/interfold-node/src/api.ts | 89 +++++++-------------------- 5 files changed, 162 insertions(+), 102 deletions(-) diff --git a/crates/logger/src/log_collector.rs b/crates/logger/src/log_collector.rs index 3eb396db1e..9312de7216 100644 --- a/crates/logger/src/log_collector.rs +++ b/crates/logger/src/log_collector.rs @@ -87,8 +87,10 @@ impl LogCollector { } pub fn record(&self, level: &str, target: &str, message: String, fields: Map) { - let entry = LogEntry { - seq: self.next_seq.fetch_add(1, Ordering::Relaxed), + let mut entry = LogEntry { + // Assigned under the entries lock below so the deque stays ordered + // by seq; query()'s cursor pagination relies on that invariant. + seq: 0, timestamp_ms: now_ms(), level: level.to_owned(), target: target.to_owned(), @@ -97,21 +99,32 @@ impl LogCollector { fields, }; - if let Ok(mut entries) = self.entries.lock() { + { + // Recover from a poisoned lock rather than silently dropping every + // subsequent log line after a single panic-while-holding-lock. + let mut entries = match self.entries.lock() { + Ok(entries) => entries, + Err(poisoned) => poisoned.into_inner(), + }; + // Stamp seq while holding the lock so increment and push are atomic + // relative to other writers (no out-of-order insertion). + entry.seq = self.next_seq.fetch_add(1, Ordering::Relaxed); if entries.len() == self.capacity { entries.pop_front(); } entries.push_back(entry.clone()); } - if let Ok(mut writer_guard) = self.writer.lock() { - let failed = writer_guard - .as_mut() - .and_then(|writer| writer.write(&entry).err()); - if let Some(error) = failed { - eprintln!("failed to write ciphernode operational log: {error}"); - *writer_guard = None; - } + let mut writer_guard = match self.writer.lock() { + Ok(writer_guard) => writer_guard, + Err(poisoned) => poisoned.into_inner(), + }; + let failed = writer_guard + .as_mut() + .and_then(|writer| writer.write(&entry).err()); + if let Some(error) = failed { + eprintln!("failed to write ciphernode operational log: {error}"); + *writer_guard = None; } } @@ -186,10 +199,12 @@ impl LogCollector { } pub fn flush(&self) { - if let Ok(mut writer) = self.writer.lock() { - if let Some(writer) = writer.as_mut() { - let _ = writer.flush(); - } + let mut writer = match self.writer.lock() { + Ok(writer) => writer, + Err(poisoned) => poisoned.into_inner(), + }; + if let Some(writer) = writer.as_mut() { + let _ = writer.flush(); } } } diff --git a/crates/logger/src/tracing_layer.rs b/crates/logger/src/tracing_layer.rs index 13db806e16..45e4485c0c 100644 --- a/crates/logger/src/tracing_layer.rs +++ b/crates/logger/src/tracing_layer.rs @@ -12,6 +12,25 @@ use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; use crate::LogCollector; +/// Maximum characters retained for a single string/Debug field value. A large +/// `?`-logged value (e.g. a ciphertext) is truncated so it cannot blow up the +/// in-memory store or the rotating JSONL file. +const MAX_FIELD_VALUE_CHARS: usize = 1024; + +/// Maximum number of structured fields retained per log entry, bounding the +/// growth from inheriting every ancestor span's fields. +const MAX_FIELDS: usize = 64; + +/// Truncate an over-long string value with an ellipsis marker. +fn truncate_value(value: String) -> Value { + if value.chars().count() > MAX_FIELD_VALUE_CHARS { + let truncated: String = value.chars().take(MAX_FIELD_VALUE_CHARS).collect(); + Value::String(format!("{truncated}…")) + } else { + Value::String(value) + } +} + /// Captures structured tracing events for the bounded dashboard store and /// rotating JSONL file. It does not subscribe to or mutate the protocol bus. #[derive(Clone, Copy, Debug, Default)] @@ -51,7 +70,16 @@ where if let Some(scope) = ctx.event_scope(event) { for span in scope.from_root() { if let Some(fields) = span.extensions().get::() { - visitor.fields.extend(fields.0.clone()); + for (key, value) in &fields.0 { + // Respect the per-entry field cap during inheritance; + // closer (child) spans overwrite ancestors' values. + if visitor.fields.len() >= MAX_FIELDS + && !visitor.fields.contains_key(key) + { + continue; + } + visitor.fields.insert(key.clone(), value.clone()); + } } } } @@ -85,6 +113,11 @@ impl LogVisitor { .map(str::to_owned) .or_else(|| Some(value.to_string())); } else { + // Bound the field count so a deeply nested span tree can't grow an + // entry without limit; always allow overwriting an existing key. + if self.fields.len() >= MAX_FIELDS && !self.fields.contains_key(field.name()) { + return; + } self.fields.insert(field.name().to_owned(), value); } } @@ -111,7 +144,7 @@ impl tracing::field::Visit for LogVisitor { } fn record_str(&mut self, field: &tracing::field::Field, value: &str) { - self.record_value(field, Value::String(value.to_owned())); + self.record_value(field, truncate_value(value.to_owned())); } fn record_error( @@ -119,11 +152,11 @@ impl tracing::field::Visit for LogVisitor { field: &tracing::field::Field, value: &(dyn std::error::Error + 'static), ) { - self.record_value(field, Value::String(value.to_string())); + self.record_value(field, truncate_value(value.to_string())); } fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - self.record_value(field, Value::String(format!("{value:?}"))); + self.record_value(field, truncate_value(format!("{value:?}"))); } } diff --git a/crates/multithread/src/effect_gate.rs b/crates/multithread/src/effect_gate.rs index b46597b123..a5d6be9f31 100644 --- a/crates/multithread/src/effect_gate.rs +++ b/crates/multithread/src/effect_gate.rs @@ -12,8 +12,8 @@ use e3_events::{ InterfoldEvent, InterfoldEventData, }; use e3_utils::MAILBOX_LIMIT; -use std::collections::HashMap; -use tracing::info; +use std::collections::{HashMap, HashSet}; +use tracing::{debug, info}; use e3_events::BusHandle; @@ -23,10 +23,19 @@ type RequestKey = (E3id, ComputeRequestKind); /// semantic requests are deduplicated independently of their correlation ID; /// the newest HLC context wins so a request regenerated by a hydrated actor is /// preferred over its stale pre-crash copy. +/// +/// Deduplication also spans the replay→live boundary: once a `(e3_id, kind)` +/// has been forwarded (either released from the replay buffer or sent live), +/// a later duplicate — e.g. the same request re-driven by a hydrated actor +/// after `EffectsEnabled` — is dropped. The key captures the full request +/// payload (only the correlation id differs), so suppressing the duplicate +/// cannot drop a semantically distinct compute. Keys are cleared per-E3 on +/// terminal events, bounding growth to live E3s. pub(crate) struct ComputeEffectGate { target: Recipient, enabled: bool, pending: HashMap, + forwarded: HashSet, } impl ComputeEffectGate { @@ -35,9 +44,34 @@ impl ComputeEffectGate { target, enabled: false, pending: HashMap::new(), + forwarded: HashSet::new(), } } + /// Extract the dedup key from a ComputeRequest event, if it is one. + fn request_key(event: &InterfoldEvent) -> Option { + match event.get_data() { + InterfoldEventData::ComputeRequest(request) => { + Some((request.e3_id.clone(), request.request.clone())) + } + _ => None, + } + } + + /// Forward a compute effect to the target once, recording its key so a + /// later duplicate (stale-vs-re-driven across the enable boundary) is + /// suppressed. Returns false if the key was already forwarded. + fn forward(&mut self, event: InterfoldEvent) -> bool { + if let Some(key) = Self::request_key(&event) { + if !self.forwarded.insert(key) { + debug!("dropping duplicate compute effect (already forwarded)"); + return false; + } + } + self.target.do_send(event); + true + } + pub(crate) fn attach(bus: &BusHandle, target: Recipient) { let gate = Self::new(target).start(); bus.subscribe_all( @@ -76,7 +110,7 @@ impl ComputeEffectGate { pending.sort_by_key(EventContextAccessors::ts); let count = pending.len(); for event in pending { - self.target.do_send(event); + self.forward(event); } info!(count, "released replay-safe compute effects"); } @@ -84,6 +118,7 @@ impl ComputeEffectGate { fn cancel(&mut self, e3_id: &E3id) { self.pending .retain(|(pending_id, _), _| pending_id != e3_id); + self.forwarded.retain(|(forwarded_id, _)| forwarded_id != e3_id); } } @@ -100,7 +135,9 @@ impl Handler for ComputeEffectGate { fn handle(&mut self, event: InterfoldEvent, _: &mut Self::Context) { match event.get_data() { - InterfoldEventData::ComputeRequest(_) if self.enabled => self.target.do_send(event), + InterfoldEventData::ComputeRequest(_) if self.enabled => { + self.forward(event); + } InterfoldEventData::ComputeRequest(_) => self.queue(event), InterfoldEventData::EffectsEnabled(_) => self.enable(), InterfoldEventData::E3RequestComplete(complete) => self.cancel(&complete.e3_id), @@ -229,4 +266,22 @@ mod tests { assert!(recorder.send(Received).await.unwrap().is_empty()); } + + #[actix::test] + async fn drops_redriven_duplicate_after_enable() { + let recorder = Recorder::default().start(); + let gate = ComputeEffectGate::new(recorder.clone().recipient()).start(); + let buffered = CorrelationId::new(); + let redriven = CorrelationId::new(); + + // Released from the replay buffer on enable. + gate.send(compute(buffered, 10)).await.unwrap(); + gate.send(effects_enabled()).await.unwrap(); + assert_eq!(recorder.send(Received).await.unwrap(), vec![buffered]); + + // Same (e3_id, kind) re-driven by a hydrated actor after EffectsEnabled + // is suppressed — only one compute reaches the target. + gate.send(compute(redriven, 40)).await.unwrap(); + assert_eq!(recorder.send(Received).await.unwrap(), vec![buffered]); + } } diff --git a/crates/net/src/net_interface.rs b/crates/net/src/net_interface.rs index 2977b3ae10..fb284ffadc 100644 --- a/crates/net/src/net_interface.rs +++ b/crates/net/src/net_interface.rs @@ -350,17 +350,21 @@ async fn process_swarm_event( info!("Peer connected: {peer_id} (total: {total})"); } let remote_addr = endpoint.get_remote_address().clone(); - status.connected( - peer_id.to_string(), - remote_addr.to_string(), - if endpoint.is_dialer() { - "outbound" - } else { - "inbound" - }, - num_established.get(), - ); - if !(should_filter_loopback(swarm) && is_loopback_addr(&remote_addr)) { + // Only track/advertise peers we consider routable. Filtered loopback + // connections are excluded from both the dashboard peer count and + // Kademlia so the two stay consistent. + let filter_loopback = should_filter_loopback(swarm) && is_loopback_addr(&remote_addr); + if !filter_loopback { + status.connected( + peer_id.to_string(), + remote_addr.to_string(), + if endpoint.is_dialer() { + "outbound" + } else { + "inbound" + }, + num_established.get(), + ); swarm .behaviour_mut() .kademlia diff --git a/packages/interfold-node/src/api.ts b/packages/interfold-node/src/api.ts index 9852709821..2cfc056dbf 100644 --- a/packages/interfold-node/src/api.ts +++ b/packages/interfold-node/src/api.ts @@ -15,11 +15,20 @@ async function getJson(path: string, signal?: AbortSignal): Promise { const detail = await response.json().catch(() => null) throw new Error(detail?.error ?? `${response.status} ${response.statusText}`) } - return response.json() as Promise + try { + return (await response.json()) as T + } catch { + throw new Error(`Invalid JSON in response from ${path}`) + } } -export function useSnapshot(intervalMs = 2_000): QueryState { - const [state, setState] = useState>({ loading: true }) +/** + * Polls a JSON endpoint on an interval with an abort on unmount, a + * re-entrancy guard so a slow endpoint can't stack overlapping requests, and a + * visibility check so background tabs don't poll. + */ +function usePolledJson(path: string, intervalMs: number): QueryState { + const [state, setState] = useState>({ loading: true }) useEffect(() => { let active = true @@ -30,7 +39,7 @@ export function useSnapshot(intervalMs = 2_000): QueryState { refreshing = true controller = new AbortController() try { - const data = await getJson('/api/snapshot', controller.signal) + const data = await getJson(path, controller.signal) if (active) setState({ data, loading: false }) } catch (error) { if (active && !(error instanceof DOMException && error.name === 'AbortError')) { @@ -49,11 +58,15 @@ export function useSnapshot(intervalMs = 2_000): QueryState { controller?.abort() window.clearInterval(timer) } - }, [intervalMs]) + }, [path, intervalMs]) return state } +export function useSnapshot(intervalMs = 2_000): QueryState { + return usePolledJson('/api/snapshot', intervalMs) +} + export function useE3Trace(e3Id?: string, refreshKey = 0): QueryState { const [state, setState] = useState>({ loading: Boolean(e3Id) }) @@ -77,73 +90,13 @@ export function useE3Trace(e3Id?: string, refreshKey = 0): QueryState { } export function useLogs(intervalMs = 2_000): QueryState { - const [state, setState] = useState>({ loading: true }) - useEffect(() => { - let active = true - const refresh = () => { - getJson('/api/logs?limit=2000') - .then((data) => { - if (active) setState({ data, loading: false }) - }) - .catch((error) => { - if (active) - setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) - }) - } - refresh() - const timer = window.setInterval(refresh, intervalMs) - return () => { - active = false - window.clearInterval(timer) - } - }, [intervalMs]) - return state + return usePolledJson('/api/logs?limit=2000', intervalMs) } export function useEvents(intervalMs = 2_500): QueryState { - const [state, setState] = useState>({ loading: true }) - useEffect(() => { - let active = true - const refresh = () => { - getJson('/api/events?limit=2000') - .then((data) => { - if (active) setState({ data, loading: false }) - }) - .catch((error) => { - if (active) - setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) - }) - } - refresh() - const timer = window.setInterval(refresh, intervalMs) - return () => { - active = false - window.clearInterval(timer) - } - }, [intervalMs]) - return state + return usePolledJson('/api/events?limit=2000', intervalMs) } export function useUpdates(intervalMs = 60 * 60 * 1_000): QueryState { - const [state, setState] = useState>({ loading: true }) - useEffect(() => { - let active = true - const refresh = () => { - getJson('/api/updates') - .then((data) => { - if (active) setState({ data, loading: false }) - }) - .catch((error) => { - if (active) - setState((previous) => ({ ...previous, error: error instanceof Error ? error.message : String(error), loading: false })) - }) - } - refresh() - const timer = window.setInterval(refresh, intervalMs) - return () => { - active = false - window.clearInterval(timer) - } - }, [intervalMs]) - return state + return usePolledJson('/api/updates', intervalMs) } From 4899f8069f5452dfab6574d88c68c1c7239b1d19 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:08:18 +0100 Subject: [PATCH 09/10] chore: pr comments --- crates/dashboard/assets/app.css | 1826 ++++- crates/dashboard/assets/app.js | 9198 +++++++++++++++++++++- crates/logger/src/tracing_layer.rs | 69 +- crates/multithread/src/effect_gate.rs | 3 +- docs/components/Tokenomics/index.tsx | 219 +- docs/next-env.d.ts | 2 +- docs/pages/faq/auction.mdx | 3 +- docs/pages/tokenomics.mdx | 21 +- packages/interfold-node/eslint.config.js | 7 +- 9 files changed, 11203 insertions(+), 145 deletions(-) diff --git a/crates/dashboard/assets/app.css b/crates/dashboard/assets/app.css index 0ccd597df1..fe00e4ae04 100644 --- a/crates/dashboard/assets/app.css +++ b/crates/dashboard/assets/app.css @@ -1 +1,1825 @@ -.page{min-height:100vh;display:flex;flex-direction:column}.site-head{position:sticky;top:0;z-index:50;border-bottom:1px solid var(--rule);background:color-mix(in oklab,var(--paper) 91%,transparent);-webkit-backdrop-filter:blur(16px) saturate(130%);backdrop-filter:blur(16px) saturate(130%)}.site-head__inner{width:min(1500px,100%);height:64px;margin:0 auto;padding:0 28px;display:flex;align-items:center;gap:16px}.wordmark{width:140px;height:24px;border:0;padding:0;background:transparent;cursor:pointer}.wordmark span{display:block;width:100%;height:100%;background:var(--ink);mask:url(/assets/interfold.svg) no-repeat left center / contain;-webkit-mask:url(/assets/interfold.svg) no-repeat left center / contain}.product-name{padding-left:16px;border-left:1px solid var(--rule);color:var(--ink-3);font-size:13px;white-space:nowrap}.site-nav{display:flex;align-items:center;gap:3px;margin-left:24px}.site-nav__link{border:0;background:transparent;padding:7px 12px;border-radius:999px;color:var(--ink-3);font-size:14px;cursor:pointer;transition:.12s ease}.site-nav__link:hover{color:var(--ink);background:var(--rule-soft)}.site-nav__link--on{color:var(--accent-ink);background:var(--accent-bg)}.node-chip{margin-left:auto;display:inline-flex;align-items:center;gap:8px;max-width:230px;padding:6px 10px;border:1px solid var(--rule);border-radius:999px;background:var(--paper-2);color:var(--ink-2);font-size:12px}.node-chip__dot,.live-dot{width:7px;height:7px;flex:none;border-radius:50%;background:var(--accent-deep);box-shadow:0 0 0 4px var(--accent-bg)}.node-chip__dot--waiting{background:var(--warning);box-shadow:0 0 0 4px var(--warning-bg)}.app-main{flex:1;width:min(1320px,100%);margin:0 auto;padding:42px 28px 80px}.app-main--inspector{width:min(1600px,100%);padding-top:0;padding-bottom:0}.view-stack{display:flex;flex-direction:column;gap:28px}.view-intro,.view-title{display:flex;justify-content:space-between;gap:36px;align-items:flex-end}.view-intro>div:first-child{max-width:780px}.eyebrow,.section-kicker{display:flex;align-items:center;gap:9px;margin-bottom:10px;color:var(--accent-deep);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.view-intro h1,.view-title h1,.trace-head h1,.blank-state h1,.loading-page h1{margin:0;color:var(--ink);font-family:var(--serif);font-size:clamp(34px,4vw,52px);font-weight:400;line-height:1.08;letter-spacing:-.02em}.view-intro p,.view-title p,.trace-head p{margin:14px 0 0;color:var(--ink-3);font-size:16px}.identity-card{width:280px;padding:18px 20px;display:flex;flex-direction:column;gap:7px;border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);box-shadow:0 12px 28px -25px #14201b66}.identity-card>span:first-child{color:var(--ink-4);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.identity-card strong{color:var(--ink);font-size:13px}.identity-card>span:last-child{color:var(--ink-4);font-size:11px}.metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);overflow:hidden}.metric{min-width:0;padding:22px 24px;display:flex;flex-direction:column;border-right:1px solid var(--rule)}.metric:last-child{border-right:0}.metric__label{color:var(--ink-4);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.metric__value{margin:7px 0 3px;color:var(--ink);font-family:var(--serif);font-size:32px;font-weight:400;line-height:1}.metric__note{color:var(--ink-3);font-size:12px}.metric--bad .metric__value{color:var(--danger)}.overview-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:20px;align-items:start}.panel{min-width:0;padding:24px;border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);box-shadow:0 14px 36px -34px #14201b73}.panel--compact{padding:18px}.panel--wide{width:100%}.panel__head{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;margin-bottom:18px}.panel__head .section-kicker{margin-bottom:4px}.panel__head h2{margin:0;color:var(--ink);font-family:var(--serif);font-size:23px;font-weight:400;line-height:1.2}.panel__aside{color:var(--ink-4);font-size:11px;white-space:nowrap}.health-pill,.status-tag,.trace-status{display:inline-flex;align-items:center;border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.health-pill{padding:5px 9px;background:var(--accent-bg);color:var(--accent-deep)}.health-pill--bad{color:var(--warning);background:var(--warning-bg)}.peer-list,.chain-list{display:flex;flex-direction:column;gap:8px}.peer-row{display:grid;grid-template-columns:9px minmax(0,1fr) auto;gap:11px;align-items:center;padding:12px 0;border-bottom:1px solid var(--rule-soft)}.peer-row:last-child{border-bottom:0}.peer-row__status{width:7px;height:7px;border-radius:50%;background:var(--accent-deep)}.peer-row>div{min-width:0;display:flex;flex-direction:column}.peer-row strong{color:var(--ink-2);font-size:12px}.peer-row div span{color:var(--ink-4);font-size:10px;overflow:hidden;text-overflow:ellipsis}.peer-row__direction{color:var(--ink-3);font-size:10px}.listen-addresses{margin-top:14px;padding-top:14px;display:flex;flex-wrap:wrap;gap:7px;align-items:center;border-top:1px solid var(--rule-soft)}.listen-addresses>span{margin-right:4px;color:var(--ink-4);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.listen-addresses code{padding:3px 7px;border-radius:5px;background:var(--paper-3);color:var(--ink-3);font-size:9px}.chain-card{padding:14px;border:1px solid var(--rule-soft);border-radius:var(--radius-sm);background:#fdfdfb}.chain-card__head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.chain-card__head strong{font-size:13px}.status-tag{padding:4px 7px;background:var(--paper-3);color:var(--ink-3)}.status-tag--complete{background:var(--accent-bg);color:var(--accent-deep)}.status-tag--active{background:var(--warning-bg);color:var(--warning)}.status-tag--failed{background:var(--danger-bg);color:var(--danger)}.mini-dl{margin:0;display:grid;grid-template-columns:1fr 1fr;gap:10px 18px}.mini-dl div{min-width:0;display:flex;flex-direction:column}.mini-dl dt{color:var(--ink-4);font-size:9px;text-transform:uppercase;letter-spacing:.06em}.mini-dl dd{margin:2px 0 0;overflow:hidden;color:var(--ink-2);font-size:12px;text-overflow:ellipsis}.event-timeline{display:flex;flex-direction:column}.event-card{position:relative;border-bottom:1px solid var(--rule-soft);transition:background .15s,box-shadow .15s}.event-card:last-child{border-bottom:0}.event-card--located{z-index:1;border-radius:8px;background:var(--accent-bg);box-shadow:0 0 0 3px var(--accent-soft)}.event-card__summary{width:100%;min-height:64px;padding:10px 4px;display:grid;grid-template-columns:22px 88px minmax(0,1fr) auto;gap:10px;align-items:center;border:0;background:transparent;text-align:left;cursor:pointer}.event-card__summary:hover{background:#fafbf8}.event-card__rail{position:relative;align-self:stretch;display:flex;justify-content:center}.event-card__rail:after{content:"";position:absolute;top:27px;bottom:-22px;width:1px;background:var(--rule)}.event-card:last-child .event-card__rail:after{display:none}.event-card__dot{position:relative;z-index:1;width:8px;height:8px;margin-top:17px;border:2px solid var(--paper-2);border-radius:50%;background:var(--accent-deep);box-shadow:0 0 0 1px var(--accent-soft)}.event-card--warn .event-card__dot{background:var(--warning);box-shadow:0 0 0 1px #ead5a6}.event-card--error .event-card__dot{background:var(--danger);box-shadow:0 0 0 1px #e8b3b0}.event-card--debug .event-card__dot{background:var(--ink-4);box-shadow:0 0 0 1px var(--rule)}.event-card__time{color:var(--ink-4);font-size:10px}.event-card__identity{min-width:0;display:flex;flex-direction:column;gap:5px}.event-card__identity strong{overflow:hidden;color:var(--ink-2);font-size:13px;font-weight:560;text-overflow:ellipsis}.event-card__meta{display:flex;flex-wrap:wrap;gap:7px;align-items:center;color:var(--ink-4);font-size:10px}.source{padding:2px 5px;border-radius:4px;background:var(--accent-bg);color:var(--accent-deep);font-size:8px;font-weight:700;letter-spacing:.05em}.source--network{background:#f0edfa;color:var(--network)}.source--evm{background:#f9f0df;color:var(--evm)}.event-card__cause{display:flex;align-items:center;gap:12px;color:var(--ink-4);font-size:10px}.chevron{width:22px;height:22px;display:grid;place-items:center;border:1px solid var(--rule);border-radius:50%;color:var(--ink-3);font-size:13px}.event-card__detail{margin:0 4px 14px 32px;padding:16px;border:1px solid var(--rule-soft);border-radius:var(--radius-sm);background:#fbfcfa}.trace-grid{margin:0 0 14px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.trace-grid div{min-width:0;display:flex;flex-direction:column}.trace-grid dt,.payload-head{color:var(--ink-4);font-size:9px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.trace-grid dd{margin:3px 0 0;color:var(--ink-2);font-size:11px;overflow:hidden;text-overflow:ellipsis}.trace-link{padding:0;border:0;background:transparent;color:var(--accent-deep);cursor:pointer;text-decoration:underline;text-decoration-color:var(--accent-soft);text-underline-offset:3px}.trace-successors{max-height:82px;display:flex;flex-direction:column;gap:3px;overflow:auto!important}.nav-update-dot{width:6px;height:6px;margin-left:5px;display:inline-block;border-radius:50%;background:var(--warning);box-shadow:0 0 0 3px var(--warning-bg)}.flow-view-title{align-items:end}.flow-selector{min-width:250px;display:flex;flex-direction:column;gap:5px;color:var(--ink-4);font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.flow-selector select{width:100%;padding:9px 11px;border:1px solid var(--rule);border-radius:7px;background:var(--paper-2);color:var(--ink-2);font:11px var(--mono)}.graph-metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;overflow:hidden;border:1px solid var(--rule);border-radius:var(--radius-sm);background:var(--rule)}.graph-metrics span{padding:10px 14px;background:var(--paper-2);color:var(--ink-3);font-size:10px}.graph-metrics strong{margin-right:5px;color:var(--ink);font:600 14px var(--mono)}.flow-layout{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px;align-items:start}.flow-graph-panel{min-width:0;padding:0;overflow:hidden}.flow-legend{min-height:42px;padding:10px 14px;display:flex;gap:14px;align-items:center;border-bottom:1px solid var(--rule-soft);color:var(--ink-3);font-size:9px}.flow-legend>span{display:inline-flex;gap:5px;align-items:center}.flow-legend__hint{margin-left:auto;color:var(--ink-4)}.legend-dot{width:7px;height:7px;display:inline-block;border-radius:50%;background:var(--accent-deep)}.legend-dot--network{background:var(--network)}.legend-dot--evm{background:var(--evm)}.flow-canvas{max-height:720px;overflow:auto;background-color:#fbfcf9;background-image:radial-gradient(var(--rule) .7px,transparent .7px);background-size:14px 14px}.flow-canvas svg{display:block}.flow-phase-title{fill:var(--ink-3);font:700 9px var(--sans);letter-spacing:.08em;text-transform:uppercase}.flow-phase-rule{stroke:var(--rule);stroke-width:1}.flow-edge{fill:none;stroke:#cbd2cd;stroke-width:1.2;opacity:.72;transition:.12s ease}.flow-edge--selected{stroke:var(--accent-deep);stroke-width:2;opacity:1}.flow-arrow{fill:#aeb8b1}.flow-event{cursor:pointer;outline:none}.flow-event rect{fill:var(--paper-2);stroke:var(--rule);stroke-width:1;filter:drop-shadow(0 2px 3px rgb(20 32 27 / .04));transition:.12s ease}.flow-event circle{fill:var(--accent-deep)}.flow-event--network circle{fill:var(--network)}.flow-event--evm circle{fill:var(--evm)}.flow-event--error rect{fill:var(--danger-bg);stroke:#dfaaa7}.flow-event--error circle{fill:var(--danger)}.flow-event:hover rect,.flow-event:focus-visible rect,.flow-event--selected rect{stroke:var(--accent-deep);stroke-width:2;filter:drop-shadow(0 4px 8px rgb(31 107 74 / .14))}.flow-event__title{fill:var(--ink);font:600 9px var(--sans)}.flow-event__meta{fill:var(--ink-4);font:8px var(--mono)}.flow-detail{position:sticky;top:82px;max-height:calc(100vh - 110px);overflow:auto}.flow-detail h2{margin:4px 0;overflow-wrap:anywhere;font:22px/1.15 var(--serif)}.flow-detail__time{margin:0 0 14px;color:var(--ink-4);font-size:9px}.flow-detail-grid{margin:0 0 14px;display:flex;flex-direction:column}.flow-detail-grid div{padding:8px 0;border-bottom:1px solid var(--rule-soft)}.flow-detail-grid dt{color:var(--ink-4);font-size:8px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}.flow-detail-grid dd{margin:3px 0 0;overflow-wrap:anywhere;color:var(--ink-2);font-size:9px}.flow-successor-list{margin:0 0 14px;display:flex;flex-direction:column;gap:4px}.flow-successor-list button{padding:7px 8px;display:flex;justify-content:space-between;border:1px solid var(--rule-soft);border-radius:6px;background:var(--paper-2);color:var(--accent-deep);font-size:9px;text-align:left;cursor:pointer}.update-grid{display:grid;grid-template-columns:minmax(300px,.8fr) minmax(480px,1.2fr);gap:14px}.update-status{display:flex;gap:18px;align-items:center}.update-status h2{margin:3px 0;font:28px var(--serif)}.update-status p{margin:0;color:var(--ink-3);font-size:11px}.update-orbit{width:76px;height:76px;flex:none;display:grid;place-items:center;border:1px solid var(--accent-soft);border-radius:50%;background:var(--accent-bg);box-shadow:inset 0 0 0 9px var(--paper-2)}.update-orbit i{width:16px;height:16px;display:block;border-radius:50%;background:var(--accent-deep)}.update-orbit--available{border-color:#efd89d;background:var(--warning-bg);box-shadow:inset 0 0 0 9px var(--paper-2),0 0 0 4px #fff6dcb3}.update-orbit--available i{background:var(--warning)}.update-steps{margin:0;padding:0;display:grid;grid-template-columns:repeat(5,1fr);list-style:none;counter-reset:updates}.update-steps li{min-width:0;padding:10px;display:flex;flex-direction:column;gap:5px;border-left:1px solid var(--rule-soft);counter-increment:updates}.update-steps li:first-child{border-left:0}.update-steps li:before{content:"0" counter(updates);color:var(--accent-deep);font:9px var(--mono)}.update-steps strong{font-size:10px}.update-steps span{color:var(--ink-3);font-size:9px}.copy-command{padding:5px 6px;border:1px solid var(--accent-soft);border-radius:5px;background:var(--accent-bg);color:var(--accent-deep);font-size:8px;cursor:pointer}.release-notes a{color:var(--accent-deep);font-size:10px;font-weight:600;text-decoration:none}.release-date{color:var(--ink-4);font:9px var(--mono)}.release-notes>pre{max-height:420px;margin:0;padding:15px;overflow:auto;border-radius:8px;background:var(--paper-3);color:var(--ink-2);font:10px/1.6 var(--mono);white-space:pre-wrap}.log-viz{display:grid;grid-template-columns:210px 1fr;gap:24px;align-items:center}.log-viz h2{margin:3px 0 0;font:22px var(--serif)}.log-viz__bars{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}.log-viz__bars>div{display:grid;grid-template-columns:1fr auto;gap:5px;color:var(--ink-4);font-size:8px;text-transform:uppercase}.log-viz__bars i{height:4px;grid-column:1 / -1;overflow:hidden;border-radius:4px;background:var(--rule-soft)}.log-viz__bar{height:100%;display:block;border-radius:inherit;background:var(--ink-4)}.log-viz__bar--error{background:var(--danger)}.log-viz__bar--warn{background:var(--warning)}.log-viz__bar--info{background:var(--accent-deep)}.log-trace-context{padding:8px 13px;display:flex;gap:14px;border-top:1px solid var(--rule-soft);background:var(--accent-bg);color:var(--ink-3);font-size:8px}.source-viz{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.source-viz__item{padding:11px 13px;display:grid;grid-template-columns:1fr auto;gap:8px;border:1px solid var(--rule);border-radius:8px;background:var(--paper-2);color:var(--ink-3);font-size:9px;text-align:left;text-transform:capitalize;cursor:pointer}.source-viz__item--selected{border-color:var(--accent-deep);box-shadow:0 0 0 2px var(--accent-bg)}.source-viz__item i{height:4px;grid-column:1 / -1;overflow:hidden;border-radius:4px;background:var(--rule-soft)}.source-viz__fill{height:100%;display:block;background:var(--accent-deep)}.source-viz__fill--network{background:var(--network)}.source-viz__fill--evm{background:var(--evm)}.payload-head{margin-bottom:6px}.payload{max-height:360px;margin:0;padding:13px;overflow:auto;border-radius:7px;background:var(--ink);color:#d9fce8;font-size:10px;line-height:1.55}.empty-inline{padding:26px 12px;color:var(--ink-4);font-size:12px;text-align:center}.alert,.stale-banner,.failure-banner{padding:11px 14px;border-radius:var(--radius-sm);font-size:12px}.alert--warning,.stale-banner{background:var(--warning-bg);color:var(--warning)}.stale-banner{margin-bottom:16px}.inspector-layout{min-height:calc(100vh - 64px);display:grid;grid-template-columns:284px minmax(0,1fr)}.e3-list{position:sticky;top:64px;height:calc(100vh - 64px);margin-left:-28px;display:flex;flex-direction:column;border-right:1px solid var(--rule);background:var(--paper-2)}.e3-list__head{height:54px;padding:0 16px 0 28px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--rule);color:var(--ink-3);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.e3-list__head strong{min-width:24px;padding:2px 6px;border-radius:999px;background:var(--paper-3);text-align:center}.e3-list__items{flex:1;overflow:auto;padding:8px}.e3-list__item{width:100%;padding:12px 10px;display:grid;grid-template-columns:8px minmax(0,1fr) auto;gap:9px;align-items:start;border:0;border-radius:9px;background:transparent;text-align:left;cursor:pointer}.e3-list__item:hover{background:var(--rule-soft)}.e3-list__item--selected{background:var(--accent-bg)}.status-dot{width:7px;height:7px;margin-top:5px;border-radius:50%;background:var(--warning)}.status-dot--complete{background:var(--accent-deep)}.status-dot--failed{background:var(--danger)}.e3-list__item>span:nth-child(2){min-width:0;display:flex;flex-direction:column}.e3-list__item strong{overflow:hidden;font-size:11px;text-overflow:ellipsis}.e3-list__item small{color:var(--ink-4);font-size:9px;text-transform:capitalize}.e3-list__time{color:var(--ink-4);font-size:8px}.inspector-main{min-width:0;padding:34px 0 70px 30px}.trace-head{display:flex;justify-content:space-between;align-items:flex-start;gap:30px;margin-bottom:24px}.trace-head h1{font-family:var(--mono);font-size:clamp(25px,3vw,39px)}.trace-status{gap:7px;padding:7px 11px;background:var(--warning-bg);color:var(--warning)}.trace-status>span{width:6px;height:6px;border-radius:50%;background:currentColor}.trace-status--complete{color:var(--accent-deep);background:var(--accent-bg)}.trace-status--failed{color:var(--danger);background:var(--danger-bg)}.failure-banner{margin-bottom:18px;display:flex;align-items:center;gap:12px;background:var(--danger-bg);color:var(--danger)}.failure-banner code{overflow:hidden;font-size:10px;text-overflow:ellipsis;white-space:nowrap}.stage-flow{display:flex;width:100%;margin-bottom:22px;overflow-x:auto;padding:3px}.stage-flow__unit{min-width:135px;flex:1;display:flex;align-items:center}.stage-node{min-width:120px;flex:1;min-height:84px;padding:11px;display:grid;grid-template-columns:23px minmax(0,1fr) auto;gap:7px;align-items:start;border:1px solid var(--rule);border-radius:10px;background:var(--paper-2);text-align:left;cursor:pointer;transition:.12s ease}.stage-node:hover{border-color:var(--accent-soft);transform:translateY(-1px)}.stage-node--selected{border-color:var(--accent-deep);box-shadow:0 0 0 2px var(--accent-bg)}.stage-node--complete{background:#fbfefc}.stage-node--active{background:var(--accent-bg);border-color:var(--accent-soft)}.stage-node--failed{background:var(--danger-bg);border-color:#efcbc8}.stage-node__index{color:var(--ink-4);font-family:var(--mono);font-size:9px}.stage-node__copy{min-width:0;display:flex;flex-direction:column;gap:5px}.stage-node__copy strong{font-size:10px;line-height:1.25}.stage-node__copy span{color:var(--ink-4);font-family:var(--mono);font-size:7px;line-height:1.35}.stage-node__state{color:var(--ink-4);font-size:12px}.stage-node--complete .stage-node__state,.stage-node--active .stage-node__state{color:var(--accent-deep)}.stage-node--failed .stage-node__state{color:var(--danger)}.stage-flow__connector{width:10px;height:1px;flex:none;background:var(--rule)}.stage-flow__connector--done{background:var(--accent-deep)}.trace-columns{display:grid;grid-template-columns:minmax(520px,1fr) 300px;gap:18px;align-items:start}.trace-events{min-height:500px}.flow-note{margin:-4px 0 12px;padding:9px 11px;border-radius:7px;background:var(--paper-3);color:var(--ink-3);font-size:10px}.trace-sidebar{display:flex;flex-direction:column;gap:12px}.committee-list,.ticket-list,.reward-list{display:flex;flex-direction:column}.committee-member{padding:9px 0;display:grid;grid-template-columns:30px minmax(0,1fr) auto;gap:9px;align-items:center;border-bottom:1px solid var(--rule-soft)}.committee-member:last-child{border-bottom:0}.committee-member--expelled{opacity:.55}.committee-member__party{width:30px;height:30px;display:grid;place-items:center;border-radius:50%;background:var(--accent-bg);color:var(--accent-deep);font-size:9px}.committee-member>div{min-width:0;display:flex;flex-direction:column}.committee-member strong{overflow:hidden;font-size:10px;text-overflow:ellipsis}.committee-member small{color:var(--ink-4);font-size:8px}.ticket-list>div,.reward-list>div{padding:8px 0;display:grid;grid-template-columns:50px minmax(0,1fr) auto;gap:8px;align-items:center;border-bottom:1px solid var(--rule-soft)}.ticket-list span,.ticket-list strong,.ticket-list small,.reward-list strong,.reward-list small{overflow:hidden;font-size:9px;text-overflow:ellipsis}.ticket-list small,.reward-list small{color:var(--ink-4)}.reward-state{padding:2px 4px;border-radius:4px;background:var(--accent-bg);color:var(--accent-deep);font-size:7px;text-align:center;text-transform:uppercase}.reward-state--claimed{background:var(--paper-3);color:var(--ink-3)}.blank-state,.loading-page{min-height:60vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.blank-state h1,.loading-page h1{margin-top:18px;font-size:34px}.blank-state p,.loading-page p{max-width:500px;color:var(--ink-3)}.blank-state__glyph{width:64px;height:64px;display:grid;place-items:center;border:1px solid var(--accent-soft);border-radius:50%;background:var(--accent-bg);color:var(--accent-deep);font-family:var(--serif);font-size:18px}.loader-ring{width:30px;height:30px;display:block;border:2px solid var(--rule);border-top-color:var(--accent-deep);border-radius:50%;animation:spin .75s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.view-title{align-items:flex-start}.view-title h1{font-size:42px}.filter-bar{margin-bottom:18px;display:flex;align-items:center;gap:9px}.search-field{min-width:260px;flex:1;height:38px;padding:0 11px;display:flex;align-items:center;gap:8px;border:1px solid var(--rule);border-radius:8px;background:#fdfdfb}.search-field span{color:var(--ink-4)}.search-field input{width:100%;border:0;outline:0;background:transparent;color:var(--ink);font-size:12px}.filter-bar select{height:38px;padding:0 28px 0 10px;border:1px solid var(--rule);border-radius:8px;background:var(--paper-2);color:var(--ink-3);font-size:11px}.filter-count{margin-left:5px;color:var(--ink-4);font-size:9px}.log-table{overflow:hidden;border:1px solid var(--rule-soft);border-radius:9px}.log-table__head,.log-row summary{display:grid;grid-template-columns:90px 58px 190px minmax(0,1fr);gap:10px;align-items:center}.log-table__head{padding:8px 12px;background:var(--paper-3);color:var(--ink-4);font-size:8px;font-weight:600;letter-spacing:.07em;text-transform:uppercase}.log-row{border-top:1px solid var(--rule-soft)}.log-row summary{min-height:42px;padding:7px 12px;cursor:pointer;list-style:none}.log-row summary::-webkit-details-marker{display:none}.log-row summary:hover{background:#fafbf8}.log-row time,.log-row summary>span:nth-child(3){color:var(--ink-4);font-size:9px;overflow:hidden;text-overflow:ellipsis}.log-row strong{overflow:hidden;font-size:11px;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.log-level{width:fit-content;padding:2px 5px;border-radius:4px;background:var(--paper-3);color:var(--ink-3);font-size:7px;font-weight:700}.log-row--error .log-level{background:var(--danger-bg);color:var(--danger)}.log-row--warn .log-level{background:var(--warning-bg);color:var(--warning)}.log-row--info .log-level{background:var(--accent-bg);color:var(--accent-deep)}.log-row pre{max-height:320px;margin:0;padding:13px;overflow:auto;background:var(--ink);color:#d9fce8;font-size:9px}.site-foot{min-height:54px;margin-top:auto;padding:0 28px;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--rule);color:var(--ink-4);font-size:10px}@media (max-width: 1100px){.metrics-grid{grid-template-columns:1fr 1fr}.metric:nth-child(2){border-right:0}.metric:nth-child(-n+2){border-bottom:1px solid var(--rule)}.overview-grid,.trace-columns{grid-template-columns:1fr}.trace-sidebar{display:grid;grid-template-columns:repeat(3,1fr)}.site-nav{margin-left:4px}.flow-layout,.update-grid{grid-template-columns:1fr}.flow-detail{position:static;max-height:none}.update-steps{grid-template-columns:repeat(3,1fr)}}@media (max-width: 820px){.site-head__inner{padding:0 14px}.wordmark{width:108px}.product-name,.node-chip span,.site-nav__link{display:none}.site-nav{margin-left:auto}.site-nav__link--on{display:block}.node-chip{margin-left:0;padding:7px}.app-main{padding:28px 16px 60px}.app-main--inspector{padding-top:0}.view-intro{align-items:flex-start;flex-direction:column}.identity-card{width:100%}.inspector-layout{display:block}.e3-list{position:static;width:calc(100% + 32px);height:auto;max-height:230px;margin:0 -16px;border-right:0;border-bottom:1px solid var(--rule)}.e3-list__head{padding-left:16px}.inspector-main{padding:24px 0 60px}.trace-sidebar{grid-template-columns:1fr}.trace-head{flex-direction:column}.event-card__summary{grid-template-columns:18px 70px minmax(0,1fr)}.event-card__cause{display:none}.trace-grid{grid-template-columns:1fr 1fr}.filter-bar{align-items:stretch;flex-direction:column}.search-field{min-width:0}.filter-count{margin:4px 0}.log-table__head{display:none}.log-row summary{grid-template-columns:70px 50px minmax(0,1fr)}.log-row summary strong{grid-column:1 / -1}.flow-view-title{align-items:stretch;flex-direction:column}.flow-selector{width:100%}.graph-metrics{grid-template-columns:1fr 1fr}.flow-legend__hint{display:none!important}.update-steps{grid-template-columns:1fr}.update-steps li,.update-steps li:first-child{border-left:0;border-top:1px solid var(--rule-soft)}.update-steps li:first-child{border-top:0}.log-viz,.log-viz__bars{grid-template-columns:1fr}}@media (max-width: 520px){.metrics-grid{grid-template-columns:1fr}.metric{border-right:0;border-bottom:1px solid var(--rule)}.metric:last-child{border-bottom:0}.mini-dl,.trace-grid{grid-template-columns:1fr}.event-card__summary{grid-template-columns:18px minmax(0,1fr)}.event-card__time{display:none}.event-card__detail{margin-left:18px}.site-foot{align-items:flex-start;flex-direction:column;justify-content:center;gap:3px}.source-viz{grid-template-columns:1fr}}:root{--ink: #14201b;--ink-2: #2c3833;--ink-3: #5b6864;--ink-4: #8b9893;--rule: #e3e7e2;--rule-soft: #eef1ec;--paper: #f7f5ee;--paper-2: #ffffff;--paper-3: #f0ede4;--accent-bg: #e8faf0;--accent-soft: #cdeede;--accent-deep: #1f6b4a;--accent-ink: #163d2c;--danger: #a93632;--danger-bg: #fcebea;--warning: #906319;--warning-bg: #fff6dc;--network: #5d4f99;--evm: #8d5c16;--serif: Georgia, "Times New Roman", serif;--sans: "Geist", "Söhne", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;--mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;--radius: 14px;--radius-sm: 9px;color:var(--ink);background:var(--paper);font:16px/1.5 var(--sans);font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}*{box-sizing:border-box}html{min-width:320px;background:var(--paper)}body{margin:0;min-width:320px;min-height:100vh;background:var(--paper)}button,input,select{font:inherit}button{color:inherit}code,pre,.mono{font-family:var(--mono);font-feature-settings:"tnum";letter-spacing:-.015em}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--accent-deep);outline-offset:2px}::selection{background:var(--accent-soft);color:var(--accent-ink)} +.page { + min-height: 100vh; + display: flex; + flex-direction: column; +} +.site-head { + position: sticky; + top: 0; + z-index: 50; + border-bottom: 1px solid var(--rule); + background: color-mix(in oklab, var(--paper) 91%, transparent); + -webkit-backdrop-filter: blur(16px) saturate(130%); + backdrop-filter: blur(16px) saturate(130%); +} +.site-head__inner { + width: min(1500px, 100%); + height: 64px; + margin: 0 auto; + padding: 0 28px; + display: flex; + align-items: center; + gap: 16px; +} +.wordmark { + width: 140px; + height: 24px; + border: 0; + padding: 0; + background: transparent; + cursor: pointer; +} +.wordmark span { + display: block; + width: 100%; + height: 100%; + background: var(--ink); + mask: url(/assets/interfold.svg) no-repeat left center / contain; + -webkit-mask: url(/assets/interfold.svg) no-repeat left center / contain; +} +.product-name { + padding-left: 16px; + border-left: 1px solid var(--rule); + color: var(--ink-3); + font-size: 13px; + white-space: nowrap; +} +.site-nav { + display: flex; + align-items: center; + gap: 3px; + margin-left: 24px; +} +.site-nav__link { + border: 0; + background: transparent; + padding: 7px 12px; + border-radius: 999px; + color: var(--ink-3); + font-size: 14px; + cursor: pointer; + transition: 0.12s ease; +} +.site-nav__link:hover { + color: var(--ink); + background: var(--rule-soft); +} +.site-nav__link--on { + color: var(--accent-ink); + background: var(--accent-bg); +} +.node-chip { + margin-left: auto; + display: inline-flex; + align-items: center; + gap: 8px; + max-width: 230px; + padding: 6px 10px; + border: 1px solid var(--rule); + border-radius: 999px; + background: var(--paper-2); + color: var(--ink-2); + font-size: 12px; +} +.node-chip__dot, +.live-dot { + width: 7px; + height: 7px; + flex: none; + border-radius: 50%; + background: var(--accent-deep); + box-shadow: 0 0 0 4px var(--accent-bg); +} +.node-chip__dot--waiting { + background: var(--warning); + box-shadow: 0 0 0 4px var(--warning-bg); +} +.app-main { + flex: 1; + width: min(1320px, 100%); + margin: 0 auto; + padding: 42px 28px 80px; +} +.app-main--inspector { + width: min(1600px, 100%); + padding-top: 0; + padding-bottom: 0; +} +.view-stack { + display: flex; + flex-direction: column; + gap: 28px; +} +.view-intro, +.view-title { + display: flex; + justify-content: space-between; + gap: 36px; + align-items: flex-end; +} +.view-intro > div:first-child { + max-width: 780px; +} +.eyebrow, +.section-kicker { + display: flex; + align-items: center; + gap: 9px; + margin-bottom: 10px; + color: var(--accent-deep); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.view-intro h1, +.view-title h1, +.trace-head h1, +.blank-state h1, +.loading-page h1 { + margin: 0; + color: var(--ink); + font-family: var(--serif); + font-size: clamp(34px, 4vw, 52px); + font-weight: 400; + line-height: 1.08; + letter-spacing: -0.02em; +} +.view-intro p, +.view-title p, +.trace-head p { + margin: 14px 0 0; + color: var(--ink-3); + font-size: 16px; +} +.identity-card { + width: 280px; + padding: 18px 20px; + display: flex; + flex-direction: column; + gap: 7px; + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + box-shadow: 0 12px 28px -25px #14201b66; +} +.identity-card > span:first-child { + color: var(--ink-4); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.identity-card strong { + color: var(--ink); + font-size: 13px; +} +.identity-card > span:last-child { + color: var(--ink-4); + font-size: 11px; +} +.metrics-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + overflow: hidden; +} +.metric { + min-width: 0; + padding: 22px 24px; + display: flex; + flex-direction: column; + border-right: 1px solid var(--rule); +} +.metric:last-child { + border-right: 0; +} +.metric__label { + color: var(--ink-4); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.metric__value { + margin: 7px 0 3px; + color: var(--ink); + font-family: var(--serif); + font-size: 32px; + font-weight: 400; + line-height: 1; +} +.metric__note { + color: var(--ink-3); + font-size: 12px; +} +.metric--bad .metric__value { + color: var(--danger); +} +.overview-grid { + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 20px; + align-items: start; +} +.panel { + min-width: 0; + padding: 24px; + border: 1px solid var(--rule); + border-radius: var(--radius); + background: var(--paper-2); + box-shadow: 0 14px 36px -34px #14201b73; +} +.panel--compact { + padding: 18px; +} +.panel--wide { + width: 100%; +} +.panel__head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + margin-bottom: 18px; +} +.panel__head .section-kicker { + margin-bottom: 4px; +} +.panel__head h2 { + margin: 0; + color: var(--ink); + font-family: var(--serif); + font-size: 23px; + font-weight: 400; + line-height: 1.2; +} +.panel__aside { + color: var(--ink-4); + font-size: 11px; + white-space: nowrap; +} +.health-pill, +.status-tag, +.trace-status { + display: inline-flex; + align-items: center; + border-radius: 999px; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; +} +.health-pill { + padding: 5px 9px; + background: var(--accent-bg); + color: var(--accent-deep); +} +.health-pill--bad { + color: var(--warning); + background: var(--warning-bg); +} +.peer-list, +.chain-list { + display: flex; + flex-direction: column; + gap: 8px; +} +.peer-row { + display: grid; + grid-template-columns: 9px minmax(0, 1fr) auto; + gap: 11px; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid var(--rule-soft); +} +.peer-row:last-child { + border-bottom: 0; +} +.peer-row__status { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--accent-deep); +} +.peer-row > div { + min-width: 0; + display: flex; + flex-direction: column; +} +.peer-row strong { + color: var(--ink-2); + font-size: 12px; +} +.peer-row div span { + color: var(--ink-4); + font-size: 10px; + overflow: hidden; + text-overflow: ellipsis; +} +.peer-row__direction { + color: var(--ink-3); + font-size: 10px; +} +.listen-addresses { + margin-top: 14px; + padding-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 7px; + align-items: center; + border-top: 1px solid var(--rule-soft); +} +.listen-addresses > span { + margin-right: 4px; + color: var(--ink-4); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.listen-addresses code { + padding: 3px 7px; + border-radius: 5px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 9px; +} +.chain-card { + padding: 14px; + border: 1px solid var(--rule-soft); + border-radius: var(--radius-sm); + background: #fdfdfb; +} +.chain-card__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} +.chain-card__head strong { + font-size: 13px; +} +.status-tag { + padding: 4px 7px; + background: var(--paper-3); + color: var(--ink-3); +} +.status-tag--complete { + background: var(--accent-bg); + color: var(--accent-deep); +} +.status-tag--active { + background: var(--warning-bg); + color: var(--warning); +} +.status-tag--failed { + background: var(--danger-bg); + color: var(--danger); +} +.mini-dl { + margin: 0; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px 18px; +} +.mini-dl div { + min-width: 0; + display: flex; + flex-direction: column; +} +.mini-dl dt { + color: var(--ink-4); + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.mini-dl dd { + margin: 2px 0 0; + overflow: hidden; + color: var(--ink-2); + font-size: 12px; + text-overflow: ellipsis; +} +.event-timeline { + display: flex; + flex-direction: column; +} +.event-card { + position: relative; + border-bottom: 1px solid var(--rule-soft); + transition: + background 0.15s, + box-shadow 0.15s; +} +.event-card:last-child { + border-bottom: 0; +} +.event-card--located { + z-index: 1; + border-radius: 8px; + background: var(--accent-bg); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.event-card__summary { + width: 100%; + min-height: 64px; + padding: 10px 4px; + display: grid; + grid-template-columns: 22px 88px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + border: 0; + background: transparent; + text-align: left; + cursor: pointer; +} +.event-card__summary:hover { + background: #fafbf8; +} +.event-card__rail { + position: relative; + align-self: stretch; + display: flex; + justify-content: center; +} +.event-card__rail:after { + content: ''; + position: absolute; + top: 27px; + bottom: -22px; + width: 1px; + background: var(--rule); +} +.event-card:last-child .event-card__rail:after { + display: none; +} +.event-card__dot { + position: relative; + z-index: 1; + width: 8px; + height: 8px; + margin-top: 17px; + border: 2px solid var(--paper-2); + border-radius: 50%; + background: var(--accent-deep); + box-shadow: 0 0 0 1px var(--accent-soft); +} +.event-card--warn .event-card__dot { + background: var(--warning); + box-shadow: 0 0 0 1px #ead5a6; +} +.event-card--error .event-card__dot { + background: var(--danger); + box-shadow: 0 0 0 1px #e8b3b0; +} +.event-card--debug .event-card__dot { + background: var(--ink-4); + box-shadow: 0 0 0 1px var(--rule); +} +.event-card__time { + color: var(--ink-4); + font-size: 10px; +} +.event-card__identity { + min-width: 0; + display: flex; + flex-direction: column; + gap: 5px; +} +.event-card__identity strong { + overflow: hidden; + color: var(--ink-2); + font-size: 13px; + font-weight: 560; + text-overflow: ellipsis; +} +.event-card__meta { + display: flex; + flex-wrap: wrap; + gap: 7px; + align-items: center; + color: var(--ink-4); + font-size: 10px; +} +.source { + padding: 2px 5px; + border-radius: 4px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 8px; + font-weight: 700; + letter-spacing: 0.05em; +} +.source--network { + background: #f0edfa; + color: var(--network); +} +.source--evm { + background: #f9f0df; + color: var(--evm); +} +.event-card__cause { + display: flex; + align-items: center; + gap: 12px; + color: var(--ink-4); + font-size: 10px; +} +.chevron { + width: 22px; + height: 22px; + display: grid; + place-items: center; + border: 1px solid var(--rule); + border-radius: 50%; + color: var(--ink-3); + font-size: 13px; +} +.event-card__detail { + margin: 0 4px 14px 32px; + padding: 16px; + border: 1px solid var(--rule-soft); + border-radius: var(--radius-sm); + background: #fbfcfa; +} +.trace-grid { + margin: 0 0 14px; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} +.trace-grid div { + min-width: 0; + display: flex; + flex-direction: column; +} +.trace-grid dt, +.payload-head { + color: var(--ink-4); + font-size: 9px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; +} +.trace-grid dd { + margin: 3px 0 0; + color: var(--ink-2); + font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; +} +.trace-link { + padding: 0; + border: 0; + background: transparent; + color: var(--accent-deep); + cursor: pointer; + text-decoration: underline; + text-decoration-color: var(--accent-soft); + text-underline-offset: 3px; +} +.trace-successors { + max-height: 82px; + display: flex; + flex-direction: column; + gap: 3px; + overflow: auto !important; +} +.nav-update-dot { + width: 6px; + height: 6px; + margin-left: 5px; + display: inline-block; + border-radius: 50%; + background: var(--warning); + box-shadow: 0 0 0 3px var(--warning-bg); +} +.flow-view-title { + align-items: end; +} +.flow-selector { + min-width: 250px; + display: flex; + flex-direction: column; + gap: 5px; + color: var(--ink-4); + font-size: 9px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.flow-selector select { + width: 100%; + padding: 9px 11px; + border: 1px solid var(--rule); + border-radius: 7px; + background: var(--paper-2); + color: var(--ink-2); + font: 11px var(--mono); +} +.graph-metrics { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1px; + overflow: hidden; + border: 1px solid var(--rule); + border-radius: var(--radius-sm); + background: var(--rule); +} +.graph-metrics span { + padding: 10px 14px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 10px; +} +.graph-metrics strong { + margin-right: 5px; + color: var(--ink); + font: 600 14px var(--mono); +} +.flow-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + gap: 14px; + align-items: start; +} +.flow-graph-panel { + min-width: 0; + padding: 0; + overflow: hidden; +} +.flow-legend { + min-height: 42px; + padding: 10px 14px; + display: flex; + gap: 14px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); + color: var(--ink-3); + font-size: 9px; +} +.flow-legend > span { + display: inline-flex; + gap: 5px; + align-items: center; +} +.flow-legend__hint { + margin-left: auto; + color: var(--ink-4); +} +.legend-dot { + width: 7px; + height: 7px; + display: inline-block; + border-radius: 50%; + background: var(--accent-deep); +} +.legend-dot--network { + background: var(--network); +} +.legend-dot--evm { + background: var(--evm); +} +.flow-canvas { + max-height: 720px; + overflow: auto; + background-color: #fbfcf9; + background-image: radial-gradient(var(--rule) 0.7px, transparent 0.7px); + background-size: 14px 14px; +} +.flow-canvas svg { + display: block; +} +.flow-phase-title { + fill: var(--ink-3); + font: 700 9px var(--sans); + letter-spacing: 0.08em; + text-transform: uppercase; +} +.flow-phase-rule { + stroke: var(--rule); + stroke-width: 1; +} +.flow-edge { + fill: none; + stroke: #cbd2cd; + stroke-width: 1.2; + opacity: 0.72; + transition: 0.12s ease; +} +.flow-edge--selected { + stroke: var(--accent-deep); + stroke-width: 2; + opacity: 1; +} +.flow-arrow { + fill: #aeb8b1; +} +.flow-event { + cursor: pointer; + outline: none; +} +.flow-event rect { + fill: var(--paper-2); + stroke: var(--rule); + stroke-width: 1; + filter: drop-shadow(0 2px 3px rgb(20 32 27 / 0.04)); + transition: 0.12s ease; +} +.flow-event circle { + fill: var(--accent-deep); +} +.flow-event--network circle { + fill: var(--network); +} +.flow-event--evm circle { + fill: var(--evm); +} +.flow-event--error rect { + fill: var(--danger-bg); + stroke: #dfaaa7; +} +.flow-event--error circle { + fill: var(--danger); +} +.flow-event:hover rect, +.flow-event:focus-visible rect, +.flow-event--selected rect { + stroke: var(--accent-deep); + stroke-width: 2; + filter: drop-shadow(0 4px 8px rgb(31 107 74 / 0.14)); +} +.flow-event__title { + fill: var(--ink); + font: 600 9px var(--sans); +} +.flow-event__meta { + fill: var(--ink-4); + font: 8px var(--mono); +} +.flow-detail { + position: sticky; + top: 82px; + max-height: calc(100vh - 110px); + overflow: auto; +} +.flow-detail h2 { + margin: 4px 0; + overflow-wrap: anywhere; + font: 22px/1.15 var(--serif); +} +.flow-detail__time { + margin: 0 0 14px; + color: var(--ink-4); + font-size: 9px; +} +.flow-detail-grid { + margin: 0 0 14px; + display: flex; + flex-direction: column; +} +.flow-detail-grid div { + padding: 8px 0; + border-bottom: 1px solid var(--rule-soft); +} +.flow-detail-grid dt { + color: var(--ink-4); + font-size: 8px; + font-weight: 700; + letter-spacing: 0.07em; + text-transform: uppercase; +} +.flow-detail-grid dd { + margin: 3px 0 0; + overflow-wrap: anywhere; + color: var(--ink-2); + font-size: 9px; +} +.flow-successor-list { + margin: 0 0 14px; + display: flex; + flex-direction: column; + gap: 4px; +} +.flow-successor-list button { + padding: 7px 8px; + display: flex; + justify-content: space-between; + border: 1px solid var(--rule-soft); + border-radius: 6px; + background: var(--paper-2); + color: var(--accent-deep); + font-size: 9px; + text-align: left; + cursor: pointer; +} +.update-grid { + display: grid; + grid-template-columns: minmax(300px, 0.8fr) minmax(480px, 1.2fr); + gap: 14px; +} +.update-status { + display: flex; + gap: 18px; + align-items: center; +} +.update-status h2 { + margin: 3px 0; + font: 28px var(--serif); +} +.update-status p { + margin: 0; + color: var(--ink-3); + font-size: 11px; +} +.update-orbit { + width: 76px; + height: 76px; + flex: none; + display: grid; + place-items: center; + border: 1px solid var(--accent-soft); + border-radius: 50%; + background: var(--accent-bg); + box-shadow: inset 0 0 0 9px var(--paper-2); +} +.update-orbit i { + width: 16px; + height: 16px; + display: block; + border-radius: 50%; + background: var(--accent-deep); +} +.update-orbit--available { + border-color: #efd89d; + background: var(--warning-bg); + box-shadow: + inset 0 0 0 9px var(--paper-2), + 0 0 0 4px #fff6dcb3; +} +.update-orbit--available i { + background: var(--warning); +} +.update-steps { + margin: 0; + padding: 0; + display: grid; + grid-template-columns: repeat(5, 1fr); + list-style: none; + counter-reset: updates; +} +.update-steps li { + min-width: 0; + padding: 10px; + display: flex; + flex-direction: column; + gap: 5px; + border-left: 1px solid var(--rule-soft); + counter-increment: updates; +} +.update-steps li:first-child { + border-left: 0; +} +.update-steps li:before { + content: '0' counter(updates); + color: var(--accent-deep); + font: 9px var(--mono); +} +.update-steps strong { + font-size: 10px; +} +.update-steps span { + color: var(--ink-3); + font-size: 9px; +} +.copy-command { + padding: 5px 6px; + border: 1px solid var(--accent-soft); + border-radius: 5px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 8px; + cursor: pointer; +} +.release-notes a { + color: var(--accent-deep); + font-size: 10px; + font-weight: 600; + text-decoration: none; +} +.release-date { + color: var(--ink-4); + font: 9px var(--mono); +} +.release-notes > pre { + max-height: 420px; + margin: 0; + padding: 15px; + overflow: auto; + border-radius: 8px; + background: var(--paper-3); + color: var(--ink-2); + font: 10px/1.6 var(--mono); + white-space: pre-wrap; +} +.log-viz { + display: grid; + grid-template-columns: 210px 1fr; + gap: 24px; + align-items: center; +} +.log-viz h2 { + margin: 3px 0 0; + font: 22px var(--serif); +} +.log-viz__bars { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 10px; +} +.log-viz__bars > div { + display: grid; + grid-template-columns: 1fr auto; + gap: 5px; + color: var(--ink-4); + font-size: 8px; + text-transform: uppercase; +} +.log-viz__bars i { + height: 4px; + grid-column: 1 / -1; + overflow: hidden; + border-radius: 4px; + background: var(--rule-soft); +} +.log-viz__bar { + height: 100%; + display: block; + border-radius: inherit; + background: var(--ink-4); +} +.log-viz__bar--error { + background: var(--danger); +} +.log-viz__bar--warn { + background: var(--warning); +} +.log-viz__bar--info { + background: var(--accent-deep); +} +.log-trace-context { + padding: 8px 13px; + display: flex; + gap: 14px; + border-top: 1px solid var(--rule-soft); + background: var(--accent-bg); + color: var(--ink-3); + font-size: 8px; +} +.source-viz { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; +} +.source-viz__item { + padding: 11px 13px; + display: grid; + grid-template-columns: 1fr auto; + gap: 8px; + border: 1px solid var(--rule); + border-radius: 8px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 9px; + text-align: left; + text-transform: capitalize; + cursor: pointer; +} +.source-viz__item--selected { + border-color: var(--accent-deep); + box-shadow: 0 0 0 2px var(--accent-bg); +} +.source-viz__item i { + height: 4px; + grid-column: 1 / -1; + overflow: hidden; + border-radius: 4px; + background: var(--rule-soft); +} +.source-viz__fill { + height: 100%; + display: block; + background: var(--accent-deep); +} +.source-viz__fill--network { + background: var(--network); +} +.source-viz__fill--evm { + background: var(--evm); +} +.payload-head { + margin-bottom: 6px; +} +.payload { + max-height: 360px; + margin: 0; + padding: 13px; + overflow: auto; + border-radius: 7px; + background: var(--ink); + color: #d9fce8; + font-size: 10px; + line-height: 1.55; +} +.empty-inline { + padding: 26px 12px; + color: var(--ink-4); + font-size: 12px; + text-align: center; +} +.alert, +.stale-banner, +.failure-banner { + padding: 11px 14px; + border-radius: var(--radius-sm); + font-size: 12px; +} +.alert--warning, +.stale-banner { + background: var(--warning-bg); + color: var(--warning); +} +.stale-banner { + margin-bottom: 16px; +} +.inspector-layout { + min-height: calc(100vh - 64px); + display: grid; + grid-template-columns: 284px minmax(0, 1fr); +} +.e3-list { + position: sticky; + top: 64px; + height: calc(100vh - 64px); + margin-left: -28px; + display: flex; + flex-direction: column; + border-right: 1px solid var(--rule); + background: var(--paper-2); +} +.e3-list__head { + height: 54px; + padding: 0 16px 0 28px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--rule); + color: var(--ink-3); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; +} +.e3-list__head strong { + min-width: 24px; + padding: 2px 6px; + border-radius: 999px; + background: var(--paper-3); + text-align: center; +} +.e3-list__items { + flex: 1; + overflow: auto; + padding: 8px; +} +.e3-list__item { + width: 100%; + padding: 12px 10px; + display: grid; + grid-template-columns: 8px minmax(0, 1fr) auto; + gap: 9px; + align-items: start; + border: 0; + border-radius: 9px; + background: transparent; + text-align: left; + cursor: pointer; +} +.e3-list__item:hover { + background: var(--rule-soft); +} +.e3-list__item--selected { + background: var(--accent-bg); +} +.status-dot { + width: 7px; + height: 7px; + margin-top: 5px; + border-radius: 50%; + background: var(--warning); +} +.status-dot--complete { + background: var(--accent-deep); +} +.status-dot--failed { + background: var(--danger); +} +.e3-list__item > span:nth-child(2) { + min-width: 0; + display: flex; + flex-direction: column; +} +.e3-list__item strong { + overflow: hidden; + font-size: 11px; + text-overflow: ellipsis; +} +.e3-list__item small { + color: var(--ink-4); + font-size: 9px; + text-transform: capitalize; +} +.e3-list__time { + color: var(--ink-4); + font-size: 8px; +} +.inspector-main { + min-width: 0; + padding: 34px 0 70px 30px; +} +.trace-head { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 30px; + margin-bottom: 24px; +} +.trace-head h1 { + font-family: var(--mono); + font-size: clamp(25px, 3vw, 39px); +} +.trace-status { + gap: 7px; + padding: 7px 11px; + background: var(--warning-bg); + color: var(--warning); +} +.trace-status > span { + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; +} +.trace-status--complete { + color: var(--accent-deep); + background: var(--accent-bg); +} +.trace-status--failed { + color: var(--danger); + background: var(--danger-bg); +} +.failure-banner { + margin-bottom: 18px; + display: flex; + align-items: center; + gap: 12px; + background: var(--danger-bg); + color: var(--danger); +} +.failure-banner code { + overflow: hidden; + font-size: 10px; + text-overflow: ellipsis; + white-space: nowrap; +} +.stage-flow { + display: flex; + width: 100%; + margin-bottom: 22px; + overflow-x: auto; + padding: 3px; +} +.stage-flow__unit { + min-width: 135px; + flex: 1; + display: flex; + align-items: center; +} +.stage-node { + min-width: 120px; + flex: 1; + min-height: 84px; + padding: 11px; + display: grid; + grid-template-columns: 23px minmax(0, 1fr) auto; + gap: 7px; + align-items: start; + border: 1px solid var(--rule); + border-radius: 10px; + background: var(--paper-2); + text-align: left; + cursor: pointer; + transition: 0.12s ease; +} +.stage-node:hover { + border-color: var(--accent-soft); + transform: translateY(-1px); +} +.stage-node--selected { + border-color: var(--accent-deep); + box-shadow: 0 0 0 2px var(--accent-bg); +} +.stage-node--complete { + background: #fbfefc; +} +.stage-node--active { + background: var(--accent-bg); + border-color: var(--accent-soft); +} +.stage-node--failed { + background: var(--danger-bg); + border-color: #efcbc8; +} +.stage-node__index { + color: var(--ink-4); + font-family: var(--mono); + font-size: 9px; +} +.stage-node__copy { + min-width: 0; + display: flex; + flex-direction: column; + gap: 5px; +} +.stage-node__copy strong { + font-size: 10px; + line-height: 1.25; +} +.stage-node__copy span { + color: var(--ink-4); + font-family: var(--mono); + font-size: 7px; + line-height: 1.35; +} +.stage-node__state { + color: var(--ink-4); + font-size: 12px; +} +.stage-node--complete .stage-node__state, +.stage-node--active .stage-node__state { + color: var(--accent-deep); +} +.stage-node--failed .stage-node__state { + color: var(--danger); +} +.stage-flow__connector { + width: 10px; + height: 1px; + flex: none; + background: var(--rule); +} +.stage-flow__connector--done { + background: var(--accent-deep); +} +.trace-columns { + display: grid; + grid-template-columns: minmax(520px, 1fr) 300px; + gap: 18px; + align-items: start; +} +.trace-events { + min-height: 500px; +} +.flow-note { + margin: -4px 0 12px; + padding: 9px 11px; + border-radius: 7px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 10px; +} +.trace-sidebar { + display: flex; + flex-direction: column; + gap: 12px; +} +.committee-list, +.ticket-list, +.reward-list { + display: flex; + flex-direction: column; +} +.committee-member { + padding: 9px 0; + display: grid; + grid-template-columns: 30px minmax(0, 1fr) auto; + gap: 9px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); +} +.committee-member:last-child { + border-bottom: 0; +} +.committee-member--expelled { + opacity: 0.55; +} +.committee-member__party { + width: 30px; + height: 30px; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 9px; +} +.committee-member > div { + min-width: 0; + display: flex; + flex-direction: column; +} +.committee-member strong { + overflow: hidden; + font-size: 10px; + text-overflow: ellipsis; +} +.committee-member small { + color: var(--ink-4); + font-size: 8px; +} +.ticket-list > div, +.reward-list > div { + padding: 8px 0; + display: grid; + grid-template-columns: 50px minmax(0, 1fr) auto; + gap: 8px; + align-items: center; + border-bottom: 1px solid var(--rule-soft); +} +.ticket-list span, +.ticket-list strong, +.ticket-list small, +.reward-list strong, +.reward-list small { + overflow: hidden; + font-size: 9px; + text-overflow: ellipsis; +} +.ticket-list small, +.reward-list small { + color: var(--ink-4); +} +.reward-state { + padding: 2px 4px; + border-radius: 4px; + background: var(--accent-bg); + color: var(--accent-deep); + font-size: 7px; + text-align: center; + text-transform: uppercase; +} +.reward-state--claimed { + background: var(--paper-3); + color: var(--ink-3); +} +.blank-state, +.loading-page { + min-height: 60vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} +.blank-state h1, +.loading-page h1 { + margin-top: 18px; + font-size: 34px; +} +.blank-state p, +.loading-page p { + max-width: 500px; + color: var(--ink-3); +} +.blank-state__glyph { + width: 64px; + height: 64px; + display: grid; + place-items: center; + border: 1px solid var(--accent-soft); + border-radius: 50%; + background: var(--accent-bg); + color: var(--accent-deep); + font-family: var(--serif); + font-size: 18px; +} +.loader-ring { + width: 30px; + height: 30px; + display: block; + border: 2px solid var(--rule); + border-top-color: var(--accent-deep); + border-radius: 50%; + animation: spin 0.75s linear infinite; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +.view-title { + align-items: flex-start; +} +.view-title h1 { + font-size: 42px; +} +.filter-bar { + margin-bottom: 18px; + display: flex; + align-items: center; + gap: 9px; +} +.search-field { + min-width: 260px; + flex: 1; + height: 38px; + padding: 0 11px; + display: flex; + align-items: center; + gap: 8px; + border: 1px solid var(--rule); + border-radius: 8px; + background: #fdfdfb; +} +.search-field span { + color: var(--ink-4); +} +.search-field input { + width: 100%; + border: 0; + outline: 0; + background: transparent; + color: var(--ink); + font-size: 12px; +} +.filter-bar select { + height: 38px; + padding: 0 28px 0 10px; + border: 1px solid var(--rule); + border-radius: 8px; + background: var(--paper-2); + color: var(--ink-3); + font-size: 11px; +} +.filter-count { + margin-left: 5px; + color: var(--ink-4); + font-size: 9px; +} +.log-table { + overflow: hidden; + border: 1px solid var(--rule-soft); + border-radius: 9px; +} +.log-table__head, +.log-row summary { + display: grid; + grid-template-columns: 90px 58px 190px minmax(0, 1fr); + gap: 10px; + align-items: center; +} +.log-table__head { + padding: 8px 12px; + background: var(--paper-3); + color: var(--ink-4); + font-size: 8px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; +} +.log-row { + border-top: 1px solid var(--rule-soft); +} +.log-row summary { + min-height: 42px; + padding: 7px 12px; + cursor: pointer; + list-style: none; +} +.log-row summary::-webkit-details-marker { + display: none; +} +.log-row summary:hover { + background: #fafbf8; +} +.log-row time, +.log-row summary > span:nth-child(3) { + color: var(--ink-4); + font-size: 9px; + overflow: hidden; + text-overflow: ellipsis; +} +.log-row strong { + overflow: hidden; + font-size: 11px; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; +} +.log-level { + width: fit-content; + padding: 2px 5px; + border-radius: 4px; + background: var(--paper-3); + color: var(--ink-3); + font-size: 7px; + font-weight: 700; +} +.log-row--error .log-level { + background: var(--danger-bg); + color: var(--danger); +} +.log-row--warn .log-level { + background: var(--warning-bg); + color: var(--warning); +} +.log-row--info .log-level { + background: var(--accent-bg); + color: var(--accent-deep); +} +.log-row pre { + max-height: 320px; + margin: 0; + padding: 13px; + overflow: auto; + background: var(--ink); + color: #d9fce8; + font-size: 9px; +} +.site-foot { + min-height: 54px; + margin-top: auto; + padding: 0 28px; + display: flex; + align-items: center; + justify-content: space-between; + border-top: 1px solid var(--rule); + color: var(--ink-4); + font-size: 10px; +} +@media (max-width: 1100px) { + .metrics-grid { + grid-template-columns: 1fr 1fr; + } + .metric:nth-child(2) { + border-right: 0; + } + .metric:nth-child(-n + 2) { + border-bottom: 1px solid var(--rule); + } + .overview-grid, + .trace-columns { + grid-template-columns: 1fr; + } + .trace-sidebar { + display: grid; + grid-template-columns: repeat(3, 1fr); + } + .site-nav { + margin-left: 4px; + } + .flow-layout, + .update-grid { + grid-template-columns: 1fr; + } + .flow-detail { + position: static; + max-height: none; + } + .update-steps { + grid-template-columns: repeat(3, 1fr); + } +} +@media (max-width: 820px) { + .site-head__inner { + padding: 0 14px; + } + .wordmark { + width: 108px; + } + .product-name, + .node-chip span, + .site-nav__link { + display: none; + } + .site-nav { + margin-left: auto; + } + .site-nav__link--on { + display: block; + } + .node-chip { + margin-left: 0; + padding: 7px; + } + .app-main { + padding: 28px 16px 60px; + } + .app-main--inspector { + padding-top: 0; + } + .view-intro { + align-items: flex-start; + flex-direction: column; + } + .identity-card { + width: 100%; + } + .inspector-layout { + display: block; + } + .e3-list { + position: static; + width: calc(100% + 32px); + height: auto; + max-height: 230px; + margin: 0 -16px; + border-right: 0; + border-bottom: 1px solid var(--rule); + } + .e3-list__head { + padding-left: 16px; + } + .inspector-main { + padding: 24px 0 60px; + } + .trace-sidebar { + grid-template-columns: 1fr; + } + .trace-head { + flex-direction: column; + } + .event-card__summary { + grid-template-columns: 18px 70px minmax(0, 1fr); + } + .event-card__cause { + display: none; + } + .trace-grid { + grid-template-columns: 1fr 1fr; + } + .filter-bar { + align-items: stretch; + flex-direction: column; + } + .search-field { + min-width: 0; + } + .filter-count { + margin: 4px 0; + } + .log-table__head { + display: none; + } + .log-row summary { + grid-template-columns: 70px 50px minmax(0, 1fr); + } + .log-row summary strong { + grid-column: 1 / -1; + } + .flow-view-title { + align-items: stretch; + flex-direction: column; + } + .flow-selector { + width: 100%; + } + .graph-metrics { + grid-template-columns: 1fr 1fr; + } + .flow-legend__hint { + display: none !important; + } + .update-steps { + grid-template-columns: 1fr; + } + .update-steps li, + .update-steps li:first-child { + border-left: 0; + border-top: 1px solid var(--rule-soft); + } + .update-steps li:first-child { + border-top: 0; + } + .log-viz, + .log-viz__bars { + grid-template-columns: 1fr; + } +} +@media (max-width: 520px) { + .metrics-grid { + grid-template-columns: 1fr; + } + .metric { + border-right: 0; + border-bottom: 1px solid var(--rule); + } + .metric:last-child { + border-bottom: 0; + } + .mini-dl, + .trace-grid { + grid-template-columns: 1fr; + } + .event-card__summary { + grid-template-columns: 18px minmax(0, 1fr); + } + .event-card__time { + display: none; + } + .event-card__detail { + margin-left: 18px; + } + .site-foot { + align-items: flex-start; + flex-direction: column; + justify-content: center; + gap: 3px; + } + .source-viz { + grid-template-columns: 1fr; + } +} +:root { + --ink: #14201b; + --ink-2: #2c3833; + --ink-3: #5b6864; + --ink-4: #8b9893; + --rule: #e3e7e2; + --rule-soft: #eef1ec; + --paper: #f7f5ee; + --paper-2: #ffffff; + --paper-3: #f0ede4; + --accent-bg: #e8faf0; + --accent-soft: #cdeede; + --accent-deep: #1f6b4a; + --accent-ink: #163d2c; + --danger: #a93632; + --danger-bg: #fcebea; + --warning: #906319; + --warning-bg: #fff6dc; + --network: #5d4f99; + --evm: #8d5c16; + --serif: Georgia, 'Times New Roman', serif; + --sans: 'Geist', 'Söhne', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; + --mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace; + --radius: 14px; + --radius-sm: 9px; + color: var(--ink); + background: var(--paper); + font: 16px/1.5 var(--sans); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} +* { + box-sizing: border-box; +} +html { + min-width: 320px; + background: var(--paper); +} +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: var(--paper); +} +button, +input, +select { + font: inherit; +} +button { + color: inherit; +} +code, +pre, +.mono { + font-family: var(--mono); + font-feature-settings: 'tnum'; + letter-spacing: -0.015em; +} +button:focus-visible, +input:focus-visible, +select:focus-visible { + outline: 2px solid var(--accent-deep); + outline-offset: 2px; +} +::selection { + background: var(--accent-soft); + color: var(--accent-ink); +} diff --git a/crates/dashboard/assets/app.js b/crates/dashboard/assets/app.js index b3f57e1501..0df1ad691c 100644 --- a/crates/dashboard/assets/app.js +++ b/crates/dashboard/assets/app.js @@ -4,7 +4,41 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const i of l)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(l){const i={};return l.integrity&&(i.integrity=l.integrity),l.referrerPolicy&&(i.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?i.credentials="include":l.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(l){if(l.ep)return;l.ep=!0;const i=n(l);fetch(l.href,i)}})();function xc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var nu={exports:{}},sl={},ru={exports:{}},R={};/** +;(function () { + const t = document.createElement('link').relList + if (t && t.supports && t.supports('modulepreload')) return + for (const l of document.querySelectorAll('link[rel="modulepreload"]')) r(l) + new MutationObserver((l) => { + for (const i of l) if (i.type === 'childList') for (const s of i.addedNodes) s.tagName === 'LINK' && s.rel === 'modulepreload' && r(s) + }).observe(document, { childList: !0, subtree: !0 }) + function n(l) { + const i = {} + return ( + l.integrity && (i.integrity = l.integrity), + l.referrerPolicy && (i.referrerPolicy = l.referrerPolicy), + l.crossOrigin === 'use-credentials' + ? (i.credentials = 'include') + : l.crossOrigin === 'anonymous' + ? (i.credentials = 'omit') + : (i.credentials = 'same-origin'), + i + ) + } + function r(l) { + if (l.ep) return + l.ep = !0 + const i = n(l) + fetch(l.href, i) + } +})() +function xc(e) { + return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, 'default') ? e.default : e +} +var nu = { exports: {} }, + sl = {}, + ru = { exports: {} }, + R = {} +/** * @license React * react.production.min.js * @@ -12,7 +46,330 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Zn=Symbol.for("react.element"),wc=Symbol.for("react.portal"),kc=Symbol.for("react.fragment"),_c=Symbol.for("react.strict_mode"),jc=Symbol.for("react.profiler"),Sc=Symbol.for("react.provider"),Nc=Symbol.for("react.context"),Ec=Symbol.for("react.forward_ref"),Cc=Symbol.for("react.suspense"),Pc=Symbol.for("react.memo"),Lc=Symbol.for("react.lazy"),Ws=Symbol.iterator;function zc(e){return e===null||typeof e!="object"?null:(e=Ws&&e[Ws]||e["@@iterator"],typeof e=="function"?e:null)}var lu={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},iu=Object.assign,su={};function on(e,t,n){this.props=e,this.context=t,this.refs=su,this.updater=n||lu}on.prototype.isReactComponent={};on.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};on.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function ou(){}ou.prototype=on.prototype;function Yi(e,t,n){this.props=e,this.context=t,this.refs=su,this.updater=n||lu}var Xi=Yi.prototype=new ou;Xi.constructor=Yi;iu(Xi,on.prototype);Xi.isPureReactComponent=!0;var Qs=Array.isArray,uu=Object.prototype.hasOwnProperty,Gi={current:null},au={key:!0,ref:!0,__self:!0,__source:!0};function cu(e,t,n){var r,l={},i=null,s=null;if(t!=null)for(r in t.ref!==void 0&&(s=t.ref),t.key!==void 0&&(i=""+t.key),t)uu.call(t,r)&&!au.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,Z=S[Q];if(0>>1;Ql(Sl,z))ytl(rr,Sl)?(S[Q]=rr,S[yt]=z,Q=yt):(S[Q]=Sl,S[gt]=z,Q=gt);else if(ytl(rr,z))S[Q]=rr,S[yt]=z,Q=yt;else break e}}return L}function l(S,L){var z=S.sortIndex-L.sortIndex;return z!==0?z:S.id-L.id}if(typeof performance=="object"&&typeof performance.now=="function"){var i=performance;e.unstable_now=function(){return i.now()}}else{var s=Date,u=s.now();e.unstable_now=function(){return s.now()-u}}var a=[],c=[],m=1,p=null,f=3,y=!1,k=!1,g=!1,P=typeof setTimeout=="function"?setTimeout:null,h=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function v(S){for(var L=n(c);L!==null;){if(L.callback===null)r(c);else if(L.startTime<=S)r(c),L.sortIndex=L.expirationTime,t(a,L);else break;L=n(c)}}function x(S){if(g=!1,v(S),!k)if(n(a)!==null)k=!0,_l(j);else{var L=n(c);L!==null&&jl(x,L.startTime-S)}}function j(S,L){k=!1,g&&(g=!1,h(C),C=-1),y=!0;var z=f;try{for(v(L),p=n(a);p!==null&&(!(p.expirationTime>L)||S&&!Pe());){var Q=p.callback;if(typeof Q=="function"){p.callback=null,f=p.priorityLevel;var Z=Q(p.expirationTime<=L);L=e.unstable_now(),typeof Z=="function"?p.callback=Z:p===n(a)&&r(a),v(L)}else r(a);p=n(a)}if(p!==null)var nr=!0;else{var gt=n(c);gt!==null&&jl(x,gt.startTime-L),nr=!1}return nr}finally{p=null,f=z,y=!1}}var N=!1,E=null,C=-1,W=5,O=-1;function Pe(){return!(e.unstable_now()-OS||125Q?(S.sortIndex=z,t(c,S),n(a)===null&&S===n(c)&&(g?(h(C),C=-1):g=!0,jl(x,z-Q))):(S.sortIndex=Z,t(a,S),k||y||(k=!0,_l(j))),S},e.unstable_shouldYield=Pe,e.unstable_wrapCallback=function(S){var L=f;return function(){var z=f;f=L;try{return S.apply(this,arguments)}finally{f=z}}}})(mu);hu.exports=mu;var Bc=hu.exports;/** + */ ;(function (e) { + function t(S, L) { + var z = S.length + S.push(L) + e: for (; 0 < z; ) { + var Q = (z - 1) >>> 1, + Z = S[Q] + if (0 < l(Z, L)) ((S[Q] = L), (S[z] = Z), (z = Q)) + else break e + } + } + function n(S) { + return S.length === 0 ? null : S[0] + } + function r(S) { + if (S.length === 0) return null + var L = S[0], + z = S.pop() + if (z !== L) { + S[0] = z + e: for (var Q = 0, Z = S.length, nr = Z >>> 1; Q < nr; ) { + var gt = 2 * (Q + 1) - 1, + Sl = S[gt], + yt = gt + 1, + rr = S[yt] + if (0 > l(Sl, z)) yt < Z && 0 > l(rr, Sl) ? ((S[Q] = rr), (S[yt] = z), (Q = yt)) : ((S[Q] = Sl), (S[gt] = z), (Q = gt)) + else if (yt < Z && 0 > l(rr, z)) ((S[Q] = rr), (S[yt] = z), (Q = yt)) + else break e + } + } + return L + } + function l(S, L) { + var z = S.sortIndex - L.sortIndex + return z !== 0 ? z : S.id - L.id + } + if (typeof performance == 'object' && typeof performance.now == 'function') { + var i = performance + e.unstable_now = function () { + return i.now() + } + } else { + var s = Date, + u = s.now() + e.unstable_now = function () { + return s.now() - u + } + } + var a = [], + c = [], + m = 1, + p = null, + f = 3, + y = !1, + k = !1, + g = !1, + P = typeof setTimeout == 'function' ? setTimeout : null, + h = typeof clearTimeout == 'function' ? clearTimeout : null, + d = typeof setImmediate < 'u' ? setImmediate : null + typeof navigator < 'u' && + navigator.scheduling !== void 0 && + navigator.scheduling.isInputPending !== void 0 && + navigator.scheduling.isInputPending.bind(navigator.scheduling) + function v(S) { + for (var L = n(c); L !== null; ) { + if (L.callback === null) r(c) + else if (L.startTime <= S) (r(c), (L.sortIndex = L.expirationTime), t(a, L)) + else break + L = n(c) + } + } + function x(S) { + if (((g = !1), v(S), !k)) + if (n(a) !== null) ((k = !0), _l(j)) + else { + var L = n(c) + L !== null && jl(x, L.startTime - S) + } + } + function j(S, L) { + ;((k = !1), g && ((g = !1), h(C), (C = -1)), (y = !0)) + var z = f + try { + for (v(L), p = n(a); p !== null && (!(p.expirationTime > L) || (S && !Pe())); ) { + var Q = p.callback + if (typeof Q == 'function') { + ;((p.callback = null), (f = p.priorityLevel)) + var Z = Q(p.expirationTime <= L) + ;((L = e.unstable_now()), typeof Z == 'function' ? (p.callback = Z) : p === n(a) && r(a), v(L)) + } else r(a) + p = n(a) + } + if (p !== null) var nr = !0 + else { + var gt = n(c) + ;(gt !== null && jl(x, gt.startTime - L), (nr = !1)) + } + return nr + } finally { + ;((p = null), (f = z), (y = !1)) + } + } + var N = !1, + E = null, + C = -1, + W = 5, + O = -1 + function Pe() { + return !(e.unstable_now() - O < W) + } + function cn() { + if (E !== null) { + var S = e.unstable_now() + O = S + var L = !0 + try { + L = E(!0, S) + } finally { + L ? dn() : ((N = !1), (E = null)) + } + } else N = !1 + } + var dn + if (typeof d == 'function') + dn = function () { + d(cn) + } + else if (typeof MessageChannel < 'u') { + var Hs = new MessageChannel(), + yc = Hs.port2 + ;((Hs.port1.onmessage = cn), + (dn = function () { + yc.postMessage(null) + })) + } else + dn = function () { + P(cn, 0) + } + function _l(S) { + ;((E = S), N || ((N = !0), dn())) + } + function jl(S, L) { + C = P(function () { + S(e.unstable_now()) + }, L) + } + ;((e.unstable_IdlePriority = 5), + (e.unstable_ImmediatePriority = 1), + (e.unstable_LowPriority = 4), + (e.unstable_NormalPriority = 3), + (e.unstable_Profiling = null), + (e.unstable_UserBlockingPriority = 2), + (e.unstable_cancelCallback = function (S) { + S.callback = null + }), + (e.unstable_continueExecution = function () { + k || y || ((k = !0), _l(j)) + }), + (e.unstable_forceFrameRate = function (S) { + 0 > S || 125 < S + ? console.error('forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported') + : (W = 0 < S ? Math.floor(1e3 / S) : 5) + }), + (e.unstable_getCurrentPriorityLevel = function () { + return f + }), + (e.unstable_getFirstCallbackNode = function () { + return n(a) + }), + (e.unstable_next = function (S) { + switch (f) { + case 1: + case 2: + case 3: + var L = 3 + break + default: + L = f + } + var z = f + f = L + try { + return S() + } finally { + f = z + } + }), + (e.unstable_pauseExecution = function () {}), + (e.unstable_requestPaint = function () {}), + (e.unstable_runWithPriority = function (S, L) { + switch (S) { + case 1: + case 2: + case 3: + case 4: + case 5: + break + default: + S = 3 + } + var z = f + f = S + try { + return L() + } finally { + f = z + } + }), + (e.unstable_scheduleCallback = function (S, L, z) { + var Q = e.unstable_now() + switch ((typeof z == 'object' && z !== null ? ((z = z.delay), (z = typeof z == 'number' && 0 < z ? Q + z : Q)) : (z = Q), S)) { + case 1: + var Z = -1 + break + case 2: + Z = 250 + break + case 5: + Z = 1073741823 + break + case 4: + Z = 1e4 + break + default: + Z = 5e3 + } + return ( + (Z = z + Z), + (S = { id: m++, callback: L, priorityLevel: S, startTime: z, expirationTime: Z, sortIndex: -1 }), + z > Q + ? ((S.sortIndex = z), t(c, S), n(a) === null && S === n(c) && (g ? (h(C), (C = -1)) : (g = !0), jl(x, z - Q))) + : ((S.sortIndex = Z), t(a, S), k || y || ((k = !0), _l(j))), + S + ) + }), + (e.unstable_shouldYield = Pe), + (e.unstable_wrapCallback = function (S) { + var L = f + return function () { + var z = f + f = L + try { + return S.apply(this, arguments) + } finally { + f = z + } + } + })) +})(mu) +hu.exports = mu +var Bc = hu.exports +/** * @license React * react-dom.production.min.js * @@ -36,11 +670,8551 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Hc=T,xe=Bc;function w(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),ti=Object.prototype.hasOwnProperty,Wc=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Ys={},Xs={};function Qc(e){return ti.call(Xs,e)?!0:ti.call(Ys,e)?!1:Wc.test(e)?Xs[e]=!0:(Ys[e]=!0,!1)}function Kc(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Yc(e,t,n,r){if(t===null||typeof t>"u"||Kc(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ae(e,t,n,r,l,i,s){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=s}var te={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){te[e]=new ae(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];te[t]=new ae(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){te[e]=new ae(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){te[e]=new ae(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){te[e]=new ae(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){te[e]=new ae(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){te[e]=new ae(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){te[e]=new ae(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){te[e]=new ae(e,5,!1,e.toLowerCase(),null,!1,!1)});var Ji=/[\-:]([a-z])/g;function qi(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ji,qi);te[t]=new ae(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!1,!1)});te.xlinkHref=new ae("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!0,!0)});function bi(e,t,n,r){var l=te.hasOwnProperty(t)?te[t]:null;(l!==null?l.type!==0:r||!(2u||l[s]!==i[u]){var a=` -`+l[s].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=s&&0<=u);break}}}finally{Cl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?wn(e):""}function Xc(e){switch(e.tag){case 5:return wn(e.type);case 16:return wn("Lazy");case 13:return wn("Suspense");case 19:return wn("SuspenseList");case 0:case 2:case 15:return e=Pl(e.type,!1),e;case 11:return e=Pl(e.type.render,!1),e;case 1:return e=Pl(e.type,!0),e;default:return""}}function ii(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case It:return"Fragment";case Mt:return"Portal";case ni:return"Profiler";case es:return"StrictMode";case ri:return"Suspense";case li:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case yu:return(e.displayName||"Context")+".Consumer";case gu:return(e._context.displayName||"Context")+".Provider";case ts:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ns:return t=e.displayName||null,t!==null?t:ii(e.type)||"Memo";case qe:t=e._payload,e=e._init;try{return ii(e(t))}catch{}}return null}function Gc(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ii(t);case 8:return t===es?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ft(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function wu(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Zc(e){var t=wu(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(s){r=""+s,i.call(this,s)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(s){r=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function sr(e){e._valueTracker||(e._valueTracker=Zc(e))}function ku(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=wu(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $r(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function si(e,t){var n=t.checked;return B({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Zs(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ft(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function _u(e,t){t=t.checked,t!=null&&bi(e,"checked",t,!1)}function oi(e,t){_u(e,t);var n=ft(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?ui(e,t.type,n):t.hasOwnProperty("defaultValue")&&ui(e,t.type,ft(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Js(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function ui(e,t,n){(t!=="number"||$r(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var kn=Array.isArray;function Kt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=or.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Mn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Sn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Jc=["Webkit","ms","Moz","O"];Object.keys(Sn).forEach(function(e){Jc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Sn[t]=Sn[e]})});function Eu(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Sn.hasOwnProperty(e)&&Sn[e]?(""+t).trim():t+"px"}function Cu(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Eu(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var qc=B({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function di(e,t){if(t){if(qc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(w(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(w(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(w(61))}if(t.style!=null&&typeof t.style!="object")throw Error(w(62))}}function fi(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var pi=null;function rs(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var hi=null,Yt=null,Xt=null;function eo(e){if(e=bn(e)){if(typeof hi!="function")throw Error(w(280));var t=e.stateNode;t&&(t=dl(t),hi(e.stateNode,e.type,t))}}function Pu(e){Yt?Xt?Xt.push(e):Xt=[e]:Yt=e}function Lu(){if(Yt){var e=Yt,t=Xt;if(Xt=Yt=null,eo(e),t)for(e=0;e>>=0,e===0?32:31-(ad(e)/cd|0)|0}var ur=64,ar=4194304;function _n(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Ar(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,i=e.pingedLanes,s=n&268435455;if(s!==0){var u=s&~l;u!==0?r=_n(u):(i&=s,i!==0&&(r=_n(i)))}else s=n&~l,s!==0?r=_n(s):i!==0&&(r=_n(i));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,i=t&-t,l>=i||l===16&&(i&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Jn(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Oe(t),e[t]=n}function hd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=En),ao=" ",co=!1;function Gu(e,t){switch(e){case"keyup":return Bd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Zu(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var $t=!1;function Wd(e,t){switch(e){case"compositionend":return Zu(t);case"keypress":return t.which!==32?null:(co=!0,ao);case"textInput":return e=t.data,e===ao&&co?null:e;default:return null}}function Qd(e,t){if($t)return e==="compositionend"||!ds&&Gu(e,t)?(e=Yu(),Cr=us=nt=null,$t=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=mo(n)}}function ea(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?ea(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ta(){for(var e=window,t=$r();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$r(e.document)}return t}function fs(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function ef(e){var t=ta(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&ea(n.ownerDocument.documentElement,n)){if(r!==null&&fs(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,i=Math.min(r.start,l);r=r.end===void 0?i:Math.min(r.end,l),!e.extend&&i>r&&(l=r,r=i,i=l),l=vo(n,i);var s=vo(n,r);l&&s&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==s.node||e.focusOffset!==s.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(s.node,s.offset)):(t.setEnd(s.node,s.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Dt=null,wi=null,Pn=null,ki=!1;function go(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ki||Dt==null||Dt!==$r(r)||(r=Dt,"selectionStart"in r&&fs(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Pn&&An(Pn,r)||(Pn=r,r=Hr(wi,"onSelect"),0At||(e.current=Ci[At],Ci[At]=null,At--)}function $(e,t){At++,Ci[At]=e.current,e.current=t}var pt={},ie=mt(pt),pe=mt(!1),Et=pt;function en(e,t){var n=e.type.contextTypes;if(!n)return pt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},i;for(i in n)l[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function he(e){return e=e.childContextTypes,e!=null}function Qr(){F(pe),F(ie)}function So(e,t,n){if(ie.current!==pt)throw Error(w(168));$(ie,t),$(pe,n)}function ca(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(w(108,Gc(e)||"Unknown",l));return B({},n,r)}function Kr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||pt,Et=ie.current,$(ie,e),$(pe,pe.current),!0}function No(e,t,n){var r=e.stateNode;if(!r)throw Error(w(169));n?(e=ca(e,t,Et),r.__reactInternalMemoizedMergedChildContext=e,F(pe),F(ie),$(ie,e)):F(pe),$(pe,n)}var Be=null,fl=!1,Bl=!1;function da(e){Be===null?Be=[e]:Be.push(e)}function pf(e){fl=!0,da(e)}function vt(){if(!Bl&&Be!==null){Bl=!0;var e=0,t=I;try{var n=Be;for(I=1;e>=s,l-=s,He=1<<32-Oe(t)+l|n<C?(W=E,E=null):W=E.sibling;var O=f(h,E,v[C],x);if(O===null){E===null&&(E=W);break}e&&E&&O.alternate===null&&t(h,E),d=i(O,d,C),N===null?j=O:N.sibling=O,N=O,E=W}if(C===v.length)return n(h,E),U&&xt(h,C),j;if(E===null){for(;CC?(W=E,E=null):W=E.sibling;var Pe=f(h,E,O.value,x);if(Pe===null){E===null&&(E=W);break}e&&E&&Pe.alternate===null&&t(h,E),d=i(Pe,d,C),N===null?j=Pe:N.sibling=Pe,N=Pe,E=W}if(O.done)return n(h,E),U&&xt(h,C),j;if(E===null){for(;!O.done;C++,O=v.next())O=p(h,O.value,x),O!==null&&(d=i(O,d,C),N===null?j=O:N.sibling=O,N=O);return U&&xt(h,C),j}for(E=r(h,E);!O.done;C++,O=v.next())O=y(E,h,C,O.value,x),O!==null&&(e&&O.alternate!==null&&E.delete(O.key===null?C:O.key),d=i(O,d,C),N===null?j=O:N.sibling=O,N=O);return e&&E.forEach(function(cn){return t(h,cn)}),U&&xt(h,C),j}function P(h,d,v,x){if(typeof v=="object"&&v!==null&&v.type===It&&v.key===null&&(v=v.props.children),typeof v=="object"&&v!==null){switch(v.$$typeof){case ir:e:{for(var j=v.key,N=d;N!==null;){if(N.key===j){if(j=v.type,j===It){if(N.tag===7){n(h,N.sibling),d=l(N,v.props.children),d.return=h,h=d;break e}}else if(N.elementType===j||typeof j=="object"&&j!==null&&j.$$typeof===qe&&Po(j)===N.type){n(h,N.sibling),d=l(N,v.props),d.ref=gn(h,N,v),d.return=h,h=d;break e}n(h,N);break}else t(h,N);N=N.sibling}v.type===It?(d=Nt(v.props.children,h.mode,x,v.key),d.return=h,h=d):(x=Ir(v.type,v.key,v.props,null,h.mode,x),x.ref=gn(h,d,v),x.return=h,h=x)}return s(h);case Mt:e:{for(N=v.key;d!==null;){if(d.key===N)if(d.tag===4&&d.stateNode.containerInfo===v.containerInfo&&d.stateNode.implementation===v.implementation){n(h,d.sibling),d=l(d,v.children||[]),d.return=h,h=d;break e}else{n(h,d);break}else t(h,d);d=d.sibling}d=Zl(v,h.mode,x),d.return=h,h=d}return s(h);case qe:return N=v._init,P(h,d,N(v._payload),x)}if(kn(v))return k(h,d,v,x);if(fn(v))return g(h,d,v,x);vr(h,v)}return typeof v=="string"&&v!==""||typeof v=="number"?(v=""+v,d!==null&&d.tag===6?(n(h,d.sibling),d=l(d,v),d.return=h,h=d):(n(h,d),d=Gl(v,h.mode,x),d.return=h,h=d),s(h)):n(h,d)}return P}var nn=ma(!0),va=ma(!1),Gr=mt(null),Zr=null,Ht=null,vs=null;function gs(){vs=Ht=Zr=null}function ys(e){var t=Gr.current;F(Gr),e._currentValue=t}function zi(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Zt(e,t){Zr=e,vs=Ht=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(de=!0),e.firstContext=null)}function Ee(e){var t=e._currentValue;if(vs!==e)if(e={context:e,memoizedValue:t,next:null},Ht===null){if(Zr===null)throw Error(w(308));Ht=e,Zr.dependencies={lanes:0,firstContext:e}}else Ht=Ht.next=e;return t}var _t=null;function xs(e){_t===null?_t=[e]:_t.push(e)}function ga(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,xs(t)):(n.next=l.next,l.next=n),t.interleaved=n,Xe(e,r)}function Xe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var be=!1;function ws(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function ya(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Qe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function ut(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,M&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Xe(e,n)}return l=r.interleaved,l===null?(t.next=t,xs(r)):(t.next=l.next,l.next=t),r.interleaved=t,Xe(e,n)}function Lr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,is(e,n)}}function Lo(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var s={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?l=i=s:i=i.next=s,n=n.next}while(n!==null);i===null?l=i=t:i=i.next=t}else l=i=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Jr(e,t,n,r){var l=e.updateQueue;be=!1;var i=l.firstBaseUpdate,s=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var a=u,c=a.next;a.next=null,s===null?i=c:s.next=c,s=a;var m=e.alternate;m!==null&&(m=m.updateQueue,u=m.lastBaseUpdate,u!==s&&(u===null?m.firstBaseUpdate=c:u.next=c,m.lastBaseUpdate=a))}if(i!==null){var p=l.baseState;s=0,m=c=a=null,u=i;do{var f=u.lane,y=u.eventTime;if((r&f)===f){m!==null&&(m=m.next={eventTime:y,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var k=e,g=u;switch(f=t,y=n,g.tag){case 1:if(k=g.payload,typeof k=="function"){p=k.call(y,p,f);break e}p=k;break e;case 3:k.flags=k.flags&-65537|128;case 0:if(k=g.payload,f=typeof k=="function"?k.call(y,p,f):k,f==null)break e;p=B({},p,f);break e;case 2:be=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,f=l.effects,f===null?l.effects=[u]:f.push(u))}else y={eventTime:y,lane:f,tag:u.tag,payload:u.payload,callback:u.callback,next:null},m===null?(c=m=y,a=p):m=m.next=y,s|=f;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;f=u,u=f.next,f.next=null,l.lastBaseUpdate=f,l.shared.pending=null}}while(!0);if(m===null&&(a=p),l.baseState=a,l.firstBaseUpdate=c,l.lastBaseUpdate=m,t=l.shared.interleaved,t!==null){l=t;do s|=l.lane,l=l.next;while(l!==t)}else i===null&&(l.shared.lanes=0);Lt|=s,e.lanes=s,e.memoizedState=p}}function zo(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Wl.transition;Wl.transition={};try{e(!1),t()}finally{I=n,Wl.transition=r}}function Ia(){return Ce().memoizedState}function gf(e,t,n){var r=ct(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},$a(e))Da(t,n);else if(n=ga(e,t,n,r),n!==null){var l=oe();Me(n,e,r,l),Fa(n,t,r)}}function yf(e,t,n){var r=ct(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if($a(e))Da(t,l);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,u=i(s,n);if(l.hasEagerState=!0,l.eagerState=u,Ie(u,s)){var a=t.interleaved;a===null?(l.next=l,xs(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}finally{}n=ga(e,t,l,r),n!==null&&(l=oe(),Me(n,e,r,l),Fa(n,t,r))}}function $a(e){var t=e.alternate;return e===V||t!==null&&t===V}function Da(e,t){Ln=br=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Fa(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,is(e,n)}}var el={readContext:Ee,useCallback:ne,useContext:ne,useEffect:ne,useImperativeHandle:ne,useInsertionEffect:ne,useLayoutEffect:ne,useMemo:ne,useReducer:ne,useRef:ne,useState:ne,useDebugValue:ne,useDeferredValue:ne,useTransition:ne,useMutableSource:ne,useSyncExternalStore:ne,useId:ne,unstable_isNewReconciler:!1},xf={readContext:Ee,useCallback:function(e,t){return De().memoizedState=[e,t===void 0?null:t],e},useContext:Ee,useEffect:Ro,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Tr(4194308,4,za.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Tr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Tr(4,2,e,t)},useMemo:function(e,t){var n=De();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=De();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=gf.bind(null,V,e),[r.memoizedState,e]},useRef:function(e){var t=De();return e={current:e},t.memoizedState=e},useState:To,useDebugValue:Ps,useDeferredValue:function(e){return De().memoizedState=e},useTransition:function(){var e=To(!1),t=e[0];return e=vf.bind(null,e[1]),De().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=V,l=De();if(U){if(n===void 0)throw Error(w(407));n=n()}else{if(n=t(),q===null)throw Error(w(349));Pt&30||_a(r,t,n)}l.memoizedState=n;var i={value:n,getSnapshot:t};return l.queue=i,Ro(Sa.bind(null,r,i,e),[e]),r.flags|=2048,Xn(9,ja.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=De(),t=q.identifierPrefix;if(U){var n=We,r=He;n=(r&~(1<<32-Oe(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Kn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=s.createElement(n,{is:r.is}):(e=s.createElement(n),n==="select"&&(s=e,r.multiple?s.multiple=!0:r.size&&(s.size=r.size))):e=s.createElementNS(e,n),e[Fe]=t,e[Hn]=r,Xa(e,t,!1,!1),t.stateNode=e;e:{switch(s=fi(n,r),n){case"dialog":D("cancel",e),D("close",e),l=r;break;case"iframe":case"object":case"embed":D("load",e),l=r;break;case"video":case"audio":for(l=0;lsn&&(t.flags|=128,r=!0,yn(i,!1),t.lanes=4194304)}else{if(!r)if(e=qr(s),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),yn(i,!0),i.tail===null&&i.tailMode==="hidden"&&!s.alternate&&!U)return re(t),null}else 2*K()-i.renderingStartTime>sn&&n!==1073741824&&(t.flags|=128,r=!0,yn(i,!1),t.lanes=4194304);i.isBackwards?(s.sibling=t.child,t.child=s):(n=i.last,n!==null?n.sibling=s:t.child=s,i.last=s)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=K(),t.sibling=null,n=A.current,$(A,r?n&1|2:n&1),t):(re(t),null);case 22:case 23:return Ms(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ve&1073741824&&(re(t),t.subtreeFlags&6&&(t.flags|=8192)):re(t),null;case 24:return null;case 25:return null}throw Error(w(156,t.tag))}function Cf(e,t){switch(hs(t),t.tag){case 1:return he(t.type)&&Qr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return rn(),F(pe),F(ie),js(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return _s(t),null;case 13:if(F(A),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(w(340));tn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return F(A),null;case 4:return rn(),null;case 10:return ys(t.type._context),null;case 22:case 23:return Ms(),null;case 24:return null;default:return null}}var yr=!1,le=!1,Pf=typeof WeakSet=="function"?WeakSet:Set,_=null;function Wt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){H(e,t,r)}else n.current=null}function Ui(e,t,n){try{n()}catch(r){H(e,t,r)}}var Ho=!1;function Lf(e,t){if(_i=Vr,e=ta(),fs(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var s=0,u=-1,a=-1,c=0,m=0,p=e,f=null;t:for(;;){for(var y;p!==n||l!==0&&p.nodeType!==3||(u=s+l),p!==i||r!==0&&p.nodeType!==3||(a=s+r),p.nodeType===3&&(s+=p.nodeValue.length),(y=p.firstChild)!==null;)f=p,p=y;for(;;){if(p===e)break t;if(f===n&&++c===l&&(u=s),f===i&&++m===r&&(a=s),(y=p.nextSibling)!==null)break;p=f,f=p.parentNode}p=y}n=u===-1||a===-1?null:{start:u,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(ji={focusedElem:e,selectionRange:n},Vr=!1,_=t;_!==null;)if(t=_,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,_=e;else for(;_!==null;){t=_;try{var k=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(k!==null){var g=k.memoizedProps,P=k.memoizedState,h=t.stateNode,d=h.getSnapshotBeforeUpdate(t.elementType===t.type?g:ze(t.type,g),P);h.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var v=t.stateNode.containerInfo;v.nodeType===1?v.textContent="":v.nodeType===9&&v.documentElement&&v.removeChild(v.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(w(163))}}catch(x){H(t,t.return,x)}if(e=t.sibling,e!==null){e.return=t.return,_=e;break}_=t.return}return k=Ho,Ho=!1,k}function zn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var i=l.destroy;l.destroy=void 0,i!==void 0&&Ui(t,n,i)}l=l.next}while(l!==r)}}function ml(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ai(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ja(e){var t=e.alternate;t!==null&&(e.alternate=null,Ja(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Fe],delete t[Hn],delete t[Ei],delete t[df],delete t[ff])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function qa(e){return e.tag===5||e.tag===3||e.tag===4}function Wo(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||qa(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Vi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Wr));else if(r!==4&&(e=e.child,e!==null))for(Vi(e,t,n),e=e.sibling;e!==null;)Vi(e,t,n),e=e.sibling}function Bi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Bi(e,t,n),e=e.sibling;e!==null;)Bi(e,t,n),e=e.sibling}var b=null,Te=!1;function Je(e,t,n){for(n=n.child;n!==null;)ba(e,t,n),n=n.sibling}function ba(e,t,n){if(Ue&&typeof Ue.onCommitFiberUnmount=="function")try{Ue.onCommitFiberUnmount(ol,n)}catch{}switch(n.tag){case 5:le||Wt(n,t);case 6:var r=b,l=Te;b=null,Je(e,t,n),b=r,Te=l,b!==null&&(Te?(e=b,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):b.removeChild(n.stateNode));break;case 18:b!==null&&(Te?(e=b,n=n.stateNode,e.nodeType===8?Vl(e.parentNode,n):e.nodeType===1&&Vl(e,n),Fn(e)):Vl(b,n.stateNode));break;case 4:r=b,l=Te,b=n.stateNode.containerInfo,Te=!0,Je(e,t,n),b=r,Te=l;break;case 0:case 11:case 14:case 15:if(!le&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var i=l,s=i.destroy;i=i.tag,s!==void 0&&(i&2||i&4)&&Ui(n,t,s),l=l.next}while(l!==r)}Je(e,t,n);break;case 1:if(!le&&(Wt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){H(n,t,u)}Je(e,t,n);break;case 21:Je(e,t,n);break;case 22:n.mode&1?(le=(r=le)||n.memoizedState!==null,Je(e,t,n),le=r):Je(e,t,n);break;default:Je(e,t,n)}}function Qo(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Pf),t.forEach(function(r){var l=Ff.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Le(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=s),r&=~i}if(r=l,r=K()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Tf(r/1960))-r,10e?16:e,rt===null)var r=!1;else{if(e=rt,rt=null,rl=0,M&6)throw Error(w(331));var l=M;for(M|=4,_=e.current;_!==null;){var i=_,s=i.child;if(_.flags&16){var u=i.deletions;if(u!==null){for(var a=0;aK()-Rs?St(e,0):Ts|=n),me(e,t)}function oc(e,t){t===0&&(e.mode&1?(t=ar,ar<<=1,!(ar&130023424)&&(ar=4194304)):t=1);var n=oe();e=Xe(e,t),e!==null&&(Jn(e,t,n),me(e,n))}function Df(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),oc(e,n)}function Ff(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(w(314))}r!==null&&r.delete(t),oc(e,n)}var uc;uc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||pe.current)de=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return de=!1,Nf(e,t,n);de=!!(e.flags&131072)}else de=!1,U&&t.flags&1048576&&fa(t,Xr,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Rr(e,t),e=t.pendingProps;var l=en(t,ie.current);Zt(t,n),l=Ns(null,t,r,e,l,n);var i=Es();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,he(r)?(i=!0,Kr(t)):i=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,ws(t),l.updater=hl,t.stateNode=l,l._reactInternals=t,Ri(t,r,e,n),t=Ii(null,t,r,!0,i,n)):(t.tag=0,U&&i&&ps(t),se(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Rr(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Af(r),e=ze(r,e),l){case 0:t=Mi(null,t,r,e,n);break e;case 1:t=Ao(null,t,r,e,n);break e;case 11:t=Fo(null,t,r,e,n);break e;case 14:t=Uo(null,t,r,ze(r.type,e),n);break e}throw Error(w(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Mi(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Ao(e,t,r,l,n);case 3:e:{if(Qa(t),e===null)throw Error(w(387));r=t.pendingProps,i=t.memoizedState,l=i.element,ya(e,t),Jr(t,r,null,n);var s=t.memoizedState;if(r=s.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:s.cache,pendingSuspenseBoundaries:s.pendingSuspenseBoundaries,transitions:s.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){l=ln(Error(w(423)),t),t=Vo(e,t,r,n,l);break e}else if(r!==l){l=ln(Error(w(424)),t),t=Vo(e,t,r,n,l);break e}else for(ge=ot(t.stateNode.containerInfo.firstChild),ye=t,U=!0,Re=null,n=va(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(tn(),r===l){t=Ge(e,t,n);break e}se(e,t,r,n)}t=t.child}return t;case 5:return xa(t),e===null&&Li(t),r=t.type,l=t.pendingProps,i=e!==null?e.memoizedProps:null,s=l.children,Si(r,l)?s=null:i!==null&&Si(r,i)&&(t.flags|=32),Wa(e,t),se(e,t,s,n),t.child;case 6:return e===null&&Li(t),null;case 13:return Ka(e,t,n);case 4:return ks(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=nn(t,null,r,n):se(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Fo(e,t,r,l,n);case 7:return se(e,t,t.pendingProps,n),t.child;case 8:return se(e,t,t.pendingProps.children,n),t.child;case 12:return se(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,i=t.memoizedProps,s=l.value,$(Gr,r._currentValue),r._currentValue=s,i!==null)if(Ie(i.value,s)){if(i.children===l.children&&!pe.current){t=Ge(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var u=i.dependencies;if(u!==null){s=i.child;for(var a=u.firstContext;a!==null;){if(a.context===r){if(i.tag===1){a=Qe(-1,n&-n),a.tag=2;var c=i.updateQueue;if(c!==null){c=c.shared;var m=c.pending;m===null?a.next=a:(a.next=m.next,m.next=a),c.pending=a}}i.lanes|=n,a=i.alternate,a!==null&&(a.lanes|=n),zi(i.return,n,t),u.lanes|=n;break}a=a.next}}else if(i.tag===10)s=i.type===t.type?null:i.child;else if(i.tag===18){if(s=i.return,s===null)throw Error(w(341));s.lanes|=n,u=s.alternate,u!==null&&(u.lanes|=n),zi(s,n,t),s=i.sibling}else s=i.child;if(s!==null)s.return=i;else for(s=i;s!==null;){if(s===t){s=null;break}if(i=s.sibling,i!==null){i.return=s.return,s=i;break}s=s.return}i=s}se(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Zt(t,n),l=Ee(l),r=r(l),t.flags|=1,se(e,t,r,n),t.child;case 14:return r=t.type,l=ze(r,t.pendingProps),l=ze(r.type,l),Uo(e,t,r,l,n);case 15:return Ba(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Rr(e,t),t.tag=1,he(r)?(e=!0,Kr(t)):e=!1,Zt(t,n),Ua(t,r,l),Ri(t,r,l,n),Ii(null,t,r,!0,e,n);case 19:return Ya(e,t,n);case 22:return Ha(e,t,n)}throw Error(w(156,t.tag))};function ac(e,t){return $u(e,t)}function Uf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Se(e,t,n,r){return new Uf(e,t,n,r)}function $s(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Af(e){if(typeof e=="function")return $s(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ts)return 11;if(e===ns)return 14}return 2}function dt(e,t){var n=e.alternate;return n===null?(n=Se(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ir(e,t,n,r,l,i){var s=2;if(r=e,typeof e=="function")$s(e)&&(s=1);else if(typeof e=="string")s=5;else e:switch(e){case It:return Nt(n.children,l,i,t);case es:s=8,l|=8;break;case ni:return e=Se(12,n,t,l|2),e.elementType=ni,e.lanes=i,e;case ri:return e=Se(13,n,t,l),e.elementType=ri,e.lanes=i,e;case li:return e=Se(19,n,t,l),e.elementType=li,e.lanes=i,e;case xu:return gl(n,l,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case gu:s=10;break e;case yu:s=9;break e;case ts:s=11;break e;case ns:s=14;break e;case qe:s=16,r=null;break e}throw Error(w(130,e==null?e:typeof e,""))}return t=Se(s,n,t,l),t.elementType=e,t.type=r,t.lanes=i,t}function Nt(e,t,n,r){return e=Se(7,e,r,t),e.lanes=n,e}function gl(e,t,n,r){return e=Se(22,e,r,t),e.elementType=xu,e.lanes=n,e.stateNode={isHidden:!1},e}function Gl(e,t,n){return e=Se(6,e,null,t),e.lanes=n,e}function Zl(e,t,n){return t=Se(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Vf(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=zl(0),this.expirationTimes=zl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=zl(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Ds(e,t,n,r,l,i,s,u,a){return e=new Vf(e,t,n,u,a),t===1?(t=1,i===!0&&(t|=8)):t=0,i=Se(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},ws(i),e}function Bf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(pc)}catch(e){console.error(e)}}pc(),pu.exports=we;var Yf=pu.exports,bo=Yf;ei.createRoot=bo.createRoot,ei.hydrateRoot=bo.hydrateRoot;async function tr(e,t){const n=await fetch(e,{signal:t,cache:"no-store"});if(!n.ok){const r=await n.json().catch(()=>null);throw new Error((r==null?void 0:r.error)??`${n.status} ${n.statusText}`)}return n.json()}function Xf(e=2e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0,l,i=!1;const s=async()=>{if(!i){i=!0,l=new AbortController;try{const a=await tr("/api/snapshot",l.signal);r&&n({data:a,loading:!1})}catch(a){r&&!(a instanceof DOMException&&a.name==="AbortError")&&n(c=>({...c,error:a instanceof Error?a.message:String(a),loading:!1}))}finally{i=!1}}};s();const u=window.setInterval(()=>{document.visibilityState==="visible"&&s()},e);return()=>{r=!1,l==null||l.abort(),window.clearInterval(u)}},[e]),t}function hc(e,t=0){const[n,r]=T.useState({loading:!!e});return T.useEffect(()=>{if(!e){r({loading:!1});return}const l=new AbortController;return r(i=>({...i,loading:!i.data})),tr(`/api/e3?e3_id=${encodeURIComponent(e)}`,l.signal).then(i=>r({data:i,loading:!1})).catch(i=>{i instanceof DOMException&&i.name==="AbortError"||r(s=>({...s,error:i instanceof Error?i.message:String(i),loading:!1}))}),()=>l.abort()},[e,t]),n}function Gf(e=2e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/logs?limit=2000").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function Zf(e=2500){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/events?limit=2000").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function Jf(e=60*60*1e3){const[t,n]=T.useState({loading:!0});return T.useEffect(()=>{let r=!0;const l=()=>{tr("/api/updates").then(s=>{r&&n({data:s,loading:!1})}).catch(s=>{r&&n(u=>({...u,error:s instanceof Error?s.message:String(s),loading:!1}))})};l();const i=window.setInterval(l,e);return()=>{r=!1,window.clearInterval(i)}},[e]),t}function fe(e,t=8,n=5){return!e||e.length<=t+n+2?e??"—":`${e.slice(0,t)}…${e.slice(-n)}`}function Vs(e){return e?new Intl.DateTimeFormat(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3}).format(new Date(e/1e3)):"—"}function mc(e){return new Date(e/1e3).toLocaleString()}function qf(e){return new Intl.NumberFormat().format(e)}function qt(e){if(!e)return"—";try{const t=BigInt(e);if(t<1000000n)return t.toLocaleString();const n=t.toString().length,r=n>18?"e18+":n>12?"T+":n>9?"B+":"M+";return`${t.toString().slice(0,4)}… ${r}`}catch{return e}}function vc(e){return JSON.stringify(e,null,2)}function bf({source:e}){const t=e==="network"?"NET":e.toUpperCase();return o.jsx("span",{className:`source source--${e}`,children:t})}function kr({value:e,label:t,onLocate:n}){return o.jsx("button",{className:"trace-link mono",type:"button",title:e,onClick:()=>n(e),children:t??fe(e,8,4)})}function ep({event:e,successors:t,onLocate:n}){const[r,l]=T.useState(!1),i=e.event_id===e.causation_id;return o.jsxs("article",{id:`event-${e.event_id}`,className:`event-card event-card--${e.severity}`,children:[o.jsxs("button",{className:"event-card__summary",type:"button",onClick:()=>l(s=>!s),"aria-expanded":r,children:[o.jsx("span",{className:"event-card__rail","aria-hidden":"true",children:o.jsx("span",{className:"event-card__dot"})}),o.jsx("span",{className:"event-card__time mono",title:mc(e.timestamp_us),children:Vs(e.timestamp_us)}),o.jsxs("span",{className:"event-card__identity",children:[o.jsx("strong",{children:e.event_type}),o.jsxs("span",{className:"event-card__meta",children:[o.jsx(bf,{source:e.source}),o.jsx("span",{children:fe(e.producer,12,6)}),e.block!=null&&o.jsxs("span",{className:"mono",children:["block #",e.block.toLocaleString()]})]})]}),o.jsxs("span",{className:"event-card__cause",children:[i?"origin":`after ${fe(e.causation_id,6,3)}`,o.jsx("span",{className:"chevron",children:r?"−":"+"})]})]}),r&&o.jsxs("div",{className:"event-card__detail",children:[o.jsxs("dl",{className:"trace-grid",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Event"}),o.jsx("dd",{children:o.jsx(kr,{value:e.event_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Caused by"}),o.jsx("dd",{children:o.jsx(kr,{value:e.causation_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Flow origin"}),o.jsx("dd",{children:o.jsx(kr,{value:e.origin_id,onLocate:n})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Followed by"}),o.jsx("dd",{className:"trace-successors",children:t.length?t.map(s=>o.jsx(kr,{value:s.event_id,label:`${s.event_type} · ${fe(s.event_id,5,3)}`,onLocate:n},s.event_id)):o.jsx("span",{children:"Nothing observed yet"})})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Local sequence"}),o.jsxs("dd",{className:"mono",children:["#",e.seq]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"HLC producer"}),o.jsx("dd",{className:"mono",children:e.producer_fingerprint})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Logical order"}),o.jsx("dd",{className:"mono",children:e.logical_counter})]})]}),o.jsx("div",{className:"payload-head",children:"Structured payload"}),o.jsx("pre",{className:"payload",children:vc(e.payload)})]})]})}function Bs({events:e,traceEvents:t=e,onNavigate:n,empty:r="No events observed for this stage yet."}){const l=T.useMemo(()=>new Set(e.map(u=>u.event_id)),[e]),i=T.useMemo(()=>{const u=new Map;for(const a of t){if(a.causation_id===a.event_id)continue;const c=u.get(a.causation_id)??[];c.push(a),u.set(a.causation_id,c)}return u},[t]),s=u=>{var c;if(n){n(u);return}const a=document.getElementById(`event-${u}`);a?(a.scrollIntoView({behavior:"smooth",block:"center"}),a.classList.add("event-card--located"),window.setTimeout(()=>a.classList.remove("event-card--located"),1400)):l.has(u)||(c=document.getElementById("flow-origin-note"))==null||c.scrollIntoView({behavior:"smooth",block:"center"})};return e.length===0?o.jsx("div",{className:"empty-inline",children:r}):o.jsx("div",{className:"event-timeline",children:e.map(u=>o.jsx(ep,{event:u,successors:i.get(u.event_id)??[],onLocate:s},`${u.aggregate_id}-${u.seq}`))})}function tp(){var p;const e=Zf(),t=T.useMemo(()=>{var f;return((f=e.data)==null?void 0:f.events)??[]},[(p=e.data)==null?void 0:p.events]),[n,r]=T.useState(""),[l,i]=T.useState("all"),[s,u]=T.useState("all"),a=T.useMemo(()=>({local:t.filter(f=>f.source==="local").length,network:t.filter(f=>f.source==="network").length,evm:t.filter(f=>f.source==="evm").length}),[t]),c=Math.max(1,a.local+a.network+a.evm),m=T.useMemo(()=>{const f=n.toLowerCase().trim();return t.filter(y=>l!=="all"&&y.source!==l||s!=="all"&&y.severity!==s?!1:!f||`${y.event_type} ${y.e3_id??""} ${y.producer} ${JSON.stringify(y.payload)}`.toLowerCase().includes(f))},[t,n,s,l]);return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"EventStore"}),o.jsx("h1",{children:"Raw protocol activity"}),o.jsx("p",{children:"The latest durable facts observed by this node across local, network, and EVM sources."})]})}),o.jsx("section",{className:"source-viz","aria-label":"Protocol event sources",children:["local","network","evm"].map(f=>o.jsxs("button",{type:"button",onClick:()=>i(f),className:l===f?"source-viz__item source-viz__item--selected":"source-viz__item",children:[o.jsx("span",{children:f}),o.jsx("strong",{className:"mono",children:a[f]}),o.jsx("i",{children:o.jsx("b",{className:`source-viz__fill source-viz__fill--${f}`,style:{width:`${a[f]/c*100}%`}})})]},f))}),o.jsxs("section",{className:"panel panel--wide",children:[e.error&&o.jsxs("div",{className:"alert alert--warning",children:["Live refresh paused: ",e.error]}),o.jsxs("div",{className:"filter-bar",children:[o.jsxs("label",{className:"search-field",children:[o.jsx("span",{children:"⌕"}),o.jsx("input",{value:n,onChange:f=>r(f.target.value),placeholder:"Search event, E3, node, or payload"})]}),o.jsxs("select",{value:l,onChange:f=>i(f.target.value),children:[o.jsx("option",{value:"all",children:"All sources"}),o.jsx("option",{value:"local",children:"Local"}),o.jsx("option",{value:"network",children:"Network"}),o.jsx("option",{value:"evm",children:"EVM"})]}),o.jsxs("select",{value:s,onChange:f=>u(f.target.value),children:[o.jsx("option",{value:"all",children:"All levels"}),o.jsx("option",{value:"error",children:"Errors"}),o.jsx("option",{value:"warn",children:"Warnings"}),o.jsx("option",{value:"info",children:"Info"}),o.jsx("option",{value:"debug",children:"Debug"})]}),o.jsxs("span",{className:"filter-count mono",children:[m.length," / ",t.length]})]}),o.jsx(Bs,{events:m,empty:"No durable events match these filters."})]})]})}const _r=["request","committee","dkg_setup","dkg_shares","key_publication","computation","decryption","settlement"],Jl=222,ql=170,bl=48,eu=66;function np(e){return e.length>24?`${e.slice(0,22)}…`:e}function rp({events:e,onSelect:t,selected:n}){const r=T.useMemo(()=>{const l=new Map,i=e.map(c=>{const m=c.phase??"request",p=l.get(m)??0;return l.set(m,p+1),{event:c,x:30+_r.indexOf(m)*Jl,y:74+p*eu}}),s=new Map(i.map(c=>[c.event.event_id,c])),u=i.flatMap(c=>{if(c.event.causation_id===c.event.event_id)return[];const m=s.get(c.event.causation_id);return m?[{cause:m,effect:c}]:[]}),a=Math.max(1,..._r.map(c=>l.get(c)??0));return{nodes:i,edges:u,byId:s,height:100+a*eu}},[e]);return o.jsx("div",{className:"flow-canvas",children:o.jsxs("svg",{width:_r.length*Jl+40,height:r.height,role:"img","aria-label":"Causal E3 event graph",children:[o.jsx("defs",{children:o.jsx("marker",{id:"flow-arrow",markerWidth:"8",markerHeight:"8",refX:"7",refY:"4",orient:"auto",children:o.jsx("path",{d:"M0,0 L8,4 L0,8 z",className:"flow-arrow"})})}),_r.map((l,i)=>o.jsxs("g",{transform:`translate(${30+i*Jl}, 20)`,children:[o.jsx("text",{className:"flow-phase-title",children:l.replaceAll("_"," ")}),o.jsx("line",{className:"flow-phase-rule",x1:"0",x2:ql,y1:"26",y2:"26"})]},l)),o.jsx("g",{className:"flow-edges",children:r.edges.map(({cause:l,effect:i})=>{const s=l.x+ql,u=l.y+bl/2,a=i.x,c=i.y+bl/2,m=Math.max(25,Math.abs(a-s)/2),p=n===l.event.event_id||n===i.event.event_id;return o.jsx("path",{className:p?"flow-edge flow-edge--selected":"flow-edge",d:`M ${s} ${u} C ${s+m} ${u}, ${a-m} ${c}, ${a} ${c}`,markerEnd:"url(#flow-arrow)"},`${l.event.event_id}-${i.event.event_id}`)})}),o.jsx("g",{className:"flow-nodes",children:r.nodes.map(({event:l,x:i,y:s})=>o.jsxs("g",{className:`flow-event flow-event--${l.source} flow-event--${l.severity} ${n===l.event_id?"flow-event--selected":""}`,transform:`translate(${i}, ${s})`,role:"button",tabIndex:0,onClick:()=>t(l),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&t(l)},children:[o.jsx("rect",{width:ql,height:bl,rx:"8"}),o.jsx("circle",{cx:"13",cy:"15",r:"4"}),o.jsx("text",{className:"flow-event__title",x:"24",y:"18",children:np(l.event_type)}),o.jsxs("text",{className:"flow-event__meta",x:"12",y:"36",children:[l.source.toUpperCase()," · #",l.seq," · ",fe(l.event_id,5,3)]})]},`${l.aggregate_id}-${l.seq}`))})]})})}function lp({e3s:e,selectedE3:t,onSelectE3:n,refreshKey:r}){var m,p,f,y,k;const l=hc(t,r),[i,s]=T.useState();T.useEffect(()=>{var g;s((g=l.data)==null?void 0:g.events.at(-1))},[t,(m=l.data)==null?void 0:m.events]);const u=((p=l.data)==null?void 0:p.events.filter(g=>{var P;return(P=l.data)==null?void 0:P.events.some(h=>h.event_id===g.causation_id)}).length)??0,a=(((f=l.data)==null?void 0:f.events.length)??0)-u,c=i?((y=l.data)==null?void 0:y.events.filter(g=>g.causation_id===i.event_id&&g.event_id!==i.event_id))??[]:[];return o.jsxs("div",{className:"view-stack",children:[o.jsxs("header",{className:"view-title flow-view-title",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Causal topology"}),o.jsx("h1",{children:"E3 event flow graph"}),o.jsx("p",{children:"Every edge means “caused by.” Select any node to inspect its stage, source, context, successors, and failure payload."})]}),o.jsxs("label",{className:"flow-selector",children:[o.jsx("span",{children:"Computation"}),o.jsx("select",{value:t??"",onChange:g=>n(g.target.value),children:e.map(g=>o.jsxs("option",{value:g.e3_id,children:["E3 ",g.e3_id," · ",g.status]},g.e3_id))})]})]}),l.data?o.jsxs(o.Fragment,{children:[o.jsxs("section",{className:"graph-metrics",children:[o.jsxs("span",{children:[o.jsx("strong",{children:l.data.events.length})," events"]}),o.jsxs("span",{children:[o.jsx("strong",{children:u})," causal edges"]}),o.jsxs("span",{children:[o.jsx("strong",{children:a})," observed roots"]}),o.jsxs("span",{children:[o.jsx("strong",{children:l.data.error_count})," failures"]})]}),o.jsxs("div",{className:"flow-layout",children:[o.jsxs("section",{className:"panel flow-graph-panel",children:[o.jsxs("div",{className:"flow-legend",children:[o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--local"}),"Local"]}),o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--network"}),"Network"]}),o.jsxs("span",{children:[o.jsx("i",{className:"legend-dot legend-dot--evm"}),"EVM"]}),o.jsx("span",{className:"flow-legend__hint",children:"Scroll horizontally and vertically · click a node"})]}),o.jsx(rp,{events:l.data.events,selected:i==null?void 0:i.event_id,onSelect:s})]}),o.jsx("aside",{className:"panel flow-detail",children:i?o.jsxs(o.Fragment,{children:[o.jsx("span",{className:"section-kicker",children:((k=i.phase)==null?void 0:k.replaceAll("_"," "))??"unclassified"}),o.jsx("h2",{children:i.event_type}),o.jsx("p",{className:"mono flow-detail__time",children:mc(i.timestamp_us)}),o.jsxs("dl",{className:"flow-detail-grid",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Event"}),o.jsx("dd",{className:"mono",children:i.event_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Caused by"}),o.jsx("dd",{className:"mono",children:i.causation_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Origin"}),o.jsx("dd",{className:"mono",children:i.origin_id})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Observed from"}),o.jsxs("dd",{children:[i.source," · ",i.producer]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Successors"}),o.jsx("dd",{children:c.length||"None observed"})]})]}),c.length>0&&o.jsx("div",{className:"flow-successor-list",children:c.map(g=>o.jsxs("button",{type:"button",onClick:()=>s(g),children:[g.event_type," ",o.jsx("span",{className:"mono",children:fe(g.event_id,5,3)})]},g.event_id))}),o.jsx("div",{className:"payload-head",children:"Structured payload"}),o.jsx("pre",{className:"payload",children:vc(i.payload)})]}):o.jsx("div",{className:"empty-inline",children:"Select an event node."})})]})]}):o.jsxs("section",{className:"panel blank-state",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h2",{children:l.error??"Reconstructing the causal graph…"})]})]})}function ip({phases:e,selected:t,onSelect:n}){return o.jsx("div",{className:"stage-flow","aria-label":"E3 protocol stages",children:e.map((r,l)=>o.jsxs("div",{className:"stage-flow__unit",children:[o.jsxs("button",{type:"button",className:`stage-node stage-node--${r.state} ${t===r.id?"stage-node--selected":""}`,onClick:()=>n(r.id),children:[o.jsx("span",{className:"stage-node__index",children:String(l+1).padStart(2,"0")}),o.jsxs("span",{className:"stage-node__copy",children:[o.jsx("strong",{children:r.label}),o.jsxs("span",{children:[r.event_count," events · L",r.sources.local," N",r.sources.net," E",r.sources.evm]})]}),o.jsx("span",{className:"stage-node__state",children:r.state==="complete"?"✓":r.state==="failed"?"!":r.state==="active"?"●":"○"})]}),lo.jsxs("button",{type:"button",className:`e3-list__item ${t===r.e3_id?"e3-list__item--selected":""}`,onClick:()=>n(r.e3_id),children:[o.jsx("span",{className:`status-dot status-dot--${r.status}`}),o.jsxs("span",{children:[o.jsx("strong",{className:"mono",children:r.e3_id}),o.jsxs("small",{children:[r.current_phase.replaceAll("_"," ")," · ",r.event_count," events"]})]}),o.jsx("span",{className:"e3-list__time mono",children:Vs(r.last_seen_us)})]},r.e3_id)),!e.length&&o.jsx("div",{className:"empty-inline",children:"No E3s have reached this node yet."})]})]})}function op({e3s:e,selected:t,onSelect:n,refreshKey:r}){var f,y,k;const l=hc(t,r),[i,s]=T.useState("request"),[u,a]=T.useState(),c=(f=l.data)==null?void 0:f.current_phase;T.useEffect(()=>{c&&s(c)},[t,c]);const m=T.useMemo(()=>{var g;return((g=l.data)==null?void 0:g.events.filter(P=>P.phase===i))??[]},[i,(y=l.data)==null?void 0:y.events]);T.useEffect(()=>{if(!u)return;const g=window.requestAnimationFrame(()=>{const P=document.getElementById(`event-${u.id}`);P==null||P.scrollIntoView({behavior:"smooth",block:"center"}),P==null||P.classList.add("event-card--located"),window.setTimeout(()=>P==null?void 0:P.classList.remove("event-card--located"),1400)});return()=>window.cancelAnimationFrame(g)},[u,i]);const p=g=>{var h;const P=(h=l.data)==null?void 0:h.events.find(d=>d.event_id===g);P&&(P.phase&&s(P.phase),a({id:g,nonce:Date.now()}))};return o.jsxs("div",{className:"inspector-layout",children:[o.jsx(sp,{e3s:e,selected:t,onSelect:n}),o.jsx("main",{className:"inspector-main",children:t?l.error&&!l.data?o.jsxs("div",{className:"blank-state blank-state--error",children:[o.jsx("h1",{children:"Couldn’t load this trace"}),o.jsx("p",{children:l.error})]}):l.data?o.jsxs(o.Fragment,{children:[o.jsxs("header",{className:"trace-head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Encrypted execution environment"}),o.jsxs("h1",{className:"mono",children:["E3 ",l.data.e3_id]}),o.jsxs("p",{children:["Chain ",l.data.chain_id," · first observed ",Vs(l.data.first_seen_us)," · ",l.data.event_count," events"]})]}),o.jsxs("span",{className:`trace-status trace-status--${l.data.status}`,children:[o.jsx("span",{}),l.data.status]})]}),l.data.failure!=null&&o.jsxs("div",{className:"failure-banner",children:[o.jsx("strong",{children:"Failure localized"}),o.jsx("code",{children:JSON.stringify(l.data.failure)})]}),o.jsx(ip,{phases:l.data.phases,selected:i,onSelect:s}),o.jsxs("div",{className:"trace-columns",children:[o.jsxs("section",{className:"panel trace-events",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Selected stage"}),o.jsx("h2",{children:(k=l.data.phases.find(g=>g.id===i))==null?void 0:k.label})]}),o.jsxs("span",{className:"panel__aside",children:[m.length," events"]})]}),o.jsx("div",{id:"flow-origin-note",className:"flow-note",children:"Every row carries its durable event, cause, origin, and immediate successors. Causal links jump across stages while preserving this E3 trace."}),o.jsx(Bs,{events:m,traceEvents:l.data.events,onNavigate:p})]}),o.jsxs("aside",{className:"trace-sidebar",children:[o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Participants"}),o.jsx("h2",{children:"Committee"})]}),o.jsx("span",{className:"panel__aside",children:l.data.committee.length})]}),o.jsxs("div",{className:"committee-list",children:[l.data.committee.map(g=>o.jsxs("div",{className:`committee-member ${g.expelled?"committee-member--expelled":""}`,children:[o.jsxs("span",{className:"committee-member__party mono",children:["P",g.party_id]}),o.jsxs("div",{children:[o.jsx("strong",{className:"mono",title:g.address,children:fe(g.address,10,6)}),o.jsxs("small",{title:g.score,children:["score ",qt(g.score)]})]}),g.expelled&&o.jsx("span",{className:"status-tag status-tag--failed",children:"Expelled"})]},g.address)),!l.data.committee.length&&o.jsx("div",{className:"empty-inline",children:"Committee not finalized."})]})]}),o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Sortition"}),o.jsx("h2",{children:"Tickets"})]}),o.jsx("span",{className:"panel__aside",children:l.data.tickets.length})]}),o.jsxs("div",{className:"ticket-list",children:[l.data.tickets.slice(0,12).map(g=>o.jsxs("div",{children:[o.jsxs("span",{className:"mono",children:["#",g.ticket_id]}),o.jsx("strong",{className:"mono",children:fe(g.node,8,5)}),o.jsx("small",{children:qt(g.score)})]},`${g.node}-${g.ticket_id}`)),!l.data.tickets.length&&o.jsx("div",{className:"empty-inline",children:"No submitted tickets observed."})]})]}),o.jsxs("section",{className:"panel panel--compact",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Settlement"}),o.jsx("h2",{children:"Rewards"})]}),o.jsx("span",{className:"panel__aside",children:l.data.rewards.length})]}),o.jsxs("div",{className:"reward-list",children:[l.data.rewards.map((g,P)=>o.jsxs("div",{children:[o.jsx("span",{className:`reward-state ${g.claimed?"reward-state--claimed":""}`,children:g.claimed?"Claimed":"Credited"}),o.jsx("strong",{className:"mono",title:g.account,children:fe(g.account,8,5)}),o.jsx("small",{className:"mono",title:g.amount,children:qt(g.amount)})]},`${g.account}-${P}`)),!l.data.rewards.length&&o.jsx("div",{className:"empty-inline",children:"Rewards are recorded when the E3 settles."})]})]})]})]})]}):o.jsxs("div",{className:"blank-state",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h1",{children:"Rebuilding the trace"}),o.jsx("p",{children:"Reading durable events from this node…"})]}):o.jsxs("div",{className:"blank-state",children:[o.jsx("span",{className:"blank-state__glyph",children:"E3"}),o.jsx("h1",{children:"No computation selected"}),o.jsx("p",{children:"Select an E3 to inspect its complete local flow."})]})})]})}function up(){var u,a,c;const e=Gf(),[t,n]=T.useState(""),[r,l]=T.useState("all"),i=T.useMemo(()=>{var f;const m=new Map;for(const y of((f=e.data)==null?void 0:f.entries)??[])m.set(y.level.toLowerCase(),(m.get(y.level.toLowerCase())??0)+1);const p=Math.max(1,...m.values());return["error","warn","info","debug","trace"].map(y=>({name:y,count:m.get(y)??0,maximum:p}))},[(u=e.data)==null?void 0:u.entries]),s=T.useMemo(()=>{var p;const m=t.toLowerCase().trim();return(((p=e.data)==null?void 0:p.entries)??[]).filter(f=>r!=="all"&&f.level.toLowerCase()!==r?!1:!m||`${f.message} ${f.target} ${JSON.stringify(f.fields??{})}`.toLowerCase().includes(m)).reverse()},[r,(a=e.data)==null?void 0:a.entries,t]);return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Operational diagnostics"}),o.jsx("h1",{children:"Ciphernode logs"}),o.jsx("p",{children:"Structured tracing output for networking, RPC, proving, startup, and resource diagnostics. Protocol facts live in EventStore."})]})}),o.jsxs("section",{className:"panel log-viz","aria-label":"Log level distribution",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Current memory window"}),o.jsx("h2",{children:"Signal distribution"})]}),o.jsx("div",{className:"log-viz__bars",children:i.map(m=>o.jsxs("div",{children:[o.jsx("span",{children:m.name}),o.jsx("i",{children:o.jsx("b",{className:`log-viz__bar log-viz__bar--${m.name}`,style:{width:`${m.count/m.maximum*100}%`}})}),o.jsx("strong",{className:"mono",children:m.count})]},m.name))})]}),o.jsxs("section",{className:"panel panel--wide",children:[o.jsxs("div",{className:"filter-bar",children:[o.jsxs("label",{className:"search-field",children:[o.jsx("span",{children:"⌕"}),o.jsx("input",{value:t,onChange:m=>n(m.target.value),placeholder:"Search messages, targets, and fields"})]}),o.jsxs("select",{value:r,onChange:m=>l(m.target.value),children:[o.jsx("option",{value:"all",children:"All levels"}),o.jsx("option",{value:"error",children:"Error"}),o.jsx("option",{value:"warn",children:"Warn"}),o.jsx("option",{value:"info",children:"Info"}),o.jsx("option",{value:"debug",children:"Debug"}),o.jsx("option",{value:"trace",children:"Trace"})]}),o.jsxs("span",{className:"filter-count mono",children:[s.length," visible · ",((c=e.data)==null?void 0:c.total_stored)??0," stored"]})]}),e.error&&o.jsx("div",{className:"alert alert--warning",children:e.error}),o.jsxs("div",{className:"log-table",children:[o.jsxs("div",{className:"log-table__head",children:[o.jsx("span",{children:"Time"}),o.jsx("span",{children:"Level"}),o.jsx("span",{children:"Target"}),o.jsx("span",{children:"Message"})]}),s.map(m=>{var p,f,y;return o.jsxs("details",{className:`log-row log-row--${m.level.toLowerCase()}`,children:[o.jsxs("summary",{children:[o.jsx("time",{className:"mono",children:new Date(m.timestamp_ms).toLocaleTimeString()}),o.jsx("span",{className:"log-level",children:m.level}),o.jsx("span",{className:"mono",title:m.target,children:fe(m.target,24,10)}),o.jsx("strong",{children:m.message})]}),(((p=m.fields)==null?void 0:p.e3_id)||((f=m.fields)==null?void 0:f.event_id)||((y=m.fields)==null?void 0:y.stage))&&o.jsxs("div",{className:"log-trace-context",children:[m.fields.stage&&o.jsxs("span",{children:["stage ",o.jsx("strong",{children:String(m.fields.stage)})]}),m.fields.e3_id&&o.jsxs("span",{children:["E3 ",o.jsx("strong",{className:"mono",children:String(m.fields.e3_id)})]}),m.fields.event_id&&o.jsxs("span",{children:["event ",o.jsx("strong",{className:"mono",children:String(m.fields.event_id)})]})]}),o.jsx("pre",{children:JSON.stringify(m.fields??{},null,2)})]},m.seq)}),!s.length&&o.jsx("div",{className:"empty-inline",children:"No operational logs match these filters."})]})]})]})}function tu(e){try{return e.reduce((t,n)=>t+BigInt(n),0n).toLocaleString()}catch{return"—"}}function jr({label:e,value:t,note:n,tone:r}){return o.jsxs("div",{className:`metric ${r?`metric--${r}`:""}`,children:[o.jsx("span",{className:"metric__label",children:e}),o.jsx("strong",{className:"metric__value",children:typeof t=="number"?qf(t):t}),o.jsx("span",{className:"metric__note",children:n})]})}function ap({snapshot:e}){const{node:t,network:n,operator:r,protocol:l}=e,i=new Map(r.chains.map(p=>[p.chain_id,p])),s=new Map(l.chains.map(p=>[p.chain_id,p])),a=[...new Set([...t.chains.map(p=>p.id),...r.chains.map(p=>p.chain_id),...l.chains.map(p=>p.chain_id)])].sort((p,f)=>p-f).map(p=>{var k;const f=i.get(p),y=s.get(p);return{chainId:p,chainName:(f==null?void 0:f.chain_name)??((k=t.chains.find(g=>g.id===p))==null?void 0:k.name)??`Chain ${p}`,registeredNodes:(f==null?void 0:f.registered_nodes)??String((y==null?void 0:y.registered_nodes)??0),activeNodes:(f==null?void 0:f.active_nodes)??String((y==null?void 0:y.active_nodes)??0),registered:(f==null?void 0:f.operator_registered)??(y==null?void 0:y.operator_registered)??!1,active:(f==null?void 0:f.operator_active)??(y==null?void 0:y.operator_active)??!1,exitInProgress:(f==null?void 0:f.exit_in_progress)??(y==null?void 0:y.exit_unlock_at)!==void 0,ticketBalance:(f==null?void 0:f.ticket_balance)??(y==null?void 0:y.ticket_balance),availableTickets:f==null?void 0:f.available_tickets,licenseBond:(f==null?void 0:f.license_bond)??(y==null?void 0:y.license_bond),rewardCredits:(y==null?void 0:y.rewards_credited.length)??0}}),c=tu(a.map(p=>p.registeredNodes)),m=tu(a.map(p=>p.activeNodes));return o.jsxs("div",{className:"view-stack",children:[o.jsxs("section",{className:"view-intro",children:[o.jsxs("div",{children:[o.jsxs("div",{className:"eyebrow",children:[o.jsx("span",{className:"live-dot"})," Node online · ",t.node_name]}),o.jsx("h1",{children:"Everything your ciphernode knows, in one place."}),o.jsx("p",{children:"Live transport health, durable protocol history, and causal E3 traces projected from this node’s own EventStore."})]}),o.jsxs("div",{className:"identity-card",children:[o.jsx("span",{children:"Operator identity"}),o.jsx("strong",{className:"mono",title:t.address,children:fe(t.address,12,8)}),o.jsx("span",{className:"mono",title:t.peer_id,children:fe(t.peer_id,13,8)})]})]}),o.jsxs("section",{className:"metrics-grid",children:[o.jsx(jr,{label:"Network reach",value:`${n.connected_peers.length}/${n.configured_peers}`,note:"connected / configured peers",tone:n.last_error?"bad":"good"}),o.jsx(jr,{label:"Registered nodes",value:c,note:`${m} currently active`}),o.jsx(jr,{label:"E3 workload",value:l.e3_total,note:`${l.e3_active} active · ${l.e3_completed} complete`}),o.jsx(jr,{label:"Durable events",value:l.events_observed,note:"observed across all aggregates"})]}),o.jsxs("div",{className:"overview-grid",children:[o.jsxs("section",{className:"panel",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Transport"}),o.jsx("h2",{children:"Connected nodes"})]}),o.jsx("span",{className:`health-pill ${n.last_error?"health-pill--bad":""}`,children:n.last_error?"Attention":"Healthy"})]}),n.connected_peers.length?o.jsx("div",{className:"peer-list",children:n.connected_peers.map(p=>o.jsxs("div",{className:"peer-row",children:[o.jsx("span",{className:"peer-row__status"}),o.jsxs("div",{children:[o.jsx("strong",{className:"mono",children:fe(p.peer_id,12,7)}),o.jsx("span",{className:"mono",children:fe(p.remote_address,22,10)})]}),o.jsxs("span",{className:"peer-row__direction",children:[p.direction," · ",p.connections," conn."]})]},p.peer_id))}):o.jsx("div",{className:"empty-inline",children:"No live peer connections. The node will keep dialing its configured peers."}),n.last_error&&o.jsx("div",{className:"alert alert--warning",children:n.last_error}),o.jsxs("div",{className:"listen-addresses",children:[o.jsx("span",{children:"Listening"}),n.listen_addresses.map(p=>o.jsx("code",{children:p},p)),!n.listen_addresses.length&&o.jsxs("code",{children:["UDP / QUIC ",t.quic_port]})]})]}),o.jsxs("section",{className:"panel",children:[o.jsx("header",{className:"panel__head",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"On-chain position"}),o.jsx("h2",{children:"Operator state"})]})}),o.jsxs("div",{className:"chain-list",children:[a.map(p=>o.jsxs("div",{className:"chain-card",children:[o.jsxs("div",{className:"chain-card__head",children:[o.jsx("strong",{children:p.chainName}),o.jsx("span",{className:`status-tag status-tag--${p.active?"complete":p.registered?"active":"pending"}`,children:p.exitInProgress?"Exit queued":p.active?"Active":p.registered?"Registered":"Not registered"})]}),o.jsxs("dl",{className:"mini-dl",children:[o.jsxs("div",{children:[o.jsx("dt",{children:"Available tickets"}),o.jsx("dd",{className:"mono",title:p.availableTickets,children:qt(p.availableTickets)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Ticket balance"}),o.jsx("dd",{className:"mono",title:p.ticketBalance,children:qt(p.ticketBalance)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"License bond"}),o.jsx("dd",{className:"mono",title:p.licenseBond,children:qt(p.licenseBond)})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Network"}),o.jsxs("dd",{children:[p.activeNodes," / ",p.registeredNodes," active"]})]}),o.jsxs("div",{children:[o.jsx("dt",{children:"Rewards"}),o.jsxs("dd",{children:[p.rewardCredits," credits"]})]})]})]},p.chainId)),!a.length&&o.jsx("div",{className:"empty-inline",children:"Waiting for on-chain registry state to sync."}),r.error&&o.jsx("div",{className:"alert alert--warning",children:r.error})]})]})]}),o.jsxs("section",{className:"panel panel--wide",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Live activity"}),o.jsx("h2",{children:"Latest protocol events"})]}),o.jsx("span",{className:"panel__aside",children:"Newest first"})]}),o.jsx(Bs,{events:e.recent_events.slice(0,12),empty:"The EventStore is synced; no protocol events have been observed yet."})]})]})}function cp({updates:e,activeE3s:t}){var s,u,a,c,m;const[n,r]=T.useState(!1),l=()=>{navigator.clipboard.writeText("interfoldup update").then(()=>{r(!0),window.setTimeout(()=>r(!1),1500)})},i=e.data;return o.jsxs("div",{className:"view-stack",children:[o.jsx("header",{className:"view-title",children:o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Operator continuity"}),o.jsx("h1",{children:"Updates and release desk"}),o.jsx("p",{children:"Release awareness and a deliberately manual, auditable upgrade path. The dashboard never replaces a running binary itself."})]})}),o.jsxs("div",{className:"update-grid",children:[o.jsxs("section",{className:"panel update-status",children:[o.jsx("span",{className:`update-orbit ${i!=null&&i.update_available?"update-orbit--available":""}`,children:o.jsx("i",{})}),o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Installed"}),o.jsxs("h2",{children:["Interfold ",(i==null?void 0:i.current_version)??"…"]}),o.jsx("p",{children:i!=null&&i.update_available?`${((s=i.latest)==null?void 0:s.tag)??"A newer release"} is available.`:i!=null&&i.latest?`This node matches the latest stable release, ${i.latest.tag}.`:"Checking the release channel…"}),((i==null?void 0:i.error)||e.error)&&o.jsx("div",{className:"alert alert--warning",children:(i==null?void 0:i.error)??e.error})]})]}),o.jsxs("section",{className:"panel safe-update",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Runbook"}),o.jsx("h2",{children:"Safe update sequence"})]}),o.jsx("span",{className:`status-tag status-tag--${t?"active":"complete"}`,children:t?`${t} active E3`:"Safe window"})]}),o.jsxs("ol",{className:"update-steps",children:[o.jsxs("li",{children:[o.jsx("strong",{children:"Wait for active work."}),o.jsx("span",{children:t?"Do not stop yet; let the active E3s settle unless this is an emergency.":"No active E3 is currently projected."})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Stop gracefully."}),o.jsx("span",{children:"Send SIGTERM or Ctrl+C and wait for “Graceful shutdown complete.”"})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Install the release."}),o.jsx("button",{type:"button",className:"copy-command mono",onClick:l,children:n?"Copied":"interfoldup update"})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Restart identically."}),o.jsx("span",{children:"Use the same service account, configuration, database, and event-log paths."})]}),o.jsxs("li",{children:[o.jsx("strong",{children:"Verify recovery."}),o.jsx("span",{children:"Confirm version, peers, operator state, and the resumed E3 stage before leaving the node unattended."})]})]})]})]}),o.jsxs("section",{className:"panel panel--wide release-notes",children:[o.jsxs("header",{className:"panel__head",children:[o.jsxs("div",{children:[o.jsx("span",{className:"section-kicker",children:"Release channel"}),o.jsx("h2",{children:((u=i==null?void 0:i.latest)==null?void 0:u.name)??"Latest stable release"})]}),o.jsx("a",{href:((a=i==null?void 0:i.latest)==null?void 0:a.url)??(i==null?void 0:i.releases_url),target:"_blank",rel:"noreferrer",children:"Open on GitHub ↗"})]}),((c=i==null?void 0:i.latest)==null?void 0:c.published_at)&&o.jsxs("p",{className:"release-date",children:["Published ",new Date(i.latest.published_at).toLocaleString()]}),o.jsx("pre",{children:((m=i==null?void 0:i.latest)==null?void 0:m.notes)||"Release notes will appear here when GitHub is reachable."})]})]})}const gc=[{id:"overview",label:"Overview"},{id:"e3",label:"E3 traces"},{id:"flow",label:"Flow graph"},{id:"events",label:"Events"},{id:"logs",label:"Logs"},{id:"updates",label:"Updates"}];function dp(){const e=window.location.hash.slice(1);return gc.some(t=>t.id===e)?e:"overview"}function fp({view:e,setView:t,nodeName:n,connected:r,updateAvailable:l}){const i=s=>{window.history.replaceState(null,"",`#${s}`),t(s)};return o.jsx("header",{className:"site-head",children:o.jsxs("div",{className:"site-head__inner",children:[o.jsx("button",{className:"wordmark",type:"button","aria-label":"Interfold node overview",onClick:()=>i("overview"),children:o.jsx("span",{})}),o.jsx("span",{className:"product-name",children:"Node observatory"}),o.jsx("nav",{className:"site-nav","aria-label":"Dashboard views",children:gc.map(s=>o.jsxs("button",{type:"button",className:e===s.id?"site-nav__link site-nav__link--on":"site-nav__link",onClick:()=>i(s.id),children:[s.label,s.id==="updates"&&l&&o.jsx("span",{className:"nav-update-dot"})]},s.id))}),o.jsxs("div",{className:"node-chip",children:[o.jsx("span",{className:r?"node-chip__dot":"node-chip__dot node-chip__dot--waiting"}),o.jsx("span",{children:n??"Connecting…"})]})]})})}function pp({error:e}){return o.jsxs("main",{className:"loading-page",children:[o.jsx("span",{className:"loader-ring"}),o.jsx("h1",{children:e?"The node API is unavailable":"Building the node picture"}),o.jsx("p",{children:e??"Reading protocol history and live transport state…"})]})}function hp(){var u,a,c;const[e,t]=T.useState(dp),[n,r]=T.useState(),l=Xf(),i=Jf();T.useEffect(()=>{var m;!n&&((m=l.data)!=null&&m.e3s[0])&&r(l.data.e3s[0].e3_id)},[n,(u=l.data)==null?void 0:u.e3s]);const s=!!(l.data&&!l.error);return o.jsxs("div",{className:"page",children:[o.jsx(fp,{view:e,setView:t,nodeName:(a=l.data)==null?void 0:a.node.node_name,connected:s,updateAvailable:!!((c=i.data)!=null&&c.update_available)}),l.data?o.jsxs(o.Fragment,{children:[o.jsxs("div",{className:e==="e3"?"app-main app-main--inspector":"app-main",children:[l.error&&o.jsxs("div",{className:"stale-banner",children:["Live refresh paused: ",l.error,". Showing the last successful snapshot."]}),e==="overview"&&o.jsx(ap,{snapshot:l.data}),e==="e3"&&o.jsx(op,{e3s:l.data.e3s,selected:n,onSelect:r,refreshKey:l.data.protocol.events_observed}),e==="flow"&&o.jsx(lp,{e3s:l.data.e3s,selectedE3:n,onSelectE3:r,refreshKey:l.data.protocol.events_observed}),e==="events"&&o.jsx(tp,{}),e==="logs"&&o.jsx(up,{}),e==="updates"&&o.jsx(cp,{updates:i,activeE3s:l.data.protocol.e3_active})]}),o.jsxs("footer",{className:"site-foot",children:[o.jsx("span",{children:"Local operator surface · bound to 127.0.0.1"}),o.jsxs("span",{className:"mono",children:["Interfold ",l.data.node.version]})]})]}):o.jsx(pp,{error:l.error})]})}ei.createRoot(document.getElementById("root")).render(o.jsx(Ic.StrictMode,{children:o.jsx(hp,{})})); + */ var Hc = T, + xe = Bc +function w(e) { + for (var t = 'https://reactjs.org/docs/error-decoder.html?invariant=' + e, n = 1; n < arguments.length; n++) + t += '&args[]=' + encodeURIComponent(arguments[n]) + return ( + 'Minified React error #' + + e + + '; visit ' + + t + + ' for the full message or use the non-minified dev environment for full errors and additional helpful warnings.' + ) +} +var vu = new Set(), + On = {} +function Tt(e, t) { + ;(bt(e, t), bt(e + 'Capture', t)) +} +function bt(e, t) { + for (On[e] = t, e = 0; e < t.length; e++) vu.add(t[e]) +} +var Ke = !(typeof window > 'u' || typeof window.document > 'u' || typeof window.document.createElement > 'u'), + ti = Object.prototype.hasOwnProperty, + Wc = + /^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, + Ys = {}, + Xs = {} +function Qc(e) { + return ti.call(Xs, e) ? !0 : ti.call(Ys, e) ? !1 : Wc.test(e) ? (Xs[e] = !0) : ((Ys[e] = !0), !1) +} +function Kc(e, t, n, r) { + if (n !== null && n.type === 0) return !1 + switch (typeof t) { + case 'function': + case 'symbol': + return !0 + case 'boolean': + return r ? !1 : n !== null ? !n.acceptsBooleans : ((e = e.toLowerCase().slice(0, 5)), e !== 'data-' && e !== 'aria-') + default: + return !1 + } +} +function Yc(e, t, n, r) { + if (t === null || typeof t > 'u' || Kc(e, t, n, r)) return !0 + if (r) return !1 + if (n !== null) + switch (n.type) { + case 3: + return !t + case 4: + return t === !1 + case 5: + return isNaN(t) + case 6: + return isNaN(t) || 1 > t + } + return !1 +} +function ae(e, t, n, r, l, i, s) { + ;((this.acceptsBooleans = t === 2 || t === 3 || t === 4), + (this.attributeName = r), + (this.attributeNamespace = l), + (this.mustUseProperty = n), + (this.propertyName = e), + (this.type = t), + (this.sanitizeURL = i), + (this.removeEmptyString = s)) +} +var te = {} +'children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style' + .split(' ') + .forEach(function (e) { + te[e] = new ae(e, 0, !1, e, null, !1, !1) + }) +;[ + ['acceptCharset', 'accept-charset'], + ['className', 'class'], + ['htmlFor', 'for'], + ['httpEquiv', 'http-equiv'], +].forEach(function (e) { + var t = e[0] + te[t] = new ae(t, 1, !1, e[1], null, !1, !1) +}) +;['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (e) { + te[e] = new ae(e, 2, !1, e.toLowerCase(), null, !1, !1) +}) +;['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (e) { + te[e] = new ae(e, 2, !1, e, null, !1, !1) +}) +'allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope' + .split(' ') + .forEach(function (e) { + te[e] = new ae(e, 3, !1, e.toLowerCase(), null, !1, !1) + }) +;['checked', 'multiple', 'muted', 'selected'].forEach(function (e) { + te[e] = new ae(e, 3, !0, e, null, !1, !1) +}) +;['capture', 'download'].forEach(function (e) { + te[e] = new ae(e, 4, !1, e, null, !1, !1) +}) +;['cols', 'rows', 'size', 'span'].forEach(function (e) { + te[e] = new ae(e, 6, !1, e, null, !1, !1) +}) +;['rowSpan', 'start'].forEach(function (e) { + te[e] = new ae(e, 5, !1, e.toLowerCase(), null, !1, !1) +}) +var Ji = /[\-:]([a-z])/g +function qi(e) { + return e[1].toUpperCase() +} +'accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height' + .split(' ') + .forEach(function (e) { + var t = e.replace(Ji, qi) + te[t] = new ae(t, 1, !1, e, null, !1, !1) + }) +'xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type'.split(' ').forEach(function (e) { + var t = e.replace(Ji, qi) + te[t] = new ae(t, 1, !1, e, 'http://www.w3.org/1999/xlink', !1, !1) +}) +;['xml:base', 'xml:lang', 'xml:space'].forEach(function (e) { + var t = e.replace(Ji, qi) + te[t] = new ae(t, 1, !1, e, 'http://www.w3.org/XML/1998/namespace', !1, !1) +}) +;['tabIndex', 'crossOrigin'].forEach(function (e) { + te[e] = new ae(e, 1, !1, e.toLowerCase(), null, !1, !1) +}) +te.xlinkHref = new ae('xlinkHref', 1, !1, 'xlink:href', 'http://www.w3.org/1999/xlink', !0, !1) +;['src', 'href', 'action', 'formAction'].forEach(function (e) { + te[e] = new ae(e, 1, !1, e.toLowerCase(), null, !0, !0) +}) +function bi(e, t, n, r) { + var l = te.hasOwnProperty(t) ? te[t] : null + ;(l !== null ? l.type !== 0 : r || !(2 < t.length) || (t[0] !== 'o' && t[0] !== 'O') || (t[1] !== 'n' && t[1] !== 'N')) && + (Yc(t, n, l, r) && (n = null), + r || l === null + ? Qc(t) && (n === null ? e.removeAttribute(t) : e.setAttribute(t, '' + n)) + : l.mustUseProperty + ? (e[l.propertyName] = n === null ? (l.type === 3 ? !1 : '') : n) + : ((t = l.attributeName), + (r = l.attributeNamespace), + n === null + ? e.removeAttribute(t) + : ((l = l.type), (n = l === 3 || (l === 4 && n === !0) ? '' : '' + n), r ? e.setAttributeNS(r, t, n) : e.setAttribute(t, n)))) +} +var Ze = Hc.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + ir = Symbol.for('react.element'), + Mt = Symbol.for('react.portal'), + It = Symbol.for('react.fragment'), + es = Symbol.for('react.strict_mode'), + ni = Symbol.for('react.profiler'), + gu = Symbol.for('react.provider'), + yu = Symbol.for('react.context'), + ts = Symbol.for('react.forward_ref'), + ri = Symbol.for('react.suspense'), + li = Symbol.for('react.suspense_list'), + ns = Symbol.for('react.memo'), + qe = Symbol.for('react.lazy'), + xu = Symbol.for('react.offscreen'), + Gs = Symbol.iterator +function fn(e) { + return e === null || typeof e != 'object' ? null : ((e = (Gs && e[Gs]) || e['@@iterator']), typeof e == 'function' ? e : null) +} +var B = Object.assign, + El +function wn(e) { + if (El === void 0) + try { + throw Error() + } catch (n) { + var t = n.stack.trim().match(/\n( *(at )?)/) + El = (t && t[1]) || '' + } + return ( + ` +` + + El + + e + ) +} +var Cl = !1 +function Pl(e, t) { + if (!e || Cl) return '' + Cl = !0 + var n = Error.prepareStackTrace + Error.prepareStackTrace = void 0 + try { + if (t) + if ( + ((t = function () { + throw Error() + }), + Object.defineProperty(t.prototype, 'props', { + set: function () { + throw Error() + }, + }), + typeof Reflect == 'object' && Reflect.construct) + ) { + try { + Reflect.construct(t, []) + } catch (c) { + var r = c + } + Reflect.construct(e, [], t) + } else { + try { + t.call() + } catch (c) { + r = c + } + e.call(t.prototype) + } + else { + try { + throw Error() + } catch (c) { + r = c + } + e() + } + } catch (c) { + if (c && r && typeof c.stack == 'string') { + for ( + var l = c.stack.split(` +`), + i = r.stack.split(` +`), + s = l.length - 1, + u = i.length - 1; + 1 <= s && 0 <= u && l[s] !== i[u]; + + ) + u-- + for (; 1 <= s && 0 <= u; s--, u--) + if (l[s] !== i[u]) { + if (s !== 1 || u !== 1) + do + if ((s--, u--, 0 > u || l[s] !== i[u])) { + var a = + ` +` + l[s].replace(' at new ', ' at ') + return (e.displayName && a.includes('') && (a = a.replace('', e.displayName)), a) + } + while (1 <= s && 0 <= u) + break + } + } + } finally { + ;((Cl = !1), (Error.prepareStackTrace = n)) + } + return (e = e ? e.displayName || e.name : '') ? wn(e) : '' +} +function Xc(e) { + switch (e.tag) { + case 5: + return wn(e.type) + case 16: + return wn('Lazy') + case 13: + return wn('Suspense') + case 19: + return wn('SuspenseList') + case 0: + case 2: + case 15: + return ((e = Pl(e.type, !1)), e) + case 11: + return ((e = Pl(e.type.render, !1)), e) + case 1: + return ((e = Pl(e.type, !0)), e) + default: + return '' + } +} +function ii(e) { + if (e == null) return null + if (typeof e == 'function') return e.displayName || e.name || null + if (typeof e == 'string') return e + switch (e) { + case It: + return 'Fragment' + case Mt: + return 'Portal' + case ni: + return 'Profiler' + case es: + return 'StrictMode' + case ri: + return 'Suspense' + case li: + return 'SuspenseList' + } + if (typeof e == 'object') + switch (e.$$typeof) { + case yu: + return (e.displayName || 'Context') + '.Consumer' + case gu: + return (e._context.displayName || 'Context') + '.Provider' + case ts: + var t = e.render + return ((e = e.displayName), e || ((e = t.displayName || t.name || ''), (e = e !== '' ? 'ForwardRef(' + e + ')' : 'ForwardRef')), e) + case ns: + return ((t = e.displayName || null), t !== null ? t : ii(e.type) || 'Memo') + case qe: + ;((t = e._payload), (e = e._init)) + try { + return ii(e(t)) + } catch {} + } + return null +} +function Gc(e) { + var t = e.type + switch (e.tag) { + case 24: + return 'Cache' + case 9: + return (t.displayName || 'Context') + '.Consumer' + case 10: + return (t._context.displayName || 'Context') + '.Provider' + case 18: + return 'DehydratedFragment' + case 11: + return ((e = t.render), (e = e.displayName || e.name || ''), t.displayName || (e !== '' ? 'ForwardRef(' + e + ')' : 'ForwardRef')) + case 7: + return 'Fragment' + case 5: + return t + case 4: + return 'Portal' + case 3: + return 'Root' + case 6: + return 'Text' + case 16: + return ii(t) + case 8: + return t === es ? 'StrictMode' : 'Mode' + case 22: + return 'Offscreen' + case 12: + return 'Profiler' + case 21: + return 'Scope' + case 13: + return 'Suspense' + case 19: + return 'SuspenseList' + case 25: + return 'TracingMarker' + case 1: + case 0: + case 17: + case 2: + case 14: + case 15: + if (typeof t == 'function') return t.displayName || t.name || null + if (typeof t == 'string') return t + } + return null +} +function ft(e) { + switch (typeof e) { + case 'boolean': + case 'number': + case 'string': + case 'undefined': + return e + case 'object': + return e + default: + return '' + } +} +function wu(e) { + var t = e.type + return (e = e.nodeName) && e.toLowerCase() === 'input' && (t === 'checkbox' || t === 'radio') +} +function Zc(e) { + var t = wu(e) ? 'checked' : 'value', + n = Object.getOwnPropertyDescriptor(e.constructor.prototype, t), + r = '' + e[t] + if (!e.hasOwnProperty(t) && typeof n < 'u' && typeof n.get == 'function' && typeof n.set == 'function') { + var l = n.get, + i = n.set + return ( + Object.defineProperty(e, t, { + configurable: !0, + get: function () { + return l.call(this) + }, + set: function (s) { + ;((r = '' + s), i.call(this, s)) + }, + }), + Object.defineProperty(e, t, { enumerable: n.enumerable }), + { + getValue: function () { + return r + }, + setValue: function (s) { + r = '' + s + }, + stopTracking: function () { + ;((e._valueTracker = null), delete e[t]) + }, + } + ) + } +} +function sr(e) { + e._valueTracker || (e._valueTracker = Zc(e)) +} +function ku(e) { + if (!e) return !1 + var t = e._valueTracker + if (!t) return !0 + var n = t.getValue(), + r = '' + return (e && (r = wu(e) ? (e.checked ? 'true' : 'false') : e.value), (e = r), e !== n ? (t.setValue(e), !0) : !1) +} +function $r(e) { + if (((e = e || (typeof document < 'u' ? document : void 0)), typeof e > 'u')) return null + try { + return e.activeElement || e.body + } catch { + return e.body + } +} +function si(e, t) { + var n = t.checked + return B({}, t, { defaultChecked: void 0, defaultValue: void 0, value: void 0, checked: n ?? e._wrapperState.initialChecked }) +} +function Zs(e, t) { + var n = t.defaultValue == null ? '' : t.defaultValue, + r = t.checked != null ? t.checked : t.defaultChecked + ;((n = ft(t.value != null ? t.value : n)), + (e._wrapperState = { + initialChecked: r, + initialValue: n, + controlled: t.type === 'checkbox' || t.type === 'radio' ? t.checked != null : t.value != null, + })) +} +function _u(e, t) { + ;((t = t.checked), t != null && bi(e, 'checked', t, !1)) +} +function oi(e, t) { + _u(e, t) + var n = ft(t.value), + r = t.type + if (n != null) + r === 'number' ? ((n === 0 && e.value === '') || e.value != n) && (e.value = '' + n) : e.value !== '' + n && (e.value = '' + n) + else if (r === 'submit' || r === 'reset') { + e.removeAttribute('value') + return + } + ;(t.hasOwnProperty('value') ? ui(e, t.type, n) : t.hasOwnProperty('defaultValue') && ui(e, t.type, ft(t.defaultValue)), + t.checked == null && t.defaultChecked != null && (e.defaultChecked = !!t.defaultChecked)) +} +function Js(e, t, n) { + if (t.hasOwnProperty('value') || t.hasOwnProperty('defaultValue')) { + var r = t.type + if (!((r !== 'submit' && r !== 'reset') || (t.value !== void 0 && t.value !== null))) return + ;((t = '' + e._wrapperState.initialValue), n || t === e.value || (e.value = t), (e.defaultValue = t)) + } + ;((n = e.name), n !== '' && (e.name = ''), (e.defaultChecked = !!e._wrapperState.initialChecked), n !== '' && (e.name = n)) +} +function ui(e, t, n) { + ;(t !== 'number' || $r(e.ownerDocument) !== e) && + (n == null ? (e.defaultValue = '' + e._wrapperState.initialValue) : e.defaultValue !== '' + n && (e.defaultValue = '' + n)) +} +var kn = Array.isArray +function Kt(e, t, n, r) { + if (((e = e.options), t)) { + t = {} + for (var l = 0; l < n.length; l++) t['$' + n[l]] = !0 + for (n = 0; n < e.length; n++) + ((l = t.hasOwnProperty('$' + e[n].value)), e[n].selected !== l && (e[n].selected = l), l && r && (e[n].defaultSelected = !0)) + } else { + for (n = '' + ft(n), t = null, l = 0; l < e.length; l++) { + if (e[l].value === n) { + ;((e[l].selected = !0), r && (e[l].defaultSelected = !0)) + return + } + t !== null || e[l].disabled || (t = e[l]) + } + t !== null && (t.selected = !0) + } +} +function ai(e, t) { + if (t.dangerouslySetInnerHTML != null) throw Error(w(91)) + return B({}, t, { value: void 0, defaultValue: void 0, children: '' + e._wrapperState.initialValue }) +} +function qs(e, t) { + var n = t.value + if (n == null) { + if (((n = t.children), (t = t.defaultValue), n != null)) { + if (t != null) throw Error(w(92)) + if (kn(n)) { + if (1 < n.length) throw Error(w(93)) + n = n[0] + } + t = n + } + ;(t == null && (t = ''), (n = t)) + } + e._wrapperState = { initialValue: ft(n) } +} +function ju(e, t) { + var n = ft(t.value), + r = ft(t.defaultValue) + ;(n != null && ((n = '' + n), n !== e.value && (e.value = n), t.defaultValue == null && e.defaultValue !== n && (e.defaultValue = n)), + r != null && (e.defaultValue = '' + r)) +} +function bs(e) { + var t = e.textContent + t === e._wrapperState.initialValue && t !== '' && t !== null && (e.value = t) +} +function Su(e) { + switch (e) { + case 'svg': + return 'http://www.w3.org/2000/svg' + case 'math': + return 'http://www.w3.org/1998/Math/MathML' + default: + return 'http://www.w3.org/1999/xhtml' + } +} +function ci(e, t) { + return e == null || e === 'http://www.w3.org/1999/xhtml' + ? Su(t) + : e === 'http://www.w3.org/2000/svg' && t === 'foreignObject' + ? 'http://www.w3.org/1999/xhtml' + : e +} +var or, + Nu = (function (e) { + return typeof MSApp < 'u' && MSApp.execUnsafeLocalFunction + ? function (t, n, r, l) { + MSApp.execUnsafeLocalFunction(function () { + return e(t, n, r, l) + }) + } + : e + })(function (e, t) { + if (e.namespaceURI !== 'http://www.w3.org/2000/svg' || 'innerHTML' in e) e.innerHTML = t + else { + for ( + or = or || document.createElement('div'), or.innerHTML = '' + t.valueOf().toString() + '', t = or.firstChild; + e.firstChild; + + ) + e.removeChild(e.firstChild) + for (; t.firstChild; ) e.appendChild(t.firstChild) + } + }) +function Mn(e, t) { + if (t) { + var n = e.firstChild + if (n && n === e.lastChild && n.nodeType === 3) { + n.nodeValue = t + return + } + } + e.textContent = t +} +var Sn = { + animationIterationCount: !0, + aspectRatio: !0, + borderImageOutset: !0, + borderImageSlice: !0, + borderImageWidth: !0, + boxFlex: !0, + boxFlexGroup: !0, + boxOrdinalGroup: !0, + columnCount: !0, + columns: !0, + flex: !0, + flexGrow: !0, + flexPositive: !0, + flexShrink: !0, + flexNegative: !0, + flexOrder: !0, + gridArea: !0, + gridRow: !0, + gridRowEnd: !0, + gridRowSpan: !0, + gridRowStart: !0, + gridColumn: !0, + gridColumnEnd: !0, + gridColumnSpan: !0, + gridColumnStart: !0, + fontWeight: !0, + lineClamp: !0, + lineHeight: !0, + opacity: !0, + order: !0, + orphans: !0, + tabSize: !0, + widows: !0, + zIndex: !0, + zoom: !0, + fillOpacity: !0, + floodOpacity: !0, + stopOpacity: !0, + strokeDasharray: !0, + strokeDashoffset: !0, + strokeMiterlimit: !0, + strokeOpacity: !0, + strokeWidth: !0, + }, + Jc = ['Webkit', 'ms', 'Moz', 'O'] +Object.keys(Sn).forEach(function (e) { + Jc.forEach(function (t) { + ;((t = t + e.charAt(0).toUpperCase() + e.substring(1)), (Sn[t] = Sn[e])) + }) +}) +function Eu(e, t, n) { + return t == null || typeof t == 'boolean' || t === '' + ? '' + : n || typeof t != 'number' || t === 0 || (Sn.hasOwnProperty(e) && Sn[e]) + ? ('' + t).trim() + : t + 'px' +} +function Cu(e, t) { + e = e.style + for (var n in t) + if (t.hasOwnProperty(n)) { + var r = n.indexOf('--') === 0, + l = Eu(n, t[n], r) + ;(n === 'float' && (n = 'cssFloat'), r ? e.setProperty(n, l) : (e[n] = l)) + } +} +var qc = B( + { menuitem: !0 }, + { + area: !0, + base: !0, + br: !0, + col: !0, + embed: !0, + hr: !0, + img: !0, + input: !0, + keygen: !0, + link: !0, + meta: !0, + param: !0, + source: !0, + track: !0, + wbr: !0, + }, +) +function di(e, t) { + if (t) { + if (qc[e] && (t.children != null || t.dangerouslySetInnerHTML != null)) throw Error(w(137, e)) + if (t.dangerouslySetInnerHTML != null) { + if (t.children != null) throw Error(w(60)) + if (typeof t.dangerouslySetInnerHTML != 'object' || !('__html' in t.dangerouslySetInnerHTML)) throw Error(w(61)) + } + if (t.style != null && typeof t.style != 'object') throw Error(w(62)) + } +} +function fi(e, t) { + if (e.indexOf('-') === -1) return typeof t.is == 'string' + switch (e) { + case 'annotation-xml': + case 'color-profile': + case 'font-face': + case 'font-face-src': + case 'font-face-uri': + case 'font-face-format': + case 'font-face-name': + case 'missing-glyph': + return !1 + default: + return !0 + } +} +var pi = null +function rs(e) { + return ( + (e = e.target || e.srcElement || window), + e.correspondingUseElement && (e = e.correspondingUseElement), + e.nodeType === 3 ? e.parentNode : e + ) +} +var hi = null, + Yt = null, + Xt = null +function eo(e) { + if ((e = bn(e))) { + if (typeof hi != 'function') throw Error(w(280)) + var t = e.stateNode + t && ((t = dl(t)), hi(e.stateNode, e.type, t)) + } +} +function Pu(e) { + Yt ? (Xt ? Xt.push(e) : (Xt = [e])) : (Yt = e) +} +function Lu() { + if (Yt) { + var e = Yt, + t = Xt + if (((Xt = Yt = null), eo(e), t)) for (e = 0; e < t.length; e++) eo(t[e]) + } +} +function zu(e, t) { + return e(t) +} +function Tu() {} +var Ll = !1 +function Ru(e, t, n) { + if (Ll) return e(t, n) + Ll = !0 + try { + return zu(e, t, n) + } finally { + ;((Ll = !1), (Yt !== null || Xt !== null) && (Tu(), Lu())) + } +} +function In(e, t) { + var n = e.stateNode + if (n === null) return null + var r = dl(n) + if (r === null) return null + n = r[t] + e: switch (t) { + case 'onClick': + case 'onClickCapture': + case 'onDoubleClick': + case 'onDoubleClickCapture': + case 'onMouseDown': + case 'onMouseDownCapture': + case 'onMouseMove': + case 'onMouseMoveCapture': + case 'onMouseUp': + case 'onMouseUpCapture': + case 'onMouseEnter': + ;((r = !r.disabled) || ((e = e.type), (r = !(e === 'button' || e === 'input' || e === 'select' || e === 'textarea'))), (e = !r)) + break e + default: + e = !1 + } + if (e) return null + if (n && typeof n != 'function') throw Error(w(231, t, typeof n)) + return n +} +var mi = !1 +if (Ke) + try { + var pn = {} + ;(Object.defineProperty(pn, 'passive', { + get: function () { + mi = !0 + }, + }), + window.addEventListener('test', pn, pn), + window.removeEventListener('test', pn, pn)) + } catch { + mi = !1 + } +function bc(e, t, n, r, l, i, s, u, a) { + var c = Array.prototype.slice.call(arguments, 3) + try { + t.apply(n, c) + } catch (m) { + this.onError(m) + } +} +var Nn = !1, + Dr = null, + Fr = !1, + vi = null, + ed = { + onError: function (e) { + ;((Nn = !0), (Dr = e)) + }, + } +function td(e, t, n, r, l, i, s, u, a) { + ;((Nn = !1), (Dr = null), bc.apply(ed, arguments)) +} +function nd(e, t, n, r, l, i, s, u, a) { + if ((td.apply(this, arguments), Nn)) { + if (Nn) { + var c = Dr + ;((Nn = !1), (Dr = null)) + } else throw Error(w(198)) + Fr || ((Fr = !0), (vi = c)) + } +} +function Rt(e) { + var t = e, + n = e + if (e.alternate) for (; t.return; ) t = t.return + else { + e = t + do ((t = e), t.flags & 4098 && (n = t.return), (e = t.return)) + while (e) + } + return t.tag === 3 ? n : null +} +function Ou(e) { + if (e.tag === 13) { + var t = e.memoizedState + if ((t === null && ((e = e.alternate), e !== null && (t = e.memoizedState)), t !== null)) return t.dehydrated + } + return null +} +function to(e) { + if (Rt(e) !== e) throw Error(w(188)) +} +function rd(e) { + var t = e.alternate + if (!t) { + if (((t = Rt(e)), t === null)) throw Error(w(188)) + return t !== e ? null : e + } + for (var n = e, r = t; ; ) { + var l = n.return + if (l === null) break + var i = l.alternate + if (i === null) { + if (((r = l.return), r !== null)) { + n = r + continue + } + break + } + if (l.child === i.child) { + for (i = l.child; i; ) { + if (i === n) return (to(l), e) + if (i === r) return (to(l), t) + i = i.sibling + } + throw Error(w(188)) + } + if (n.return !== r.return) ((n = l), (r = i)) + else { + for (var s = !1, u = l.child; u; ) { + if (u === n) { + ;((s = !0), (n = l), (r = i)) + break + } + if (u === r) { + ;((s = !0), (r = l), (n = i)) + break + } + u = u.sibling + } + if (!s) { + for (u = i.child; u; ) { + if (u === n) { + ;((s = !0), (n = i), (r = l)) + break + } + if (u === r) { + ;((s = !0), (r = i), (n = l)) + break + } + u = u.sibling + } + if (!s) throw Error(w(189)) + } + } + if (n.alternate !== r) throw Error(w(190)) + } + if (n.tag !== 3) throw Error(w(188)) + return n.stateNode.current === n ? e : t +} +function Mu(e) { + return ((e = rd(e)), e !== null ? Iu(e) : null) +} +function Iu(e) { + if (e.tag === 5 || e.tag === 6) return e + for (e = e.child; e !== null; ) { + var t = Iu(e) + if (t !== null) return t + e = e.sibling + } + return null +} +var $u = xe.unstable_scheduleCallback, + no = xe.unstable_cancelCallback, + ld = xe.unstable_shouldYield, + id = xe.unstable_requestPaint, + K = xe.unstable_now, + sd = xe.unstable_getCurrentPriorityLevel, + ls = xe.unstable_ImmediatePriority, + Du = xe.unstable_UserBlockingPriority, + Ur = xe.unstable_NormalPriority, + od = xe.unstable_LowPriority, + Fu = xe.unstable_IdlePriority, + ol = null, + Ue = null +function ud(e) { + if (Ue && typeof Ue.onCommitFiberRoot == 'function') + try { + Ue.onCommitFiberRoot(ol, e, void 0, (e.current.flags & 128) === 128) + } catch {} +} +var Oe = Math.clz32 ? Math.clz32 : dd, + ad = Math.log, + cd = Math.LN2 +function dd(e) { + return ((e >>>= 0), e === 0 ? 32 : (31 - ((ad(e) / cd) | 0)) | 0) +} +var ur = 64, + ar = 4194304 +function _n(e) { + switch (e & -e) { + case 1: + return 1 + case 2: + return 2 + case 4: + return 4 + case 8: + return 8 + case 16: + return 16 + case 32: + return 32 + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + case 8192: + case 16384: + case 32768: + case 65536: + case 131072: + case 262144: + case 524288: + case 1048576: + case 2097152: + return e & 4194240 + case 4194304: + case 8388608: + case 16777216: + case 33554432: + case 67108864: + return e & 130023424 + case 134217728: + return 134217728 + case 268435456: + return 268435456 + case 536870912: + return 536870912 + case 1073741824: + return 1073741824 + default: + return e + } +} +function Ar(e, t) { + var n = e.pendingLanes + if (n === 0) return 0 + var r = 0, + l = e.suspendedLanes, + i = e.pingedLanes, + s = n & 268435455 + if (s !== 0) { + var u = s & ~l + u !== 0 ? (r = _n(u)) : ((i &= s), i !== 0 && (r = _n(i))) + } else ((s = n & ~l), s !== 0 ? (r = _n(s)) : i !== 0 && (r = _n(i))) + if (r === 0) return 0 + if (t !== 0 && t !== r && !(t & l) && ((l = r & -r), (i = t & -t), l >= i || (l === 16 && (i & 4194240) !== 0))) return t + if ((r & 4 && (r |= n & 16), (t = e.entangledLanes), t !== 0)) + for (e = e.entanglements, t &= r; 0 < t; ) ((n = 31 - Oe(t)), (l = 1 << n), (r |= e[n]), (t &= ~l)) + return r +} +function fd(e, t) { + switch (e) { + case 1: + case 2: + case 4: + return t + 250 + case 8: + case 16: + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + case 8192: + case 16384: + case 32768: + case 65536: + case 131072: + case 262144: + case 524288: + case 1048576: + case 2097152: + return t + 5e3 + case 4194304: + case 8388608: + case 16777216: + case 33554432: + case 67108864: + return -1 + case 134217728: + case 268435456: + case 536870912: + case 1073741824: + return -1 + default: + return -1 + } +} +function pd(e, t) { + for (var n = e.suspendedLanes, r = e.pingedLanes, l = e.expirationTimes, i = e.pendingLanes; 0 < i; ) { + var s = 31 - Oe(i), + u = 1 << s, + a = l[s] + ;(a === -1 ? (!(u & n) || u & r) && (l[s] = fd(u, t)) : a <= t && (e.expiredLanes |= u), (i &= ~u)) + } +} +function gi(e) { + return ((e = e.pendingLanes & -1073741825), e !== 0 ? e : e & 1073741824 ? 1073741824 : 0) +} +function Uu() { + var e = ur + return ((ur <<= 1), !(ur & 4194240) && (ur = 64), e) +} +function zl(e) { + for (var t = [], n = 0; 31 > n; n++) t.push(e) + return t +} +function Jn(e, t, n) { + ;((e.pendingLanes |= t), + t !== 536870912 && ((e.suspendedLanes = 0), (e.pingedLanes = 0)), + (e = e.eventTimes), + (t = 31 - Oe(t)), + (e[t] = n)) +} +function hd(e, t) { + var n = e.pendingLanes & ~t + ;((e.pendingLanes = t), + (e.suspendedLanes = 0), + (e.pingedLanes = 0), + (e.expiredLanes &= t), + (e.mutableReadLanes &= t), + (e.entangledLanes &= t), + (t = e.entanglements)) + var r = e.eventTimes + for (e = e.expirationTimes; 0 < n; ) { + var l = 31 - Oe(n), + i = 1 << l + ;((t[l] = 0), (r[l] = -1), (e[l] = -1), (n &= ~i)) + } +} +function is(e, t) { + var n = (e.entangledLanes |= t) + for (e = e.entanglements; n; ) { + var r = 31 - Oe(n), + l = 1 << r + ;((l & t) | (e[r] & t) && (e[r] |= t), (n &= ~l)) + } +} +var I = 0 +function Au(e) { + return ((e &= -e), 1 < e ? (4 < e ? (e & 268435455 ? 16 : 536870912) : 4) : 1) +} +var Vu, + ss, + Bu, + Hu, + Wu, + yi = !1, + cr = [], + lt = null, + it = null, + st = null, + $n = new Map(), + Dn = new Map(), + et = [], + md = + 'mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit'.split( + ' ', + ) +function ro(e, t) { + switch (e) { + case 'focusin': + case 'focusout': + lt = null + break + case 'dragenter': + case 'dragleave': + it = null + break + case 'mouseover': + case 'mouseout': + st = null + break + case 'pointerover': + case 'pointerout': + $n.delete(t.pointerId) + break + case 'gotpointercapture': + case 'lostpointercapture': + Dn.delete(t.pointerId) + } +} +function hn(e, t, n, r, l, i) { + return e === null || e.nativeEvent !== i + ? ((e = { blockedOn: t, domEventName: n, eventSystemFlags: r, nativeEvent: i, targetContainers: [l] }), + t !== null && ((t = bn(t)), t !== null && ss(t)), + e) + : ((e.eventSystemFlags |= r), (t = e.targetContainers), l !== null && t.indexOf(l) === -1 && t.push(l), e) +} +function vd(e, t, n, r, l) { + switch (t) { + case 'focusin': + return ((lt = hn(lt, e, t, n, r, l)), !0) + case 'dragenter': + return ((it = hn(it, e, t, n, r, l)), !0) + case 'mouseover': + return ((st = hn(st, e, t, n, r, l)), !0) + case 'pointerover': + var i = l.pointerId + return ($n.set(i, hn($n.get(i) || null, e, t, n, r, l)), !0) + case 'gotpointercapture': + return ((i = l.pointerId), Dn.set(i, hn(Dn.get(i) || null, e, t, n, r, l)), !0) + } + return !1 +} +function Qu(e) { + var t = kt(e.target) + if (t !== null) { + var n = Rt(t) + if (n !== null) { + if (((t = n.tag), t === 13)) { + if (((t = Ou(n)), t !== null)) { + ;((e.blockedOn = t), + Wu(e.priority, function () { + Bu(n) + })) + return + } + } else if (t === 3 && n.stateNode.current.memoizedState.isDehydrated) { + e.blockedOn = n.tag === 3 ? n.stateNode.containerInfo : null + return + } + } + } + e.blockedOn = null +} +function Er(e) { + if (e.blockedOn !== null) return !1 + for (var t = e.targetContainers; 0 < t.length; ) { + var n = xi(e.domEventName, e.eventSystemFlags, t[0], e.nativeEvent) + if (n === null) { + n = e.nativeEvent + var r = new n.constructor(n.type, n) + ;((pi = r), n.target.dispatchEvent(r), (pi = null)) + } else return ((t = bn(n)), t !== null && ss(t), (e.blockedOn = n), !1) + t.shift() + } + return !0 +} +function lo(e, t, n) { + Er(e) && n.delete(t) +} +function gd() { + ;((yi = !1), + lt !== null && Er(lt) && (lt = null), + it !== null && Er(it) && (it = null), + st !== null && Er(st) && (st = null), + $n.forEach(lo), + Dn.forEach(lo)) +} +function mn(e, t) { + e.blockedOn === t && ((e.blockedOn = null), yi || ((yi = !0), xe.unstable_scheduleCallback(xe.unstable_NormalPriority, gd))) +} +function Fn(e) { + function t(l) { + return mn(l, e) + } + if (0 < cr.length) { + mn(cr[0], e) + for (var n = 1; n < cr.length; n++) { + var r = cr[n] + r.blockedOn === e && (r.blockedOn = null) + } + } + for ( + lt !== null && mn(lt, e), it !== null && mn(it, e), st !== null && mn(st, e), $n.forEach(t), Dn.forEach(t), n = 0; + n < et.length; + n++ + ) + ((r = et[n]), r.blockedOn === e && (r.blockedOn = null)) + for (; 0 < et.length && ((n = et[0]), n.blockedOn === null); ) (Qu(n), n.blockedOn === null && et.shift()) +} +var Gt = Ze.ReactCurrentBatchConfig, + Vr = !0 +function yd(e, t, n, r) { + var l = I, + i = Gt.transition + Gt.transition = null + try { + ;((I = 1), os(e, t, n, r)) + } finally { + ;((I = l), (Gt.transition = i)) + } +} +function xd(e, t, n, r) { + var l = I, + i = Gt.transition + Gt.transition = null + try { + ;((I = 4), os(e, t, n, r)) + } finally { + ;((I = l), (Gt.transition = i)) + } +} +function os(e, t, n, r) { + if (Vr) { + var l = xi(e, t, n, r) + if (l === null) (Al(e, t, r, Br, n), ro(e, r)) + else if (vd(l, e, t, n, r)) r.stopPropagation() + else if ((ro(e, r), t & 4 && -1 < md.indexOf(e))) { + for (; l !== null; ) { + var i = bn(l) + if ((i !== null && Vu(i), (i = xi(e, t, n, r)), i === null && Al(e, t, r, Br, n), i === l)) break + l = i + } + l !== null && r.stopPropagation() + } else Al(e, t, r, null, n) + } +} +var Br = null +function xi(e, t, n, r) { + if (((Br = null), (e = rs(r)), (e = kt(e)), e !== null)) + if (((t = Rt(e)), t === null)) e = null + else if (((n = t.tag), n === 13)) { + if (((e = Ou(t)), e !== null)) return e + e = null + } else if (n === 3) { + if (t.stateNode.current.memoizedState.isDehydrated) return t.tag === 3 ? t.stateNode.containerInfo : null + e = null + } else t !== e && (e = null) + return ((Br = e), null) +} +function Ku(e) { + switch (e) { + case 'cancel': + case 'click': + case 'close': + case 'contextmenu': + case 'copy': + case 'cut': + case 'auxclick': + case 'dblclick': + case 'dragend': + case 'dragstart': + case 'drop': + case 'focusin': + case 'focusout': + case 'input': + case 'invalid': + case 'keydown': + case 'keypress': + case 'keyup': + case 'mousedown': + case 'mouseup': + case 'paste': + case 'pause': + case 'play': + case 'pointercancel': + case 'pointerdown': + case 'pointerup': + case 'ratechange': + case 'reset': + case 'resize': + case 'seeked': + case 'submit': + case 'touchcancel': + case 'touchend': + case 'touchstart': + case 'volumechange': + case 'change': + case 'selectionchange': + case 'textInput': + case 'compositionstart': + case 'compositionend': + case 'compositionupdate': + case 'beforeblur': + case 'afterblur': + case 'beforeinput': + case 'blur': + case 'fullscreenchange': + case 'focus': + case 'hashchange': + case 'popstate': + case 'select': + case 'selectstart': + return 1 + case 'drag': + case 'dragenter': + case 'dragexit': + case 'dragleave': + case 'dragover': + case 'mousemove': + case 'mouseout': + case 'mouseover': + case 'pointermove': + case 'pointerout': + case 'pointerover': + case 'scroll': + case 'toggle': + case 'touchmove': + case 'wheel': + case 'mouseenter': + case 'mouseleave': + case 'pointerenter': + case 'pointerleave': + return 4 + case 'message': + switch (sd()) { + case ls: + return 1 + case Du: + return 4 + case Ur: + case od: + return 16 + case Fu: + return 536870912 + default: + return 16 + } + default: + return 16 + } +} +var nt = null, + us = null, + Cr = null +function Yu() { + if (Cr) return Cr + var e, + t = us, + n = t.length, + r, + l = 'value' in nt ? nt.value : nt.textContent, + i = l.length + for (e = 0; e < n && t[e] === l[e]; e++); + var s = n - e + for (r = 1; r <= s && t[n - r] === l[i - r]; r++); + return (Cr = l.slice(e, 1 < r ? 1 - r : void 0)) +} +function Pr(e) { + var t = e.keyCode + return ( + 'charCode' in e ? ((e = e.charCode), e === 0 && t === 13 && (e = 13)) : (e = t), + e === 10 && (e = 13), + 32 <= e || e === 13 ? e : 0 + ) +} +function dr() { + return !0 +} +function io() { + return !1 +} +function ke(e) { + function t(n, r, l, i, s) { + ;((this._reactName = n), + (this._targetInst = l), + (this.type = r), + (this.nativeEvent = i), + (this.target = s), + (this.currentTarget = null)) + for (var u in e) e.hasOwnProperty(u) && ((n = e[u]), (this[u] = n ? n(i) : i[u])) + return ( + (this.isDefaultPrevented = (i.defaultPrevented != null ? i.defaultPrevented : i.returnValue === !1) ? dr : io), + (this.isPropagationStopped = io), + this + ) + } + return ( + B(t.prototype, { + preventDefault: function () { + this.defaultPrevented = !0 + var n = this.nativeEvent + n && + (n.preventDefault ? n.preventDefault() : typeof n.returnValue != 'unknown' && (n.returnValue = !1), + (this.isDefaultPrevented = dr)) + }, + stopPropagation: function () { + var n = this.nativeEvent + n && + (n.stopPropagation ? n.stopPropagation() : typeof n.cancelBubble != 'unknown' && (n.cancelBubble = !0), + (this.isPropagationStopped = dr)) + }, + persist: function () {}, + isPersistent: dr, + }), + t + ) +} +var un = { + eventPhase: 0, + bubbles: 0, + cancelable: 0, + timeStamp: function (e) { + return e.timeStamp || Date.now() + }, + defaultPrevented: 0, + isTrusted: 0, + }, + as = ke(un), + qn = B({}, un, { view: 0, detail: 0 }), + wd = ke(qn), + Tl, + Rl, + vn, + ul = B({}, qn, { + screenX: 0, + screenY: 0, + clientX: 0, + clientY: 0, + pageX: 0, + pageY: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, + getModifierState: cs, + button: 0, + buttons: 0, + relatedTarget: function (e) { + return e.relatedTarget === void 0 ? (e.fromElement === e.srcElement ? e.toElement : e.fromElement) : e.relatedTarget + }, + movementX: function (e) { + return 'movementX' in e + ? e.movementX + : (e !== vn && + (vn && e.type === 'mousemove' ? ((Tl = e.screenX - vn.screenX), (Rl = e.screenY - vn.screenY)) : (Rl = Tl = 0), (vn = e)), + Tl) + }, + movementY: function (e) { + return 'movementY' in e ? e.movementY : Rl + }, + }), + so = ke(ul), + kd = B({}, ul, { dataTransfer: 0 }), + _d = ke(kd), + jd = B({}, qn, { relatedTarget: 0 }), + Ol = ke(jd), + Sd = B({}, un, { animationName: 0, elapsedTime: 0, pseudoElement: 0 }), + Nd = ke(Sd), + Ed = B({}, un, { + clipboardData: function (e) { + return 'clipboardData' in e ? e.clipboardData : window.clipboardData + }, + }), + Cd = ke(Ed), + Pd = B({}, un, { data: 0 }), + oo = ke(Pd), + Ld = { + Esc: 'Escape', + Spacebar: ' ', + Left: 'ArrowLeft', + Up: 'ArrowUp', + Right: 'ArrowRight', + Down: 'ArrowDown', + Del: 'Delete', + Win: 'OS', + Menu: 'ContextMenu', + Apps: 'ContextMenu', + Scroll: 'ScrollLock', + MozPrintableKey: 'Unidentified', + }, + zd = { + 8: 'Backspace', + 9: 'Tab', + 12: 'Clear', + 13: 'Enter', + 16: 'Shift', + 17: 'Control', + 18: 'Alt', + 19: 'Pause', + 20: 'CapsLock', + 27: 'Escape', + 32: ' ', + 33: 'PageUp', + 34: 'PageDown', + 35: 'End', + 36: 'Home', + 37: 'ArrowLeft', + 38: 'ArrowUp', + 39: 'ArrowRight', + 40: 'ArrowDown', + 45: 'Insert', + 46: 'Delete', + 112: 'F1', + 113: 'F2', + 114: 'F3', + 115: 'F4', + 116: 'F5', + 117: 'F6', + 118: 'F7', + 119: 'F8', + 120: 'F9', + 121: 'F10', + 122: 'F11', + 123: 'F12', + 144: 'NumLock', + 145: 'ScrollLock', + 224: 'Meta', + }, + Td = { Alt: 'altKey', Control: 'ctrlKey', Meta: 'metaKey', Shift: 'shiftKey' } +function Rd(e) { + var t = this.nativeEvent + return t.getModifierState ? t.getModifierState(e) : (e = Td[e]) ? !!t[e] : !1 +} +function cs() { + return Rd +} +var Od = B({}, qn, { + key: function (e) { + if (e.key) { + var t = Ld[e.key] || e.key + if (t !== 'Unidentified') return t + } + return e.type === 'keypress' + ? ((e = Pr(e)), e === 13 ? 'Enter' : String.fromCharCode(e)) + : e.type === 'keydown' || e.type === 'keyup' + ? zd[e.keyCode] || 'Unidentified' + : '' + }, + code: 0, + location: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, + repeat: 0, + locale: 0, + getModifierState: cs, + charCode: function (e) { + return e.type === 'keypress' ? Pr(e) : 0 + }, + keyCode: function (e) { + return e.type === 'keydown' || e.type === 'keyup' ? e.keyCode : 0 + }, + which: function (e) { + return e.type === 'keypress' ? Pr(e) : e.type === 'keydown' || e.type === 'keyup' ? e.keyCode : 0 + }, + }), + Md = ke(Od), + Id = B({}, ul, { + pointerId: 0, + width: 0, + height: 0, + pressure: 0, + tangentialPressure: 0, + tiltX: 0, + tiltY: 0, + twist: 0, + pointerType: 0, + isPrimary: 0, + }), + uo = ke(Id), + $d = B({}, qn, { touches: 0, targetTouches: 0, changedTouches: 0, altKey: 0, metaKey: 0, ctrlKey: 0, shiftKey: 0, getModifierState: cs }), + Dd = ke($d), + Fd = B({}, un, { propertyName: 0, elapsedTime: 0, pseudoElement: 0 }), + Ud = ke(Fd), + Ad = B({}, ul, { + deltaX: function (e) { + return 'deltaX' in e ? e.deltaX : 'wheelDeltaX' in e ? -e.wheelDeltaX : 0 + }, + deltaY: function (e) { + return 'deltaY' in e ? e.deltaY : 'wheelDeltaY' in e ? -e.wheelDeltaY : 'wheelDelta' in e ? -e.wheelDelta : 0 + }, + deltaZ: 0, + deltaMode: 0, + }), + Vd = ke(Ad), + Bd = [9, 13, 27, 32], + ds = Ke && 'CompositionEvent' in window, + En = null +Ke && 'documentMode' in document && (En = document.documentMode) +var Hd = Ke && 'TextEvent' in window && !En, + Xu = Ke && (!ds || (En && 8 < En && 11 >= En)), + ao = ' ', + co = !1 +function Gu(e, t) { + switch (e) { + case 'keyup': + return Bd.indexOf(t.keyCode) !== -1 + case 'keydown': + return t.keyCode !== 229 + case 'keypress': + case 'mousedown': + case 'focusout': + return !0 + default: + return !1 + } +} +function Zu(e) { + return ((e = e.detail), typeof e == 'object' && 'data' in e ? e.data : null) +} +var $t = !1 +function Wd(e, t) { + switch (e) { + case 'compositionend': + return Zu(t) + case 'keypress': + return t.which !== 32 ? null : ((co = !0), ao) + case 'textInput': + return ((e = t.data), e === ao && co ? null : e) + default: + return null + } +} +function Qd(e, t) { + if ($t) return e === 'compositionend' || (!ds && Gu(e, t)) ? ((e = Yu()), (Cr = us = nt = null), ($t = !1), e) : null + switch (e) { + case 'paste': + return null + case 'keypress': + if (!(t.ctrlKey || t.altKey || t.metaKey) || (t.ctrlKey && t.altKey)) { + if (t.char && 1 < t.char.length) return t.char + if (t.which) return String.fromCharCode(t.which) + } + return null + case 'compositionend': + return Xu && t.locale !== 'ko' ? null : t.data + default: + return null + } +} +var Kd = { + color: !0, + date: !0, + datetime: !0, + 'datetime-local': !0, + email: !0, + month: !0, + number: !0, + password: !0, + range: !0, + search: !0, + tel: !0, + text: !0, + time: !0, + url: !0, + week: !0, +} +function fo(e) { + var t = e && e.nodeName && e.nodeName.toLowerCase() + return t === 'input' ? !!Kd[e.type] : t === 'textarea' +} +function Ju(e, t, n, r) { + ;(Pu(r), (t = Hr(t, 'onChange')), 0 < t.length && ((n = new as('onChange', 'change', null, n, r)), e.push({ event: n, listeners: t }))) +} +var Cn = null, + Un = null +function Yd(e) { + ua(e, 0) +} +function al(e) { + var t = Ut(e) + if (ku(t)) return e +} +function Xd(e, t) { + if (e === 'change') return t +} +var qu = !1 +if (Ke) { + var Ml + if (Ke) { + var Il = 'oninput' in document + if (!Il) { + var po = document.createElement('div') + ;(po.setAttribute('oninput', 'return;'), (Il = typeof po.oninput == 'function')) + } + Ml = Il + } else Ml = !1 + qu = Ml && (!document.documentMode || 9 < document.documentMode) +} +function ho() { + Cn && (Cn.detachEvent('onpropertychange', bu), (Un = Cn = null)) +} +function bu(e) { + if (e.propertyName === 'value' && al(Un)) { + var t = [] + ;(Ju(t, Un, e, rs(e)), Ru(Yd, t)) + } +} +function Gd(e, t, n) { + e === 'focusin' ? (ho(), (Cn = t), (Un = n), Cn.attachEvent('onpropertychange', bu)) : e === 'focusout' && ho() +} +function Zd(e) { + if (e === 'selectionchange' || e === 'keyup' || e === 'keydown') return al(Un) +} +function Jd(e, t) { + if (e === 'click') return al(t) +} +function qd(e, t) { + if (e === 'input' || e === 'change') return al(t) +} +function bd(e, t) { + return (e === t && (e !== 0 || 1 / e === 1 / t)) || (e !== e && t !== t) +} +var Ie = typeof Object.is == 'function' ? Object.is : bd +function An(e, t) { + if (Ie(e, t)) return !0 + if (typeof e != 'object' || e === null || typeof t != 'object' || t === null) return !1 + var n = Object.keys(e), + r = Object.keys(t) + if (n.length !== r.length) return !1 + for (r = 0; r < n.length; r++) { + var l = n[r] + if (!ti.call(t, l) || !Ie(e[l], t[l])) return !1 + } + return !0 +} +function mo(e) { + for (; e && e.firstChild; ) e = e.firstChild + return e +} +function vo(e, t) { + var n = mo(e) + e = 0 + for (var r; n; ) { + if (n.nodeType === 3) { + if (((r = e + n.textContent.length), e <= t && r >= t)) return { node: n, offset: t - e } + e = r + } + e: { + for (; n; ) { + if (n.nextSibling) { + n = n.nextSibling + break e + } + n = n.parentNode + } + n = void 0 + } + n = mo(n) + } +} +function ea(e, t) { + return e && t + ? e === t + ? !0 + : e && e.nodeType === 3 + ? !1 + : t && t.nodeType === 3 + ? ea(e, t.parentNode) + : 'contains' in e + ? e.contains(t) + : e.compareDocumentPosition + ? !!(e.compareDocumentPosition(t) & 16) + : !1 + : !1 +} +function ta() { + for (var e = window, t = $r(); t instanceof e.HTMLIFrameElement; ) { + try { + var n = typeof t.contentWindow.location.href == 'string' + } catch { + n = !1 + } + if (n) e = t.contentWindow + else break + t = $r(e.document) + } + return t +} +function fs(e) { + var t = e && e.nodeName && e.nodeName.toLowerCase() + return ( + t && + ((t === 'input' && (e.type === 'text' || e.type === 'search' || e.type === 'tel' || e.type === 'url' || e.type === 'password')) || + t === 'textarea' || + e.contentEditable === 'true') + ) +} +function ef(e) { + var t = ta(), + n = e.focusedElem, + r = e.selectionRange + if (t !== n && n && n.ownerDocument && ea(n.ownerDocument.documentElement, n)) { + if (r !== null && fs(n)) { + if (((t = r.start), (e = r.end), e === void 0 && (e = t), 'selectionStart' in n)) + ((n.selectionStart = t), (n.selectionEnd = Math.min(e, n.value.length))) + else if (((e = ((t = n.ownerDocument || document) && t.defaultView) || window), e.getSelection)) { + e = e.getSelection() + var l = n.textContent.length, + i = Math.min(r.start, l) + ;((r = r.end === void 0 ? i : Math.min(r.end, l)), !e.extend && i > r && ((l = r), (r = i), (i = l)), (l = vo(n, i))) + var s = vo(n, r) + l && + s && + (e.rangeCount !== 1 || + e.anchorNode !== l.node || + e.anchorOffset !== l.offset || + e.focusNode !== s.node || + e.focusOffset !== s.offset) && + ((t = t.createRange()), + t.setStart(l.node, l.offset), + e.removeAllRanges(), + i > r ? (e.addRange(t), e.extend(s.node, s.offset)) : (t.setEnd(s.node, s.offset), e.addRange(t))) + } + } + for (t = [], e = n; (e = e.parentNode); ) e.nodeType === 1 && t.push({ element: e, left: e.scrollLeft, top: e.scrollTop }) + for (typeof n.focus == 'function' && n.focus(), n = 0; n < t.length; n++) + ((e = t[n]), (e.element.scrollLeft = e.left), (e.element.scrollTop = e.top)) + } +} +var tf = Ke && 'documentMode' in document && 11 >= document.documentMode, + Dt = null, + wi = null, + Pn = null, + ki = !1 +function go(e, t, n) { + var r = n.window === n ? n.document : n.nodeType === 9 ? n : n.ownerDocument + ki || + Dt == null || + Dt !== $r(r) || + ((r = Dt), + 'selectionStart' in r && fs(r) + ? (r = { start: r.selectionStart, end: r.selectionEnd }) + : ((r = ((r.ownerDocument && r.ownerDocument.defaultView) || window).getSelection()), + (r = { anchorNode: r.anchorNode, anchorOffset: r.anchorOffset, focusNode: r.focusNode, focusOffset: r.focusOffset })), + (Pn && An(Pn, r)) || + ((Pn = r), + (r = Hr(wi, 'onSelect')), + 0 < r.length && ((t = new as('onSelect', 'select', null, t, n)), e.push({ event: t, listeners: r }), (t.target = Dt)))) +} +function fr(e, t) { + var n = {} + return ((n[e.toLowerCase()] = t.toLowerCase()), (n['Webkit' + e] = 'webkit' + t), (n['Moz' + e] = 'moz' + t), n) +} +var Ft = { + animationend: fr('Animation', 'AnimationEnd'), + animationiteration: fr('Animation', 'AnimationIteration'), + animationstart: fr('Animation', 'AnimationStart'), + transitionend: fr('Transition', 'TransitionEnd'), + }, + $l = {}, + na = {} +Ke && + ((na = document.createElement('div').style), + 'AnimationEvent' in window || + (delete Ft.animationend.animation, delete Ft.animationiteration.animation, delete Ft.animationstart.animation), + 'TransitionEvent' in window || delete Ft.transitionend.transition) +function cl(e) { + if ($l[e]) return $l[e] + if (!Ft[e]) return e + var t = Ft[e], + n + for (n in t) if (t.hasOwnProperty(n) && n in na) return ($l[e] = t[n]) + return e +} +var ra = cl('animationend'), + la = cl('animationiteration'), + ia = cl('animationstart'), + sa = cl('transitionend'), + oa = new Map(), + yo = + 'abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel'.split( + ' ', + ) +function ht(e, t) { + ;(oa.set(e, t), Tt(t, [e])) +} +for (var Dl = 0; Dl < yo.length; Dl++) { + var Fl = yo[Dl], + nf = Fl.toLowerCase(), + rf = Fl[0].toUpperCase() + Fl.slice(1) + ht(nf, 'on' + rf) +} +ht(ra, 'onAnimationEnd') +ht(la, 'onAnimationIteration') +ht(ia, 'onAnimationStart') +ht('dblclick', 'onDoubleClick') +ht('focusin', 'onFocus') +ht('focusout', 'onBlur') +ht(sa, 'onTransitionEnd') +bt('onMouseEnter', ['mouseout', 'mouseover']) +bt('onMouseLeave', ['mouseout', 'mouseover']) +bt('onPointerEnter', ['pointerout', 'pointerover']) +bt('onPointerLeave', ['pointerout', 'pointerover']) +Tt('onChange', 'change click focusin focusout input keydown keyup selectionchange'.split(' ')) +Tt('onSelect', 'focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange'.split(' ')) +Tt('onBeforeInput', ['compositionend', 'keypress', 'textInput', 'paste']) +Tt('onCompositionEnd', 'compositionend focusout keydown keypress keyup mousedown'.split(' ')) +Tt('onCompositionStart', 'compositionstart focusout keydown keypress keyup mousedown'.split(' ')) +Tt('onCompositionUpdate', 'compositionupdate focusout keydown keypress keyup mousedown'.split(' ')) +var jn = + 'abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting'.split( + ' ', + ), + lf = new Set('cancel close invalid load scroll toggle'.split(' ').concat(jn)) +function xo(e, t, n) { + var r = e.type || 'unknown-event' + ;((e.currentTarget = n), nd(r, t, void 0, e), (e.currentTarget = null)) +} +function ua(e, t) { + t = (t & 4) !== 0 + for (var n = 0; n < e.length; n++) { + var r = e[n], + l = r.event + r = r.listeners + e: { + var i = void 0 + if (t) + for (var s = r.length - 1; 0 <= s; s--) { + var u = r[s], + a = u.instance, + c = u.currentTarget + if (((u = u.listener), a !== i && l.isPropagationStopped())) break e + ;(xo(l, u, c), (i = a)) + } + else + for (s = 0; s < r.length; s++) { + if (((u = r[s]), (a = u.instance), (c = u.currentTarget), (u = u.listener), a !== i && l.isPropagationStopped())) break e + ;(xo(l, u, c), (i = a)) + } + } + } + if (Fr) throw ((e = vi), (Fr = !1), (vi = null), e) +} +function D(e, t) { + var n = t[Ei] + n === void 0 && (n = t[Ei] = new Set()) + var r = e + '__bubble' + n.has(r) || (aa(t, e, 2, !1), n.add(r)) +} +function Ul(e, t, n) { + var r = 0 + ;(t && (r |= 4), aa(n, e, r, t)) +} +var pr = '_reactListening' + Math.random().toString(36).slice(2) +function Vn(e) { + if (!e[pr]) { + ;((e[pr] = !0), + vu.forEach(function (n) { + n !== 'selectionchange' && (lf.has(n) || Ul(n, !1, e), Ul(n, !0, e)) + })) + var t = e.nodeType === 9 ? e : e.ownerDocument + t === null || t[pr] || ((t[pr] = !0), Ul('selectionchange', !1, t)) + } +} +function aa(e, t, n, r) { + switch (Ku(t)) { + case 1: + var l = yd + break + case 4: + l = xd + break + default: + l = os + } + ;((n = l.bind(null, t, n, e)), + (l = void 0), + !mi || (t !== 'touchstart' && t !== 'touchmove' && t !== 'wheel') || (l = !0), + r + ? l !== void 0 + ? e.addEventListener(t, n, { capture: !0, passive: l }) + : e.addEventListener(t, n, !0) + : l !== void 0 + ? e.addEventListener(t, n, { passive: l }) + : e.addEventListener(t, n, !1)) +} +function Al(e, t, n, r, l) { + var i = r + if (!(t & 1) && !(t & 2) && r !== null) + e: for (;;) { + if (r === null) return + var s = r.tag + if (s === 3 || s === 4) { + var u = r.stateNode.containerInfo + if (u === l || (u.nodeType === 8 && u.parentNode === l)) break + if (s === 4) + for (s = r.return; s !== null; ) { + var a = s.tag + if ((a === 3 || a === 4) && ((a = s.stateNode.containerInfo), a === l || (a.nodeType === 8 && a.parentNode === l))) return + s = s.return + } + for (; u !== null; ) { + if (((s = kt(u)), s === null)) return + if (((a = s.tag), a === 5 || a === 6)) { + r = i = s + continue e + } + u = u.parentNode + } + } + r = r.return + } + Ru(function () { + var c = i, + m = rs(n), + p = [] + e: { + var f = oa.get(e) + if (f !== void 0) { + var y = as, + k = e + switch (e) { + case 'keypress': + if (Pr(n) === 0) break e + case 'keydown': + case 'keyup': + y = Md + break + case 'focusin': + ;((k = 'focus'), (y = Ol)) + break + case 'focusout': + ;((k = 'blur'), (y = Ol)) + break + case 'beforeblur': + case 'afterblur': + y = Ol + break + case 'click': + if (n.button === 2) break e + case 'auxclick': + case 'dblclick': + case 'mousedown': + case 'mousemove': + case 'mouseup': + case 'mouseout': + case 'mouseover': + case 'contextmenu': + y = so + break + case 'drag': + case 'dragend': + case 'dragenter': + case 'dragexit': + case 'dragleave': + case 'dragover': + case 'dragstart': + case 'drop': + y = _d + break + case 'touchcancel': + case 'touchend': + case 'touchmove': + case 'touchstart': + y = Dd + break + case ra: + case la: + case ia: + y = Nd + break + case sa: + y = Ud + break + case 'scroll': + y = wd + break + case 'wheel': + y = Vd + break + case 'copy': + case 'cut': + case 'paste': + y = Cd + break + case 'gotpointercapture': + case 'lostpointercapture': + case 'pointercancel': + case 'pointerdown': + case 'pointermove': + case 'pointerout': + case 'pointerover': + case 'pointerup': + y = uo + } + var g = (t & 4) !== 0, + P = !g && e === 'scroll', + h = g ? (f !== null ? f + 'Capture' : null) : f + g = [] + for (var d = c, v; d !== null; ) { + v = d + var x = v.stateNode + if ((v.tag === 5 && x !== null && ((v = x), h !== null && ((x = In(d, h)), x != null && g.push(Bn(d, x, v)))), P)) break + d = d.return + } + 0 < g.length && ((f = new y(f, k, null, n, m)), p.push({ event: f, listeners: g })) + } + } + if (!(t & 7)) { + e: { + if ( + ((f = e === 'mouseover' || e === 'pointerover'), + (y = e === 'mouseout' || e === 'pointerout'), + f && n !== pi && (k = n.relatedTarget || n.fromElement) && (kt(k) || k[Ye])) + ) + break e + if ( + (y || f) && + ((f = m.window === m ? m : (f = m.ownerDocument) ? f.defaultView || f.parentWindow : window), + y + ? ((k = n.relatedTarget || n.toElement), + (y = c), + (k = k ? kt(k) : null), + k !== null && ((P = Rt(k)), k !== P || (k.tag !== 5 && k.tag !== 6)) && (k = null)) + : ((y = null), (k = c)), + y !== k) + ) { + if ( + ((g = so), + (x = 'onMouseLeave'), + (h = 'onMouseEnter'), + (d = 'mouse'), + (e === 'pointerout' || e === 'pointerover') && ((g = uo), (x = 'onPointerLeave'), (h = 'onPointerEnter'), (d = 'pointer')), + (P = y == null ? f : Ut(y)), + (v = k == null ? f : Ut(k)), + (f = new g(x, d + 'leave', y, n, m)), + (f.target = P), + (f.relatedTarget = v), + (x = null), + kt(m) === c && ((g = new g(h, d + 'enter', k, n, m)), (g.target = v), (g.relatedTarget = P), (x = g)), + (P = x), + y && k) + ) + t: { + for (g = y, h = k, d = 0, v = g; v; v = Ot(v)) d++ + for (v = 0, x = h; x; x = Ot(x)) v++ + for (; 0 < d - v; ) ((g = Ot(g)), d--) + for (; 0 < v - d; ) ((h = Ot(h)), v--) + for (; d--; ) { + if (g === h || (h !== null && g === h.alternate)) break t + ;((g = Ot(g)), (h = Ot(h))) + } + g = null + } + else g = null + ;(y !== null && wo(p, f, y, g, !1), k !== null && P !== null && wo(p, P, k, g, !0)) + } + } + e: { + if ( + ((f = c ? Ut(c) : window), (y = f.nodeName && f.nodeName.toLowerCase()), y === 'select' || (y === 'input' && f.type === 'file')) + ) + var j = Xd + else if (fo(f)) + if (qu) j = qd + else { + j = Zd + var N = Gd + } + else (y = f.nodeName) && y.toLowerCase() === 'input' && (f.type === 'checkbox' || f.type === 'radio') && (j = Jd) + if (j && (j = j(e, c))) { + Ju(p, j, n, m) + break e + } + ;(N && N(e, f, c), e === 'focusout' && (N = f._wrapperState) && N.controlled && f.type === 'number' && ui(f, 'number', f.value)) + } + switch (((N = c ? Ut(c) : window), e)) { + case 'focusin': + ;(fo(N) || N.contentEditable === 'true') && ((Dt = N), (wi = c), (Pn = null)) + break + case 'focusout': + Pn = wi = Dt = null + break + case 'mousedown': + ki = !0 + break + case 'contextmenu': + case 'mouseup': + case 'dragend': + ;((ki = !1), go(p, n, m)) + break + case 'selectionchange': + if (tf) break + case 'keydown': + case 'keyup': + go(p, n, m) + } + var E + if (ds) + e: { + switch (e) { + case 'compositionstart': + var C = 'onCompositionStart' + break e + case 'compositionend': + C = 'onCompositionEnd' + break e + case 'compositionupdate': + C = 'onCompositionUpdate' + break e + } + C = void 0 + } + else $t ? Gu(e, n) && (C = 'onCompositionEnd') : e === 'keydown' && n.keyCode === 229 && (C = 'onCompositionStart') + ;(C && + (Xu && + n.locale !== 'ko' && + ($t || C !== 'onCompositionStart' + ? C === 'onCompositionEnd' && $t && (E = Yu()) + : ((nt = m), (us = 'value' in nt ? nt.value : nt.textContent), ($t = !0))), + (N = Hr(c, C)), + 0 < N.length && + ((C = new oo(C, e, null, n, m)), + p.push({ event: C, listeners: N }), + E ? (C.data = E) : ((E = Zu(n)), E !== null && (C.data = E)))), + (E = Hd ? Wd(e, n) : Qd(e, n)) && + ((c = Hr(c, 'onBeforeInput')), + 0 < c.length && ((m = new oo('onBeforeInput', 'beforeinput', null, n, m)), p.push({ event: m, listeners: c }), (m.data = E)))) + } + ua(p, t) + }) +} +function Bn(e, t, n) { + return { instance: e, listener: t, currentTarget: n } +} +function Hr(e, t) { + for (var n = t + 'Capture', r = []; e !== null; ) { + var l = e, + i = l.stateNode + ;(l.tag === 5 && + i !== null && + ((l = i), (i = In(e, n)), i != null && r.unshift(Bn(e, i, l)), (i = In(e, t)), i != null && r.push(Bn(e, i, l))), + (e = e.return)) + } + return r +} +function Ot(e) { + if (e === null) return null + do e = e.return + while (e && e.tag !== 5) + return e || null +} +function wo(e, t, n, r, l) { + for (var i = t._reactName, s = []; n !== null && n !== r; ) { + var u = n, + a = u.alternate, + c = u.stateNode + if (a !== null && a === r) break + ;(u.tag === 5 && + c !== null && + ((u = c), l ? ((a = In(n, i)), a != null && s.unshift(Bn(n, a, u))) : l || ((a = In(n, i)), a != null && s.push(Bn(n, a, u)))), + (n = n.return)) + } + s.length !== 0 && e.push({ event: t, listeners: s }) +} +var sf = /\r\n?/g, + of = /\u0000|\uFFFD/g +function ko(e) { + return (typeof e == 'string' ? e : '' + e) + .replace( + sf, + ` +`, + ) + .replace(of, '') +} +function hr(e, t, n) { + if (((t = ko(t)), ko(e) !== t && n)) throw Error(w(425)) +} +function Wr() {} +var _i = null, + ji = null +function Si(e, t) { + return ( + e === 'textarea' || + e === 'noscript' || + typeof t.children == 'string' || + typeof t.children == 'number' || + (typeof t.dangerouslySetInnerHTML == 'object' && t.dangerouslySetInnerHTML !== null && t.dangerouslySetInnerHTML.__html != null) + ) +} +var Ni = typeof setTimeout == 'function' ? setTimeout : void 0, + uf = typeof clearTimeout == 'function' ? clearTimeout : void 0, + _o = typeof Promise == 'function' ? Promise : void 0, + af = + typeof queueMicrotask == 'function' + ? queueMicrotask + : typeof _o < 'u' + ? function (e) { + return _o.resolve(null).then(e).catch(cf) + } + : Ni +function cf(e) { + setTimeout(function () { + throw e + }) +} +function Vl(e, t) { + var n = t, + r = 0 + do { + var l = n.nextSibling + if ((e.removeChild(n), l && l.nodeType === 8)) + if (((n = l.data), n === '/$')) { + if (r === 0) { + ;(e.removeChild(l), Fn(t)) + return + } + r-- + } else (n !== '$' && n !== '$?' && n !== '$!') || r++ + n = l + } while (n) + Fn(t) +} +function ot(e) { + for (; e != null; e = e.nextSibling) { + var t = e.nodeType + if (t === 1 || t === 3) break + if (t === 8) { + if (((t = e.data), t === '$' || t === '$!' || t === '$?')) break + if (t === '/$') return null + } + } + return e +} +function jo(e) { + e = e.previousSibling + for (var t = 0; e; ) { + if (e.nodeType === 8) { + var n = e.data + if (n === '$' || n === '$!' || n === '$?') { + if (t === 0) return e + t-- + } else n === '/$' && t++ + } + e = e.previousSibling + } + return null +} +var an = Math.random().toString(36).slice(2), + Fe = '__reactFiber$' + an, + Hn = '__reactProps$' + an, + Ye = '__reactContainer$' + an, + Ei = '__reactEvents$' + an, + df = '__reactListeners$' + an, + ff = '__reactHandles$' + an +function kt(e) { + var t = e[Fe] + if (t) return t + for (var n = e.parentNode; n; ) { + if ((t = n[Ye] || n[Fe])) { + if (((n = t.alternate), t.child !== null || (n !== null && n.child !== null))) + for (e = jo(e); e !== null; ) { + if ((n = e[Fe])) return n + e = jo(e) + } + return t + } + ;((e = n), (n = e.parentNode)) + } + return null +} +function bn(e) { + return ((e = e[Fe] || e[Ye]), !e || (e.tag !== 5 && e.tag !== 6 && e.tag !== 13 && e.tag !== 3) ? null : e) +} +function Ut(e) { + if (e.tag === 5 || e.tag === 6) return e.stateNode + throw Error(w(33)) +} +function dl(e) { + return e[Hn] || null +} +var Ci = [], + At = -1 +function mt(e) { + return { current: e } +} +function F(e) { + 0 > At || ((e.current = Ci[At]), (Ci[At] = null), At--) +} +function $(e, t) { + ;(At++, (Ci[At] = e.current), (e.current = t)) +} +var pt = {}, + ie = mt(pt), + pe = mt(!1), + Et = pt +function en(e, t) { + var n = e.type.contextTypes + if (!n) return pt + var r = e.stateNode + if (r && r.__reactInternalMemoizedUnmaskedChildContext === t) return r.__reactInternalMemoizedMaskedChildContext + var l = {}, + i + for (i in n) l[i] = t[i] + return ( + r && ((e = e.stateNode), (e.__reactInternalMemoizedUnmaskedChildContext = t), (e.__reactInternalMemoizedMaskedChildContext = l)), + l + ) +} +function he(e) { + return ((e = e.childContextTypes), e != null) +} +function Qr() { + ;(F(pe), F(ie)) +} +function So(e, t, n) { + if (ie.current !== pt) throw Error(w(168)) + ;($(ie, t), $(pe, n)) +} +function ca(e, t, n) { + var r = e.stateNode + if (((t = t.childContextTypes), typeof r.getChildContext != 'function')) return n + r = r.getChildContext() + for (var l in r) if (!(l in t)) throw Error(w(108, Gc(e) || 'Unknown', l)) + return B({}, n, r) +} +function Kr(e) { + return ( + (e = ((e = e.stateNode) && e.__reactInternalMemoizedMergedChildContext) || pt), + (Et = ie.current), + $(ie, e), + $(pe, pe.current), + !0 + ) +} +function No(e, t, n) { + var r = e.stateNode + if (!r) throw Error(w(169)) + ;(n ? ((e = ca(e, t, Et)), (r.__reactInternalMemoizedMergedChildContext = e), F(pe), F(ie), $(ie, e)) : F(pe), $(pe, n)) +} +var Be = null, + fl = !1, + Bl = !1 +function da(e) { + Be === null ? (Be = [e]) : Be.push(e) +} +function pf(e) { + ;((fl = !0), da(e)) +} +function vt() { + if (!Bl && Be !== null) { + Bl = !0 + var e = 0, + t = I + try { + var n = Be + for (I = 1; e < n.length; e++) { + var r = n[e] + do r = r(!0) + while (r !== null) + } + ;((Be = null), (fl = !1)) + } catch (l) { + throw (Be !== null && (Be = Be.slice(e + 1)), $u(ls, vt), l) + } finally { + ;((I = t), (Bl = !1)) + } + } + return null +} +var Vt = [], + Bt = 0, + Yr = null, + Xr = 0, + _e = [], + je = 0, + Ct = null, + He = 1, + We = '' +function xt(e, t) { + ;((Vt[Bt++] = Xr), (Vt[Bt++] = Yr), (Yr = e), (Xr = t)) +} +function fa(e, t, n) { + ;((_e[je++] = He), (_e[je++] = We), (_e[je++] = Ct), (Ct = e)) + var r = He + e = We + var l = 32 - Oe(r) - 1 + ;((r &= ~(1 << l)), (n += 1)) + var i = 32 - Oe(t) + l + if (30 < i) { + var s = l - (l % 5) + ;((i = (r & ((1 << s) - 1)).toString(32)), (r >>= s), (l -= s), (He = (1 << (32 - Oe(t) + l)) | (n << l) | r), (We = i + e)) + } else ((He = (1 << i) | (n << l) | r), (We = e)) +} +function ps(e) { + e.return !== null && (xt(e, 1), fa(e, 1, 0)) +} +function hs(e) { + for (; e === Yr; ) ((Yr = Vt[--Bt]), (Vt[Bt] = null), (Xr = Vt[--Bt]), (Vt[Bt] = null)) + for (; e === Ct; ) ((Ct = _e[--je]), (_e[je] = null), (We = _e[--je]), (_e[je] = null), (He = _e[--je]), (_e[je] = null)) +} +var ye = null, + ge = null, + U = !1, + Re = null +function pa(e, t) { + var n = Se(5, null, null, 0) + ;((n.elementType = 'DELETED'), + (n.stateNode = t), + (n.return = e), + (t = e.deletions), + t === null ? ((e.deletions = [n]), (e.flags |= 16)) : t.push(n)) +} +function Eo(e, t) { + switch (e.tag) { + case 5: + var n = e.type + return ( + (t = t.nodeType !== 1 || n.toLowerCase() !== t.nodeName.toLowerCase() ? null : t), + t !== null ? ((e.stateNode = t), (ye = e), (ge = ot(t.firstChild)), !0) : !1 + ) + case 6: + return ((t = e.pendingProps === '' || t.nodeType !== 3 ? null : t), t !== null ? ((e.stateNode = t), (ye = e), (ge = null), !0) : !1) + case 13: + return ( + (t = t.nodeType !== 8 ? null : t), + t !== null + ? ((n = Ct !== null ? { id: He, overflow: We } : null), + (e.memoizedState = { dehydrated: t, treeContext: n, retryLane: 1073741824 }), + (n = Se(18, null, null, 0)), + (n.stateNode = t), + (n.return = e), + (e.child = n), + (ye = e), + (ge = null), + !0) + : !1 + ) + default: + return !1 + } +} +function Pi(e) { + return (e.mode & 1) !== 0 && (e.flags & 128) === 0 +} +function Li(e) { + if (U) { + var t = ge + if (t) { + var n = t + if (!Eo(e, t)) { + if (Pi(e)) throw Error(w(418)) + t = ot(n.nextSibling) + var r = ye + t && Eo(e, t) ? pa(r, n) : ((e.flags = (e.flags & -4097) | 2), (U = !1), (ye = e)) + } + } else { + if (Pi(e)) throw Error(w(418)) + ;((e.flags = (e.flags & -4097) | 2), (U = !1), (ye = e)) + } + } +} +function Co(e) { + for (e = e.return; e !== null && e.tag !== 5 && e.tag !== 3 && e.tag !== 13; ) e = e.return + ye = e +} +function mr(e) { + if (e !== ye) return !1 + if (!U) return (Co(e), (U = !0), !1) + var t + if ( + ((t = e.tag !== 3) && !(t = e.tag !== 5) && ((t = e.type), (t = t !== 'head' && t !== 'body' && !Si(e.type, e.memoizedProps))), + t && (t = ge)) + ) { + if (Pi(e)) throw (ha(), Error(w(418))) + for (; t; ) (pa(e, t), (t = ot(t.nextSibling))) + } + if ((Co(e), e.tag === 13)) { + if (((e = e.memoizedState), (e = e !== null ? e.dehydrated : null), !e)) throw Error(w(317)) + e: { + for (e = e.nextSibling, t = 0; e; ) { + if (e.nodeType === 8) { + var n = e.data + if (n === '/$') { + if (t === 0) { + ge = ot(e.nextSibling) + break e + } + t-- + } else (n !== '$' && n !== '$!' && n !== '$?') || t++ + } + e = e.nextSibling + } + ge = null + } + } else ge = ye ? ot(e.stateNode.nextSibling) : null + return !0 +} +function ha() { + for (var e = ge; e; ) e = ot(e.nextSibling) +} +function tn() { + ;((ge = ye = null), (U = !1)) +} +function ms(e) { + Re === null ? (Re = [e]) : Re.push(e) +} +var hf = Ze.ReactCurrentBatchConfig +function gn(e, t, n) { + if (((e = n.ref), e !== null && typeof e != 'function' && typeof e != 'object')) { + if (n._owner) { + if (((n = n._owner), n)) { + if (n.tag !== 1) throw Error(w(309)) + var r = n.stateNode + } + if (!r) throw Error(w(147, e)) + var l = r, + i = '' + e + return t !== null && t.ref !== null && typeof t.ref == 'function' && t.ref._stringRef === i + ? t.ref + : ((t = function (s) { + var u = l.refs + s === null ? delete u[i] : (u[i] = s) + }), + (t._stringRef = i), + t) + } + if (typeof e != 'string') throw Error(w(284)) + if (!n._owner) throw Error(w(290, e)) + } + return e +} +function vr(e, t) { + throw ( + (e = Object.prototype.toString.call(t)), + Error(w(31, e === '[object Object]' ? 'object with keys {' + Object.keys(t).join(', ') + '}' : e)) + ) +} +function Po(e) { + var t = e._init + return t(e._payload) +} +function ma(e) { + function t(h, d) { + if (e) { + var v = h.deletions + v === null ? ((h.deletions = [d]), (h.flags |= 16)) : v.push(d) + } + } + function n(h, d) { + if (!e) return null + for (; d !== null; ) (t(h, d), (d = d.sibling)) + return null + } + function r(h, d) { + for (h = new Map(); d !== null; ) (d.key !== null ? h.set(d.key, d) : h.set(d.index, d), (d = d.sibling)) + return h + } + function l(h, d) { + return ((h = dt(h, d)), (h.index = 0), (h.sibling = null), h) + } + function i(h, d, v) { + return ( + (h.index = v), + e + ? ((v = h.alternate), v !== null ? ((v = v.index), v < d ? ((h.flags |= 2), d) : v) : ((h.flags |= 2), d)) + : ((h.flags |= 1048576), d) + ) + } + function s(h) { + return (e && h.alternate === null && (h.flags |= 2), h) + } + function u(h, d, v, x) { + return d === null || d.tag !== 6 ? ((d = Gl(v, h.mode, x)), (d.return = h), d) : ((d = l(d, v)), (d.return = h), d) + } + function a(h, d, v, x) { + var j = v.type + return j === It + ? m(h, d, v.props.children, x, v.key) + : d !== null && (d.elementType === j || (typeof j == 'object' && j !== null && j.$$typeof === qe && Po(j) === d.type)) + ? ((x = l(d, v.props)), (x.ref = gn(h, d, v)), (x.return = h), x) + : ((x = Ir(v.type, v.key, v.props, null, h.mode, x)), (x.ref = gn(h, d, v)), (x.return = h), x) + } + function c(h, d, v, x) { + return d === null || d.tag !== 4 || d.stateNode.containerInfo !== v.containerInfo || d.stateNode.implementation !== v.implementation + ? ((d = Zl(v, h.mode, x)), (d.return = h), d) + : ((d = l(d, v.children || [])), (d.return = h), d) + } + function m(h, d, v, x, j) { + return d === null || d.tag !== 7 ? ((d = Nt(v, h.mode, x, j)), (d.return = h), d) : ((d = l(d, v)), (d.return = h), d) + } + function p(h, d, v) { + if ((typeof d == 'string' && d !== '') || typeof d == 'number') return ((d = Gl('' + d, h.mode, v)), (d.return = h), d) + if (typeof d == 'object' && d !== null) { + switch (d.$$typeof) { + case ir: + return ((v = Ir(d.type, d.key, d.props, null, h.mode, v)), (v.ref = gn(h, null, d)), (v.return = h), v) + case Mt: + return ((d = Zl(d, h.mode, v)), (d.return = h), d) + case qe: + var x = d._init + return p(h, x(d._payload), v) + } + if (kn(d) || fn(d)) return ((d = Nt(d, h.mode, v, null)), (d.return = h), d) + vr(h, d) + } + return null + } + function f(h, d, v, x) { + var j = d !== null ? d.key : null + if ((typeof v == 'string' && v !== '') || typeof v == 'number') return j !== null ? null : u(h, d, '' + v, x) + if (typeof v == 'object' && v !== null) { + switch (v.$$typeof) { + case ir: + return v.key === j ? a(h, d, v, x) : null + case Mt: + return v.key === j ? c(h, d, v, x) : null + case qe: + return ((j = v._init), f(h, d, j(v._payload), x)) + } + if (kn(v) || fn(v)) return j !== null ? null : m(h, d, v, x, null) + vr(h, v) + } + return null + } + function y(h, d, v, x, j) { + if ((typeof x == 'string' && x !== '') || typeof x == 'number') return ((h = h.get(v) || null), u(d, h, '' + x, j)) + if (typeof x == 'object' && x !== null) { + switch (x.$$typeof) { + case ir: + return ((h = h.get(x.key === null ? v : x.key) || null), a(d, h, x, j)) + case Mt: + return ((h = h.get(x.key === null ? v : x.key) || null), c(d, h, x, j)) + case qe: + var N = x._init + return y(h, d, v, N(x._payload), j) + } + if (kn(x) || fn(x)) return ((h = h.get(v) || null), m(d, h, x, j, null)) + vr(d, x) + } + return null + } + function k(h, d, v, x) { + for (var j = null, N = null, E = d, C = (d = 0), W = null; E !== null && C < v.length; C++) { + E.index > C ? ((W = E), (E = null)) : (W = E.sibling) + var O = f(h, E, v[C], x) + if (O === null) { + E === null && (E = W) + break + } + ;(e && E && O.alternate === null && t(h, E), (d = i(O, d, C)), N === null ? (j = O) : (N.sibling = O), (N = O), (E = W)) + } + if (C === v.length) return (n(h, E), U && xt(h, C), j) + if (E === null) { + for (; C < v.length; C++) ((E = p(h, v[C], x)), E !== null && ((d = i(E, d, C)), N === null ? (j = E) : (N.sibling = E), (N = E))) + return (U && xt(h, C), j) + } + for (E = r(h, E); C < v.length; C++) + ((W = y(E, h, C, v[C], x)), + W !== null && + (e && W.alternate !== null && E.delete(W.key === null ? C : W.key), + (d = i(W, d, C)), + N === null ? (j = W) : (N.sibling = W), + (N = W))) + return ( + e && + E.forEach(function (Pe) { + return t(h, Pe) + }), + U && xt(h, C), + j + ) + } + function g(h, d, v, x) { + var j = fn(v) + if (typeof j != 'function') throw Error(w(150)) + if (((v = j.call(v)), v == null)) throw Error(w(151)) + for (var N = (j = null), E = d, C = (d = 0), W = null, O = v.next(); E !== null && !O.done; C++, O = v.next()) { + E.index > C ? ((W = E), (E = null)) : (W = E.sibling) + var Pe = f(h, E, O.value, x) + if (Pe === null) { + E === null && (E = W) + break + } + ;(e && E && Pe.alternate === null && t(h, E), (d = i(Pe, d, C)), N === null ? (j = Pe) : (N.sibling = Pe), (N = Pe), (E = W)) + } + if (O.done) return (n(h, E), U && xt(h, C), j) + if (E === null) { + for (; !O.done; C++, O = v.next()) + ((O = p(h, O.value, x)), O !== null && ((d = i(O, d, C)), N === null ? (j = O) : (N.sibling = O), (N = O))) + return (U && xt(h, C), j) + } + for (E = r(h, E); !O.done; C++, O = v.next()) + ((O = y(E, h, C, O.value, x)), + O !== null && + (e && O.alternate !== null && E.delete(O.key === null ? C : O.key), + (d = i(O, d, C)), + N === null ? (j = O) : (N.sibling = O), + (N = O))) + return ( + e && + E.forEach(function (cn) { + return t(h, cn) + }), + U && xt(h, C), + j + ) + } + function P(h, d, v, x) { + if ( + (typeof v == 'object' && v !== null && v.type === It && v.key === null && (v = v.props.children), typeof v == 'object' && v !== null) + ) { + switch (v.$$typeof) { + case ir: + e: { + for (var j = v.key, N = d; N !== null; ) { + if (N.key === j) { + if (((j = v.type), j === It)) { + if (N.tag === 7) { + ;(n(h, N.sibling), (d = l(N, v.props.children)), (d.return = h), (h = d)) + break e + } + } else if (N.elementType === j || (typeof j == 'object' && j !== null && j.$$typeof === qe && Po(j) === N.type)) { + ;(n(h, N.sibling), (d = l(N, v.props)), (d.ref = gn(h, N, v)), (d.return = h), (h = d)) + break e + } + n(h, N) + break + } else t(h, N) + N = N.sibling + } + v.type === It + ? ((d = Nt(v.props.children, h.mode, x, v.key)), (d.return = h), (h = d)) + : ((x = Ir(v.type, v.key, v.props, null, h.mode, x)), (x.ref = gn(h, d, v)), (x.return = h), (h = x)) + } + return s(h) + case Mt: + e: { + for (N = v.key; d !== null; ) { + if (d.key === N) + if (d.tag === 4 && d.stateNode.containerInfo === v.containerInfo && d.stateNode.implementation === v.implementation) { + ;(n(h, d.sibling), (d = l(d, v.children || [])), (d.return = h), (h = d)) + break e + } else { + n(h, d) + break + } + else t(h, d) + d = d.sibling + } + ;((d = Zl(v, h.mode, x)), (d.return = h), (h = d)) + } + return s(h) + case qe: + return ((N = v._init), P(h, d, N(v._payload), x)) + } + if (kn(v)) return k(h, d, v, x) + if (fn(v)) return g(h, d, v, x) + vr(h, v) + } + return (typeof v == 'string' && v !== '') || typeof v == 'number' + ? ((v = '' + v), + d !== null && d.tag === 6 + ? (n(h, d.sibling), (d = l(d, v)), (d.return = h), (h = d)) + : (n(h, d), (d = Gl(v, h.mode, x)), (d.return = h), (h = d)), + s(h)) + : n(h, d) + } + return P +} +var nn = ma(!0), + va = ma(!1), + Gr = mt(null), + Zr = null, + Ht = null, + vs = null +function gs() { + vs = Ht = Zr = null +} +function ys(e) { + var t = Gr.current + ;(F(Gr), (e._currentValue = t)) +} +function zi(e, t, n) { + for (; e !== null; ) { + var r = e.alternate + if ( + ((e.childLanes & t) !== t + ? ((e.childLanes |= t), r !== null && (r.childLanes |= t)) + : r !== null && (r.childLanes & t) !== t && (r.childLanes |= t), + e === n) + ) + break + e = e.return + } +} +function Zt(e, t) { + ;((Zr = e), + (vs = Ht = null), + (e = e.dependencies), + e !== null && e.firstContext !== null && (e.lanes & t && (de = !0), (e.firstContext = null))) +} +function Ee(e) { + var t = e._currentValue + if (vs !== e) + if (((e = { context: e, memoizedValue: t, next: null }), Ht === null)) { + if (Zr === null) throw Error(w(308)) + ;((Ht = e), (Zr.dependencies = { lanes: 0, firstContext: e })) + } else Ht = Ht.next = e + return t +} +var _t = null +function xs(e) { + _t === null ? (_t = [e]) : _t.push(e) +} +function ga(e, t, n, r) { + var l = t.interleaved + return (l === null ? ((n.next = n), xs(t)) : ((n.next = l.next), (l.next = n)), (t.interleaved = n), Xe(e, r)) +} +function Xe(e, t) { + e.lanes |= t + var n = e.alternate + for (n !== null && (n.lanes |= t), n = e, e = e.return; e !== null; ) + ((e.childLanes |= t), (n = e.alternate), n !== null && (n.childLanes |= t), (n = e), (e = e.return)) + return n.tag === 3 ? n.stateNode : null +} +var be = !1 +function ws(e) { + e.updateQueue = { + baseState: e.memoizedState, + firstBaseUpdate: null, + lastBaseUpdate: null, + shared: { pending: null, interleaved: null, lanes: 0 }, + effects: null, + } +} +function ya(e, t) { + ;((e = e.updateQueue), + t.updateQueue === e && + (t.updateQueue = { + baseState: e.baseState, + firstBaseUpdate: e.firstBaseUpdate, + lastBaseUpdate: e.lastBaseUpdate, + shared: e.shared, + effects: e.effects, + })) +} +function Qe(e, t) { + return { eventTime: e, lane: t, tag: 0, payload: null, callback: null, next: null } +} +function ut(e, t, n) { + var r = e.updateQueue + if (r === null) return null + if (((r = r.shared), M & 2)) { + var l = r.pending + return (l === null ? (t.next = t) : ((t.next = l.next), (l.next = t)), (r.pending = t), Xe(e, n)) + } + return ((l = r.interleaved), l === null ? ((t.next = t), xs(r)) : ((t.next = l.next), (l.next = t)), (r.interleaved = t), Xe(e, n)) +} +function Lr(e, t, n) { + if (((t = t.updateQueue), t !== null && ((t = t.shared), (n & 4194240) !== 0))) { + var r = t.lanes + ;((r &= e.pendingLanes), (n |= r), (t.lanes = n), is(e, n)) + } +} +function Lo(e, t) { + var n = e.updateQueue, + r = e.alternate + if (r !== null && ((r = r.updateQueue), n === r)) { + var l = null, + i = null + if (((n = n.firstBaseUpdate), n !== null)) { + do { + var s = { eventTime: n.eventTime, lane: n.lane, tag: n.tag, payload: n.payload, callback: n.callback, next: null } + ;(i === null ? (l = i = s) : (i = i.next = s), (n = n.next)) + } while (n !== null) + i === null ? (l = i = t) : (i = i.next = t) + } else l = i = t + ;((n = { baseState: r.baseState, firstBaseUpdate: l, lastBaseUpdate: i, shared: r.shared, effects: r.effects }), (e.updateQueue = n)) + return + } + ;((e = n.lastBaseUpdate), e === null ? (n.firstBaseUpdate = t) : (e.next = t), (n.lastBaseUpdate = t)) +} +function Jr(e, t, n, r) { + var l = e.updateQueue + be = !1 + var i = l.firstBaseUpdate, + s = l.lastBaseUpdate, + u = l.shared.pending + if (u !== null) { + l.shared.pending = null + var a = u, + c = a.next + ;((a.next = null), s === null ? (i = c) : (s.next = c), (s = a)) + var m = e.alternate + m !== null && + ((m = m.updateQueue), + (u = m.lastBaseUpdate), + u !== s && (u === null ? (m.firstBaseUpdate = c) : (u.next = c), (m.lastBaseUpdate = a))) + } + if (i !== null) { + var p = l.baseState + ;((s = 0), (m = c = a = null), (u = i)) + do { + var f = u.lane, + y = u.eventTime + if ((r & f) === f) { + m !== null && (m = m.next = { eventTime: y, lane: 0, tag: u.tag, payload: u.payload, callback: u.callback, next: null }) + e: { + var k = e, + g = u + switch (((f = t), (y = n), g.tag)) { + case 1: + if (((k = g.payload), typeof k == 'function')) { + p = k.call(y, p, f) + break e + } + p = k + break e + case 3: + k.flags = (k.flags & -65537) | 128 + case 0: + if (((k = g.payload), (f = typeof k == 'function' ? k.call(y, p, f) : k), f == null)) break e + p = B({}, p, f) + break e + case 2: + be = !0 + } + } + u.callback !== null && u.lane !== 0 && ((e.flags |= 64), (f = l.effects), f === null ? (l.effects = [u]) : f.push(u)) + } else + ((y = { eventTime: y, lane: f, tag: u.tag, payload: u.payload, callback: u.callback, next: null }), + m === null ? ((c = m = y), (a = p)) : (m = m.next = y), + (s |= f)) + if (((u = u.next), u === null)) { + if (((u = l.shared.pending), u === null)) break + ;((f = u), (u = f.next), (f.next = null), (l.lastBaseUpdate = f), (l.shared.pending = null)) + } + } while (!0) + if ( + (m === null && (a = p), (l.baseState = a), (l.firstBaseUpdate = c), (l.lastBaseUpdate = m), (t = l.shared.interleaved), t !== null) + ) { + l = t + do ((s |= l.lane), (l = l.next)) + while (l !== t) + } else i === null && (l.shared.lanes = 0) + ;((Lt |= s), (e.lanes = s), (e.memoizedState = p)) + } +} +function zo(e, t, n) { + if (((e = t.effects), (t.effects = null), e !== null)) + for (t = 0; t < e.length; t++) { + var r = e[t], + l = r.callback + if (l !== null) { + if (((r.callback = null), (r = n), typeof l != 'function')) throw Error(w(191, l)) + l.call(r) + } + } +} +var er = {}, + Ae = mt(er), + Wn = mt(er), + Qn = mt(er) +function jt(e) { + if (e === er) throw Error(w(174)) + return e +} +function ks(e, t) { + switch (($(Qn, t), $(Wn, e), $(Ae, er), (e = t.nodeType), e)) { + case 9: + case 11: + t = (t = t.documentElement) ? t.namespaceURI : ci(null, '') + break + default: + ;((e = e === 8 ? t.parentNode : t), (t = e.namespaceURI || null), (e = e.tagName), (t = ci(t, e))) + } + ;(F(Ae), $(Ae, t)) +} +function rn() { + ;(F(Ae), F(Wn), F(Qn)) +} +function xa(e) { + jt(Qn.current) + var t = jt(Ae.current), + n = ci(t, e.type) + t !== n && ($(Wn, e), $(Ae, n)) +} +function _s(e) { + Wn.current === e && (F(Ae), F(Wn)) +} +var A = mt(0) +function qr(e) { + for (var t = e; t !== null; ) { + if (t.tag === 13) { + var n = t.memoizedState + if (n !== null && ((n = n.dehydrated), n === null || n.data === '$?' || n.data === '$!')) return t + } else if (t.tag === 19 && t.memoizedProps.revealOrder !== void 0) { + if (t.flags & 128) return t + } else if (t.child !== null) { + ;((t.child.return = t), (t = t.child)) + continue + } + if (t === e) break + for (; t.sibling === null; ) { + if (t.return === null || t.return === e) return null + t = t.return + } + ;((t.sibling.return = t.return), (t = t.sibling)) + } + return null +} +var Hl = [] +function js() { + for (var e = 0; e < Hl.length; e++) Hl[e]._workInProgressVersionPrimary = null + Hl.length = 0 +} +var zr = Ze.ReactCurrentDispatcher, + Wl = Ze.ReactCurrentBatchConfig, + Pt = 0, + V = null, + X = null, + J = null, + br = !1, + Ln = !1, + Kn = 0, + mf = 0 +function ne() { + throw Error(w(321)) +} +function Ss(e, t) { + if (t === null) return !1 + for (var n = 0; n < t.length && n < e.length; n++) if (!Ie(e[n], t[n])) return !1 + return !0 +} +function Ns(e, t, n, r, l, i) { + if ( + ((Pt = i), + (V = t), + (t.memoizedState = null), + (t.updateQueue = null), + (t.lanes = 0), + (zr.current = e === null || e.memoizedState === null ? xf : wf), + (e = n(r, l)), + Ln) + ) { + i = 0 + do { + if (((Ln = !1), (Kn = 0), 25 <= i)) throw Error(w(301)) + ;((i += 1), (J = X = null), (t.updateQueue = null), (zr.current = kf), (e = n(r, l))) + } while (Ln) + } + if (((zr.current = el), (t = X !== null && X.next !== null), (Pt = 0), (J = X = V = null), (br = !1), t)) throw Error(w(300)) + return e +} +function Es() { + var e = Kn !== 0 + return ((Kn = 0), e) +} +function De() { + var e = { memoizedState: null, baseState: null, baseQueue: null, queue: null, next: null } + return (J === null ? (V.memoizedState = J = e) : (J = J.next = e), J) +} +function Ce() { + if (X === null) { + var e = V.alternate + e = e !== null ? e.memoizedState : null + } else e = X.next + var t = J === null ? V.memoizedState : J.next + if (t !== null) ((J = t), (X = e)) + else { + if (e === null) throw Error(w(310)) + ;((X = e), + (e = { memoizedState: X.memoizedState, baseState: X.baseState, baseQueue: X.baseQueue, queue: X.queue, next: null }), + J === null ? (V.memoizedState = J = e) : (J = J.next = e)) + } + return J +} +function Yn(e, t) { + return typeof t == 'function' ? t(e) : t +} +function Ql(e) { + var t = Ce(), + n = t.queue + if (n === null) throw Error(w(311)) + n.lastRenderedReducer = e + var r = X, + l = r.baseQueue, + i = n.pending + if (i !== null) { + if (l !== null) { + var s = l.next + ;((l.next = i.next), (i.next = s)) + } + ;((r.baseQueue = l = i), (n.pending = null)) + } + if (l !== null) { + ;((i = l.next), (r = r.baseState)) + var u = (s = null), + a = null, + c = i + do { + var m = c.lane + if ((Pt & m) === m) + (a !== null && (a = a.next = { lane: 0, action: c.action, hasEagerState: c.hasEagerState, eagerState: c.eagerState, next: null }), + (r = c.hasEagerState ? c.eagerState : e(r, c.action))) + else { + var p = { lane: m, action: c.action, hasEagerState: c.hasEagerState, eagerState: c.eagerState, next: null } + ;(a === null ? ((u = a = p), (s = r)) : (a = a.next = p), (V.lanes |= m), (Lt |= m)) + } + c = c.next + } while (c !== null && c !== i) + ;(a === null ? (s = r) : (a.next = u), + Ie(r, t.memoizedState) || (de = !0), + (t.memoizedState = r), + (t.baseState = s), + (t.baseQueue = a), + (n.lastRenderedState = r)) + } + if (((e = n.interleaved), e !== null)) { + l = e + do ((i = l.lane), (V.lanes |= i), (Lt |= i), (l = l.next)) + while (l !== e) + } else l === null && (n.lanes = 0) + return [t.memoizedState, n.dispatch] +} +function Kl(e) { + var t = Ce(), + n = t.queue + if (n === null) throw Error(w(311)) + n.lastRenderedReducer = e + var r = n.dispatch, + l = n.pending, + i = t.memoizedState + if (l !== null) { + n.pending = null + var s = (l = l.next) + do ((i = e(i, s.action)), (s = s.next)) + while (s !== l) + ;(Ie(i, t.memoizedState) || (de = !0), (t.memoizedState = i), t.baseQueue === null && (t.baseState = i), (n.lastRenderedState = i)) + } + return [i, r] +} +function wa() {} +function ka(e, t) { + var n = V, + r = Ce(), + l = t(), + i = !Ie(r.memoizedState, l) + if ( + (i && ((r.memoizedState = l), (de = !0)), + (r = r.queue), + Cs(Sa.bind(null, n, r, e), [e]), + r.getSnapshot !== t || i || (J !== null && J.memoizedState.tag & 1)) + ) { + if (((n.flags |= 2048), Xn(9, ja.bind(null, n, r, l, t), void 0, null), q === null)) throw Error(w(349)) + Pt & 30 || _a(n, t, l) + } + return l +} +function _a(e, t, n) { + ;((e.flags |= 16384), + (e = { getSnapshot: t, value: n }), + (t = V.updateQueue), + t === null + ? ((t = { lastEffect: null, stores: null }), (V.updateQueue = t), (t.stores = [e])) + : ((n = t.stores), n === null ? (t.stores = [e]) : n.push(e))) +} +function ja(e, t, n, r) { + ;((t.value = n), (t.getSnapshot = r), Na(t) && Ea(e)) +} +function Sa(e, t, n) { + return n(function () { + Na(t) && Ea(e) + }) +} +function Na(e) { + var t = e.getSnapshot + e = e.value + try { + var n = t() + return !Ie(e, n) + } catch { + return !0 + } +} +function Ea(e) { + var t = Xe(e, 1) + t !== null && Me(t, e, 1, -1) +} +function To(e) { + var t = De() + return ( + typeof e == 'function' && (e = e()), + (t.memoizedState = t.baseState = e), + (e = { pending: null, interleaved: null, lanes: 0, dispatch: null, lastRenderedReducer: Yn, lastRenderedState: e }), + (t.queue = e), + (e = e.dispatch = yf.bind(null, V, e)), + [t.memoizedState, e] + ) +} +function Xn(e, t, n, r) { + return ( + (e = { tag: e, create: t, destroy: n, deps: r, next: null }), + (t = V.updateQueue), + t === null + ? ((t = { lastEffect: null, stores: null }), (V.updateQueue = t), (t.lastEffect = e.next = e)) + : ((n = t.lastEffect), n === null ? (t.lastEffect = e.next = e) : ((r = n.next), (n.next = e), (e.next = r), (t.lastEffect = e))), + e + ) +} +function Ca() { + return Ce().memoizedState +} +function Tr(e, t, n, r) { + var l = De() + ;((V.flags |= e), (l.memoizedState = Xn(1 | t, n, void 0, r === void 0 ? null : r))) +} +function pl(e, t, n, r) { + var l = Ce() + r = r === void 0 ? null : r + var i = void 0 + if (X !== null) { + var s = X.memoizedState + if (((i = s.destroy), r !== null && Ss(r, s.deps))) { + l.memoizedState = Xn(t, n, i, r) + return + } + } + ;((V.flags |= e), (l.memoizedState = Xn(1 | t, n, i, r))) +} +function Ro(e, t) { + return Tr(8390656, 8, e, t) +} +function Cs(e, t) { + return pl(2048, 8, e, t) +} +function Pa(e, t) { + return pl(4, 2, e, t) +} +function La(e, t) { + return pl(4, 4, e, t) +} +function za(e, t) { + if (typeof t == 'function') + return ( + (e = e()), + t(e), + function () { + t(null) + } + ) + if (t != null) + return ( + (e = e()), + (t.current = e), + function () { + t.current = null + } + ) +} +function Ta(e, t, n) { + return ((n = n != null ? n.concat([e]) : null), pl(4, 4, za.bind(null, t, e), n)) +} +function Ps() {} +function Ra(e, t) { + var n = Ce() + t = t === void 0 ? null : t + var r = n.memoizedState + return r !== null && t !== null && Ss(t, r[1]) ? r[0] : ((n.memoizedState = [e, t]), e) +} +function Oa(e, t) { + var n = Ce() + t = t === void 0 ? null : t + var r = n.memoizedState + return r !== null && t !== null && Ss(t, r[1]) ? r[0] : ((e = e()), (n.memoizedState = [e, t]), e) +} +function Ma(e, t, n) { + return Pt & 21 + ? (Ie(n, t) || ((n = Uu()), (V.lanes |= n), (Lt |= n), (e.baseState = !0)), t) + : (e.baseState && ((e.baseState = !1), (de = !0)), (e.memoizedState = n)) +} +function vf(e, t) { + var n = I + ;((I = n !== 0 && 4 > n ? n : 4), e(!0)) + var r = Wl.transition + Wl.transition = {} + try { + ;(e(!1), t()) + } finally { + ;((I = n), (Wl.transition = r)) + } +} +function Ia() { + return Ce().memoizedState +} +function gf(e, t, n) { + var r = ct(e) + if (((n = { lane: r, action: n, hasEagerState: !1, eagerState: null, next: null }), $a(e))) Da(t, n) + else if (((n = ga(e, t, n, r)), n !== null)) { + var l = oe() + ;(Me(n, e, r, l), Fa(n, t, r)) + } +} +function yf(e, t, n) { + var r = ct(e), + l = { lane: r, action: n, hasEagerState: !1, eagerState: null, next: null } + if ($a(e)) Da(t, l) + else { + var i = e.alternate + if (e.lanes === 0 && (i === null || i.lanes === 0) && ((i = t.lastRenderedReducer), i !== null)) + try { + var s = t.lastRenderedState, + u = i(s, n) + if (((l.hasEagerState = !0), (l.eagerState = u), Ie(u, s))) { + var a = t.interleaved + ;(a === null ? ((l.next = l), xs(t)) : ((l.next = a.next), (a.next = l)), (t.interleaved = l)) + return + } + } catch { + } finally { + } + ;((n = ga(e, t, l, r)), n !== null && ((l = oe()), Me(n, e, r, l), Fa(n, t, r))) + } +} +function $a(e) { + var t = e.alternate + return e === V || (t !== null && t === V) +} +function Da(e, t) { + Ln = br = !0 + var n = e.pending + ;(n === null ? (t.next = t) : ((t.next = n.next), (n.next = t)), (e.pending = t)) +} +function Fa(e, t, n) { + if (n & 4194240) { + var r = t.lanes + ;((r &= e.pendingLanes), (n |= r), (t.lanes = n), is(e, n)) + } +} +var el = { + readContext: Ee, + useCallback: ne, + useContext: ne, + useEffect: ne, + useImperativeHandle: ne, + useInsertionEffect: ne, + useLayoutEffect: ne, + useMemo: ne, + useReducer: ne, + useRef: ne, + useState: ne, + useDebugValue: ne, + useDeferredValue: ne, + useTransition: ne, + useMutableSource: ne, + useSyncExternalStore: ne, + useId: ne, + unstable_isNewReconciler: !1, + }, + xf = { + readContext: Ee, + useCallback: function (e, t) { + return ((De().memoizedState = [e, t === void 0 ? null : t]), e) + }, + useContext: Ee, + useEffect: Ro, + useImperativeHandle: function (e, t, n) { + return ((n = n != null ? n.concat([e]) : null), Tr(4194308, 4, za.bind(null, t, e), n)) + }, + useLayoutEffect: function (e, t) { + return Tr(4194308, 4, e, t) + }, + useInsertionEffect: function (e, t) { + return Tr(4, 2, e, t) + }, + useMemo: function (e, t) { + var n = De() + return ((t = t === void 0 ? null : t), (e = e()), (n.memoizedState = [e, t]), e) + }, + useReducer: function (e, t, n) { + var r = De() + return ( + (t = n !== void 0 ? n(t) : t), + (r.memoizedState = r.baseState = t), + (e = { pending: null, interleaved: null, lanes: 0, dispatch: null, lastRenderedReducer: e, lastRenderedState: t }), + (r.queue = e), + (e = e.dispatch = gf.bind(null, V, e)), + [r.memoizedState, e] + ) + }, + useRef: function (e) { + var t = De() + return ((e = { current: e }), (t.memoizedState = e)) + }, + useState: To, + useDebugValue: Ps, + useDeferredValue: function (e) { + return (De().memoizedState = e) + }, + useTransition: function () { + var e = To(!1), + t = e[0] + return ((e = vf.bind(null, e[1])), (De().memoizedState = e), [t, e]) + }, + useMutableSource: function () {}, + useSyncExternalStore: function (e, t, n) { + var r = V, + l = De() + if (U) { + if (n === void 0) throw Error(w(407)) + n = n() + } else { + if (((n = t()), q === null)) throw Error(w(349)) + Pt & 30 || _a(r, t, n) + } + l.memoizedState = n + var i = { value: n, getSnapshot: t } + return ((l.queue = i), Ro(Sa.bind(null, r, i, e), [e]), (r.flags |= 2048), Xn(9, ja.bind(null, r, i, n, t), void 0, null), n) + }, + useId: function () { + var e = De(), + t = q.identifierPrefix + if (U) { + var n = We, + r = He + ;((n = (r & ~(1 << (32 - Oe(r) - 1))).toString(32) + n), + (t = ':' + t + 'R' + n), + (n = Kn++), + 0 < n && (t += 'H' + n.toString(32)), + (t += ':')) + } else ((n = mf++), (t = ':' + t + 'r' + n.toString(32) + ':')) + return (e.memoizedState = t) + }, + unstable_isNewReconciler: !1, + }, + wf = { + readContext: Ee, + useCallback: Ra, + useContext: Ee, + useEffect: Cs, + useImperativeHandle: Ta, + useInsertionEffect: Pa, + useLayoutEffect: La, + useMemo: Oa, + useReducer: Ql, + useRef: Ca, + useState: function () { + return Ql(Yn) + }, + useDebugValue: Ps, + useDeferredValue: function (e) { + var t = Ce() + return Ma(t, X.memoizedState, e) + }, + useTransition: function () { + var e = Ql(Yn)[0], + t = Ce().memoizedState + return [e, t] + }, + useMutableSource: wa, + useSyncExternalStore: ka, + useId: Ia, + unstable_isNewReconciler: !1, + }, + kf = { + readContext: Ee, + useCallback: Ra, + useContext: Ee, + useEffect: Cs, + useImperativeHandle: Ta, + useInsertionEffect: Pa, + useLayoutEffect: La, + useMemo: Oa, + useReducer: Kl, + useRef: Ca, + useState: function () { + return Kl(Yn) + }, + useDebugValue: Ps, + useDeferredValue: function (e) { + var t = Ce() + return X === null ? (t.memoizedState = e) : Ma(t, X.memoizedState, e) + }, + useTransition: function () { + var e = Kl(Yn)[0], + t = Ce().memoizedState + return [e, t] + }, + useMutableSource: wa, + useSyncExternalStore: ka, + useId: Ia, + unstable_isNewReconciler: !1, + } +function ze(e, t) { + if (e && e.defaultProps) { + ;((t = B({}, t)), (e = e.defaultProps)) + for (var n in e) t[n] === void 0 && (t[n] = e[n]) + return t + } + return t +} +function Ti(e, t, n, r) { + ;((t = e.memoizedState), + (n = n(r, t)), + (n = n == null ? t : B({}, t, n)), + (e.memoizedState = n), + e.lanes === 0 && (e.updateQueue.baseState = n)) +} +var hl = { + isMounted: function (e) { + return (e = e._reactInternals) ? Rt(e) === e : !1 + }, + enqueueSetState: function (e, t, n) { + e = e._reactInternals + var r = oe(), + l = ct(e), + i = Qe(r, l) + ;((i.payload = t), n != null && (i.callback = n), (t = ut(e, i, l)), t !== null && (Me(t, e, l, r), Lr(t, e, l))) + }, + enqueueReplaceState: function (e, t, n) { + e = e._reactInternals + var r = oe(), + l = ct(e), + i = Qe(r, l) + ;((i.tag = 1), (i.payload = t), n != null && (i.callback = n), (t = ut(e, i, l)), t !== null && (Me(t, e, l, r), Lr(t, e, l))) + }, + enqueueForceUpdate: function (e, t) { + e = e._reactInternals + var n = oe(), + r = ct(e), + l = Qe(n, r) + ;((l.tag = 2), t != null && (l.callback = t), (t = ut(e, l, r)), t !== null && (Me(t, e, r, n), Lr(t, e, r))) + }, +} +function Oo(e, t, n, r, l, i, s) { + return ( + (e = e.stateNode), + typeof e.shouldComponentUpdate == 'function' + ? e.shouldComponentUpdate(r, i, s) + : t.prototype && t.prototype.isPureReactComponent + ? !An(n, r) || !An(l, i) + : !0 + ) +} +function Ua(e, t, n) { + var r = !1, + l = pt, + i = t.contextType + return ( + typeof i == 'object' && i !== null + ? (i = Ee(i)) + : ((l = he(t) ? Et : ie.current), (r = t.contextTypes), (i = (r = r != null) ? en(e, l) : pt)), + (t = new t(n, i)), + (e.memoizedState = t.state !== null && t.state !== void 0 ? t.state : null), + (t.updater = hl), + (e.stateNode = t), + (t._reactInternals = e), + r && ((e = e.stateNode), (e.__reactInternalMemoizedUnmaskedChildContext = l), (e.__reactInternalMemoizedMaskedChildContext = i)), + t + ) +} +function Mo(e, t, n, r) { + ;((e = t.state), + typeof t.componentWillReceiveProps == 'function' && t.componentWillReceiveProps(n, r), + typeof t.UNSAFE_componentWillReceiveProps == 'function' && t.UNSAFE_componentWillReceiveProps(n, r), + t.state !== e && hl.enqueueReplaceState(t, t.state, null)) +} +function Ri(e, t, n, r) { + var l = e.stateNode + ;((l.props = n), (l.state = e.memoizedState), (l.refs = {}), ws(e)) + var i = t.contextType + ;(typeof i == 'object' && i !== null ? (l.context = Ee(i)) : ((i = he(t) ? Et : ie.current), (l.context = en(e, i))), + (l.state = e.memoizedState), + (i = t.getDerivedStateFromProps), + typeof i == 'function' && (Ti(e, t, i, n), (l.state = e.memoizedState)), + typeof t.getDerivedStateFromProps == 'function' || + typeof l.getSnapshotBeforeUpdate == 'function' || + (typeof l.UNSAFE_componentWillMount != 'function' && typeof l.componentWillMount != 'function') || + ((t = l.state), + typeof l.componentWillMount == 'function' && l.componentWillMount(), + typeof l.UNSAFE_componentWillMount == 'function' && l.UNSAFE_componentWillMount(), + t !== l.state && hl.enqueueReplaceState(l, l.state, null), + Jr(e, n, l, r), + (l.state = e.memoizedState)), + typeof l.componentDidMount == 'function' && (e.flags |= 4194308)) +} +function ln(e, t) { + try { + var n = '', + r = t + do ((n += Xc(r)), (r = r.return)) + while (r) + var l = n + } catch (i) { + l = + ` +Error generating stack: ` + + i.message + + ` +` + + i.stack + } + return { value: e, source: t, stack: l, digest: null } +} +function Yl(e, t, n) { + return { value: e, source: null, stack: n ?? null, digest: t ?? null } +} +function Oi(e, t) { + try { + console.error(t.value) + } catch (n) { + setTimeout(function () { + throw n + }) + } +} +var _f = typeof WeakMap == 'function' ? WeakMap : Map +function Aa(e, t, n) { + ;((n = Qe(-1, n)), (n.tag = 3), (n.payload = { element: null })) + var r = t.value + return ( + (n.callback = function () { + ;(nl || ((nl = !0), (Hi = r)), Oi(e, t)) + }), + n + ) +} +function Va(e, t, n) { + ;((n = Qe(-1, n)), (n.tag = 3)) + var r = e.type.getDerivedStateFromError + if (typeof r == 'function') { + var l = t.value + ;((n.payload = function () { + return r(l) + }), + (n.callback = function () { + Oi(e, t) + })) + } + var i = e.stateNode + return ( + i !== null && + typeof i.componentDidCatch == 'function' && + (n.callback = function () { + ;(Oi(e, t), typeof r != 'function' && (at === null ? (at = new Set([this])) : at.add(this))) + var s = t.stack + this.componentDidCatch(t.value, { componentStack: s !== null ? s : '' }) + }), + n + ) +} +function Io(e, t, n) { + var r = e.pingCache + if (r === null) { + r = e.pingCache = new _f() + var l = new Set() + r.set(t, l) + } else ((l = r.get(t)), l === void 0 && ((l = new Set()), r.set(t, l))) + l.has(n) || (l.add(n), (e = $f.bind(null, e, t, n)), t.then(e, e)) +} +function $o(e) { + do { + var t + if (((t = e.tag === 13) && ((t = e.memoizedState), (t = t !== null ? t.dehydrated !== null : !0)), t)) return e + e = e.return + } while (e !== null) + return null +} +function Do(e, t, n, r, l) { + return e.mode & 1 + ? ((e.flags |= 65536), (e.lanes = l), e) + : (e === t + ? (e.flags |= 65536) + : ((e.flags |= 128), + (n.flags |= 131072), + (n.flags &= -52805), + n.tag === 1 && (n.alternate === null ? (n.tag = 17) : ((t = Qe(-1, 1)), (t.tag = 2), ut(n, t, 1))), + (n.lanes |= 1)), + e) +} +var jf = Ze.ReactCurrentOwner, + de = !1 +function se(e, t, n, r) { + t.child = e === null ? va(t, null, n, r) : nn(t, e.child, n, r) +} +function Fo(e, t, n, r, l) { + n = n.render + var i = t.ref + return ( + Zt(t, l), + (r = Ns(e, t, n, r, i, l)), + (n = Es()), + e !== null && !de + ? ((t.updateQueue = e.updateQueue), (t.flags &= -2053), (e.lanes &= ~l), Ge(e, t, l)) + : (U && n && ps(t), (t.flags |= 1), se(e, t, r, l), t.child) + ) +} +function Uo(e, t, n, r, l) { + if (e === null) { + var i = n.type + return typeof i == 'function' && !$s(i) && i.defaultProps === void 0 && n.compare === null && n.defaultProps === void 0 + ? ((t.tag = 15), (t.type = i), Ba(e, t, i, r, l)) + : ((e = Ir(n.type, null, r, t, t.mode, l)), (e.ref = t.ref), (e.return = t), (t.child = e)) + } + if (((i = e.child), !(e.lanes & l))) { + var s = i.memoizedProps + if (((n = n.compare), (n = n !== null ? n : An), n(s, r) && e.ref === t.ref)) return Ge(e, t, l) + } + return ((t.flags |= 1), (e = dt(i, r)), (e.ref = t.ref), (e.return = t), (t.child = e)) +} +function Ba(e, t, n, r, l) { + if (e !== null) { + var i = e.memoizedProps + if (An(i, r) && e.ref === t.ref) + if (((de = !1), (t.pendingProps = r = i), (e.lanes & l) !== 0)) e.flags & 131072 && (de = !0) + else return ((t.lanes = e.lanes), Ge(e, t, l)) + } + return Mi(e, t, n, r, l) +} +function Ha(e, t, n) { + var r = t.pendingProps, + l = r.children, + i = e !== null ? e.memoizedState : null + if (r.mode === 'hidden') + if (!(t.mode & 1)) ((t.memoizedState = { baseLanes: 0, cachePool: null, transitions: null }), $(Qt, ve), (ve |= n)) + else { + if (!(n & 1073741824)) + return ( + (e = i !== null ? i.baseLanes | n : n), + (t.lanes = t.childLanes = 1073741824), + (t.memoizedState = { baseLanes: e, cachePool: null, transitions: null }), + (t.updateQueue = null), + $(Qt, ve), + (ve |= e), + null + ) + ;((t.memoizedState = { baseLanes: 0, cachePool: null, transitions: null }), (r = i !== null ? i.baseLanes : n), $(Qt, ve), (ve |= r)) + } + else (i !== null ? ((r = i.baseLanes | n), (t.memoizedState = null)) : (r = n), $(Qt, ve), (ve |= r)) + return (se(e, t, l, n), t.child) +} +function Wa(e, t) { + var n = t.ref + ;((e === null && n !== null) || (e !== null && e.ref !== n)) && ((t.flags |= 512), (t.flags |= 2097152)) +} +function Mi(e, t, n, r, l) { + var i = he(n) ? Et : ie.current + return ( + (i = en(t, i)), + Zt(t, l), + (n = Ns(e, t, n, r, i, l)), + (r = Es()), + e !== null && !de + ? ((t.updateQueue = e.updateQueue), (t.flags &= -2053), (e.lanes &= ~l), Ge(e, t, l)) + : (U && r && ps(t), (t.flags |= 1), se(e, t, n, l), t.child) + ) +} +function Ao(e, t, n, r, l) { + if (he(n)) { + var i = !0 + Kr(t) + } else i = !1 + if ((Zt(t, l), t.stateNode === null)) (Rr(e, t), Ua(t, n, r), Ri(t, n, r, l), (r = !0)) + else if (e === null) { + var s = t.stateNode, + u = t.memoizedProps + s.props = u + var a = s.context, + c = n.contextType + typeof c == 'object' && c !== null ? (c = Ee(c)) : ((c = he(n) ? Et : ie.current), (c = en(t, c))) + var m = n.getDerivedStateFromProps, + p = typeof m == 'function' || typeof s.getSnapshotBeforeUpdate == 'function' + ;(p || + (typeof s.UNSAFE_componentWillReceiveProps != 'function' && typeof s.componentWillReceiveProps != 'function') || + ((u !== r || a !== c) && Mo(t, s, r, c)), + (be = !1)) + var f = t.memoizedState + ;((s.state = f), + Jr(t, r, s, l), + (a = t.memoizedState), + u !== r || f !== a || pe.current || be + ? (typeof m == 'function' && (Ti(t, n, m, r), (a = t.memoizedState)), + (u = be || Oo(t, n, u, r, f, a, c)) + ? (p || + (typeof s.UNSAFE_componentWillMount != 'function' && typeof s.componentWillMount != 'function') || + (typeof s.componentWillMount == 'function' && s.componentWillMount(), + typeof s.UNSAFE_componentWillMount == 'function' && s.UNSAFE_componentWillMount()), + typeof s.componentDidMount == 'function' && (t.flags |= 4194308)) + : (typeof s.componentDidMount == 'function' && (t.flags |= 4194308), (t.memoizedProps = r), (t.memoizedState = a)), + (s.props = r), + (s.state = a), + (s.context = c), + (r = u)) + : (typeof s.componentDidMount == 'function' && (t.flags |= 4194308), (r = !1))) + } else { + ;((s = t.stateNode), + ya(e, t), + (u = t.memoizedProps), + (c = t.type === t.elementType ? u : ze(t.type, u)), + (s.props = c), + (p = t.pendingProps), + (f = s.context), + (a = n.contextType), + typeof a == 'object' && a !== null ? (a = Ee(a)) : ((a = he(n) ? Et : ie.current), (a = en(t, a)))) + var y = n.getDerivedStateFromProps + ;((m = typeof y == 'function' || typeof s.getSnapshotBeforeUpdate == 'function') || + (typeof s.UNSAFE_componentWillReceiveProps != 'function' && typeof s.componentWillReceiveProps != 'function') || + ((u !== p || f !== a) && Mo(t, s, r, a)), + (be = !1), + (f = t.memoizedState), + (s.state = f), + Jr(t, r, s, l)) + var k = t.memoizedState + u !== p || f !== k || pe.current || be + ? (typeof y == 'function' && (Ti(t, n, y, r), (k = t.memoizedState)), + (c = be || Oo(t, n, c, r, f, k, a) || !1) + ? (m || + (typeof s.UNSAFE_componentWillUpdate != 'function' && typeof s.componentWillUpdate != 'function') || + (typeof s.componentWillUpdate == 'function' && s.componentWillUpdate(r, k, a), + typeof s.UNSAFE_componentWillUpdate == 'function' && s.UNSAFE_componentWillUpdate(r, k, a)), + typeof s.componentDidUpdate == 'function' && (t.flags |= 4), + typeof s.getSnapshotBeforeUpdate == 'function' && (t.flags |= 1024)) + : (typeof s.componentDidUpdate != 'function' || (u === e.memoizedProps && f === e.memoizedState) || (t.flags |= 4), + typeof s.getSnapshotBeforeUpdate != 'function' || (u === e.memoizedProps && f === e.memoizedState) || (t.flags |= 1024), + (t.memoizedProps = r), + (t.memoizedState = k)), + (s.props = r), + (s.state = k), + (s.context = a), + (r = c)) + : (typeof s.componentDidUpdate != 'function' || (u === e.memoizedProps && f === e.memoizedState) || (t.flags |= 4), + typeof s.getSnapshotBeforeUpdate != 'function' || (u === e.memoizedProps && f === e.memoizedState) || (t.flags |= 1024), + (r = !1)) + } + return Ii(e, t, n, r, i, l) +} +function Ii(e, t, n, r, l, i) { + Wa(e, t) + var s = (t.flags & 128) !== 0 + if (!r && !s) return (l && No(t, n, !1), Ge(e, t, i)) + ;((r = t.stateNode), (jf.current = t)) + var u = s && typeof n.getDerivedStateFromError != 'function' ? null : r.render() + return ( + (t.flags |= 1), + e !== null && s ? ((t.child = nn(t, e.child, null, i)), (t.child = nn(t, null, u, i))) : se(e, t, u, i), + (t.memoizedState = r.state), + l && No(t, n, !0), + t.child + ) +} +function Qa(e) { + var t = e.stateNode + ;(t.pendingContext ? So(e, t.pendingContext, t.pendingContext !== t.context) : t.context && So(e, t.context, !1), ks(e, t.containerInfo)) +} +function Vo(e, t, n, r, l) { + return (tn(), ms(l), (t.flags |= 256), se(e, t, n, r), t.child) +} +var $i = { dehydrated: null, treeContext: null, retryLane: 0 } +function Di(e) { + return { baseLanes: e, cachePool: null, transitions: null } +} +function Ka(e, t, n) { + var r = t.pendingProps, + l = A.current, + i = !1, + s = (t.flags & 128) !== 0, + u + if ( + ((u = s) || (u = e !== null && e.memoizedState === null ? !1 : (l & 2) !== 0), + u ? ((i = !0), (t.flags &= -129)) : (e === null || e.memoizedState !== null) && (l |= 1), + $(A, l & 1), + e === null) + ) + return ( + Li(t), + (e = t.memoizedState), + e !== null && ((e = e.dehydrated), e !== null) + ? (t.mode & 1 ? (e.data === '$!' ? (t.lanes = 8) : (t.lanes = 1073741824)) : (t.lanes = 1), null) + : ((s = r.children), + (e = r.fallback), + i + ? ((r = t.mode), + (i = t.child), + (s = { mode: 'hidden', children: s }), + !(r & 1) && i !== null ? ((i.childLanes = 0), (i.pendingProps = s)) : (i = gl(s, r, 0, null)), + (e = Nt(e, r, n, null)), + (i.return = t), + (e.return = t), + (i.sibling = e), + (t.child = i), + (t.child.memoizedState = Di(n)), + (t.memoizedState = $i), + e) + : Ls(t, s)) + ) + if (((l = e.memoizedState), l !== null && ((u = l.dehydrated), u !== null))) return Sf(e, t, s, r, u, l, n) + if (i) { + ;((i = r.fallback), (s = t.mode), (l = e.child), (u = l.sibling)) + var a = { mode: 'hidden', children: r.children } + return ( + !(s & 1) && t.child !== l + ? ((r = t.child), (r.childLanes = 0), (r.pendingProps = a), (t.deletions = null)) + : ((r = dt(l, a)), (r.subtreeFlags = l.subtreeFlags & 14680064)), + u !== null ? (i = dt(u, i)) : ((i = Nt(i, s, n, null)), (i.flags |= 2)), + (i.return = t), + (r.return = t), + (r.sibling = i), + (t.child = r), + (r = i), + (i = t.child), + (s = e.child.memoizedState), + (s = s === null ? Di(n) : { baseLanes: s.baseLanes | n, cachePool: null, transitions: s.transitions }), + (i.memoizedState = s), + (i.childLanes = e.childLanes & ~n), + (t.memoizedState = $i), + r + ) + } + return ( + (i = e.child), + (e = i.sibling), + (r = dt(i, { mode: 'visible', children: r.children })), + !(t.mode & 1) && (r.lanes = n), + (r.return = t), + (r.sibling = null), + e !== null && ((n = t.deletions), n === null ? ((t.deletions = [e]), (t.flags |= 16)) : n.push(e)), + (t.child = r), + (t.memoizedState = null), + r + ) +} +function Ls(e, t) { + return ((t = gl({ mode: 'visible', children: t }, e.mode, 0, null)), (t.return = e), (e.child = t)) +} +function gr(e, t, n, r) { + return (r !== null && ms(r), nn(t, e.child, null, n), (e = Ls(t, t.pendingProps.children)), (e.flags |= 2), (t.memoizedState = null), e) +} +function Sf(e, t, n, r, l, i, s) { + if (n) + return t.flags & 256 + ? ((t.flags &= -257), (r = Yl(Error(w(422)))), gr(e, t, s, r)) + : t.memoizedState !== null + ? ((t.child = e.child), (t.flags |= 128), null) + : ((i = r.fallback), + (l = t.mode), + (r = gl({ mode: 'visible', children: r.children }, l, 0, null)), + (i = Nt(i, l, s, null)), + (i.flags |= 2), + (r.return = t), + (i.return = t), + (r.sibling = i), + (t.child = r), + t.mode & 1 && nn(t, e.child, null, s), + (t.child.memoizedState = Di(s)), + (t.memoizedState = $i), + i) + if (!(t.mode & 1)) return gr(e, t, s, null) + if (l.data === '$!') { + if (((r = l.nextSibling && l.nextSibling.dataset), r)) var u = r.dgst + return ((r = u), (i = Error(w(419))), (r = Yl(i, r, void 0)), gr(e, t, s, r)) + } + if (((u = (s & e.childLanes) !== 0), de || u)) { + if (((r = q), r !== null)) { + switch (s & -s) { + case 4: + l = 2 + break + case 16: + l = 8 + break + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + case 8192: + case 16384: + case 32768: + case 65536: + case 131072: + case 262144: + case 524288: + case 1048576: + case 2097152: + case 4194304: + case 8388608: + case 16777216: + case 33554432: + case 67108864: + l = 32 + break + case 536870912: + l = 268435456 + break + default: + l = 0 + } + ;((l = l & (r.suspendedLanes | s) ? 0 : l), l !== 0 && l !== i.retryLane && ((i.retryLane = l), Xe(e, l), Me(r, e, l, -1))) + } + return (Is(), (r = Yl(Error(w(421)))), gr(e, t, s, r)) + } + return l.data === '$?' + ? ((t.flags |= 128), (t.child = e.child), (t = Df.bind(null, e)), (l._reactRetry = t), null) + : ((e = i.treeContext), + (ge = ot(l.nextSibling)), + (ye = t), + (U = !0), + (Re = null), + e !== null && ((_e[je++] = He), (_e[je++] = We), (_e[je++] = Ct), (He = e.id), (We = e.overflow), (Ct = t)), + (t = Ls(t, r.children)), + (t.flags |= 4096), + t) +} +function Bo(e, t, n) { + e.lanes |= t + var r = e.alternate + ;(r !== null && (r.lanes |= t), zi(e.return, t, n)) +} +function Xl(e, t, n, r, l) { + var i = e.memoizedState + i === null + ? (e.memoizedState = { isBackwards: t, rendering: null, renderingStartTime: 0, last: r, tail: n, tailMode: l }) + : ((i.isBackwards = t), (i.rendering = null), (i.renderingStartTime = 0), (i.last = r), (i.tail = n), (i.tailMode = l)) +} +function Ya(e, t, n) { + var r = t.pendingProps, + l = r.revealOrder, + i = r.tail + if ((se(e, t, r.children, n), (r = A.current), r & 2)) ((r = (r & 1) | 2), (t.flags |= 128)) + else { + if (e !== null && e.flags & 128) + e: for (e = t.child; e !== null; ) { + if (e.tag === 13) e.memoizedState !== null && Bo(e, n, t) + else if (e.tag === 19) Bo(e, n, t) + else if (e.child !== null) { + ;((e.child.return = e), (e = e.child)) + continue + } + if (e === t) break e + for (; e.sibling === null; ) { + if (e.return === null || e.return === t) break e + e = e.return + } + ;((e.sibling.return = e.return), (e = e.sibling)) + } + r &= 1 + } + if (($(A, r), !(t.mode & 1))) t.memoizedState = null + else + switch (l) { + case 'forwards': + for (n = t.child, l = null; n !== null; ) ((e = n.alternate), e !== null && qr(e) === null && (l = n), (n = n.sibling)) + ;((n = l), n === null ? ((l = t.child), (t.child = null)) : ((l = n.sibling), (n.sibling = null)), Xl(t, !1, l, n, i)) + break + case 'backwards': + for (n = null, l = t.child, t.child = null; l !== null; ) { + if (((e = l.alternate), e !== null && qr(e) === null)) { + t.child = l + break + } + ;((e = l.sibling), (l.sibling = n), (n = l), (l = e)) + } + Xl(t, !0, n, null, i) + break + case 'together': + Xl(t, !1, null, null, void 0) + break + default: + t.memoizedState = null + } + return t.child +} +function Rr(e, t) { + !(t.mode & 1) && e !== null && ((e.alternate = null), (t.alternate = null), (t.flags |= 2)) +} +function Ge(e, t, n) { + if ((e !== null && (t.dependencies = e.dependencies), (Lt |= t.lanes), !(n & t.childLanes))) return null + if (e !== null && t.child !== e.child) throw Error(w(153)) + if (t.child !== null) { + for (e = t.child, n = dt(e, e.pendingProps), t.child = n, n.return = t; e.sibling !== null; ) + ((e = e.sibling), (n = n.sibling = dt(e, e.pendingProps)), (n.return = t)) + n.sibling = null + } + return t.child +} +function Nf(e, t, n) { + switch (t.tag) { + case 3: + ;(Qa(t), tn()) + break + case 5: + xa(t) + break + case 1: + he(t.type) && Kr(t) + break + case 4: + ks(t, t.stateNode.containerInfo) + break + case 10: + var r = t.type._context, + l = t.memoizedProps.value + ;($(Gr, r._currentValue), (r._currentValue = l)) + break + case 13: + if (((r = t.memoizedState), r !== null)) + return r.dehydrated !== null + ? ($(A, A.current & 1), (t.flags |= 128), null) + : n & t.child.childLanes + ? Ka(e, t, n) + : ($(A, A.current & 1), (e = Ge(e, t, n)), e !== null ? e.sibling : null) + $(A, A.current & 1) + break + case 19: + if (((r = (n & t.childLanes) !== 0), e.flags & 128)) { + if (r) return Ya(e, t, n) + t.flags |= 128 + } + if (((l = t.memoizedState), l !== null && ((l.rendering = null), (l.tail = null), (l.lastEffect = null)), $(A, A.current), r)) break + return null + case 22: + case 23: + return ((t.lanes = 0), Ha(e, t, n)) + } + return Ge(e, t, n) +} +var Xa, Fi, Ga, Za +Xa = function (e, t) { + for (var n = t.child; n !== null; ) { + if (n.tag === 5 || n.tag === 6) e.appendChild(n.stateNode) + else if (n.tag !== 4 && n.child !== null) { + ;((n.child.return = n), (n = n.child)) + continue + } + if (n === t) break + for (; n.sibling === null; ) { + if (n.return === null || n.return === t) return + n = n.return + } + ;((n.sibling.return = n.return), (n = n.sibling)) + } +} +Fi = function () {} +Ga = function (e, t, n, r) { + var l = e.memoizedProps + if (l !== r) { + ;((e = t.stateNode), jt(Ae.current)) + var i = null + switch (n) { + case 'input': + ;((l = si(e, l)), (r = si(e, r)), (i = [])) + break + case 'select': + ;((l = B({}, l, { value: void 0 })), (r = B({}, r, { value: void 0 })), (i = [])) + break + case 'textarea': + ;((l = ai(e, l)), (r = ai(e, r)), (i = [])) + break + default: + typeof l.onClick != 'function' && typeof r.onClick == 'function' && (e.onclick = Wr) + } + di(n, r) + var s + n = null + for (c in l) + if (!r.hasOwnProperty(c) && l.hasOwnProperty(c) && l[c] != null) + if (c === 'style') { + var u = l[c] + for (s in u) u.hasOwnProperty(s) && (n || (n = {}), (n[s] = '')) + } else + c !== 'dangerouslySetInnerHTML' && + c !== 'children' && + c !== 'suppressContentEditableWarning' && + c !== 'suppressHydrationWarning' && + c !== 'autoFocus' && + (On.hasOwnProperty(c) ? i || (i = []) : (i = i || []).push(c, null)) + for (c in r) { + var a = r[c] + if (((u = l != null ? l[c] : void 0), r.hasOwnProperty(c) && a !== u && (a != null || u != null))) + if (c === 'style') + if (u) { + for (s in u) !u.hasOwnProperty(s) || (a && a.hasOwnProperty(s)) || (n || (n = {}), (n[s] = '')) + for (s in a) a.hasOwnProperty(s) && u[s] !== a[s] && (n || (n = {}), (n[s] = a[s])) + } else (n || (i || (i = []), i.push(c, n)), (n = a)) + else + c === 'dangerouslySetInnerHTML' + ? ((a = a ? a.__html : void 0), (u = u ? u.__html : void 0), a != null && u !== a && (i = i || []).push(c, a)) + : c === 'children' + ? (typeof a != 'string' && typeof a != 'number') || (i = i || []).push(c, '' + a) + : c !== 'suppressContentEditableWarning' && + c !== 'suppressHydrationWarning' && + (On.hasOwnProperty(c) + ? (a != null && c === 'onScroll' && D('scroll', e), i || u === a || (i = [])) + : (i = i || []).push(c, a)) + } + n && (i = i || []).push('style', n) + var c = i + ;(t.updateQueue = c) && (t.flags |= 4) + } +} +Za = function (e, t, n, r) { + n !== r && (t.flags |= 4) +} +function yn(e, t) { + if (!U) + switch (e.tailMode) { + case 'hidden': + t = e.tail + for (var n = null; t !== null; ) (t.alternate !== null && (n = t), (t = t.sibling)) + n === null ? (e.tail = null) : (n.sibling = null) + break + case 'collapsed': + n = e.tail + for (var r = null; n !== null; ) (n.alternate !== null && (r = n), (n = n.sibling)) + r === null ? (t || e.tail === null ? (e.tail = null) : (e.tail.sibling = null)) : (r.sibling = null) + } +} +function re(e) { + var t = e.alternate !== null && e.alternate.child === e.child, + n = 0, + r = 0 + if (t) + for (var l = e.child; l !== null; ) + ((n |= l.lanes | l.childLanes), (r |= l.subtreeFlags & 14680064), (r |= l.flags & 14680064), (l.return = e), (l = l.sibling)) + else + for (l = e.child; l !== null; ) ((n |= l.lanes | l.childLanes), (r |= l.subtreeFlags), (r |= l.flags), (l.return = e), (l = l.sibling)) + return ((e.subtreeFlags |= r), (e.childLanes = n), t) +} +function Ef(e, t, n) { + var r = t.pendingProps + switch ((hs(t), t.tag)) { + case 2: + case 16: + case 15: + case 0: + case 11: + case 7: + case 8: + case 12: + case 9: + case 14: + return (re(t), null) + case 1: + return (he(t.type) && Qr(), re(t), null) + case 3: + return ( + (r = t.stateNode), + rn(), + F(pe), + F(ie), + js(), + r.pendingContext && ((r.context = r.pendingContext), (r.pendingContext = null)), + (e === null || e.child === null) && + (mr(t) + ? (t.flags |= 4) + : e === null || + (e.memoizedState.isDehydrated && !(t.flags & 256)) || + ((t.flags |= 1024), Re !== null && (Ki(Re), (Re = null)))), + Fi(e, t), + re(t), + null + ) + case 5: + _s(t) + var l = jt(Qn.current) + if (((n = t.type), e !== null && t.stateNode != null)) + (Ga(e, t, n, r, l), e.ref !== t.ref && ((t.flags |= 512), (t.flags |= 2097152))) + else { + if (!r) { + if (t.stateNode === null) throw Error(w(166)) + return (re(t), null) + } + if (((e = jt(Ae.current)), mr(t))) { + ;((r = t.stateNode), (n = t.type)) + var i = t.memoizedProps + switch (((r[Fe] = t), (r[Hn] = i), (e = (t.mode & 1) !== 0), n)) { + case 'dialog': + ;(D('cancel', r), D('close', r)) + break + case 'iframe': + case 'object': + case 'embed': + D('load', r) + break + case 'video': + case 'audio': + for (l = 0; l < jn.length; l++) D(jn[l], r) + break + case 'source': + D('error', r) + break + case 'img': + case 'image': + case 'link': + ;(D('error', r), D('load', r)) + break + case 'details': + D('toggle', r) + break + case 'input': + ;(Zs(r, i), D('invalid', r)) + break + case 'select': + ;((r._wrapperState = { wasMultiple: !!i.multiple }), D('invalid', r)) + break + case 'textarea': + ;(qs(r, i), D('invalid', r)) + } + ;(di(n, i), (l = null)) + for (var s in i) + if (i.hasOwnProperty(s)) { + var u = i[s] + s === 'children' + ? typeof u == 'string' + ? r.textContent !== u && (i.suppressHydrationWarning !== !0 && hr(r.textContent, u, e), (l = ['children', u])) + : typeof u == 'number' && + r.textContent !== '' + u && + (i.suppressHydrationWarning !== !0 && hr(r.textContent, u, e), (l = ['children', '' + u])) + : On.hasOwnProperty(s) && u != null && s === 'onScroll' && D('scroll', r) + } + switch (n) { + case 'input': + ;(sr(r), Js(r, i, !0)) + break + case 'textarea': + ;(sr(r), bs(r)) + break + case 'select': + case 'option': + break + default: + typeof i.onClick == 'function' && (r.onclick = Wr) + } + ;((r = l), (t.updateQueue = r), r !== null && (t.flags |= 4)) + } else { + ;((s = l.nodeType === 9 ? l : l.ownerDocument), + e === 'http://www.w3.org/1999/xhtml' && (e = Su(n)), + e === 'http://www.w3.org/1999/xhtml' + ? n === 'script' + ? ((e = s.createElement('div')), (e.innerHTML = '