diff --git a/.github/workflows/telos-security.yml b/.github/workflows/telos-security.yml index fb9ad82c7..88b9e7f38 100644 --- a/.github/workflows/telos-security.yml +++ b/.github/workflows/telos-security.yml @@ -74,7 +74,13 @@ jobs: # GHSA-frvp-7c67-39w9 is a Windows-only serve-static traversal in the nested # @modelcontextprotocol/sdk Hono adapter. Vocs does not import or bundle that adapter, # and this Linux static build serves no files through it. No patched 1.x release exists. - bun audit --ignore GHSA-frvp-7c67-39w9 + # GHSA-w3rx-r6r6-pgpr and GHSA-5p2g-fcmc-qvqq affect optional image-size decoders. + # Vocs 2.6.1 pins the latest release, which has no patched successor; the controlled + # documentation build does not process ICNS, JXL, or HEIF input. + bun audit \ + --ignore GHSA-frvp-7c67-39w9 \ + --ignore GHSA-w3rx-r6r6-pgpr \ + --ignore GHSA-5p2g-fcmc-qvqq security-success: name: telos / security success diff --git a/Cargo.lock b/Cargo.lock index 31cc75ca2..00ebcf861 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9641,6 +9641,7 @@ dependencies = [ "reth-stages", "reth-stages-types", "reth-storage-errors", + "reth-tasks", "reth-telos-rpc", "reth-telos-rpc-engine-api", "revm", diff --git a/crates/telos/bin/src/main.rs b/crates/telos/bin/src/main.rs index e84d5e42e..e4f16acfa 100644 --- a/crates/telos/bin/src/main.rs +++ b/crates/telos/bin/src/main.rs @@ -21,7 +21,7 @@ use reth_ethereum_cli::ExtendedCommand; use reth_node_telos::{ rpc_policy::{ enforce_exact_auth_rpc_surface, enforce_exact_public_rpc_surface, enforce_telos_rpc_policy, - validate_telos_transaction_count_block, REPLAY_UNSAFE_RPC_METHODS, + telos_pubsub_module, validate_telos_transaction_count_block, REPLAY_UNSAFE_RPC_METHODS, TELOS_FORWARDER_REQUIRED_RPC_METHODS, TELOS_UNSUPPORTED_AUTH_METHODS, TELOS_UNSUPPORTED_RPC_METHODS, }, @@ -304,6 +304,18 @@ fn main() { "removed RPC methods that require a native Telos forwarder" ); } + if ctx + .modules + .module_config() + .contains_ws(&RethRpcModule::Eth) + { + let module = telos_pubsub_module( + ctx.registry.eth_handlers().pubsub.clone(), + ctx.registry.tasks().clone(), + )?; + ctx.modules.add_or_replace_ws(module)?; + info!(target: "reth::cli", "installed restricted Telos WebSocket subscriptions"); + } enforce_exact_public_rpc_surface(ctx.modules, forwarder_enabled)?; enforce_exact_auth_rpc_surface(ctx.auth_module.module_mut())?; Ok(()) diff --git a/crates/telos/node/Cargo.toml b/crates/telos/node/Cargo.toml index 41f5fcfa1..00b3d183b 100644 --- a/crates/telos/node/Cargo.toml +++ b/crates/telos/node/Cargo.toml @@ -46,6 +46,7 @@ reth-rpc-server-types.workspace = true reth-rpc-builder.workspace = true reth-stages.workspace = true reth-stages-types.workspace = true +reth-tasks.workspace = true # telos reth-telos-rpc.workspace = true diff --git a/crates/telos/node/src/args.rs b/crates/telos/node/src/args.rs index bbd0f01d8..d97722f28 100644 --- a/crates/telos/node/src/args.rs +++ b/crates/telos/node/src/args.rs @@ -35,6 +35,10 @@ pub struct TelosArgs { /// Seconds to cache the on-chain gas price. #[arg(long = "telos.gas-cache-seconds", default_value_t = 8)] pub gas_cache_seconds: u32, + + /// Ask the native Telos API to retry submitted transactions until they enter a block. + #[arg(long = "telos.transaction-retry", default_value_t = false)] + pub transaction_retry: bool, } impl Default for TelosArgs { @@ -46,6 +50,7 @@ impl Default for TelosArgs { signer_permission: None, signer_key_file: None, gas_cache_seconds: 8, + transaction_retry: false, } } } @@ -123,6 +128,7 @@ impl From for TelosClientArgs { signer_permission: args.signer_permission, signer_key_file: args.signer_key_file, gas_cache_seconds: Some(args.gas_cache_seconds), + transaction_retry: args.transaction_retry, } } } @@ -148,6 +154,16 @@ mod tests { assert!(args.validate().is_ok()); } + #[test] + fn transaction_retry_is_explicitly_enabled() { + let defaults = CommandParser::::parse_from(["reth"]).args; + assert!(!defaults.transaction_retry); + + let enabled = + CommandParser::::parse_from(["reth", "--telos.transaction-retry"]).args; + assert!(enabled.transaction_retry); + } + #[test] fn partial_forwarder_configuration_is_rejected() { let args = CommandParser::::parse_from([ diff --git a/crates/telos/node/src/frame.rs b/crates/telos/node/src/frame.rs index 82da3d060..4f2e72fb3 100644 --- a/crates/telos/node/src/frame.rs +++ b/crates/telos/node/src/frame.rs @@ -128,7 +128,7 @@ impl TelosFrame { return return_result(result) } - // Native chain-ID-3 transactions from the zero address represent withdrawals. The value + // Native chain-ID-3 transactions from the zero address represent deposits. The value // transfer above is authoritative, but executing target bytecode would fabricate a second // state transition that does not exist in the native runtime. if stops_after_native_zero_caller_transfer(context) { @@ -303,10 +303,9 @@ fn apply_call_value( inputs.target_address.is_zero() && !inputs.caller.is_zero() => { - // A native deposit to address zero is a burn. Debit and journal the source while only - // touching the destination; crediting the zero account would mint an EVM-only balance. - // The legacy transfer still checked whether the temporary destination credit would - // overflow before undoing it, so preserve both error precedence and that edge case. + // A native withdrawal to address zero is a burn. Debit and journal the source while + // only touching the destination; crediting the zero account would create an EVM-only + // balance. Preserve the legacy temporary-credit overflow check and its error order. let source_balance = { let source = journal.load_account_mut(inputs.caller)?.data; *source.balance() @@ -335,9 +334,9 @@ fn apply_call_value( inputs.target_address.is_zero() && inputs.caller.is_zero() => { - // The legacy burn guard explicitly excludes the zero sender. A zero-to-zero transfer - // therefore behaves as a checked self-transfer: it touches the account but leaves the - // balance unchanged before the native zero-caller early stop. + // The withdrawal burn guard explicitly excludes the zero sender. A zero-to-zero + // transfer therefore behaves as a checked self-transfer: it touches the account but + // leaves the balance unchanged before the native zero-caller early stop. let mut account = journal.load_account_mut(Address::ZERO)?.data; if value > *account.balance() { return Ok(Some(InstructionResult::OutOfFunds)) @@ -577,7 +576,7 @@ mod tests { } #[test] - fn chain_three_transfer_to_zero_burns_without_crediting_zero() { + fn chain_three_withdrawal_to_zero_burns_without_crediting_zero() { let caller = Address::repeat_byte(0x11); let mut context = context_with_accounts( native_tx(caller, 3, 1), @@ -643,7 +642,7 @@ mod tests { } #[test] - fn chain_three_burn_preserves_legacy_destination_overflow_error() { + fn chain_three_withdrawal_preserves_destination_overflow_error() { let caller = Address::repeat_byte(0x11); let mut context = context_with_accounts( native_tx(caller, 3, 1), diff --git a/crates/telos/node/src/handler.rs b/crates/telos/node/src/handler.rs index d7670773c..f55ee22aa 100644 --- a/crates/telos/node/src/handler.rs +++ b/crates/telos/node/src/handler.rs @@ -386,7 +386,7 @@ mod tests { } #[test] - fn chain_three_deposit_to_zero_burns_value() { + fn chain_three_withdrawal_to_zero_burns_value() { let caller = Address::repeat_byte(0x11); let mut evm = evm_with_caller(caller, 0); evm.components_mut().0.insert_account_info( @@ -451,6 +451,26 @@ mod tests { assert_eq!(output.state[&caller].info.nonce, 5); } + #[test] + fn chain_three_stale_nonce_withdrawal_burns_and_preserves_state_nonce() { + let caller = Address::repeat_byte(0x11); + let mut evm = evm_with_caller(caller, 12); + evm.components_mut().0.insert_account_info( + Address::ZERO, + AccountInfo { balance: U256::from(7), ..Default::default() }, + ); + let mut tx = transaction(caller, 0, true); + tx.inner.kind = TxKind::Call(Address::ZERO); + tx.inner.value = U256::from(25); + + let output = evm.transact_raw(tx).unwrap(); + let gas_used = output.result.gas().tx_gas_used(); + + assert_eq!(output.state[&caller].info.balance, U256::from(10_000_000 - gas_used - 25)); + assert_eq!(output.state[&caller].info.nonce, 12); + assert_eq!(output.state[&Address::ZERO].info.balance, U256::from(7)); + } + #[test] fn chain_three_stale_nonce_to_nonzero_advances_existing_nonce() { let caller = Address::repeat_byte(0x11); diff --git a/crates/telos/node/src/rpc_policy.rs b/crates/telos/node/src/rpc_policy.rs index 4279c2dab..00ce75257 100644 --- a/crates/telos/node/src/rpc_policy.rs +++ b/crates/telos/node/src/rpc_policy.rs @@ -1,8 +1,13 @@ //! Telos RPC surface policy. use alloy_eips::BlockId; +use alloy_rpc_types_eth::pubsub::{Params, SubscriptionKind}; +use jsonrpsee::{types::ErrorObjectOwned, RpcModule}; use reth_node_core::args::RpcServerArgs; +use reth_rpc::EthPubSub; use reth_rpc_builder::{RethRpcModule, TransportRpcModules}; +use reth_rpc_eth_api::helpers::EthSubscriptions; +use reth_tasks::Runtime; use std::collections::BTreeSet; /// Complete authenticated API exposed to the trusted companion. @@ -61,6 +66,9 @@ pub const TELOS_PUBLIC_ETH_RPC_ALLOWLIST: &[&str] = &[ "eth_uninstallFilter", ]; +/// WebSocket-only Ethereum methods qualified for Telos canonical-chain notifications. +pub const TELOS_PUBLIC_ETH_WS_RPC_ALLOWLIST: &[&str] = &["eth_subscribe", "eth_unsubscribe"]; + /// Exact public network methods qualified for the follower's no-peer network implementation. pub const TELOS_PUBLIC_NET_RPC_ALLOWLIST: &[&str] = &["net_peerCount", "net_version"]; @@ -118,6 +126,60 @@ pub fn validate_telos_transaction_count_block(block: Option) -> Result< Ok(()) } +/// Builds the restricted Telos subscription surface. +/// +/// Canonical heads, logs, and receipts are sourced from the follower's canonical-state stream. +/// Pending transactions and syncing are intentionally rejected because Reth's local pool and +/// peer-network status do not represent nodeos transaction propagation or companion catch-up. +pub fn telos_pubsub_module( + pubsub: EthPubSub, + tasks: Runtime, +) -> eyre::Result> +where + Eth: EthSubscriptions + 'static, +{ + let mut module = RpcModule::new(()); + module.register_subscription( + "eth_subscribe", + "eth_subscription", + "eth_unsubscribe", + move |params, pending, _ctx, _ext| { + let pubsub = pubsub.clone(); + let tasks = tasks.clone(); + async move { + let mut params = params.sequence(); + let kind: SubscriptionKind = params.next()?; + let subscription_params: Option = params.optional_next()?; + + if !is_telos_subscription_kind_supported(&kind) { + pending + .reject(ErrorObjectOwned::owned( + -32602, + "unsupported Telos subscription: use newHeads, logs, or transactionReceipts", + None::<()>, + )) + .await; + return Ok(()) + } + + let sink = pending.accept().await?; + tasks.spawn_task(async move { + let _ = pubsub.handle_accepted(sink, kind, subscription_params).await; + }); + Ok(()) + } + }, + )?; + Ok(module) +} + +const fn is_telos_subscription_kind_supported(kind: &SubscriptionKind) -> bool { + matches!( + kind, + SubscriptionKind::NewHeads | SubscriptionKind::Logs | SubscriptionKind::TransactionReceipts + ) +} + /// Authenticated methods outside the minimal Telos companion protocol. pub const TELOS_UNSUPPORTED_AUTH_METHODS: [&str; 17] = [ "engine_getClientVersionV1", @@ -238,6 +300,9 @@ pub fn enforce_exact_public_rpc_surface( expected.extend(TELOS_PUBLIC_ETH_RPC_ALLOWLIST.iter().copied().filter(|method| { forwarder_enabled || !TELOS_FORWARDER_REQUIRED_RPC_METHODS.contains(method) })); + if transport == "WebSocket" { + expected.extend(TELOS_PUBLIC_ETH_WS_RPC_ALLOWLIST.iter().copied()); + } } if net { expected.extend(TELOS_PUBLIC_NET_RPC_ALLOWLIST.iter().copied()); @@ -310,6 +375,15 @@ mod tests { assert!(validate_telos_transaction_count_block(Some(BlockId::pending())).is_err()); } + #[test] + fn subscriptions_expose_only_canonical_chain_events() { + assert!(is_telos_subscription_kind_supported(&SubscriptionKind::NewHeads)); + assert!(is_telos_subscription_kind_supported(&SubscriptionKind::Logs)); + assert!(is_telos_subscription_kind_supported(&SubscriptionKind::TransactionReceipts)); + assert!(!is_telos_subscription_kind_supported(&SubscriptionKind::NewPendingTransactions)); + assert!(!is_telos_subscription_kind_supported(&SubscriptionKind::Syncing)); + } + #[test] fn closed_gate_disables_regular_ipc_but_not_auth_ipc() { let mut rpc = RpcServerArgs { auth_ipc: true, ..Default::default() }; @@ -424,7 +498,7 @@ mod tests { fn public_surface_is_exact_and_namespace_aware() { let config = TransportRpcModuleConfig::default() .with_http([RethRpcModule::Eth, RethRpcModule::Net]) - .with_ws([RethRpcModule::Web3]); + .with_ws([RethRpcModule::Eth, RethRpcModule::Web3]); let mut http = RpcModule::new(()); for method in TELOS_PUBLIC_ETH_RPC_ALLOWLIST .iter() @@ -434,7 +508,12 @@ mod tests { http.register_method(method, |_, _, _| "ok").unwrap(); } let mut ws = RpcModule::new(()); - for method in TELOS_PUBLIC_WEB3_RPC_ALLOWLIST.iter().copied() { + for method in TELOS_PUBLIC_ETH_RPC_ALLOWLIST + .iter() + .copied() + .chain(TELOS_PUBLIC_ETH_WS_RPC_ALLOWLIST.iter().copied()) + .chain(TELOS_PUBLIC_WEB3_RPC_ALLOWLIST.iter().copied()) + { ws.register_method(method, |_, _, _| "ok").unwrap(); } let modules = diff --git a/crates/telos/node/testdata/revm-differential/legacy-telos-revm-237d6322.v1.json b/crates/telos/node/testdata/revm-differential/legacy-telos-revm-237d6322.v1.json index 5a70b53ff..f063c6559 100644 --- a/crates/telos/node/testdata/revm-differential/legacy-telos-revm-237d6322.v1.json +++ b/crates/telos/node/testdata/revm-differential/legacy-telos-revm-237d6322.v1.json @@ -164,12 +164,13 @@ "crates/revm/src/journaled_state.rs" ], "legacy_semantics": "A nonzero chain-3 caller transferring to address zero is debited without crediting zero, while zero-to-zero remains neutral and the destination overflow check still occurs.", - "port": "Telos frame value application preserves the legacy burn guard, touch, self-transfer, and overflow ordering.", + "port": "Telos frame value application preserves the legacy withdrawal burn, touch, self-transfer, and overflow ordering.", "tests": [ - { "path": "src/frame.rs", "function": "chain_three_transfer_to_zero_burns_without_crediting_zero" }, + { "path": "src/frame.rs", "function": "chain_three_withdrawal_to_zero_burns_without_crediting_zero" }, { "path": "src/frame.rs", "function": "chain_three_zero_caller_to_zero_self_transfer_is_balance_neutral" }, - { "path": "src/frame.rs", "function": "chain_three_burn_preserves_legacy_destination_overflow_error" }, - { "path": "src/frame.rs", "function": "unauthenticated_transfer_to_zero_uses_ethereum_credit" } + { "path": "src/frame.rs", "function": "chain_three_withdrawal_preserves_destination_overflow_error" }, + { "path": "src/frame.rs", "function": "unauthenticated_transfer_to_zero_uses_ethereum_credit" }, + { "path": "src/handler.rs", "function": "chain_three_stale_nonce_withdrawal_burns_and_preserves_state_nonce" } ] }, { diff --git a/crates/telos/rpc/src/telos_client.rs b/crates/telos/rpc/src/telos_client.rs index ad87ce690..f540809db 100644 --- a/crates/telos/rpc/src/telos_client.rs +++ b/crates/telos/rpc/src/telos_client.rs @@ -85,6 +85,8 @@ pub struct TelosClientArgs { /// Seconds to cache the gas-price reading from the `eosio.evm` config table. /// Defaults to 8 seconds when unset. pub gas_cache_seconds: Option, + /// Whether to ask nodeos to retry a transaction until it enters a block. + pub transaction_retry: bool, } impl fmt::Debug for TelosClientArgs { @@ -96,6 +98,7 @@ impl fmt::Debug for TelosClientArgs { .field("signer_permission", &self.signer_permission) .field("signer_key_file", &self.signer_key_file.as_ref().map(|_| "")) .field("gas_cache_seconds", &self.gas_cache_seconds) + .field("transaction_retry", &self.transaction_retry) .finish() } } @@ -142,7 +145,7 @@ pub enum TelosClientError { /// A client that forwards signed Ethereum transactions to the Telos native chain /// by wrapping them in an `eosio.evm::raw` action and submitting a signed Antelope -/// transaction to `/v1/chain/push_transaction`. +/// transaction to a native Telos submission endpoint. #[derive(Clone)] pub struct TelosClient { inner: Arc, @@ -161,6 +164,7 @@ struct TelosClientInner { expected_native_chain_id: B256, max_priority_fee_per_gas: U256, gas_cache_seconds: u32, + transaction_retry: bool, gas_price_cache: Mutex>, } @@ -378,6 +382,7 @@ impl TelosClient { expected_native_chain_id, max_priority_fee_per_gas, gas_cache_seconds, + transaction_retry: args.transaction_retry, gas_price_cache: Mutex::new(None), }), }) @@ -394,7 +399,7 @@ impl TelosClient { /// 2. Build the action + packed transaction. /// 3. `sha256(chain_id || packed_trx || zero_cfa_hash)` → digest. /// 4. K1 canonical sign. - /// 5. POST to `/v1/chain/push_transaction`. + /// 5. POST to the configured native submission endpoint. pub async fn send_to_telos(&self, tx: &[u8]) -> Result<(), EthApiError> { validate_raw_transaction(tx, self.inner.expected_evm_chain_id)?; let submission = self.prepare_submission_with_retry(tx).await.map_err(|err| { @@ -508,8 +513,9 @@ impl TelosClient { &self, submission: &PreparedSubmission, ) -> Result<(), antelope::AntelopeError> { - let url = format!("{}/v1/chain/push_transaction", self.inner.endpoint); - let response = self.inner.http_client.post(&url).json(&submission.payload).send().await?; + let (path, payload) = submission_request(&submission.payload, self.inner.transaction_retry); + let url = format!("{}{}", self.inner.endpoint, path); + let response = self.inner.http_client.post(&url).json(&payload).send().await?; let (status, body) = read_nodeos_response(response).await?; let status_code = status.as_u16(); if !status.is_success() { @@ -695,6 +701,25 @@ impl TelosClient { } } +fn submission_request( + transaction: &serde_json::Value, + transaction_retry: bool, +) -> (&'static str, serde_json::Value) { + if transaction_retry { + ( + "/v1/chain/send_transaction2", + serde_json::json!({ + "return_failure_trace": true, + "retry_trx": true, + "retry_trx_num_blocks": 0, + "transaction": transaction, + }), + ) + } else { + ("/v1/chain/push_transaction", transaction.clone()) + } +} + fn validate_raw_transaction(raw: &[u8], expected_chain_id: u64) -> Result<(), EthApiError> { if raw.len() > MAX_RAW_TRANSACTION_BYTES { return Err(EthApiError::EvmCustom(format!( @@ -913,6 +938,7 @@ mod tests { expected_native_chain_id, max_priority_fee_per_gas, gas_cache_seconds: DEFAULT_GAS_CACHE_SECONDS, + transaction_retry: false, gas_price_cache: Mutex::new(None), }), } @@ -1026,6 +1052,37 @@ mod tests { assert!(validate_transaction_response(&value, &expected_transaction_id).is_ok()); } + #[test] + fn durable_retry_uses_send_transaction2_until_inclusion() { + let transaction = serde_json::json!({ + "signatures": ["SIG_K1_test"], + "compression": "none", + "packed_context_free_data": "", + "packed_trx": "00", + }); + let (path, payload) = submission_request(&transaction, true); + + assert_eq!(path, "/v1/chain/send_transaction2"); + assert_eq!(payload["return_failure_trace"], true); + assert_eq!(payload["retry_trx"], true); + assert_eq!(payload["retry_trx_num_blocks"], 0); + assert_eq!(payload["transaction"], transaction); + } + + #[test] + fn retry_disabled_preserves_push_transaction_request() { + let transaction = serde_json::json!({ + "signatures": ["SIG_K1_test"], + "compression": "none", + "packed_context_free_data": "", + "packed_trx": "00", + }); + let (path, payload) = submission_request(&transaction, false); + + assert_eq!(path, "/v1/chain/push_transaction"); + assert_eq!(payload, transaction); + } + #[test] fn rejects_incomplete_success_response() { let expected_transaction_id = [0xabu8; 32]; diff --git a/deny.toml b/deny.toml index be45b22db..e09d3531e 100644 --- a/deny.toml +++ b/deny.toml @@ -12,6 +12,9 @@ ignore = [ "RUSTSEC-2023-0089", # https://rustsec.org/advisories/RUSTSEC-2026-0173 proc-macro-error2 is unmaintained "RUSTSEC-2026-0173", + # https://rustsec.org/advisories/RUSTSEC-2026-0247 bitmaps is unmaintained and has no + # maintained drop-in release; it is transitively pinned by upstream Reth 2.4.1 through imbl. + "RUSTSEC-2026-0247", ] # This section is considered when running `cargo deny check bans`. diff --git a/docs/vocs/bun.lock b/docs/vocs/bun.lock index bb1398b7e..b8ddb88ca 100644 --- a/docs/vocs/bun.lock +++ b/docs/vocs/bun.lock @@ -5,7 +5,7 @@ "": { "name": "vocs", "dependencies": { - "mermaid": "^11.16.0", + "mermaid": "^11.16.1", "react": "19.2.8", "react-dom": "19.2.8", "react-server-dom-webpack": "19.2.8", @@ -27,13 +27,14 @@ "@hono/node-server": "2.0.12", "body-parser": "2.3.0", "brace-expansion": "5.0.9", - "dompurify": "3.4.12", + "dompurify": "3.4.13", "esbuild": "0.28.1", "fast-uri": "3.1.5", "hono": "4.13.0", "ip-address": "10.4.0", - "js-yaml": "4.3.0", + "js-yaml": "4.3.1", "mdast-util-to-hast": "13.2.1", + "nanoid": "3.3.18", "picomatch": "4.0.5", "postcss": "8.5.25", "react-router": "7.18.1", @@ -879,7 +880,7 @@ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], - "dompurify": ["dompurify@3.4.12", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg=="], + "dompurify": ["dompurify@3.4.13", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ=="], "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], @@ -1149,7 +1150,7 @@ "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="], + "js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -1269,7 +1270,7 @@ "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], - "mermaid": ["mermaid@11.16.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.20", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "katex": "^0.16.45", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA=="], + "mermaid": ["mermaid@11.16.1", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.20", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "katex": "^0.16.45", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g=="], "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], @@ -1361,7 +1362,7 @@ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], @@ -1789,8 +1790,6 @@ "@scalar/api-client/@scalar/workspace-store": ["@scalar/workspace-store@0.55.6", "", { "dependencies": { "@scalar/asyncapi-upgrader": "0.1.4", "@scalar/helpers": "0.9.2", "@scalar/json-magic": "0.12.19", "@scalar/openapi-upgrader": "0.2.11", "@scalar/schemas": "0.7.4", "@scalar/snippetz": "0.9.23", "@scalar/typebox": "0.1.3", "@scalar/types": "0.16.4", "@scalar/validation": "0.6.2", "js-base64": "^3.7.8", "type-fest": "^5.3.1", "vue": "^3.5.30", "yaml": "^2.8.3" } }, "sha512-l7yuW1XplVRu+bqN7Mt+XtMovuKRBOHSGb00e+nEvjkkL1WIFd6pMJCogYt0ZlLvyLFPbWODWA4oG6LZjr7ijQ=="], - "@scalar/api-client/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - "@scalar/blocks/@scalar/workspace-store": ["@scalar/workspace-store@0.55.6", "", { "dependencies": { "@scalar/asyncapi-upgrader": "0.1.4", "@scalar/helpers": "0.9.2", "@scalar/json-magic": "0.12.19", "@scalar/openapi-upgrader": "0.2.11", "@scalar/schemas": "0.7.4", "@scalar/snippetz": "0.9.23", "@scalar/typebox": "0.1.3", "@scalar/types": "0.16.4", "@scalar/validation": "0.6.2", "js-base64": "^3.7.8", "type-fest": "^5.3.1", "vue": "^3.5.30", "yaml": "^2.8.3" } }, "sha512-l7yuW1XplVRu+bqN7Mt+XtMovuKRBOHSGb00e+nEvjkkL1WIFd6pMJCogYt0ZlLvyLFPbWODWA4oG6LZjr7ijQ=="], "@scalar/components/@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], @@ -1803,10 +1802,6 @@ "@scalar/sidebar/@scalar/workspace-store": ["@scalar/workspace-store@0.55.6", "", { "dependencies": { "@scalar/asyncapi-upgrader": "0.1.4", "@scalar/helpers": "0.9.2", "@scalar/json-magic": "0.12.19", "@scalar/openapi-upgrader": "0.2.11", "@scalar/schemas": "0.7.4", "@scalar/snippetz": "0.9.23", "@scalar/typebox": "0.1.3", "@scalar/types": "0.16.4", "@scalar/validation": "0.6.2", "js-base64": "^3.7.8", "type-fest": "^5.3.1", "vue": "^3.5.30", "yaml": "^2.8.3" } }, "sha512-l7yuW1XplVRu+bqN7Mt+XtMovuKRBOHSGb00e+nEvjkkL1WIFd6pMJCogYt0ZlLvyLFPbWODWA4oG6LZjr7ijQ=="], - "@scalar/themes/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - - "@scalar/types/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - "@scalar/use-hooks/@scalar/validation": ["@scalar/validation@0.6.2", "", {}, "sha512-Sc1TkcwGV6aVCO51AyKeaGiP8gpwAHxEtO5d3tZzPV+KsnlC/YokQxFxwBrbIXw73k9hmcExnJyGu3k5i6n6VA=="], "@scalar/use-hooks/cva": ["cva@1.0.0-beta.4", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "typescript": ">= 4.5.5" }, "optionalPeers": ["typescript"] }, "sha512-F/JS9hScapq4DBVQXcK85l9U91M6ePeXoBMSp7vypzShoefUBxjQTo3g3935PUHgQd+IW77DjbPRIxugy4/GCQ=="], @@ -1919,8 +1914,6 @@ "radix-vue/@vueuse/shared": ["@vueuse/shared@10.11.1", "", { "dependencies": { "vue-demi": ">=0.14.8" } }, "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="], - "radix-vue/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - "raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], @@ -1993,8 +1986,6 @@ "@scalar/workspace-store/@scalar/openapi-upgrader/@scalar/openapi-types": ["@scalar/openapi-types@0.9.1", "", {}, "sha512-gkGhSkxSzADaBiNg+ZAbJuwj+ZUmzP2Pg9CWZ7ZP+0fck2WjPeDDM7aAbouAm0aQQMF9xBjSPXSA9a/qTHYaTw=="], - "@scalar/workspace-store/@scalar/types/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - "@svgr/hast-util-to-babel-ast/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@svgr/hast-util-to-babel-ast/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], diff --git a/docs/vocs/package.json b/docs/vocs/package.json index f07442415..a2d93f599 100644 --- a/docs/vocs/package.json +++ b/docs/vocs/package.json @@ -14,7 +14,7 @@ "fix-rustdocs-navigation": "bun scripts/fix-rustdocs-navigation.ts" }, "dependencies": { - "mermaid": "^11.16.0", + "mermaid": "^11.16.1", "react": "19.2.8", "react-dom": "19.2.8", "react-server-dom-webpack": "19.2.8", @@ -34,13 +34,14 @@ "@hono/node-server": "2.0.12", "body-parser": "2.3.0", "brace-expansion": "5.0.9", - "dompurify": "3.4.12", + "dompurify": "3.4.13", "esbuild": "0.28.1", "fast-uri": "3.1.5", "hono": "4.13.0", "ip-address": "10.4.0", - "js-yaml": "4.3.0", + "js-yaml": "4.3.1", "mdast-util-to-hast": "13.2.1", + "nanoid": "3.3.18", "picomatch": "4.0.5", "postcss": "8.5.25", "react-router": "7.18.1",